2025-02-13 12:51:11 +01:00
|
|
|
/** @file
|
|
|
|
|
Common code PlatformBootManager and PlatformBootManagerLight.
|
|
|
|
|
|
|
|
|
|
Copyright (C) 2025, Red Hat, Inc.
|
|
|
|
|
|
|
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
|
|
#include <Library/QemuFwCfgLib.h>
|
|
|
|
|
#include <Library/DebugLib.h>
|
|
|
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
|
#include <Library/UefiBootManagerLib.h>
|
|
|
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
|
|
|
#include <Library/UefiRuntimeServicesTableLib.h>
|
|
|
|
|
#include <Library/BaseLib.h>
|
|
|
|
|
#include <Library/PrintLib.h>
|
|
|
|
|
#include <Library/DevicePathLib.h>
|
|
|
|
|
#include <Library/PlatformBootManagerCommonLib.h>
|
|
|
|
|
#include <Library/BaseMemoryLib.h>
|
|
|
|
|
#include <Guid/GlobalVariable.h>
|
|
|
|
|
#include <Protocol/LoadedImage.h>
|
|
|
|
|
#include <Library/UefiLib.h>
|
|
|
|
|
#include <Protocol/FirmwareVolume2.h>
|
|
|
|
|
|
2025-01-20 18:36:47 +01:00
|
|
|
/**
|
|
|
|
|
Return the index of the load option in the load option array.
|
|
|
|
|
|
|
|
|
|
The function consider two load options are equal when the
|
|
|
|
|
FilePath are equal.
|
|
|
|
|
|
|
|
|
|
@param Key Pointer to the load option to be found.
|
|
|
|
|
@param Array Pointer to the array of load options to be found.
|
|
|
|
|
@param Count Number of entries in the Array.
|
|
|
|
|
|
|
|
|
|
@retval -1 Key wasn't found in the Array.
|
|
|
|
|
@retval 0 ~ Count-1 The index of the Key in the Array.
|
|
|
|
|
**/
|
|
|
|
|
INTN
|
|
|
|
|
STATIC
|
|
|
|
|
OvmfFindLoadOption (
|
|
|
|
|
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key,
|
|
|
|
|
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array,
|
|
|
|
|
IN UINTN Count
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
UINTN Index;
|
|
|
|
|
|
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
|
|
|
if (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) {
|
|
|
|
|
return (INTN)Index;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-13 12:51:11 +01:00
|
|
|
/**
|
|
|
|
|
This function checks whether a File exists within the firmware volume.
|
|
|
|
|
|
|
|
|
|
@param[in] FilePath Path of the file.
|
|
|
|
|
|
|
|
|
|
@return TRUE if the file exists within the volume, false
|
|
|
|
|
otherwise.
|
|
|
|
|
**/
|
|
|
|
|
BOOLEAN
|
|
|
|
|
static
|
|
|
|
|
FileIsInFv (
|
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *FilePath
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
UINT32 AuthenticationStatus;
|
|
|
|
|
EFI_FV_FILE_ATTRIBUTES FileAttributes;
|
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *SearchNode;
|
|
|
|
|
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *FvFileNode;
|
|
|
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *FvProtocol;
|
|
|
|
|
UINTN BufferSize;
|
|
|
|
|
EFI_FV_FILETYPE FoundType;
|
|
|
|
|
EFI_HANDLE FvHandle;
|
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Locate the Firmware Volume2 protocol instance that is denoted by the
|
|
|
|
|
// boot option. If this lookup fails (i.e., the boot option references a
|
|
|
|
|
// firmware volume that doesn't exist), then we'll proceed to delete the
|
|
|
|
|
// boot option.
|
|
|
|
|
//
|
|
|
|
|
SearchNode = FilePath;
|
|
|
|
|
Status = gBS->LocateDevicePath (
|
|
|
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
|
|
|
&SearchNode,
|
|
|
|
|
&FvHandle
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// File not Found
|
|
|
|
|
//
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The firmware volume was found; now let's see if it contains the FvFile
|
|
|
|
|
// identified by GUID.
|
|
|
|
|
//
|
|
|
|
|
Status = gBS->HandleProtocol (
|
|
|
|
|
FvHandle,
|
|
|
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
|
|
|
(VOID **)&FvProtocol
|
|
|
|
|
);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
|
|
|
|
FvFileNode = (MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *)NextDevicePathNode (FilePath);
|
|
|
|
|
//
|
|
|
|
|
// Buffer==NULL means we request metadata only: BufferSize, FoundType,
|
|
|
|
|
// FileAttributes.
|
|
|
|
|
//
|
|
|
|
|
Status = FvProtocol->ReadFile (
|
|
|
|
|
FvProtocol,
|
|
|
|
|
&FvFileNode->FvFileName, // NameGuid
|
|
|
|
|
NULL, // Buffer
|
|
|
|
|
&BufferSize,
|
|
|
|
|
&FoundType,
|
|
|
|
|
&FileAttributes,
|
|
|
|
|
&AuthenticationStatus
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// File not Found
|
|
|
|
|
//
|
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// The FvFile was found.
|
|
|
|
|
//
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
|
PlatformRegisterFvBootOption (
|
|
|
|
|
EFI_GUID *FileGuid,
|
|
|
|
|
CHAR16 *Description,
|
|
|
|
|
UINT32 Attributes,
|
|
|
|
|
BOOLEAN Enabled
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
INTN OptionIndex;
|
|
|
|
|
EFI_BOOT_MANAGER_LOAD_OPTION NewOption;
|
|
|
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
|
|
|
UINTN BootOptionCount;
|
|
|
|
|
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
|
|
|
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
|
|
|
|
|
|
|
|
Status = gBS->HandleProtocol (
|
|
|
|
|
gImageHandle,
|
|
|
|
|
&gEfiLoadedImageProtocolGuid,
|
|
|
|
|
(VOID **)&LoadedImage
|
|
|
|
|
);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
|
|
|
|
EfiInitializeFwVolDevicepathNode (&FileNode, FileGuid);
|
|
|
|
|
DevicePath = DevicePathFromHandle (LoadedImage->DeviceHandle);
|
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
|
|
|
DevicePath = AppendDevicePathNode (
|
|
|
|
|
DevicePath,
|
|
|
|
|
(EFI_DEVICE_PATH_PROTOCOL *)&FileNode
|
|
|
|
|
);
|
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// File is not in firmware, so it is going to be deleted anyway by
|
|
|
|
|
// RemoveStaleFvFileOptions, let's not add it.
|
|
|
|
|
//
|
|
|
|
|
if (!FileIsInFv (DevicePath)) {
|
|
|
|
|
FreePool (DevicePath);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
|
|
|
&NewOption,
|
|
|
|
|
LoadOptionNumberUnassigned,
|
|
|
|
|
LoadOptionTypeBoot,
|
|
|
|
|
Attributes,
|
|
|
|
|
Description,
|
|
|
|
|
DevicePath,
|
|
|
|
|
NULL,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
FreePool (DevicePath);
|
|
|
|
|
|
|
|
|
|
BootOptions = EfiBootManagerGetLoadOptions (
|
|
|
|
|
&BootOptionCount,
|
|
|
|
|
LoadOptionTypeBoot
|
|
|
|
|
);
|
|
|
|
|
|
2025-01-20 18:36:47 +01:00
|
|
|
OptionIndex = OvmfFindLoadOption (
|
2025-02-13 12:51:11 +01:00
|
|
|
&NewOption,
|
|
|
|
|
BootOptions,
|
|
|
|
|
BootOptionCount
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if ((OptionIndex == -1) && Enabled) {
|
|
|
|
|
Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
} else if ((OptionIndex != -1) && !Enabled) {
|
|
|
|
|
Status = EfiBootManagerDeleteLoadOptionVariable (
|
|
|
|
|
BootOptions[OptionIndex].OptionNumber,
|
|
|
|
|
LoadOptionTypeBoot
|
|
|
|
|
);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
2025-01-20 18:36:47 +01:00
|
|
|
} else if ((OptionIndex != -1) && Enabled && (BootOptions[OptionIndex].Attributes != NewOption.Attributes)) {
|
|
|
|
|
// Option Found and enabled but with different Attributes!
|
|
|
|
|
Status = EfiBootManagerDeleteLoadOptionVariable (
|
|
|
|
|
BootOptions[OptionIndex].OptionNumber,
|
|
|
|
|
LoadOptionTypeBoot
|
|
|
|
|
);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
|
|
|
|
Status = EfiBootManagerAddLoadOptionVariable (&NewOption, MAX_UINTN);
|
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
2025-02-13 12:51:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EfiBootManagerFreeLoadOption (&NewOption);
|
|
|
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
Remove all MemoryMapped(...)/FvFile(...) and Fv(...)/FvFile(...) boot options
|
|
|
|
|
whose device paths do not resolve exactly to an FvFile in the system.
|
|
|
|
|
|
|
|
|
|
This removes any boot options that point to binaries built into the firmware
|
|
|
|
|
and have become stale due to any of the following:
|
|
|
|
|
- DXEFV's base address or size changed (historical),
|
|
|
|
|
- DXEFV's FvNameGuid changed,
|
|
|
|
|
- the FILE_GUID of the pointed-to binary changed,
|
|
|
|
|
- the referenced binary is no longer built into the firmware.
|
|
|
|
|
|
|
|
|
|
EfiBootManagerFindLoadOption() used in PlatformRegisterFvBootOption() only
|
|
|
|
|
avoids exact duplicates.
|
|
|
|
|
**/
|
|
|
|
|
VOID
|
|
|
|
|
RemoveStaleFvFileOptions (
|
|
|
|
|
VOID
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
|
|
|
UINTN BootOptionCount;
|
|
|
|
|
UINTN Index;
|
|
|
|
|
|
|
|
|
|
BootOptions = EfiBootManagerGetLoadOptions (
|
|
|
|
|
&BootOptionCount,
|
|
|
|
|
LoadOptionTypeBoot
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (Index = 0; Index < BootOptionCount; ++Index) {
|
|
|
|
|
EFI_DEVICE_PATH_PROTOCOL *Node1, *Node2;
|
|
|
|
|
EFI_STATUS Status;
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the device path starts with neither MemoryMapped(...) nor Fv(...),
|
|
|
|
|
// then keep the boot option.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
Node1 = BootOptions[Index].FilePath;
|
|
|
|
|
if (!((DevicePathType (Node1) == HARDWARE_DEVICE_PATH) &&
|
|
|
|
|
(DevicePathSubType (Node1) == HW_MEMMAP_DP)) &&
|
|
|
|
|
!((DevicePathType (Node1) == MEDIA_DEVICE_PATH) &&
|
|
|
|
|
(DevicePathSubType (Node1) == MEDIA_PIWG_FW_VOL_DP)))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// If the second device path node is not FvFile(...), then keep the boot
|
|
|
|
|
// option.
|
|
|
|
|
//
|
|
|
|
|
Node2 = NextDevicePathNode (Node1);
|
|
|
|
|
if ((DevicePathType (Node2) != MEDIA_DEVICE_PATH) ||
|
|
|
|
|
(DevicePathSubType (Node2) != MEDIA_PIWG_FW_FILE_DP))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If file is in firmware then keep the entry
|
|
|
|
|
if (FileIsInFv (BootOptions[Index].FilePath)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Delete the boot option.
|
|
|
|
|
//
|
|
|
|
|
Status = EfiBootManagerDeleteLoadOptionVariable (
|
|
|
|
|
BootOptions[Index].OptionNumber,
|
|
|
|
|
LoadOptionTypeBoot
|
|
|
|
|
);
|
|
|
|
|
DEBUG_CODE_BEGIN ();
|
|
|
|
|
CHAR16 *DevicePathString;
|
|
|
|
|
|
|
|
|
|
DevicePathString = ConvertDevicePathToText (
|
|
|
|
|
BootOptions[Index].FilePath,
|
|
|
|
|
FALSE,
|
|
|
|
|
FALSE
|
|
|
|
|
);
|
|
|
|
|
DEBUG ((
|
|
|
|
|
EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_VERBOSE,
|
|
|
|
|
"%a: removing stale Boot#%04x %s: %r\n",
|
|
|
|
|
__func__,
|
|
|
|
|
(UINT32)BootOptions[Index].OptionNumber,
|
|
|
|
|
DevicePathString == NULL ? L"<unavailable>" : DevicePathString,
|
|
|
|
|
Status
|
|
|
|
|
));
|
|
|
|
|
if (DevicePathString != NULL) {
|
|
|
|
|
FreePool (DevicePathString);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUG_CODE_END ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
|
|
|
}
|