-/** @file
- Library functions which relates with booting.
-
-Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
-This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-**/
-
-#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 ";
-
-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 } };
-
-/**
- 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;
-}
-
-/**
- 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 MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
- and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,
- MSG_IPv4_DP or MSG_IPv6_DP.
- @retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.
-
-**/
-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)) {
- break;
- }
-
- 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;
-
- case MSG_MAC_ADDR_DP:
- case MSG_VLAN_DP:
- case MSG_IPv4_DP:
- case MSG_IPv6_DP:
- return BmMessageNetworkBoot;
- break;
- }
- }
- }
-
- return BmMiscBoot;
-}
-
-/**
- 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 = BmFindLoadOption (OptionToFind, BootOptions, BootOptionCount);
- if (Index != -1) {
- OptionNumber = BootOptions[Index].OptionNumber;
- }
-
- EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
- }
-
- return OptionNumber;
-}
-
-/**
- Get the file buffer using a Memory Mapped Device Path.
-
- FV address may change across reboot. This routine promises the FV file device path is right.
-
- @param DevicePath The Memory Mapped Device Path to get the file buffer.
- @param FullPath Receive the updated FV Device Path pointint to the file.
- @param FileSize Receive the file buffer size.
-
- @return The file buffer.
-**/
-VOID *
-BmGetFileBufferByMemmapFv (
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
- OUT UINTN *FileSize
- )
-{
- EFI_STATUS Status;
- UINTN Index;
- EFI_DEVICE_PATH_PROTOCOL *FvFileNode;
- EFI_HANDLE FvHandle;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- UINT32 AuthenticationStatus;
- UINTN FvHandleCount;
- EFI_HANDLE *FvHandles;
- EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
- VOID *FileBuffer;
-
- FvFileNode = DevicePath;
- Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
- if (!EFI_ERROR (Status)) {
- FileBuffer = GetFileBufferByFilePath (TRUE, DevicePath, FileSize, &AuthenticationStatus);
- if (FileBuffer != NULL) {
- *FullPath = DuplicateDevicePath (DevicePath);
- }
- return FileBuffer;
- }
-
- FvFileNode = NextDevicePathNode (DevicePath);
-
- //
- // Firstly find the FV file in current FV
- //
- gBS->HandleProtocol (
- gImageHandle,
- &gEfiLoadedImageProtocolGuid,
- (VOID **) &LoadedImage
- );
- NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
- FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize);
- FreePool (NewDevicePath);
-
- if (FileBuffer != NULL) {
- return FileBuffer;
- }
-
- //
- // Secondly find the FV file in all other FVs
- //
- gBS->LocateHandleBuffer (
- ByProtocol,
- &gEfiFirmwareVolume2ProtocolGuid,
- NULL,
- &FvHandleCount,
- &FvHandles
- );
- for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) {
- if (FvHandles[Index] == LoadedImage->DeviceHandle) {
- //
- // Skip current FV
- //
- continue;
- }
- NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
- FileBuffer = BmGetFileBufferByMemmapFv (NewDevicePath, FullPath, FileSize);
- FreePool (NewDevicePath);
- }
-
- if (FvHandles != NULL) {
- FreePool (FvHandles);
- }
- return FileBuffer;
-}
-
-/**
- Check if it's a Memory Mapped FV Device Path.
-
- 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 Memory Mapped FV Device Path.
- @retval FALSE The device path is NOT a Memory Mapped FV Device Path.
-**/
-BOOLEAN
-BmIsMemmapFvFilePath (
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
- )
-{
- EFI_DEVICE_PATH_PROTOCOL *FileNode;
-
- if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {
- FileNode = NextDevicePathNode (DevicePath);
- if ((DevicePathType (FileNode) == MEDIA_DEVICE_PATH) && (DevicePathSubType (FileNode) == MEDIA_PIWG_FW_FILE_DP)) {
- return IsDevicePathEnd (NextDevicePathNode (FileNode));
- }
- }
-
- 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;
-}
-
-/**
- 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;
-
- Description = NULL;
-
- Status = gBS->HandleProtocol (
- Handle,
- &gEfiDiskInfoProtocolGuid,
- (VOID **) &DiskInfo
- );
- if (EFI_ERROR (Status)) {
- return NULL;
- }
-
- if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
- CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
- BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
- Status = DiskInfo->Identify (
- DiskInfo,
- &IdentifyData,
- &BufferSize
- );
- if (!EFI_ERROR (Status)) {
- Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
- ASSERT (Description != NULL);
- for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
- Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];
- Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
- }
-
- Length = Index;
- Description[Length++] = L' ';
-
- for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
- Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];
- Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
- }
- Length += Index;
- Description[Length++] = L'\0';
- ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
-
- BmEliminateExtraSpaces (Description);
- }
- } else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
- BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
- Status = DiskInfo->Inquiry (
- DiskInfo,
- &InquiryData,
- &BufferSize
- );
- if (!EFI_ERROR (Status)) {
- Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
- ASSERT (Description != NULL);
-
- //
- // Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
- // EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
- // Here combine the vendor identification and product identification to the description.
- //
- StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
- Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
- StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
- AsciiStrToUnicodeStr (StrPtr, Description);
- 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';
- AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);
-
- BmEliminateExtraSpaces (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;
-
- 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;
- }
-
- Description = AllocateZeroPool (StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber));
- ASSERT (Description != NULL);
- StrCat (Description, Manufacturer);
- StrCat (Description, L" ");
-
- StrCat (Description, Product);
- StrCat (Description, L" ");
-
- StrCat (Description, SerialNumber);
-
- if (Manufacturer != &NullChar) {
- FreePool (Manufacturer);
- }
- if (Product != &NullChar) {
- FreePool (Product);
- }
- if (SerialNumber != &NullChar) {
- FreePool (SerialNumber);
- }
-
- 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;
-
- 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:
- Description = L"Misc Device";
- break;
- }
-
- return AllocateCopyPool (StrSize (Description), Description);
-}
-
-BM_GET_BOOT_DESCRIPTION mBmGetBootDescription[] = {
- BmGetUsbDescription,
- BmGetDescriptionFromDiskInfo,
- BmGetMiscDescription
-};
-
-/**
- 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;
- UINTN UsbIoDevicePathSize;
- 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)) {
- UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH;
-
- //
- // 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 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.
- @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
-
- @return The load option buffer. Caller is responsible to free the memory.
-**/
-VOID *
-BmExpandUsbDevicePath (
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
- OUT UINTN *FileSize,
- IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode
- )
-{
- UINTN ParentDevicePathSize;
- EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
- EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
- EFI_HANDLE *Handles;
- UINTN HandleCount;
- UINTN Index;
- VOID *FileBuffer;
-
- ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;
- RemainingDevicePath = NextDevicePathNode (ShortformNode);
- FileBuffer = NULL;
- Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
-
- for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {
- FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
- FileBuffer = BmGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);
- FreePool (FullDevicePath);
- }
-
- if (Handles != NULL) {
- FreePool (Handles);
- }
-
- return FileBuffer;
-}
-
-/**
- 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.
- @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 *
-BmExpandPartitionDevicePath (
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
- OUT UINTN *FileSize
- )
-{
- EFI_STATUS Status;
- UINTN BlockIoHandleCount;
- EFI_HANDLE *BlockIoBuffer;
- VOID *FileBuffer;
- EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
- UINTN Index;
- EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
- EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
- EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
- UINTN CachedDevicePathSize;
- BOOLEAN NeedAdjust;
- EFI_DEVICE_PATH_PROTOCOL *Instance;
- UINTN Size;
-
- FileBuffer = NULL;
- //
- // 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);
- }
-
- 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));
- FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
- FreePool (TempDevicePath);
-
- if (FileBuffer != 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 FileBuffer;
- }
- }
- }
- //
- // Come here means the first instance is not matched
- //
- NeedAdjust = TRUE;
- FreePool(Instance);
- } while (TempNewDevicePath != NULL);
- }
-
- //
- // If we get here we fail to find or 'HDDP' not exist, and now we need
- // to search all devices in the system for a matched partition
- //
- EfiBootManagerConnectAll ();
- Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);
- if (EFI_ERROR (Status)) {
- BlockIoHandleCount = 0;
- BlockIoBuffer = NULL;
- }
- //
- // Loop through all the device handles that support the BLOCK_IO Protocol
- //
- for (Index = 0; Index < BlockIoHandleCount; Index++) {
- BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);
- if (BlockIoDevicePath == NULL) {
- continue;
- }
-
- if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {
- //
- // Find the matched partition device path
- //
- TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
- FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
- FreePool (TempDevicePath);
-
- if (FileBuffer != NULL) {
- BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
-
- //
- // Save the matching Device Path so we don't need to do a connect all next time
- // Failing to save only impacts performance next time expanding the short-form device path
- //
- Status = gRT->SetVariable (
- L"HDDP",
- &mBmHardDriveBootVariableGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
- GetDevicePathSize (CachedDevicePath),
- CachedDevicePath
- );
-
- break;
- }
- }
- }
-
- if (CachedDevicePath != NULL) {
- FreePool (CachedDevicePath);
- }
- if (BlockIoBuffer != NULL) {
- FreePool (BlockIoBuffer);
- }
- return FileBuffer;
-}
-
-/**
- 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 Return the full device path pointing to the load option.
- @param FileSize Return the size of the load option.
-
- @return The load option buffer.
-**/
-VOID *
-BmExpandMediaDevicePath (
- IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
- OUT UINTN *FileSize
- )
-{
- EFI_STATUS Status;
- EFI_HANDLE Handle;
- EFI_BLOCK_IO_PROTOCOL *BlockIo;
- VOID *Buffer;
- EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
- UINTN Size;
- UINTN TempSize;
- EFI_HANDLE *SimpleFileSystemHandles;
- UINTN NumberSimpleFileSystemHandles;
- UINTN Index;
- VOID *FileBuffer;
- UINT32 AuthenticationStatus;
-
- //
- // Check whether the device is connected
- //
- TempDevicePath = DevicePath;
- Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);
- if (!EFI_ERROR (Status)) {
- ASSERT (IsDevicePathEnd (TempDevicePath));
-
- TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
- FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
- if (FileBuffer == NULL) {
- FreePool (TempDevicePath);
- TempDevicePath = NULL;
- }
- *FullPath = TempDevicePath;
- return FileBuffer;
- }
-
- //
- // 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->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
- ASSERT_EFI_ERROR (Status);
- Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
- ASSERT_EFI_ERROR (Status);
- 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
- //
- FileBuffer = NULL;
- *FullPath = 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)) {
- TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
- FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
- if (FileBuffer != NULL) {
- *FullPath = TempDevicePath;
- break;
- }
- FreePool (TempDevicePath);
- }
- }
-
- if (SimpleFileSystemHandles != NULL) {
- FreePool (SimpleFileSystemHandles);
- }
-
- return FileBuffer;
-}
-
-/**
- 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 *
-BmGetLoadOptionBuffer (
- IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
- OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,
- OUT UINTN *FileSize
- )
-{
- EFI_HANDLE Handle;
- VOID *FileBuffer;
- UINT32 AuthenticationStatus;
- EFI_DEVICE_PATH_PROTOCOL *Node;
- EFI_STATUS Status;
-
- ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));
-
- EfiBootManagerConnectDevicePath (FilePath, NULL);
-
- *FullPath = NULL;
- *FileSize = 0;
- FileBuffer = 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, FileSize);
- }
-
- //
- // 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
- //
- return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);
- } else {
- 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;
- }
- }
-
- if (!IsDevicePathEnd (Node)) {
- //
- // Expand the USB WWID/Class device path
- //
- FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
- if ((FileBuffer == NULL) && (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);
- FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
- }
- return FileBuffer;
- }
- }
-
- //
- // Fix up the boot option path if it points to a FV in memory map style of device path
- //
- if (BmIsMemmapFvFilePath (FilePath)) {
- return BmGetFileBufferByMemmapFv (FilePath, FullPath, FileSize);
- }
-
- //
- // Directly reads the load option when it doesn't reside in simple file system instance (LoadFile/LoadFile2),
- // or it directly points to a file in simple file system instance.
- //
- FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
- if (FileBuffer != NULL) {
- *FullPath = DuplicateDevicePath (FilePath);
- }
-
- return FileBuffer;
-}
-
-/**
- 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 *Node;
- EFI_HANDLE FvHandle;
- VOID *FileBuffer;
- UINTN FileSize;
- EFI_BOOT_LOGO_PROTOCOL *BootLogo;
- EFI_EVENT LegacyBootEvent;
-
- if (BootOption == NULL) {
- return;
- }
-
- if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {
- BootOption->Status = EFI_INVALID_PARAMETER;
- return;
- }
-
- //
- // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File")
- //
- OptionNumber = BmFindBootOptionInVariable (BootOption);
- if (OptionNumber == LoadOptionNumberUnassigned) {
- Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);
- if (!EFI_ERROR (Status)) {
- //
- // Save the BootOption->OptionNumber to restore later
- //
- OptionNumber = Uint16;
- OriginalOptionNumber = BootOption->OptionNumber;
- BootOption->OptionNumber = OptionNumber;
- Status = EfiBootManagerLoadOptionToVariable (BootOption);
- BootOption->OptionNumber = OriginalOptionNumber;
- }
-
- if (EFI_ERROR (Status)) {
- DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));
- BootOption->Status = Status;
- return ;
- }
- }
-
- //
- // 2. Set BootCurrent
- //
- Uint16 = (UINT16) OptionNumber;
- BmSetVariableAndReportStatusCodeOnError (
- L"BootCurrent",
- &gEfiGlobalVariableGuid,
- EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
- sizeof (UINT16),
- &Uint16
- );
-
- //
- // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute
- // the boot option.
- //
- Node = BootOption->FilePath;
- Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
- if (!EFI_ERROR (Status) && CompareGuid (
- EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
- PcdGetPtr (PcdBootManagerMenuFile)
- )) {
- DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));
- BmStopHotkeyService (NULL, NULL);
- } else {
- EfiSignalEventReadyToBoot();
- //
- // Report Status Code to indicate ReadyToBoot was signalled
- //
- REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
- //
- // 4. Repair system through DriverHealth protocol
- //
- BmRepairAllControllers ();
- }
-
- PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
-
- //
- // 5. Load EFI boot option to ImageHandle
- //
- ImageHandle = NULL;
- if (BmDevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
- Status = EFI_NOT_FOUND;
- FileBuffer = BmGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);
- DEBUG_CODE (
- if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
- DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
- BmPrintDp (BootOption->FilePath);
- DEBUG ((EFI_D_INFO, " -> "));
- BmPrintDp (FilePath);
- DEBUG ((EFI_D_INFO, "\n"));
- }
- );
- if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {
- 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)) {
- //
- // Report Status Code to indicate that the failure to load boot option
- //
- REPORT_STATUS_CODE (
- EFI_ERROR_CODE | EFI_ERROR_MINOR,
- (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)
- );
- BootOption->Status = Status;
- return;
- }
- }
-
- //
- // 6. Adjust the different type memory page number just before booting
- // and save the updated info into the variable for next boot to use
- //
- if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) {
- if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
- BmSetMemoryTypeInformationVariable ();
- }
- }
-
- 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();
-
- //
- // 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,
- BmWriteBootToOsPerformanceData,
- 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);
-
- 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 (
- BmWriteBootToOsPerformanceData (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;
- if (EFI_ERROR (Status)) {
- //
- // Report Status Code to indicate that boot failure
- //
- REPORT_STATUS_CODE (
- EFI_ERROR_CODE | EFI_ERROR_MINOR,
- (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
- );
- }
- 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;
- }
-
- //
- // find the partition device path node
- //
- while (!IsDevicePathEnd (BlockIoDevicePath)) {
- if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&
- (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)
- ) {
- break;
- }
-
- BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);
- }
-
- if (IsDevicePathEnd (BlockIoDevicePath)) {
- return FALSE;
- }
-
- //
- // 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.
- //
- return (BOOLEAN) (
- (Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&
- (Node->MBRType == HardDriveDevicePath->MBRType) &&
- (Node->SignatureType == HardDriveDevicePath->SignatureType) &&
- (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)
- );
-}
-
-/**
- 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;
- UINT16 NonBlockNumber;
- UINTN HandleCount;
- EFI_HANDLE *Handles;
- EFI_BLOCK_IO_PROTOCOL *BlkIo;
- UINTN Removable;
- UINTN Index;
- UINTN FunctionIndex;
- CHAR16 *Temp;
- CHAR16 *DescriptionPtr;
- CHAR16 Description[30];
-
- 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;
- }
-
- DescriptionPtr = NULL;
- for (FunctionIndex = 0; FunctionIndex < sizeof (mBmGetBootDescription) / sizeof (mBmGetBootDescription[0]); FunctionIndex++) {
- DescriptionPtr = mBmGetBootDescription[FunctionIndex] (Handles[Index]);
- if (DescriptionPtr != NULL) {
- break;
- }
- }
-
- if (DescriptionPtr == NULL) {
- continue;
- }
-
- //
- // Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
- //
- Temp = AllocatePool (StrSize (DescriptionPtr) + sizeof (mBmUefiPrefix));
- ASSERT (Temp != NULL);
- StrCpy (Temp, mBmUefiPrefix);
- StrCat (Temp, DescriptionPtr);
- FreePool (DescriptionPtr);
- DescriptionPtr = Temp;
-
- 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,
- DescriptionPtr,
- DevicePathFromHandle (Handles[Index]),
- NULL,
- 0
- );
- ASSERT_EFI_ERROR (Status);
-
- FreePool (DescriptionPtr);
- }
- }
-
- if (HandleCount != 0) {
- FreePool (Handles);
- }
-
- //
- // Parse simple file system not based on block io
- //
- NonBlockNumber = 0;
- 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;
- }
- UnicodeSPrint (Description, sizeof (Description), NonBlockNumber > 0 ? L"%s %d" : L"%s", L"UEFI Non-Block Boot Device", NonBlockNumber);
-
- 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);
- }
-
- if (HandleCount != 0) {
- FreePool (Handles);
- }
-
- //
- // Parse load file, assuming UEFI Network boot option
- //
- gBS->LocateHandleBuffer (
- ByProtocol,
- &gEfiLoadFileProtocolGuid,
- NULL,
- &HandleCount,
- &Handles
- );
- for (Index = 0; Index < HandleCount; Index++) {
-
- UnicodeSPrint (Description, sizeof (Description), Index > 0 ? L"%s %d" : L"%s", L"UEFI Network", 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);
- }
-
- if (HandleCount != 0) {
- FreePool (Handles);
- }
-
- 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;
- UINTN Index;
-
- //
- // Optionally refresh the legacy boot option
- //
- if (mBmRefreshLegacyBootOption != NULL) {
- mBmRefreshLegacyBootOption ();
- }
-
- BootOptions = BmEnumerateBootOptions (&BootOptionCount);
- NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);
-
- //
- // 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);
- }
-
- //
- // 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)
- ) &&
- (NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&
- CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mBmAutoCreateBootOptionGuid)
- ) {
- //
- // Only check those added by BDS
- // so that the boot options added by end-user or OS installer won't be deleted
- //
- if (BmFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -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 (BmFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -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 create the boot option for the Boot Manager Menu.
-
- The Boot Manager Menu is shown after successfully booting a boot option.
- Assume the BootManagerMenuFile is in the same FV as the module links to this library.
-
- @param BootOption Return the boot option of the Boot Manager Menu
-
- @retval EFI_SUCCESS Successfully register the Boot Manager Menu.
- @retval Status Return status of gRT->SetVariable (). BootOption still points
- to the Boot Manager Menu even the Status is not EFI_SUCCESS.
-**/
-EFI_STATUS
-BmRegisterBootManagerMenu (
- OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
- )
-{
- EFI_STATUS Status;
- CHAR16 *Description;
- UINTN DescriptionLength;
- EFI_DEVICE_PATH_PROTOCOL *DevicePath;
- EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
- MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
-
- Status = GetSectionFromFv (
- PcdGetPtr (PcdBootManagerMenuFile),
- EFI_SECTION_USER_INTERFACE,
- 0,
- (VOID **) &Description,
- &DescriptionLength
- );
- if (EFI_ERROR (Status)) {
- Description = NULL;
- }
-
- EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));
- Status = gBS->HandleProtocol (
- gImageHandle,
- &gEfiLoadedImageProtocolGuid,
- (VOID **) &LoadedImage
- );
- ASSERT_EFI_ERROR (Status);
- DevicePath = AppendDevicePathNode (
- DevicePathFromHandle (LoadedImage->DeviceHandle),
- (EFI_DEVICE_PATH_PROTOCOL *) &FileNode
- );
- ASSERT (DevicePath != NULL);
-
- Status = EfiBootManagerInitializeLoadOption (
- BootOption,
- LoadOptionNumberUnassigned,
- LoadOptionTypeBoot,
- LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,
- (Description != NULL) ? Description : L"Boot Manager Menu",
- DevicePath,
- NULL,
- 0
- );
- ASSERT_EFI_ERROR (Status);
- FreePool (DevicePath);
- if (Description != NULL) {
- FreePool (Description);
- }
-
- DEBUG_CODE (
- EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
- UINTN BootOptionCount;
-
- BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
- ASSERT (BmFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);
- EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
- );
-
- return EfiBootManagerAddLoadOptionVariable (BootOption, 0);
-}
-
-/**
- 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 Status Return status of gRT->SetVariable (). BootOption still points
- to the Boot Manager Menu even the Status is not EFI_SUCCESS.
-**/
-EFI_STATUS
-EFIAPI
-EfiBootManagerGetBootManagerMenu (
- EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
- )
-{
- EFI_STATUS Status;
- UINTN BootOptionCount;
- EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
- UINTN Index;
- EFI_DEVICE_PATH_PROTOCOL *Node;
- EFI_HANDLE FvHandle;
-
- BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
-
- for (Index = 0; Index < BootOptionCount; Index++) {
- Node = BootOptions[Index].FilePath;
- Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
- if (!EFI_ERROR (Status)) {
- if (CompareGuid (
- EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
- PcdGetPtr (PcdBootManagerMenuFile)
- )
- ) {
- 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;
- }
-}
-
+/** @file\r
+ Library functions which relates with booting.\r
+\r
+Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.\r
+Copyright (c) 2011 - 2021, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2015-2021 Hewlett Packard Enterprise Development LP<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "InternalBm.h"\r
+\r
+EFI_RAM_DISK_PROTOCOL *mRamDisk = NULL;\r
+\r
+EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mBmRefreshLegacyBootOption = NULL;\r
+EFI_BOOT_MANAGER_LEGACY_BOOT mBmLegacyBoot = NULL;\r
+\r
+///\r
+/// This GUID is used for an EFI Variable that stores the front device pathes\r
+/// for a partial device path that starts with the HD node.\r
+///\r
+EFI_GUID mBmHardDriveBootVariableGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };\r
+EFI_GUID mBmAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };\r
+\r
+/**\r
+\r
+ End Perf entry of BDS\r
+\r
+ @param Event The triggered event.\r
+ @param Context Context for this event.\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+BmEndOfBdsPerfCode (\r
+ IN EFI_EVENT Event,\r
+ IN VOID *Context\r
+ )\r
+{\r
+ //\r
+ // Record the performance data for End of BDS\r
+ //\r
+ PERF_CROSSMODULE_END("BDS");\r
+\r
+ return ;\r
+}\r
+\r
+/**\r
+ The function registers the legacy boot support capabilities.\r
+\r
+ @param RefreshLegacyBootOption The function pointer to create all the legacy boot options.\r
+ @param LegacyBoot The function pointer to boot the legacy boot option.\r
+**/\r
+VOID\r
+EFIAPI\r
+EfiBootManagerRegisterLegacyBootSupport (\r
+ EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION RefreshLegacyBootOption,\r
+ EFI_BOOT_MANAGER_LEGACY_BOOT LegacyBoot\r
+ )\r
+{\r
+ mBmRefreshLegacyBootOption = RefreshLegacyBootOption;\r
+ mBmLegacyBoot = LegacyBoot;\r
+}\r
+\r
+/**\r
+ Return TRUE when the boot option is auto-created instead of manually added.\r
+\r
+ @param BootOption Pointer to the boot option to check.\r
+\r
+ @retval TRUE The boot option is auto-created.\r
+ @retval FALSE The boot option is manually added.\r
+**/\r
+BOOLEAN\r
+BmIsAutoCreateBootOption (\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
+ )\r
+{\r
+ if ((BootOption->OptionalDataSize == sizeof (EFI_GUID)) &&\r
+ CompareGuid ((EFI_GUID *) BootOption->OptionalData, &mBmAutoCreateBootOptionGuid)\r
+ ) {\r
+ return TRUE;\r
+ } else {\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+/**\r
+ Find the boot option in the NV storage and return the option number.\r
+\r
+ @param OptionToFind Boot option to be checked.\r
+\r
+ @return The option number of the found boot option.\r
+\r
+**/\r
+UINTN\r
+BmFindBootOptionInVariable (\r
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
+ UINTN OptionNumber;\r
+ CHAR16 OptionName[BM_OPTION_NAME_LEN];\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN BootOptionCount;\r
+ UINTN Index;\r
+\r
+ OptionNumber = LoadOptionNumberUnassigned;\r
+\r
+ //\r
+ // Try to match the variable exactly if the option number is assigned\r
+ //\r
+ if (OptionToFind->OptionNumber != LoadOptionNumberUnassigned) {\r
+ UnicodeSPrint (\r
+ OptionName, sizeof (OptionName), L"%s%04x",\r
+ mBmLoadOptionName[OptionToFind->OptionType], OptionToFind->OptionNumber\r
+ );\r
+ Status = EfiBootManagerVariableToLoadOption (OptionName, &BootOption);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ ASSERT (OptionToFind->OptionNumber == BootOption.OptionNumber);\r
+ if ((OptionToFind->Attributes == BootOption.Attributes) &&\r
+ (StrCmp (OptionToFind->Description, BootOption.Description) == 0) &&\r
+ (CompareMem (OptionToFind->FilePath, BootOption.FilePath, GetDevicePathSize (OptionToFind->FilePath)) == 0) &&\r
+ (OptionToFind->OptionalDataSize == BootOption.OptionalDataSize) &&\r
+ (CompareMem (OptionToFind->OptionalData, BootOption.OptionalData, OptionToFind->OptionalDataSize) == 0)\r
+ ) {\r
+ OptionNumber = OptionToFind->OptionNumber;\r
+ }\r
+ EfiBootManagerFreeLoadOption (&BootOption);\r
+ }\r
+ }\r
+\r
+ //\r
+ // The option number assigned is either incorrect or unassigned.\r
+ //\r
+ if (OptionNumber == LoadOptionNumberUnassigned) {\r
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
+\r
+ Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);\r
+ if (Index != -1) {\r
+ OptionNumber = BootOptions[Index].OptionNumber;\r
+ }\r
+\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+ }\r
+\r
+ return OptionNumber;\r
+}\r
+\r
+/**\r
+ Return the correct FV file path.\r
+ FV address may change across reboot. This routine promises the FV file device path is right.\r
+\r
+ @param FilePath The Memory Mapped Device Path to get the file buffer.\r
+\r
+ @return The updated FV Device Path pointint to the file.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmAdjustFvFilePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *FvFileNode;\r
+ EFI_HANDLE FvHandle;\r
+ EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;\r
+ UINTN FvHandleCount;\r
+ EFI_HANDLE *FvHandles;\r
+ EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
+\r
+ //\r
+ // Get the file buffer by using the exactly FilePath.\r
+ //\r
+ FvFileNode = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);\r
+ if (!EFI_ERROR (Status)) {\r
+ return DuplicateDevicePath (FilePath);\r
+ }\r
+\r
+ //\r
+ // Only wide match other FVs if it's a memory mapped FV file path.\r
+ //\r
+ if ((DevicePathType (FilePath) != HARDWARE_DEVICE_PATH) || (DevicePathSubType (FilePath) != HW_MEMMAP_DP)) {\r
+ return NULL;\r
+ }\r
+\r
+ FvFileNode = NextDevicePathNode (FilePath);\r
+\r
+ //\r
+ // Firstly find the FV file in current FV\r
+ //\r
+ gBS->HandleProtocol (\r
+ gImageHandle,\r
+ &gEfiLoadedImageProtocolGuid,\r
+ (VOID **) &LoadedImage\r
+ );\r
+ NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);\r
+ FullPath = BmAdjustFvFilePath (NewDevicePath);\r
+ FreePool (NewDevicePath);\r
+ if (FullPath != NULL) {\r
+ return FullPath;\r
+ }\r
+\r
+ //\r
+ // Secondly find the FV file in all other FVs\r
+ //\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiFirmwareVolume2ProtocolGuid,\r
+ NULL,\r
+ &FvHandleCount,\r
+ &FvHandles\r
+ );\r
+ for (Index = 0; Index < FvHandleCount; Index++) {\r
+ if (FvHandles[Index] == LoadedImage->DeviceHandle) {\r
+ //\r
+ // Skip current FV, it was handed in first step.\r
+ //\r
+ continue;\r
+ }\r
+ NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);\r
+ FullPath = BmAdjustFvFilePath (NewDevicePath);\r
+ FreePool (NewDevicePath);\r
+ if (FullPath != NULL) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (FvHandles != NULL) {\r
+ FreePool (FvHandles);\r
+ }\r
+ return FullPath;\r
+}\r
+\r
+/**\r
+ Check if it's a Device Path pointing to FV file.\r
+\r
+ The function doesn't garentee the device path points to existing FV file.\r
+\r
+ @param DevicePath Input device path.\r
+\r
+ @retval TRUE The device path is a FV File Device Path.\r
+ @retval FALSE The device path is NOT a FV File Device Path.\r
+**/\r
+BOOLEAN\r
+BmIsFvFilePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+\r
+ Node = DevicePath;\r
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &Handle);\r
+ if (!EFI_ERROR (Status)) {\r
+ return TRUE;\r
+ }\r
+\r
+ if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {\r
+ DevicePath = NextDevicePathNode (DevicePath);\r
+ if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) && (DevicePathSubType (DevicePath) == MEDIA_PIWG_FW_FILE_DP)) {\r
+ return IsDevicePathEnd (NextDevicePathNode (DevicePath));\r
+ }\r
+ }\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Check whether a USB device match the specified USB Class device path. This\r
+ function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+ @param UsbIo USB I/O protocol associated with the USB device.\r
+ @param UsbClass The USB Class device path to match.\r
+\r
+ @retval TRUE The USB device match the USB Class device path.\r
+ @retval FALSE The USB device does not match the USB Class device path.\r
+\r
+**/\r
+BOOLEAN\r
+BmMatchUsbClass (\r
+ IN EFI_USB_IO_PROTOCOL *UsbIo,\r
+ IN USB_CLASS_DEVICE_PATH *UsbClass\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
+ UINT8 DeviceClass;\r
+ UINT8 DeviceSubClass;\r
+ UINT8 DeviceProtocol;\r
+\r
+ if ((DevicePathType (UsbClass) != MESSAGING_DEVICE_PATH) ||\r
+ (DevicePathSubType (UsbClass) != MSG_USB_CLASS_DP)){\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Vendor Id and Product Id.\r
+ //\r
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->VendorId != 0xffff) &&\r
+ (UsbClass->VendorId != DevDesc.IdVendor)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->ProductId != 0xffff) &&\r
+ (UsbClass->ProductId != DevDesc.IdProduct)) {\r
+ return FALSE;\r
+ }\r
+\r
+ DeviceClass = DevDesc.DeviceClass;\r
+ DeviceSubClass = DevDesc.DeviceSubClass;\r
+ DeviceProtocol = DevDesc.DeviceProtocol;\r
+ if (DeviceClass == 0) {\r
+ //\r
+ // If Class in Device Descriptor is set to 0, use the Class, SubClass and\r
+ // Protocol in Interface Descriptor instead.\r
+ //\r
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+\r
+ DeviceClass = IfDesc.InterfaceClass;\r
+ DeviceSubClass = IfDesc.InterfaceSubClass;\r
+ DeviceProtocol = IfDesc.InterfaceProtocol;\r
+ }\r
+\r
+ //\r
+ // Check Class, SubClass and Protocol.\r
+ //\r
+ if ((UsbClass->DeviceClass != 0xff) &&\r
+ (UsbClass->DeviceClass != DeviceClass)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->DeviceSubClass != 0xff) &&\r
+ (UsbClass->DeviceSubClass != DeviceSubClass)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if ((UsbClass->DeviceProtocol != 0xff) &&\r
+ (UsbClass->DeviceProtocol != DeviceProtocol)) {\r
+ return FALSE;\r
+ }\r
+\r
+ return TRUE;\r
+}\r
+\r
+/**\r
+ Check whether a USB device match the specified USB WWID device path. This\r
+ function follows "Load Option Processing" behavior in UEFI specification.\r
+\r
+ @param UsbIo USB I/O protocol associated with the USB device.\r
+ @param UsbWwid The USB WWID device path to match.\r
+\r
+ @retval TRUE The USB device match the USB WWID device path.\r
+ @retval FALSE The USB device does not match the USB WWID device path.\r
+\r
+**/\r
+BOOLEAN\r
+BmMatchUsbWwid (\r
+ IN EFI_USB_IO_PROTOCOL *UsbIo,\r
+ IN USB_WWID_DEVICE_PATH *UsbWwid\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_USB_DEVICE_DESCRIPTOR DevDesc;\r
+ EFI_USB_INTERFACE_DESCRIPTOR IfDesc;\r
+ UINT16 *LangIdTable;\r
+ UINT16 TableSize;\r
+ UINT16 Index;\r
+ CHAR16 *CompareStr;\r
+ UINTN CompareLen;\r
+ CHAR16 *SerialNumberStr;\r
+ UINTN Length;\r
+\r
+ if ((DevicePathType (UsbWwid) != MESSAGING_DEVICE_PATH) ||\r
+ (DevicePathSubType (UsbWwid) != MSG_USB_WWID_DP)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Vendor Id and Product Id.\r
+ //\r
+ Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ if ((DevDesc.IdVendor != UsbWwid->VendorId) ||\r
+ (DevDesc.IdProduct != UsbWwid->ProductId)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Interface Number.\r
+ //\r
+ Status = UsbIo->UsbGetInterfaceDescriptor (UsbIo, &IfDesc);\r
+ if (EFI_ERROR (Status)) {\r
+ return FALSE;\r
+ }\r
+ if (IfDesc.InterfaceNumber != UsbWwid->InterfaceNumber) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Check Serial Number.\r
+ //\r
+ if (DevDesc.StrSerialNumber == 0) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Get all supported languages.\r
+ //\r
+ TableSize = 0;\r
+ LangIdTable = NULL;\r
+ Status = UsbIo->UsbGetSupportedLanguages (UsbIo, &LangIdTable, &TableSize);\r
+ if (EFI_ERROR (Status) || (TableSize == 0) || (LangIdTable == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Serial number in USB WWID device path is the last 64-or-less UTF-16 characters.\r
+ //\r
+ CompareStr = (CHAR16 *) (UINTN) (UsbWwid + 1);\r
+ CompareLen = (DevicePathNodeLength (UsbWwid) - sizeof (USB_WWID_DEVICE_PATH)) / sizeof (CHAR16);\r
+ if (CompareStr[CompareLen - 1] == L'\0') {\r
+ CompareLen--;\r
+ }\r
+\r
+ //\r
+ // Compare serial number in each supported language.\r
+ //\r
+ for (Index = 0; Index < TableSize / sizeof (UINT16); Index++) {\r
+ SerialNumberStr = NULL;\r
+ Status = UsbIo->UsbGetStringDescriptor (\r
+ UsbIo,\r
+ LangIdTable[Index],\r
+ DevDesc.StrSerialNumber,\r
+ &SerialNumberStr\r
+ );\r
+ if (EFI_ERROR (Status) || (SerialNumberStr == NULL)) {\r
+ continue;\r
+ }\r
+\r
+ Length = StrLen (SerialNumberStr);\r
+ if ((Length >= CompareLen) &&\r
+ (CompareMem (SerialNumberStr + Length - CompareLen, CompareStr, CompareLen * sizeof (CHAR16)) == 0)) {\r
+ FreePool (SerialNumberStr);\r
+ return TRUE;\r
+ }\r
+\r
+ FreePool (SerialNumberStr);\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Find a USB device which match the specified short-form device path start with\r
+ USB Class or USB WWID device path. If ParentDevicePath is NULL, this function\r
+ will search in all USB devices of the platform. If ParentDevicePath is not NULL,\r
+ this function will only search in its child devices.\r
+\r
+ @param DevicePath The device path that contains USB Class or USB WWID device path.\r
+ @param ParentDevicePathSize The length of the device path before the USB Class or\r
+ USB WWID device path.\r
+ @param UsbIoHandleCount A pointer to the count of the returned USB IO handles.\r
+\r
+ @retval NULL The matched USB IO handles cannot be found.\r
+ @retval other The matched USB IO handles.\r
+\r
+**/\r
+EFI_HANDLE *\r
+BmFindUsbDevice (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ IN UINTN ParentDevicePathSize,\r
+ OUT UINTN *UsbIoHandleCount\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE *UsbIoHandles;\r
+ EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;\r
+ EFI_USB_IO_PROTOCOL *UsbIo;\r
+ UINTN Index;\r
+ BOOLEAN Matched;\r
+\r
+ ASSERT (UsbIoHandleCount != NULL);\r
+\r
+ //\r
+ // Get all UsbIo Handles.\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiUsbIoProtocolGuid,\r
+ NULL,\r
+ UsbIoHandleCount,\r
+ &UsbIoHandles\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ *UsbIoHandleCount = 0;\r
+ UsbIoHandles = NULL;\r
+ }\r
+\r
+ for (Index = 0; Index < *UsbIoHandleCount; ) {\r
+ //\r
+ // Get the Usb IO interface.\r
+ //\r
+ Status = gBS->HandleProtocol(\r
+ UsbIoHandles[Index],\r
+ &gEfiUsbIoProtocolGuid,\r
+ (VOID **) &UsbIo\r
+ );\r
+ UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);\r
+ Matched = FALSE;\r
+ if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {\r
+\r
+ //\r
+ // Compare starting part of UsbIoHandle's device path with ParentDevicePath.\r
+ //\r
+ if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {\r
+ if (BmMatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||\r
+ BmMatchUsbWwid (UsbIo, (USB_WWID_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize))) {\r
+ Matched = TRUE;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (!Matched) {\r
+ (*UsbIoHandleCount) --;\r
+ CopyMem (&UsbIoHandles[Index], &UsbIoHandles[Index + 1], (*UsbIoHandleCount - Index) * sizeof (EFI_HANDLE));\r
+ } else {\r
+ Index++;\r
+ }\r
+ }\r
+\r
+ return UsbIoHandles;\r
+}\r
+\r
+/**\r
+ Expand USB Class or USB WWID device path node to be full device path of a USB\r
+ device in platform.\r
+\r
+ This function support following 4 cases:\r
+ 1) Boot Option device path starts with a USB Class or USB WWID device path,\r
+ and there is no Media FilePath device path in the end.\r
+ In this case, it will follow Removable Media Boot Behavior.\r
+ 2) Boot Option device path starts with a USB Class or USB WWID device path,\r
+ and ended with Media FilePath device path.\r
+ 3) Boot Option device path starts with a full device path to a USB Host Controller,\r
+ contains a USB Class or USB WWID device path node, while not ended with Media\r
+ FilePath device path. In this case, it will follow Removable Media Boot Behavior.\r
+ 4) Boot Option device path starts with a full device path to a USB Host Controller,\r
+ contains a USB Class or USB WWID device path node, and ended with Media\r
+ FilePath device path.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+ @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandUsbDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode\r
+ )\r
+{\r
+ UINTN ParentDevicePathSize;\r
+ EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+ BOOLEAN GetNext;\r
+\r
+ NextFullPath = NULL;\r
+ GetNext = (BOOLEAN)(FullPath == NULL);\r
+ ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;\r
+ RemainingDevicePath = NextDevicePathNode (ShortformNode);\r
+ Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);\r
+\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);\r
+ if (FilePath == NULL) {\r
+ //\r
+ // Out of memory.\r
+ //\r
+ continue;\r
+ }\r
+ NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);\r
+ FreePool (FilePath);\r
+ if (NextFullPath == NULL) {\r
+ //\r
+ // No BlockIo or SimpleFileSystem under FilePath.\r
+ //\r
+ continue;\r
+ }\r
+ if (GetNext) {\r
+ break;\r
+ } else {\r
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
+ FreePool (NextFullPath);\r
+ NextFullPath = NULL;\r
+ }\r
+ }\r
+\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ return NextFullPath;\r
+}\r
+\r
+/**\r
+ Expand File-path device path node to be full device path in platform.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandFileDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *Handles;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ UINTN MediaType;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
+ BOOLEAN GetNext;\r
+\r
+ EfiBootManagerConnectAll ();\r
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);\r
+ if (EFI_ERROR (Status)) {\r
+ HandleCount = 0;\r
+ Handles = NULL;\r
+ }\r
+\r
+ GetNext = (BOOLEAN)(FullPath == NULL);\r
+ NextFullPath = NULL;\r
+ //\r
+ // Enumerate all removable media devices followed by all fixed media devices,\r
+ // followed by media devices which don't layer on block io.\r
+ //\r
+ for (MediaType = 0; MediaType < 3; MediaType++) {\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (Handles[Index], &gEfiBlockIoProtocolGuid, (VOID *) &BlockIo);\r
+ if (EFI_ERROR (Status)) {\r
+ BlockIo = NULL;\r
+ }\r
+ if ((MediaType == 0 && BlockIo != NULL && BlockIo->Media->RemovableMedia) ||\r
+ (MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||\r
+ (MediaType == 2 && BlockIo == NULL)\r
+ ) {\r
+ NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);\r
+ if (GetNext) {\r
+ break;\r
+ } else {\r
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
+ FreePool (NextFullPath);\r
+ NextFullPath = NULL;\r
+ }\r
+ }\r
+ }\r
+ if (NextFullPath != NULL) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ return NextFullPath;\r
+}\r
+\r
+/**\r
+ Expand URI device path node to be full device path in platform.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandUriDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Index;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *Handles;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
+ BOOLEAN GetNext;\r
+\r
+ EfiBootManagerConnectAll ();\r
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);\r
+ if (EFI_ERROR (Status)) {\r
+ HandleCount = 0;\r
+ Handles = NULL;\r
+ }\r
+\r
+ NextFullPath = NULL;\r
+ GetNext = (BOOLEAN)(FullPath == NULL);\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);\r
+\r
+ if (NextFullPath == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (GetNext) {\r
+ break;\r
+ } else {\r
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
+ //\r
+ // Free the resource occupied by the RAM disk.\r
+ //\r
+ RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);\r
+ if (RamDiskDevicePath != NULL) {\r
+ BmDestroyRamDisk (RamDiskDevicePath);\r
+ FreePool (RamDiskDevicePath);\r
+ }\r
+ FreePool (NextFullPath);\r
+ NextFullPath = NULL;\r
+ }\r
+ }\r
+\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ return NextFullPath;\r
+}\r
+\r
+/**\r
+ Save the partition DevicePath to the CachedDevicePath as the first instance.\r
+\r
+ @param CachedDevicePath The device path cache.\r
+ @param DevicePath The partition device path to be cached.\r
+**/\r
+VOID\r
+BmCachePartitionDevicePath (\r
+ IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+ )\r
+{\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+ UINTN Count;\r
+\r
+ if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) {\r
+ TempDevicePath = *CachedDevicePath;\r
+ *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath);\r
+ FreePool (TempDevicePath);\r
+ }\r
+\r
+ if (*CachedDevicePath == NULL) {\r
+ *CachedDevicePath = DuplicateDevicePath (DevicePath);\r
+ return;\r
+ }\r
+\r
+ TempDevicePath = *CachedDevicePath;\r
+ *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath);\r
+ if (TempDevicePath != NULL) {\r
+ FreePool (TempDevicePath);\r
+ }\r
+\r
+ //\r
+ // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller\r
+ // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger.\r
+ //\r
+ Count = 0;\r
+ TempDevicePath = *CachedDevicePath;\r
+ while (!IsDevicePathEnd (TempDevicePath)) {\r
+ TempDevicePath = NextDevicePathNode (TempDevicePath);\r
+ //\r
+ // Parse one instance\r
+ //\r
+ while (!IsDevicePathEndType (TempDevicePath)) {\r
+ TempDevicePath = NextDevicePathNode (TempDevicePath);\r
+ }\r
+ Count++;\r
+ //\r
+ // If the CachedDevicePath variable contain too much instance, only remain 12 instances.\r
+ //\r
+ if (Count == 12) {\r
+ SetDevicePathEndNode (TempDevicePath);\r
+ break;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ Expand a device path that starts with a hard drive media device path node to be a\r
+ full device path that includes the full hardware path to the device. We need\r
+ to do this so it can be booted. As an optimization the front match (the part point\r
+ to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable\r
+ so a connect all is not required on every boot. All successful history device path\r
+ which point to partition node (the front part) will be saved.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+\r
+ @return The full device path pointing to the load option.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandPartitionDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BlockIoHandleCount;\r
+ EFI_HANDLE *BlockIoBuffer;\r
+ EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
+ UINTN CachedDevicePathSize;\r
+ BOOLEAN NeedAdjust;\r
+ EFI_DEVICE_PATH_PROTOCOL *Instance;\r
+ UINTN Size;\r
+\r
+ //\r
+ // Check if there is prestore 'HDDP' variable.\r
+ // If exist, search the front path which point to partition node in the variable instants.\r
+ // If fail to find or 'HDDP' not exist, reconnect all and search in all system\r
+ //\r
+ GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);\r
+\r
+ //\r
+ // Delete the invalid 'HDDP' variable.\r
+ //\r
+ if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {\r
+ FreePool (CachedDevicePath);\r
+ CachedDevicePath = NULL;\r
+ Status = gRT->SetVariable (\r
+ L"HDDP",\r
+ &mBmHardDriveBootVariableGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ FullPath = NULL;\r
+ if (CachedDevicePath != NULL) {\r
+ TempNewDevicePath = CachedDevicePath;\r
+ NeedAdjust = FALSE;\r
+ do {\r
+ //\r
+ // Check every instance of the variable\r
+ // First, check whether the instance contain the partition node, which is needed for distinguishing multi\r
+ // partial partition boot option. Second, check whether the instance could be connected.\r
+ //\r
+ Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size);\r
+ if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
+ //\r
+ // Connect the device path instance, the device path point to hard drive media device path node\r
+ // e.g. ACPI() /PCI()/ATA()/Partition()\r
+ //\r
+ Status = EfiBootManagerConnectDevicePath (Instance, NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));\r
+ //\r
+ // TempDevicePath = ACPI()/PCI()/ATA()/Partition()\r
+ // or = ACPI()/PCI()/ATA()/Partition()/.../A.EFI\r
+ //\r
+ // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),\r
+ // it may expand to two potienal full paths (nested partition, rarely happen):\r
+ // 1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI\r
+ // 2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI\r
+ // For simplicity, only #1 is returned.\r
+ //\r
+ FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);\r
+ FreePool (TempDevicePath);\r
+\r
+ if (FullPath != NULL) {\r
+ //\r
+ // Adjust the 'HDDP' instances sequence if the matched one is not first one.\r
+ //\r
+ if (NeedAdjust) {\r
+ BmCachePartitionDevicePath (&CachedDevicePath, Instance);\r
+ //\r
+ // Save the matching Device Path so we don't need to do a connect all next time\r
+ // Failing to save only impacts performance next time expanding the short-form device path\r
+ //\r
+ Status = gRT->SetVariable (\r
+ L"HDDP",\r
+ &mBmHardDriveBootVariableGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ GetDevicePathSize (CachedDevicePath),\r
+ CachedDevicePath\r
+ );\r
+ }\r
+\r
+ FreePool (Instance);\r
+ FreePool (CachedDevicePath);\r
+ return FullPath;\r
+ }\r
+ }\r
+ }\r
+ //\r
+ // Come here means the first instance is not matched\r
+ //\r
+ NeedAdjust = TRUE;\r
+ FreePool(Instance);\r
+ } while (TempNewDevicePath != NULL);\r
+ }\r
+\r
+ //\r
+ // If we get here we fail to find or 'HDDP' not exist, and now we need\r
+ // to search all devices in the system for a matched partition\r
+ //\r
+ EfiBootManagerConnectAll ();\r
+ Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer);\r
+ if (EFI_ERROR (Status)) {\r
+ BlockIoHandleCount = 0;\r
+ BlockIoBuffer = NULL;\r
+ }\r
+ //\r
+ // Loop through all the device handles that support the BLOCK_IO Protocol\r
+ //\r
+ for (Index = 0; Index < BlockIoHandleCount; Index++) {\r
+ BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]);\r
+ if (BlockIoDevicePath == NULL) {\r
+ continue;\r
+ }\r
+\r
+ if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) {\r
+ //\r
+ // Find the matched partition device path\r
+ //\r
+ TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));\r
+ FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);\r
+ FreePool (TempDevicePath);\r
+\r
+ if (FullPath != NULL) {\r
+ BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);\r
+\r
+ //\r
+ // Save the matching Device Path so we don't need to do a connect all next time\r
+ // Failing to save only impacts performance next time expanding the short-form device path\r
+ //\r
+ Status = gRT->SetVariable (\r
+ L"HDDP",\r
+ &mBmHardDriveBootVariableGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ GetDevicePathSize (CachedDevicePath),\r
+ CachedDevicePath\r
+ );\r
+\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (CachedDevicePath != NULL) {\r
+ FreePool (CachedDevicePath);\r
+ }\r
+ if (BlockIoBuffer != NULL) {\r
+ FreePool (BlockIoBuffer);\r
+ }\r
+ return FullPath;\r
+}\r
+\r
+/**\r
+ Expand the media device path which points to a BlockIo or SimpleFileSystem instance\r
+ by appending EFI_REMOVABLE_MEDIA_FILE_NAME.\r
+\r
+ @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandMediaDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ VOID *Buffer;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *NextFullPath;\r
+ UINTN Size;\r
+ UINTN TempSize;\r
+ EFI_HANDLE *SimpleFileSystemHandles;\r
+ UINTN NumberSimpleFileSystemHandles;\r
+ UINTN Index;\r
+ BOOLEAN GetNext;\r
+\r
+ GetNext = (BOOLEAN)(FullPath == NULL);\r
+ //\r
+ // Check whether the device is connected\r
+ //\r
+ TempDevicePath = DevicePath;\r
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle);\r
+ if (!EFI_ERROR (Status)) {\r
+ ASSERT (IsDevicePathEnd (TempDevicePath));\r
+\r
+ NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);\r
+ //\r
+ // For device path pointing to simple file system, it only expands to one full path.\r
+ //\r
+ if (GetNext) {\r
+ return NextFullPath;\r
+ } else {\r
+ FreePool (NextFullPath);\r
+ return NULL;\r
+ }\r
+ }\r
+\r
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // For device boot option only pointing to the removable device handle,\r
+ // should make sure all its children handles (its child partion or media handles)\r
+ // are created and connected.\r
+ //\r
+ gBS->ConnectController (Handle, NULL, NULL, TRUE);\r
+\r
+ //\r
+ // Issue a dummy read to the device to check for media change.\r
+ // When the removable media is changed, any Block IO read/write will\r
+ // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is\r
+ // returned. After the Block IO protocol is reinstalled, subsequent\r
+ // Block IO read/write will success.\r
+ //\r
+ Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ Buffer = AllocatePool (BlockIo->Media->BlockSize);\r
+ if (Buffer != NULL) {\r
+ BlockIo->ReadBlocks (\r
+ BlockIo,\r
+ BlockIo->Media->MediaId,\r
+ 0,\r
+ BlockIo->Media->BlockSize,\r
+ Buffer\r
+ );\r
+ FreePool (Buffer);\r
+ }\r
+\r
+ //\r
+ // Detect the the default boot file from removable Media\r
+ //\r
+ NextFullPath = NULL;\r
+ Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ NULL,\r
+ &NumberSimpleFileSystemHandles,\r
+ &SimpleFileSystemHandles\r
+ );\r
+ for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) {\r
+ //\r
+ // Get the device path size of SimpleFileSystem handle\r
+ //\r
+ TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]);\r
+ TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH;\r
+ //\r
+ // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path\r
+ //\r
+ if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {\r
+ NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);\r
+ if (GetNext) {\r
+ break;\r
+ } else {\r
+ GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);\r
+ FreePool (NextFullPath);\r
+ NextFullPath = NULL;\r
+ }\r
+ }\r
+ }\r
+\r
+ if (SimpleFileSystemHandles != NULL) {\r
+ FreePool (SimpleFileSystemHandles);\r
+ }\r
+\r
+ return NextFullPath;\r
+}\r
+\r
+/**\r
+ Check whether Left and Right are the same without matching the specific\r
+ device path data in IP device path and URI device path node.\r
+\r
+ @retval TRUE Left and Right are the same.\r
+ @retval FALSE Left and Right are the different.\r
+**/\r
+BOOLEAN\r
+BmMatchHttpBootDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *Left,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *Right\r
+ )\r
+{\r
+ for (; !IsDevicePathEnd (Left) && !IsDevicePathEnd (Right)\r
+ ; Left = NextDevicePathNode (Left), Right = NextDevicePathNode (Right)\r
+ ) {\r
+ if (CompareMem (Left, Right, DevicePathNodeLength (Left)) != 0) {\r
+ if ((DevicePathType (Left) != MESSAGING_DEVICE_PATH) || (DevicePathType (Right) != MESSAGING_DEVICE_PATH)) {\r
+ return FALSE;\r
+ }\r
+\r
+ if (DevicePathSubType (Left) == MSG_DNS_DP) {\r
+ Left = NextDevicePathNode (Left);\r
+ }\r
+\r
+ if (DevicePathSubType (Right) == MSG_DNS_DP) {\r
+ Right = NextDevicePathNode (Right);\r
+ }\r
+\r
+ if (((DevicePathSubType (Left) != MSG_IPv4_DP) || (DevicePathSubType (Right) != MSG_IPv4_DP)) &&\r
+ ((DevicePathSubType (Left) != MSG_IPv6_DP) || (DevicePathSubType (Right) != MSG_IPv6_DP)) &&\r
+ ((DevicePathSubType (Left) != MSG_URI_DP) || (DevicePathSubType (Right) != MSG_URI_DP))\r
+ ) {\r
+ return FALSE;\r
+ }\r
+ }\r
+ }\r
+ return (BOOLEAN) (IsDevicePathEnd (Left) && IsDevicePathEnd (Right));\r
+}\r
+\r
+/**\r
+ Get the file buffer from the file system produced by Load File instance.\r
+\r
+ @param LoadFileHandle The handle of LoadFile instance.\r
+ @param RamDiskHandle Return the RAM Disk handle.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandNetworkFileSystem (\r
+ IN EFI_HANDLE LoadFileHandle,\r
+ OUT EFI_HANDLE *RamDiskHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiBlockIoProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Handles = NULL;\r
+ HandleCount = 0;\r
+ }\r
+\r
+ Handle = NULL;\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Node = DevicePathFromHandle (Handles[Index]);\r
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
+ if (!EFI_ERROR (Status) &&\r
+ (Handle == LoadFileHandle) &&\r
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {\r
+ //\r
+ // Find the BlockIo instance populated from the LoadFile.\r
+ //\r
+ Handle = Handles[Index];\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ if (Index == HandleCount) {\r
+ Handle = NULL;\r
+ }\r
+\r
+ *RamDiskHandle = Handle;\r
+\r
+ if (Handle != NULL) {\r
+ //\r
+ // Re-use BmExpandMediaDevicePath() to get the full device path of load option.\r
+ // But assume only one SimpleFileSystem can be found under the BlockIo.\r
+ //\r
+ return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);\r
+ } else {\r
+ return NULL;\r
+ }\r
+}\r
+\r
+/**\r
+ Return the RAM Disk device path created by LoadFile.\r
+\r
+ @param FilePath The source file path.\r
+\r
+ @return Callee-to-free RAM Disk device path\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmGetRamDiskDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+ EFI_HANDLE Handle;\r
+\r
+ Node = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
+ if (!EFI_ERROR (Status) &&\r
+ (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)\r
+ ) {\r
+\r
+ //\r
+ // Construct the device path pointing to RAM Disk\r
+ //\r
+ Node = NextDevicePathNode (Node);\r
+ RamDiskDevicePath = DuplicateDevicePath (FilePath);\r
+ ASSERT (RamDiskDevicePath != NULL);\r
+ SetDevicePathEndNode ((VOID *) ((UINTN) RamDiskDevicePath + ((UINTN) Node - (UINTN) FilePath)));\r
+ return RamDiskDevicePath;\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+/**\r
+ Return the buffer and buffer size occupied by the RAM Disk.\r
+\r
+ @param RamDiskDevicePath RAM Disk device path.\r
+ @param RamDiskSizeInPages Return RAM Disk size in pages.\r
+\r
+ @retval RAM Disk buffer.\r
+**/\r
+VOID *\r
+BmGetRamDiskMemoryInfo (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,\r
+ OUT UINTN *RamDiskSizeInPages\r
+ )\r
+{\r
+\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ UINT64 StartingAddr;\r
+ UINT64 EndingAddr;\r
+\r
+ ASSERT (RamDiskDevicePath != NULL);\r
+\r
+ *RamDiskSizeInPages = 0;\r
+\r
+ //\r
+ // Get the buffer occupied by RAM Disk.\r
+ //\r
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);\r
+ ASSERT_EFI_ERROR (Status);\r
+ ASSERT ((DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP));\r
+ StartingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->StartingAddr);\r
+ EndingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->EndingAddr);\r
+ *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN) (EndingAddr - StartingAddr + 1));\r
+ return (VOID *) (UINTN) StartingAddr;\r
+}\r
+\r
+/**\r
+ Destroy the RAM Disk.\r
+\r
+ The destroy operation includes to call RamDisk.Unregister to\r
+ unregister the RAM DISK from RAM DISK driver, free the memory\r
+ allocated for the RAM Disk.\r
+\r
+ @param RamDiskDevicePath RAM Disk device path.\r
+**/\r
+VOID\r
+BmDestroyRamDisk (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ VOID *RamDiskBuffer;\r
+ UINTN RamDiskSizeInPages;\r
+\r
+ ASSERT (RamDiskDevicePath != NULL);\r
+\r
+ RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);\r
+\r
+ //\r
+ // Destroy RAM Disk.\r
+ //\r
+ if (mRamDisk == NULL) {\r
+ Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *) &mRamDisk);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ Status = mRamDisk->Unregister (RamDiskDevicePath);\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePages (RamDiskBuffer, RamDiskSizeInPages);\r
+}\r
+\r
+/**\r
+ Get the file buffer from the specified Load File instance.\r
+\r
+ @param LoadFileHandle The specified Load File instance.\r
+ @param FilePath The file path which will pass to LoadFile().\r
+\r
+ @return The full device path pointing to the load option buffer.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandLoadFile (\r
+ IN EFI_HANDLE LoadFileHandle,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_LOAD_FILE_PROTOCOL *LoadFile;\r
+ VOID *FileBuffer;\r
+ EFI_HANDLE RamDiskHandle;\r
+ UINTN BufferSize;\r
+ EFI_DEVICE_PATH_PROTOCOL *FullPath;\r
+\r
+ Status = gBS->OpenProtocol (\r
+ LoadFileHandle,\r
+ &gEfiLoadFileProtocolGuid,\r
+ (VOID **) &LoadFile,\r
+ gImageHandle,\r
+ NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FileBuffer = NULL;\r
+ BufferSize = 0;\r
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
+ if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {\r
+ return NULL;\r
+ }\r
+\r
+ if (Status == EFI_BUFFER_TOO_SMALL) {\r
+ //\r
+ // The load option buffer is directly returned by LoadFile.\r
+ //\r
+ return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));\r
+ }\r
+\r
+ //\r
+ // The load option resides in a RAM disk.\r
+ //\r
+ FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));\r
+ if (FileBuffer == NULL) {\r
+ DEBUG_CODE (\r
+ EFI_DEVICE_PATH *LoadFilePath;\r
+ CHAR16 *LoadFileText;\r
+ CHAR16 *FileText;\r
+\r
+ LoadFilePath = DevicePathFromHandle (LoadFileHandle);\r
+ if (LoadFilePath == NULL) {\r
+ LoadFileText = NULL;\r
+ } else {\r
+ LoadFileText = ConvertDevicePathToText (LoadFilePath, FALSE, FALSE);\r
+ }\r
+ FileText = ConvertDevicePathToText (FilePath, FALSE, FALSE);\r
+\r
+ DEBUG ((\r
+ DEBUG_ERROR,\r
+ "%a:%a: failed to allocate reserved pages: "\r
+ "BufferSize=%Lu LoadFile=\"%s\" FilePath=\"%s\"\n",\r
+ gEfiCallerBaseName,\r
+ __FUNCTION__,\r
+ (UINT64)BufferSize,\r
+ LoadFileText,\r
+ FileText\r
+ ));\r
+\r
+ if (FileText != NULL) {\r
+ FreePool (FileText);\r
+ }\r
+ if (LoadFileText != NULL) {\r
+ FreePool (LoadFileText);\r
+ }\r
+ );\r
+ return NULL;\r
+ }\r
+\r
+ Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));\r
+ return NULL;\r
+ }\r
+\r
+ FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);\r
+ if (FullPath == NULL) {\r
+ //\r
+ // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.\r
+ //\r
+ BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));\r
+ }\r
+\r
+ return FullPath;\r
+}\r
+\r
+/**\r
+ Return the full device path pointing to the load option.\r
+\r
+ FilePath may:\r
+ 1. Exactly matches to a LoadFile instance.\r
+ 2. Cannot match to any LoadFile instance. Wide match is required.\r
+ In either case, the routine may return:\r
+ 1. A copy of FilePath when FilePath matches to a LoadFile instance and\r
+ the LoadFile returns a load option buffer.\r
+ 2. A new device path with IP and URI information updated when wide match\r
+ happens.\r
+ 3. A new device path pointing to a load option in RAM disk.\r
+ In either case, only one full device path is returned for a specified\r
+ FilePath.\r
+\r
+ @param FilePath The media device path pointing to a LoadFile instance.\r
+\r
+ @return The load option buffer.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmExpandLoadFiles (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE Handle;\r
+ EFI_HANDLE *Handles;\r
+ UINTN HandleCount;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+\r
+ //\r
+ // Get file buffer from load file instance.\r
+ //\r
+ Node = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
+ if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
+ //\r
+ // When wide match happens, pass full device path to LoadFile (),\r
+ // otherwise, pass remaining device path to LoadFile ().\r
+ //\r
+ FilePath = Node;\r
+ } else {\r
+ Handle = NULL;\r
+ //\r
+ // Use wide match algorithm to find one when\r
+ // cannot find a LoadFile instance to exactly match the FilePath\r
+ //\r
+ Status = gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiLoadFileProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Handles = NULL;\r
+ HandleCount = 0;\r
+ }\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ if (BmMatchHttpBootDevicePath (DevicePathFromHandle (Handles[Index]), FilePath)) {\r
+ Handle = Handles[Index];\r
+ break;\r
+ }\r
+ }\r
+ if (Handles != NULL) {\r
+ FreePool (Handles);\r
+ }\r
+ }\r
+\r
+ if (Handle == NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ return BmExpandLoadFile (Handle, FilePath);\r
+}\r
+\r
+/**\r
+ Get the load option by its device path.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath Return the full device path of the load option after\r
+ short-form device path expanding.\r
+ Caller is responsible to free it.\r
+ @param FileSize Return the load option size.\r
+\r
+ @return The load option buffer. Caller is responsible to free the memory.\r
+**/\r
+VOID *\r
+EFIAPI\r
+EfiBootManagerGetLoadOptionBuffer (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ OUT EFI_DEVICE_PATH_PROTOCOL **FullPath,\r
+ OUT UINTN *FileSize\r
+ )\r
+{\r
+ *FullPath = NULL;\r
+\r
+ EfiBootManagerConnectDevicePath (FilePath, NULL);\r
+ return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);\r
+}\r
+\r
+/**\r
+ Get the next possible full path pointing to the load option.\r
+ The routine doesn't guarantee the returned full path points to an existing\r
+ file, and it also doesn't guarantee the existing file is a valid load option.\r
+ BmGetNextLoadOptionBuffer() guarantees.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+BmGetNextLoadOptionDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
+ )\r
+{\r
+ EFI_HANDLE Handle;\r
+ EFI_DEVICE_PATH_PROTOCOL *Node;\r
+ EFI_STATUS Status;\r
+\r
+ ASSERT (FilePath != NULL);\r
+\r
+ //\r
+ // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI\r
+ //\r
+ Node = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
+ if (EFI_ERROR (Status)) {\r
+ Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle);\r
+ }\r
+\r
+ if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {\r
+ return BmExpandMediaDevicePath (FilePath, FullPath);\r
+ }\r
+\r
+ //\r
+ // Expand the short-form device path to full device path\r
+ //\r
+ if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) {\r
+ //\r
+ // Expand the Harddrive device path\r
+ //\r
+ if (FullPath == NULL) {\r
+ return BmExpandPartitionDevicePath (FilePath);\r
+ } else {\r
+ return NULL;\r
+ }\r
+ } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {\r
+ //\r
+ // Expand the File-path device path\r
+ //\r
+ return BmExpandFileDevicePath (FilePath, FullPath);\r
+ } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&\r
+ (DevicePathSubType (FilePath) == MSG_URI_DP)) {\r
+ //\r
+ // Expand the URI device path\r
+ //\r
+ return BmExpandUriDevicePath (FilePath, FullPath);\r
+ } else {\r
+ Node = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiUsbIoProtocolGuid, &Node, &Handle);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Only expand the USB WWID/Class device path\r
+ // when FilePath doesn't point to a physical UsbIo controller.\r
+ // Otherwise, infinite recursion will happen.\r
+ //\r
+ for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {\r
+ if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&\r
+ ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Expand the USB WWID/Class device path\r
+ //\r
+ if (!IsDevicePathEnd (Node)) {\r
+ if (FilePath == Node) {\r
+ //\r
+ // Boot Option device path starts with USB Class or USB WWID device path.\r
+ // For Boot Option device path which doesn't begin with the USB Class or\r
+ // USB WWID device path, it's not needed to connect again here.\r
+ //\r
+ BmConnectUsbShortFormDevicePath (FilePath);\r
+ }\r
+ return BmExpandUsbDevicePath (FilePath, FullPath, Node);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // For the below cases, FilePath only expands to one Full path.\r
+ // So just handle the case when FullPath == NULL.\r
+ //\r
+ if (FullPath != NULL) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Load option resides in FV.\r
+ //\r
+ if (BmIsFvFilePath (FilePath)) {\r
+ return BmAdjustFvFilePath (FilePath);\r
+ }\r
+\r
+ //\r
+ // Load option resides in Simple File System.\r
+ //\r
+ Node = FilePath;\r
+ Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);\r
+ if (!EFI_ERROR (Status)) {\r
+ return DuplicateDevicePath (FilePath);\r
+ }\r
+\r
+ //\r
+ // Last chance to try: Load option may be loaded through LoadFile.\r
+ //\r
+ return BmExpandLoadFiles (FilePath);\r
+}\r
+\r
+/**\r
+ Check if it's a Device Path pointing to BootManagerMenu.\r
+\r
+ @param DevicePath Input device path.\r
+\r
+ @retval TRUE The device path is BootManagerMenu File Device Path.\r
+ @retval FALSE The device path is NOT BootManagerMenu File Device Path.\r
+**/\r
+BOOLEAN\r
+BmIsBootManagerMenuFilePath (\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath\r
+)\r
+{\r
+ EFI_HANDLE FvHandle;\r
+ VOID *NameGuid;\r
+ EFI_STATUS Status;\r
+\r
+ Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &DevicePath, &FvHandle);\r
+ if (!EFI_ERROR (Status)) {\r
+ NameGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) DevicePath);\r
+ if (NameGuid != NULL) {\r
+ return CompareGuid (NameGuid, PcdGetPtr (PcdBootManagerMenuFile));\r
+ }\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Report status code with EFI_RETURN_STATUS_EXTENDED_DATA about LoadImage() or\r
+ StartImage() failure.\r
+\r
+ @param[in] ErrorCode An Error Code in the Software Class, DXE Boot\r
+ Service Driver Subclass. ErrorCode will be used to\r
+ compose the Value parameter for status code\r
+ reporting. Must be one of\r
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR and\r
+ EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED.\r
+\r
+ @param[in] FailureStatus The failure status returned by the boot service\r
+ that should be reported.\r
+**/\r
+VOID\r
+BmReportLoadFailure (\r
+ IN UINT32 ErrorCode,\r
+ IN EFI_STATUS FailureStatus\r
+ )\r
+{\r
+ EFI_RETURN_STATUS_EXTENDED_DATA ExtendedData;\r
+\r
+ if (!ReportErrorCodeEnabled ()) {\r
+ return;\r
+ }\r
+\r
+ ASSERT (\r
+ (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) ||\r
+ (ErrorCode == EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)\r
+ );\r
+\r
+ ZeroMem (&ExtendedData, sizeof (ExtendedData));\r
+ ExtendedData.ReturnStatus = FailureStatus;\r
+\r
+ REPORT_STATUS_CODE_EX (\r
+ (EFI_ERROR_CODE | EFI_ERROR_MINOR),\r
+ (EFI_SOFTWARE_DXE_BS_DRIVER | ErrorCode),\r
+ 0,\r
+ NULL,\r
+ NULL,\r
+ &ExtendedData.DataHeader + 1,\r
+ sizeof (ExtendedData) - sizeof (ExtendedData.DataHeader)\r
+ );\r
+}\r
+\r
+/**\r
+ Attempt to boot the EFI boot option. This routine sets L"BootCurent" and\r
+ also signals the EFI ready to boot event. If the device path for the option\r
+ starts with a BBS device path a legacy boot is attempted via the registered\r
+ gLegacyBoot function. Short form device paths are also supported via this\r
+ rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,\r
+ MSG_USB_CLASS_DP gets expaned out to find the first device that matches.\r
+ If the BootOption Device Path fails the removable media boot algorithm\r
+ is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type\r
+ is tried per processor type)\r
+\r
+ @param BootOption Boot Option to try and boot.\r
+ On return, BootOption->Status contains the boot status.\r
+ EFI_SUCCESS BootOption was booted\r
+ EFI_UNSUPPORTED A BBS device path was found with no valid callback\r
+ registered via EfiBootManagerInitialize().\r
+ EFI_NOT_FOUND The BootOption was not found on the system\r
+ !EFI_SUCCESS BootOption failed with this error status\r
+\r
+**/\r
+VOID\r
+EFIAPI\r
+EfiBootManagerBoot (\r
+ IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HANDLE ImageHandle;\r
+ EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;\r
+ UINT16 Uint16;\r
+ UINTN OptionNumber;\r
+ UINTN OriginalOptionNumber;\r
+ EFI_DEVICE_PATH_PROTOCOL *FilePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath;\r
+ VOID *FileBuffer;\r
+ UINTN FileSize;\r
+ EFI_BOOT_LOGO_PROTOCOL *BootLogo;\r
+ EFI_EVENT LegacyBootEvent;\r
+\r
+ if (BootOption == NULL) {\r
+ return;\r
+ }\r
+\r
+ if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) {\r
+ BootOption->Status = EFI_INVALID_PARAMETER;\r
+ return;\r
+ }\r
+\r
+ //\r
+ // 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")\r
+ //\r
+ OptionNumber = BmFindBootOptionInVariable (BootOption);\r
+ if (OptionNumber == LoadOptionNumberUnassigned) {\r
+ Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Save the BootOption->OptionNumber to restore later\r
+ //\r
+ OptionNumber = Uint16;\r
+ OriginalOptionNumber = BootOption->OptionNumber;\r
+ BootOption->OptionNumber = OptionNumber;\r
+ Status = EfiBootManagerLoadOptionToVariable (BootOption);\r
+ BootOption->OptionNumber = OriginalOptionNumber;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));\r
+ BootOption->Status = Status;\r
+ return ;\r
+ }\r
+ }\r
+\r
+ //\r
+ // 2. Set BootCurrent\r
+ //\r
+ Uint16 = (UINT16) OptionNumber;\r
+ BmSetVariableAndReportStatusCodeOnError (\r
+ L"BootCurrent",\r
+ &gEfiGlobalVariableGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,\r
+ sizeof (UINT16),\r
+ &Uint16\r
+ );\r
+\r
+ //\r
+ // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute\r
+ // the boot option.\r
+ //\r
+ if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) {\r
+ DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n"));\r
+ BmStopHotkeyService (NULL, NULL);\r
+ } else {\r
+ EfiSignalEventReadyToBoot();\r
+ //\r
+ // Report Status Code to indicate ReadyToBoot was signalled\r
+ //\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));\r
+ //\r
+ // 4. Repair system through DriverHealth protocol\r
+ //\r
+ BmRepairAllControllers (0);\r
+ }\r
+\r
+ PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
+\r
+ //\r
+ // 5. Adjust the different type memory page number just before booting\r
+ // and save the updated info into the variable for next boot to use\r
+ //\r
+ BmSetMemoryTypeInformationVariable (\r
+ (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)\r
+ );\r
+\r
+ //\r
+ // 6. Load EFI boot option to ImageHandle\r
+ //\r
+ DEBUG_CODE_BEGIN ();\r
+ if (BootOption->Description == NULL) {\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));\r
+ } else {\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));\r
+ }\r
+ DEBUG_CODE_END ();\r
+\r
+ ImageHandle = NULL;\r
+ RamDiskDevicePath = NULL;\r
+ if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
+ Status = EFI_NOT_FOUND;\r
+ FilePath = NULL;\r
+ EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);\r
+ FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);\r
+ if (FileBuffer != NULL) {\r
+ RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);\r
+\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));\r
+ Status = gBS->LoadImage (\r
+ TRUE,\r
+ gImageHandle,\r
+ FilePath,\r
+ FileBuffer,\r
+ FileSize,\r
+ &ImageHandle\r
+ );\r
+ }\r
+ if (FileBuffer != NULL) {\r
+ FreePool (FileBuffer);\r
+ }\r
+ if (FilePath != NULL) {\r
+ FreePool (FilePath);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created\r
+ // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.\r
+ // If the caller doesn't have the option to defer the execution of an image, we should\r
+ // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.\r
+ //\r
+ if (Status == EFI_SECURITY_VIOLATION) {\r
+ gBS->UnloadImage (ImageHandle);\r
+ }\r
+ //\r
+ // Destroy the RAM disk\r
+ //\r
+ if (RamDiskDevicePath != NULL) {\r
+ BmDestroyRamDisk (RamDiskDevicePath);\r
+ FreePool (RamDiskDevicePath);\r
+ }\r
+ //\r
+ // Report Status Code with the failure status to indicate that the failure to load boot option\r
+ //\r
+ BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status);\r
+ BootOption->Status = Status;\r
+ return;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Check to see if we should legacy BOOT. If yes then do the legacy boot\r
+ // Write boot to OS performance data for Legacy boot\r
+ //\r
+ if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {\r
+ if (mBmLegacyBoot != NULL) {\r
+ //\r
+ // Write boot to OS performance data for legacy boot.\r
+ //\r
+ PERF_CODE (\r
+ //\r
+ // Create an event to be signalled when Legacy Boot occurs to write performance data.\r
+ //\r
+ Status = EfiCreateEventLegacyBootEx(\r
+ TPL_NOTIFY,\r
+ BmEndOfBdsPerfCode,\r
+ NULL,\r
+ &LegacyBootEvent\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ );\r
+\r
+ mBmLegacyBoot (BootOption);\r
+ } else {\r
+ BootOption->Status = EFI_UNSUPPORTED;\r
+ }\r
+\r
+ PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
+ return;\r
+ }\r
+\r
+ //\r
+ // Provide the image with its load options\r
+ //\r
+ Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ if (!BmIsAutoCreateBootOption (BootOption)) {\r
+ ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;\r
+ ImageInfo->LoadOptions = BootOption->OptionalData;\r
+ }\r
+\r
+ //\r
+ // Clean to NULL because the image is loaded directly from the firmwares boot manager.\r
+ //\r
+ ImageInfo->ParentHandle = NULL;\r
+\r
+ //\r
+ // Before calling the image, enable the Watchdog Timer for 5 minutes period\r
+ //\r
+ gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);\r
+\r
+ //\r
+ // Write boot to OS performance data for UEFI boot\r
+ //\r
+ PERF_CODE (\r
+ BmEndOfBdsPerfCode (NULL, NULL);\r
+ );\r
+\r
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));\r
+\r
+ Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);\r
+ DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));\r
+ BootOption->Status = Status;\r
+\r
+ //\r
+ // Destroy the RAM disk\r
+ //\r
+ if (RamDiskDevicePath != NULL) {\r
+ BmDestroyRamDisk (RamDiskDevicePath);\r
+ FreePool (RamDiskDevicePath);\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Report Status Code with the failure status to indicate that boot failure\r
+ //\r
+ BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status);\r
+ }\r
+ PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
+\r
+\r
+ //\r
+ // Clear the Watchdog Timer after the image returns\r
+ //\r
+ gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);\r
+\r
+ //\r
+ // Set Logo status invalid after trying one boot option\r
+ //\r
+ BootLogo = NULL;\r
+ Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);\r
+ if (!EFI_ERROR (Status) && (BootLogo != NULL)) {\r
+ Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ //\r
+ // Clear Boot Current\r
+ //\r
+ Status = gRT->SetVariable (\r
+ L"BootCurrent",\r
+ &gEfiGlobalVariableGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+ //\r
+ // Deleting variable with current variable implementation shouldn't fail.\r
+ // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted,\r
+ // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND.\r
+ //\r
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
+}\r
+\r
+/**\r
+ Check whether there is a instance in BlockIoDevicePath, which contain multi device path\r
+ instances, has the same partition node with HardDriveDevicePath device path\r
+\r
+ @param BlockIoDevicePath Multi device path instances which need to check\r
+ @param HardDriveDevicePath A device path which starts with a hard drive media\r
+ device path.\r
+\r
+ @retval TRUE There is a matched device path instance.\r
+ @retval FALSE There is no matched device path instance.\r
+\r
+**/\r
+BOOLEAN\r
+BmMatchPartitionDevicePathNode (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,\r
+ IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath\r
+ )\r
+{\r
+ HARDDRIVE_DEVICE_PATH *Node;\r
+\r
+ if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {\r
+ return FALSE;\r
+ }\r
+\r
+ //\r
+ // Match all the partition device path nodes including the nested partition nodes\r
+ //\r
+ while (!IsDevicePathEnd (BlockIoDevicePath)) {\r
+ if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP)\r
+ ) {\r
+ //\r
+ // See if the harddrive device path in blockio matches the orig Hard Drive Node\r
+ //\r
+ Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath;\r
+\r
+ //\r
+ // Match Signature and PartitionNumber.\r
+ // Unused bytes in Signature are initiaized with zeros.\r
+ //\r
+ if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) &&\r
+ (Node->MBRType == HardDriveDevicePath->MBRType) &&\r
+ (Node->SignatureType == HardDriveDevicePath->SignatureType) &&\r
+ (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) {\r
+ return TRUE;\r
+ }\r
+ }\r
+\r
+ BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath);\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+/**\r
+ Emuerate all possible bootable medias in the following order:\r
+ 1. Removable BlockIo - The boot option only points to the removable media\r
+ device, like USB key, DVD, Floppy etc.\r
+ 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device,\r
+ like HardDisk.\r
+ 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting\r
+ SimpleFileSystem Protocol, but not supporting BlockIo\r
+ protocol.\r
+ 4. LoadFile - The boot option points to the media supporting\r
+ LoadFile protocol.\r
+ Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior\r
+\r
+ @param BootOptionCount Return the boot option count which has been found.\r
+\r
+ @retval Pointer to the boot option array.\r
+**/\r
+EFI_BOOT_MANAGER_LOAD_OPTION *\r
+BmEnumerateBootOptions (\r
+ UINTN *BootOptionCount\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *Handles;\r
+ EFI_BLOCK_IO_PROTOCOL *BlkIo;\r
+ UINTN Removable;\r
+ UINTN Index;\r
+ CHAR16 *Description;\r
+\r
+ ASSERT (BootOptionCount != NULL);\r
+\r
+ *BootOptionCount = 0;\r
+ BootOptions = NULL;\r
+\r
+ //\r
+ // Parse removable block io followed by fixed block io\r
+ //\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiBlockIoProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+\r
+ for (Removable = 0; Removable < 2; Removable++) {\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ Handles[Index],\r
+ &gEfiBlockIoProtocolGuid,\r
+ (VOID **) &BlkIo\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip the logical partitions\r
+ //\r
+ if (BlkIo->Media->LogicalPartition) {\r
+ continue;\r
+ }\r
+\r
+ //\r
+ // Skip the fixed block io then the removable block io\r
+ //\r
+ if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {\r
+ continue;\r
+ }\r
+\r
+ Description = BmGetBootDescription (Handles[Index]);\r
+ BootOptions = ReallocatePool (\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
+ BootOptions\r
+ );\r
+ ASSERT (BootOptions != NULL);\r
+\r
+ Status = EfiBootManagerInitializeLoadOption (\r
+ &BootOptions[(*BootOptionCount)++],\r
+ LoadOptionNumberUnassigned,\r
+ LoadOptionTypeBoot,\r
+ LOAD_OPTION_ACTIVE,\r
+ Description,\r
+ DevicePathFromHandle (Handles[Index]),\r
+ NULL,\r
+ 0\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ FreePool (Description);\r
+ }\r
+ }\r
+\r
+ if (HandleCount != 0) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ //\r
+ // Parse simple file system not based on block io\r
+ //\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ Status = gBS->HandleProtocol (\r
+ Handles[Index],\r
+ &gEfiBlockIoProtocolGuid,\r
+ (VOID **) &BlkIo\r
+ );\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // Skip if the file system handle supports a BlkIo protocol, which we've handled in above\r
+ //\r
+ continue;\r
+ }\r
+ Description = BmGetBootDescription (Handles[Index]);\r
+ BootOptions = ReallocatePool (\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
+ BootOptions\r
+ );\r
+ ASSERT (BootOptions != NULL);\r
+\r
+ Status = EfiBootManagerInitializeLoadOption (\r
+ &BootOptions[(*BootOptionCount)++],\r
+ LoadOptionNumberUnassigned,\r
+ LoadOptionTypeBoot,\r
+ LOAD_OPTION_ACTIVE,\r
+ Description,\r
+ DevicePathFromHandle (Handles[Index]),\r
+ NULL,\r
+ 0\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (Description);\r
+ }\r
+\r
+ if (HandleCount != 0) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ //\r
+ // Parse load file protocol\r
+ //\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiLoadFileProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ //\r
+ // Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu().\r
+ //\r
+ if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {\r
+ continue;\r
+ }\r
+\r
+ Description = BmGetBootDescription (Handles[Index]);\r
+ BootOptions = ReallocatePool (\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),\r
+ sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),\r
+ BootOptions\r
+ );\r
+ ASSERT (BootOptions != NULL);\r
+\r
+ Status = EfiBootManagerInitializeLoadOption (\r
+ &BootOptions[(*BootOptionCount)++],\r
+ LoadOptionNumberUnassigned,\r
+ LoadOptionTypeBoot,\r
+ LOAD_OPTION_ACTIVE,\r
+ Description,\r
+ DevicePathFromHandle (Handles[Index]),\r
+ NULL,\r
+ 0\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (Description);\r
+ }\r
+\r
+ if (HandleCount != 0) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount);\r
+ return BootOptions;\r
+}\r
+\r
+/**\r
+ The function enumerates all boot options, creates them and registers them in the BootOrder variable.\r
+**/\r
+VOID\r
+EFIAPI\r
+EfiBootManagerRefreshAllBootOption (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;\r
+ UINTN NvBootOptionCount;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN BootOptionCount;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions;\r
+ UINTN UpdatedBootOptionCount;\r
+ UINTN Index;\r
+ EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager;\r
+\r
+ //\r
+ // Optionally refresh the legacy boot option\r
+ //\r
+ if (mBmRefreshLegacyBootOption != NULL) {\r
+ mBmRefreshLegacyBootOption ();\r
+ }\r
+\r
+ BootOptions = BmEnumerateBootOptions (&BootOptionCount);\r
+\r
+ //\r
+ // Mark the boot option as added by BDS by setting OptionalData to a special GUID\r
+ //\r
+ for (Index = 0; Index < BootOptionCount; Index++) {\r
+ BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid);\r
+ BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);\r
+ }\r
+\r
+ //\r
+ // Locate Platform Boot Options Protocol\r
+ //\r
+ Status = gBS->LocateProtocol (&gEdkiiPlatformBootManagerProtocolGuid,\r
+ NULL,\r
+ (VOID **)&PlatformBootManager);\r
+ if (!EFI_ERROR (Status)) {\r
+ //\r
+ // If found, call platform specific refresh to all auto enumerated and NV\r
+ // boot options.\r
+ //\r
+ Status = PlatformBootManager->RefreshAllBootOptions ((CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions,\r
+ (CONST UINTN)BootOptionCount,\r
+ &UpdatedBootOptions,\r
+ &UpdatedBootOptionCount);\r
+ if (!EFI_ERROR (Status)) {\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+ BootOptions = UpdatedBootOptions;\r
+ BootOptionCount = UpdatedBootOptionCount;\r
+ }\r
+ }\r
+\r
+ NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);\r
+\r
+ //\r
+ // Remove invalid EFI boot options from NV\r
+ //\r
+ for (Index = 0; Index < NvBootOptionCount; Index++) {\r
+ if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||\r
+ (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)\r
+ ) && BmIsAutoCreateBootOption (&NvBootOptions[Index])\r
+ ) {\r
+ //\r
+ // Only check those added by BDS\r
+ // so that the boot options added by end-user or OS installer won't be deleted\r
+ //\r
+ if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) {\r
+ Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);\r
+ //\r
+ // Deleting variable with current variable implementation shouldn't fail.\r
+ //\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Add new EFI boot options to NV\r
+ //\r
+ for (Index = 0; Index < BootOptionCount; Index++) {\r
+ if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) {\r
+ EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);\r
+ //\r
+ // Try best to add the boot options so continue upon failure.\r
+ //\r
+ }\r
+ }\r
+\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+ EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);\r
+}\r
+\r
+/**\r
+ This function is called to get or create the boot option for the Boot Manager Menu.\r
+\r
+ The Boot Manager Menu is shown after successfully booting a boot option.\r
+ This function will first try to search the BootManagerMenuFile is in the same FV as\r
+ the module links to this library. If fails, it will search in all FVs.\r
+\r
+ @param BootOption Return the boot option of the Boot Manager Menu\r
+\r
+ @retval EFI_SUCCESS Successfully register the Boot Manager Menu.\r
+ @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.\r
+ @retval others Return status of gRT->SetVariable (). BootOption still points\r
+ to the Boot Manager Menu even the Status is not EFI_SUCCESS\r
+ and EFI_NOT_FOUND.\r
+**/\r
+EFI_STATUS\r
+BmRegisterBootManagerMenu (\r
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *Description;\r
+ UINTN DescriptionLength;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ UINTN HandleCount;\r
+ EFI_HANDLE *Handles;\r
+ UINTN Index;\r
+\r
+ DevicePath = NULL;\r
+ Description = NULL;\r
+ //\r
+ // Try to find BootManagerMenu from LoadFile protocol\r
+ //\r
+ gBS->LocateHandleBuffer (\r
+ ByProtocol,\r
+ &gEfiLoadFileProtocolGuid,\r
+ NULL,\r
+ &HandleCount,\r
+ &Handles\r
+ );\r
+ for (Index = 0; Index < HandleCount; Index++) {\r
+ if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) {\r
+ DevicePath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));\r
+ Description = BmGetBootDescription (Handles[Index]);\r
+ break;\r
+ }\r
+ }\r
+ if (HandleCount != 0) {\r
+ FreePool (Handles);\r
+ }\r
+\r
+ if (DevicePath == NULL) {\r
+ Status = GetFileDevicePathFromAnyFv (\r
+ PcdGetPtr (PcdBootManagerMenuFile),\r
+ EFI_SECTION_PE32,\r
+ 0,\r
+ &DevicePath\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((EFI_D_WARN, "[Bds]BootManagerMenu FFS section can not be found, skip its boot option registration\n"));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ ASSERT (DevicePath != NULL);\r
+ //\r
+ // Get BootManagerMenu application's description from EFI User Interface Section.\r
+ //\r
+ Status = GetSectionFromAnyFv (\r
+ PcdGetPtr (PcdBootManagerMenuFile),\r
+ EFI_SECTION_USER_INTERFACE,\r
+ 0,\r
+ (VOID **) &Description,\r
+ &DescriptionLength\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ Description = NULL;\r
+ }\r
+ }\r
+\r
+ Status = EfiBootManagerInitializeLoadOption (\r
+ BootOption,\r
+ LoadOptionNumberUnassigned,\r
+ LoadOptionTypeBoot,\r
+ LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,\r
+ (Description != NULL) ? Description : L"Boot Manager Menu",\r
+ DevicePath,\r
+ NULL,\r
+ 0\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ FreePool (DevicePath);\r
+ if (Description != NULL) {\r
+ FreePool (Description);\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN BootOptionCount;\r
+\r
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
+ ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+ );\r
+\r
+ return EfiBootManagerAddLoadOptionVariable (BootOption, (UINTN) -1);\r
+}\r
+\r
+/**\r
+ Return the boot option corresponding to the Boot Manager Menu.\r
+ It may automatically create one if the boot option hasn't been created yet.\r
+\r
+ @param BootOption Return the Boot Manager Menu.\r
+\r
+ @retval EFI_SUCCESS The Boot Manager Menu is successfully returned.\r
+ @retval EFI_NOT_FOUND The Boot Manager Menu cannot be found.\r
+ @retval others Return status of gRT->SetVariable (). BootOption still points\r
+ to the Boot Manager Menu even the Status is not EFI_SUCCESS\r
+ and EFI_NOT_FOUND.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EfiBootManagerGetBootManagerMenu (\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOption\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN BootOptionCount;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;\r
+ UINTN Index;\r
+\r
+ BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);\r
+\r
+ for (Index = 0; Index < BootOptionCount; Index++) {\r
+ if (BmIsBootManagerMenuFilePath (BootOptions[Index].FilePath)) {\r
+ Status = EfiBootManagerInitializeLoadOption (\r
+ BootOption,\r
+ BootOptions[Index].OptionNumber,\r
+ BootOptions[Index].OptionType,\r
+ BootOptions[Index].Attributes,\r
+ BootOptions[Index].Description,\r
+ BootOptions[Index].FilePath,\r
+ BootOptions[Index].OptionalData,\r
+ BootOptions[Index].OptionalDataSize\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ break;\r
+ }\r
+ }\r
+\r
+ EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);\r
+\r
+ //\r
+ // Automatically create the Boot#### for Boot Manager Menu when not found.\r
+ //\r
+ if (Index == BootOptionCount) {\r
+ return BmRegisterBootManagerMenu (BootOption);\r
+ } else {\r
+ return EFI_SUCCESS;\r
+ }\r
+}\r
+\r
+/**\r
+ Get the next possible full path pointing to the load option.\r
+ The routine doesn't guarantee the returned full path points to an existing\r
+ file, and it also doesn't guarantee the existing file is a valid load option.\r
+ BmGetNextLoadOptionBuffer() guarantees.\r
+\r
+ @param FilePath The device path pointing to a load option.\r
+ It could be a short-form device path.\r
+ @param FullPath The full path returned by the routine in last call.\r
+ Set to NULL in first call.\r
+\r
+ @return The next possible full path pointing to the load option.\r
+ Caller is responsible to free the memory.\r
+**/\r
+EFI_DEVICE_PATH_PROTOCOL *\r
+EFIAPI\r
+EfiBootManagerGetNextLoadOptionDevicePath (\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FullPath\r
+ )\r
+{\r
+ return BmGetNextLoadOptionDevicePath(FilePath, FullPath);\r
+}\r