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