/** @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; | |
if (BootOption == NULL) { | |
return; | |
} | |
if (BootOption->FilePath == NULL || BootOption->OptionType != LoadOptionTypeBoot) { | |
BootOption->Status = EFI_INVALID_PARAMETER; | |
return; | |
} | |
// | |
// 1. Create Boot#### for a temporary boot if there is no match Boot#### (i.e. a boot by selected a EFI Shell using "Boot From File") | |
// | |
OptionNumber = BmFindBootOptionInVariable (BootOption); | |
if (OptionNumber == LoadOptionNumberUnassigned) { | |
Status = BmGetFreeOptionNumber (LoadOptionTypeBoot, &Uint16); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Save the BootOption->OptionNumber to restore later | |
// | |
OptionNumber = Uint16; | |
OriginalOptionNumber = BootOption->OptionNumber; | |
BootOption->OptionNumber = OptionNumber; | |
Status = EfiBootManagerLoadOptionToVariable (BootOption); | |
BootOption->OptionNumber = OriginalOptionNumber; | |
} | |
if (EFI_ERROR (Status)) { | |
DEBUG ((EFI_D_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status)); | |
BootOption->Status = Status; | |
return ; | |
} | |
} | |
// | |
// 2. Set BootCurrent | |
// | |
Uint16 = (UINT16) OptionNumber; | |
BmSetVariableAndReportStatusCodeOnError ( | |
L"BootCurrent", | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, | |
sizeof (UINT16), | |
&Uint16 | |
); | |
// | |
// 3. Signal the EVT_SIGNAL_READY_TO_BOOT event when we are about to load and execute | |
// the boot option. | |
// | |
if (BmIsBootManagerMenuFilePath (BootOption->FilePath)) { | |
DEBUG ((EFI_D_INFO, "[Bds] Booting Boot Manager Menu.\n")); | |
BmStopHotkeyService (NULL, NULL); | |
} else { | |
EfiSignalEventReadyToBoot(); | |
// | |
// Report Status Code to indicate ReadyToBoot was signalled | |
// | |
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); | |
// | |
// 4. Repair system through DriverHealth protocol | |
// | |
BmRepairAllControllers (0); | |
} | |
PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
// | |
// 5. Adjust the different type memory page number just before booting | |
// and save the updated info into the variable for next boot to use | |
// | |
BmSetMemoryTypeInformationVariable ( | |
(BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) | |
); | |
// | |
// 6. Load EFI boot option to ImageHandle | |
// | |
DEBUG_CODE_BEGIN (); | |
if (BootOption->Description == NULL) { | |
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n")); | |
} else { | |
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description)); | |
} | |
DEBUG_CODE_END (); | |
ImageHandle = NULL; | |
RamDiskDevicePath = NULL; | |
if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) { | |
Status = EFI_NOT_FOUND; | |
FilePath = NULL; | |
EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL); | |
FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize); | |
if (FileBuffer != NULL) { | |
RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath); | |
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad)); | |
Status = gBS->LoadImage ( | |
TRUE, | |
gImageHandle, | |
FilePath, | |
FileBuffer, | |
FileSize, | |
&ImageHandle | |
); | |
} | |
if (FileBuffer != NULL) { | |
FreePool (FileBuffer); | |
} | |
if (FilePath != NULL) { | |
FreePool (FilePath); | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created | |
// with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. | |
// If the caller doesn't have the option to defer the execution of an image, we should | |
// unload image for the EFI_SECURITY_VIOLATION to avoid resource leak. | |
// | |
if (Status == EFI_SECURITY_VIOLATION) { | |
gBS->UnloadImage (ImageHandle); | |
} | |
// | |
// Destroy the RAM disk | |
// | |
if (RamDiskDevicePath != NULL) { | |
BmDestroyRamDisk (RamDiskDevicePath); | |
FreePool (RamDiskDevicePath); | |
} | |
// | |
// Report Status Code with the failure status to indicate that the failure to load boot option | |
// | |
BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR, Status); | |
BootOption->Status = Status; | |
return; | |
} | |
} | |
// | |
// Check to see if we should legacy BOOT. If yes then do the legacy boot | |
// Write boot to OS performance data for Legacy boot | |
// | |
if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) { | |
if (mBmLegacyBoot != NULL) { | |
// | |
// Write boot to OS performance data for legacy boot. | |
// | |
PERF_CODE ( | |
// | |
// Create an event to be signalled when Legacy Boot occurs to write performance data. | |
// | |
Status = EfiCreateEventLegacyBootEx( | |
TPL_NOTIFY, | |
BmEndOfBdsPerfCode, | |
NULL, | |
&LegacyBootEvent | |
); | |
ASSERT_EFI_ERROR (Status); | |
); | |
mBmLegacyBoot (BootOption); | |
} else { | |
BootOption->Status = EFI_UNSUPPORTED; | |
} | |
PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
return; | |
} | |
// | |
// Provide the image with its load options | |
// | |
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); | |
ASSERT_EFI_ERROR (Status); | |
if (!BmIsAutoCreateBootOption (BootOption)) { | |
ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize; | |
ImageInfo->LoadOptions = BootOption->OptionalData; | |
} | |
// | |
// Clean to NULL because the image is loaded directly from the firmwares boot manager. | |
// | |
ImageInfo->ParentHandle = NULL; | |
// | |
// Before calling the image, enable the Watchdog Timer for 5 minutes period | |
// | |
gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL); | |
// | |
// Write boot to OS performance data for UEFI boot | |
// | |
PERF_CODE ( | |
BmEndOfBdsPerfCode (NULL, NULL); | |
); | |
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart)); | |
Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData); | |
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status)); | |
BootOption->Status = Status; | |
// | |
// Destroy the RAM disk | |
// | |
if (RamDiskDevicePath != NULL) { | |
BmDestroyRamDisk (RamDiskDevicePath); | |
FreePool (RamDiskDevicePath); | |
} | |
if (EFI_ERROR (Status)) { | |
// | |
// Report Status Code with the failure status to indicate that boot failure | |
// | |
BmReportLoadFailure (EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED, Status); | |
} | |
PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber); | |
// | |
// Clear the Watchdog Timer after the image returns | |
// | |
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL); | |
// | |
// Set Logo status invalid after trying one boot option | |
// | |
BootLogo = NULL; | |
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo); | |
if (!EFI_ERROR (Status) && (BootLogo != NULL)) { | |
Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0); | |
ASSERT_EFI_ERROR (Status); | |
} | |
// | |
// Clear Boot Current | |
// | |
Status = gRT->SetVariable ( | |
L"BootCurrent", | |
&gEfiGlobalVariableGuid, | |
0, | |
0, | |
NULL | |
); | |
// | |
// Deleting variable with current variable implementation shouldn't fail. | |
// When BootXXXX (e.g.: BootManagerMenu) boots BootYYYY, exiting BootYYYY causes BootCurrent deleted, | |
// exiting BootXXXX causes deleting BootCurrent returns EFI_NOT_FOUND. | |
// | |
ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND); | |
} | |
/** | |
Check whether there is a instance in BlockIoDevicePath, which contain multi device path | |
instances, has the same partition node with HardDriveDevicePath device path | |
@param BlockIoDevicePath Multi device path instances which need to check | |
@param HardDriveDevicePath A device path which starts with a hard drive media | |
device path. | |
@retval TRUE There is a matched device path instance. | |
@retval FALSE There is no matched device path instance. | |
**/ | |
BOOLEAN | |
BmMatchPartitionDevicePathNode ( | |
IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath, | |
IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath | |
) | |
{ | |
HARDDRIVE_DEVICE_PATH *Node; | |
if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) { | |
return FALSE; | |
} | |
// | |
// Match all the partition device path nodes including the nested partition nodes | |
// | |
while (!IsDevicePathEnd (BlockIoDevicePath)) { | |
if ((DevicePathType (BlockIoDevicePath) == MEDIA_DEVICE_PATH) && | |
(DevicePathSubType (BlockIoDevicePath) == MEDIA_HARDDRIVE_DP) | |
) { | |
// | |
// See if the harddrive device path in blockio matches the orig Hard Drive Node | |
// | |
Node = (HARDDRIVE_DEVICE_PATH *) BlockIoDevicePath; | |
// | |
// Match Signature and PartitionNumber. | |
// Unused bytes in Signature are initiaized with zeros. | |
// | |
if ((Node->PartitionNumber == HardDriveDevicePath->PartitionNumber) && | |
(Node->MBRType == HardDriveDevicePath->MBRType) && | |
(Node->SignatureType == HardDriveDevicePath->SignatureType) && | |
(CompareMem (Node->Signature, HardDriveDevicePath->Signature, sizeof (Node->Signature)) == 0)) { | |
return TRUE; | |
} | |
} | |
BlockIoDevicePath = NextDevicePathNode (BlockIoDevicePath); | |
} | |
return FALSE; | |
} | |
/** | |
Emuerate all possible bootable medias in the following order: | |
1. Removable BlockIo - The boot option only points to the removable media | |
device, like USB key, DVD, Floppy etc. | |
2. Fixed BlockIo - The boot option only points to a Fixed blockIo device, | |
like HardDisk. | |
3. Non-BlockIo SimpleFileSystem - The boot option points to a device supporting | |
SimpleFileSystem Protocol, but not supporting BlockIo | |
protocol. | |
4. LoadFile - The boot option points to the media supporting | |
LoadFile protocol. | |
Reference: UEFI Spec chapter 3.3 Boot Option Variables Default Boot Behavior | |
@param BootOptionCount Return the boot option count which has been found. | |
@retval Pointer to the boot option array. | |
**/ | |
EFI_BOOT_MANAGER_LOAD_OPTION * | |
BmEnumerateBootOptions ( | |
UINTN *BootOptionCount | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
UINTN HandleCount; | |
EFI_HANDLE *Handles; | |
EFI_BLOCK_IO_PROTOCOL *BlkIo; | |
UINTN Removable; | |
UINTN Index; | |
CHAR16 *Description; | |
ASSERT (BootOptionCount != NULL); | |
*BootOptionCount = 0; | |
BootOptions = NULL; | |
// | |
// Parse removable block io followed by fixed block io | |
// | |
gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiBlockIoProtocolGuid, | |
NULL, | |
&HandleCount, | |
&Handles | |
); | |
for (Removable = 0; Removable < 2; Removable++) { | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
Handles[Index], | |
&gEfiBlockIoProtocolGuid, | |
(VOID **) &BlkIo | |
); | |
if (EFI_ERROR (Status)) { | |
continue; | |
} | |
// | |
// Skip the logical partitions | |
// | |
if (BlkIo->Media->LogicalPartition) { | |
continue; | |
} | |
// | |
// Skip the fixed block io then the removable block io | |
// | |
if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) { | |
continue; | |
} | |
Description = BmGetBootDescription (Handles[Index]); | |
BootOptions = ReallocatePool ( | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), | |
BootOptions | |
); | |
ASSERT (BootOptions != NULL); | |
Status = EfiBootManagerInitializeLoadOption ( | |
&BootOptions[(*BootOptionCount)++], | |
LoadOptionNumberUnassigned, | |
LoadOptionTypeBoot, | |
LOAD_OPTION_ACTIVE, | |
Description, | |
DevicePathFromHandle (Handles[Index]), | |
NULL, | |
0 | |
); | |
ASSERT_EFI_ERROR (Status); | |
FreePool (Description); | |
} | |
} | |
if (HandleCount != 0) { | |
FreePool (Handles); | |
} | |
// | |
// Parse simple file system not based on block io | |
// | |
gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiSimpleFileSystemProtocolGuid, | |
NULL, | |
&HandleCount, | |
&Handles | |
); | |
for (Index = 0; Index < HandleCount; Index++) { | |
Status = gBS->HandleProtocol ( | |
Handles[Index], | |
&gEfiBlockIoProtocolGuid, | |
(VOID **) &BlkIo | |
); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Skip if the file system handle supports a BlkIo protocol, which we've handled in above | |
// | |
continue; | |
} | |
Description = BmGetBootDescription (Handles[Index]); | |
BootOptions = ReallocatePool ( | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), | |
BootOptions | |
); | |
ASSERT (BootOptions != NULL); | |
Status = EfiBootManagerInitializeLoadOption ( | |
&BootOptions[(*BootOptionCount)++], | |
LoadOptionNumberUnassigned, | |
LoadOptionTypeBoot, | |
LOAD_OPTION_ACTIVE, | |
Description, | |
DevicePathFromHandle (Handles[Index]), | |
NULL, | |
0 | |
); | |
ASSERT_EFI_ERROR (Status); | |
FreePool (Description); | |
} | |
if (HandleCount != 0) { | |
FreePool (Handles); | |
} | |
// | |
// Parse load file protocol | |
// | |
gBS->LocateHandleBuffer ( | |
ByProtocol, | |
&gEfiLoadFileProtocolGuid, | |
NULL, | |
&HandleCount, | |
&Handles | |
); | |
for (Index = 0; Index < HandleCount; Index++) { | |
// | |
// Ignore BootManagerMenu. its boot option will be created by EfiBootManagerGetBootManagerMenu(). | |
// | |
if (BmIsBootManagerMenuFilePath (DevicePathFromHandle (Handles[Index]))) { | |
continue; | |
} | |
Description = BmGetBootDescription (Handles[Index]); | |
BootOptions = ReallocatePool ( | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount), | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1), | |
BootOptions | |
); | |
ASSERT (BootOptions != NULL); | |
Status = EfiBootManagerInitializeLoadOption ( | |
&BootOptions[(*BootOptionCount)++], | |
LoadOptionNumberUnassigned, | |
LoadOptionTypeBoot, | |
LOAD_OPTION_ACTIVE, | |
Description, | |
DevicePathFromHandle (Handles[Index]), | |
NULL, | |
0 | |
); | |
ASSERT_EFI_ERROR (Status); | |
FreePool (Description); | |
} | |
if (HandleCount != 0) { | |
FreePool (Handles); | |
} | |
BmMakeBootOptionDescriptionUnique (BootOptions, *BootOptionCount); | |
return BootOptions; | |
} | |
/** | |
The function enumerates all boot options, creates them and registers them in the BootOrder variable. | |
**/ | |
VOID | |
EFIAPI | |
EfiBootManagerRefreshAllBootOption ( | |
VOID | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions; | |
UINTN NvBootOptionCount; | |
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions; | |
UINTN BootOptionCount; | |
EFI_BOOT_MANAGER_LOAD_OPTION *UpdatedBootOptions; | |
UINTN UpdatedBootOptionCount; | |
UINTN Index; | |
EDKII_PLATFORM_BOOT_MANAGER_PROTOCOL *PlatformBootManager; | |
// | |
// Optionally refresh the legacy boot option | |
// | |
if (mBmRefreshLegacyBootOption != NULL) { | |
mBmRefreshLegacyBootOption (); | |
} | |
BootOptions = BmEnumerateBootOptions (&BootOptionCount); | |
// | |
// Mark the boot option as added by BDS by setting OptionalData to a special GUID | |
// | |
for (Index = 0; Index < BootOptionCount; Index++) { | |
BootOptions[Index].OptionalData = AllocateCopyPool (sizeof (EFI_GUID), &mBmAutoCreateBootOptionGuid); | |
BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID); | |
} | |
// | |
// Locate Platform Boot Options Protocol | |
// | |
Status = gBS->LocateProtocol (&gEdkiiPlatformBootManagerProtocolGuid, | |
NULL, | |
(VOID **)&PlatformBootManager); | |
if (!EFI_ERROR (Status)) { | |
// | |
// If found, call platform specific refresh to all auto enumerated and NV | |
// boot options. | |
// | |
Status = PlatformBootManager->RefreshAllBootOptions ((CONST EFI_BOOT_MANAGER_LOAD_OPTION *)BootOptions, | |
(CONST UINTN)BootOptionCount, | |
&UpdatedBootOptions, | |
&UpdatedBootOptionCount); | |
if (!EFI_ERROR (Status)) { | |
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
BootOptions = UpdatedBootOptions; | |
BootOptionCount = UpdatedBootOptionCount; | |
} | |
} | |
NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot); | |
// | |
// Remove invalid EFI boot options from NV | |
// | |
for (Index = 0; Index < NvBootOptionCount; Index++) { | |
if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) || | |
(DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP) | |
) && BmIsAutoCreateBootOption (&NvBootOptions[Index]) | |
) { | |
// | |
// Only check those added by BDS | |
// so that the boot options added by end-user or OS installer won't be deleted | |
// | |
if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == -1) { | |
Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot); | |
// | |
// Deleting variable with current variable implementation shouldn't fail. | |
// | |
ASSERT_EFI_ERROR (Status); | |
} | |
} | |
} | |
// | |
// Add new EFI boot options to NV | |
// | |
for (Index = 0; Index < BootOptionCount; Index++) { | |
if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == -1) { | |
EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1); | |
// | |
// Try best to add the boot options so continue upon failure. | |
// | |
} | |
} | |
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount); | |
EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount); | |
} | |
/** | |
This function is called to get or create the boot option for the Boot Manager Menu. | |
The Boot Manager Menu is shown after successfully booting a boot option. | |
This function will first try to search the BootManagerMenuFile is in the same FV as | |
the module links to this library. If fails, it will search in all FVs. | |
@param BootOption Return the boot option of the Boot Manager Menu | |
@retval EFI_SUCCESS Successfully register the Boot Manager Menu. | |
@retval EFI_NOT_FOUND The Boot Manager Menu cannot be found. | |
@retval others Return status of gRT->SetVariable (). BootOption still points | |
to the Boot Manager Menu even the Status is not EFI_SUCCESS | |
and EFI_NOT_FOUND. | |
**/ | |
EFI_STATUS | |
BmRegisterBootManagerMenu ( | |
OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *Description; | |
UINTN DescriptionLength; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
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); | |
} |