summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c2670
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c875
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c315
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c761
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c585
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c1151
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c1469
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c535
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h471
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf123
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni18
11 files changed, 8973 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
new file mode 100644
index 00000000..aa240ab0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
@@ -0,0 +1,2670 @@
+/** @file
+ Library functions which relates with booting.
+
+Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;
+
+EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL;
+EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL;
+
+///
+/// This GUID is used for an EFI Variable that stores the front device pathes
+/// for a partial device path that starts with the HD node.
+///
+EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };
+EFI_GUID mBmAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };
+
+/**
+
+ End Perf entry of BDS
+
+ @param Event The triggered event.
+ @param Context Context for this event.
+
+**/
+VOID
+EFIAPI
+BmEndOfBdsPerfCode (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ //
+ // Record the performance data for End of BDS
+ //
+ PERF_CROSSMODULE_END("BDS");
+
+ return ;
+}
+
+/**
+ The function registers the legacy boot support capabilities.
+
+ @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.
+ @param LegacyBoot The function pointer to boot the legacy boot option.
+**/
+VOID
+EFIAPI
+EfiBootManagerRegisterLegacyBootSupport (
+ EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption,
+ EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot
+ )
+{
+ mBmRefreshLegacyBootOption = RefreshLegacyBootOption;
+ mBmLegacyBoot = LegacyBoot;
+}
+
+/**
+ Return TRUE when the boot option is auto-created instead of manually added.
+
+ @param BootOption Pointer to the boot option to check.
+
+ @retval TRUE The boot option is auto-created.
+ @retval FALSE The boot option is manually added.
+**/
+BOOLEAN
+BmIsAutoCreateBootOption (
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+ )
+{
+ if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) &&
+ CompareGuid ((EFI_GUID *) BootOption->OptionalData, &mBmAutoCreateBootOptionGuid)
+ ) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/**
+ Find the boot option in the NV storage and return the option number.
+
+ @param OptionToFind Boot option to be checked.
+
+ @return The option number of the found boot option.
+
+**/
+UINTN
+BmFindBootOptionInVariable (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
+ UINTN OptionNumber;
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ UINTN Index;
+
+ OptionNumber = LoadOptionNumberUnassigned;
+
+ //
+ // Try to match the variable exactly if the option number is assigned
+ //
+ if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {
+ UnicodeSPrint (
+ OptionName, sizeof (OptionName), L"%s%04x",
+ mBmLoadOptionName[OptionToFind->OptionType], OptionToFind->OptionNumber
+ );
+ Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);
+
+ if (!EFI_ERROR (Status)) {
+ ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);
+ if ((OptionToFind->Attributes == BootOption.Attributes) &&
+ (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&
+ (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&
+ (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&
+ (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)
+ ) {
+ OptionNumber = OptionToFind->OptionNumber;
+ }
+ EfiBootManagerFreeLoadOption (&BootOption);
+ }
+ }
+
+ //
+ // The option number assigned is either incorrect or unassigned.
+ //
+ if (OptionNumber == LoadOptionNumberUnassigned) {
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+
+ Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);
+ if (Index != -1) {
+ OptionNumber = BootOptions[Index].OptionNumber;
+ }
+
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+ }
+
+ return OptionNumber;
+}
+
+/**
+ Return the correct FV file path.
+ FV address may change across reboot. This routine promises the FV file device path is right.
+
+ @param FilePath The Memory Mapped Device Path to get the file buffer.
+
+ @return The updated FV Device Path pointint to the file.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmAdjustFvFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *FvFileNode;
+ EFI_HANDLE FvHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ UINTN FvHandleCount;
+ EFI_HANDLE *FvHandles;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;
+
+ //
+ // Get the file buffer by using the exactly FilePath.
+ //
+ FvFileNode = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
+ if (!EFI_ERROR (Status)) {
+ return DuplicateDevicePath (FilePath);
+ }
+
+ //
+ // Only wide match other FVs if it's a memory mapped FV file path.
+ //
+ if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) {
+ return NULL;
+ }
+
+ FvFileNode = NextDevicePathNode (FilePath);
+
+ //
+ // Firstly find the FV file in current FV
+ //
+ gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage
+ );
+ NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
+ FullPath = BmAdjustFvFilePath (NewDevicePath);
+ FreePool (NewDevicePath);
+ if (FullPath != NULL) {
+ return FullPath;
+ }
+
+ //
+ // Secondly find the FV file in all other FVs
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiFirmwareVolume2ProtocolGuid,
+ NULL,
+ &FvHandleCount,
+ &FvHandles
+ );
+ for (Index = 0; Index < FvHandleCount; Index++) {
+ if (FvHandles[Index] == LoadedImage->DeviceHandle) {
+ //
+ // Skip current FV, it was handed in first step.
+ //
+ continue;
+ }
+ NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
+ FullPath = BmAdjustFvFilePath (NewDevicePath);
+ FreePool (NewDevicePath);
+ if (FullPath != NULL) {
+ break;
+ }
+ }
+
+ if (FvHandles != NULL) {
+ FreePool (FvHandles);
+ }
+ return FullPath;
+}
+
+/**
+ Check if it's a Device Path pointing to FV file.
+
+ The function doesn't garentee the device path points to existing FV file.
+
+ @param DevicePath Input device path.
+
+ @retval TRUE The device path is a FV File Device Path.
+ @retval FALSE The device path is NOT a FV File Device Path.
+**/
+BOOLEAN
+BmIsFvFilePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ Node = DevicePath;
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status)) {
+ return TRUE;
+ }
+
+ if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {
+ DevicePath = NextDevicePathNode (DevicePath);
+ if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) {
+ return IsDevicePathEnd (NextDevicePathNode (DevicePath));
+ }
+ }
+ return FALSE;
+}
+
+/**
+ Check whether a USB device match the specified USB Class device path. This
+ function follows "Load Option Processing" behavior in UEFI specification.
+
+ @param UsbIo USB I/O protocol associated with the USB device.
+ @param UsbClass The USB Class device path to match.
+
+ @retval TRUE The USB device match the USB Class device path.
+ @retval FALSE The USB device does not match the USB Class device path.
+
+**/
+BOOLEAN
+BmMatchUsbClass (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN USB_CLASS_DEVICE_PATH *UsbClass
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
+ UINT8 DeviceClass;
+ UINT8 DeviceSubClass;
+ UINT8 DeviceProtocol;
+
+ if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){
+ return FALSE;
+ }
+
+ //
+ // Check Vendor Id and Product Id.
+ //
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->VendorId != 0xffff) &&
+ (UsbClass->VendorId != DevDesc.IdVendor)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->ProductId != 0xffff) &&
+ (UsbClass->ProductId != DevDesc.IdProduct)) {
+ return FALSE;
+ }
+
+ DeviceClass = DevDesc.DeviceClass;
+ DeviceSubClass = DevDesc.DeviceSubClass;
+ DeviceProtocol = DevDesc.DeviceProtocol;
+ if (DeviceClass == 0) {
+ //
+ // If Class in Device Descriptor is set to 0, use the Class, SubClass and
+ // Protocol in Interface Descriptor instead.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ DeviceClass = IfDesc.InterfaceClass;
+ DeviceSubClass = IfDesc.InterfaceSubClass;
+ DeviceProtocol = IfDesc.InterfaceProtocol;
+ }
+
+ //
+ // Check Class, SubClass and Protocol.
+ //
+ if ((UsbClass->DeviceClass != 0xff) &&
+ (UsbClass->DeviceClass != DeviceClass)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->DeviceSubClass != 0xff) &&
+ (UsbClass->DeviceSubClass != DeviceSubClass)) {
+ return FALSE;
+ }
+
+ if ((UsbClass->DeviceProtocol != 0xff) &&
+ (UsbClass->DeviceProtocol != DeviceProtocol)) {
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ Check whether a USB device match the specified USB WWID device path. This
+ function follows "Load Option Processing" behavior in UEFI specification.
+
+ @param UsbIo USB I/O protocol associated with the USB device.
+ @param UsbWwid The USB WWID device path to match.
+
+ @retval TRUE The USB device match the USB WWID device path.
+ @retval FALSE The USB device does not match the USB WWID device path.
+
+**/
+BOOLEAN
+BmMatchUsbWwid (
+ IN EFI_USB_IO_PROTOCOL *UsbIo,
+ IN USB_WWID_DEVICE_PATH *UsbWwid
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;
+ UINT16 *LangIdTable;
+ UINT16 TableSize;
+ UINT16 Index;
+ CHAR16 *CompareStr;
+ UINTN CompareLen;
+ CHAR16 *SerialNumberStr;
+ UINTN Length;
+
+ if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {
+ return FALSE;
+ }
+
+ //
+ // Check Vendor Id and Product Id.
+ //
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ if ((DevDesc.IdVendor != UsbWwid->VendorId) ||
+ (DevDesc.IdProduct != UsbWwid->ProductId)) {
+ return FALSE;
+ }
+
+ //
+ // Check Interface Number.
+ //
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+ if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {
+ return FALSE;
+ }
+
+ //
+ // Check Serial Number.
+ //
+ if (DevDesc.StrSerialNumber == 0) {
+ return FALSE;
+ }
+
+ //
+ // Get all supported languages.
+ //
+ TableSize = 0;
+ LangIdTable = NULL;
+ Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);
+ if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {
+ return FALSE;
+ }
+
+ //
+ // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.
+ //
+ CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);
+ CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);
+ if (CompareStr[CompareLen - 1] == L'\0') {
+ CompareLen--;
+ }
+
+ //
+ // Compare serial number in each supported language.
+ //
+ for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {
+ SerialNumberStr = NULL;
+ Status = UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ LangIdTable[Index],
+ DevDesc.StrSerialNumber,
+ &SerialNumberStr
+ );
+ if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {
+ continue;
+ }
+
+ Length = StrLen (SerialNumberStr);
+ if ((Length >= CompareLen) &&
+ (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {
+ FreePool (SerialNumberStr);
+ return TRUE;
+ }
+
+ FreePool (SerialNumberStr);
+ }
+
+ return FALSE;
+}
+
+/**
+ Find a USB device which match the specified short-form device path start with
+ USB Class or USB WWID device path. If ParentDevicePath is NULL, this function
+ will search in all USB devices of the platform. If ParentDevicePath is not NULL,
+ this function will only search in its child devices.
+
+ @param DevicePath The device path that contains USB Class or USB WWID device path.
+ @param ParentDevicePathSize The length of the device path before the USB Class or
+ USB WWID device path.
+ @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.
+
+ @retval NULL The matched USB IO handles cannot be found.
+ @retval other The matched USB IO handles.
+
+**/
+EFI_HANDLE *
+BmFindUsbDevice (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINTN ParentDevicePathSize,
+ OUT UINTN *UsbIoHandleCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *UsbIoHandles;
+ EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ UINTN Index;
+ BOOLEAN Matched;
+
+ ASSERT (UsbIoHandleCount != NULL);
+
+ //
+ // Get all UsbIo Handles.
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiUsbIoProtocolGuid,
+ NULL,
+ UsbIoHandleCount,
+ &UsbIoHandles
+ );
+ if (EFI_ERROR (Status)) {
+ *UsbIoHandleCount = 0;
+ UsbIoHandles = NULL;
+ }
+
+ for (Index = 0; Index < *UsbIoHandleCount; ) {
+ //
+ // Get the Usb IO interface.
+ //
+ Status = gBS->HandleProtocol(
+ UsbIoHandles[Index],
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo
+ );
+ UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);
+ Matched = FALSE;
+ if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {
+
+ //
+ // Compare starting part of UsbIoHandle's device path with ParentDevicePath.
+ //
+ if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {
+ if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||
+ BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {
+ Matched = TRUE;
+ }
+ }
+ }
+
+ if (!Matched) {
+ (*UsbIoHandleCount) --;
+ CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));
+ } else {
+ Index++;
+ }
+ }
+
+ return UsbIoHandles;
+}
+
+/**
+ Expand USB Class or USB WWID device path node to be full device path of a USB
+ device in platform.
+
+ This function support following 4 cases:
+ 1) Boot Option device path starts with a USB Class or USB WWID device path,
+ and there is no Media FilePath device path in the end.
+ In this case, it will follow Removable Media Boot Behavior.
+ 2) Boot Option device path starts with a USB Class or USB WWID device path,
+ and ended with Media FilePath device path.
+ 3) Boot Option device path starts with a full device path to a USB Host Controller,
+ contains a USB Class or USB WWID device path node, while not ended with Media
+ FilePath device path. In this case, it will follow Removable Media Boot Behavior.
+ 4) Boot Option device path starts with a full device path to a USB Host Controller,
+ contains a USB Class or USB WWID device path node, and ended with Media
+ FilePath device path.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+ @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandUsbDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath,
+ IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode
+ )
+{
+ UINTN ParentDevicePathSize;
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ BOOLEAN GetNext;
+
+ NextFullPath = NULL;
+ GetNext = (BOOLEAN)(FullPath == NULL);
+ ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;
+ RemainingDevicePath = NextDevicePathNode (ShortformNode);
+ Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
+ if (FilePath == NULL) {
+ //
+ // Out of memory.
+ //
+ continue;
+ }
+ NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);
+ FreePool (FilePath);
+ if (NextFullPath == NULL) {
+ //
+ // No BlockIo or SimpleFileSystem under FilePath.
+ //
+ continue;
+ }
+ if (GetNext) {
+ break;
+ } else {
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+ FreePool (NextFullPath);
+ NextFullPath = NULL;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ return NextFullPath;
+}
+
+/**
+ Expand File-path device path node to be full device path in platform.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandFileDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN HandleCount;
+ EFI_HANDLE *Handles;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ UINTN MediaType;
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
+ BOOLEAN GetNext;
+
+ EfiBootManagerConnectAll ();
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);
+ if (EFI_ERROR (Status)) {
+ HandleCount = 0;
+ Handles = NULL;
+ }
+
+ GetNext = (BOOLEAN)(FullPath == NULL);
+ NextFullPath = NULL;
+ //
+ // Enumerate all removable media devices followed by all fixed media devices,
+ // followed by media devices which don't layer on block io.
+ //
+ for (MediaType = 0; MediaType < 3; MediaType++) {
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *) &BlockIo);
+ if (EFI_ERROR (Status)) {
+ BlockIo = NULL;
+ }
+ if ((MediaType == 0 && BlockIo != NULL && BlockIo->Media->RemovableMedia) ||
+ (MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||
+ (MediaType == 2 && BlockIo == NULL)
+ ) {
+ NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
+ if (GetNext) {
+ break;
+ } else {
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+ FreePool (NextFullPath);
+ NextFullPath = NULL;
+ }
+ }
+ }
+ if (NextFullPath != NULL) {
+ break;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ return NextFullPath;
+}
+
+/**
+ Expand URI device path node to be full device path in platform.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandUriDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN HandleCount;
+ EFI_HANDLE *Handles;
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+ BOOLEAN GetNext;
+
+ EfiBootManagerConnectAll ();
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);
+ if (EFI_ERROR (Status)) {
+ HandleCount = 0;
+ Handles = NULL;
+ }
+
+ NextFullPath = NULL;
+ GetNext = (BOOLEAN)(FullPath == NULL);
+ for (Index = 0; Index < HandleCount; Index++) {
+ NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);
+
+ if (NextFullPath == NULL) {
+ continue;
+ }
+
+ if (GetNext) {
+ break;
+ } else {
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+ //
+ // Free the resource occupied by the RAM disk.
+ //
+ RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);
+ if (RamDiskDevicePath != NULL) {
+ BmDestroyRamDisk (RamDiskDevicePath);
+ FreePool (RamDiskDevicePath);
+ }
+ FreePool (NextFullPath);
+ NextFullPath = NULL;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ return NextFullPath;
+}
+
+/**
+ Save the partition DevicePath to the CachedDevicePath as the first instance.
+
+ @param CachedDevicePath The device path cache.
+ @param DevicePath The partition device path to be cached.
+**/
+VOID
+BmCachePartitionDevicePath (
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ UINTN Count;
+
+ if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {
+ TempDevicePath = *CachedDevicePath;
+ *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);
+ FreePool (TempDevicePath);
+ }
+
+ if (*CachedDevicePath == NULL) {
+ *CachedDevicePath = DuplicateDevicePath (DevicePath);
+ return;
+ }
+
+ TempDevicePath = *CachedDevicePath;
+ *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);
+ if (TempDevicePath != NULL) {
+ FreePool (TempDevicePath);
+ }
+
+ //
+ // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller
+ // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.
+ //
+ Count = 0;
+ TempDevicePath = *CachedDevicePath;
+ while (!IsDevicePathEnd (TempDevicePath)) {
+ TempDevicePath = NextDevicePathNode (TempDevicePath);
+ //
+ // Parse one instance
+ //
+ while (!IsDevicePathEndType (TempDevicePath)) {
+ TempDevicePath = NextDevicePathNode (TempDevicePath);
+ }
+ Count++;
+ //
+ // If the CachedDevicePath variable contain too much instance, only remain 12 instances.
+ //
+ if (Count == 12) {
+ SetDevicePathEndNode (TempDevicePath);
+ break;
+ }
+ }
+}
+
+/**
+ Expand a device path that starts with a hard drive media device path node to be a
+ full device path that includes the full hardware path to the device. We need
+ to do this so it can be booted. As an optimization the front match (the part point
+ to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable
+ so a connect all is not required on every boot. All successful history device path
+ which point to partition node (the front part) will be saved.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+
+ @return The full device path pointing to the load option.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandPartitionDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ UINTN BlockIoHandleCount;
+ EFI_HANDLE *BlockIoBuffer;
+ EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;
+ UINTN CachedDevicePathSize;
+ BOOLEAN NeedAdjust;
+ EFI_DEVICE_PATH_PROTOCOL *Instance;
+ UINTN Size;
+
+ //
+ // Check if there is prestore 'HDDP' variable.
+ // If exist, search the front path which point to partition node in the variable instants.
+ // If fail to find or 'HDDP' not exist, reconnect all and search in all system
+ //
+ GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);
+
+ //
+ // Delete the invalid 'HDDP' variable.
+ //
+ if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
+ FreePool (CachedDevicePath);
+ CachedDevicePath = NULL;
+ Status = gRT->SetVariable (
+ L"HDDP",
+ &mBmHardDriveBootVariableGuid,
+ 0,
+ 0,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ FullPath = NULL;
+ if (CachedDevicePath != NULL) {
+ TempNewDevicePath = CachedDevicePath;
+ NeedAdjust = FALSE;
+ do {
+ //
+ // Check every instance of the variable
+ // First, check whether the instance contain the partition node, which is needed for distinguishing multi
+ // partial partition boot option. Second, check whether the instance could be connected.
+ //
+ Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);
+ if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) {
+ //
+ // Connect the device path instance, the device path point to hard drive media device path node
+ // e.g. ACPI() /PCI()/ATA()/Partition()
+ //
+ Status = EfiBootManagerConnectDevicePath (Instance, NULL);
+ if (!EFI_ERROR (Status)) {
+ TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));
+ //
+ // TempDevicePath = ACPI()/PCI()/ATA()/Partition()
+ // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI
+ //
+ // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),
+ // it may expand to two potienal full paths (nested partition, rarely happen):
+ // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI
+ // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI
+ // For simplicity, only #1 is returned.
+ //
+ FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
+ FreePool (TempDevicePath);
+
+ if (FullPath != NULL) {
+ //
+ // Adjust the 'HDDP' instances sequence if the matched one is not first one.
+ //
+ if (NeedAdjust) {
+ BmCachePartitionDevicePath (&CachedDevicePath, Instance);
+ //
+ // Save the matching Device Path so we don't need to do a connect all next time
+ // Failing to save only impacts performance next time expanding the short-form device path
+ //
+ Status = gRT->SetVariable (
+ L"HDDP",
+ &mBmHardDriveBootVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ GetDevicePathSize (CachedDevicePath),
+ CachedDevicePath
+ );
+ }
+
+ FreePool (Instance);
+ FreePool (CachedDevicePath);
+ return FullPath;
+ }
+ }
+ }
+ //
+ // Come here means the first instance is not matched
+ //
+ NeedAdjust = TRUE;
+ FreePool(Instance);
+ } while (TempNewDevicePath != NULL);
+ }
+
+ //
+ // If we get here we fail to find or 'HDDP' not exist, and now we need
+ // to search all devices in the system for a matched partition
+ //
+ EfiBootManagerConnectAll ();
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
+ if (EFI_ERROR (Status)) {
+ BlockIoHandleCount = 0;
+ BlockIoBuffer = NULL;
+ }
+ //
+ // Loop through all the device handles that support the BLOCK_IO Protocol
+ //
+ for (Index = 0; Index < BlockIoHandleCount; Index++) {
+ BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);
+ if (BlockIoDevicePath == NULL) {
+ continue;
+ }
+
+ if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {
+ //
+ // Find the matched partition device path
+ //
+ TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
+ FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
+ FreePool (TempDevicePath);
+
+ if (FullPath != NULL) {
+ BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
+
+ //
+ // Save the matching Device Path so we don't need to do a connect all next time
+ // Failing to save only impacts performance next time expanding the short-form device path
+ //
+ Status = gRT->SetVariable (
+ L"HDDP",
+ &mBmHardDriveBootVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ GetDevicePathSize (CachedDevicePath),
+ CachedDevicePath
+ );
+
+ break;
+ }
+ }
+ }
+
+ if (CachedDevicePath != NULL) {
+ FreePool (CachedDevicePath);
+ }
+ if (BlockIoBuffer != NULL) {
+ FreePool (BlockIoBuffer);
+ }
+ return FullPath;
+}
+
+#ifdef VBOX
+/**
+ * Checks which filename to try loading by inspecting what is existing on the provided
+ * simple filesystem protocol provider.
+ *
+ * This is required to support booting macOS as it stores the efi OS loader in a non standard location
+ * and we have to support both styles without rewriting half of the boot manager library.
+ */
+EFI_STATUS VBoxBmQueryMediaFileNameForSFs(EFI_HANDLE hSFs, CHAR16 **ppwszFileName)
+{
+ EFI_STATUS Status = EFI_SUCCESS;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pSFs = NULL;
+ EFI_FILE_PROTOCOL *pRoot = NULL;
+
+ *ppwszFileName = EFI_REMOVABLE_MEDIA_FILE_NAME;
+
+ Status = gBS->HandleProtocol(hSFs, &gEfiSimpleFileSystemProtocolGuid, (void **)&pSFs);
+ if (!EFI_ERROR(Status))
+ {
+ Status = pSFs->OpenVolume(pSFs, &pRoot);
+ if (!EFI_ERROR(Status))
+ {
+#if 0
+# define VBOX_EFI_APPLE_MEDIA_FILE_NAME L"\\System\\Library\\CoreServices\\boot.efi"
+ EFI_FILE_PROTOCOL *pFile = NULL;
+
+ Status = pRoot->Open(pRoot, &pFile, VBOX_EFI_APPLE_MEDIA_FILE_NAME, EFI_FILE_MODE_READ,
+ EFI_FILE_READ_ONLY | EFI_FILE_HIDDEN | EFI_FILE_SYSTEM);
+ if (!EFI_ERROR(Status))
+ {
+ *ppwszFileName = VBOX_EFI_APPLE_MEDIA_FILE_NAME;
+ pFile->Close(pFile);
+ }
+#else /* Doesn't quite work yet. */
+ VBOX_FS_BLESSED_FILE *Buffer = NULL;
+ UINTN BufferSize = 0;
+
+ Status = pRoot->GetInfo(pRoot, &gVBoxFsBlessedFileInfoGuid, &BufferSize, Buffer);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Buffer = AllocatePool (BufferSize);
+ ASSERT (Buffer != NULL);
+
+ /** @todo We might leak this allocation but it doesn't really matter as it
+ * is of type BootServicesData and will be reclaimed by the OS when it boots.
+ */
+ Status = pRoot->GetInfo(pRoot, &gVBoxFsBlessedFileInfoGuid, &BufferSize, Buffer);
+ if (!EFI_ERROR(Status))
+ {
+ DEBUG ((EFI_D_INFO, "[Bds] VBoxBmQueryMediaFileNameForSFs: Got blessed file info %s\n", &Buffer->BlessedFile[0]));
+ *ppwszFileName = &Buffer->BlessedFile[0];
+ }
+ }
+#endif
+
+ pRoot->Close(pRoot);
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+#endif
+
+/**
+ Expand the media device path which points to a BlockIo or SimpleFileSystem instance
+ by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
+
+ @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandMediaDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ VOID *Buffer;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;
+ UINTN Size;
+ UINTN TempSize;
+ EFI_HANDLE *SimpleFileSystemHandles;
+ UINTN NumberSimpleFileSystemHandles;
+ UINTN Index;
+ BOOLEAN GetNext;
+#ifdef VBOX
+ CHAR16 *pwszFilename = NULL;
+#endif
+
+ GetNext = (BOOLEAN)(FullPath == NULL);
+
+ //
+ // Check whether the device is connected
+ //
+ TempDevicePath = DevicePath;
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
+ if (!EFI_ERROR (Status)) {
+ ASSERT (IsDevicePathEnd (TempDevicePath));
+
+#ifndef VBOX
+ NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
+#else
+ Status = VBoxBmQueryMediaFileNameForSFs(Handle, &pwszFilename);
+ if (!EFI_ERROR(Status))
+ NextFullPath = FileDevicePath (Handle, pwszFilename);
+ else
+ return NULL;
+#endif
+ //
+ // For device path pointing to simple file system, it only expands to one full path.
+ //
+ if (GetNext) {
+ return NextFullPath;
+ } else {
+ FreePool (NextFullPath);
+ return NULL;
+ }
+ }
+
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // For device boot option only pointing to the removable device handle,
+ // should make sure all its children handles (its child partion or media handles)
+ // are created and connected.
+ //
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);
+
+ //
+ // Issue a dummy read to the device to check for media change.
+ // When the removable media is changed, any Block IO read/write will
+ // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is
+ // returned. After the Block IO protocol is reinstalled, subsequent
+ // Block IO read/write will success.
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ Buffer = AllocatePool (BlockIo->Media->BlockSize);
+ if (Buffer != NULL) {
+ BlockIo->ReadBlocks (
+ BlockIo,
+ BlockIo->Media->MediaId,
+ 0,
+ BlockIo->Media->BlockSize,
+ Buffer
+ );
+ FreePool (Buffer);
+ }
+
+ //
+ // Detect the the default boot file from removable Media
+ //
+ NextFullPath = NULL;
+ Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleFileSystemProtocolGuid,
+ NULL,
+ &NumberSimpleFileSystemHandles,
+ &SimpleFileSystemHandles
+ );
+ for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {
+ //
+ // Get the device path size of SimpleFileSystem handle
+ //
+ TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);
+ TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;
+ //
+ // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
+ //
+ if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
+#ifndef VBOX
+ NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
+#else
+ Status = VBoxBmQueryMediaFileNameForSFs(SimpleFileSystemHandles[Index], &pwszFilename);
+ if (!EFI_ERROR(Status))
+ NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], pwszFilename);
+ else
+ return NULL;
+#endif
+ if (GetNext) {
+ break;
+ } else {
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+ FreePool (NextFullPath);
+ NextFullPath = NULL;
+ }
+ }
+ }
+
+ if (SimpleFileSystemHandles != NULL) {
+ FreePool (SimpleFileSystemHandles);
+ }
+
+ return NextFullPath;
+}
+
+/**
+ Check whether Left and Right are the same without matching the specific
+ device path data in IP device path and URI device path node.
+
+ @retval TRUE Left and Right are the same.
+ @retval FALSE Left and Right are the different.
+**/
+BOOLEAN
+BmMatchHttpBootDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *Left,
+ IN EFI_DEVICE_PATH_PROTOCOL *Right
+ )
+{
+ for (; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right)
+ ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right)
+ ) {
+ if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) {
+ if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) {
+ return FALSE;
+ }
+
+ if (DevicePathSubType (Left) == MSG_DNS_DP) {
+ Left = NextDevicePathNode (Left);
+ }
+
+ if (DevicePathSubType (Right) == MSG_DNS_DP) {
+ Right = NextDevicePathNode (Right);
+ }
+
+ if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) &&
+ ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) &&
+ ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP))
+ ) {
+ return FALSE;
+ }
+ }
+ }
+ return (BOOLEAN) (IsDevicePathEnd (Left) && IsDevicePathEnd (Right));
+}
+
+/**
+ Get the file buffer from the file system produced by Load File instance.
+
+ @param LoadFileHandle The handle of LoadFile instance.
+ @param RamDiskHandle Return the RAM Disk handle.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandNetworkFileSystem (
+ IN EFI_HANDLE LoadFileHandle,
+ OUT EFI_HANDLE *RamDiskHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ Handles = NULL;
+ HandleCount = 0;
+ }
+
+ Handle = NULL;
+ for (Index = 0; Index < HandleCount; Index++) {
+ Node = DevicePathFromHandle (Handles[Index]);
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) &&
+ (Handle == LoadFileHandle) &&
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {
+ //
+ // Find the BlockIo instance populated from the LoadFile.
+ //
+ Handle = Handles[Index];
+ break;
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ if (Index == HandleCount) {
+ Handle = NULL;
+ }
+
+ *RamDiskHandle = Handle;
+
+ if (Handle != NULL) {
+ //
+ // Re-use BmExpandMediaDevicePath() to get the full device path of load option.
+ // But assume only one SimpleFileSystem can be found under the BlockIo.
+ //
+ return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ Return the RAM Disk device path created by LoadFile.
+
+ @param FilePath The source file path.
+
+ @return Callee-to-free RAM Disk device path
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetRamDiskDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_HANDLE Handle;
+
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) &&
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)
+ ) {
+
+ //
+ // Construct the device path pointing to RAM Disk
+ //
+ Node = NextDevicePathNode (Node);
+ RamDiskDevicePath = DuplicateDevicePath (FilePath);
+ ASSERT (RamDiskDevicePath != NULL);
+ SetDevicePathEndNode ((VOID *) ((UINTN) RamDiskDevicePath + ((UINTN) Node - (UINTN) FilePath)));
+ return RamDiskDevicePath;
+ }
+
+ return NULL;
+}
+
+/**
+ Return the buffer and buffer size occupied by the RAM Disk.
+
+ @param RamDiskDevicePath RAM Disk device path.
+ @param RamDiskSizeInPages Return RAM Disk size in pages.
+
+ @retval RAM Disk buffer.
+**/
+VOID *
+BmGetRamDiskMemoryInfo (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,
+ OUT UINTN *RamDiskSizeInPages
+ )
+{
+
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ UINT64 StartingAddr;
+ UINT64 EndingAddr;
+
+ ASSERT (RamDiskDevicePath != NULL);
+
+ *RamDiskSizeInPages = 0;
+
+ //
+ // Get the buffer occupied by RAM Disk.
+ //
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);
+ ASSERT_EFI_ERROR (Status);
+ ASSERT ((DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP));
+ StartingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->StartingAddr);
+ EndingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->EndingAddr);
+ *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN) (EndingAddr - StartingAddr + 1));
+ return (VOID *) (UINTN) StartingAddr;
+}
+
+/**
+ Destroy the RAM Disk.
+
+ The destroy operation includes to call RamDisk.Unregister to
+ unregister the RAM DISK from RAM DISK driver, free the memory
+ allocated for the RAM Disk.
+
+ @param RamDiskDevicePath RAM Disk device path.
+**/
+VOID
+BmDestroyRamDisk (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+ )
+{
+ EFI_STATUS Status;
+ VOID *RamDiskBuffer;
+ UINTN RamDiskSizeInPages;
+
+ ASSERT (RamDiskDevicePath != NULL);
+
+ RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);
+
+ //
+ // Destroy RAM Disk.
+ //
+ if (mRamDisk == NULL) {
+ Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *) &mRamDisk);
+ ASSERT_EFI_ERROR (Status);
+ }
+ Status = mRamDisk->Unregister (RamDiskDevicePath);
+ ASSERT_EFI_ERROR (Status);
+ FreePages (RamDiskBuffer, RamDiskSizeInPages);
+}
+
+/**
+ Get the file buffer from the specified Load File instance.
+
+ @param LoadFileHandle The specified Load File instance.
+ @param FilePath The file path which will pass to LoadFile().
+
+ @return The full device path pointing to the load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
+ IN EFI_HANDLE LoadFileHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+ VOID *FileBuffer;
+ EFI_HANDLE RamDiskHandle;
+ UINTN BufferSize;
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;
+
+ Status = gBS->OpenProtocol (
+ LoadFileHandle,
+ &gEfiLoadFileProtocolGuid,
+ (VOID **) &LoadFile,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ FileBuffer = NULL;
+ BufferSize = 0;
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
+ if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {
+ return NULL;
+ }
+
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ //
+ // The load option buffer is directly returned by LoadFile.
+ //
+ return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
+ }
+
+ //
+ // The load option resides in a RAM disk.
+ //
+ FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
+ if (FileBuffer == NULL) {
+ DEBUG_CODE (
+ EFI_DEVICE_PATH *LoadFilePath;
+ CHAR16 *LoadFileText;
+ CHAR16 *FileText;
+
+ LoadFilePath = DevicePathFromHandle (LoadFileHandle);
+ if (LoadFilePath == NULL) {
+ LoadFileText = NULL;
+ } else {
+ LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE);
+ }
+ FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE);
+
+ DEBUG ((
+ DEBUG_ERROR,
+ "%a:%a: failed to allocate reserved pages: "
+ "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n",
+ gEfiCallerBaseName,
+ __FUNCTION__,
+ (UINT64)BufferSize,
+ LoadFileText,
+ FileText
+ ));
+
+ if (FileText != NULL) {
+ FreePool (FileText);
+ }
+ if (LoadFileText != NULL) {
+ FreePool (LoadFileText);
+ }
+ );
+ return NULL;
+ }
+
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
+ if (EFI_ERROR (Status)) {
+ FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
+ return NULL;
+ }
+
+ FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);
+ if (FullPath == NULL) {
+ //
+ // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
+ //
+ BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
+ }
+
+ return FullPath;
+}
+
+/**
+ Return the full device path pointing to the load option.
+
+ FilePath may:
+ 1. Exactly matches to a LoadFile instance.
+ 2. Cannot match to any LoadFile instance. Wide match is required.
+ In either case, the routine may return:
+ 1. A copy of FilePath when FilePath matches to a LoadFile instance and
+ the LoadFile returns a load option buffer.
+ 2. A new device path with IP and URI information updated when wide match
+ happens.
+ 3. A new device path pointing to a load option in RAM disk.
+ In either case, only one full device path is returned for a specified
+ FilePath.
+
+ @param FilePath The media device path pointing to a LoadFile instance.
+
+ @return The load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFiles (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+
+ //
+ // Get file buffer from load file instance.
+ //
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
+ //
+ // When wide match happens, pass full device path to LoadFile (),
+ // otherwise, pass remaining device path to LoadFile ().
+ //
+ FilePath = Node;
+ } else {
+ Handle = NULL;
+ //
+ // Use wide match algorithm to find one when
+ // cannot find a LoadFile instance to exactly match the FilePath
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ Handles = NULL;
+ HandleCount = 0;
+ }
+ for (Index = 0; Index < HandleCount; Index++) {
+ if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) {
+ Handle = Handles[Index];
+ break;
+ }
+ }
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+ }
+
+ if (Handle == NULL) {
+ return NULL;
+ }
+
+ return BmExpandLoadFile (Handle, FilePath);
+}
+
+/**
+ Get the load option by its device path.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath Return the full device path of the load option after
+ short-form device path expanding.
+ Caller is responsible to free it.
+ @param FileSize Return the load option size.
+
+ @return The load option buffer. Caller is responsible to free the memory.
+**/
+VOID *
+EFIAPI
+EfiBootManagerGetLoadOptionBuffer (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
+ OUT UINTN *FileSize
+ )
+{
+ *FullPath = NULL;
+
+ EfiBootManagerConnectDevicePath (FilePath, NULL);
+ return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);
+}
+
+/**
+ Get the next possible full path pointing to the load option.
+ The routine doesn't guarantee the returned full path points to an existing
+ file, and it also doesn't guarantee the existing file is a valid load option.
+ BmGetNextLoadOptionBuffer() guarantees.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ EFI_HANDLE Handle;
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_STATUS Status;
+
+ ASSERT (FilePath != NULL);
+
+ //
+ // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI
+ //
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
+ if (EFI_ERROR (Status)) {
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);
+ }
+
+ if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
+ return BmExpandMediaDevicePath (FilePath, FullPath);
+ }
+
+ //
+ // Expand the short-form device path to full device path
+ //
+ if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) {
+ //
+ // Expand the Harddrive device path
+ //
+ if (FullPath == NULL) {
+ return BmExpandPartitionDevicePath (FilePath);
+ } else {
+ return NULL;
+ }
+ } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {
+ //
+ // Expand the File-path device path
+ //
+ return BmExpandFileDevicePath (FilePath, FullPath);
+ } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (FilePath) == MSG_URI_DP)) {
+ //
+ // Expand the URI device path
+ //
+ return BmExpandUriDevicePath (FilePath, FullPath);
+ } else {
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle);
+ if (EFI_ERROR (Status)) {
+ //
+ // Only expand the USB WWID/Class device path
+ // when FilePath doesn't point to a physical UsbIo controller.
+ // Otherwise, infinite recursion will happen.
+ //
+ for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
+ if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
+ break;
+ }
+ }
+
+ //
+ // Expand the USB WWID/Class device path
+ //
+ if (!IsDevicePathEnd (Node)) {
+ if (FilePath == Node) {
+ //
+ // Boot Option device path starts with USB Class or USB WWID device path.
+ // For Boot Option device path which doesn't begin with the USB Class or
+ // USB WWID device path, it's not needed to connect again here.
+ //
+ BmConnectUsbShortFormDevicePath (FilePath);
+ }
+ return BmExpandUsbDevicePath (FilePath, FullPath, Node);
+ }
+ }
+ }
+
+ //
+ // For the below cases, FilePath only expands to one Full path.
+ // So just handle the case when FullPath == NULL.
+ //
+ if (FullPath != NULL) {
+ return NULL;
+ }
+
+ //
+ // Load option resides in FV.
+ //
+ if (BmIsFvFilePath (FilePath)) {
+ return BmAdjustFvFilePath (FilePath);
+ }
+
+ //
+ // Load option resides in Simple File System.
+ //
+ Node = FilePath;
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
+ if (!EFI_ERROR (Status)) {
+ return DuplicateDevicePath (FilePath);
+ }
+
+ //
+ // Last chance to try: Load option may be loaded through LoadFile.
+ //
+ return BmExpandLoadFiles (FilePath);
+}
+
+/**
+ Check if it's a Device Path pointing to BootManagerMenu.
+
+ @param DevicePath Input device path.
+
+ @retval TRUE The device path is BootManagerMenu File Device Path.
+ @retval FALSE The device path is NOT BootManagerMenu File Device Path.
+**/
+BOOLEAN
+BmIsBootManagerMenuFilePath (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+)
+{
+ EFI_HANDLE FvHandle;
+ VOID *NameGuid;
+ EFI_STATUS Status;
+
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);
+ if (!EFI_ERROR (Status)) {
+ NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);
+ if (NameGuid != NULL) {
+ return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile));
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or
+ StartImage() failure.
+
+ @param[in] ErrorCode An Error Code in the Software Class, DXE Boot
+ Service Driver Subclass. ErrorCode will be used to
+ compose the Value parameter for status code
+ reporting. Must be one of
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED.
+
+ @param[in] FailureStatus The failure status returned by the boot service
+ that should be reported.
+**/
+VOID
+BmReportLoadFailure (
+ IN UINT32 ErrorCode,
+ IN EFI_STATUS FailureStatus
+ )
+{
+ EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData;
+
+ if (!ReportErrorCodeEnabled ()) {
+ return;
+ }
+
+ ASSERT (
+ (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) ||
+ (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
+ );
+
+ ZeroMem (&ExtendedData, sizeof (ExtendedData));
+ ExtendedData.ReturnStatus = FailureStatus;
+
+ REPORT_STATUS_CODE_EX (
+ (EFI_ERROR_CODE | EFI_ERROR_MINOR),
+ (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode),
+ 0,
+ NULL,
+ NULL,
+ &ExtendedData.DataHeader + 1,
+ sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader)
+ );
+}
+
+/**
+ Attempt to boot the EFI boot option. This routine sets L"BootCurent" and
+ also signals the EFI ready to boot event. If the device path for the option
+ starts with a BBS device path a legacy boot is attempted via the registered
+ gLegacyBoot function. Short form device paths are also supported via this
+ rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,
+ MSG_USB_CLASS_DP gets expaned out to find the first device that matches.
+ If the BootOption Device Path fails the removable media boot algorithm
+ is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type
+ is tried per processor type)
+
+ @param BootOption Boot Option to try and boot.
+ On return, BootOption->Status contains the boot status.
+ EFI_SUCCESS BootOption was booted
+ EFI_UNSUPPORTED A BBS device path was found with no valid callback
+ registered via EfiBootManagerInitialize().
+ EFI_NOT_FOUND The BootOption was not found on the system
+ !EFI_SUCCESS BootOption failed with this error status
+
+**/
+VOID
+EFIAPI
+EfiBootManagerBoot (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE ImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
+ UINT16 Uint16;
+ UINTN OptionNumber;
+ UINTN OriginalOptionNumber;
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+ VOID *FileBuffer;
+ UINTN FileSize;
+ EFI_BOOT_LOGO_PROTOCOL *BootLogo;
+ EFI_EVENT LegacyBootEvent;
+
+ if (BootOption == NULL) {
+ return;
+ }
+
+ if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {
+ BootOption->Status = EFI_INVALID_PARAMETER;
+ return;
+ }
+
+ //
+ // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")
+ //
+ OptionNumber = BmFindBootOptionInVariable (BootOption);
+ if (OptionNumber == LoadOptionNumberUnassigned) {
+ Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Save the BootOption->OptionNumber to restore later
+ //
+ OptionNumber = Uint16;
+ OriginalOptionNumber = BootOption->OptionNumber;
+ BootOption->OptionNumber = OptionNumber;
+ Status = EfiBootManagerLoadOptionToVariable (BootOption);
+ BootOption->OptionNumber = OriginalOptionNumber;
+ }
+
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));
+ BootOption->Status = Status;
+ return ;
+ }
+ }
+
+ //
+ // 2. Set BootCurrent
+ //
+ Uint16 = (UINT16) OptionNumber;
+ BmSetVariableAndReportStatusCodeOnError (
+ L"BootCurrent",
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+ sizeof (UINT16),
+ &Uint16
+ );
+
+ //
+ // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute
+ // the boot option.
+ //
+ if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {
+ DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));
+ BmStopHotkeyService (NULL, NULL);
+ } else {
+ EfiSignalEventReadyToBoot();
+ //
+ // Report Status Code to indicate ReadyToBoot was signalled
+ //
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
+ //
+ // 4. Repair system through DriverHealth protocol
+ //
+ BmRepairAllControllers (0);
+ }
+
+ PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+
+ //
+ // 5. Adjust the different type memory page number just before booting
+ // and save the updated info into the variable for next boot to use
+ //
+ BmSetMemoryTypeInformationVariable (
+ (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)
+ );
+
+ //
+ // 6. Load EFI boot option to ImageHandle
+ //
+ DEBUG_CODE_BEGIN ();
+ if (BootOption->Description == NULL) {
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));
+ } else {
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));
+ }
+ DEBUG_CODE_END ();
+
+ ImageHandle = NULL;
+ RamDiskDevicePath = NULL;
+ if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
+ Status = EFI_NOT_FOUND;
+ FilePath = NULL;
+ EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);
+ FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);
+ if (FileBuffer != NULL) {
+ RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
+
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
+ Status = gBS->LoadImage (
+ TRUE,
+ gImageHandle,
+ FilePath,
+ FileBuffer,
+ FileSize,
+ &ImageHandle
+ );
+ }
+ if (FileBuffer != NULL) {
+ FreePool (FileBuffer);
+ }
+ if (FilePath != NULL) {
+ FreePool (FilePath);
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
+ // If the caller doesn't have the option to defer the execution of an image, we should
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
+ //
+ if (Status == EFI_SECURITY_VIOLATION) {
+ gBS->UnloadImage (ImageHandle);
+ }
+ //
+ // Destroy the RAM disk
+ //
+ if (RamDiskDevicePath != NULL) {
+ BmDestroyRamDisk (RamDiskDevicePath);
+ FreePool (RamDiskDevicePath);
+ }
+ //
+ // Report Status Code with the failure status to indicate that the failure to load boot option
+ //
+ BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);
+ BootOption->Status = Status;
+ return;
+ }
+ }
+
+ //
+ // Check to see if we should legacy BOOT. If yes then do the legacy boot
+ // Write boot to OS performance data for Legacy boot
+ //
+ if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
+ if (mBmLegacyBoot != NULL) {
+ //
+ // Write boot to OS performance data for legacy boot.
+ //
+ PERF_CODE (
+ //
+ // Create an event to be signalled when Legacy Boot occurs to write performance data.
+ //
+ Status = EfiCreateEventLegacyBootEx(
+ TPL_NOTIFY,
+ BmEndOfBdsPerfCode,
+ NULL,
+ &LegacyBootEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+ );
+
+ mBmLegacyBoot (BootOption);
+ } else {
+ BootOption->Status = EFI_UNSUPPORTED;
+ }
+
+ PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+ return;
+ }
+
+ //
+ // Provide the image with its load options
+ //
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ if (!BmIsAutoCreateBootOption (BootOption)) {
+ ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;
+ ImageInfo->LoadOptions = BootOption->OptionalData;
+ }
+
+ //
+ // Clean to NULL because the image is loaded directly from the firmwares boot manager.
+ //
+ ImageInfo->ParentHandle = NULL;
+
+ //
+ // Before calling the image, enable the Watchdog Timer for 5 minutes period
+ //
+ gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
+
+ //
+ // Write boot to OS performance data for UEFI boot
+ //
+ PERF_CODE (
+ BmEndOfBdsPerfCode (NULL, NULL);
+ );
+
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
+
+ Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
+ BootOption->Status = Status;
+
+ //
+ // Destroy the RAM disk
+ //
+ if (RamDiskDevicePath != NULL) {
+ BmDestroyRamDisk (RamDiskDevicePath);
+ FreePool (RamDiskDevicePath);
+ }
+
+ if (EFI_ERROR (Status)) {
+ //
+ // Report Status Code with the failure status to indicate that boot failure
+ //
+ BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);
+ }
+ PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
+
+
+ //
+ // Clear the Watchdog Timer after the image returns
+ //
+ gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
+
+ //
+ // Set Logo status invalid after trying one boot option
+ //
+ BootLogo = NULL;
+ Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
+ if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
+ Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ //
+ // Clear Boot Current
+ //
+ Status = gRT->SetVariable (
+ L"BootCurrent",
+ &gEfiGlobalVariableGuid,
+ 0,
+ 0,
+ NULL
+ );
+ //
+ // Deleting variable with current variable implementation shouldn't fail.
+ // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,
+ // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.
+ //
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
+}
+
+/**
+ Check whether there is a instance in BlockIoDevicePath, which contain multi device path
+ instances, has the same partition node with HardDriveDevicePath device path
+
+ @param BlockIoDevicePath Multi device path instances which need to check
+ @param HardDriveDevicePath A device path which starts with a hard drive media
+ device path.
+
+ @retval TRUE There is a matched device path instance.
+ @retval FALSE There is no matched device path instance.
+
+**/
+BOOLEAN
+BmMatchPartitionDevicePathNode (
+ IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
+ IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
+ )
+{
+ HARDDRIVE_DEVICE_PATH *Node;
+
+ if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
+ return FALSE;
+ }
+
+ //
+ // Match all the partition device path nodes including the nested partition nodes
+ //
+ while (!IsDevicePathEnd (BlockIoDevicePath)) {
+ if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&
+ (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)
+ ) {
+ //
+ // See if the harddrive device path in blockio matches the orig Hard Drive Node
+ //
+ Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;
+
+ //
+ // Match Signature and PartitionNumber.
+ // Unused bytes in Signature are initiaized with zeros.
+ //
+ if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&
+ (Node->MBRType == HardDriveDevicePath->MBRType) &&
+ (Node->SignatureType == HardDriveDevicePath->SignatureType) &&
+ (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) {
+ return TRUE;
+ }
+ }
+
+ BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);
+ }
+
+ return FALSE;
+}
+
+/**
+ Emuerate all possible bootable medias in the following order:
+ 1. Removable BlockIo - The boot option only points to the removable media
+ device, like USB key, DVD, Floppy etc.
+ 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,
+ like HardDisk.
+ 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting
+ SimpleFileSystem Protocol, but not supporting BlockIo
+ protocol.
+ 4. LoadFile - The boot option points to the media supporting
+ LoadFile protocol.
+ Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior
+
+ @param BootOptionCount Return the boot option count which has been found.
+
+ @retval Pointer to the boot option array.
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+BmEnumerateBootOptions (
+ UINTN *BootOptionCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN HandleCount;
+ EFI_HANDLE *Handles;
+ EFI_BLOCK_IO_PROTOCOL *BlkIo;
+ UINTN Removable;
+ UINTN Index;
+ CHAR16 *Description;
+
+ ASSERT (BootOptionCount != NULL);
+
+ *BootOptionCount = 0;
+ BootOptions = NULL;
+
+ //
+ // Parse removable block io followed by fixed block io
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiBlockIoProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+
+ for (Removable = 0; Removable < 2; Removable++) {
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlkIo
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Skip the logical partitions
+ //
+ if (BlkIo->Media->LogicalPartition) {
+ continue;
+ }
+
+ //
+ // Skip the fixed block io then the removable block io
+ //
+ if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {
+ continue;
+ }
+
+ Description = BmGetBootDescription (Handles[Index]);
+ BootOptions = ReallocatePool (
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+ BootOptions
+ );
+ ASSERT (BootOptions != NULL);
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &BootOptions[(*BootOptionCount)++],
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_ACTIVE,
+ Description,
+ DevicePathFromHandle (Handles[Index]),
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ FreePool (Description);
+ }
+ }
+
+ if (HandleCount != 0) {
+ FreePool (Handles);
+ }
+
+ //
+ // Parse simple file system not based on block io
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleFileSystemProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiBlockIoProtocolGuid,
+ (VOID **) &BlkIo
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Skip if the file system handle supports a BlkIo protocol, which we've handled in above
+ //
+ continue;
+ }
+ Description = BmGetBootDescription (Handles[Index]);
+ BootOptions = ReallocatePool (
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+ BootOptions
+ );
+ ASSERT (BootOptions != NULL);
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &BootOptions[(*BootOptionCount)++],
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_ACTIVE,
+ Description,
+ DevicePathFromHandle (Handles[Index]),
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (Description);
+ }
+
+ if (HandleCount != 0) {
+ FreePool (Handles);
+ }
+
+ //
+ // Parse load file protocol
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ for (Index = 0; Index < HandleCount; Index++) {
+ //
+ // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu().
+ //
+ if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {
+ continue;
+ }
+
+ Description = BmGetBootDescription (Handles[Index]);
+ BootOptions = ReallocatePool (
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
+ BootOptions
+ );
+ ASSERT (BootOptions != NULL);
+
+ Status = EfiBootManagerInitializeLoadOption (
+ &BootOptions[(*BootOptionCount)++],
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_ACTIVE,
+ Description,
+ DevicePathFromHandle (Handles[Index]),
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (Description);
+ }
+
+ if (HandleCount != 0) {
+ FreePool (Handles);
+ }
+
+ BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);
+ return BootOptions;
+}
+
+/**
+ The function enumerates all boot options, creates them and registers them in the BootOrder variable.
+**/
+VOID
+EFIAPI
+EfiBootManagerRefreshAllBootOption (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;
+ UINTN NvBootOptionCount;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+ EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions;
+ UINTN UpdatedBootOptionCount;
+ UINTN Index;
+ EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager;
+
+ //
+ // Optionally refresh the legacy boot option
+ //
+ if (mBmRefreshLegacyBootOption != NULL) {
+ mBmRefreshLegacyBootOption ();
+ }
+
+ BootOptions = BmEnumerateBootOptions (&BootOptionCount);
+
+ //
+ // Mark the boot option as added by BDS by setting OptionalData to a special GUID
+ //
+ for (Index = 0; Index < BootOptionCount; Index++) {
+ BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);
+ BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);
+ }
+
+ //
+ // Locate Platform Boot Options Protocol
+ //
+ Status = gBS->LocateProtocol (&gEdkiiPlatformBootManagerProtocolGuid,
+ NULL,
+ (VOID **)&PlatformBootManager);
+ if (!EFI_ERROR (Status)) {
+ //
+ // If found, call platform specific refresh to all auto enumerated and NV
+ // boot options.
+ //
+ Status = PlatformBootManager->RefreshAllBootOptions ((CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions,
+ (CONST UINTN)BootOptionCount,
+ &UpdatedBootOptions,
+ &UpdatedBootOptionCount);
+ if (!EFI_ERROR (Status)) {
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+ BootOptions = UpdatedBootOptions;
+ BootOptionCount = UpdatedBootOptionCount;
+ }
+ }
+
+ NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);
+
+ //
+ // Remove invalid EFI boot options from NV
+ //
+ for (Index = 0; Index < NvBootOptionCount; Index++) {
+ if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||
+ (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)
+ ) && BmIsAutoCreateBootOption (&NvBootOptions[Index])
+ ) {
+ //
+ // Only check those added by BDS
+ // so that the boot options added by end-user or OS installer won't be deleted
+ //
+ if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) {
+ Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);
+ //
+ // Deleting variable with current variable implementation shouldn't fail.
+ //
+ ASSERT_EFI_ERROR (Status);
+ }
+ }
+ }
+
+ //
+ // Add new EFI boot options to NV
+ //
+ for (Index = 0; Index < BootOptionCount; Index++) {
+ if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) {
+ EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);
+ //
+ // Try best to add the boot options so continue upon failure.
+ //
+ }
+ }
+
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+ EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);
+}
+
+/**
+ This function is called to get or create the boot option for the Boot Manager Menu.
+
+ The Boot Manager Menu is shown after successfully booting a boot option.
+ This function will first try to search the BootManagerMenuFile is in the same FV as
+ the module links to this library. If fails, it will search in all FVs.
+
+ @param BootOption Return the boot option of the Boot Manager Menu
+
+ @retval EFI_SUCCESS Successfully register the Boot Manager Menu.
+ @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.
+ @retval others Return status of gRT->SetVariable (). BootOption still points
+ to the Boot Manager Menu even the Status is not EFI_SUCCESS
+ and EFI_NOT_FOUND.
+**/
+EFI_STATUS
+BmRegisterBootManagerMenu (
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Description;
+ UINTN DescriptionLength;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
+ MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
+ UINTN HandleCount;
+ EFI_HANDLE *Handles;
+ UINTN Index;
+ VOID *Data;
+ UINTN DataSize;
+
+ DevicePath = NULL;
+ Description = NULL;
+ //
+ // Try to find BootManagerMenu from LoadFile protocol
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ for (Index = 0; Index < HandleCount; Index++) {
+ if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {
+ DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));
+ Description = BmGetBootDescription (Handles[Index]);
+ break;
+ }
+ }
+ if (HandleCount != 0) {
+ FreePool (Handles);
+ }
+
+ if (DevicePath == NULL) {
+ Data = NULL;
+ Status = GetSectionFromAnyFv (
+ PcdGetPtr (PcdBootManagerMenuFile),
+ EFI_SECTION_PE32,
+ 0,
+ (VOID **) &Data,
+ &DataSize
+ );
+ if (Data != NULL) {
+ FreePool (Data);
+ }
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Get BootManagerMenu application's description from EFI User Interface Section.
+ //
+ Status = GetSectionFromAnyFv (
+ PcdGetPtr (PcdBootManagerMenuFile),
+ EFI_SECTION_USER_INTERFACE,
+ 0,
+ (VOID **) &Description,
+ &DescriptionLength
+ );
+ if (EFI_ERROR (Status)) {
+ Description = NULL;
+ }
+
+ EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));
+ Status = gBS->HandleProtocol (
+ gImageHandle,
+ &gEfiLoadedImageProtocolGuid,
+ (VOID **) &LoadedImage
+ );
+ ASSERT_EFI_ERROR (Status);
+ DevicePath = AppendDevicePathNode (
+ DevicePathFromHandle (LoadedImage->DeviceHandle),
+ (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
+ );
+ ASSERT (DevicePath != NULL);
+ }
+
+ Status = EfiBootManagerInitializeLoadOption (
+ BootOption,
+ LoadOptionNumberUnassigned,
+ LoadOptionTypeBoot,
+ LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,
+ (Description != NULL) ? Description : L"Boot Manager Menu",
+ DevicePath,
+ NULL,
+ 0
+ );
+ ASSERT_EFI_ERROR (Status);
+ FreePool (DevicePath);
+ if (Description != NULL) {
+ FreePool (Description);
+ }
+
+ DEBUG_CODE (
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN BootOptionCount;
+
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+ ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+ );
+
+ return EfiBootManagerAddLoadOptionVariable (BootOption, (UINTN) -1);
+}
+
+/**
+ Return the boot option corresponding to the Boot Manager Menu.
+ It may automatically create one if the boot option hasn't been created yet.
+
+ @param BootOption Return the Boot Manager Menu.
+
+ @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.
+ @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.
+ @retval others Return status of gRT->SetVariable (). BootOption still points
+ to the Boot Manager Menu even the Status is not EFI_SUCCESS
+ and EFI_NOT_FOUND.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerGetBootManagerMenu (
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
+ )
+{
+ EFI_STATUS Status;
+ UINTN BootOptionCount;
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
+ UINTN Index;
+
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
+
+ for (Index = 0; Index < BootOptionCount; Index++) {
+ if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) {
+ Status = EfiBootManagerInitializeLoadOption (
+ BootOption,
+ BootOptions[Index].OptionNumber,
+ BootOptions[Index].OptionType,
+ BootOptions[Index].Attributes,
+ BootOptions[Index].Description,
+ BootOptions[Index].FilePath,
+ BootOptions[Index].OptionalData,
+ BootOptions[Index].OptionalDataSize
+ );
+ ASSERT_EFI_ERROR (Status);
+ break;
+ }
+ }
+
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
+
+ //
+ // Automatically create the Boot#### for Boot Manager Menu when not found.
+ //
+ if (Index == BootOptionCount) {
+ return BmRegisterBootManagerMenu (BootOption);
+ } else {
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Get the next possible full path pointing to the load option.
+ The routine doesn't guarantee the returned full path points to an existing
+ file, and it also doesn't guarantee the existing file is a valid load option.
+ BmGetNextLoadOptionBuffer() guarantees.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiBootManagerGetNextLoadOptionDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ )
+{
+ return BmGetNextLoadOptionDevicePath(FilePath, FullPath);
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
new file mode 100644
index 00000000..34d1f5a0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c
@@ -0,0 +1,875 @@
+/** @file
+ Library functions which relate with boot option description.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+#define VENDOR_IDENTIFICATION_OFFSET 3
+#define VENDOR_IDENTIFICATION_LENGTH 8
+#define PRODUCT_IDENTIFICATION_OFFSET 11
+#define PRODUCT_IDENTIFICATION_LENGTH 16
+
+CONST UINT16 mBmUsbLangId = 0x0409; // English
+CHAR16 mBmUefiPrefix[] = L"UEFI ";
+
+LIST_ENTRY mPlatformBootDescriptionHandlers = INITIALIZE_LIST_HEAD_VARIABLE (mPlatformBootDescriptionHandlers);
+
+/**
+ For a bootable Device path, return its boot type.
+
+ @param DevicePath The bootable device Path to check
+
+ @retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node
+ which HID is floppy device.
+ @retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
+ and its last device path node's subtype is MSG_ATAPI_DP.
+ @retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
+ and its last device path node's subtype is MSG_SATA_DP.
+ @retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
+ and its last device path node's subtype is MSG_SCSI_DP.
+ @retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
+ and its last device path node's subtype is MSG_USB_DP.
+ @retval BmMiscBoot If tiven device path doesn't match the above condition.
+
+**/
+BM_BOOT_TYPE
+BmDevicePathType (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Node;
+ EFI_DEVICE_PATH_PROTOCOL *NextNode;
+
+ ASSERT (DevicePath != NULL);
+
+ for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
+ switch (DevicePathType (Node)) {
+
+ case ACPI_DEVICE_PATH:
+ if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
+ return BmAcpiFloppyBoot;
+ }
+ break;
+
+ case HARDWARE_DEVICE_PATH:
+ if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
+ return BmHardwareDeviceBoot;
+ }
+ break;
+
+ case MESSAGING_DEVICE_PATH:
+ //
+ // Skip LUN device node
+ //
+ NextNode = Node;
+ do {
+ NextNode = NextDevicePathNode (NextNode);
+ } while (
+ (DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
+ );
+
+ //
+ // If the device path not only point to driver device, it is not a messaging device path,
+ //
+ if (!IsDevicePathEndType (NextNode)) {
+ continue;
+ }
+
+ switch (DevicePathSubType (Node)) {
+ case MSG_ATAPI_DP:
+ return BmMessageAtapiBoot;
+ break;
+
+ case MSG_SATA_DP:
+ return BmMessageSataBoot;
+ break;
+
+ case MSG_USB_DP:
+ return BmMessageUsbBoot;
+ break;
+
+ case MSG_SCSI_DP:
+ return BmMessageScsiBoot;
+ break;
+ }
+ }
+ }
+
+ return BmMiscBoot;
+}
+
+/**
+ Eliminate the extra spaces in the Str to one space.
+
+ @param Str Input string info.
+**/
+VOID
+BmEliminateExtraSpaces (
+ IN CHAR16 *Str
+ )
+{
+ UINTN Index;
+ UINTN ActualIndex;
+
+ for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
+ if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
+ Str[ActualIndex++] = Str[Index];
+ }
+ }
+ Str[ActualIndex] = L'\0';
+}
+
+/**
+ Try to get the controller's ATA/ATAPI description.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetDescriptionFromDiskInfo (
+ IN EFI_HANDLE Handle
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_DISK_INFO_PROTOCOL *DiskInfo;
+ UINT32 BufferSize;
+ EFI_ATAPI_IDENTIFY_DATA IdentifyData;
+ EFI_SCSI_INQUIRY_DATA InquiryData;
+ CHAR16 *Description;
+ UINTN Length;
+ CONST UINTN ModelNameLength = 40;
+ CONST UINTN SerialNumberLength = 20;
+ CHAR8 *StrPtr;
+ UINT8 Temp;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+
+ Description = NULL;
+
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiDiskInfoProtocolGuid,
+ (VOID **) &DiskInfo
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
+ CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
+ BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
+ Status = DiskInfo->Identify (
+ DiskInfo,
+ &IdentifyData,
+ &BufferSize
+ );
+ if (!EFI_ERROR (Status)) {
+ Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
+ ASSERT (Description != NULL);
+ for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
+ Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];
+ Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
+ }
+
+ Length = Index;
+ Description[Length++] = L' ';
+
+ for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
+ Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];
+ Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
+ }
+ Length += Index;
+ Description[Length++] = L'\0';
+ ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
+
+ BmEliminateExtraSpaces (Description);
+ }
+ } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
+ BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
+ Status = DiskInfo->Inquiry (
+ DiskInfo,
+ &InquiryData,
+ &BufferSize
+ );
+ if (!EFI_ERROR (Status)) {
+ Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
+ ASSERT (Description != NULL);
+
+ //
+ // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
+ // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
+ // Here combine the vendor identification and product identification to the description.
+ //
+ StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
+ Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
+ StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
+ AsciiStrToUnicodeStrS (StrPtr, Description, VENDOR_IDENTIFICATION_LENGTH + 1);
+ StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
+
+ //
+ // Add one space at the middle of vendor information and product information.
+ //
+ Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
+
+ StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
+ StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
+ AsciiStrToUnicodeStrS (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1, PRODUCT_IDENTIFICATION_LENGTH + 1);
+
+ BmEliminateExtraSpaces (Description);
+ }
+ } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoSdMmcInterfaceGuid)) {
+ DevicePath = DevicePathFromHandle (Handle);
+ if (DevicePath == NULL) {
+ return NULL;
+ }
+
+ while (!IsDevicePathEnd (DevicePath) && (DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH)) {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+ if (IsDevicePathEnd (DevicePath)) {
+ return NULL;
+ }
+
+ if (DevicePathSubType (DevicePath) == MSG_SD_DP) {
+ Description = L"SD Device";
+ } else if (DevicePathSubType (DevicePath) == MSG_EMMC_DP) {
+ Description = L"eMMC Device";
+ } else {
+ return NULL;
+ }
+
+ Description = AllocateCopyPool (StrSize (Description), Description);
+ }
+
+ return Description;
+}
+
+/**
+ Try to get the controller's USB description.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetUsbDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_USB_IO_PROTOCOL *UsbIo;
+ CHAR16 NullChar;
+ CHAR16 *Manufacturer;
+ CHAR16 *Product;
+ CHAR16 *SerialNumber;
+ CHAR16 *Description;
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;
+ UINTN DescMaxSize;
+
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiUsbIoProtocolGuid,
+ (VOID **) &UsbIo
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ NullChar = L'\0';
+
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ mBmUsbLangId,
+ DevDesc.StrManufacturer,
+ &Manufacturer
+ );
+ if (EFI_ERROR (Status)) {
+ Manufacturer = &NullChar;
+ }
+
+ Status = UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ mBmUsbLangId,
+ DevDesc.StrProduct,
+ &Product
+ );
+ if (EFI_ERROR (Status)) {
+ Product = &NullChar;
+ }
+
+ Status = UsbIo->UsbGetStringDescriptor (
+ UsbIo,
+ mBmUsbLangId,
+ DevDesc.StrSerialNumber,
+ &SerialNumber
+ );
+ if (EFI_ERROR (Status)) {
+ SerialNumber = &NullChar;
+ }
+
+ if ((Manufacturer == &NullChar) &&
+ (Product == &NullChar) &&
+ (SerialNumber == &NullChar)
+ ) {
+ return NULL;
+ }
+
+ DescMaxSize = StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber);
+ Description = AllocateZeroPool (DescMaxSize);
+ ASSERT (Description != NULL);
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), Manufacturer);
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
+
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), Product);
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), L" ");
+
+ StrCatS (Description, DescMaxSize/sizeof(CHAR16), SerialNumber);
+
+ if (Manufacturer != &NullChar) {
+ FreePool (Manufacturer);
+ }
+ if (Product != &NullChar) {
+ FreePool (Product);
+ }
+ if (SerialNumber != &NullChar) {
+ FreePool (SerialNumber);
+ }
+
+ BmEliminateExtraSpaces (Description);
+
+ return Description;
+}
+
+/**
+ Return the description for network boot device.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetNetworkDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ MAC_ADDR_DEVICE_PATH *Mac;
+ VLAN_DEVICE_PATH *Vlan;
+ EFI_DEVICE_PATH_PROTOCOL *Ip;
+ EFI_DEVICE_PATH_PROTOCOL *Uri;
+ CHAR16 *Description;
+ UINTN DescriptionSize;
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiLoadFileProtocolGuid,
+ NULL,
+ gImageHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = gBS->OpenProtocol (
+ Handle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ gImageHandle,
+ Handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status) || (DevicePath == NULL)) {
+ return NULL;
+ }
+
+ //
+ // The PXE device path is like:
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)
+ //
+ // The HTTP device path is like:
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv4(...)[/Dns(...)]/Uri(...)
+ // ....../Mac(...)[/Vlan(...)][/Wi-Fi(...)]/IPv6(...)[/Dns(...)]/Uri(...)
+ //
+ while (!IsDevicePathEnd (DevicePath) &&
+ ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (DevicePath) != MSG_MAC_ADDR_DP))
+ ) {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ if (IsDevicePathEnd (DevicePath)) {
+ return NULL;
+ }
+
+ Mac = (MAC_ADDR_DEVICE_PATH *) DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+
+ //
+ // Locate the optional Vlan node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_VLAN_DP)
+ ) {
+ Vlan = (VLAN_DEVICE_PATH *) DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Vlan = NULL;
+ }
+
+ //
+ // Skip the optional Wi-Fi node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_WIFI_DP)
+ ) {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ //
+ // Locate the IP node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (DevicePath) == MSG_IPv4_DP) ||
+ (DevicePathSubType (DevicePath) == MSG_IPv6_DP))
+ ) {
+ Ip = DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Ip = NULL;
+ }
+
+ //
+ // Skip the optional DNS node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_DNS_DP)
+ ) {
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+
+ //
+ // Locate the URI node
+ //
+ if ((DevicePathType (DevicePath) == MESSAGING_DEVICE_PATH) &&
+ (DevicePathSubType (DevicePath) == MSG_URI_DP)
+ ) {
+ Uri = DevicePath;
+ DevicePath = NextDevicePathNode (DevicePath);
+ } else {
+ Uri = NULL;
+ }
+
+ //
+ // Build description like below:
+ // "PXEv6 (MAC:112233445566 VLAN1)"
+ // "HTTPv4 (MAC:112233445566)"
+ //
+ DescriptionSize = sizeof (L"HTTPv6 (MAC:112233445566 VLAN65535)");
+ Description = AllocatePool (DescriptionSize);
+ ASSERT (Description != NULL);
+ UnicodeSPrint (
+ Description, DescriptionSize,
+ (Vlan == NULL) ?
+ L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x)" :
+ L"%sv%d (MAC:%02x%02x%02x%02x%02x%02x VLAN%d)",
+ (Uri == NULL) ? L"PXE" : L"HTTP",
+ ((Ip == NULL) || (DevicePathSubType (Ip) == MSG_IPv4_DP)) ? 4 : 6,
+ Mac->MacAddress.Addr[0], Mac->MacAddress.Addr[1], Mac->MacAddress.Addr[2],
+ Mac->MacAddress.Addr[3], Mac->MacAddress.Addr[4], Mac->MacAddress.Addr[5],
+ (Vlan == NULL) ? 0 : Vlan->VlanId
+ );
+ return Description;
+}
+
+/**
+ Return the boot description for LoadFile
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetLoadFileDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
+ CHAR16 *Description;
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;
+
+ Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **)&LoadFile);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ //
+ // Get the file name
+ //
+ Description = NULL;
+ Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **)&FilePath);
+ if (!EFI_ERROR (Status)) {
+ DevicePathNode = FilePath;
+ while (!IsDevicePathEnd (DevicePathNode)) {
+ if (DevicePathNode->Type == MEDIA_DEVICE_PATH && DevicePathNode->SubType == MEDIA_FILEPATH_DP) {
+ Description = (CHAR16 *)(DevicePathNode + 1);
+ break;
+ }
+ DevicePathNode = NextDevicePathNode (DevicePathNode);
+ }
+ }
+
+ if (Description != NULL) {
+ return AllocateCopyPool (StrSize (Description), Description);
+ }
+
+ return NULL;
+}
+
+/**
+ Return the boot description for NVME boot device.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetNvmeDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ EFI_NVM_EXPRESS_PASS_THRU_PROTOCOL *NvmePassthru;
+ EFI_DEV_PATH_PTR DevicePath;
+ EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET CommandPacket;
+ EFI_NVM_EXPRESS_COMMAND Command;
+ EFI_NVM_EXPRESS_COMPLETION Completion;
+ NVME_ADMIN_CONTROLLER_DATA ControllerData;
+ CHAR16 *Description;
+ CHAR16 *Char;
+ UINTN Index;
+
+ Status = gBS->HandleProtocol (Handle, &gEfiDevicePathProtocolGuid, (VOID **) &DevicePath.DevPath);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Status = gBS->LocateDevicePath (&gEfiNvmExpressPassThruProtocolGuid, &DevicePath.DevPath, &Handle);
+ if (EFI_ERROR (Status) ||
+ (DevicePathType (DevicePath.DevPath) != MESSAGING_DEVICE_PATH) ||
+ (DevicePathSubType (DevicePath.DevPath) != MSG_NVME_NAMESPACE_DP)) {
+ //
+ // Do not return description when the Handle is not a child of NVME controller.
+ //
+ return NULL;
+ }
+
+ //
+ // Send ADMIN_IDENTIFY command to NVME controller to get the model and serial number.
+ //
+ Status = gBS->HandleProtocol (Handle, &gEfiNvmExpressPassThruProtocolGuid, (VOID **) &NvmePassthru);
+ ASSERT_EFI_ERROR (Status);
+
+ ZeroMem (&CommandPacket, sizeof(EFI_NVM_EXPRESS_PASS_THRU_COMMAND_PACKET));
+ ZeroMem (&Command, sizeof(EFI_NVM_EXPRESS_COMMAND));
+ ZeroMem (&Completion, sizeof(EFI_NVM_EXPRESS_COMPLETION));
+
+ Command.Cdw0.Opcode = NVME_ADMIN_IDENTIFY_CMD;
+ //
+ // According to Nvm Express 1.1 spec Figure 38, When not used, the field shall be cleared to 0h.
+ // For the Identify command, the Namespace Identifier is only used for the Namespace data structure.
+ //
+ Command.Nsid = 0;
+ CommandPacket.NvmeCmd = &Command;
+ CommandPacket.NvmeCompletion = &Completion;
+ CommandPacket.TransferBuffer = &ControllerData;
+ CommandPacket.TransferLength = sizeof (ControllerData);
+ CommandPacket.CommandTimeout = EFI_TIMER_PERIOD_SECONDS (5);
+ CommandPacket.QueueType = NVME_ADMIN_QUEUE;
+ //
+ // Set bit 0 (Cns bit) to 1 to identify a controller
+ //
+ Command.Cdw10 = 1;
+ Command.Flags = CDW10_VALID;
+
+ Status = NvmePassthru->PassThru (
+ NvmePassthru,
+ 0,
+ &CommandPacket,
+ NULL
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ Description = AllocateZeroPool (
+ (ARRAY_SIZE (ControllerData.Mn) + 1
+ + ARRAY_SIZE (ControllerData.Sn) + 1
+ + MAXIMUM_VALUE_CHARACTERS + 1
+ ) * sizeof (CHAR16));
+ if (Description != NULL) {
+ Char = Description;
+ for (Index = 0; Index < ARRAY_SIZE (ControllerData.Mn); Index++) {
+ *(Char++) = (CHAR16) ControllerData.Mn[Index];
+ }
+ *(Char++) = L' ';
+ for (Index = 0; Index < ARRAY_SIZE (ControllerData.Sn); Index++) {
+ *(Char++) = (CHAR16) ControllerData.Sn[Index];
+ }
+ *(Char++) = L' ';
+ UnicodeValueToStringS (
+ Char, sizeof (CHAR16) * (MAXIMUM_VALUE_CHARACTERS + 1),
+ 0, DevicePath.NvmeNamespace->NamespaceId, 0
+ );
+ BmEliminateExtraSpaces (Description);
+ }
+
+ return Description;
+}
+
+/**
+ Return the boot description for the controller based on the type.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetMiscDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Description;
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;
+
+ switch (BmDevicePathType (DevicePathFromHandle (Handle))) {
+ case BmAcpiFloppyBoot:
+ Description = L"Floppy";
+ break;
+
+ case BmMessageAtapiBoot:
+ case BmMessageSataBoot:
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+ ASSERT_EFI_ERROR (Status);
+ //
+ // Assume a removable SATA device should be the DVD/CD device
+ //
+ Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
+ break;
+
+ case BmMessageUsbBoot:
+ Description = L"USB Device";
+ break;
+
+ case BmMessageScsiBoot:
+ Description = L"SCSI Device";
+ break;
+
+ case BmHardwareDeviceBoot:
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
+ if (!EFI_ERROR (Status)) {
+ Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
+ } else {
+ Description = L"Misc Device";
+ }
+ break;
+
+ default:
+ Status = gBS->HandleProtocol (Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **) &Fs);
+ if (!EFI_ERROR (Status)) {
+ Description = L"Non-Block Boot Device";
+ } else {
+ Description = L"Misc Device";
+ }
+ break;
+ }
+
+ return AllocateCopyPool (StrSize (Description), Description);
+}
+
+/**
+ Register the platform provided boot description handler.
+
+ @param Handler The platform provided boot description handler
+
+ @retval EFI_SUCCESS The handler was registered successfully.
+ @retval EFI_ALREADY_STARTED The handler was already registered.
+ @retval EFI_OUT_OF_RESOURCES There is not enough resource to perform the registration.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerRegisterBootDescriptionHandler (
+ IN EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler
+ )
+{
+ LIST_ENTRY *Link;
+ BM_BOOT_DESCRIPTION_ENTRY *Entry;
+
+ for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
+ ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
+ ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
+ ) {
+ Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
+ if (Entry->Handler == Handler) {
+ return EFI_ALREADY_STARTED;
+ }
+ }
+
+ Entry = AllocatePool (sizeof (BM_BOOT_DESCRIPTION_ENTRY));
+ if (Entry == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ Entry->Signature = BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE;
+ Entry->Handler = Handler;
+ InsertTailList (&mPlatformBootDescriptionHandlers, &Entry->Link);
+ return EFI_SUCCESS;
+}
+
+BM_GET_BOOT_DESCRIPTION mBmBootDescriptionHandlers[] = {
+ BmGetUsbDescription,
+ BmGetDescriptionFromDiskInfo,
+ BmGetNetworkDescription,
+ BmGetLoadFileDescription,
+ BmGetNvmeDescription,
+ BmGetMiscDescription
+};
+
+/**
+ Return the boot description for the controller.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetBootDescription (
+ IN EFI_HANDLE Handle
+ )
+{
+ LIST_ENTRY *Link;
+ BM_BOOT_DESCRIPTION_ENTRY *Entry;
+ CHAR16 *Description;
+ CHAR16 *DefaultDescription;
+ CHAR16 *Temp;
+ UINTN Index;
+
+ //
+ // Firstly get the default boot description
+ //
+ DefaultDescription = NULL;
+ for (Index = 0; Index < ARRAY_SIZE (mBmBootDescriptionHandlers); Index++) {
+ DefaultDescription = mBmBootDescriptionHandlers[Index] (Handle);
+ if (DefaultDescription != NULL) {
+ //
+ // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
+ // ONLY for core provided boot description handler.
+ //
+ Temp = AllocatePool (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix));
+ ASSERT (Temp != NULL);
+ StrCpyS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), mBmUefiPrefix);
+ StrCatS (Temp, (StrSize (DefaultDescription) + sizeof (mBmUefiPrefix)) / sizeof (CHAR16), DefaultDescription);
+ FreePool (DefaultDescription);
+ DefaultDescription = Temp;
+ break;
+ }
+ }
+ ASSERT (DefaultDescription != NULL);
+
+ //
+ // Secondly query platform for the better boot description
+ //
+ for ( Link = GetFirstNode (&mPlatformBootDescriptionHandlers)
+ ; !IsNull (&mPlatformBootDescriptionHandlers, Link)
+ ; Link = GetNextNode (&mPlatformBootDescriptionHandlers, Link)
+ ) {
+ Entry = CR (Link, BM_BOOT_DESCRIPTION_ENTRY, Link, BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE);
+ Description = Entry->Handler (Handle, DefaultDescription);
+ if (Description != NULL) {
+ FreePool (DefaultDescription);
+ return Description;
+ }
+ }
+
+ return DefaultDescription;
+}
+
+/**
+ Enumerate all boot option descriptions and append " 2"/" 3"/... to make
+ unique description.
+
+ @param BootOptions Array of boot options.
+ @param BootOptionCount Count of boot options.
+**/
+VOID
+BmMakeBootOptionDescriptionUnique (
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
+ UINTN BootOptionCount
+ )
+{
+ UINTN Base;
+ UINTN Index;
+ UINTN DescriptionSize;
+ UINTN MaxSuffixSize;
+ BOOLEAN *Visited;
+ UINTN MatchCount;
+
+ if (BootOptionCount == 0) {
+ return;
+ }
+
+ //
+ // Calculate the maximum buffer size for the number suffix.
+ // The initial sizeof (CHAR16) is for the blank space before the number.
+ //
+ MaxSuffixSize = sizeof (CHAR16);
+ for (Index = BootOptionCount; Index != 0; Index = Index / 10) {
+ MaxSuffixSize += sizeof (CHAR16);
+ }
+
+ Visited = AllocateZeroPool (sizeof (BOOLEAN) * BootOptionCount);
+ ASSERT (Visited != NULL);
+
+ for (Base = 0; Base < BootOptionCount; Base++) {
+ if (!Visited[Base]) {
+ MatchCount = 1;
+ Visited[Base] = TRUE;
+ DescriptionSize = StrSize (BootOptions[Base].Description);
+ for (Index = Base + 1; Index < BootOptionCount; Index++) {
+ if (!Visited[Index] && StrCmp (BootOptions[Base].Description, BootOptions[Index].Description) == 0) {
+ Visited[Index] = TRUE;
+ MatchCount++;
+ FreePool (BootOptions[Index].Description);
+ BootOptions[Index].Description = AllocatePool (DescriptionSize + MaxSuffixSize);
+ UnicodeSPrint (
+ BootOptions[Index].Description, DescriptionSize + MaxSuffixSize,
+ L"%s %d",
+ BootOptions[Base].Description, MatchCount
+ );
+ }
+ }
+ }
+ }
+
+ FreePool (Visited);
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c
new file mode 100644
index 00000000..840cc74b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c
@@ -0,0 +1,315 @@
+/** @file
+ Library functions which relate with connecting the device.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+/**
+ Connect all the drivers to all the controllers.
+
+ This function makes sure all the current system drivers manage the correspoinding
+ controllers if have. And at the same time, makes sure all the system controllers
+ have driver to manage it if have.
+**/
+VOID
+BmConnectAllDriversToAllControllers (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+
+ do {
+ //
+ // Connect All EFI 1.10 drivers following EFI 1.10 algorithm
+ //
+ gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ gBS->ConnectController (HandleBuffer[Index], NULL, NULL, TRUE);
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool (HandleBuffer);
+ }
+
+ //
+ // Check to see if it's possible to dispatch an more DXE drivers.
+ // The above code may have made new DXE drivers show up.
+ // If any new driver is dispatched (Status == EFI_SUCCESS) and we will try
+ // the connect again.
+ //
+ Status = gDS->Dispatch ();
+
+ } while (!EFI_ERROR (Status));
+}
+
+/**
+ This function will connect all the system driver to controller
+ first, and then special connect the default console, this make
+ sure all the system controller available and the platform default
+ console connected.
+
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAll (
+ VOID
+ )
+{
+ //
+ // Connect the platform console first
+ //
+ EfiBootManagerConnectAllDefaultConsoles ();
+
+ //
+ // Generic way to connect all the drivers
+ //
+ BmConnectAllDriversToAllControllers ();
+
+ //
+ // Here we have the assumption that we have already had
+ // platform default console
+ //
+ EfiBootManagerConnectAllDefaultConsoles ();
+}
+
+/**
+ This function will create all handles associate with every device
+ path node. If the handle associate with one device path node can not
+ be created successfully, then still give chance to do the dispatch,
+ which load the missing drivers if possible.
+
+ @param DevicePathToConnect The device path which will be connected, it can be
+ a multi-instance device path
+ @param MatchingHandle Return the controller handle closest to the DevicePathToConnect
+
+ @retval EFI_SUCCESS All handles associate with every device path node
+ have been created.
+ @retval EFI_OUT_OF_RESOURCES There is no resource to create new handles.
+ @retval EFI_NOT_FOUND Create the handle associate with one device path
+ node failed.
+ @retval EFI_SECURITY_VIOLATION The user has no permission to start UEFI device
+ drivers on the DevicePath.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePathToConnect,
+ OUT EFI_HANDLE *MatchingHandle OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
+ EFI_HANDLE Handle;
+ EFI_HANDLE PreviousHandle;
+ EFI_TPL CurrentTpl;
+
+ if (DevicePathToConnect == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ CurrentTpl = EfiGetCurrentTpl ();
+ //
+ // Start the real work of connect with RemainingDevicePath
+ //
+ PreviousHandle = NULL;
+ do {
+ //
+ // Find the handle that best matches the Device Path. If it is only a
+ // partial match the remaining part of the device path is returned in
+ // RemainingDevicePath.
+ //
+ RemainingDevicePath = DevicePathToConnect;
+ Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &RemainingDevicePath, &Handle);
+ if (!EFI_ERROR (Status)) {
+ if (Handle == PreviousHandle) {
+ //
+ // If no forward progress is made try invoking the Dispatcher.
+ // A new FV may have been added to the system an new drivers
+ // may now be found.
+ // Status == EFI_SUCCESS means a driver was dispatched
+ // Status == EFI_NOT_FOUND means no new drivers were dispatched
+ //
+ if (CurrentTpl == TPL_APPLICATION) {
+ Status = gDS->Dispatch ();
+ } else {
+ //
+ // Always return EFI_NOT_FOUND here
+ // to prevent dead loop when control handle is found but connection failded case
+ //
+ Status = EFI_NOT_FOUND;
+ }
+ }
+
+
+ if (!EFI_ERROR (Status)) {
+ PreviousHandle = Handle;
+ //
+ // Connect all drivers that apply to Handle and RemainingDevicePath,
+ // the Recursive flag is FALSE so only one level will be expanded.
+ //
+ // If ConnectController fails to find a driver, then still give the chance to
+ // do dispatch, because partial RemainingDevicePath may be in the new FV
+ //
+ // 1. If the connect fail, RemainingDevicepath and handle will not
+ // change, so next time will do the dispatch, then dispatch's status
+ // will take effect
+ // 2. If the connect success, the RemainingDevicepath and handle will
+ // change, then avoid the dispatch, we have chance to continue the
+ // next connection
+ //
+ Status = gBS->ConnectController (Handle, NULL, RemainingDevicePath, FALSE);
+ if (Status == EFI_NOT_FOUND) {
+ Status = EFI_SUCCESS;
+ }
+ if (MatchingHandle != NULL) {
+ *MatchingHandle = Handle;
+ }
+ }
+ }
+ //
+ // Loop until RemainingDevicePath is an empty device path
+ //
+ } while (!EFI_ERROR (Status) && !IsDevicePathEnd (RemainingDevicePath));
+
+ ASSERT (EFI_ERROR (Status) || IsDevicePathEnd (RemainingDevicePath));
+
+ return Status;
+}
+
+/**
+ This function will disconnect all current system handles.
+
+ gBS->DisconnectController() is invoked for each handle exists in system handle buffer.
+ If handle is a bus type handle, all childrens also are disconnected recursively by
+ gBS->DisconnectController().
+**/
+VOID
+EFIAPI
+EfiBootManagerDisconnectAll (
+ VOID
+ )
+{
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN Index;
+
+ //
+ // Disconnect all
+ //
+ gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ for (Index = 0; Index < HandleCount; Index++) {
+ gBS->DisconnectController (HandleBuffer[Index], NULL, NULL);
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool (HandleBuffer);
+ }
+}
+
+/**
+ Connect the specific Usb device which match the short form device path,
+ and whose bus is determined by Host Controller (Uhci or Ehci).
+
+ @param DevicePath A short-form device path that starts with the first
+ element being a USB WWID or a USB Class device
+ path
+
+ @return EFI_INVALID_PARAMETER DevicePath is NULL pointer.
+ DevicePath is not a USB device path.
+
+ @return EFI_SUCCESS Success to connect USB device
+ @return EFI_NOT_FOUND Fail to find handle for USB controller to connect.
+
+**/
+EFI_STATUS
+BmConnectUsbShortFormDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN Index;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ UINT8 Class[3];
+ BOOLEAN AtLeastOneConnected;
+
+ //
+ // Check the passed in parameters
+ //
+ if (DevicePath == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((DevicePathType (DevicePath) != MESSAGING_DEVICE_PATH) ||
+ ((DevicePathSubType (DevicePath) != MSG_USB_CLASS_DP) && (DevicePathSubType (DevicePath) != MSG_USB_WWID_DP))
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Find the usb host controller firstly, then connect with the remaining device path
+ //
+ AtLeastOneConnected = FALSE;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiPciIoProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (!EFI_ERROR (Status)) {
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (
+ Handles[Index],
+ &gEfiPciIoProtocolGuid,
+ (VOID **) &PciIo
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Check whether the Pci device is the wanted usb host controller
+ //
+ Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, 0x09, 3, &Class);
+ if (!EFI_ERROR (Status) &&
+ ((PCI_CLASS_SERIAL == Class[2]) && (PCI_CLASS_SERIAL_USB == Class[1]))
+ ) {
+ Status = gBS->ConnectController (
+ Handles[Index],
+ NULL,
+ DevicePath,
+ FALSE
+ );
+ if (!EFI_ERROR(Status)) {
+ AtLeastOneConnected = TRUE;
+ }
+ }
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+ }
+
+ return AtLeastOneConnected ? EFI_SUCCESS : EFI_NOT_FOUND;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c
new file mode 100644
index 00000000..5477273c
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c
@@ -0,0 +1,761 @@
+/** @file
+ Library functions which contain all the code to connect console device.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+CHAR16 *mConVarName[] = {
+ L"ConIn",
+ L"ConOut",
+ L"ErrOut",
+ L"ConInDev",
+ L"ConOutDev",
+ L"ErrOutDev"
+};
+
+/**
+ Search out the video controller.
+
+ @return PCI device path of the video controller.
+**/
+EFI_HANDLE
+BmGetVideoController (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN RootBridgeHandleCount;
+ EFI_HANDLE *RootBridgeHandleBuffer;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+ UINTN RootBridgeIndex;
+ UINTN Index;
+ EFI_HANDLE VideoController;
+ EFI_PCI_IO_PROTOCOL *PciIo;
+ PCI_TYPE00 Pci;
+
+ //
+ // Make all the PCI_IO protocols show up
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiPciRootBridgeIoProtocolGuid,
+ NULL,
+ &RootBridgeHandleCount,
+ &RootBridgeHandleBuffer
+ );
+ if (EFI_ERROR (Status) || (RootBridgeHandleCount == 0)) {
+ return NULL;
+ }
+
+ VideoController = NULL;
+ for (RootBridgeIndex = 0; RootBridgeIndex < RootBridgeHandleCount; RootBridgeIndex++) {
+ gBS->ConnectController (RootBridgeHandleBuffer[RootBridgeIndex], NULL, NULL, FALSE);
+
+ //
+ // Start to check all the pci io to find the first video controller
+ //
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiPciIoProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (HandleBuffer[Index], &gEfiPciIoProtocolGuid, (VOID **) &PciIo);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Check for all video controller
+ //
+ Status = PciIo->Pci.Read (
+ PciIo,
+ EfiPciIoWidthUint32,
+ 0,
+ sizeof (Pci) / sizeof (UINT32),
+ &Pci
+ );
+ if (!EFI_ERROR (Status) && IS_PCI_VGA (&Pci)) {
+ // TODO: use IS_PCI_DISPLAY??
+ VideoController = HandleBuffer[Index];
+ break;
+ }
+ }
+ }
+ FreePool (HandleBuffer);
+
+ if (VideoController != NULL) {
+ break;
+ }
+ }
+ FreePool (RootBridgeHandleBuffer);
+
+ return VideoController;
+}
+
+/**
+ Query all the children of VideoController and return the device paths of all the
+ children that support GraphicsOutput protocol.
+
+ @param VideoController PCI handle of video controller.
+
+ @return Device paths of all the children that support GraphicsOutput protocol.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+EFIAPI
+EfiBootManagerGetGopDevicePath (
+ IN EFI_HANDLE VideoController
+ )
+{
+ UINTN Index;
+ EFI_STATUS Status;
+ EFI_GUID **ProtocolBuffer;
+ UINTN ProtocolBufferCount;
+ UINTN ProtocolIndex;
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *OpenInfoBuffer;
+ UINTN EntryCount;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Next;
+ EFI_DEVICE_PATH_PROTOCOL *Previous;
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *GopPool;
+ EFI_DEVICE_PATH_PROTOCOL *ReturnDevicePath;
+
+
+ Status = gBS->ProtocolsPerHandle (
+ VideoController,
+ &ProtocolBuffer,
+ &ProtocolBufferCount
+ );
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ GopPool = NULL;
+
+ for (ProtocolIndex = 0; ProtocolIndex < ProtocolBufferCount; ProtocolIndex++) {
+ Status = gBS->OpenProtocolInformation (
+ VideoController,
+ ProtocolBuffer[ProtocolIndex],
+ &OpenInfoBuffer,
+ &EntryCount
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ for (Index = 0; Index < EntryCount; Index++) {
+ //
+ // Query all the children
+ //
+ if ((OpenInfoBuffer[Index].Attributes & EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER) != 0) {
+ Status = gBS->OpenProtocol (
+ OpenInfoBuffer[Index].ControllerHandle,
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &DevicePath,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL
+ );
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ Previous = NULL;
+ for (Next = DevicePath; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) {
+ Previous = Next;
+ }
+ ASSERT (Previous != NULL);
+
+ if (DevicePathType (Previous) == ACPI_DEVICE_PATH && DevicePathSubType (Previous) == ACPI_ADR_DP) {
+ Status = gBS->OpenProtocol (
+ OpenInfoBuffer[Index].ControllerHandle,
+ &gEfiGraphicsOutputProtocolGuid,
+ NULL,
+ NULL,
+ NULL,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Append the device path to GOP pool when there is GOP protocol installed.
+ //
+ TempDevicePath = GopPool;
+ GopPool = AppendDevicePathInstance (GopPool, DevicePath);
+ gBS->FreePool (TempDevicePath);
+ }
+ }
+
+ if (DevicePathType (Previous) == HARDWARE_DEVICE_PATH && DevicePathSubType (Previous) == HW_CONTROLLER_DP) {
+ //
+ // Recursively look for GOP child in this frame buffer handle
+ //
+ DEBUG ((EFI_D_INFO, "[Bds] Looking for GOP child deeper ... \n"));
+ TempDevicePath = GopPool;
+ ReturnDevicePath = EfiBootManagerGetGopDevicePath (OpenInfoBuffer[Index].ControllerHandle);
+ GopPool = AppendDevicePathInstance (GopPool, ReturnDevicePath);
+ gBS->FreePool (ReturnDevicePath);
+ gBS->FreePool (TempDevicePath);
+ }
+ }
+ }
+
+ FreePool (OpenInfoBuffer);
+ }
+
+ FreePool (ProtocolBuffer);
+
+ return GopPool;
+}
+
+/**
+ Connect the platform active active video controller.
+
+ @param VideoController PCI handle of video controller.
+
+ @retval EFI_NOT_FOUND There is no active video controller.
+ @retval EFI_SUCCESS The video controller is connected.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectVideoController (
+ EFI_HANDLE VideoController OPTIONAL
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Gop;
+
+ if (VideoController == NULL) {
+ //
+ // Get the platform vga device
+ //
+ VideoController = BmGetVideoController ();
+ }
+
+ if (VideoController == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Try to connect the PCI device path, so that GOP driver could start on this
+ // device and create child handles with GraphicsOutput Protocol installed
+ // on them, then we get device paths of these child handles and select
+ // them as possible console device.
+ //
+ gBS->ConnectController (VideoController, NULL, NULL, FALSE);
+
+ Gop = EfiBootManagerGetGopDevicePath (VideoController);
+ if (Gop == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ EfiBootManagerUpdateConsoleVariable (ConOut, Gop, NULL);
+ FreePool (Gop);
+
+ //
+ // Necessary for ConPlatform and ConSplitter driver to start up again after ConOut is updated.
+ //
+ return gBS->ConnectController (VideoController, NULL, NULL, TRUE);
+}
+
+/**
+ Fill console handle in System Table if there are no valid console handle in.
+
+ Firstly, check the validation of console handle in System Table. If it is invalid,
+ update it by the first console device handle from EFI console variable.
+
+ @param VarName The name of the EFI console variable.
+ @param ConsoleGuid Specified Console protocol GUID.
+ @param ConsoleHandle On IN, console handle in System Table to be checked.
+ On OUT, new console handle in system table.
+ @param ProtocolInterface On IN, console protocol on console handle in System Table to be checked.
+ On OUT, new console protocol on new console handle in system table.
+
+ @retval TRUE System Table has been updated.
+ @retval FALSE System Table hasn't been updated.
+
+**/
+BOOLEAN
+BmUpdateSystemTableConsole (
+ IN CHAR16 *VarName,
+ IN EFI_GUID *ConsoleGuid,
+ IN OUT EFI_HANDLE *ConsoleHandle,
+ IN OUT VOID **ProtocolInterface
+ )
+{
+ EFI_STATUS Status;
+ UINTN DevicePathSize;
+ EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *VarConsole;
+ EFI_DEVICE_PATH_PROTOCOL *Instance;
+ EFI_DEVICE_PATH_PROTOCOL *FullInstance;
+ VOID *Interface;
+ EFI_HANDLE NewHandle;
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *TextOut;
+
+ ASSERT (VarName != NULL);
+ ASSERT (ConsoleHandle != NULL);
+ ASSERT (ConsoleGuid != NULL);
+ ASSERT (ProtocolInterface != NULL);
+
+ if (*ConsoleHandle != NULL) {
+ Status = gBS->HandleProtocol (
+ *ConsoleHandle,
+ ConsoleGuid,
+ &Interface
+ );
+ if (Status == EFI_SUCCESS && Interface == *ProtocolInterface) {
+ //
+ // If ConsoleHandle is valid and console protocol on this handle also
+ // also matched, just return.
+ //
+ return FALSE;
+ }
+ }
+
+ //
+ // Get all possible consoles device path from EFI variable
+ //
+ GetEfiGlobalVariable2 (VarName, (VOID **) &VarConsole, NULL);
+ if (VarConsole == NULL) {
+ //
+ // If there is no any console device, just return.
+ //
+ return FALSE;
+ }
+
+ FullDevicePath = VarConsole;
+
+ do {
+ //
+ // Check every instance of the console variable
+ //
+ Instance = GetNextDevicePathInstance (&VarConsole, &DevicePathSize);
+ if (Instance == NULL) {
+ DEBUG ((EFI_D_ERROR, "[Bds] No valid console instance is found for %s!\n", VarName));
+ // We should not ASSERT when all the console devices are removed.
+ // ASSERT_EFI_ERROR (EFI_NOT_FOUND);
+ FreePool (FullDevicePath);
+ return FALSE;
+ }
+
+ //
+ // Find console device handle by device path instance
+ //
+ FullInstance = Instance;
+ Status = gBS->LocateDevicePath (
+ ConsoleGuid,
+ &Instance,
+ &NewHandle
+ );
+ FreePool (FullInstance);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Get the console protocol on this console device handle
+ //
+ Status = gBS->HandleProtocol (
+ NewHandle,
+ ConsoleGuid,
+ &Interface
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Update new console handle in System Table.
+ //
+ *ConsoleHandle = NewHandle;
+ *ProtocolInterface = Interface;
+ if (CompareGuid (ConsoleGuid, &gEfiSimpleTextOutProtocolGuid)) {
+ //
+ // If it is console out device, set console mode 80x25 if current mode is invalid.
+ //
+ TextOut = (EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *) Interface;
+ if (TextOut->Mode->Mode == -1) {
+ TextOut->SetMode (TextOut, 0);
+ }
+ }
+ FreePool (FullDevicePath);
+ return TRUE;
+ }
+ }
+
+ } while (Instance != NULL);
+
+ //
+ // No any available console devcie found.
+ //
+ FreePool (FullDevicePath);
+ return FALSE;
+}
+
+/**
+ This function updates the console variable based on ConVarName. It can
+ add or remove one specific console device path from the variable
+
+ @param ConsoleType ConIn, ConOut, ErrOut, ConInDev, ConOutDev or ErrOutDev.
+ @param CustomizedConDevicePath The console device path to be added to
+ the console variable. Cannot be multi-instance.
+ @param ExclusiveDevicePath The console device path to be removed
+ from the console variable. Cannot be multi-instance.
+
+ @retval EFI_UNSUPPORTED The added device path is the same as a removed one.
+ @retval EFI_SUCCESS Successfully added or removed the device path from the
+ console variable.
+ @retval others Return status of RT->SetVariable().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerUpdateConsoleVariable (
+ IN CONSOLE_TYPE ConsoleType,
+ IN EFI_DEVICE_PATH_PROTOCOL *CustomizedConDevicePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *ExclusiveDevicePath
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *VarConsole;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
+
+ if (ConsoleType >= ARRAY_SIZE (mConVarName)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Notes: check the device path point, here should check
+ // with compare memory
+ //
+ if (CustomizedConDevicePath == ExclusiveDevicePath) {
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Delete the ExclusiveDevicePath from current default console
+ //
+ GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &VarConsole, NULL);
+ //
+ // Initialize NewDevicePath
+ //
+ NewDevicePath = VarConsole;
+
+ //
+ // If ExclusiveDevicePath is even the part of the instance in VarConsole, delete it.
+ // In the end, NewDevicePath is the final device path.
+ //
+ if (ExclusiveDevicePath != NULL && VarConsole != NULL) {
+ NewDevicePath = BmDelPartMatchInstance (VarConsole, ExclusiveDevicePath);
+ }
+ //
+ // Try to append customized device path to NewDevicePath.
+ //
+ if (CustomizedConDevicePath != NULL) {
+ if (!BmMatchDevicePaths (NewDevicePath, CustomizedConDevicePath)) {
+ //
+ // Check if there is part of CustomizedConDevicePath in NewDevicePath, delete it.
+ //
+ NewDevicePath = BmDelPartMatchInstance (NewDevicePath, CustomizedConDevicePath);
+ //
+ // In the first check, the default console variable will be _ModuleEntryPoint,
+ // just append current customized device path
+ //
+ TempNewDevicePath = NewDevicePath;
+ NewDevicePath = AppendDevicePathInstance (NewDevicePath, CustomizedConDevicePath);
+ if (TempNewDevicePath != NULL) {
+ FreePool(TempNewDevicePath);
+ }
+ }
+ }
+
+ //
+ // Finally, Update the variable of the default console by NewDevicePath
+ //
+ Status = gRT->SetVariable (
+ mConVarName[ConsoleType],
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS
+ | ((ConsoleType < ConInDev) ? EFI_VARIABLE_NON_VOLATILE : 0),
+ GetDevicePathSize (NewDevicePath),
+ NewDevicePath
+ );
+
+ if (VarConsole == NewDevicePath) {
+ if (VarConsole != NULL) {
+ FreePool(VarConsole);
+ }
+ } else {
+ if (VarConsole != NULL) {
+ FreePool(VarConsole);
+ }
+ if (NewDevicePath != NULL) {
+ FreePool(NewDevicePath);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Connect the console device base on the variable ConsoleType.
+
+ @param ConsoleType ConIn, ConOut or ErrOut.
+
+ @retval EFI_NOT_FOUND There is not any console devices connected
+ success
+ @retval EFI_SUCCESS Success connect any one instance of the console
+ device path base on the variable ConVarName.
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectConsoleVariable (
+ IN CONSOLE_TYPE ConsoleType
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *StartDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *Instance;
+ EFI_DEVICE_PATH_PROTOCOL *Next;
+ EFI_DEVICE_PATH_PROTOCOL *CopyOfDevicePath;
+ UINTN Size;
+ BOOLEAN DeviceExist;
+ EFI_HANDLE Handle;
+
+ if ((ConsoleType != ConIn) && (ConsoleType != ConOut) && (ConsoleType != ErrOut)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = EFI_SUCCESS;
+ DeviceExist = FALSE;
+ Handle = NULL;
+
+ //
+ // Check if the console variable exist
+ //
+ GetEfiGlobalVariable2 (mConVarName[ConsoleType], (VOID **) &StartDevicePath, NULL);
+ if (StartDevicePath == NULL) {
+ return EFI_UNSUPPORTED;
+ }
+
+ CopyOfDevicePath = StartDevicePath;
+ do {
+ //
+ // Check every instance of the console variable
+ //
+ Instance = GetNextDevicePathInstance (&CopyOfDevicePath, &Size);
+ if (Instance == NULL) {
+ FreePool (StartDevicePath);
+ return EFI_UNSUPPORTED;
+ }
+
+ Next = Instance;
+ while (!IsDevicePathEndType (Next)) {
+ Next = NextDevicePathNode (Next);
+ }
+
+ SetDevicePathEndNode (Next);
+ //
+ // Connect the USB console
+ // USB console device path is a short-form device path that
+ // starts with the first element being a USB WWID
+ // or a USB Class device path
+ //
+ if ((DevicePathType (Instance) == MESSAGING_DEVICE_PATH) &&
+ ((DevicePathSubType (Instance) == MSG_USB_CLASS_DP) || (DevicePathSubType (Instance) == MSG_USB_WWID_DP))
+ ) {
+ Status = BmConnectUsbShortFormDevicePath (Instance);
+ if (!EFI_ERROR (Status)) {
+ DeviceExist = TRUE;
+ }
+ } else {
+ for (Next = Instance; !IsDevicePathEnd (Next); Next = NextDevicePathNode (Next)) {
+ if (DevicePathType (Next) == ACPI_DEVICE_PATH && DevicePathSubType (Next) == ACPI_ADR_DP) {
+ break;
+ } else if (DevicePathType (Next) == HARDWARE_DEVICE_PATH &&
+ DevicePathSubType (Next) == HW_CONTROLLER_DP &&
+ DevicePathType (NextDevicePathNode (Next)) == ACPI_DEVICE_PATH &&
+ DevicePathSubType (NextDevicePathNode (Next)) == ACPI_ADR_DP
+ ) {
+ break;
+ }
+ }
+ if (!IsDevicePathEnd (Next)) {
+ //
+ // For GOP device path, start the video driver with NULL remaining device path
+ //
+ SetDevicePathEndNode (Next);
+ Status = EfiBootManagerConnectDevicePath (Instance, &Handle);
+ if (!EFI_ERROR (Status)) {
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);
+ }
+ } else {
+ Status = EfiBootManagerConnectDevicePath (Instance, NULL);
+ }
+ if (EFI_ERROR (Status)) {
+ //
+ // Delete the instance from the console varialbe
+ //
+ EfiBootManagerUpdateConsoleVariable (ConsoleType, NULL, Instance);
+ } else {
+ DeviceExist = TRUE;
+ }
+ }
+ FreePool(Instance);
+ } while (CopyOfDevicePath != NULL);
+
+ FreePool (StartDevicePath);
+
+ if (!DeviceExist) {
+ return EFI_NOT_FOUND;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This function will search every input/output device in current system,
+ and make every input/output device as potential console device.
+**/
+VOID
+EFIAPI
+EfiBootManagerConnectAllConsoles (
+ VOID
+ )
+{
+ UINTN Index;
+ EFI_DEVICE_PATH_PROTOCOL *ConDevicePath;
+ UINTN HandleCount;
+ EFI_HANDLE *HandleBuffer;
+
+ Index = 0;
+ HandleCount = 0;
+ HandleBuffer = NULL;
+ ConDevicePath = NULL;
+
+ //
+ // Update all the console variables
+ //
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleTextInProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+
+ for (Index = 0; Index < HandleCount; Index++) {
+ gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ConDevicePath
+ );
+ EfiBootManagerUpdateConsoleVariable (ConIn, ConDevicePath, NULL);
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool(HandleBuffer);
+ HandleBuffer = NULL;
+ }
+
+ gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleTextOutProtocolGuid,
+ NULL,
+ &HandleCount,
+ &HandleBuffer
+ );
+ for (Index = 0; Index < HandleCount; Index++) {
+ gBS->HandleProtocol (
+ HandleBuffer[Index],
+ &gEfiDevicePathProtocolGuid,
+ (VOID **) &ConDevicePath
+ );
+ EfiBootManagerUpdateConsoleVariable (ConOut, ConDevicePath, NULL);
+ EfiBootManagerUpdateConsoleVariable (ErrOut, ConDevicePath, NULL);
+ }
+
+ if (HandleBuffer != NULL) {
+ FreePool(HandleBuffer);
+ }
+
+ //
+ // Connect all console variables
+ //
+ EfiBootManagerConnectAllDefaultConsoles ();
+}
+
+
+/**
+ This function will connect all the console devices base on the console
+ device variable ConIn, ConOut and ErrOut.
+
+ @retval EFI_DEVICE_ERROR All the consoles were not connected due to an error.
+ @retval EFI_SUCCESS Success connect any one instance of the console
+ device path base on the variable ConVarName.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerConnectAllDefaultConsoles (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ BOOLEAN OneConnected;
+ BOOLEAN SystemTableUpdated;
+
+ OneConnected = FALSE;
+
+ Status = EfiBootManagerConnectConsoleVariable (ConOut);
+ if (!EFI_ERROR (Status)) {
+ OneConnected = TRUE;
+ }
+ PERF_EVENT ("ConOutReady");
+
+
+ Status = EfiBootManagerConnectConsoleVariable (ConIn);
+ if (!EFI_ERROR (Status)) {
+ OneConnected = TRUE;
+ }
+ PERF_EVENT ("ConInReady");
+
+ Status = EfiBootManagerConnectConsoleVariable (ErrOut);
+ if (!EFI_ERROR (Status)) {
+ OneConnected = TRUE;
+ }
+ PERF_EVENT ("ErrOutReady");
+
+ SystemTableUpdated = FALSE;
+ //
+ // Fill console handles in System Table if no console device assignd.
+ //
+ if (BmUpdateSystemTableConsole (L"ConIn", &gEfiSimpleTextInProtocolGuid, &gST->ConsoleInHandle, (VOID **) &gST->ConIn)) {
+ SystemTableUpdated = TRUE;
+ }
+ if (BmUpdateSystemTableConsole (L"ConOut", &gEfiSimpleTextOutProtocolGuid, &gST->ConsoleOutHandle, (VOID **) &gST->ConOut)) {
+ SystemTableUpdated = TRUE;
+ }
+ if (BmUpdateSystemTableConsole (L"ErrOut", &gEfiSimpleTextOutProtocolGuid, &gST->StandardErrorHandle, (VOID **) &gST->StdErr)) {
+ SystemTableUpdated = TRUE;
+ }
+
+ if (SystemTableUpdated) {
+ //
+ // Update the CRC32 in the EFI System Table header
+ //
+ gST->Hdr.CRC32 = 0;
+ gBS->CalculateCrc32 (
+ (UINT8 *) &gST->Hdr,
+ gST->Hdr.HeaderSize,
+ &gST->Hdr.CRC32
+ );
+ }
+
+ return OneConnected ? EFI_SUCCESS : EFI_DEVICE_ERROR;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
new file mode 100644
index 00000000..13571114
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c
@@ -0,0 +1,585 @@
+/** @file
+ Library functions which relates with driver health.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett-Packard Development Company, L.P.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+ CHAR16 *mBmHealthStatusText[] = {
+ L"Healthy",
+ L"Repair Required",
+ L"Configuration Required",
+ L"Failed",
+ L"Reconnect Required",
+ L"Reboot Required"
+ };
+
+/**
+ Return the controller name.
+
+ @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
+ @param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing.
+ This handle specifies the controller whose name is to be returned.
+ @param ChildHandle The handle of the child controller to retrieve the name of. This is an
+ optional parameter that may be NULL. It will be NULL for device drivers.
+ It will also be NULL for bus drivers that attempt to retrieve the name
+ of the bus controller. It will not be NULL for a bus driver that attempts
+ to retrieve the name of a child controller.
+
+ @return A pointer to the Unicode string to return. This Unicode string is the name of the controller
+ specified by ControllerHandle and ChildHandle.
+**/
+CHAR16 *
+BmGetControllerName (
+ IN EFI_HANDLE DriverHealthHandle,
+ IN EFI_HANDLE ControllerHandle,
+ IN EFI_HANDLE ChildHandle
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *ControllerName;
+ CHAR8 *LanguageVariable;
+ CHAR8 *BestLanguage;
+ BOOLEAN Iso639Language;
+ EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
+
+ ControllerName = NULL;
+
+ //
+ // Locate Component Name (2) protocol on the driver binging handle.
+ //
+ Iso639Language = FALSE;
+ Status = gBS->HandleProtocol (
+ DriverHealthHandle,
+ &gEfiComponentName2ProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (EFI_ERROR (Status)) {
+ Status = gBS->HandleProtocol (
+ DriverHealthHandle,
+ &gEfiComponentNameProtocolGuid,
+ (VOID **) &ComponentName
+ );
+ if (!EFI_ERROR (Status)) {
+ Iso639Language = TRUE;
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL);
+ BestLanguage = GetBestLanguage(
+ ComponentName->SupportedLanguages,
+ Iso639Language,
+ (LanguageVariable != NULL) ? LanguageVariable : "",
+ Iso639Language ? "eng" : "en-US",
+ NULL
+ );
+ if (LanguageVariable != NULL) {
+ FreePool (LanguageVariable);
+ }
+
+ Status = ComponentName->GetControllerName (
+ ComponentName,
+ ControllerHandle,
+ ChildHandle,
+ BestLanguage,
+ &ControllerName
+ );
+ }
+
+ if (!EFI_ERROR (Status)) {
+ return AllocateCopyPool (StrSize (ControllerName), ControllerName);
+ } else {
+ return ConvertDevicePathToText (
+ DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),
+ FALSE,
+ FALSE
+ );
+ }
+}
+
+/**
+ Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
+
+ @param DriverHealthInfo Pointer to the Driver Health information entry.
+**/
+VOID
+BmDisplayMessages (
+ IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo
+ )
+{
+ UINTN Index;
+ EFI_STRING String;
+ CHAR16 *ControllerName;
+
+ if (DriverHealthInfo->MessageList == NULL ||
+ DriverHealthInfo->MessageList[0].HiiHandle == NULL) {
+ return;
+ }
+
+ ControllerName = BmGetControllerName (
+ DriverHealthInfo->DriverHealthHandle,
+ DriverHealthInfo->ControllerHandle,
+ DriverHealthInfo->ChildHandle
+ );
+
+ DEBUG ((EFI_D_INFO, "Controller: %s\n", ControllerName));
+ Print (L"Controller: %s\n", ControllerName);
+ for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) {
+ String = HiiGetString (
+ DriverHealthInfo->MessageList[Index].HiiHandle,
+ DriverHealthInfo->MessageList[Index].StringId,
+ NULL
+ );
+ if (String != NULL) {
+ Print (L" %s\n", String);
+ DEBUG ((EFI_D_INFO, " %s\n", String));
+ FreePool (String);
+ }
+ }
+
+ if (ControllerName != NULL) {
+ FreePool (ControllerName);
+ }
+}
+
+/**
+ The repair notify function.
+ @param Value A value between 0 and Limit that identifies the current progress
+ of the repair operation.
+ @param Limit The maximum value of Value for the current repair operation.
+ If Limit is 0, then the completion progress is indeterminate.
+ For example, a driver that wants to specify progress in percent
+ would use a Limit value of 100.
+
+ @retval EFI_SUCCESS Successfully return from the notify function.
+**/
+EFI_STATUS
+EFIAPI
+BmRepairNotify (
+ IN UINTN Value,
+ IN UINTN Limit
+ )
+{
+ DEBUG ((EFI_D_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));
+ Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Collect the Driver Health status of a single controller.
+
+ @param DriverHealthInfo A pointer to the array containing all of the platform driver health information.
+ @param Count Return the updated array count.
+ @param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
+ @param ControllerHandle The handle of the controller..
+ @param ChildHandle The handle of the child controller to retrieve the health
+ status on. This is an optional parameter that may be NULL.
+
+ @retval Status The status returned from GetHealthStatus.
+ @retval EFI_ABORTED The health status is healthy so no further query is needed.
+
+**/
+EFI_STATUS
+BmGetSingleControllerHealthStatus (
+ IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,
+ IN OUT UINTN *Count,
+ IN EFI_HANDLE DriverHealthHandle,
+ IN EFI_HANDLE ControllerHandle, OPTIONAL
+ IN EFI_HANDLE ChildHandle OPTIONAL
+ )
+{
+ EFI_STATUS Status;
+ EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth;
+ EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList;
+ EFI_HII_HANDLE FormHiiHandle;
+ EFI_DRIVER_HEALTH_STATUS HealthStatus;
+
+ ASSERT (DriverHealthHandle != NULL);
+ //
+ // Retrieve the Driver Health Protocol from DriverHandle
+ //
+ Status = gBS->HandleProtocol (
+ DriverHealthHandle,
+ &gEfiDriverHealthProtocolGuid,
+ (VOID **) &DriverHealth
+ );
+ ASSERT_EFI_ERROR (Status);
+
+
+ if (ControllerHandle == NULL) {
+ //
+ // If ControllerHandle is NULL, the return the cumulative health status of the driver
+ //
+ Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL);
+ if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) {
+ *DriverHealthInfo = ReallocatePool (
+ (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+ (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+ *DriverHealthInfo
+ );
+ ASSERT (*DriverHealthInfo != NULL);
+
+ (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
+ (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
+ (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
+
+ *Count = *Count + 1;
+
+ Status = EFI_ABORTED;
+ }
+ return Status;
+ }
+
+ MessageList = NULL;
+ FormHiiHandle = NULL;
+
+ //
+ // Collect the health status with the optional HII message list
+ //
+ Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle);
+ if (!EFI_ERROR (Status)) {
+ *DriverHealthInfo = ReallocatePool (
+ (*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+ (*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
+ *DriverHealthInfo
+ );
+ ASSERT (*DriverHealthInfo != NULL);
+ (*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
+ (*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
+ (*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle;
+ (*DriverHealthInfo)[*Count].ChildHandle = ChildHandle;
+ (*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle;
+ (*DriverHealthInfo)[*Count].MessageList = MessageList;
+ (*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
+
+ *Count = *Count + 1;
+ }
+
+ return Status;
+}
+
+/**
+ Return all the Driver Health information.
+
+ When the cumulative health status of all the controllers managed by the
+ driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
+ EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
+ EFI_DRIVER_HEALTH_PROTOCOL instance.
+ Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
+ entry. Additionally every child controller creates one
+ EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
+
+ @param Count Return the count of the Driver Health information.
+
+ @retval NULL No Driver Health information is returned.
+ @retval !NULL Pointer to the Driver Health information array.
+**/
+EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
+EFIAPI
+EfiBootManagerGetDriverHealthInfo (
+ UINTN *Count
+ )
+{
+ EFI_STATUS Status;
+ UINTN NumHandles;
+ EFI_HANDLE *DriverHealthHandles;
+ UINTN DriverHealthIndex;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN ControllerIndex;
+ UINTN ChildIndex;
+ EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
+
+ //
+ // Initialize local variables
+ //
+ *Count = 0;
+ DriverHealthInfo = NULL;
+ Handles = NULL;
+ DriverHealthHandles = NULL;
+ NumHandles = 0;
+ HandleCount = 0;
+
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiDriverHealthProtocolGuid,
+ NULL,
+ &NumHandles,
+ &DriverHealthHandles
+ );
+
+ if (Status == EFI_NOT_FOUND || NumHandles == 0) {
+ //
+ // If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
+ //
+ return NULL;
+ }
+
+ ASSERT_EFI_ERROR (Status);
+ ASSERT (DriverHealthHandles != NULL);
+
+ //
+ // Check the health status of all controllers in the platform
+ // Start by looping through all the Driver Health Protocol handles in the handle database
+ //
+ for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {
+ //
+ // Get the cumulative health status of the driver
+ //
+ Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // See if the list of all handles in the handle database has been retrieved
+ //
+ //
+ if (Handles == NULL) {
+ //
+ // Retrieve the list of all handles from the handle database
+ //
+ Status = gBS->LocateHandleBuffer (
+ AllHandles,
+ NULL,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ ASSERT_EFI_ERROR (Status);
+ }
+ //
+ // Loop through all the controller handles in the handle database
+ //
+ for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {
+ Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ //
+ // Loop through all the child handles in the handle database
+ //
+ for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
+ Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+ }
+ }
+ }
+
+ Status = EFI_SUCCESS;
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+ if (DriverHealthHandles != NULL) {
+ FreePool (DriverHealthHandles);
+ }
+
+ return DriverHealthInfo;
+}
+
+/**
+ Free the Driver Health information array.
+
+ @param DriverHealthInfo Pointer to array of the Driver Health information.
+ @param Count Count of the array.
+
+ @retval EFI_SUCCESS The array is freed.
+ @retval EFI_INVALID_PARAMETER The array is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeDriverHealthInfo (
+ EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,
+ UINTN Count
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; Index < Count; Index++) {
+ if (DriverHealthInfo[Index].MessageList != NULL) {
+ FreePool (DriverHealthInfo[Index].MessageList);
+ }
+ }
+ return gBS->FreePool (DriverHealthInfo);
+}
+
+/**
+ Repair all the controllers according to the Driver Health status queried.
+
+ @param ReconnectRepairCount To record the number of recursive call of
+ this function itself.
+**/
+VOID
+BmRepairAllControllers (
+ UINTN ReconnectRepairCount
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
+ EFI_DRIVER_HEALTH_STATUS HealthStatus;
+ UINTN Count;
+ UINTN Index;
+ BOOLEAN RepairRequired;
+ BOOLEAN ConfigurationRequired;
+ BOOLEAN ReconnectRequired;
+ BOOLEAN RebootRequired;
+ EFI_HII_HANDLE *HiiHandles;
+ EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2;
+ UINT32 MaxRepairCount;
+ UINT32 RepairCount;
+
+ //
+ // Configure PcdDriverHealthConfigureForm to ZeroGuid to disable driver health check.
+ //
+ if (IsZeroGuid (PcdGetPtr (PcdDriverHealthConfigureForm))) {
+ return;
+ }
+
+ Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);
+ ASSERT_EFI_ERROR (Status);
+
+ MaxRepairCount = PcdGet32 (PcdMaxRepairCount);
+ RepairCount = 0;
+
+ do {
+ RepairRequired = FALSE;
+ ConfigurationRequired = FALSE;
+
+ //
+ // Deal with Repair Required
+ //
+ DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
+ for (Index = 0; Index < Count; Index++) {
+ if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {
+ ConfigurationRequired = TRUE;
+ }
+
+ if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {
+ RepairRequired = TRUE;
+
+ BmDisplayMessages (&DriverHealthInfo[Index]);
+
+ Status = DriverHealthInfo[Index].DriverHealth->Repair (
+ DriverHealthInfo[Index].DriverHealth,
+ DriverHealthInfo[Index].ControllerHandle,
+ DriverHealthInfo[Index].ChildHandle,
+ BmRepairNotify
+ );
+ if (!EFI_ERROR (Status) && !ConfigurationRequired) {
+ Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (
+ DriverHealthInfo[Index].DriverHealth,
+ DriverHealthInfo[Index].ControllerHandle,
+ DriverHealthInfo[Index].ChildHandle,
+ &HealthStatus,
+ NULL,
+ NULL
+ );
+ if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {
+ ConfigurationRequired = TRUE;
+ }
+ }
+ }
+ }
+
+ if (ConfigurationRequired) {
+ HiiHandles = HiiGetHiiHandles (NULL);
+ if (HiiHandles != NULL) {
+ for (Index = 0; HiiHandles[Index] != NULL; Index++) {
+ Status = FormBrowser2->SendForm (
+ FormBrowser2,
+ &HiiHandles[Index],
+ 1,
+ PcdGetPtr (PcdDriverHealthConfigureForm),
+ 0,
+ NULL,
+ NULL
+ );
+ if (!EFI_ERROR (Status)) {
+ break;
+ }
+ }
+ FreePool (HiiHandles);
+ }
+ }
+
+ EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+ RepairCount++;
+ } while ((RepairRequired || ConfigurationRequired) && ((MaxRepairCount == 0) || (RepairCount < MaxRepairCount)));
+
+ RebootRequired = FALSE;
+ ReconnectRequired = FALSE;
+ DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
+ for (Index = 0; Index < Count; Index++) {
+
+ BmDisplayMessages (&DriverHealthInfo[Index]);
+
+ if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {
+ Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);
+ if (EFI_ERROR (Status)) {
+ //
+ // Disconnect failed. Need to promote reconnect to a reboot.
+ //
+ RebootRequired = TRUE;
+ } else {
+ gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);
+ ReconnectRequired = TRUE;
+ }
+ }
+
+ if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {
+ RebootRequired = TRUE;
+ }
+ }
+ EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+
+
+ DEBUG_CODE (
+ CHAR16 *ControllerName;
+
+ DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
+ for (Index = 0; Index < Count; Index++) {
+ ControllerName = BmGetControllerName (
+ DriverHealthInfo[Index].DriverHealthHandle,
+ DriverHealthInfo[Index].ControllerHandle,
+ DriverHealthInfo[Index].ChildHandle
+ );
+ DEBUG ((
+ EFI_D_INFO,
+ "%02d: %s - %s\n",
+ Index,
+ ControllerName,
+ mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]
+ ));
+ if (ControllerName != NULL) {
+ FreePool (ControllerName);
+ }
+ }
+ EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
+ );
+
+ if (ReconnectRequired) {
+ if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) {
+ BmRepairAllControllers (ReconnectRepairCount + 1);
+ } else {
+ DEBUG ((DEBUG_ERROR, "[%a:%d] Repair failed after %d retries.\n",
+ __FUNCTION__, __LINE__, ReconnectRepairCount));
+ }
+ }
+
+ if (RebootRequired) {
+ DEBUG ((EFI_D_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
+ gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
new file mode 100644
index 00000000..50185318
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c
@@ -0,0 +1,1151 @@
+/** @file
+ Hotkey library functions.
+
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+//
+// Lock for linked list
+//
+EFI_LOCK mBmHotkeyLock = EFI_INITIALIZE_LOCK_VARIABLE (TPL_NOTIFY);
+LIST_ENTRY mBmHotkeyList = INITIALIZE_LIST_HEAD_VARIABLE (mBmHotkeyList);
+EFI_EVENT mBmHotkeyTriggered = NULL;
+BOOLEAN mBmHotkeyServiceStarted = FALSE;
+UINTN mBmHotkeySupportCount = 0;
+
+//
+// Set OptionNumber as unassigned value to indicate the option isn't initialized
+//
+EFI_BOOT_MANAGER_LOAD_OPTION mBmHotkeyBootOption = { LoadOptionNumberUnassigned };
+
+EFI_BOOT_MANAGER_KEY_OPTION *mBmContinueKeyOption = NULL;
+VOID *mBmTxtInExRegistration = NULL;
+
+
+/**
+ Return the buffer size of the EFI_BOOT_MANAGER_KEY_OPTION data.
+
+ @param KeyOption The input key option info.
+
+ @retval The buffer size of the key option data.
+**/
+UINTN
+BmSizeOfKeyOption (
+ IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
+ )
+{
+ return OFFSET_OF (EFI_BOOT_MANAGER_KEY_OPTION, Keys)
+ + KeyOption->KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY);
+}
+
+/**
+
+ Check whether the input key option is valid.
+
+ @param KeyOption Key option.
+ @param KeyOptionSize Size of the key option.
+
+ @retval TRUE Input key option is valid.
+ @retval FALSE Input key option is not valid.
+**/
+BOOLEAN
+BmIsKeyOptionValid (
+ IN CONST EFI_BOOT_MANAGER_KEY_OPTION *KeyOption,
+ IN UINTN KeyOptionSize
+)
+{
+ UINT16 OptionName[BM_OPTION_NAME_LEN];
+ UINT8 *BootOption;
+ UINTN BootOptionSize;
+ UINT32 Crc;
+
+ if (BmSizeOfKeyOption (KeyOption) != KeyOptionSize) {
+ return FALSE;
+ }
+
+ //
+ // Check whether corresponding Boot Option exist
+ //
+ UnicodeSPrint (
+ OptionName, sizeof (OptionName), L"%s%04x",
+ mBmLoadOptionName[LoadOptionTypeBoot], KeyOption->BootOption
+ );
+ GetEfiGlobalVariable2 (OptionName, (VOID **) &BootOption, &BootOptionSize);
+
+ if (BootOption == NULL) {
+ return FALSE;
+ }
+
+ //
+ // Check CRC for Boot Option
+ //
+ gBS->CalculateCrc32 (BootOption, BootOptionSize, &Crc);
+ FreePool (BootOption);
+
+ return (BOOLEAN) (KeyOption->BootOptionCrc == Crc);
+}
+
+/**
+
+ Check whether the input variable is an key option variable.
+
+ @param Name Input variable name.
+ @param Guid Input variable guid.
+ @param OptionNumber The option number of this key option variable.
+
+ @retval TRUE Input variable is a key option variable.
+ @retval FALSE Input variable is not a key option variable.
+**/
+BOOLEAN
+BmIsKeyOptionVariable (
+ CHAR16 *Name,
+ EFI_GUID *Guid,
+ UINT16 *OptionNumber
+ )
+{
+ UINTN Index;
+ UINTN Uint;
+
+ if (!CompareGuid (Guid, &gEfiGlobalVariableGuid) ||
+ (StrSize (Name) != sizeof (L"Key####")) ||
+ (StrnCmp (Name, L"Key", 3) != 0)
+ ) {
+ return FALSE;
+ }
+
+ *OptionNumber = 0;
+ for (Index = 3; Index < 7; Index++) {
+ Uint = BmCharToUint (Name[Index]);
+ if (Uint == -1) {
+ return FALSE;
+ } else {
+ *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;
+ }
+ }
+
+ return TRUE;
+}
+
+typedef struct {
+ EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
+ UINTN KeyOptionCount;
+} BM_COLLECT_KEY_OPTIONS_PARAM;
+
+/**
+ Visitor function to collect the key options from NV storage.
+
+ @param Name Variable name.
+ @param Guid Variable GUID.
+ @param Context The same context passed to BmForEachVariable.
+**/
+VOID
+BmCollectKeyOptions (
+ CHAR16 *Name,
+ EFI_GUID *Guid,
+ VOID *Context
+ )
+{
+ UINTN Index;
+ BM_COLLECT_KEY_OPTIONS_PARAM *Param;
+ VOID *KeyOption;
+ UINT16 OptionNumber;
+ UINTN KeyOptionSize;
+
+ Param = (BM_COLLECT_KEY_OPTIONS_PARAM *) Context;
+
+ if (BmIsKeyOptionVariable (Name, Guid, &OptionNumber)) {
+ GetEfiGlobalVariable2 (Name, &KeyOption, &KeyOptionSize);
+ ASSERT (KeyOption != NULL);
+ if (BmIsKeyOptionValid (KeyOption, KeyOptionSize)) {
+ Param->KeyOptions = ReallocatePool (
+ Param->KeyOptionCount * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
+ (Param->KeyOptionCount + 1) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION),
+ Param->KeyOptions
+ );
+ ASSERT (Param->KeyOptions != NULL);
+ //
+ // Insert the key option in order
+ //
+ for (Index = 0; Index < Param->KeyOptionCount; Index++) {
+ if (OptionNumber < Param->KeyOptions[Index].OptionNumber) {
+ break;
+ }
+ }
+ CopyMem (&Param->KeyOptions[Index + 1], &Param->KeyOptions[Index], (Param->KeyOptionCount - Index) * sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ CopyMem (&Param->KeyOptions[Index], KeyOption, KeyOptionSize);
+ Param->KeyOptions[Index].OptionNumber = OptionNumber;
+ Param->KeyOptionCount++;
+ }
+ FreePool (KeyOption);
+ }
+}
+
+/**
+ Return the array of key options.
+
+ @param Count Return the number of key options.
+
+ @retval NULL No key option.
+ @retval Other Pointer to the key options.
+**/
+EFI_BOOT_MANAGER_KEY_OPTION *
+BmGetKeyOptions (
+ OUT UINTN *Count
+ )
+{
+ BM_COLLECT_KEY_OPTIONS_PARAM Param;
+
+ if (Count == NULL) {
+ return NULL;
+ }
+
+ Param.KeyOptions = NULL;
+ Param.KeyOptionCount = 0;
+
+ BmForEachVariable (BmCollectKeyOptions, (VOID *) &Param);
+
+ *Count = Param.KeyOptionCount;
+
+ return Param.KeyOptions;
+}
+
+/**
+ Check whether the bit is set in the value.
+
+ @param Value The value need to be check.
+ @param Bit The bit filed need to be check.
+
+ @retval TRUE The bit is set.
+ @retval FALSE The bit is not set.
+**/
+BOOLEAN
+BmBitSet (
+ IN UINT32 Value,
+ IN UINT32 Bit
+ )
+{
+ return (BOOLEAN) ((Value & Bit) != 0);
+}
+
+/**
+ Initialize the KeyData and Key[] in the EFI_BOOT_MANAGER_KEY_OPTION.
+
+ @param Modifier Input key info.
+ @param Args Va_list info.
+ @param KeyOption Key info which need to update.
+
+ @retval EFI_SUCCESS Succeed to initialize the KeyData and Key[].
+ @return EFI_INVALID_PARAMETER Input parameter error.
+**/
+EFI_STATUS
+BmInitializeKeyFields (
+ IN UINT32 Modifier,
+ IN VA_LIST Args,
+ OUT EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
+ )
+{
+ EFI_INPUT_KEY *Key;
+
+ if (KeyOption == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Key = NULL;
+ while (KeyOption->KeyData.Options.InputKeyCount < sizeof (KeyOption->Keys) / sizeof (KeyOption->Keys[0])) {
+ Key = VA_ARG (Args, EFI_INPUT_KEY *);
+ if (Key == NULL) {
+ break;
+ }
+ CopyMem (
+ &KeyOption->Keys[KeyOption->KeyData.Options.InputKeyCount],
+ Key,
+ sizeof (EFI_INPUT_KEY)
+ );
+ KeyOption->KeyData.Options.InputKeyCount++;
+ }
+
+ if (Key != NULL) {
+ //
+ // Too many keys
+ //
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((Modifier & ~(EFI_BOOT_MANAGER_SHIFT_PRESSED
+ | EFI_BOOT_MANAGER_CONTROL_PRESSED
+ | EFI_BOOT_MANAGER_ALT_PRESSED
+ | EFI_BOOT_MANAGER_LOGO_PRESSED
+ | EFI_BOOT_MANAGER_MENU_KEY_PRESSED
+ | EFI_BOOT_MANAGER_SYS_REQ_PRESSED
+ )) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SHIFT_PRESSED)) {
+ KeyOption->KeyData.Options.ShiftPressed = 1;
+ }
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_CONTROL_PRESSED)) {
+ KeyOption->KeyData.Options.ControlPressed = 1;
+ }
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_ALT_PRESSED)) {
+ KeyOption->KeyData.Options.AltPressed = 1;
+ }
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_LOGO_PRESSED)) {
+ KeyOption->KeyData.Options.LogoPressed = 1;
+ }
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_MENU_KEY_PRESSED)) {
+ KeyOption->KeyData.Options.MenuPressed = 1;
+ }
+ if (BmBitSet (Modifier, EFI_BOOT_MANAGER_SYS_REQ_PRESSED)) {
+ KeyOption->KeyData.Options.SysReqPressed = 1;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Try to boot the boot option triggered by hot key.
+**/
+VOID
+EFIAPI
+EfiBootManagerHotkeyBoot (
+ VOID
+ )
+{
+ if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
+ EfiBootManagerBoot (&mBmHotkeyBootOption);
+ EfiBootManagerFreeLoadOption (&mBmHotkeyBootOption);
+ mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
+ }
+}
+
+/**
+ This is the common notification function for HotKeys, it will be registered
+ with SimpleTextInEx protocol interface - RegisterKeyNotify() of ConIn handle.
+
+ @param KeyData A pointer to a buffer that is filled in with the keystroke
+ information for the key that was pressed.
+
+ @retval EFI_SUCCESS KeyData is successfully processed.
+ @return EFI_NOT_FOUND Fail to find boot option variable.
+**/
+EFI_STATUS
+EFIAPI
+BmHotkeyCallback (
+ IN EFI_KEY_DATA *KeyData
+)
+{
+ LIST_ENTRY *Link;
+ BM_HOTKEY *Hotkey;
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];
+ EFI_STATUS Status;
+ EFI_KEY_DATA *HotkeyData;
+
+ if (mBmHotkeyBootOption.OptionNumber != LoadOptionNumberUnassigned) {
+ //
+ // Do not process sequential hotkey stroke until the current boot option returns
+ //
+ return EFI_SUCCESS;
+ }
+
+ DEBUG ((EFI_D_INFO, "[Bds]BmHotkeyCallback: %04x:%04x\n", KeyData->Key.ScanCode, KeyData->Key.UnicodeChar));
+
+ EfiAcquireLock (&mBmHotkeyLock);
+ for ( Link = GetFirstNode (&mBmHotkeyList)
+ ; !IsNull (&mBmHotkeyList, Link)
+ ; Link = GetNextNode (&mBmHotkeyList, Link)
+ ) {
+ Hotkey = BM_HOTKEY_FROM_LINK (Link);
+
+ //
+ // Is this Key Stroke we are waiting for?
+ //
+ ASSERT (Hotkey->WaitingKey < (sizeof (Hotkey->KeyData) / sizeof (Hotkey->KeyData[0])));
+ HotkeyData = &Hotkey->KeyData[Hotkey->WaitingKey];
+ if ((KeyData->Key.ScanCode == HotkeyData->Key.ScanCode) &&
+ (KeyData->Key.UnicodeChar == HotkeyData->Key.UnicodeChar) &&
+ (((KeyData->KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) != 0) ?
+ (KeyData->KeyState.KeyShiftState == HotkeyData->KeyState.KeyShiftState) : TRUE
+ )
+ ) {
+
+ //
+ // Receive an expecting key stroke, transit to next waiting state
+ //
+ Hotkey->WaitingKey++;
+
+ if (Hotkey->WaitingKey == Hotkey->CodeCount) {
+ //
+ // Reset to initial waiting state
+ //
+ Hotkey->WaitingKey = 0;
+ //
+ // Received the whole key stroke sequence
+ //
+ Status = gBS->SignalEvent (mBmHotkeyTriggered);
+ ASSERT_EFI_ERROR (Status);
+
+ if (!Hotkey->IsContinue) {
+ //
+ // Launch its BootOption
+ //
+ UnicodeSPrint (
+ OptionName, sizeof (OptionName), L"%s%04x",
+ mBmLoadOptionName[LoadOptionTypeBoot], Hotkey->BootOption
+ );
+ Status = EfiBootManagerVariableToLoadOption (OptionName, &mBmHotkeyBootOption);
+ DEBUG ((EFI_D_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));
+ if (EFI_ERROR (Status)) {
+ mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
+ }
+ } else {
+ DEBUG ((EFI_D_INFO, "[Bds]Continue key pressed!\n"));
+ }
+ }
+ } else {
+ //
+ // Receive an unexpected key stroke, reset to initial waiting state
+ //
+ Hotkey->WaitingKey = 0;
+ }
+
+ }
+ EfiReleaseLock (&mBmHotkeyLock);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return the active Simple Text Input Ex handle array.
+ If the SystemTable.ConsoleInHandle is NULL, the function returns all
+ founded Simple Text Input Ex handles.
+ Otherwise, it just returns the ConsoleInHandle.
+
+ @param Count Return the handle count.
+
+ @retval The active console handles.
+**/
+EFI_HANDLE *
+BmGetActiveConsoleIn (
+ OUT UINTN *Count
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE *Handles;
+
+ Handles = NULL;
+ *Count = 0;
+
+ if (gST->ConsoleInHandle != NULL) {
+ Status = gBS->OpenProtocol (
+ gST->ConsoleInHandle,
+ &gEfiSimpleTextInputExProtocolGuid,
+ NULL,
+ gImageHandle,
+ NULL,
+ EFI_OPEN_PROTOCOL_TEST_PROTOCOL
+ );
+ if (!EFI_ERROR (Status)) {
+ Handles = AllocateCopyPool (sizeof (EFI_HANDLE), &gST->ConsoleInHandle);
+ if (Handles != NULL) {
+ *Count = 1;
+ }
+ }
+ } else {
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiSimpleTextInputExProtocolGuid,
+ NULL,
+ Count,
+ &Handles
+ );
+ }
+
+ return Handles;
+}
+
+/**
+ Unregister hotkey notify list.
+
+ @param Hotkey Hotkey list.
+
+ @retval EFI_SUCCESS Unregister hotkey notify success.
+ @retval Others Unregister hotkey notify failed.
+**/
+EFI_STATUS
+BmUnregisterHotkeyNotify (
+ IN BM_HOTKEY *Hotkey
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINTN KeyIndex;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
+ VOID *NotifyHandle;
+
+ Handles = BmGetActiveConsoleIn (&HandleCount);
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (Handles[Index], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
+ ASSERT_EFI_ERROR (Status);
+ for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
+ Status = TxtInEx->RegisterKeyNotify (
+ TxtInEx,
+ &Hotkey->KeyData[KeyIndex],
+ BmHotkeyCallback,
+ &NotifyHandle
+ );
+ if (!EFI_ERROR (Status)) {
+ Status = TxtInEx->UnregisterKeyNotify (TxtInEx, NotifyHandle);
+ DEBUG ((EFI_D_INFO, "[Bds]UnregisterKeyNotify: %04x/%04x %r\n", Hotkey->KeyData[KeyIndex].Key.ScanCode, Hotkey->KeyData[KeyIndex].Key.UnicodeChar, Status));
+ }
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Register hotkey notify list.
+
+ @param TxtInEx Pointer to EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL protocol.
+ @param Hotkey Hotkey list.
+
+ @retval EFI_SUCCESS Register hotkey notify success.
+ @retval Others Register hotkey notify failed.
+**/
+EFI_STATUS
+BmRegisterHotkeyNotify (
+ IN EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx,
+ IN BM_HOTKEY *Hotkey
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ VOID *NotifyHandle;
+
+ for (Index = 0; Index < Hotkey->CodeCount; Index++) {
+ Status = TxtInEx->RegisterKeyNotify (
+ TxtInEx,
+ &Hotkey->KeyData[Index],
+ BmHotkeyCallback,
+ &NotifyHandle
+ );
+ DEBUG ((
+ EFI_D_INFO,
+ "[Bds]RegisterKeyNotify: %04x/%04x %08x/%02x %r\n",
+ Hotkey->KeyData[Index].Key.ScanCode,
+ Hotkey->KeyData[Index].Key.UnicodeChar,
+ Hotkey->KeyData[Index].KeyState.KeyShiftState,
+ Hotkey->KeyData[Index].KeyState.KeyToggleState,
+ Status
+ ));
+ if (EFI_ERROR (Status)) {
+ //
+ // some of the hotkey registry failed
+ // do not unregister all in case we have both CTRL-ALT-P and CTRL-ALT-P-R
+ //
+ break;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Generate key shift state base on the input key option info.
+
+ @param Depth Which key is checked.
+ @param KeyOption Input key option info.
+ @param KeyShiftState Input key shift state.
+ @param KeyShiftStates Return possible key shift state array.
+ @param KeyShiftStateCount Possible key shift state count.
+**/
+VOID
+BmGenerateKeyShiftState (
+ IN UINTN Depth,
+ IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption,
+ IN UINT32 KeyShiftState,
+ IN UINT32 *KeyShiftStates,
+ IN UINTN *KeyShiftStateCount
+ )
+{
+ switch (Depth) {
+ case 0:
+ if (KeyOption->KeyData.Options.ShiftPressed) {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_SHIFT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ } else {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+ }
+ break;
+
+ case 1:
+ if (KeyOption->KeyData.Options.ControlPressed) {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_CONTROL_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ } else {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+ }
+ break;
+
+ case 2:
+ if (KeyOption->KeyData.Options.AltPressed) {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_ALT_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ } else {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+ }
+ break;
+ case 3:
+ if (KeyOption->KeyData.Options.LogoPressed) {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_RIGHT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState | EFI_LEFT_LOGO_PRESSED, KeyShiftStates, KeyShiftStateCount);
+ } else {
+ BmGenerateKeyShiftState (Depth + 1, KeyOption, KeyShiftState, KeyShiftStates, KeyShiftStateCount);
+ }
+ break;
+ case 4:
+ if (KeyOption->KeyData.Options.MenuPressed) {
+ KeyShiftState |= EFI_MENU_KEY_PRESSED;
+ }
+ if (KeyOption->KeyData.Options.SysReqPressed) {
+ KeyShiftState |= EFI_SYS_REQ_PRESSED;
+ }
+ KeyShiftStates[*KeyShiftStateCount] = KeyShiftState;
+ (*KeyShiftStateCount)++;
+ break;
+ }
+}
+
+/**
+ Add it to hot key database, register it to existing TxtInEx.
+ New TxtInEx will be automatically registered with all the hot key in dababase
+
+ @param KeyOption Input key option info.
+**/
+EFI_STATUS
+BmProcessKeyOption (
+ IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOption
+ )
+{
+ EFI_STATUS Status;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
+ EFI_HANDLE *Handles;
+ UINTN HandleCount;
+ UINTN HandleIndex;
+ UINTN Index;
+ BM_HOTKEY *Hotkey;
+ UINTN KeyIndex;
+ //
+ // 16 is enough to enumerate all the possible combination of LEFT_XXX and RIGHT_XXX
+ //
+ UINT32 KeyShiftStates[16];
+ UINTN KeyShiftStateCount;
+
+ if (KeyOption->KeyData.Options.InputKeyCount > mBmHotkeySupportCount) {
+ return EFI_UNSUPPORTED;
+ }
+
+ KeyShiftStateCount = 0;
+ BmGenerateKeyShiftState (0, KeyOption, EFI_SHIFT_STATE_VALID, KeyShiftStates, &KeyShiftStateCount);
+ ASSERT (KeyShiftStateCount <= ARRAY_SIZE (KeyShiftStates));
+
+ EfiAcquireLock (&mBmHotkeyLock);
+
+ Handles = BmGetActiveConsoleIn (&HandleCount);
+
+ for (Index = 0; Index < KeyShiftStateCount; Index++) {
+ Hotkey = AllocateZeroPool (sizeof (BM_HOTKEY));
+ ASSERT (Hotkey != NULL);
+
+ Hotkey->Signature = BM_HOTKEY_SIGNATURE;
+ Hotkey->BootOption = KeyOption->BootOption;
+ Hotkey->IsContinue = (BOOLEAN) (KeyOption == mBmContinueKeyOption);
+ Hotkey->CodeCount = (UINT8) KeyOption->KeyData.Options.InputKeyCount;
+
+ for (KeyIndex = 0; KeyIndex < Hotkey->CodeCount; KeyIndex++) {
+ CopyMem (&Hotkey->KeyData[KeyIndex].Key, &KeyOption->Keys[KeyIndex], sizeof (EFI_INPUT_KEY));
+ Hotkey->KeyData[KeyIndex].KeyState.KeyShiftState = KeyShiftStates[Index];
+ }
+ InsertTailList (&mBmHotkeyList, &Hotkey->Link);
+
+ for (HandleIndex = 0; HandleIndex < HandleCount; HandleIndex++) {
+ Status = gBS->HandleProtocol (Handles[HandleIndex], &gEfiSimpleTextInputExProtocolGuid, (VOID **) &TxtInEx);
+ ASSERT_EFI_ERROR (Status);
+ BmRegisterHotkeyNotify (TxtInEx, Hotkey);
+ }
+ }
+
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+ EfiReleaseLock (&mBmHotkeyLock);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Callback function for SimpleTextInEx protocol install events
+
+ @param Event the event that is signaled.
+ @param Context not used here.
+
+**/
+VOID
+EFIAPI
+BmTxtInExCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ UINTN BufferSize;
+ EFI_HANDLE Handle;
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *TxtInEx;
+ LIST_ENTRY *Link;
+
+ while (TRUE) {
+ BufferSize = sizeof (EFI_HANDLE);
+ Status = gBS->LocateHandle (
+ ByRegisterNotify,
+ NULL,
+ mBmTxtInExRegistration,
+ &BufferSize,
+ &Handle
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // If no more notification events exist
+ //
+ return ;
+ }
+
+ Status = gBS->HandleProtocol (
+ Handle,
+ &gEfiSimpleTextInputExProtocolGuid,
+ (VOID **) &TxtInEx
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Register the hot key notification for the existing items in the list
+ //
+ EfiAcquireLock (&mBmHotkeyLock);
+ for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); Link = GetNextNode (&mBmHotkeyList, Link)) {
+ BmRegisterHotkeyNotify (TxtInEx, BM_HOTKEY_FROM_LINK (Link));
+ }
+ EfiReleaseLock (&mBmHotkeyLock);
+ }
+}
+
+/**
+ Free the key options returned from BmGetKeyOptions.
+
+ @param KeyOptions Pointer to the key options.
+ @param KeyOptionCount Number of the key options.
+
+ @retval EFI_SUCCESS The key options are freed.
+ @retval EFI_NOT_FOUND KeyOptions is NULL.
+**/
+EFI_STATUS
+BmFreeKeyOptions (
+ IN EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions,
+ IN UINTN KeyOptionCount
+ )
+{
+ if (KeyOptions != NULL) {
+ FreePool (KeyOptions);
+ return EFI_SUCCESS;
+ } else {
+ return EFI_NOT_FOUND;
+ }
+}
+
+/**
+ Register the key option to exit the waiting of the Boot Manager timeout.
+ Platform should ensure that the continue key option isn't conflict with
+ other boot key options.
+
+ @param Modifier Key shift state.
+ @param ... Parameter list of pointer of EFI_INPUT_KEY.
+
+ @retval EFI_SUCCESS Successfully register the continue key option.
+ @retval EFI_ALREADY_STARTED The continue key option is already registered.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerRegisterContinueKeyOption (
+ IN UINT32 Modifier,
+ ...
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
+ VA_LIST Args;
+
+ if (mBmContinueKeyOption != NULL) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ VA_START (Args, Modifier);
+ Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+ VA_END (Args);
+
+ if (!EFI_ERROR (Status)) {
+ mBmContinueKeyOption = AllocateCopyPool (sizeof (EFI_BOOT_MANAGER_KEY_OPTION), &KeyOption);
+ ASSERT (mBmContinueKeyOption != NULL);
+ if (mBmHotkeyServiceStarted) {
+ BmProcessKeyOption (mBmContinueKeyOption);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Stop the hotkey processing.
+
+ @param Event Event pointer related to hotkey service.
+ @param Context Context pass to this function.
+**/
+VOID
+EFIAPI
+BmStopHotkeyService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ LIST_ENTRY *Link;
+ BM_HOTKEY *Hotkey;
+
+ DEBUG ((EFI_D_INFO, "[Bds]Stop Hotkey Service!\n"));
+ gBS->CloseEvent (Event);
+
+ EfiAcquireLock (&mBmHotkeyLock);
+ for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
+ Hotkey = BM_HOTKEY_FROM_LINK (Link);
+ BmUnregisterHotkeyNotify (Hotkey);
+ Link = RemoveEntryList (Link);
+ FreePool (Hotkey);
+ }
+ EfiReleaseLock (&mBmHotkeyLock);
+}
+
+/**
+ Start the hot key service so that the key press can trigger the boot option.
+
+ @param HotkeyTriggered Return the waitable event and it will be signaled
+ when a valid hot key is pressed.
+
+ @retval EFI_SUCCESS The hot key service is started.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerStartHotkeyService (
+ IN EFI_EVENT *HotkeyTriggered
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
+ UINTN KeyOptionCount;
+ UINTN Index;
+ EFI_EVENT Event;
+ UINT32 *BootOptionSupport;
+
+ GetEfiGlobalVariable2 (EFI_BOOT_OPTION_SUPPORT_VARIABLE_NAME, (VOID **) &BootOptionSupport, NULL);
+ if (BootOptionSupport != NULL) {
+ if ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_KEY) != 0) {
+ mBmHotkeySupportCount = ((*BootOptionSupport & EFI_BOOT_OPTION_SUPPORT_COUNT) >> LowBitSet32 (EFI_BOOT_OPTION_SUPPORT_COUNT));
+ }
+ FreePool (BootOptionSupport);
+ }
+
+ if (mBmHotkeySupportCount == 0) {
+ DEBUG ((EFI_D_INFO, "Bds: BootOptionSupport NV variable forbids starting the hotkey service.\n"));
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = gBS->CreateEvent (
+ EVT_NOTIFY_WAIT,
+ TPL_CALLBACK,
+ EfiEventEmptyFunction,
+ NULL,
+ &mBmHotkeyTriggered
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ if (HotkeyTriggered != NULL) {
+ *HotkeyTriggered = mBmHotkeyTriggered;
+ }
+
+ KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+ for (Index = 0; Index < KeyOptionCount; Index ++) {
+ BmProcessKeyOption (&KeyOptions[Index]);
+ }
+ BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+ if (mBmContinueKeyOption != NULL) {
+ BmProcessKeyOption (mBmContinueKeyOption);
+ }
+
+ //
+ // Hook hotkey on every future SimpleTextInputEx instance when
+ // SystemTable.ConsoleInHandle == NULL, which means the console
+ // manager (ConSplitter) is absent.
+ //
+ if (gST->ConsoleInHandle == NULL) {
+ EfiCreateProtocolNotifyEvent (
+ &gEfiSimpleTextInputExProtocolGuid,
+ TPL_CALLBACK,
+ BmTxtInExCallback,
+ NULL,
+ &mBmTxtInExRegistration
+ );
+ }
+
+ Status = EfiCreateEventReadyToBootEx (
+ TPL_CALLBACK,
+ BmStopHotkeyService,
+ NULL,
+ &Event
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ mBmHotkeyServiceStarted = TRUE;
+ return Status;
+}
+
+/**
+ Add the key option.
+ It adds the key option variable and the key option takes affect immediately.
+
+ @param AddedOption Return the added key option.
+ @param BootOptionNumber The boot option number for the key option.
+ @param Modifier Key shift state.
+ @param ... Parameter list of pointer of EFI_INPUT_KEY.
+
+ @retval EFI_SUCCESS The key option is added.
+ @retval EFI_ALREADY_STARTED The hot key is already used by certain key option.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddKeyOptionVariable (
+ OUT EFI_BOOT_MANAGER_KEY_OPTION *AddedOption, OPTIONAL
+ IN UINT16 BootOptionNumber,
+ IN UINT32 Modifier,
+ ...
+ )
+{
+ EFI_STATUS Status;
+ VA_LIST Args;
+ VOID *BootOption;
+ UINTN BootOptionSize;
+ CHAR16 BootOptionName[BM_OPTION_NAME_LEN];
+ EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
+ EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
+ UINTN KeyOptionCount;
+ UINTN Index;
+ UINTN KeyOptionNumber;
+ CHAR16 KeyOptionName[sizeof ("Key####")];
+
+ UnicodeSPrint (
+ BootOptionName, sizeof (BootOptionName), L"%s%04x",
+ mBmLoadOptionName[LoadOptionTypeBoot], BootOptionNumber
+ );
+ GetEfiGlobalVariable2 (BootOptionName, &BootOption, &BootOptionSize);
+
+ if (BootOption == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ KeyOption.BootOption = BootOptionNumber;
+ Status = gBS->CalculateCrc32 (BootOption, BootOptionSize, &KeyOption.BootOptionCrc);
+ ASSERT_EFI_ERROR (Status);
+ FreePool (BootOption);
+
+ VA_START (Args, Modifier);
+ Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+ VA_END (Args);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ KeyOptionNumber = LoadOptionNumberUnassigned;
+ //
+ // Check if the hot key sequence was defined already
+ //
+ KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+ for (Index = 0; Index < KeyOptionCount; Index++) {
+ if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
+ (CompareMem (KeyOptions[Index].Keys, KeyOption.Keys, KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)) {
+ break;
+ }
+
+ if ((KeyOptionNumber == LoadOptionNumberUnassigned) &&
+ (KeyOptions[Index].OptionNumber > Index)
+ ){
+ KeyOptionNumber = Index;
+ }
+ }
+ BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+ if (Index < KeyOptionCount) {
+ return EFI_ALREADY_STARTED;
+ }
+
+ if (KeyOptionNumber == LoadOptionNumberUnassigned) {
+ KeyOptionNumber = KeyOptionCount;
+ }
+
+ UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptionNumber);
+
+ Status = gRT->SetVariable (
+ KeyOptionName,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ BmSizeOfKeyOption (&KeyOption),
+ &KeyOption
+ );
+ if (!EFI_ERROR (Status)) {
+ //
+ // Return the Key Option in case needed by caller
+ //
+ if (AddedOption != NULL) {
+ CopyMem (AddedOption, &KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ }
+
+ //
+ // Register the newly added hot key
+ // Calling this function before EfiBootManagerStartHotkeyService doesn't
+ // need to call BmProcessKeyOption
+ //
+ if (mBmHotkeyServiceStarted) {
+ BmProcessKeyOption (&KeyOption);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Delete the Key Option variable and unregister the hot key
+
+ @param DeletedOption Return the deleted key options.
+ @param Modifier Key shift state.
+ @param ... Parameter list of pointer of EFI_INPUT_KEY.
+
+ @retval EFI_SUCCESS The key option is deleted.
+ @retval EFI_NOT_FOUND The key option cannot be found.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteKeyOptionVariable (
+ IN EFI_BOOT_MANAGER_KEY_OPTION *DeletedOption, OPTIONAL
+ IN UINT32 Modifier,
+ ...
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ VA_LIST Args;
+ EFI_BOOT_MANAGER_KEY_OPTION KeyOption;
+ EFI_BOOT_MANAGER_KEY_OPTION *KeyOptions;
+ UINTN KeyOptionCount;
+ LIST_ENTRY *Link;
+ BM_HOTKEY *Hotkey;
+ UINT32 ShiftState;
+ BOOLEAN Match;
+ CHAR16 KeyOptionName[sizeof ("Key####")];
+
+ ZeroMem (&KeyOption, sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ VA_START (Args, Modifier);
+ Status = BmInitializeKeyFields (Modifier, Args, &KeyOption);
+ VA_END (Args);
+
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ EfiAcquireLock (&mBmHotkeyLock);
+ //
+ // Delete the key option from active hot key list
+ // Could have multiple entries when modifier isn't 0 because we map the ShiftPressed to RIGHT_SHIFT and RIGHT_SHIFT
+ //
+ for (Link = GetFirstNode (&mBmHotkeyList); !IsNull (&mBmHotkeyList, Link); ) {
+ Hotkey = BM_HOTKEY_FROM_LINK (Link);
+ Match = (BOOLEAN) (Hotkey->CodeCount == KeyOption.KeyData.Options.InputKeyCount);
+
+ for (Index = 0; Match && (Index < Hotkey->CodeCount); Index++) {
+ ShiftState = Hotkey->KeyData[Index].KeyState.KeyShiftState;
+ if (
+ (BmBitSet (ShiftState, EFI_RIGHT_SHIFT_PRESSED | EFI_LEFT_SHIFT_PRESSED) != KeyOption.KeyData.Options.ShiftPressed) ||
+ (BmBitSet (ShiftState, EFI_RIGHT_CONTROL_PRESSED | EFI_LEFT_CONTROL_PRESSED) != KeyOption.KeyData.Options.ControlPressed) ||
+ (BmBitSet (ShiftState, EFI_RIGHT_ALT_PRESSED | EFI_LEFT_ALT_PRESSED) != KeyOption.KeyData.Options.AltPressed) ||
+ (BmBitSet (ShiftState, EFI_RIGHT_LOGO_PRESSED | EFI_LEFT_LOGO_PRESSED) != KeyOption.KeyData.Options.LogoPressed) ||
+ (BmBitSet (ShiftState, EFI_MENU_KEY_PRESSED) != KeyOption.KeyData.Options.MenuPressed) ||
+ (BmBitSet (ShiftState, EFI_SYS_REQ_PRESSED) != KeyOption.KeyData.Options.SysReqPressed) ||
+ (CompareMem (&Hotkey->KeyData[Index].Key, &KeyOption.Keys[Index], sizeof (EFI_INPUT_KEY)) != 0)
+ ) {
+ //
+ // Break when any field doesn't match
+ //
+ Match = FALSE;
+ break;
+ }
+ }
+
+ if (Match) {
+ Link = RemoveEntryList (Link);
+ FreePool (Hotkey);
+ } else {
+ Link = GetNextNode (&mBmHotkeyList, Link);
+ }
+ }
+
+ //
+ // Delete the key option from the variable
+ //
+ Status = EFI_NOT_FOUND;
+ KeyOptions = BmGetKeyOptions (&KeyOptionCount);
+ for (Index = 0; Index < KeyOptionCount; Index++) {
+ if ((KeyOptions[Index].KeyData.PackedValue == KeyOption.KeyData.PackedValue) &&
+ (CompareMem (
+ KeyOptions[Index].Keys, KeyOption.Keys,
+ KeyOption.KeyData.Options.InputKeyCount * sizeof (EFI_INPUT_KEY)) == 0)
+ ) {
+ UnicodeSPrint (KeyOptionName, sizeof (KeyOptionName), L"Key%04x", KeyOptions[Index].OptionNumber);
+ Status = gRT->SetVariable (
+ KeyOptionName,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ 0,
+ NULL
+ );
+ //
+ // Return the deleted key option in case needed by caller
+ //
+ if (DeletedOption != NULL) {
+ CopyMem (DeletedOption, &KeyOptions[Index], sizeof (EFI_BOOT_MANAGER_KEY_OPTION));
+ }
+ break;
+ }
+ }
+ BmFreeKeyOptions (KeyOptions, KeyOptionCount);
+
+ EfiReleaseLock (&mBmHotkeyLock);
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
new file mode 100644
index 00000000..dd17ee43
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
@@ -0,0 +1,1469 @@
+/** @file
+ Load option library functions which relate with creating and processing load options.
+
+Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+ CHAR16 *mBmLoadOptionName[] = {
+ L"Driver",
+ L"SysPrep",
+ L"Boot",
+ L"PlatformRecovery"
+ };
+
+GLOBAL_REMOVE_IF_UNREFERENCED
+ CHAR16 *mBmLoadOptionOrderName[] = {
+ EFI_DRIVER_ORDER_VARIABLE_NAME,
+ EFI_SYS_PREP_ORDER_VARIABLE_NAME,
+ EFI_BOOT_ORDER_VARIABLE_NAME,
+ NULL // PlatformRecovery#### doesn't have associated *Order variable
+ };
+
+/**
+ Call Visitor function for each variable in variable storage.
+
+ @param Visitor Visitor function.
+ @param Context The context passed to Visitor function.
+**/
+VOID
+BmForEachVariable (
+ BM_VARIABLE_VISITOR Visitor,
+ VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ CHAR16 *Name;
+ EFI_GUID Guid;
+ UINTN NameSize;
+ UINTN NewNameSize;
+
+ NameSize = sizeof (CHAR16);
+ Name = AllocateZeroPool (NameSize);
+ ASSERT (Name != NULL);
+ while (TRUE) {
+ NewNameSize = NameSize;
+ Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ Name = ReallocatePool (NameSize, NewNameSize, Name);
+ ASSERT (Name != NULL);
+ Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid);
+ NameSize = NewNameSize;
+ }
+
+ if (Status == EFI_NOT_FOUND) {
+ break;
+ }
+ ASSERT_EFI_ERROR (Status);
+
+ Visitor (Name, &Guid, Context);
+ }
+
+ FreePool (Name);
+}
+
+/**
+ Get the Option Number that wasn't used.
+
+ @param LoadOptionType The load option type.
+ @param FreeOptionNumber Return the minimal free option number.
+
+ @retval EFI_SUCCESS The option number is found and will be returned.
+ @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used.
+ @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL
+
+**/
+EFI_STATUS
+BmGetFreeOptionNumber (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType,
+ OUT UINT16 *FreeOptionNumber
+ )
+{
+
+ UINTN OptionNumber;
+ UINTN Index;
+ UINT16 *OptionOrder;
+ UINTN OptionOrderSize;
+ UINT16 *BootNext;
+
+ ASSERT (FreeOptionNumber != NULL);
+ ASSERT (LoadOptionType == LoadOptionTypeDriver ||
+ LoadOptionType == LoadOptionTypeBoot ||
+ LoadOptionType == LoadOptionTypeSysPrep);
+
+ GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **) &OptionOrder, &OptionOrderSize);
+ ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
+
+ BootNext = NULL;
+ if (LoadOptionType == LoadOptionTypeBoot) {
+ GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL);
+ }
+
+ for (OptionNumber = 0;
+ OptionNumber < OptionOrderSize / sizeof (UINT16)
+ + ((BootNext != NULL) ? 1 : 0);
+ OptionNumber++
+ ) {
+ //
+ // Search in OptionOrder whether the OptionNumber exists
+ //
+ for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+ if (OptionNumber == OptionOrder[Index]) {
+ break;
+ }
+ }
+
+ //
+ // We didn't find it in the ****Order array and it doesn't equal to BootNext
+ // Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1
+ //
+ if ((Index == OptionOrderSize / sizeof (UINT16)) &&
+ ((BootNext == NULL) || (OptionNumber != *BootNext))
+ ) {
+ break;
+ }
+ }
+ if (OptionOrder != NULL) {
+ FreePool (OptionOrder);
+ }
+
+ if (BootNext != NULL) {
+ FreePool (BootNext);
+ }
+
+ //
+ // When BootOrder & BootNext conver all numbers in the range [0 ... 0xffff],
+ // OptionNumber equals to 0x10000 which is not valid.
+ //
+ ASSERT (OptionNumber <= 0x10000);
+ if (OptionNumber == 0x10000) {
+ return EFI_OUT_OF_RESOURCES;
+ } else {
+ *FreeOptionNumber = (UINT16) OptionNumber;
+ return EFI_SUCCESS;
+ }
+}
+
+/**
+ Create the Boot####, Driver####, SysPrep####, PlatformRecovery#### variable
+ from the load option.
+
+ @param LoadOption Pointer to the load option.
+
+ @retval EFI_SUCCESS The variable was created.
+ @retval Others Error status returned by RT->SetVariable.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerLoadOptionToVariable (
+ IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Option
+ )
+{
+ EFI_STATUS Status;
+ UINTN VariableSize;
+ UINT8 *Variable;
+ UINT8 *Ptr;
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];
+ CHAR16 *Description;
+ CHAR16 NullChar;
+ EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
+ UINT32 VariableAttributes;
+
+ if ((Option->OptionNumber == LoadOptionNumberUnassigned) ||
+ (Option->FilePath == NULL) ||
+ ((UINT32) Option->OptionType >= LoadOptionTypeMax)
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Convert NULL description to empty description
+ //
+ NullChar = L'\0';
+ Description = Option->Description;
+ if (Description == NULL) {
+ Description = &NullChar;
+ }
+
+ /*
+ UINT32 Attributes;
+ UINT16 FilePathListLength;
+ CHAR16 Description[];
+ EFI_DEVICE_PATH_PROTOCOL FilePathList[];
+ UINT8 OptionalData[];
+TODO: FilePathList[] IS:
+A packed array of UEFI device paths. The first element of the
+array is a device path that describes the device and location of the
+Image for this load option. The FilePathList[0] is specific
+to the device type. Other device paths may optionally exist in the
+FilePathList, but their usage is OSV specific. Each element
+in the array is variable length, and ends at the device path end
+structure.
+ */
+ VariableSize = sizeof (Option->Attributes)
+ + sizeof (UINT16)
+ + StrSize (Description)
+ + GetDevicePathSize (Option->FilePath)
+ + Option->OptionalDataSize;
+
+ Variable = AllocatePool (VariableSize);
+ ASSERT (Variable != NULL);
+
+ Ptr = Variable;
+ WriteUnaligned32 ((UINT32 *) Ptr, Option->Attributes);
+ Ptr += sizeof (Option->Attributes);
+
+ WriteUnaligned16 ((UINT16 *) Ptr, (UINT16) GetDevicePathSize (Option->FilePath));
+ Ptr += sizeof (UINT16);
+
+ CopyMem (Ptr, Description, StrSize (Description));
+ Ptr += StrSize (Description);
+
+ CopyMem (Ptr, Option->FilePath, GetDevicePathSize (Option->FilePath));
+ Ptr += GetDevicePathSize (Option->FilePath);
+
+ CopyMem (Ptr, Option->OptionalData, Option->OptionalDataSize);
+
+ UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[Option->OptionType], Option->OptionNumber);
+
+ VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE;
+ if (Option->OptionType == LoadOptionTypePlatformRecovery) {
+ //
+ // Lock the PlatformRecovery####
+ //
+ Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
+ if (!EFI_ERROR (Status)) {
+ Status = VariableLock->RequestToLock (VariableLock, OptionName, &gEfiGlobalVariableGuid);
+ ASSERT_EFI_ERROR (Status);
+ }
+ VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;
+ }
+
+ Status = gRT->SetVariable (
+ OptionName,
+ &gEfiGlobalVariableGuid,
+ VariableAttributes,
+ VariableSize,
+ Variable
+ );
+ FreePool (Variable);
+
+ return Status;
+}
+
+/**
+ Update order variable .
+
+ @param OptionOrderName Order variable name which need to be updated.
+ @param OptionNumber Option number for the new option.
+ @param Position Position of the new load option to put in the ****Order variable.
+
+ @retval EFI_SUCCESS The boot#### or driver#### have been successfully registered.
+ @retval EFI_ALREADY_STARTED The option number of Option is being used already.
+ @retval EFI_STATUS Return the status of gRT->SetVariable ().
+
+**/
+EFI_STATUS
+BmAddOptionNumberToOrderVariable (
+ IN CHAR16 *OptionOrderName,
+ IN UINT16 OptionNumber,
+ IN UINTN Position
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+ UINT16 *OptionOrder;
+ UINT16 *NewOptionOrder;
+ UINTN OptionOrderSize;
+ //
+ // Update the option order variable
+ //
+ GetEfiGlobalVariable2 (OptionOrderName, (VOID **) &OptionOrder, &OptionOrderSize);
+ ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
+
+ Status = EFI_SUCCESS;
+ for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+ if (OptionOrder[Index] == OptionNumber) {
+ Status = EFI_ALREADY_STARTED;
+ break;
+ }
+ }
+
+ if (!EFI_ERROR (Status)) {
+ Position = MIN (Position, OptionOrderSize / sizeof (UINT16));
+
+ NewOptionOrder = AllocatePool (OptionOrderSize + sizeof (UINT16));
+ ASSERT (NewOptionOrder != NULL);
+ if (OptionOrderSize != 0) {
+ CopyMem (NewOptionOrder, OptionOrder, Position * sizeof (UINT16));
+ CopyMem (&NewOptionOrder[Position + 1], &OptionOrder[Position], OptionOrderSize - Position * sizeof (UINT16));
+ }
+ NewOptionOrder[Position] = OptionNumber;
+
+ Status = gRT->SetVariable (
+ OptionOrderName,
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ OptionOrderSize + sizeof (UINT16),
+ NewOptionOrder
+ );
+ FreePool (NewOptionOrder);
+ }
+
+ if (OptionOrder != NULL) {
+ FreePool (OptionOrder);
+ }
+
+ return Status;
+}
+
+/**
+ This function will register the new Boot####, Driver#### or SysPrep#### option.
+ After the *#### is updated, the *Order will also be updated.
+
+ @param Option Pointer to load option to add. If on input
+ Option->OptionNumber is LoadOptionNumberUnassigned,
+ then on output Option->OptionNumber is updated to
+ the number of the new Boot####,
+ Driver#### or SysPrep#### option.
+ @param Position Position of the new load option to put in the ****Order variable.
+
+ @retval EFI_SUCCESS The *#### have been successfully registered.
+ @retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF.
+ @retval EFI_ALREADY_STARTED The option number of Option is being used already.
+ Note: this API only adds new load option, no replacement support.
+ @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used when the
+ option number specified in the Option is LoadOptionNumberUnassigned.
+ @return Status codes of gRT->SetVariable ().
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerAddLoadOptionVariable (
+ IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,
+ IN UINTN Position
+ )
+{
+ EFI_STATUS Status;
+ UINT16 OptionNumber;
+
+ if (Option == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (Option->OptionType != LoadOptionTypeDriver &&
+ Option->OptionType != LoadOptionTypeSysPrep &&
+ Option->OptionType != LoadOptionTypeBoot
+ ) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the free option number if the option number is unassigned
+ //
+ if (Option->OptionNumber == LoadOptionNumberUnassigned) {
+ Status = BmGetFreeOptionNumber (Option->OptionType, &OptionNumber);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+ Option->OptionNumber = OptionNumber;
+ }
+
+ if (Option->OptionNumber >= LoadOptionNumberMax) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = BmAddOptionNumberToOrderVariable (mBmLoadOptionOrderName[Option->OptionType], (UINT16) Option->OptionNumber, Position);
+ if (!EFI_ERROR (Status)) {
+ //
+ // Save the Boot#### or Driver#### variable
+ //
+ Status = EfiBootManagerLoadOptionToVariable (Option);
+ if (EFI_ERROR (Status)) {
+ //
+ // Remove the #### from *Order variable when the Driver####/SysPrep####/Boot#### cannot be saved.
+ //
+ EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType);
+ }
+ }
+
+ return Status;
+}
+
+/**
+ Sort the load option. The DriverOrder or BootOrder will be re-created to
+ reflect the new order.
+
+ @param OptionType Load option type
+ @param CompareFunction The comparator
+**/
+VOID
+EFIAPI
+EfiBootManagerSortLoadOptionVariable (
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
+ SORT_COMPARE CompareFunction
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption;
+ UINTN LoadOptionCount;
+ UINTN Index;
+ UINT16 *OptionOrder;
+
+ LoadOption = EfiBootManagerGetLoadOptions (&LoadOptionCount, OptionType);
+
+ //
+ // Insertion sort algorithm
+ //
+ PerformQuickSort (
+ LoadOption,
+ LoadOptionCount,
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+ CompareFunction
+ );
+
+ //
+ // Create new ****Order variable
+ //
+ OptionOrder = AllocatePool (LoadOptionCount * sizeof (UINT16));
+ ASSERT (OptionOrder != NULL);
+ for (Index = 0; Index < LoadOptionCount; Index++) {
+ OptionOrder[Index] = (UINT16) LoadOption[Index].OptionNumber;
+ }
+
+ Status = gRT->SetVariable (
+ mBmLoadOptionOrderName[OptionType],
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ LoadOptionCount * sizeof (UINT16),
+ OptionOrder
+ );
+ //
+ // Changing the *Order content without increasing its size with current variable implementation shouldn't fail.
+ //
+ ASSERT_EFI_ERROR (Status);
+
+ FreePool (OptionOrder);
+ EfiBootManagerFreeLoadOptions (LoadOption, LoadOptionCount);
+}
+
+/**
+ Initialize a load option.
+
+ @param Option Pointer to the load option to be initialized.
+ @param OptionNumber Option number of the load option.
+ @param OptionType Type of the load option.
+ @param Attributes Attributes of the load option.
+ @param Description Description of the load option.
+ @param FilePath Device path of the load option.
+ @param OptionalData Optional data of the load option.
+ @param OptionalDataSize Size of the optional data of the load option.
+
+ @retval EFI_SUCCESS The load option was initialized successfully.
+ @retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerInitializeLoadOption (
+ IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,
+ IN UINTN OptionNumber,
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType,
+ IN UINT32 Attributes,
+ IN CHAR16 *Description,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN UINT8 *OptionalData, OPTIONAL
+ IN UINT32 OptionalDataSize
+ )
+{
+ if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (((OptionalData != NULL) && (OptionalDataSize == 0)) ||
+ ((OptionalData == NULL) && (OptionalDataSize != 0))) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((UINT32) OptionType >= LoadOptionTypeMax) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ Option->OptionNumber = OptionNumber;
+ Option->OptionType = OptionType;
+ Option->Attributes = Attributes;
+ Option->Description = AllocateCopyPool (StrSize (Description), Description);
+ Option->FilePath = DuplicateDevicePath (FilePath);
+ if (OptionalData != NULL) {
+ Option->OptionalData = AllocateCopyPool (OptionalDataSize, OptionalData);
+ Option->OptionalDataSize = OptionalDataSize;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ Return the index of the load option in the load option array.
+
+ The function consider two load options are equal when the
+ OptionType, Attributes, Description, FilePath and OptionalData 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
+EFIAPI
+EfiBootManagerFindLoadOption (
+ 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 ((Key->OptionType == Array[Index].OptionType) &&
+ (Key->Attributes == Array[Index].Attributes) &&
+ (StrCmp (Key->Description, Array[Index].Description) == 0) &&
+ (CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) &&
+ (Key->OptionalDataSize == Array[Index].OptionalDataSize) &&
+ (CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) {
+ return (INTN) Index;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ Delete the load option.
+
+ @param OptionNumber Indicate the option number of load option
+ @param OptionType Indicate the type of load option
+
+ @retval EFI_INVALID_PARAMETER OptionType or OptionNumber is invalid.
+ @retval EFI_NOT_FOUND The load option cannot be found
+ @retval EFI_SUCCESS The load option was deleted
+ @retval others Status of RT->SetVariable()
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDeleteLoadOptionVariable (
+ IN UINTN OptionNumber,
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType
+ )
+{
+ UINT16 *OptionOrder;
+ UINTN OptionOrderSize;
+ UINTN Index;
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];
+
+ if (((UINT32) OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (OptionType == LoadOptionTypeDriver || OptionType == LoadOptionTypeSysPrep || OptionType == LoadOptionTypeBoot) {
+ //
+ // If the associated *Order exists, firstly remove the reference in *Order for
+ // Driver####, SysPrep#### and Boot####.
+ //
+ GetEfiGlobalVariable2 (mBmLoadOptionOrderName[OptionType], (VOID **) &OptionOrder, &OptionOrderSize);
+ ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));
+
+ for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) {
+ if (OptionOrder[Index] == OptionNumber) {
+ OptionOrderSize -= sizeof (UINT16);
+ CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16));
+ gRT->SetVariable (
+ mBmLoadOptionOrderName[OptionType],
+ &gEfiGlobalVariableGuid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
+ OptionOrderSize,
+ OptionOrder
+ );
+ break;
+ }
+ }
+ if (OptionOrder != NULL) {
+ FreePool (OptionOrder);
+ }
+ }
+
+ //
+ // Remove the Driver####, SysPrep####, Boot#### or PlatformRecovery#### itself.
+ //
+ UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[OptionType], OptionNumber);
+ return gRT->SetVariable (
+ OptionName,
+ &gEfiGlobalVariableGuid,
+ 0,
+ 0,
+ NULL
+ );
+}
+
+/**
+ Returns the size of a device path in bytes.
+
+ This function returns the size, in bytes, of the device path data structure
+ specified by DevicePath including the end of device path node. If DevicePath
+ is NULL, then 0 is returned. If the length of the device path is bigger than
+ MaxSize, also return 0 to indicate this is an invalidate device path.
+
+ @param DevicePath A pointer to a device path data structure.
+ @param MaxSize Max valid device path size. If big than this size,
+ return error.
+
+ @retval 0 An invalid device path.
+ @retval Others The size of a device path in bytes.
+
+**/
+UINTN
+BmGetDevicePathSizeEx (
+ IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ IN UINTN MaxSize
+ )
+{
+ UINTN Size;
+ UINTN NodeSize;
+
+ if (DevicePath == NULL) {
+ return 0;
+ }
+
+ //
+ // Search for the end of the device path structure
+ //
+ Size = 0;
+ while (!IsDevicePathEnd (DevicePath)) {
+ NodeSize = DevicePathNodeLength (DevicePath);
+ if (NodeSize == 0) {
+ return 0;
+ }
+ Size += NodeSize;
+ if (Size > MaxSize) {
+ return 0;
+ }
+ DevicePath = NextDevicePathNode (DevicePath);
+ }
+ Size += DevicePathNodeLength (DevicePath);
+ if (Size > MaxSize) {
+ return 0;
+ }
+
+ return Size;
+}
+
+/**
+ Returns the length of a Null-terminated Unicode string. If the length is
+ bigger than MaxStringLen, return length 0 to indicate that this is an
+ invalidate string.
+
+ This function returns the number of Unicode characters in the Null-terminated
+ Unicode string specified by String.
+
+ If String is NULL, then ASSERT().
+ If String is not aligned on a 16-bit boundary, then ASSERT().
+
+ @param String A pointer to a Null-terminated Unicode string.
+ @param MaxStringLen Max string len in this string.
+
+ @retval 0 An invalid string.
+ @retval Others The length of String.
+
+**/
+UINTN
+BmStrSizeEx (
+ IN CONST CHAR16 *String,
+ IN UINTN MaxStringLen
+ )
+{
+ UINTN Length;
+
+ ASSERT (String != NULL && MaxStringLen != 0);
+ ASSERT (((UINTN) String & BIT0) == 0);
+
+ for (Length = 0; *String != L'\0' && MaxStringLen != Length; String++, Length+=2);
+
+ if (*String != L'\0' && MaxStringLen == Length) {
+ return 0;
+ }
+
+ return Length + 2;
+}
+
+/**
+ Validate the Boot####, Driver####, SysPrep#### and PlatformRecovery####
+ variable (VendorGuid/Name)
+
+ @param Variable The variable data.
+ @param VariableSize The variable size.
+
+ @retval TRUE The variable data is correct.
+ @retval FALSE The variable data is corrupted.
+
+**/
+BOOLEAN
+BmValidateOption (
+ UINT8 *Variable,
+ UINTN VariableSize
+ )
+{
+ UINT16 FilePathSize;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ UINTN DescriptionSize;
+
+ if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) {
+ return FALSE;
+ }
+
+ //
+ // Skip the option attribute
+ //
+ Variable += sizeof (UINT32);
+
+ //
+ // Get the option's device path size
+ //
+ FilePathSize = ReadUnaligned16 ((UINT16 *) Variable);
+ Variable += sizeof (UINT16);
+
+ //
+ // Get the option's description string size
+ //
+ DescriptionSize = BmStrSizeEx ((CHAR16 *) Variable, VariableSize - sizeof (UINT16) - sizeof (UINT32));
+ Variable += DescriptionSize;
+
+ //
+ // Get the option's device path
+ //
+ DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Variable;
+
+ //
+ // Validation boot option variable.
+ //
+ if ((FilePathSize == 0) || (DescriptionSize == 0)) {
+ return FALSE;
+ }
+
+ if (sizeof (UINT32) + sizeof (UINT16) + DescriptionSize + FilePathSize > VariableSize) {
+ return FALSE;
+ }
+
+ return (BOOLEAN) (BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0);
+}
+
+/**
+ Check whether the VariableName is a valid load option variable name
+ and return the load option type and option number.
+
+ @param VariableName The name of the load option variable.
+ @param OptionType Return the load option type.
+ @param OptionNumber Return the load option number.
+
+ @retval TRUE The variable name is valid; The load option type and
+ load option number is returned.
+ @retval FALSE The variable name is NOT valid.
+**/
+BOOLEAN
+EFIAPI
+EfiBootManagerIsValidLoadOptionVariableName (
+ IN CHAR16 *VariableName,
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType OPTIONAL,
+ OUT UINT16 *OptionNumber OPTIONAL
+ )
+{
+ UINTN VariableNameLen;
+ UINTN Index;
+ UINTN Uint;
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LocalOptionType;
+ UINT16 LocalOptionNumber;
+
+ if (VariableName == NULL) {
+ return FALSE;
+ }
+
+ VariableNameLen = StrLen (VariableName);
+
+ //
+ // Return FALSE when the variable name length is too small.
+ //
+ if (VariableNameLen <= 4) {
+ return FALSE;
+ }
+
+ //
+ // Return FALSE when the variable name doesn't start with Driver/SysPrep/Boot/PlatformRecovery.
+ //
+ for (LocalOptionType = 0; LocalOptionType < ARRAY_SIZE (mBmLoadOptionName); LocalOptionType++) {
+ if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[LocalOptionType])) &&
+ (StrnCmp (VariableName, mBmLoadOptionName[LocalOptionType], VariableNameLen - 4) == 0)
+ ) {
+ break;
+ }
+ }
+ if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) {
+ return FALSE;
+ }
+
+ //
+ // Return FALSE when the last four characters are not hex digits.
+ //
+ LocalOptionNumber = 0;
+ for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {
+ Uint = BmCharToUint (VariableName[Index]);
+ if (Uint == -1) {
+ break;
+ } else {
+ LocalOptionNumber = (UINT16) Uint + LocalOptionNumber * 0x10;
+ }
+ }
+ if (Index != VariableNameLen) {
+ return FALSE;
+ }
+
+ if (OptionType != NULL) {
+ *OptionType = LocalOptionType;
+ }
+
+ if (OptionNumber != NULL) {
+ *OptionNumber = LocalOptionNumber;
+ }
+
+ return TRUE;
+}
+
+/**
+ Build the Boot#### or Driver#### option from the VariableName.
+
+ @param VariableName Variable name of the load option
+ @param VendorGuid Variable GUID of the load option
+ @param Option Return the load option.
+
+ @retval EFI_SUCCESS Get the option just been created
+ @retval EFI_NOT_FOUND Failed to get the new option
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerVariableToLoadOptionEx (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
+ )
+{
+ EFI_STATUS Status;
+ UINT32 Attribute;
+ UINT16 FilePathSize;
+ UINT8 *Variable;
+ UINT8 *VariablePtr;
+ UINTN VariableSize;
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;
+ UINT8 *OptionalData;
+ UINT32 OptionalDataSize;
+ CHAR16 *Description;
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
+ UINT16 OptionNumber;
+
+ if ((VariableName == NULL) || (Option == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!EfiBootManagerIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Read the variable
+ //
+ GetVariable2 (VariableName, VendorGuid, (VOID **) &Variable, &VariableSize);
+ if (Variable == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ //
+ // Validate *#### variable data.
+ //
+ if (!BmValidateOption(Variable, VariableSize)) {
+ FreePool (Variable);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Get the option attribute
+ //
+ VariablePtr = Variable;
+ Attribute = ReadUnaligned32 ((UINT32 *) VariablePtr);
+ VariablePtr += sizeof (UINT32);
+
+ //
+ // Get the option's device path size
+ //
+ FilePathSize = ReadUnaligned16 ((UINT16 *) VariablePtr);
+ VariablePtr += sizeof (UINT16);
+
+ //
+ // Get the option's description string
+ //
+ Description = (CHAR16 *) VariablePtr;
+
+ //
+ // Get the option's description string size
+ //
+ VariablePtr += StrSize ((CHAR16 *) VariablePtr);
+
+ //
+ // Get the option's device path
+ //
+ FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr;
+ VariablePtr += FilePathSize;
+
+ OptionalDataSize = (UINT32) (VariableSize - ((UINTN) VariablePtr - (UINTN) Variable));
+ if (OptionalDataSize == 0) {
+ OptionalData = NULL;
+ } else {
+ OptionalData = VariablePtr;
+ }
+
+ Status = EfiBootManagerInitializeLoadOption (
+ Option,
+ OptionNumber,
+ OptionType,
+ Attribute,
+ Description,
+ FilePath,
+ OptionalData,
+ OptionalDataSize
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ CopyGuid (&Option->VendorGuid, VendorGuid);
+
+ FreePool (Variable);
+ return Status;
+}
+
+/**
+Build the Boot#### or Driver#### option from the VariableName.
+
+@param VariableName EFI Variable name indicate if it is Boot#### or Driver####
+@param Option Return the Boot#### or Driver#### option.
+
+@retval EFI_SUCCESS Get the option just been created
+@retval EFI_NOT_FOUND Failed to get the new option
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerVariableToLoadOption (
+ IN CHAR16 *VariableName,
+ IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option
+ )
+{
+ return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, Option);
+}
+
+typedef struct {
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
+ EFI_GUID *Guid;
+ EFI_BOOT_MANAGER_LOAD_OPTION *Options;
+ UINTN OptionCount;
+} BM_COLLECT_LOAD_OPTIONS_PARAM;
+
+/**
+ Visitor function to collect the Platform Recovery load options or OS Recovery
+ load options from NV storage.
+
+ @param Name Variable name.
+ @param Guid Variable GUID.
+ @param Context The same context passed to BmForEachVariable.
+**/
+VOID
+BmCollectLoadOptions (
+ IN CHAR16 *Name,
+ IN EFI_GUID *Guid,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;
+ UINT16 OptionNumber;
+ EFI_BOOT_MANAGER_LOAD_OPTION Option;
+ UINTN Index;
+ BM_COLLECT_LOAD_OPTIONS_PARAM *Param;
+
+ Param = (BM_COLLECT_LOAD_OPTIONS_PARAM *) Context;
+
+ if (CompareGuid (Guid, Param->Guid) && (
+ Param->OptionType == LoadOptionTypePlatformRecovery &&
+ EfiBootManagerIsValidLoadOptionVariableName (Name, &OptionType, &OptionNumber) &&
+ OptionType == LoadOptionTypePlatformRecovery
+ )) {
+ Status = EfiBootManagerVariableToLoadOptionEx (Name, Guid, &Option);
+ if (!EFI_ERROR (Status)) {
+ for (Index = 0; Index < Param->OptionCount; Index++) {
+ if (Param->Options[Index].OptionNumber > Option.OptionNumber) {
+ break;
+ }
+ }
+ Param->Options = ReallocatePool (
+ Param->OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+ (Param->OptionCount + 1) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),
+ Param->Options
+ );
+ ASSERT (Param->Options != NULL);
+ CopyMem (&Param->Options[Index + 1], &Param->Options[Index], (Param->OptionCount - Index) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ CopyMem (&Param->Options[Index], &Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ Param->OptionCount++;
+ }
+ }
+}
+
+/**
+ Returns an array of load options based on the EFI variable
+ L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.
+ #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry.
+
+ @param LoadOptionCount Returns number of entries in the array.
+ @param LoadOptionType The type of the load option.
+
+ @retval NULL No load options exist.
+ @retval !NULL Array of load option entries.
+
+**/
+EFI_BOOT_MANAGER_LOAD_OPTION *
+EFIAPI
+EfiBootManagerGetLoadOptions (
+ OUT UINTN *OptionCount,
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType
+ )
+{
+ EFI_STATUS Status;
+ UINT16 *OptionOrder;
+ UINTN OptionOrderSize;
+ UINTN Index;
+ UINTN OptionIndex;
+ EFI_BOOT_MANAGER_LOAD_OPTION *Options;
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];
+ UINT16 OptionNumber;
+ BM_COLLECT_LOAD_OPTIONS_PARAM Param;
+
+ *OptionCount = 0;
+ Options = NULL;
+
+ if (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeSysPrep || LoadOptionType == LoadOptionTypeBoot) {
+ //
+ // Read the BootOrder, or DriverOrder variable.
+ //
+ GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **) &OptionOrder, &OptionOrderSize);
+ if (OptionOrder == NULL) {
+ return NULL;
+ }
+
+ *OptionCount = OptionOrderSize / sizeof (UINT16);
+
+ Options = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));
+ ASSERT (Options != NULL);
+
+ OptionIndex = 0;
+ for (Index = 0; Index < *OptionCount; Index++) {
+ OptionNumber = OptionOrder[Index];
+ UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[LoadOptionType], OptionNumber);
+
+ Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));
+ EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType);
+ } else {
+ ASSERT (Options[OptionIndex].OptionNumber == OptionNumber);
+ OptionIndex++;
+ }
+ }
+
+ if (OptionOrder != NULL) {
+ FreePool (OptionOrder);
+ }
+
+ if (OptionIndex < *OptionCount) {
+ Options = ReallocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), Options);
+ ASSERT (Options != NULL);
+ *OptionCount = OptionIndex;
+ }
+
+ } else if (LoadOptionType == LoadOptionTypePlatformRecovery) {
+ Param.OptionType = LoadOptionTypePlatformRecovery;
+ Param.Options = NULL;
+ Param.OptionCount = 0;
+ Param.Guid = &gEfiGlobalVariableGuid;
+
+ BmForEachVariable (BmCollectLoadOptions, (VOID *) &Param);
+
+ *OptionCount = Param.OptionCount;
+ Options = Param.Options;
+ }
+
+ return Options;
+}
+
+/**
+ Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library.
+
+ @param LoadOption Pointer to boot option to Free.
+
+ @return EFI_SUCCESS BootOption was freed
+ @return EFI_NOT_FOUND BootOption == NULL
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOption (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption
+ )
+{
+ if (LoadOption == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (LoadOption->Description != NULL) {
+ FreePool (LoadOption->Description);
+ }
+ if (LoadOption->FilePath != NULL) {
+ FreePool (LoadOption->FilePath);
+ }
+ if (LoadOption->OptionalData != NULL) {
+ FreePool (LoadOption->OptionalData);
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by
+ EfiBootManagerGetLoadOptions().
+
+ @param Option Pointer to boot option array to free.
+ @param OptionCount Number of array entries in BootOption
+
+ @return EFI_SUCCESS BootOption was freed
+ @return EFI_NOT_FOUND BootOption == NULL
+
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerFreeLoadOptions (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *Option,
+ IN UINTN OptionCount
+ )
+{
+ UINTN Index;
+
+ if (Option == NULL) {
+ return EFI_NOT_FOUND;
+ }
+
+ for (Index = 0;Index < OptionCount; Index++) {
+ EfiBootManagerFreeLoadOption (&Option[Index]);
+ }
+
+ FreePool (Option);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Return whether the PE header of the load option is valid or not.
+
+ @param[in] Type The load option type.
+ It's used to check whether the load option is valid.
+ When it's LoadOptionTypeMax, the routine only guarantees
+ the load option is a valid PE image but doesn't guarantee
+ the PE's subsystem type is valid.
+ @param[in] FileBuffer The PE file buffer of the load option.
+ @param[in] FileSize The size of the load option file.
+
+ @retval TRUE The PE header of the load option is valid.
+ @retval FALSE The PE header of the load option is not valid.
+**/
+BOOLEAN
+BmIsLoadOptionPeHeaderValid (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
+ IN VOID *FileBuffer,
+ IN UINTN FileSize
+ )
+{
+ EFI_IMAGE_DOS_HEADER *DosHeader;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHeader;
+ EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHeader;
+ UINT16 Subsystem;
+
+ if (FileBuffer == NULL || FileSize == 0) {
+ return FALSE;
+ }
+
+#ifdef VBOX
+ /*
+ * Check for Fat/Universal EFI binaries provided by older macOS versions
+ * (Mountain Lion and older).
+ *
+ * @todo More checks here? (VBoxPeCoffLib will do more thorough checks
+ * when the image is actually loaded).
+ */
+ if (*(UINT32 *)FileBuffer == 0x0ef1fab9)
+ return TRUE;
+#endif
+
+ //
+ // Read dos header
+ //
+ DosHeader = (EFI_IMAGE_DOS_HEADER *) FileBuffer;
+ if (FileSize >= sizeof (EFI_IMAGE_DOS_HEADER) &&
+ FileSize > DosHeader->e_lfanew && DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE
+ ) {
+ //
+ // Read and check PE signature
+ //
+ PeHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *) ((UINT8 *) FileBuffer + DosHeader->e_lfanew);
+ if (FileSize >= DosHeader->e_lfanew + sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION) &&
+ PeHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE
+ ) {
+ //
+ // Check PE32 or PE32+ magic, and machine type
+ //
+ OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *) &PeHeader->Pe32.OptionalHeader;
+ if (OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC ||
+ OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {
+ //
+ // Check the Subsystem:
+ // Driver#### must be of type BootServiceDriver or RuntimeDriver
+ // SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application
+ //
+ Subsystem = OptionalHeader->Subsystem;
+ if ((Type == LoadOptionTypeMax) ||
+ (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||
+ (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) ||
+ (Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
+ (Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||
+ (Type == LoadOptionTypePlatformRecovery && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)
+ ) {
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+/**
+ Return the next matched load option buffer.
+ The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid
+ load option is read.
+
+ @param Type The load option type.
+ It's used to check whether the load option is valid.
+ When it's LoadOptionTypeMax, the routine only guarantees
+ the load option is a valid PE image but doesn't guarantee
+ the PE's subsystem type is valid.
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath Return the next full device path of the load option after
+ short-form device path expanding.
+ Caller is responsible to free it.
+ NULL to return the first matched full device path.
+ @param FileSize Return the load option size.
+
+ @return The load option buffer. Caller is responsible to free the memory.
+**/
+VOID *
+BmGetNextLoadOptionBuffer (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
+ OUT UINTN *FileSize
+ )
+{
+ VOID *FileBuffer;
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
+ UINTN LocalFileSize;
+ UINT32 AuthenticationStatus;
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;
+
+ LocalFileSize = 0;
+ FileBuffer = NULL;
+ CurFullPath = *FullPath;
+ do {
+ PreFullPath = CurFullPath;
+ CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath);
+ //
+ // Only free the full path created *inside* this routine
+ //
+ if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) {
+ FreePool (PreFullPath);
+ }
+ if (CurFullPath == NULL) {
+ break;
+ }
+ FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus);
+ if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) {
+ //
+ // Free the RAM disk file system if the load option is invalid.
+ //
+ RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
+ if (RamDiskDevicePath != NULL) {
+ BmDestroyRamDisk (RamDiskDevicePath);
+ FreePool (RamDiskDevicePath);
+ }
+
+ //
+ // Free the invalid load option buffer.
+ //
+ FreePool (FileBuffer);
+ FileBuffer = NULL;
+ }
+ } while (FileBuffer == NULL);
+
+ if (FileBuffer == NULL) {
+ CurFullPath = NULL;
+ LocalFileSize = 0;
+ }
+
+ DEBUG ((DEBUG_INFO, "[Bds] Expand "));
+ BmPrintDp (FilePath);
+ DEBUG ((DEBUG_INFO, " -> "));
+ BmPrintDp (CurFullPath);
+ DEBUG ((DEBUG_INFO, "\n"));
+
+ *FullPath = CurFullPath;
+ *FileSize = LocalFileSize;
+ return FileBuffer;
+}
+
+/**
+ Process (load and execute) the load option.
+
+ @param LoadOption Pointer to the load option.
+
+ @retval EFI_INVALID_PARAMETER The load option type is invalid,
+ or the load option file path doesn't point to a valid file.
+ @retval EFI_UNSUPPORTED The load option type is of LoadOptionTypeBoot.
+ @retval EFI_SUCCESS The load option is inactive, or successfully loaded and executed.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerProcessLoadOption (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;
+ EFI_HANDLE ImageHandle;
+ EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
+ VOID *FileBuffer;
+ UINTN FileSize;
+
+ if ((UINT32) LoadOption->OptionType >= LoadOptionTypeMax) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (LoadOption->OptionType == LoadOptionTypeBoot) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // If a load option is not marked as LOAD_OPTION_ACTIVE,
+ // the boot manager will not automatically load the option.
+ //
+ if ((LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Load and start the load option.
+ //
+ DEBUG ((
+ DEBUG_INFO | DEBUG_LOAD, "Process %s%04x (%s) ...\n",
+ mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber,
+ LoadOption->Description
+ ));
+ ImageHandle = NULL;
+ CurFullPath = NULL;
+ EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL);
+
+ //
+ // while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status.
+ //
+ while (TRUE) {
+ Status = EFI_INVALID_PARAMETER;
+ PreFullPath = CurFullPath;
+ FileBuffer = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize);
+ if (PreFullPath != NULL) {
+ FreePool (PreFullPath);
+ }
+ if (FileBuffer == NULL) {
+ break;
+ }
+ Status = gBS->LoadImage (
+ FALSE,
+ gImageHandle,
+ CurFullPath,
+ FileBuffer,
+ FileSize,
+ &ImageHandle
+ );
+ FreePool (FileBuffer);
+
+ if (EFI_ERROR (Status)) {
+ //
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
+ // If the caller doesn't have the option to defer the execution of an image, we should
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
+ //
+ if (Status == EFI_SECURITY_VIOLATION) {
+ gBS->UnloadImage (ImageHandle);
+ }
+ } else {
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);
+ ASSERT_EFI_ERROR (Status);
+
+ ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;
+ ImageInfo->LoadOptions = LoadOption->OptionalData;
+ //
+ // Before calling the image, enable the Watchdog Timer for the 5-minute period
+ //
+ gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);
+
+ LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);
+ DEBUG ((
+ DEBUG_INFO | DEBUG_LOAD, "%s%04x Return Status = %r\n",
+ mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status
+ ));
+
+ //
+ // Clear the Watchdog Timer after the image returns
+ //
+ gBS->SetWatchdogTimer (0, 0, 0, NULL);
+
+ if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) {
+ break;
+ }
+ }
+ }
+
+ if (CurFullPath != NULL) {
+ FreePool (CurFullPath);
+ }
+
+ return Status;
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c
new file mode 100644
index 00000000..39775e18
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c
@@ -0,0 +1,535 @@
+/** @file
+ Misc library functions.
+
+Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#include "InternalBm.h"
+
+/**
+ Delete the instance in Multi which matches partly with Single instance
+
+ @param Multi A pointer to a multi-instance device path data
+ structure.
+ @param Single A pointer to a single-instance device path data
+ structure.
+
+ @return This function will remove the device path instances in Multi which partly
+ match with the Single, and return the result device path. If there is no
+ remaining device path as a result, this function will return NULL.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmDelPartMatchInstance (
+ IN EFI_DEVICE_PATH_PROTOCOL *Multi,
+ IN EFI_DEVICE_PATH_PROTOCOL *Single
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *Instance;
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
+ UINTN InstanceSize;
+ UINTN SingleDpSize;
+
+ NewDevicePath = NULL;
+ TempNewDevicePath = NULL;
+
+ if (Multi == NULL || Single == NULL) {
+ return Multi;
+ }
+
+ Instance = GetNextDevicePathInstance (&Multi, &InstanceSize);
+ SingleDpSize = GetDevicePathSize (Single) - END_DEVICE_PATH_LENGTH;
+ InstanceSize -= END_DEVICE_PATH_LENGTH;
+
+ while (Instance != NULL) {
+
+ if (CompareMem (Instance, Single, MIN (SingleDpSize, InstanceSize)) != 0) {
+ //
+ // Append the device path instance which does not match with Single
+ //
+ TempNewDevicePath = NewDevicePath;
+ NewDevicePath = AppendDevicePathInstance (NewDevicePath, Instance);
+ if (TempNewDevicePath != NULL) {
+ FreePool(TempNewDevicePath);
+ }
+ }
+ FreePool(Instance);
+ Instance = GetNextDevicePathInstance (&Multi, &InstanceSize);
+ InstanceSize -= END_DEVICE_PATH_LENGTH;
+ }
+
+ return NewDevicePath;
+}
+
+/**
+ Function compares a device path data structure to that of all the nodes of a
+ second device path instance.
+
+ @param Multi A pointer to a multi-instance device path data
+ structure.
+ @param Single A pointer to a single-instance device path data
+ structure.
+
+ @retval TRUE If the Single device path is contained within Multi device path.
+ @retval FALSE The Single device path is not match within Multi device path.
+
+**/
+BOOLEAN
+BmMatchDevicePaths (
+ IN EFI_DEVICE_PATH_PROTOCOL *Multi,
+ IN EFI_DEVICE_PATH_PROTOCOL *Single
+ )
+{
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;
+ EFI_DEVICE_PATH_PROTOCOL *DevicePathInst;
+ UINTN Size;
+
+ if (Multi == NULL || Single == NULL) {
+ return FALSE;
+ }
+
+ DevicePath = Multi;
+ DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+
+ //
+ // Search for the match of 'Single' in 'Multi'
+ //
+ while (DevicePathInst != NULL) {
+ //
+ // If the single device path is found in multiple device paths,
+ // return success
+ //
+ if (CompareMem (Single, DevicePathInst, Size) == 0) {
+ FreePool (DevicePathInst);
+ return TRUE;
+ }
+
+ FreePool (DevicePathInst);
+ DevicePathInst = GetNextDevicePathInstance (&DevicePath, &Size);
+ }
+
+ return FALSE;
+}
+
+/**
+ This routine adjust the memory information for different memory type and
+ save them into the variables for next boot. It resets the system when
+ memory information is updated and the current boot option belongs to
+ boot category instead of application category. It doesn't count the
+ reserved memory occupied by RAM Disk.
+
+ @param Boot TRUE if current boot option belongs to boot
+ category instead of application category.
+**/
+VOID
+BmSetMemoryTypeInformationVariable (
+ IN BOOLEAN Boot
+ )
+{
+ EFI_STATUS Status;
+ EFI_MEMORY_TYPE_INFORMATION *PreviousMemoryTypeInformation;
+ EFI_MEMORY_TYPE_INFORMATION *CurrentMemoryTypeInformation;
+ UINTN VariableSize;
+ UINTN Index;
+ UINTN Index1;
+ UINT32 Previous;
+ UINT32 Current;
+ UINT32 Next;
+ EFI_HOB_GUID_TYPE *GuidHob;
+ BOOLEAN MemoryTypeInformationModified;
+ BOOLEAN MemoryTypeInformationVariableExists;
+ EFI_BOOT_MODE BootMode;
+
+ MemoryTypeInformationModified = FALSE;
+ MemoryTypeInformationVariableExists = FALSE;
+
+
+ BootMode = GetBootModeHob ();
+ //
+ // In BOOT_IN_RECOVERY_MODE, Variable region is not reliable.
+ //
+ if (BootMode == BOOT_IN_RECOVERY_MODE) {
+ return;
+ }
+
+ //
+ // Only check the the Memory Type Information variable in the boot mode
+ // other than BOOT_WITH_DEFAULT_SETTINGS because the Memory Type
+ // Information is not valid in this boot mode.
+ //
+ if (BootMode != BOOT_WITH_DEFAULT_SETTINGS) {
+ VariableSize = 0;
+ Status = gRT->GetVariable (
+ EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
+ &gEfiMemoryTypeInformationGuid,
+ NULL,
+ &VariableSize,
+ NULL
+ );
+ if (Status == EFI_BUFFER_TOO_SMALL) {
+ MemoryTypeInformationVariableExists = TRUE;
+ }
+ }
+
+ //
+ // Retrieve the current memory usage statistics. If they are not found, then
+ // no adjustments can be made to the Memory Type Information variable.
+ //
+ Status = EfiGetSystemConfigurationTable (
+ &gEfiMemoryTypeInformationGuid,
+ (VOID **) &CurrentMemoryTypeInformation
+ );
+ if (EFI_ERROR (Status) || CurrentMemoryTypeInformation == NULL) {
+ return;
+ }
+
+ //
+ // Get the Memory Type Information settings from Hob if they exist,
+ // PEI is responsible for getting them from variable and build a Hob to save them.
+ // If the previous Memory Type Information is not available, then set defaults
+ //
+ GuidHob = GetFirstGuidHob (&gEfiMemoryTypeInformationGuid);
+ if (GuidHob == NULL) {
+ //
+ // If Platform has not built Memory Type Info into the Hob, just return.
+ //
+ return;
+ }
+ VariableSize = GET_GUID_HOB_DATA_SIZE (GuidHob);
+ PreviousMemoryTypeInformation = AllocateCopyPool (VariableSize, GET_GUID_HOB_DATA (GuidHob));
+ if (PreviousMemoryTypeInformation == NULL) {
+ return;
+ }
+
+ //
+ // Use a heuristic to adjust the Memory Type Information for the next boot
+ //
+ DEBUG ((EFI_D_INFO, "Memory Previous Current Next \n"));
+ DEBUG ((EFI_D_INFO, " Type Pages Pages Pages \n"));
+ DEBUG ((EFI_D_INFO, "====== ======== ======== ========\n"));
+
+ for (Index = 0; PreviousMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {
+
+ for (Index1 = 0; CurrentMemoryTypeInformation[Index1].Type != EfiMaxMemoryType; Index1++) {
+ if (PreviousMemoryTypeInformation[Index].Type == CurrentMemoryTypeInformation[Index1].Type) {
+ break;
+ }
+ }
+ if (CurrentMemoryTypeInformation[Index1].Type == EfiMaxMemoryType) {
+ continue;
+ }
+
+ //
+ // Previous is the number of pages pre-allocated
+ // Current is the number of pages actually needed
+ //
+ Previous = PreviousMemoryTypeInformation[Index].NumberOfPages;
+ Current = CurrentMemoryTypeInformation[Index1].NumberOfPages;
+ Next = Previous;
+
+ //
+ // Inconsistent Memory Reserved across bootings may lead to S4 fail
+ // Write next varible to 125% * current when the pre-allocated memory is:
+ // 1. More than 150% of needed memory and boot mode is BOOT_WITH_DEFAULT_SETTING
+ // 2. Less than the needed memory
+ //
+ if ((Current + (Current >> 1)) < Previous) {
+ if (BootMode == BOOT_WITH_DEFAULT_SETTINGS) {
+ Next = Current + (Current >> 2);
+ }
+ } else if (Current > Previous) {
+ Next = Current + (Current >> 2);
+ }
+ if (Next > 0 && Next < 4) {
+ Next = 4;
+ }
+
+ if (Next != Previous) {
+ PreviousMemoryTypeInformation[Index].NumberOfPages = Next;
+ MemoryTypeInformationModified = TRUE;
+ }
+
+ DEBUG ((EFI_D_INFO, " %02x %08x %08x %08x\n", PreviousMemoryTypeInformation[Index].Type, Previous, Current, Next));
+ }
+
+ //
+ // If any changes were made to the Memory Type Information settings, then set the new variable value;
+ // Or create the variable in first boot.
+ //
+ if (MemoryTypeInformationModified || !MemoryTypeInformationVariableExists) {
+ Status = BmSetVariableAndReportStatusCodeOnError (
+ EFI_MEMORY_TYPE_INFORMATION_VARIABLE_NAME,
+ &gEfiMemoryTypeInformationGuid,
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ VariableSize,
+ PreviousMemoryTypeInformation
+ );
+
+ if (!EFI_ERROR (Status)) {
+ //
+ // If the Memory Type Information settings have been modified and the boot option belongs to boot category,
+ // then reset the platform so the new Memory Type Information setting will be used to guarantee that an S4
+ // entry/resume cycle will not fail.
+ //
+ if (MemoryTypeInformationModified) {
+ DEBUG ((EFI_D_INFO, "Memory Type Information settings change.\n"));
+ if (Boot && PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
+ DEBUG ((EFI_D_INFO, "...Warm Reset!!!\n"));
+ gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
+ }
+ }
+ } else {
+ DEBUG ((EFI_D_ERROR, "Memory Type Information settings cannot be saved. OS S4 may fail!\n"));
+ }
+ }
+ FreePool (PreviousMemoryTypeInformation);
+}
+
+/**
+ Set the variable and report the error through status code upon failure.
+
+ @param VariableName A Null-terminated string that is the name of the vendor's variable.
+ Each VariableName is unique for each VendorGuid. VariableName must
+ contain 1 or more characters. If VariableName is an empty string,
+ then EFI_INVALID_PARAMETER is returned.
+ @param VendorGuid A unique identifier for the vendor.
+ @param Attributes Attributes bitmask to set for the variable.
+ @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
+ or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
+ causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
+ set, then a SetVariable() call with a DataSize of zero will not cause any change to
+ the variable value (the timestamp associated with the variable may be updated however
+ even if no new data value is provided,see the description of the
+ EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
+ be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
+ @param Data The contents for the variable.
+
+ @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
+ defined by the Attributes.
+ @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the
+ DataSize exceeds the maximum allowed.
+ @retval EFI_INVALID_PARAMETER VariableName is an empty string.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error.
+ @retval EFI_WRITE_PROTECTED The variable in question is read-only.
+ @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
+ @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS
+ being set, but the AuthInfo does NOT pass the validation check carried out by the firmware.
+
+ @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
+**/
+EFI_STATUS
+BmSetVariableAndReportStatusCodeOnError (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ )
+{
+ EFI_STATUS Status;
+ EDKII_SET_VARIABLE_STATUS *SetVariableStatus;
+ UINTN NameSize;
+
+ Status = gRT->SetVariable (
+ VariableName,
+ VendorGuid,
+ Attributes,
+ DataSize,
+ Data
+ );
+ if (EFI_ERROR (Status)) {
+ NameSize = StrSize (VariableName);
+ SetVariableStatus = AllocatePool (sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize);
+ if (SetVariableStatus != NULL) {
+ CopyGuid (&SetVariableStatus->Guid, VendorGuid);
+ SetVariableStatus->NameSize = NameSize;
+ SetVariableStatus->DataSize = DataSize;
+ SetVariableStatus->SetStatus = Status;
+ SetVariableStatus->Attributes = Attributes;
+ CopyMem (SetVariableStatus + 1, VariableName, NameSize);
+ CopyMem (((UINT8 *) (SetVariableStatus + 1)) + NameSize, Data, DataSize);
+
+ REPORT_STATUS_CODE_EX (
+ EFI_ERROR_CODE,
+ PcdGet32 (PcdErrorCodeSetVariable),
+ 0,
+ NULL,
+ &gEdkiiStatusCodeDataTypeVariableGuid,
+ SetVariableStatus,
+ sizeof (EDKII_SET_VARIABLE_STATUS) + NameSize + DataSize
+ );
+
+ FreePool (SetVariableStatus);
+ }
+ }
+
+ return Status;
+}
+
+
+/**
+ Print the device path info.
+
+ @param DevicePath The device path need to print.
+**/
+VOID
+BmPrintDp (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+{
+ CHAR16 *Str;
+
+ Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
+ DEBUG ((EFI_D_INFO, "%s", Str));
+ if (Str != NULL) {
+ FreePool (Str);
+ }
+}
+
+/**
+ Convert a single character to number.
+ It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F'
+
+ @param Char The input char which need to convert to int.
+
+ @return The converted 8-bit number or (UINTN) -1 if conversion failed.
+**/
+UINTN
+BmCharToUint (
+ IN CHAR16 Char
+ )
+{
+ if ((Char >= L'0') && (Char <= L'9')) {
+ return (Char - L'0');
+ }
+
+ if ((Char >= L'A') && (Char <= L'F')) {
+ return (Char - L'A' + 0xA);
+ }
+
+ return (UINTN) -1;
+}
+
+/**
+ Dispatch the deferred images that are returned from all DeferredImageLoad instances.
+
+ @retval EFI_SUCCESS At least one deferred image is loaded successfully and started.
+ @retval EFI_NOT_FOUND There is no deferred image.
+ @retval EFI_ACCESS_DENIED There are deferred images but all of them are failed to load.
+**/
+EFI_STATUS
+EFIAPI
+EfiBootManagerDispatchDeferredImages (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_DEFERRED_IMAGE_LOAD_PROTOCOL *DeferredImage;
+ UINTN HandleCount;
+ EFI_HANDLE *Handles;
+ UINTN Index;
+ UINTN ImageIndex;
+ EFI_DEVICE_PATH_PROTOCOL *ImageDevicePath;
+ VOID *Image;
+ UINTN ImageSize;
+ BOOLEAN BootOption;
+ EFI_HANDLE ImageHandle;
+ UINTN ImageCount;
+ UINTN LoadCount;
+
+ //
+ // Find all the deferred image load protocols.
+ //
+ HandleCount = 0;
+ Handles = NULL;
+ Status = gBS->LocateHandleBuffer (
+ ByProtocol,
+ &gEfiDeferredImageLoadProtocolGuid,
+ NULL,
+ &HandleCount,
+ &Handles
+ );
+ if (EFI_ERROR (Status)) {
+ return EFI_NOT_FOUND;
+ }
+
+ ImageCount = 0;
+ LoadCount = 0;
+ for (Index = 0; Index < HandleCount; Index++) {
+ Status = gBS->HandleProtocol (Handles[Index], &gEfiDeferredImageLoadProtocolGuid, (VOID **) &DeferredImage);
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ for (ImageIndex = 0; ;ImageIndex++) {
+ //
+ // Load all the deferred images in this protocol instance.
+ //
+ Status = DeferredImage->GetImageInfo (
+ DeferredImage,
+ ImageIndex,
+ &ImageDevicePath,
+ (VOID **) &Image,
+ &ImageSize,
+ &BootOption
+ );
+ if (EFI_ERROR (Status)) {
+ break;
+ }
+ ImageCount++;
+ //
+ // Load and start the image.
+ //
+ Status = gBS->LoadImage (
+ BootOption,
+ gImageHandle,
+ ImageDevicePath,
+ NULL,
+ 0,
+ &ImageHandle
+ );
+ if (EFI_ERROR (Status)) {
+ //
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.
+ // If the caller doesn't have the option to defer the execution of an image, we should
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.
+ //
+ if (Status == EFI_SECURITY_VIOLATION) {
+ gBS->UnloadImage (ImageHandle);
+ }
+ } else {
+ LoadCount++;
+ //
+ // Before calling the image, enable the Watchdog Timer for
+ // a 5 Minute period
+ //
+ gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
+ gBS->StartImage (ImageHandle, NULL, NULL);
+
+ //
+ // Clear the Watchdog Timer after the image returns.
+ //
+ gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
+ }
+ }
+ }
+ if (Handles != NULL) {
+ FreePool (Handles);
+ }
+
+ if (ImageCount == 0) {
+ return EFI_NOT_FOUND;
+ } else {
+ if (LoadCount == 0) {
+ return EFI_ACCESS_DENIED;
+ } else {
+ return EFI_SUCCESS;
+ }
+ }
+}
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
new file mode 100644
index 00000000..4cdae792
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
@@ -0,0 +1,471 @@
+/** @file
+ BDS library definition, include the file and data structure
+
+Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
+(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+**/
+
+#ifndef _INTERNAL_BM_H_
+#define _INTERNAL_BM_H_
+
+#include <PiDxe.h>
+
+#include <IndustryStandard/Pci.h>
+#include <IndustryStandard/PeImage.h>
+#include <IndustryStandard/Atapi.h>
+#include <IndustryStandard/Scsi.h>
+#include <IndustryStandard/Nvme.h>
+
+#include <Protocol/PciRootBridgeIo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/LoadFile.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/SimpleTextIn.h>
+#include <Protocol/SimpleTextInEx.h>
+#include <Protocol/SimpleTextOut.h>
+#include <Protocol/SimpleNetwork.h>
+#include <Protocol/FirmwareVolume2.h>
+#include <Protocol/PciIo.h>
+#include <Protocol/GraphicsOutput.h>
+#include <Protocol/UsbIo.h>
+#include <Protocol/DiskInfo.h>
+#include <Protocol/NvmExpressPassthru.h>
+#include <Protocol/IdeControllerInit.h>
+#include <Protocol/BootLogo.h>
+#include <Protocol/DriverHealth.h>
+#include <Protocol/FormBrowser2.h>
+#include <Protocol/VariableLock.h>
+#include <Protocol/RamDisk.h>
+#include <Protocol/DeferredImageLoad.h>
+#include <Protocol/PlatformBootManager.h>
+
+#include <Guid/MemoryTypeInformation.h>
+#include <Guid/FileInfo.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/StatusCodeDataTypeId.h>
+#include <Guid/StatusCodeDataTypeVariable.h>
+#ifdef VBOX
+#include <Guid/VBoxFsBlessedFileInfo.h>
+#endif
+
+#include <Library/PrintLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/DxeServicesTableLib.h>
+#include <Library/HobLib.h>
+#include <Library/BaseLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/PerformanceLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PeCoffGetEntryPointLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/DxeServicesLib.h>
+#include <Library/ReportStatusCodeLib.h>
+#include <Library/CapsuleLib.h>
+#include <Library/PerformanceLib.h>
+#include <Library/HiiLib.h>
+
+#if !defined (EFI_REMOVABLE_MEDIA_FILE_NAME)
+ #if defined (MDE_CPU_EBC)
+ //
+ // Uefi specification only defines the default boot file name for IA32, X64
+ // and IPF processor, so need define boot file name for EBC architecture here.
+ //
+ #define EFI_REMOVABLE_MEDIA_FILE_NAME L"\\EFI\\BOOT\\BOOTEBC.EFI"
+ #else
+ #error "Can not determine the default boot file name for unknown processor type!"
+ #endif
+#endif
+
+typedef enum {
+ BmAcpiFloppyBoot,
+ BmHardwareDeviceBoot,
+ BmMessageAtapiBoot,
+ BmMessageSataBoot,
+ BmMessageUsbBoot,
+ BmMessageScsiBoot,
+ BmMiscBoot
+} BM_BOOT_TYPE;
+
+typedef
+CHAR16 *
+(* BM_GET_BOOT_DESCRIPTION) (
+ IN EFI_HANDLE Handle
+ );
+
+//
+// PlatformRecovery#### is the load option with the longest name
+//
+#define BM_OPTION_NAME_LEN sizeof ("PlatformRecovery####")
+extern CHAR16 *mBmLoadOptionName[];
+
+//
+// Maximum number of reconnect retry to repair controller; it is to limit the
+// number of recursive call of BmRepairAllControllers.
+//
+#define MAX_RECONNECT_REPAIR 10
+
+/**
+ Visitor function to be called by BmForEachVariable for each variable
+ in variable storage.
+
+ @param Name Variable name.
+ @param Guid Variable GUID.
+ @param Context The same context passed to BmForEachVariable.
+**/
+typedef
+VOID
+(*BM_VARIABLE_VISITOR) (
+ CHAR16 *Name,
+ EFI_GUID *Guid,
+ VOID *Context
+ );
+
+/**
+ Call Visitor function for each variable in variable storage.
+
+ @param Visitor Visitor function.
+ @param Context The context passed to Visitor function.
+**/
+VOID
+BmForEachVariable (
+ BM_VARIABLE_VISITOR Visitor,
+ VOID *Context
+ );
+
+#define BM_BOOT_DESCRIPTION_ENTRY_SIGNATURE SIGNATURE_32 ('b', 'm', 'd', 'h')
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+ EFI_BOOT_MANAGER_BOOT_DESCRIPTION_HANDLER Handler;
+} BM_BOOT_DESCRIPTION_ENTRY;
+
+/**
+ Repair all the controllers according to the Driver Health status queried.
+
+ @param ReconnectRepairCount To record the number of recursive call of
+ this function itself.
+**/
+VOID
+BmRepairAllControllers (
+ UINTN ReconnectRepairCount
+ );
+
+#define BM_HOTKEY_SIGNATURE SIGNATURE_32 ('b', 'm', 'h', 'k')
+typedef struct {
+ UINT32 Signature;
+ LIST_ENTRY Link;
+
+ BOOLEAN IsContinue;
+ UINT16 BootOption;
+ UINT8 CodeCount;
+ UINT8 WaitingKey;
+ EFI_KEY_DATA KeyData[3];
+} BM_HOTKEY;
+
+#define BM_HOTKEY_FROM_LINK(a) CR (a, BM_HOTKEY, Link, BM_HOTKEY_SIGNATURE)
+
+/**
+ Get the Option Number that wasn't used.
+
+ @param LoadOptionType Load option type.
+ @param FreeOptionNumber To receive the minimal free option number.
+
+ @retval EFI_SUCCESS The option number is found
+ @retval EFI_OUT_OF_RESOURCES There is no free option number that can be used.
+ @retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL
+
+**/
+EFI_STATUS
+BmGetFreeOptionNumber (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType,
+ OUT UINT16 *FreeOptionNumber
+ );
+
+/**
+ This routine adjust the memory information for different memory type and
+ save them into the variables for next boot. It resets the system when
+ memory information is updated and the current boot option belongs to
+ boot category instead of application category. It doesn't count the
+ reserved memory occupied by RAM Disk.
+
+ @param Boot TRUE if current boot option belongs to boot
+ category instead of application category.
+**/
+VOID
+BmSetMemoryTypeInformationVariable (
+ IN BOOLEAN Boot
+ );
+
+/**
+ Check whether there is a instance in BlockIoDevicePath, which contain multi device path
+ instances, has the same partition node with HardDriveDevicePath device path
+
+ @param BlockIoDevicePath Multi device path instances which need to check
+ @param HardDriveDevicePath A device path which starts with a hard drive media
+ device path.
+
+ @retval TRUE There is a matched device path instance.
+ @retval FALSE There is no matched device path instance.
+
+**/
+BOOLEAN
+BmMatchPartitionDevicePathNode (
+ IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
+ IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
+ );
+
+/**
+ Connect the specific Usb device which match the short form device path.
+
+ @param DevicePath A short-form device path that starts with the first
+ element being a USB WWID or a USB Class device
+ path
+
+ @return EFI_INVALID_PARAMETER DevicePath is NULL pointer.
+ DevicePath is not a USB device path.
+
+ @return EFI_SUCCESS Success to connect USB device
+ @return EFI_NOT_FOUND Fail to find handle for USB controller to connect.
+
+**/
+EFI_STATUS
+BmConnectUsbShortFormDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ );
+
+/**
+ Stop the hotkey processing.
+
+ @param Event Event pointer related to hotkey service.
+ @param Context Context pass to this function.
+**/
+VOID
+EFIAPI
+BmStopHotkeyService (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+/**
+ Set the variable and report the error through status code upon failure.
+
+ @param VariableName A Null-terminated string that is the name of the vendor's variable.
+ Each VariableName is unique for each VendorGuid. VariableName must
+ contain 1 or more characters. If VariableName is an empty string,
+ then EFI_INVALID_PARAMETER is returned.
+ @param VendorGuid A unique identifier for the vendor.
+ @param Attributes Attributes bitmask to set for the variable.
+ @param DataSize The size in bytes of the Data buffer. Unless the EFI_VARIABLE_APPEND_WRITE,
+ or EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS attribute is set, a size of zero
+ causes the variable to be deleted. When the EFI_VARIABLE_APPEND_WRITE attribute is
+ set, then a SetVariable() call with a DataSize of zero will not cause any change to
+ the variable value (the timestamp associated with the variable may be updated however
+ even if no new data value is provided,see the description of the
+ EFI_VARIABLE_AUTHENTICATION_2 descriptor below. In this case the DataSize will not
+ be zero since the EFI_VARIABLE_AUTHENTICATION_2 descriptor will be populated).
+ @param Data The contents for the variable.
+
+ @retval EFI_SUCCESS The firmware has successfully stored the variable and its data as
+ defined by the Attributes.
+ @retval EFI_INVALID_PARAMETER An invalid combination of attribute bits, name, and GUID was supplied, or the
+ DataSize exceeds the maximum allowed.
+ @retval EFI_INVALID_PARAMETER VariableName is an empty string.
+ @retval EFI_OUT_OF_RESOURCES Not enough storage is available to hold the variable and its data.
+ @retval EFI_DEVICE_ERROR The variable could not be retrieved due to a hardware error.
+ @retval EFI_WRITE_PROTECTED The variable in question is read-only.
+ @retval EFI_WRITE_PROTECTED The variable in question cannot be deleted.
+ @retval EFI_SECURITY_VIOLATION The variable could not be written due to EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACESS
+ being set, but the AuthInfo does NOT pass the validation check carried out by the firmware.
+
+ @retval EFI_NOT_FOUND The variable trying to be updated or deleted was not found.
+**/
+EFI_STATUS
+BmSetVariableAndReportStatusCodeOnError (
+ IN CHAR16 *VariableName,
+ IN EFI_GUID *VendorGuid,
+ IN UINT32 Attributes,
+ IN UINTN DataSize,
+ IN VOID *Data
+ );
+
+/**
+ Function compares a device path data structure to that of all the nodes of a
+ second device path instance.
+
+ @param Multi A pointer to a multi-instance device path data
+ structure.
+ @param Single A pointer to a single-instance device path data
+ structure.
+
+ @retval TRUE If the Single device path is contained within Multi device path.
+ @retval FALSE The Single device path is not match within Multi device path.
+
+**/
+BOOLEAN
+BmMatchDevicePaths (
+ IN EFI_DEVICE_PATH_PROTOCOL *Multi,
+ IN EFI_DEVICE_PATH_PROTOCOL *Single
+ );
+
+/**
+ Delete the instance in Multi which matches partly with Single instance
+
+ @param Multi A pointer to a multi-instance device path data
+ structure.
+ @param Single A pointer to a single-instance device path data
+ structure.
+
+ @return This function will remove the device path instances in Multi which partly
+ match with the Single, and return the result device path. If there is no
+ remaining device path as a result, this function will return NULL.
+
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmDelPartMatchInstance (
+ IN EFI_DEVICE_PATH_PROTOCOL *Multi,
+ IN EFI_DEVICE_PATH_PROTOCOL *Single
+ );
+
+/**
+ Print the device path info.
+
+ @param DevicePath The device path need to print.
+**/
+VOID
+BmPrintDp (
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ );
+
+/**
+ Convert a single character to number.
+ It assumes the input Char is in the scope of L'0' ~ L'9' and L'A' ~ L'F'
+
+ @param Char The input char which need to convert to int.
+
+ @return The converted 8-bit number or (UINTN) -1 if conversion failed.
+**/
+UINTN
+BmCharToUint (
+ IN CHAR16 Char
+ );
+
+/**
+ Return the boot description for the controller.
+
+ @param Handle Controller handle.
+
+ @return The description string.
+**/
+CHAR16 *
+BmGetBootDescription (
+ IN EFI_HANDLE Handle
+ );
+
+/**
+ Enumerate all boot option descriptions and append " 2"/" 3"/... to make
+ unique description.
+
+ @param BootOptions Array of boot options.
+ @param BootOptionCount Count of boot options.
+**/
+VOID
+BmMakeBootOptionDescriptionUnique (
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions,
+ UINTN BootOptionCount
+ );
+
+/**
+ Get the file buffer from the specified Load File instance.
+
+ @param LoadFileHandle The specified Load File instance.
+ @param FilePath The file path which will pass to LoadFile().
+
+ @return The full device path pointing to the load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
+ IN EFI_HANDLE LoadFileHandle,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ );
+
+/**
+ Return the RAM Disk device path created by LoadFile.
+
+ @param FilePath The source file path.
+
+ @return Callee-to-free RAM Disk device path
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetRamDiskDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+ );
+
+/**
+ Destroy the RAM Disk.
+
+ The destroy operation includes to call RamDisk.Unregister to
+ unregister the RAM DISK from RAM DISK driver, free the memory
+ allocated for the RAM Disk.
+
+ @param RamDiskDevicePath RAM Disk device path.
+**/
+VOID
+BmDestroyRamDisk (
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+ );
+
+/**
+ Get the next possible full path pointing to the load option.
+
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath The full path returned by the routine in last call.
+ Set to NULL in first call.
+
+ @return The next possible full path pointing to the load option.
+ Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath
+ );
+
+/**
+ Return the next matched load option buffer.
+ The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid
+ load option is read.
+
+ @param Type The load option type.
+ It's used to check whether the load option is valid.
+ When it's LoadOptionTypeMax, the routine only guarantees
+ the load option is a valid PE image but doesn't guarantee
+ the PE's subsystem type is valid.
+ @param FilePath The device path pointing to a load option.
+ It could be a short-form device path.
+ @param FullPath Return the next full device path of the load option after
+ short-form device path expanding.
+ Caller is responsible to free it.
+ NULL to return the first matched full device path.
+ @param FileSize Return the load option size.
+
+ @return The load option buffer. Caller is responsible to free the memory.
+**/
+VOID *
+BmGetNextLoadOptionBuffer (
+ IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
+ OUT UINTN *FileSize
+ );
+#endif // _INTERNAL_BM_H_
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
new file mode 100644
index 00000000..ff6fcae0
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
@@ -0,0 +1,123 @@
+## @file
+# Define and produce general Boot Manager related interfaces.
+#
+# The implementation provides richful library functions supporting load option
+# manipulation, hotkey registration, UEFI boot, connect/disconnect, console
+# manipulation, driver health checking and etc.
+#
+# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>
+# (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = UefiBootManagerLib
+ MODULE_UNI_FILE = UefiBootManagerLib.uni
+ FILE_GUID = 8D4752BC-595E-49a2-B4AF-F3F57B601DE9
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ LIBRARY_CLASS = UefiBootManagerLib|DXE_DRIVER DXE_RUNTIME_DRIVER UEFI_APPLICATION UEFI_DRIVER
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 EBC
+#
+
+[Sources]
+ BmConnect.c
+ BmMisc.c
+ BmConsole.c
+ BmBoot.c
+ BmBootDescription.c
+ BmLoadOption.c
+ BmHotkey.c
+ BmDriverHealth.c
+ InternalBm.h
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ VBoxPkg/VBoxPkg.dec
+
+[LibraryClasses]
+ HobLib
+ PcdLib
+ BaseLib
+ UefiLib
+ DebugLib
+ PrintLib
+ BaseMemoryLib
+ DevicePathLib
+ PerformanceLib
+ PeCoffGetEntryPointLib
+ UefiBootServicesTableLib
+ UefiRuntimeServicesTableLib
+ DxeServicesTableLib
+ MemoryAllocationLib
+ DxeServicesLib
+ ReportStatusCodeLib
+ PerformanceLib
+ HiiLib
+ SortLib
+
+[Guids]
+ ## SOMETIMES_CONSUMES ## SystemTable (The identifier of memory type information type in system table)
+ ## SOMETIMES_CONSUMES ## HOB (The hob holding memory type information)
+ ## SOMETIMES_CONSUMES ## Variable:L"MemoryTypeInformation."
+ ## SOMETIMES_PRODUCES ## Variable:L"MemoryTypeInformation."
+ gEfiMemoryTypeInformationGuid
+
+ ## SOMETIMES_PRODUCES ## Variable:L"BootCurrent" (The boot option of current boot)
+ ## SOMETIMES_CONSUMES ## Variable:L"BootXX" (Boot option variable)
+ ## SOMETIMES_CONSUMES ## Variable:L"BootOrder" (The boot option array)
+ ## SOMETIMES_CONSUMES ## Variable:L"DriverOrder" (The driver order list)
+ ## SOMETIMES_CONSUMES ## Variable:L"ConIn" (The device path of console in device)
+ ## SOMETIMES_CONSUMES ## Variable:L"ConOut" (The device path of console out device)
+ ## SOMETIMES_CONSUMES ## Variable:L"ErrOut" (The device path of error out device)
+ gEfiGlobalVariableGuid
+
+ gEdkiiStatusCodeDataTypeVariableGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiDiskInfoAhciInterfaceGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiDiskInfoIdeInterfaceGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiDiskInfoScsiInterfaceGuid ## SOMETIMES_CONSUMES ## GUID
+ gEfiDiskInfoSdMmcInterfaceGuid ## SOMETIMES_CONSUMES ## GUID
+
+ gVBoxFsBlessedFileInfoGuid ## SOMETIMES_CONSUMES ## GUID
+
+[Protocols]
+ gEfiPciRootBridgeIoProtocolGuid ## CONSUMES
+ gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiLoadFileProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSimpleTextOutProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiPciIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiLoadedImageProtocolGuid ## CONSUMES
+ gEfiSimpleNetworkProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSimpleTextInProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBlockIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiFirmwareVolume2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDevicePathProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiBootLogoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSimpleTextInputExProtocolGuid ## SOMETIMES_CONSUMES
+ gEdkiiVariableLockProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiGraphicsOutputProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiUsbIoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiNvmExpressPassThruProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDiskInfoProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDriverHealthProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiFormBrowser2ProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiRamDiskProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiDeferredImageLoadProtocolGuid ## SOMETIMES_CONSUMES
+ gEdkiiPlatformBootManagerProtocolGuid ## SOMETIMES_CONSUMES
+
+[Pcd]
+ gEfiMdeModulePkgTokenSpaceGuid.PcdResetOnMemoryTypeInformationChange ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderLoad ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdProgressCodeOsLoaderStart ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdErrorCodeSetVariable ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdBootManagerMenuFile ## CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdDriverHealthConfigureForm ## SOMETIMES_CONSUMES
+ gEfiMdeModulePkgTokenSpaceGuid.PcdMaxRepairCount ## CONSUMES
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni
new file mode 100644
index 00000000..9471f81b
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni
@@ -0,0 +1,18 @@
+// /** @file
+// Define and produce general Boot Manager related interfaces.
+//
+// The implementation provides richful library functions supporting load option
+// manipulation, hotkey registration, UEFI boot, connect/disconnect, console
+// manipulation, driver health checking and etc.
+//
+// Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
+//
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// **/
+
+
+#string STR_MODULE_ABSTRACT #language en-US "Define and produce general Boot Manager related interfaces"
+
+#string STR_MODULE_DESCRIPTION #language en-US "The implementation provides richful library functions supporting load option manipulation, hotkey registration, UEFI boot, connect/disconnect, console manipulation, driver health checking and etc."
+