mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-26 16:47:40 +01:00
14a7206c5b
Signed-off-by: Sergey Isakov <isakov-sl@bk.ru>
2549 lines
82 KiB
C
2549 lines
82 KiB
C
/** @file
|
|
Library functions which relates with booting.
|
|
|
|
Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>
|
|
(C) Copyright 2015-2016 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);
|
|
if (!RamDiskDevicePath) {
|
|
return NULL;
|
|
}
|
|
|
|
*RamDiskSizeInPages = 0;
|
|
|
|
//
|
|
// Get the buffer occupied by RAM Disk.
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR(Status)) {
|
|
return NULL;
|
|
}
|
|
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);
|
|
if (!RamDiskDevicePath) {
|
|
return;
|
|
}
|
|
|
|
|
|
RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);
|
|
|
|
//
|
|
// Destroy RAM Disk.
|
|
//
|
|
if (mRamDisk == NULL) {
|
|
Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *) &mRamDisk);
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR(Status)) {
|
|
return;
|
|
}
|
|
}
|
|
/* 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);
|
|
if (EFI_ERROR(Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
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) {
|
|
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);
|
|
if (!FilePath) {
|
|
return 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)) {
|
|
//
|
|
// 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;
|
|
//
|
|
// Destroy the RAM disk
|
|
//
|
|
if (RamDiskDevicePath != NULL) {
|
|
BmDestroyRamDisk (RamDiskDevicePath);
|
|
FreePool (RamDiskDevicePath);
|
|
}
|
|
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;
|
|
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);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
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) == -1) {
|
|
Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);
|
|
//
|
|
// Deleting variable with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
if (EFI_ERROR(Status)) break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
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 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;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *Handles;
|
|
UINTN Index;
|
|
VOID *Data;
|
|
UINTN DataSize;
|
|
|
|
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) {
|
|
Data = NULL;
|
|
Status = GetSectionFromFv (
|
|
PcdGetPtr (PcdBootManagerMenuFile),
|
|
EFI_SECTION_PE32,
|
|
0,
|
|
(VOID **) &Data,
|
|
&DataSize
|
|
);
|
|
if (Data != NULL) {
|
|
FreePool (Data);
|
|
}
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Get BootManagerMenu application's description from EFI User Interface Section.
|
|
//
|
|
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);
|
|
if (EFI_ERROR (Status)) {
|
|
return 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);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
if (DevicePath) {
|
|
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 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);
|
|
}
|