From a255b52fbc27405096f94441d70502ad8852ba15 Mon Sep 17 00:00:00 2001 From: Luigi Leonardi Date: Thu, 13 Feb 2025 12:51:11 +0100 Subject: [PATCH] OvmfPkg: Introduce PlatformBootManagerCommonLib Many function present in PlatformBootManagerLib and PlatformBootManagerLibLight have the same implementation. The idea of this new Library is to collect them to enable code reuse. Signed-off-by: Luigi Leonardi --- .../Library/PlatformBootManagerCommonLib.h | 28 ++ .../PlatformBootManagerCommonLib.c | 282 ++++++++++++++++++ .../PlatformBootManagerCommonLib.inf | 47 +++ 3 files changed, 357 insertions(+) create mode 100644 OvmfPkg/Include/Library/PlatformBootManagerCommonLib.h create mode 100644 OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.c create mode 100644 OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.inf diff --git a/OvmfPkg/Include/Library/PlatformBootManagerCommonLib.h b/OvmfPkg/Include/Library/PlatformBootManagerCommonLib.h new file mode 100644 index 0000000000..724f8ff37f --- /dev/null +++ b/OvmfPkg/Include/Library/PlatformBootManagerCommonLib.h @@ -0,0 +1,28 @@ +/** @file + Common code PlatformBootManager and PlatformBootManagerLight. + + Copyright (C) 2025, Red Hat, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef __PLATFORMBOOTMANAGERCOMMON_LIB_H__ +#define __PLATFORMBOOTMANAGERCOMMON_LIB_H__ + +#include +#include + +VOID +PlatformRegisterFvBootOption ( + EFI_GUID *FileGuid, + CHAR16 *Description, + UINT32 Attributes, + BOOLEAN Enabled + ); + +VOID +RemoveStaleFvFileOptions ( + VOID + ); + +#endif diff --git a/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.c b/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.c new file mode 100644 index 0000000000..41e033f21f --- /dev/null +++ b/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.c @@ -0,0 +1,282 @@ +/** @file + Common code PlatformBootManager and PlatformBootManagerLight. + + Copyright (C) 2025, Red Hat, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** +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 + ); + + OptionIndex = EfiBootManagerFindLoadOption ( + &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); + } + + 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"" : DevicePathString, + Status + )); + if (DevicePathString != NULL) { + FreePool (DevicePathString); + } + + DEBUG_CODE_END (); + } + + EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); +} diff --git a/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.inf b/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.inf new file mode 100644 index 0000000000..e6f45315c5 --- /dev/null +++ b/OvmfPkg/Library/PlatformBootManagerCommonLib/PlatformBootManagerCommonLib.inf @@ -0,0 +1,47 @@ +## @file +# Common code PlatformBootManager and PlatformBootManagerLight. +# +# Copyright (C) 2025, Red Hat, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PlatformBootManagerCommonLib + FILE_GUID = B6169BD3-EB70-4E00-B4CB-4C24C2C5235E + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformBootManagerCommonLib|DXE_DRIVER + +[Sources] +PlatformBootManagerCommonLib.c + +[Packages] + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + QemuFwCfgLib + DebugLib + MemoryAllocationLib + UefiBootManagerLib + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + BaseLib + PrintLib + DevicePathLib + BaseMemoryLib + OrderedCollectionLib + +[Guids] + gEfiGlobalVariableGuid + gVirtioMmioTransportGuid + gVMMBootOrderGuid + +[Protocols] + gEfiDevicePathProtocolGuid ## CONSUMES + gEfiPciRootBridgeIoProtocolGuid ## CONSUMES