diff options
Diffstat (limited to 'MdeModulePkg/Library/UefiBootManagerLib')
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c | 5328 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c | 1822 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c | 634 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c | 1544 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c | 1190 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c | 2382 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c | 3030 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c | 1076 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h | 936 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf | 244 | ||||
| -rw-r--r-- | MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni | 36 |
11 files changed, 9111 insertions, 9111 deletions
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 7a97f7cdcc..d3aebc9262 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -1,2664 +1,2664 @@ -/** @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;
- BOOLEAN MatchFound;
- BOOLEAN ConnectAllAttempted;
-
- //
- // 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
- //
- BlockIoBuffer = NULL;
- MatchFound = FALSE;
- ConnectAllAttempted = FALSE;
- do {
- if (BlockIoBuffer != NULL) {
- FreePool (BlockIoBuffer);
- }
-
- 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
- );
- MatchFound = TRUE;
- break;
- }
- }
- }
-
- //
- // If we found a matching BLOCK_IO handle or we've already
- // tried a ConnectAll, we are done searching.
- //
- if (MatchFound || ConnectAllAttempted) {
- break;
- }
-
- EfiBootManagerConnectAll ();
- ConnectAllAttempted = TRUE;
- } while (1);
-
- if (CachedDevicePath != NULL) {
- FreePool (CachedDevicePath);
- }
-
- if (BlockIoBuffer != NULL) {
- FreePool (BlockIoBuffer);
- }
-
- return FullPath;
-}
-
-/**
- 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;
-
- 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));
-
- NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
- //
- // 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)) {
- NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
- 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_BEGIN ();
- 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,
- __func__,
- (UINT64)BufferSize,
- LoadFileText,
- FileText
- ));
-
- if (FileText != NULL) {
- FreePool (FileText);
- }
-
- if (LoadFileText != NULL) {
- FreePool (LoadFileText);
- }
-
- DEBUG_CODE_END ();
- 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 ((DEBUG_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 ((DEBUG_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;
- }
-
- //
- // Skip removable media if not present
- //
- if ((BlkIo->Media->RemovableMedia == TRUE) &&
- (BlkIo->Media->MediaPresent == FALSE))
- {
- 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;
- UINTN HandleCount;
- EFI_HANDLE *Handles;
- UINTN Index;
-
- 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) {
- Status = GetFileDevicePathFromAnyFv (
- PcdGetPtr (PcdBootManagerMenuFile),
- EFI_SECTION_PE32,
- 0,
- &DevicePath
- );
- if (EFI_ERROR (Status)) {
- DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));
- return EFI_NOT_FOUND;
- }
-
- ASSERT (DevicePath != NULL);
- //
- // 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;
- }
- }
-
- 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);
-}
+/** @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; + BOOLEAN MatchFound; + BOOLEAN ConnectAllAttempted; + + // + // 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 + // + BlockIoBuffer = NULL; + MatchFound = FALSE; + ConnectAllAttempted = FALSE; + do { + if (BlockIoBuffer != NULL) { + FreePool (BlockIoBuffer); + } + + 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 + ); + MatchFound = TRUE; + break; + } + } + } + + // + // If we found a matching BLOCK_IO handle or we've already + // tried a ConnectAll, we are done searching. + // + if (MatchFound || ConnectAllAttempted) { + break; + } + + EfiBootManagerConnectAll (); + ConnectAllAttempted = TRUE; + } while (1); + + if (CachedDevicePath != NULL) { + FreePool (CachedDevicePath); + } + + if (BlockIoBuffer != NULL) { + FreePool (BlockIoBuffer); + } + + return FullPath; +} + +/** + 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; + + 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)); + + NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); + // + // 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)) { + NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); + 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_BEGIN (); + 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, + __func__, + (UINT64)BufferSize, + LoadFileText, + FileText + )); + + if (FileText != NULL) { + FreePool (FileText); + } + + if (LoadFileText != NULL) { + FreePool (LoadFileText); + } + + DEBUG_CODE_END (); + 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 ((DEBUG_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 ((DEBUG_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; + } + + // + // Skip removable media if not present + // + if ((BlkIo->Media->RemovableMedia == TRUE) && + (BlkIo->Media->MediaPresent == FALSE)) + { + 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; + UINTN HandleCount; + EFI_HANDLE *Handles; + UINTN Index; + + 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) { + Status = GetFileDevicePathFromAnyFv ( + PcdGetPtr (PcdBootManagerMenuFile), + EFI_SECTION_PE32, + 0, + &DevicePath + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n")); + return EFI_NOT_FOUND; + } + + ASSERT (DevicePath != NULL); + // + // 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; + } + } + + 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/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c index 030b2ee3ec..480f0931e0 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBootDescription.c @@ -1,911 +1,911 @@ -/** @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) ||
- CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid))
- {
- 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);
-}
+/** @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) || + CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoUfsInterfaceGuid)) + { + 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/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c index d1fb0f72ba..02a101bee7 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConnect.c @@ -1,317 +1,317 @@ -/** @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;
-}
+/** @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/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c index 60e7b1099a..6d4e938481 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmConsole.c @@ -1,772 +1,772 @@ -/** @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 ((DEBUG_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 ((DEBUG_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;
-}
+/** @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 ((DEBUG_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 ((DEBUG_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/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c b/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c index 46a8b780e3..284c6f5d94 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmDriverHealth.c @@ -1,595 +1,595 @@ -/** @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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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_BEGIN ();
- CHAR16 *ControllerName;
-
- DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
- for (Index = 0; Index < Count; Index++) {
- ControllerName = BmGetControllerName (
- DriverHealthInfo[Index].DriverHealthHandle,
- DriverHealthInfo[Index].ControllerHandle,
- DriverHealthInfo[Index].ChildHandle
- );
- DEBUG ((
- DEBUG_INFO,
- "%02d: %s - %s\n",
- Index,
- ControllerName,
- mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus]
- ));
- if (ControllerName != NULL) {
- FreePool (ControllerName);
- }
- }
-
- EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
- DEBUG_CODE_END ();
-
- if (ReconnectRequired) {
- if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) {
- BmRepairAllControllers (ReconnectRepairCount + 1);
- } else {
- DEBUG ((
- DEBUG_ERROR,
- "[%a:%d] Repair failed after %d retries.\n",
- __func__,
- DEBUG_LINE_NUMBER,
- ReconnectRepairCount
- ));
- }
- }
-
- if (RebootRequired) {
- DEBUG ((DEBUG_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
- gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
- }
-}
+/** @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 ((DEBUG_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 ((DEBUG_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 ((DEBUG_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_BEGIN (); + CHAR16 *ControllerName; + + DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count); + for (Index = 0; Index < Count; Index++) { + ControllerName = BmGetControllerName ( + DriverHealthInfo[Index].DriverHealthHandle, + DriverHealthInfo[Index].ControllerHandle, + DriverHealthInfo[Index].ChildHandle + ); + DEBUG (( + DEBUG_INFO, + "%02d: %s - %s\n", + Index, + ControllerName, + mBmHealthStatusText[DriverHealthInfo[Index].HealthStatus] + )); + if (ControllerName != NULL) { + FreePool (ControllerName); + } + } + + EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count); + DEBUG_CODE_END (); + + if (ReconnectRequired) { + if (ReconnectRepairCount < MAX_RECONNECT_REPAIR) { + BmRepairAllControllers (ReconnectRepairCount + 1); + } else { + DEBUG (( + DEBUG_ERROR, + "[%a:%d] Repair failed after %d retries.\n", + __func__, + DEBUG_LINE_NUMBER, + ReconnectRepairCount + )); + } + } + + if (RebootRequired) { + DEBUG ((DEBUG_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n")); + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } +} diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c index 90bee73a23..54eea738cf 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmHotkey.c @@ -1,1191 +1,1191 @@ -/** @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 ((DEBUG_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 ((DEBUG_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status));
- if (EFI_ERROR (Status)) {
- mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned;
- }
- } else {
- DEBUG ((DEBUG_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 ((DEBUG_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 ((
- DEBUG_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 ((DEBUG_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 ((DEBUG_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;
-}
+/** @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 ((DEBUG_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 ((DEBUG_INFO, "[Bds]Hotkey for %s pressed - %r\n", OptionName, Status)); + if (EFI_ERROR (Status)) { + mBmHotkeyBootOption.OptionNumber = LoadOptionNumberUnassigned; + } + } else { + DEBUG ((DEBUG_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 ((DEBUG_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 (( + DEBUG_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 ((DEBUG_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 ((DEBUG_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/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c index 83a2f893e4..006db2e6f5 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c @@ -1,1515 +1,1515 @@ -/** @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"
-
-#include <Library/VariablePolicyHelperLib.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_POLICY_PROTOCOL *VariablePolicy;
- 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 (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy);
- if (!EFI_ERROR (Status)) {
- Status = RegisterBasicVariablePolicy (
- VariablePolicy,
- &gEfiGlobalVariableGuid,
- OptionName,
- VARIABLE_POLICY_NO_MIN_SIZE,
- VARIABLE_POLICY_NO_MAX_SIZE,
- VARIABLE_POLICY_NO_MUST_ATTR,
- VARIABLE_POLICY_NO_CANT_ATTR,
- VARIABLE_POLICY_TYPE_LOCK_NOW
- );
- 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 ((DEBUG_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;
- }
-
- //
- // 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;
- }
-
- if (LoadOption->OptionType == LoadOptionTypePlatformRecovery) {
- //
- // Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute the boot option.
- //
- EfiSignalEventReadyToBoot ();
- //
- // Report Status Code to indicate ReadyToBoot was signaled
- //
- REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
- }
-
- //
- // 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;
-}
+/** @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" + +#include <Library/VariablePolicyHelperLib.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_POLICY_PROTOCOL *VariablePolicy; + 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 (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy); + if (!EFI_ERROR (Status)) { + Status = RegisterBasicVariablePolicy ( + VariablePolicy, + &gEfiGlobalVariableGuid, + OptionName, + VARIABLE_POLICY_NO_MIN_SIZE, + VARIABLE_POLICY_NO_MAX_SIZE, + VARIABLE_POLICY_NO_MUST_ATTR, + VARIABLE_POLICY_NO_CANT_ATTR, + VARIABLE_POLICY_TYPE_LOCK_NOW + ); + 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 ((DEBUG_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; + } + + // + // 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; + } + + if (LoadOption->OptionType == LoadOptionTypePlatformRecovery) { + // + // Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute the boot option. + // + EfiSignalEventReadyToBoot (); + // + // Report Status Code to indicate ReadyToBoot was signaled + // + REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); + } + + // + // 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/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c index a5e32ebdba..b2778b3989 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmMisc.c @@ -1,538 +1,538 @@ -/** @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 ((DEBUG_INFO, "Memory Previous Current Next \n"));
- DEBUG ((DEBUG_INFO, " Type Pages Pages Pages \n"));
- DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_INFO, "Memory Type Information settings change.\n"));
- if (Boot && PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
- DEBUG ((DEBUG_INFO, "...Warm Reset!!!\n"));
- gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
- }
- }
- } else {
- DEBUG ((DEBUG_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 ((DEBUG_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;
- }
- }
-}
+/** @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 ((DEBUG_INFO, "Memory Previous Current Next \n")); + DEBUG ((DEBUG_INFO, " Type Pages Pages Pages \n")); + DEBUG ((DEBUG_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 ((DEBUG_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 ((DEBUG_INFO, "Memory Type Information settings change.\n")); + if (Boot && PcdGetBool (PcdResetOnMemoryTypeInformationChange)) { + DEBUG ((DEBUG_INFO, "...Warm Reset!!!\n")); + gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL); + } + } + } else { + DEBUG ((DEBUG_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 ((DEBUG_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/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h index b7dfe2a7e0..2fd09312f6 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h +++ b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h @@ -1,468 +1,468 @@ -/** @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/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>
-
-#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_
+/** @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/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> + +#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/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf index 2fc0a80a4e..51d13f2c52 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf +++ b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf @@ -1,122 +1,122 @@ -## @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
-
-[LibraryClasses]
- HobLib
- PcdLib
- BaseLib
- UefiLib
- DebugLib
- PrintLib
- BaseMemoryLib
- DevicePathLib
- PerformanceLib
- PeCoffGetEntryPointLib
- UefiBootServicesTableLib
- UefiRuntimeServicesTableLib
- DxeServicesTableLib
- MemoryAllocationLib
- DxeServicesLib
- ReportStatusCodeLib
- PerformanceLib
- HiiLib
- SortLib
- VariablePolicyHelperLib
-
-[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
- gEfiDiskInfoUfsInterfaceGuid ## 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
- gEdkiiVariablePolicyProtocolGuid ## 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
+## @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 + +[LibraryClasses] + HobLib + PcdLib + BaseLib + UefiLib + DebugLib + PrintLib + BaseMemoryLib + DevicePathLib + PerformanceLib + PeCoffGetEntryPointLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + DxeServicesTableLib + MemoryAllocationLib + DxeServicesLib + ReportStatusCodeLib + PerformanceLib + HiiLib + SortLib + VariablePolicyHelperLib + +[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 + gEfiDiskInfoUfsInterfaceGuid ## 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 + gEdkiiVariablePolicyProtocolGuid ## 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/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni index 9471f81bf6..9c4d748a10 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni +++ b/MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.uni @@ -1,18 +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."
-
+// /** @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." + |
