diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib')
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."
+
|