| /** @file | |
| Library functions which relates with booting. | |
| Copyright (c) 2011 - 2016, Intel Corporation. All rights reserved.<BR> | |
| (C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "InternalBm.h" | |
| 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 } }; | |
| /** | |
| 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; | |
| } | |
| /** | |
| Get the file buffer using a Memory Mapped Device Path. | |
| FV address may change across reboot. This routine promises the FV file device path is right. | |
| @param FilePath The Memory Mapped Device Path to get the file buffer. | |
| @param FullPath Receive the updated FV Device Path pointint to the file. | |
| @param FileSize Receive the file buffer size. | |
| @return The file buffer. | |
| **/ | |
| VOID * | |
| BmGetFileBufferByFvFilePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *FvFileNode; | |
| EFI_HANDLE FvHandle; | |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
| UINT32 AuthenticationStatus; | |
| UINTN FvHandleCount; | |
| EFI_HANDLE *FvHandles; | |
| EFI_DEVICE_PATH_PROTOCOL *NewDevicePath; | |
| VOID *FileBuffer; | |
| // | |
| // Get the file buffer by using the exactly FilePath. | |
| // | |
| FvFileNode = FilePath; | |
| Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle); | |
| if (!EFI_ERROR (Status)) { | |
| FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus); | |
| if (FileBuffer != NULL) { | |
| *FullPath = DuplicateDevicePath (FilePath); | |
| } | |
| return FileBuffer; | |
| } | |
| // | |
| // 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); | |
| FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize); | |
| FreePool (NewDevicePath); | |
| if (FileBuffer != NULL) { | |
| return FileBuffer; | |
| } | |
| // | |
| // Secondly find the FV file in all other FVs | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiFirmwareVolume2ProtocolGuid, | |
| NULL, | |
| &FvHandleCount, | |
| &FvHandles | |
| ); | |
| for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) { | |
| if (FvHandles[Index] == LoadedImage->DeviceHandle) { | |
| // | |
| // Skip current FV | |
| // | |
| continue; | |
| } | |
| NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode); | |
| FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize); | |
| FreePool (NewDevicePath); | |
| } | |
| if (FvHandles != NULL) { | |
| FreePool (FvHandles); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| 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 Return the full device path of the load option after | |
| short-form device path expanding. | |
| Caller is responsible to free it. | |
| @param FileSize Return the load option size. | |
| @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer. | |
| @return The load option buffer. Caller is responsible to free the memory. | |
| **/ | |
| VOID * | |
| BmExpandUsbDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize, | |
| IN EFI_DEVICE_PATH_PROTOCOL *ShortformNode | |
| ) | |
| { | |
| UINTN ParentDevicePathSize; | |
| EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; | |
| EFI_HANDLE *Handles; | |
| UINTN HandleCount; | |
| UINTN Index; | |
| VOID *FileBuffer; | |
| ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath; | |
| RemainingDevicePath = NextDevicePathNode (ShortformNode); | |
| FileBuffer = NULL; | |
| Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount); | |
| for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) { | |
| FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath); | |
| FileBuffer = BmGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize); | |
| FreePool (FullDevicePath); | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| 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 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 * | |
| BmExpandFileDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN HandleCount; | |
| EFI_HANDLE *Handles; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| UINTN MediaType; | |
| EFI_DEVICE_PATH_PROTOCOL *FullDevicePath; | |
| VOID *FileBuffer; | |
| UINT32 AuthenticationStatus; | |
| EfiBootManagerConnectAll (); | |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles); | |
| if (EFI_ERROR (Status)) { | |
| HandleCount = 0; | |
| Handles = 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) | |
| ) { | |
| FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath); | |
| FileBuffer = GetFileBufferByFilePath (TRUE, FullDevicePath, FileSize, &AuthenticationStatus); | |
| if (FileBuffer != NULL) { | |
| *FullPath = FullDevicePath; | |
| FreePool (Handles); | |
| return FileBuffer; | |
| } | |
| FreePool (FullDevicePath); | |
| } | |
| } | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| *FullPath = NULL; | |
| return NULL; | |
| } | |
| /** | |
| 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 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 * | |
| BmExpandUriDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| UINTN HandleCount; | |
| EFI_HANDLE *Handles; | |
| VOID *FileBuffer; | |
| EfiBootManagerConnectAll (); | |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles); | |
| if (EFI_ERROR (Status)) { | |
| HandleCount = 0; | |
| Handles = NULL; | |
| } | |
| FileBuffer = NULL; | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| FileBuffer = BmGetFileBufferFromLoadFile (Handles[Index], FilePath, FullPath, FileSize); | |
| if (FileBuffer != NULL) { | |
| break; | |
| } | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| Save the partition DevicePath to the CachedDevicePath as the first instance. | |
| @param CachedDevicePath The device path cache. | |
| @param DevicePath The partition device path to be cached. | |
| **/ | |
| VOID | |
| BmCachePartitionDevicePath ( | |
| IN OUT EFI_DEVICE_PATH_PROTOCOL **CachedDevicePath, | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath | |
| ) | |
| { | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| UINTN Count; | |
| if (BmMatchDevicePaths (*CachedDevicePath, DevicePath)) { | |
| TempDevicePath = *CachedDevicePath; | |
| *CachedDevicePath = BmDelPartMatchInstance (*CachedDevicePath, DevicePath); | |
| FreePool (TempDevicePath); | |
| } | |
| if (*CachedDevicePath == NULL) { | |
| *CachedDevicePath = DuplicateDevicePath (DevicePath); | |
| return; | |
| } | |
| TempDevicePath = *CachedDevicePath; | |
| *CachedDevicePath = AppendDevicePathInstance (DevicePath, *CachedDevicePath); | |
| if (TempDevicePath != NULL) { | |
| FreePool (TempDevicePath); | |
| } | |
| // | |
| // Here limit the device path instance number to 12, which is max number for a system support 3 IDE controller | |
| // If the user try to boot many OS in different HDs or partitions, in theory, the 'HDDP' variable maybe become larger and larger. | |
| // | |
| Count = 0; | |
| TempDevicePath = *CachedDevicePath; | |
| while (!IsDevicePathEnd (TempDevicePath)) { | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| // | |
| // Parse one instance | |
| // | |
| while (!IsDevicePathEndType (TempDevicePath)) { | |
| TempDevicePath = NextDevicePathNode (TempDevicePath); | |
| } | |
| Count++; | |
| // | |
| // If the CachedDevicePath variable contain too much instance, only remain 12 instances. | |
| // | |
| if (Count == 12) { | |
| SetDevicePathEndNode (TempDevicePath); | |
| break; | |
| } | |
| } | |
| } | |
| /** | |
| Expand a device path that starts with a hard drive media device path node to be a | |
| full device path that includes the full hardware path to the device. We need | |
| to do this so it can be booted. As an optimization the front match (the part point | |
| to the partition node. E.g. ACPI() /PCI()/ATA()/Partition() ) is saved in a variable | |
| so a connect all is not required on every boot. All successful history device path | |
| which point to partition node (the front part) will be saved. | |
| @param FilePath The device path pointing to a load option. | |
| It could be a short-form device path. | |
| @param FullPath Return the full device path of the load option after | |
| short-form device path expanding. | |
| Caller is responsible to free it. | |
| @param FileSize Return the load option size. | |
| @return The load option buffer. Caller is responsible to free the memory. | |
| **/ | |
| VOID * | |
| BmExpandPartitionDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN BlockIoHandleCount; | |
| EFI_HANDLE *BlockIoBuffer; | |
| VOID *FileBuffer; | |
| EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath; | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| UINTN CachedDevicePathSize; | |
| BOOLEAN NeedAdjust; | |
| EFI_DEVICE_PATH_PROTOCOL *Instance; | |
| UINTN Size; | |
| FileBuffer = NULL; | |
| // | |
| // Check if there is prestore 'HDDP' variable. | |
| // If exist, search the front path which point to partition node in the variable instants. | |
| // If fail to find or 'HDDP' not exist, reconnect all and search in all system | |
| // | |
| GetVariable2 (L"HDDP", &mBmHardDriveBootVariableGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize); | |
| // | |
| // Delete the invalid 'HDDP' variable. | |
| // | |
| if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) { | |
| FreePool (CachedDevicePath); | |
| CachedDevicePath = NULL; | |
| Status = gRT->SetVariable ( | |
| L"HDDP", | |
| &mBmHardDriveBootVariableGuid, | |
| 0, | |
| 0, | |
| NULL | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| if (CachedDevicePath != NULL) { | |
| TempNewDevicePath = CachedDevicePath; | |
| NeedAdjust = FALSE; | |
| do { | |
| // | |
| // Check every instance of the variable | |
| // First, check whether the instance contain the partition node, which is needed for distinguishing multi | |
| // partial partition boot option. Second, check whether the instance could be connected. | |
| // | |
| Instance = GetNextDevicePathInstance (&TempNewDevicePath, &Size); | |
| if (BmMatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) FilePath)) { | |
| // | |
| // Connect the device path instance, the device path point to hard drive media device path node | |
| // e.g. ACPI() /PCI()/ATA()/Partition() | |
| // | |
| Status = EfiBootManagerConnectDevicePath (Instance, NULL); | |
| if (!EFI_ERROR (Status)) { | |
| TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath)); | |
| FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize); | |
| FreePool (TempDevicePath); | |
| if (FileBuffer != NULL) { | |
| // | |
| // Adjust the 'HDDP' instances sequence if the matched one is not first one. | |
| // | |
| if (NeedAdjust) { | |
| BmCachePartitionDevicePath (&CachedDevicePath, Instance); | |
| // | |
| // Save the matching Device Path so we don't need to do a connect all next time | |
| // Failing to save only impacts performance next time expanding the short-form device path | |
| // | |
| Status = gRT->SetVariable ( | |
| L"HDDP", | |
| &mBmHardDriveBootVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| GetDevicePathSize (CachedDevicePath), | |
| CachedDevicePath | |
| ); | |
| } | |
| FreePool (Instance); | |
| FreePool (CachedDevicePath); | |
| return FileBuffer; | |
| } | |
| } | |
| } | |
| // | |
| // Come here means the first instance is not matched | |
| // | |
| NeedAdjust = TRUE; | |
| FreePool(Instance); | |
| } while (TempNewDevicePath != NULL); | |
| } | |
| // | |
| // If we get here we fail to find or 'HDDP' not exist, and now we need | |
| // to search all devices in the system for a matched partition | |
| // | |
| EfiBootManagerConnectAll (); | |
| Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiBlockIoProtocolGuid, NULL, &BlockIoHandleCount, &BlockIoBuffer); | |
| if (EFI_ERROR (Status)) { | |
| BlockIoHandleCount = 0; | |
| BlockIoBuffer = NULL; | |
| } | |
| // | |
| // Loop through all the device handles that support the BLOCK_IO Protocol | |
| // | |
| for (Index = 0; Index < BlockIoHandleCount; Index++) { | |
| BlockIoDevicePath = DevicePathFromHandle (BlockIoBuffer[Index]); | |
| if (BlockIoDevicePath == NULL) { | |
| continue; | |
| } | |
| if (BmMatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) FilePath)) { | |
| // | |
| // Find the matched partition device path | |
| // | |
| TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath)); | |
| FileBuffer = BmGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize); | |
| FreePool (TempDevicePath); | |
| if (FileBuffer != NULL) { | |
| BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath); | |
| // | |
| // Save the matching Device Path so we don't need to do a connect all next time | |
| // Failing to save only impacts performance next time expanding the short-form device path | |
| // | |
| Status = gRT->SetVariable ( | |
| L"HDDP", | |
| &mBmHardDriveBootVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
| GetDevicePathSize (CachedDevicePath), | |
| CachedDevicePath | |
| ); | |
| break; | |
| } | |
| } | |
| } | |
| if (CachedDevicePath != NULL) { | |
| FreePool (CachedDevicePath); | |
| } | |
| if (BlockIoBuffer != NULL) { | |
| FreePool (BlockIoBuffer); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| Expand the media device path which points to a BlockIo or SimpleFileSystem instance | |
| by appending EFI_REMOVABLE_MEDIA_FILE_NAME. | |
| @param DevicePath The media device path pointing to a BlockIo or SimpleFileSystem instance. | |
| @param FullPath Return the full device path pointing to the load option. | |
| @param FileSize Return the size of the load option. | |
| @return The load option buffer. | |
| **/ | |
| VOID * | |
| BmExpandMediaDevicePath ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE Handle; | |
| EFI_BLOCK_IO_PROTOCOL *BlockIo; | |
| VOID *Buffer; | |
| EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; | |
| UINTN Size; | |
| UINTN TempSize; | |
| EFI_HANDLE *SimpleFileSystemHandles; | |
| UINTN NumberSimpleFileSystemHandles; | |
| UINTN Index; | |
| VOID *FileBuffer; | |
| UINT32 AuthenticationStatus; | |
| // | |
| // Check whether the device is connected | |
| // | |
| TempDevicePath = DevicePath; | |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &TempDevicePath, &Handle); | |
| if (!EFI_ERROR (Status)) { | |
| ASSERT (IsDevicePathEnd (TempDevicePath)); | |
| TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME); | |
| FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus); | |
| if (FileBuffer == NULL) { | |
| FreePool (TempDevicePath); | |
| TempDevicePath = NULL; | |
| } | |
| *FullPath = TempDevicePath; | |
| return FileBuffer; | |
| } | |
| // | |
| // For device boot option only pointing to the removable device handle, | |
| // should make sure all its children handles (its child partion or media handles) are created and connected. | |
| // | |
| gBS->ConnectController (Handle, NULL, NULL, TRUE); | |
| // | |
| // Issue a dummy read to the device to check for media change. | |
| // When the removable media is changed, any Block IO read/write will | |
| // cause the BlockIo protocol be reinstalled and EFI_MEDIA_CHANGED is | |
| // returned. After the Block IO protocol is reinstalled, subsequent | |
| // Block IO read/write will success. | |
| // | |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); | |
| ASSERT_EFI_ERROR (Status); | |
| Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo); | |
| ASSERT_EFI_ERROR (Status); | |
| Buffer = AllocatePool (BlockIo->Media->BlockSize); | |
| if (Buffer != NULL) { | |
| BlockIo->ReadBlocks ( | |
| BlockIo, | |
| BlockIo->Media->MediaId, | |
| 0, | |
| BlockIo->Media->BlockSize, | |
| Buffer | |
| ); | |
| FreePool (Buffer); | |
| } | |
| // | |
| // Detect the the default boot file from removable Media | |
| // | |
| FileBuffer = NULL; | |
| *FullPath = NULL; | |
| Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH; | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &NumberSimpleFileSystemHandles, | |
| &SimpleFileSystemHandles | |
| ); | |
| for (Index = 0; Index < NumberSimpleFileSystemHandles; Index++) { | |
| // | |
| // Get the device path size of SimpleFileSystem handle | |
| // | |
| TempDevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); | |
| TempSize = GetDevicePathSize (TempDevicePath) - END_DEVICE_PATH_LENGTH; | |
| // | |
| // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path | |
| // | |
| if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) { | |
| TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME); | |
| FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus); | |
| if (FileBuffer != NULL) { | |
| *FullPath = TempDevicePath; | |
| break; | |
| } | |
| FreePool (TempDevicePath); | |
| } | |
| } | |
| if (SimpleFileSystemHandles != NULL) { | |
| FreePool (SimpleFileSystemHandles); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| 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_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 FullPath Return the full device path pointing to the load option. | |
| @param FileSize Return the size of the load option. | |
| @param RamDiskHandle Return the RAM Disk handle. | |
| @return The load option buffer. | |
| **/ | |
| VOID * | |
| BmGetFileBufferFromLoadFileSystem ( | |
| IN EFI_HANDLE LoadFileHandle, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize, | |
| 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)) { | |
| Handle = Handles[Index]; | |
| break; | |
| } | |
| } | |
| if (Handles != NULL) { | |
| FreePool (Handles); | |
| } | |
| if (Index == HandleCount) { | |
| Handle = NULL; | |
| } | |
| *RamDiskHandle = Handle; | |
| if (Handle != NULL) { | |
| return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), FullPath, FileSize); | |
| } 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(). | |
| @param FullPath Return the full device path pointing to the load option. | |
| @param FileSize Return the size of the load option. | |
| @return The load option buffer or NULL if fails. | |
| **/ | |
| VOID * | |
| BmGetFileBufferFromLoadFile ( | |
| IN EFI_HANDLE LoadFileHandle, | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_LOAD_FILE_PROTOCOL *LoadFile; | |
| VOID *FileBuffer; | |
| BOOLEAN LoadFileSystem; | |
| EFI_HANDLE RamDiskHandle; | |
| UINTN BufferSize; | |
| *FileSize = 0; | |
| 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; | |
| } | |
| LoadFileSystem = (BOOLEAN) (Status == EFI_WARN_FILE_SYSTEM); | |
| FileBuffer = LoadFileSystem ? AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)) : AllocatePool (BufferSize); | |
| if (FileBuffer == NULL) { | |
| return NULL; | |
| } | |
| Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer); | |
| if (EFI_ERROR (Status)) { | |
| if (LoadFileSystem) { | |
| FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize)); | |
| } else { | |
| FreePool (FileBuffer); | |
| } | |
| return NULL; | |
| } | |
| if (LoadFileSystem) { | |
| FileBuffer = BmGetFileBufferFromLoadFileSystem (LoadFileHandle, FullPath, FileSize, &RamDiskHandle); | |
| if (FileBuffer == NULL) { | |
| // | |
| // If there is no bootable executable in the populated | |
| // | |
| BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle)); | |
| } | |
| } else { | |
| *FileSize = BufferSize; | |
| *FullPath = DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle)); | |
| } | |
| return FileBuffer; | |
| } | |
| /** | |
| Get the file buffer from all the Load File instances. | |
| @param FilePath The media device path pointing to a LoadFile instance. | |
| @param FullPath Return the full device path pointing to the load option. | |
| @param FileSize Return the size of the load option. | |
| @return The load option buffer. | |
| **/ | |
| VOID * | |
| BmGetFileBufferFromLoadFiles ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| 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 BmGetFileBufferFromLoadFile (Handle, FilePath, FullPath, FileSize); | |
| } | |
| /** | |
| Get the load option by its device path. | |
| @param FilePath The device path pointing to a load option. | |
| It could be a short-form device path. | |
| @param FullPath Return the full device path of the load option after | |
| short-form device path expanding. | |
| Caller is responsible to free it. | |
| @param FileSize Return the load option size. | |
| @return The load option buffer. Caller is responsible to free the memory. | |
| **/ | |
| VOID * | |
| BmGetLoadOptionBuffer ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
| OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
| OUT UINTN *FileSize | |
| ) | |
| { | |
| EFI_HANDLE Handle; | |
| VOID *FileBuffer; | |
| UINT32 AuthenticationStatus; | |
| EFI_DEVICE_PATH_PROTOCOL *Node; | |
| EFI_STATUS Status; | |
| ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL)); | |
| EfiBootManagerConnectDevicePath (FilePath, NULL); | |
| *FullPath = NULL; | |
| *FileSize = 0; | |
| FileBuffer = NULL; | |
| // | |
| // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI | |
| // | |
| Node = FilePath; | |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); | |
| if (EFI_ERROR (Status)) { | |
| Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &Node, &Handle); | |
| } | |
| if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) { | |
| return BmExpandMediaDevicePath (FilePath, FullPath, FileSize); | |
| } | |
| // | |
| // Expand the short-form device path to full device path | |
| // | |
| if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (FilePath) == MEDIA_HARDDRIVE_DP)) { | |
| // | |
| // Expand the Harddrive device path | |
| // | |
| return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize); | |
| } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) { | |
| // | |
| // Expand the File-path device path | |
| // | |
| return BmExpandFileDevicePath (FilePath, FullPath, FileSize); | |
| } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) && | |
| (DevicePathSubType (FilePath) == MSG_URI_DP)) { | |
| // | |
| // Expand the URI device path | |
| // | |
| return BmExpandUriDevicePath (FilePath, FullPath, FileSize); | |
| } else { | |
| for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) { | |
| if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && | |
| ((DevicePathSubType (Node) == MSG_USB_CLASS_DP) || (DevicePathSubType (Node) == MSG_USB_WWID_DP))) { | |
| break; | |
| } | |
| } | |
| if (!IsDevicePathEnd (Node)) { | |
| // | |
| // Expand the USB WWID/Class device path | |
| // | |
| FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node); | |
| if ((FileBuffer == NULL) && (FilePath == Node)) { | |
| // | |
| // Boot Option device path starts with USB Class or USB WWID device path. | |
| // For Boot Option device path which doesn't begin with the USB Class or | |
| // USB WWID device path, it's not needed to connect again here. | |
| // | |
| BmConnectUsbShortFormDevicePath (FilePath); | |
| FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node); | |
| } | |
| return FileBuffer; | |
| } | |
| } | |
| // | |
| // Get file buffer from FV file path. | |
| // | |
| if (BmIsFvFilePath (FilePath)) { | |
| return BmGetFileBufferByFvFilePath (FilePath, FullPath, FileSize); | |
| } | |
| // | |
| // Get file buffer from simple file system. | |
| // | |
| Node = FilePath; | |
| Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle); | |
| if (!EFI_ERROR (Status)) { | |
| FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus); | |
| if (FileBuffer != NULL) { | |
| *FullPath = DuplicateDevicePath (FilePath); | |
| } | |
| return FileBuffer; | |
| } | |
| return BmGetFileBufferFromLoadFiles (FilePath, FullPath, FileSize); | |
| } | |
| /** | |
| Attempt to boot the EFI boot option. This routine sets L"BootCurent" and | |
| also signals the EFI ready to boot event. If the device path for the option | |
| starts with a BBS device path a legacy boot is attempted via the registered | |
| gLegacyBoot function. Short form device paths are also supported via this | |
| rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP, | |
| MSG_USB_CLASS_DP gets expaned out to find the first device that matches. | |
| If the BootOption Device Path fails the removable media boot algorithm | |
| is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type | |
| is tried per processor type) | |
| @param BootOption Boot Option to try and boot. | |
| On return, BootOption->Status contains the boot status. | |
| EFI_SUCCESS BootOption was booted | |
| EFI_UNSUPPORTED A BBS device path was found with no valid callback | |
| registered via EfiBootManagerInitialize(). | |
| EFI_NOT_FOUND The BootOption was not found on the system | |
| !EFI_SUCCESS BootOption failed with this error status | |
| **/ | |
| VOID | |
| EFIAPI | |
| EfiBootManagerBoot ( | |
| IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_HANDLE ImageHandle; | |
| EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; | |
| UINT16 Uint16; | |
| UINTN OptionNumber; | |
| UINTN OriginalOptionNumber; | |
| EFI_DEVICE_PATH_PROTOCOL *FilePath; | |
| EFI_DEVICE_PATH_PROTOCOL *Node; | |
| EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; | |
| EFI_HANDLE FvHandle; | |
| VOID *FileBuffer; | |
| UINTN FileSize; | |
| EFI_BOOT_LOGO_PROTOCOL *BootLogo; | |
| EFI_EVENT LegacyBootEvent; | |
| UINTN RamDiskSizeInPages; | |
| if (BootOption == NULL) { | |
| return; | |
| } | |
| if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) { | |
| BootOption->Status = EFI_INVALID_PARAMETER; | |
| return; | |
| } | |
| // | |
| // 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File") | |
| // | |
| OptionNumber = BmFindBootOptionInVariable (BootOption); | |
| if (OptionNumber == LoadOptionNumberUnassigned) { | |
| Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Save the BootOption->OptionNumber to restore later | |
| // | |
| OptionNumber = Uint16; | |
| OriginalOptionNumber = BootOption->OptionNumber; | |
| BootOption->OptionNumber = OptionNumber; | |
| Status = EfiBootManagerLoadOptionToVariable (BootOption); | |
| BootOption->OptionNumber = OriginalOptionNumber; | |
| } | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status)); | |
| BootOption->Status = Status; | |
| return ; | |
| } | |
| } | |
| // | |
| // 2. Set BootCurrent | |
| // | |
| Uint16 = (UINT16) OptionNumber; | |
| BmSetVariableAndReportStatusCodeOnError ( | |
| L"BootCurrent", | |
| &gEfiGlobalVariableGuid, | |
| EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
| sizeof (UINT16), | |
| &Uint16 | |
| ); | |
| // | |
| // 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute | |
| // the boot option. | |
| // | |
| Node = BootOption->FilePath; | |
| Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); | |
| if (!EFI_ERROR (Status) && CompareGuid ( | |
| EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node), | |
| PcdGetPtr (PcdBootManagerMenuFile) | |
| )) { | |
| DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n")); | |
| BmStopHotkeyService (NULL, NULL); | |
| } else { | |
| EfiSignalEventReadyToBoot(); | |
| // | |
| // Report Status Code to indicate ReadyToBoot was signalled | |
| // | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); | |
| // | |
| // 4. Repair system through DriverHealth protocol | |
| // | |
| BmRepairAllControllers (); | |
| } | |
| PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
| // | |
| // 5. Load EFI boot option to ImageHandle | |
| // | |
| ImageHandle = NULL; | |
| RamDiskDevicePath = NULL; | |
| if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { | |
| Status = EFI_NOT_FOUND; | |
| FileBuffer = BmGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize); | |
| if (FileBuffer != NULL) { | |
| RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath); | |
| } | |
| DEBUG_CODE ( | |
| if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) { | |
| DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: ")); | |
| BmPrintDp (BootOption->FilePath); | |
| DEBUG ((EFI_D_INFO, " -> ")); | |
| BmPrintDp (FilePath); | |
| DEBUG ((EFI_D_INFO, "\n")); | |
| } | |
| ); | |
| if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) { | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
| Status = gBS->LoadImage ( | |
| TRUE, | |
| gImageHandle, | |
| FilePath, | |
| FileBuffer, | |
| FileSize, | |
| &ImageHandle | |
| ); | |
| } | |
| if (FileBuffer != NULL) { | |
| FreePool (FileBuffer); | |
| } | |
| if (FilePath != NULL) { | |
| FreePool (FilePath); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Report Status Code to indicate that the failure to load boot option | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR) | |
| ); | |
| BootOption->Status = Status; | |
| // | |
| // Destroy the RAM disk | |
| // | |
| if (RamDiskDevicePath != NULL) { | |
| BmDestroyRamDisk (RamDiskDevicePath); | |
| FreePool (RamDiskDevicePath); | |
| } | |
| return; | |
| } | |
| } | |
| // | |
| // 6. Adjust the different type memory page number just before booting | |
| // and save the updated info into the variable for next boot to use | |
| // | |
| if (RamDiskDevicePath == NULL) { | |
| RamDiskSizeInPages = 0; | |
| } else { | |
| BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages); | |
| } | |
| BmSetMemoryTypeInformationVariable ( | |
| (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT), | |
| RamDiskSizeInPages | |
| ); | |
| DEBUG_CODE_BEGIN(); | |
| if (BootOption->Description == NULL) { | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n")); | |
| } else { | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description)); | |
| } | |
| DEBUG_CODE_END(); | |
| // | |
| // Check to see if we should legacy BOOT. If yes then do the legacy boot | |
| // Write boot to OS performance data for Legacy boot | |
| // | |
| if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { | |
| if (mBmLegacyBoot != NULL) { | |
| // | |
| // Write boot to OS performance data for legacy boot. | |
| // | |
| PERF_CODE ( | |
| // | |
| // Create an event to be signalled when Legacy Boot occurs to write performance data. | |
| // | |
| Status = EfiCreateEventLegacyBootEx( | |
| TPL_NOTIFY, | |
| BmWriteBootToOsPerformanceData, | |
| NULL, | |
| &LegacyBootEvent | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| ); | |
| mBmLegacyBoot (BootOption); | |
| } else { | |
| BootOption->Status = EFI_UNSUPPORTED; | |
| } | |
| PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
| return; | |
| } | |
| // | |
| // Provide the image with its load options | |
| // | |
| Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); | |
| ASSERT_EFI_ERROR (Status); | |
| 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 ( | |
| BmWriteBootToOsPerformanceData (NULL, NULL); | |
| ); | |
| REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); | |
| Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData); | |
| DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); | |
| BootOption->Status = Status; | |
| if (EFI_ERROR (Status)) { | |
| // | |
| // Report Status Code to indicate that boot failure | |
| // | |
| REPORT_STATUS_CODE ( | |
| EFI_ERROR_CODE | EFI_ERROR_MINOR, | |
| (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED) | |
| ); | |
| } | |
| PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
| // | |
| // Destroy the RAM disk | |
| // | |
| if (RamDiskDevicePath != NULL) { | |
| BmDestroyRamDisk (RamDiskDevicePath); | |
| FreePool (RamDiskDevicePath); | |
| } | |
| // | |
| // Clear the Watchdog Timer after the image returns | |
| // | |
| gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); | |
| // | |
| // Set Logo status invalid after trying one boot option | |
| // | |
| BootLogo = NULL; | |
| Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo); | |
| if (!EFI_ERROR (Status) && (BootLogo != NULL)) { | |
| Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| // | |
| // Clear Boot Current | |
| // | |
| Status = gRT->SetVariable ( | |
| L"BootCurrent", | |
| &gEfiGlobalVariableGuid, | |
| 0, | |
| 0, | |
| NULL | |
| ); | |
| // | |
| // Deleting variable with current variable implementation shouldn't fail. | |
| // When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted, | |
| // exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND. | |
| // | |
| ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); | |
| } | |
| /** | |
| Check whether there is a instance in BlockIoDevicePath, which contain multi device path | |
| instances, has the same partition node with HardDriveDevicePath device path | |
| @param BlockIoDevicePath Multi device path instances which need to check | |
| @param HardDriveDevicePath A device path which starts with a hard drive media | |
| device path. | |
| @retval TRUE There is a matched device path instance. | |
| @retval FALSE There is no matched device path instance. | |
| **/ | |
| BOOLEAN | |
| BmMatchPartitionDevicePathNode ( | |
| IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, | |
| IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath | |
| ) | |
| { | |
| HARDDRIVE_DEVICE_PATH *Node; | |
| if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { | |
| return FALSE; | |
| } | |
| // | |
| // find the partition device path node | |
| // | |
| while (!IsDevicePathEnd (BlockIoDevicePath)) { | |
| if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) && | |
| (DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP) | |
| ) { | |
| break; | |
| } | |
| BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath); | |
| } | |
| if (IsDevicePathEnd (BlockIoDevicePath)) { | |
| return FALSE; | |
| } | |
| // | |
| // See if the harddrive device path in blockio matches the orig Hard Drive Node | |
| // | |
| Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath; | |
| // | |
| // Match Signature and PartitionNumber. | |
| // Unused bytes in Signature are initiaized with zeros. | |
| // | |
| return (BOOLEAN) ( | |
| (Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) && | |
| (Node->MBRType == HardDriveDevicePath->MBRType) && | |
| (Node->SignatureType == HardDriveDevicePath->SignatureType) && | |
| (CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0) | |
| ); | |
| } | |
| /** | |
| Emuerate all possible bootable medias in the following order: | |
| 1. Removable BlockIo - The boot option only points to the removable media | |
| device, like USB key, DVD, Floppy etc. | |
| 2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, | |
| like HardDisk. | |
| 3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting | |
| SimpleFileSystem Protocol, but not supporting BlockIo | |
| protocol. | |
| 4. LoadFile - The boot option points to the media supporting | |
| LoadFile protocol. | |
| Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior | |
| @param BootOptionCount Return the boot option count which has been found. | |
| @retval Pointer to the boot option array. | |
| **/ | |
| EFI_BOOT_MANAGER_LOAD_OPTION * | |
| BmEnumerateBootOptions ( | |
| UINTN *BootOptionCount | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN HandleCount; | |
| EFI_HANDLE *Handles; | |
| EFI_BLOCK_IO_PROTOCOL *BlkIo; | |
| UINTN Removable; | |
| UINTN Index; | |
| CHAR16 *Description; | |
| ASSERT (BootOptionCount != NULL); | |
| *BootOptionCount = 0; | |
| BootOptions = NULL; | |
| // | |
| // Parse removable block io followed by fixed block io | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiBlockIoProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &Handles | |
| ); | |
| for (Removable = 0; Removable < 2; Removable++) { | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| Handles[Index], | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlkIo | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| // | |
| // Skip the logical partitions | |
| // | |
| if (BlkIo->Media->LogicalPartition) { | |
| continue; | |
| } | |
| // | |
| // Skip the fixed block io then the removable block io | |
| // | |
| if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) { | |
| continue; | |
| } | |
| Description = BmGetBootDescription (Handles[Index]); | |
| BootOptions = ReallocatePool ( | |
| sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), | |
| sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), | |
| BootOptions | |
| ); | |
| ASSERT (BootOptions != NULL); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &BootOptions[(*BootOptionCount)++], | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| LOAD_OPTION_ACTIVE, | |
| Description, | |
| DevicePathFromHandle (Handles[Index]), | |
| NULL, | |
| 0 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (Description); | |
| } | |
| } | |
| if (HandleCount != 0) { | |
| FreePool (Handles); | |
| } | |
| // | |
| // Parse simple file system not based on block io | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiSimpleFileSystemProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &Handles | |
| ); | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| Status = gBS->HandleProtocol ( | |
| Handles[Index], | |
| &gEfiBlockIoProtocolGuid, | |
| (VOID **) &BlkIo | |
| ); | |
| if (!EFI_ERROR (Status)) { | |
| // | |
| // Skip if the file system handle supports a BlkIo protocol, which we've handled in above | |
| // | |
| continue; | |
| } | |
| Description = BmGetBootDescription (Handles[Index]); | |
| BootOptions = ReallocatePool ( | |
| sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), | |
| sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), | |
| BootOptions | |
| ); | |
| ASSERT (BootOptions != NULL); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| &BootOptions[(*BootOptionCount)++], | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| LOAD_OPTION_ACTIVE, | |
| Description, | |
| DevicePathFromHandle (Handles[Index]), | |
| NULL, | |
| 0 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (Description); | |
| } | |
| if (HandleCount != 0) { | |
| FreePool (Handles); | |
| } | |
| // | |
| // Parse load file, assuming UEFI Network boot option | |
| // | |
| gBS->LocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiLoadFileProtocolGuid, | |
| NULL, | |
| &HandleCount, | |
| &Handles | |
| ); | |
| for (Index = 0; Index < HandleCount; Index++) { | |
| 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; | |
| UINTN Index; | |
| // | |
| // Optionally refresh the legacy boot option | |
| // | |
| if (mBmRefreshLegacyBootOption != NULL) { | |
| mBmRefreshLegacyBootOption (); | |
| } | |
| BootOptions = BmEnumerateBootOptions (&BootOptionCount); | |
| NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); | |
| // | |
| // Mark the boot option as added by BDS by setting OptionalData to a special GUID | |
| // | |
| for (Index = 0; Index < BootOptionCount; Index++) { | |
| BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); | |
| BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID); | |
| } | |
| // | |
| // Remove invalid EFI boot options from NV | |
| // | |
| for (Index = 0; Index < NvBootOptionCount; Index++) { | |
| if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || | |
| (DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP) | |
| ) && 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) == (UINTN) -1) { | |
| Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot); | |
| // | |
| // Deleting variable with current variable implementation shouldn't fail. | |
| // | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| } | |
| } | |
| // | |
| // Add new EFI boot options to NV | |
| // | |
| for (Index = 0; Index < BootOptionCount; Index++) { | |
| if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) { | |
| EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1); | |
| // | |
| // Try best to add the boot options so continue upon failure. | |
| // | |
| } | |
| } | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount); | |
| } | |
| /** | |
| This function is called to create the boot option for the Boot Manager Menu. | |
| The Boot Manager Menu is shown after successfully booting a boot option. | |
| Assume the BootManagerMenuFile is in the same FV as the module links to this library. | |
| @param BootOption Return the boot option of the Boot Manager Menu | |
| @retval EFI_SUCCESS Successfully register the Boot Manager Menu. | |
| @retval Status Return status of gRT->SetVariable (). BootOption still points | |
| to the Boot Manager Menu even the Status is not EFI_SUCCESS. | |
| **/ | |
| EFI_STATUS | |
| BmRegisterBootManagerMenu ( | |
| OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| CHAR16 *Description; | |
| UINTN DescriptionLength; | |
| EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
| MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode; | |
| Status = GetSectionFromFv ( | |
| PcdGetPtr (PcdBootManagerMenuFile), | |
| EFI_SECTION_USER_INTERFACE, | |
| 0, | |
| (VOID **) &Description, | |
| &DescriptionLength | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| Description = NULL; | |
| } | |
| EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile)); | |
| Status = gBS->HandleProtocol ( | |
| gImageHandle, | |
| &gEfiLoadedImageProtocolGuid, | |
| (VOID **) &LoadedImage | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| DevicePath = AppendDevicePathNode ( | |
| DevicePathFromHandle (LoadedImage->DeviceHandle), | |
| (EFI_DEVICE_PATH_PROTOCOL *) &FileNode | |
| ); | |
| ASSERT (DevicePath != NULL); | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| BootOption, | |
| LoadOptionNumberUnassigned, | |
| LoadOptionTypeBoot, | |
| LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN, | |
| (Description != NULL) ? Description : L"Boot Manager Menu", | |
| DevicePath, | |
| NULL, | |
| 0 | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| FreePool (DevicePath); | |
| if (Description != NULL) { | |
| FreePool (Description); | |
| } | |
| DEBUG_CODE ( | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN BootOptionCount; | |
| BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); | |
| ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1); | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| ); | |
| return EfiBootManagerAddLoadOptionVariable (BootOption, 0); | |
| } | |
| /** | |
| Return the boot option corresponding to the Boot Manager Menu. | |
| It may automatically create one if the boot option hasn't been created yet. | |
| @param BootOption Return the Boot Manager Menu. | |
| @retval EFI_SUCCESS The Boot Manager Menu is successfully returned. | |
| @retval Status Return status of gRT->SetVariable (). BootOption still points | |
| to the Boot Manager Menu even the Status is not EFI_SUCCESS. | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| EfiBootManagerGetBootManagerMenu ( | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOption | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN BootOptionCount; | |
| EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
| UINTN Index; | |
| EFI_DEVICE_PATH_PROTOCOL *Node; | |
| EFI_HANDLE FvHandle; | |
| BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); | |
| for (Index = 0; Index < BootOptionCount; Index++) { | |
| Node = BootOptions[Index].FilePath; | |
| Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle); | |
| if (!EFI_ERROR (Status)) { | |
| if (CompareGuid ( | |
| EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node), | |
| PcdGetPtr (PcdBootManagerMenuFile) | |
| ) | |
| ) { | |
| Status = EfiBootManagerInitializeLoadOption ( | |
| BootOption, | |
| BootOptions[Index].OptionNumber, | |
| BootOptions[Index].OptionType, | |
| BootOptions[Index].Attributes, | |
| BootOptions[Index].Description, | |
| BootOptions[Index].FilePath, | |
| BootOptions[Index].OptionalData, | |
| BootOptions[Index].OptionalDataSize | |
| ); | |
| ASSERT_EFI_ERROR (Status); | |
| break; | |
| } | |
| } | |
| } | |
| EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
| // | |
| // Automatically create the Boot#### for Boot Manager Menu when not found. | |
| // | |
| if (Index == BootOptionCount) { | |
| return BmRegisterBootManagerMenu (BootOption); | |
| } else { | |
| return EFI_SUCCESS; | |
| } | |
| } | |