3114 lines
100 KiB
C
3114 lines
100 KiB
C
/** @file
|
|
BDS Lib functions which relate with create or process the boot option.
|
|
|
|
;******************************************************************************
|
|
;* Copyright (c) 2019 - 2022, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
|
|
This file contains 'Framework Code' and is licensed as such
|
|
under the terms of your license agreement with Intel or your
|
|
vendor. This file may not be modified, except as allowed by
|
|
additional terms of your license agreement.
|
|
|
|
Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>
|
|
This software and associated documentation (if any) is furnished
|
|
under a license and may only be used or copied in accordance
|
|
with the terms of the license. Except as permitted by such
|
|
license, no part of this software or documentation may be
|
|
reproduced, stored in a retrieval system, or transmitted in any
|
|
form or by any means without the express written consent of
|
|
Intel Corporation.
|
|
**/
|
|
|
|
#include "InternalBdsLib.h"
|
|
#include <Library/GenericBdsLib.h>
|
|
#include <PostCode.h>
|
|
#if 0
|
|
#define VENDOR_IDENTIFICATION_OFFSET 3
|
|
#define VENDOR_IDENTIFICATION_LENGTH 8
|
|
#define PRODUCT_IDENTIFICATION_OFFSET 11
|
|
#define PRODUCT_IDENTIFICATION_LENGTH 16
|
|
|
|
CONST UINT16 gUsbLangID = 0x0409; // English
|
|
CHAR16 mUefiPrefix[] = L"UEFI ";
|
|
|
|
EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION mEfiBootManagerRefreshLegacyBootOption = NULL;
|
|
#endif
|
|
//EFI_BOOT_MANAGER_LEGACY_BOOT mEfiBootManagerLegacyBoot = 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 mHdBootVariablePrivateGuid = { 0xfab7e9e1, 0x39dd, 0x4f2b, { 0x84, 0x08, 0xe2, 0x0e, 0x90, 0x6c, 0xb6, 0xde } };
|
|
//EFI_GUID mAutoCreateBootOptionGuid = { 0x8108ac4e, 0x9f11, 0x4d59, { 0x85, 0x0e, 0xe2, 0x1a, 0x52, 0x2c, 0x59, 0xb2 } };
|
|
|
|
GLOBAL_REMOVE_IF_UNREFERENCED
|
|
CHAR16 *mHealthStatusText[] = {
|
|
L"Healthy",
|
|
L"Repair Required",
|
|
L"Configuration Required",
|
|
L"Failed",
|
|
L"Reconnect Required",
|
|
L"Reboot Required"
|
|
};
|
|
|
|
/**
|
|
Get the image file buffer data and buffer size by its device path.
|
|
|
|
@param FilePath On input, a pointer to an allocated buffer containing the device
|
|
path of the file.
|
|
On output the pointer could be NULL when the function fails to
|
|
load the boot option, or could point to an allocated buffer containing
|
|
the device path of the file.
|
|
It could be updated by either short-form device path expanding,
|
|
or default boot file path appending.
|
|
Caller is responsible to free it when it's non-NULL.
|
|
@param FileSize A pointer to the size of the file buffer.
|
|
|
|
@retval NULL File is NULL, or FileSize is NULL. Or, the file can't be found.
|
|
@retval other The file buffer. The caller is responsible to free the memory.
|
|
**/
|
|
VOID *
|
|
LoadEfiBootOption (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
|
|
OUT UINTN *FileSize
|
|
);
|
|
#if 0
|
|
/**
|
|
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
|
|
)
|
|
{
|
|
mEfiBootManagerRefreshLegacyBootOption = RefreshLegacyBootOption;
|
|
mEfiBootManagerLegacyBoot = LegacyBoot;
|
|
}
|
|
#endif
|
|
/**
|
|
Free old buffer and reuse the pointer to return new buffer.
|
|
|
|
@param Orig Pointer to the old buffer.
|
|
@param New Pointer to the new buffer.
|
|
**/
|
|
VOID
|
|
FreeAndSet (
|
|
VOID **Orig,
|
|
VOID *New
|
|
)
|
|
{
|
|
FreePool (*Orig);
|
|
*Orig = New;
|
|
}
|
|
|
|
/**
|
|
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
|
|
FindBootOptionInVariable (
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *OptionToFind
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION BootOption;
|
|
UINTN OptionNumber;
|
|
CHAR16 OptionName[sizeof ("Boot####")];
|
|
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"Boot%04x", 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);
|
|
if (BootOptions != NULL) {
|
|
Index = EfiBootManagerFindLoadOption (OptionToFind, BootOptions, BootOptionCount);
|
|
if (Index != -1) {
|
|
OptionNumber = BootOptions[Index].OptionNumber;
|
|
}
|
|
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
}
|
|
}
|
|
|
|
return OptionNumber;
|
|
}
|
|
|
|
/**
|
|
According to a file guild, check a Fv file device path is valid. If it is invalid,
|
|
try to return the valid device path.
|
|
FV address maybe changes for memory layout adjust from time to time, use this function
|
|
could promise the Fv file device path is right.
|
|
|
|
@param DevicePath The Fv file device path to be fixed up.
|
|
|
|
**/
|
|
VOID
|
|
FixupMemmapFvFilePath (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
EFI_DEVICE_PATH_PROTOCOL *Node;
|
|
EFI_HANDLE FvHandle;
|
|
EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
UINTN Size;
|
|
EFI_FV_FILETYPE Type;
|
|
EFI_FV_FILE_ATTRIBUTES Attributes;
|
|
UINT32 AuthenticationStatus;
|
|
UINTN FvHandleCount;
|
|
EFI_HANDLE *FvHandleBuffer;
|
|
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
|
|
|
|
Node = *DevicePath;
|
|
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
|
|
if (!EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (FvHandle, &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Fv);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
Status = Fv->ReadFile (
|
|
Fv,
|
|
EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node),
|
|
NULL,
|
|
&Size,
|
|
&Type,
|
|
&Attributes,
|
|
&AuthenticationStatus
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
FreeAndSet ((VOID **) DevicePath, NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
Node = NextDevicePathNode (DevicePath);
|
|
|
|
//
|
|
// Firstly find the FV file in current FV
|
|
//
|
|
gBS->HandleProtocol (
|
|
gImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **) &LoadedImage
|
|
);
|
|
NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), Node);
|
|
FixupMemmapFvFilePath (&NewDevicePath);
|
|
|
|
if (NewDevicePath != NULL) {
|
|
FreeAndSet ((VOID **) DevicePath, NewDevicePath);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Secondly find the FV file in all other FVs
|
|
//
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiFirmwareVolume2ProtocolGuid,
|
|
NULL,
|
|
&FvHandleCount,
|
|
&FvHandleBuffer
|
|
);
|
|
for (Index = 0; Index < FvHandleCount; Index++) {
|
|
if (FvHandleBuffer[Index] == LoadedImage->DeviceHandle) {
|
|
//
|
|
// Skip current FV
|
|
//
|
|
continue;
|
|
}
|
|
NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandleBuffer[Index]), Node);
|
|
FixupMemmapFvFilePath (&NewDevicePath);
|
|
|
|
if (NewDevicePath != NULL) {
|
|
FreeAndSet ((VOID **) DevicePath, NewDevicePath);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Check if it's of Fv file device path type.
|
|
|
|
The function doesn't garentee the device path points to existing Fv file.
|
|
|
|
@param DevicePath Input device path info.
|
|
|
|
@retval TRUE The device path is of Fv file device path type.
|
|
@retval FALSE The device path isn't of Fv file device path type.
|
|
**/
|
|
BOOLEAN
|
|
IsMemmapFvFilePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *FileNode;
|
|
|
|
if ((DevicePathType (DevicePath) == HARDWARE_DEVICE_PATH) && (DevicePathSubType (DevicePath) == HW_MEMMAP_DP)) {
|
|
FileNode = NextDevicePathNode (DevicePath);
|
|
if ((DevicePathType (FileNode) == MEDIA_DEVICE_PATH) && (DevicePathSubType (FileNode) == MEDIA_PIWG_FW_FILE_DP)) {
|
|
return IsDevicePathEnd (NextDevicePathNode (FileNode));
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check whether a USB device match the specified USB Class device path. This
|
|
function follows "Load Option Processing" behavior in UEFI specification.
|
|
|
|
@param UsbIo USB I/O protocol associated with the USB device.
|
|
@param UsbClass The USB Class device path to match.
|
|
|
|
@retval TRUE The USB device match the USB Class device path.
|
|
@retval FALSE The USB device does not match the USB Class device path.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MatchUsbClass (
|
|
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;
|
|
}
|
|
#if 0
|
|
/**
|
|
Eliminate the extra spaces in the Str to one space.
|
|
|
|
@param Str Input string info.
|
|
**/
|
|
VOID
|
|
EliminateExtraSpaces (
|
|
IN CHAR16 *Str
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN ActualIndex;
|
|
|
|
for (Index = 0, ActualIndex = 0; Str[Index] != L'\0'; Index++) {
|
|
if ((Str[Index] != L' ') || ((ActualIndex > 0) && (Str[ActualIndex - 1] != L' '))) {
|
|
Str[ActualIndex++] = Str[Index];
|
|
}
|
|
}
|
|
Str[ActualIndex] = L'\0';
|
|
}
|
|
|
|
/**
|
|
Try to get the controller's ATA/ATAPI description.
|
|
|
|
@param Handle Controller handle.
|
|
|
|
@return The description string.
|
|
**/
|
|
CHAR16 *
|
|
GetDescriptionFromDiskInfo (
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STATUS Status;
|
|
EFI_DISK_INFO_PROTOCOL *DiskInfo;
|
|
UINT32 BufferSize;
|
|
EFI_ATAPI_IDENTIFY_DATA IdentifyData;
|
|
EFI_SCSI_INQUIRY_DATA InquiryData;
|
|
CHAR16 *Description;
|
|
UINTN Length;
|
|
CONST UINTN ModelNameLength = 40;
|
|
CONST UINTN SerialNumberLength = 20;
|
|
CHAR8 *StrPtr;
|
|
UINT8 Temp;
|
|
|
|
Description = NULL;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiDiskInfoProtocolGuid,
|
|
(VOID **) &DiskInfo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoAhciInterfaceGuid) ||
|
|
CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoIdeInterfaceGuid)) {
|
|
BufferSize = sizeof (EFI_ATAPI_IDENTIFY_DATA);
|
|
Status = DiskInfo->Identify (
|
|
DiskInfo,
|
|
&IdentifyData,
|
|
&BufferSize
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Description = AllocateZeroPool ((ModelNameLength + SerialNumberLength + 2) * sizeof (CHAR16));
|
|
ASSERT (Description != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (Description == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
for (Index = 0; Index + 1 < ModelNameLength; Index += 2) {
|
|
Description[Index] = (CHAR16) IdentifyData.ModelName[Index + 1];
|
|
Description[Index + 1] = (CHAR16) IdentifyData.ModelName[Index];
|
|
}
|
|
|
|
Length = Index;
|
|
Description[Length++] = L' ';
|
|
|
|
for (Index = 0; Index + 1 < SerialNumberLength; Index += 2) {
|
|
Description[Length + Index] = (CHAR16) IdentifyData.SerialNo[Index + 1];
|
|
Description[Length + Index + 1] = (CHAR16) IdentifyData.SerialNo[Index];
|
|
}
|
|
Length += Index;
|
|
Description[Length++] = L'\0';
|
|
ASSERT (Length == ModelNameLength + SerialNumberLength + 2);
|
|
|
|
EliminateExtraSpaces (Description);
|
|
}
|
|
} else if (CompareGuid (&DiskInfo->Interface, &gEfiDiskInfoScsiInterfaceGuid)) {
|
|
BufferSize = sizeof (EFI_SCSI_INQUIRY_DATA);
|
|
Status = DiskInfo->Inquiry (
|
|
DiskInfo,
|
|
&InquiryData,
|
|
&BufferSize
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Description = AllocateZeroPool ((VENDOR_IDENTIFICATION_LENGTH + PRODUCT_IDENTIFICATION_LENGTH + 2) * sizeof (CHAR16));
|
|
ASSERT (Description != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (Description == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
//
|
|
// Per SCSI spec, EFI_SCSI_INQUIRY_DATA.Reserved_5_95[3 - 10] save the Verdor identification
|
|
// EFI_SCSI_INQUIRY_DATA.Reserved_5_95[11 - 26] save the product identification,
|
|
// Here combine the vendor identification and product identification to the description.
|
|
//
|
|
StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[VENDOR_IDENTIFICATION_OFFSET]);
|
|
Temp = StrPtr[VENDOR_IDENTIFICATION_LENGTH];
|
|
StrPtr[VENDOR_IDENTIFICATION_LENGTH] = '\0';
|
|
AsciiStrToUnicodeStr (StrPtr, Description);
|
|
StrPtr[VENDOR_IDENTIFICATION_LENGTH] = Temp;
|
|
|
|
//
|
|
// Add one space at the middle of vendor information and product information.
|
|
//
|
|
Description[VENDOR_IDENTIFICATION_LENGTH] = L' ';
|
|
|
|
StrPtr = (CHAR8 *) (&InquiryData.Reserved_5_95[PRODUCT_IDENTIFICATION_OFFSET]);
|
|
StrPtr[PRODUCT_IDENTIFICATION_LENGTH] = '\0';
|
|
AsciiStrToUnicodeStr (StrPtr, Description + VENDOR_IDENTIFICATION_LENGTH + 1);
|
|
|
|
EliminateExtraSpaces (Description);
|
|
}
|
|
}
|
|
|
|
return Description;
|
|
}
|
|
|
|
/**
|
|
Try to get the controller's USB description.
|
|
|
|
@param Handle Controller handle.
|
|
|
|
@return The description string.
|
|
**/
|
|
CHAR16 *
|
|
GetUsbDescription (
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
CHAR16 NullChar;
|
|
CHAR16 *Manufacturer;
|
|
CHAR16 *Product;
|
|
CHAR16 *SerialNumber;
|
|
CHAR16 *Description;
|
|
EFI_USB_DEVICE_DESCRIPTOR DevDesc;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
Handle,
|
|
&gEfiUsbIoProtocolGuid,
|
|
(VOID **) &UsbIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
NullChar = L'\0';
|
|
|
|
Status = UsbIo->UsbGetDeviceDescriptor (UsbIo, &DevDesc);
|
|
if (EFI_ERROR (Status)) {
|
|
return NULL;
|
|
}
|
|
|
|
Status = UsbIo->UsbGetStringDescriptor (
|
|
UsbIo,
|
|
gUsbLangID,
|
|
DevDesc.StrManufacturer,
|
|
&Manufacturer
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Manufacturer = &NullChar;
|
|
}
|
|
|
|
Status = UsbIo->UsbGetStringDescriptor (
|
|
UsbIo,
|
|
gUsbLangID,
|
|
DevDesc.StrProduct,
|
|
&Product
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Product = &NullChar;
|
|
}
|
|
|
|
Status = UsbIo->UsbGetStringDescriptor (
|
|
UsbIo,
|
|
gUsbLangID,
|
|
DevDesc.StrSerialNumber,
|
|
&SerialNumber
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
SerialNumber = &NullChar;
|
|
}
|
|
|
|
if ((Manufacturer == &NullChar) &&
|
|
(Product == &NullChar) &&
|
|
(SerialNumber == &NullChar)
|
|
) {
|
|
return NULL;
|
|
}
|
|
|
|
Description = AllocateZeroPool (StrSize (Manufacturer) + StrSize (Product) + StrSize (SerialNumber));
|
|
ASSERT (Description != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (Description == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
StrCat (Description, Manufacturer);
|
|
StrCat (Description, L" ");
|
|
|
|
StrCat (Description, Product);
|
|
StrCat (Description, L" ");
|
|
|
|
StrCat (Description, SerialNumber);
|
|
|
|
if (Manufacturer != &NullChar) {
|
|
FreePool (Manufacturer);
|
|
}
|
|
if (Product != &NullChar) {
|
|
FreePool (Product);
|
|
}
|
|
if (SerialNumber != &NullChar) {
|
|
FreePool (SerialNumber);
|
|
}
|
|
|
|
EliminateExtraSpaces (Description);
|
|
|
|
return Description;
|
|
}
|
|
|
|
/**
|
|
Return the boot description for the controller based on the type.
|
|
|
|
@param Handle Controller handle.
|
|
|
|
@return The description string.
|
|
**/
|
|
CHAR16 *
|
|
GetMiscDescription (
|
|
IN EFI_HANDLE Handle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *Description;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
|
|
DevicePath = DevicePathFromHandle (Handle);
|
|
if (DevicePath == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
switch (BootTypeFromDevicePath (DevicePath)) { // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
case AcpiFloppyBoot:
|
|
Description = L"Floppy";
|
|
break;
|
|
|
|
case MessageAtapiBoot:
|
|
case MessageSataBoot:
|
|
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
|
|
ASSERT_EFI_ERROR (Status);
|
|
//
|
|
// Assume a removable SATA device should be the DVD/CD device
|
|
//
|
|
Description = BlockIo->Media->RemovableMedia ? L"DVD/CDROM" : L"Hard Drive";
|
|
break;
|
|
|
|
case MessageUsbBoot:
|
|
Description = L"USB Device";
|
|
break;
|
|
|
|
case MessageScsiBoot:
|
|
Description = L"SCSI Device";
|
|
break;
|
|
|
|
case HardwareDeviceBoot:
|
|
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **) &BlockIo);
|
|
if (!EFI_ERROR (Status)) {
|
|
Description = BlockIo->Media->RemovableMedia ? L"Removable Disk" : L"Hard Drive";
|
|
} else {
|
|
Description = L"Misc Device";
|
|
}
|
|
break;
|
|
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0005
|
|
//
|
|
case MessageNvmeBoot:
|
|
Description = L"NVMe Storage";
|
|
break;
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
default:
|
|
Description = L"Misc Device";
|
|
break;
|
|
}
|
|
|
|
return AllocateCopyPool (StrSize (Description), Description);
|
|
}
|
|
|
|
GET_BOOT_DESCRIPTION mGetBootDescription[] = {
|
|
GetUsbDescription,
|
|
GetDescriptionFromDiskInfo,
|
|
GetMiscDescription
|
|
};
|
|
#endif
|
|
/**
|
|
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
|
|
MatchUsbWwid (
|
|
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;
|
|
}
|
|
|
|
/**
|
|
Print the device path info.
|
|
|
|
@param DevicePath The device path need to print.
|
|
|
|
**/
|
|
VOID
|
|
PrintDp (
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
CHAR16 *Str;
|
|
|
|
Str = ConvertDevicePathToText (DevicePath, FALSE, FALSE);
|
|
DEBUG ((DEBUG_INFO, "%s", Str));
|
|
if (Str != NULL) {
|
|
FreePool (Str);
|
|
}
|
|
}
|
|
|
|
/**
|
|
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 *
|
|
FindUsbDevice (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,
|
|
IN UINTN ParentDevicePathSize,
|
|
OUT UINTN *UsbIoHandleCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE *UsbIoHandles;
|
|
EFI_DEVICE_PATH_PROTOCOL *UsbIoDevicePath;
|
|
EFI_USB_IO_PROTOCOL *UsbIo;
|
|
UINTN Index;
|
|
UINTN UsbIoDevicePathSize;
|
|
BOOLEAN Matched;
|
|
|
|
ASSERT (UsbIoHandleCount != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (UsbIoHandleCount == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
//
|
|
// Get all UsbIo Handles.
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiUsbIoProtocolGuid,
|
|
NULL,
|
|
UsbIoHandleCount,
|
|
&UsbIoHandles
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
*UsbIoHandleCount = 0;
|
|
UsbIoHandles = NULL;
|
|
}
|
|
|
|
for (Index = 0; Index < *UsbIoHandleCount; ) {
|
|
//
|
|
// Get the Usb IO interface.
|
|
//
|
|
Status = gBS->HandleProtocol(
|
|
UsbIoHandles[Index],
|
|
&gEfiUsbIoProtocolGuid,
|
|
(VOID **) &UsbIo
|
|
);
|
|
UsbIoDevicePath = DevicePathFromHandle (UsbIoHandles[Index]);
|
|
Matched = FALSE;
|
|
if (!EFI_ERROR (Status) && (UsbIoDevicePath != NULL)) {
|
|
UsbIoDevicePathSize = GetDevicePathSize (UsbIoDevicePath) - END_DEVICE_PATH_LENGTH;
|
|
|
|
//
|
|
// Compare starting part of UsbIoHandle's device path with ParentDevicePath.
|
|
//
|
|
if (CompareMem (UsbIoDevicePath, DevicePath, ParentDevicePathSize) == 0) {
|
|
if (MatchUsbClass (UsbIo, (USB_CLASS_DEVICE_PATH *) ((UINTN) DevicePath + ParentDevicePathSize)) ||
|
|
MatchUsbWwid (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 DevicePath On input, a pointer to an allocated buffer that contains the
|
|
file device path.
|
|
On output, a pointer to an reallocated buffer that contains
|
|
the expanded device path. It would point to NULL if the file
|
|
cannot be read.
|
|
|
|
@param FileSize A pointer to the file size.
|
|
|
|
@retval !NULL The file buffer.
|
|
@retval NULL The input device path doesn't point to a valid file.
|
|
**/
|
|
VOID *
|
|
ExpandUsbShortFormDevicePath (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath,
|
|
OUT UINTN *FileSize
|
|
)
|
|
{
|
|
UINTN ParentDevicePathSize;
|
|
EFI_DEVICE_PATH_PROTOCOL *ShortformNode;
|
|
EFI_DEVICE_PATH_PROTOCOL *RemainingDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
|
|
EFI_HANDLE *UsbIoHandles;
|
|
UINTN UsbIoHandleCount;
|
|
UINTN Index;
|
|
VOID *FileBuffer;
|
|
|
|
//
|
|
// Search for USB Class or USB WWID device path node.
|
|
//
|
|
for ( ShortformNode = *DevicePath
|
|
; !IsDevicePathEnd (ShortformNode)
|
|
; ShortformNode = NextDevicePathNode (ShortformNode)
|
|
) {
|
|
if ((DevicePathType (ShortformNode) == MESSAGING_DEVICE_PATH) &&
|
|
((DevicePathSubType (ShortformNode) == MSG_USB_CLASS_DP) ||
|
|
(DevicePathSubType (ShortformNode) == MSG_USB_WWID_DP))) {
|
|
break;
|
|
}
|
|
}
|
|
ASSERT (!IsDevicePathEnd (ShortformNode));
|
|
|
|
FullDevicePath = NULL;
|
|
ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) *DevicePath;
|
|
RemainingDevicePath = NextDevicePathNode (ShortformNode);
|
|
FileBuffer = NULL;
|
|
UsbIoHandles = FindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount);
|
|
|
|
for (Index = 0; Index < UsbIoHandleCount; Index++) {
|
|
FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath);
|
|
DEBUG ((DEBUG_INFO, "[Bds] FullDp1[%d]:", Index)); DEBUG_CODE (PrintDp (FullDevicePath); ); DEBUG ((DEBUG_INFO, "\n"));
|
|
FileBuffer = LoadEfiBootOption (&FullDevicePath, FileSize);
|
|
if (FileBuffer != NULL) {
|
|
DEBUG ((DEBUG_INFO, "-->")); DEBUG_CODE (PrintDp (FullDevicePath); ); DEBUG ((DEBUG_INFO, FileBuffer != NULL ? " - Found\n" : "\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (UsbIoHandles != NULL) {
|
|
FreePool (UsbIoHandles);
|
|
}
|
|
|
|
if (FileBuffer == NULL) {
|
|
//
|
|
// 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.
|
|
//
|
|
if ((DevicePathType (*DevicePath) == MESSAGING_DEVICE_PATH) &&
|
|
((DevicePathSubType (*DevicePath) == MSG_USB_CLASS_DP) ||
|
|
(DevicePathSubType (*DevicePath) == MSG_USB_WWID_DP))) {
|
|
EfiBootManagerConnectUsbShortFormDevicePath (*DevicePath);
|
|
|
|
UsbIoHandles = FindUsbDevice (*DevicePath, ParentDevicePathSize, &UsbIoHandleCount);
|
|
for (Index = 0; Index < UsbIoHandleCount; Index++) {
|
|
FullDevicePath = AppendDevicePath (DevicePathFromHandle (UsbIoHandles[Index]), RemainingDevicePath);
|
|
DEBUG ((DEBUG_INFO, "[Bds] FullDp2[%d]:", Index)); DEBUG_CODE (PrintDp (FullDevicePath); ); DEBUG ((DEBUG_INFO, "\n"));
|
|
FileBuffer = LoadEfiBootOption (&FullDevicePath, FileSize);
|
|
if (FileBuffer != NULL) {
|
|
DEBUG ((DEBUG_INFO, "-->")); DEBUG_CODE (PrintDp (FullDevicePath); ); DEBUG ((DEBUG_INFO, FileBuffer != NULL ? " - Found\n" : "\n"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (UsbIoHandles != NULL) {
|
|
FreePool (UsbIoHandles);
|
|
}
|
|
}
|
|
}
|
|
|
|
FreeAndSet ((VOID **) DevicePath, FullDevicePath);
|
|
return FileBuffer;
|
|
}
|
|
|
|
/**
|
|
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 DevicePath On input, a pointer to an allocated buffer that contains the
|
|
file device path.
|
|
On output, a pointer to an reallocated buffer that contains
|
|
the expanded device path. It would point to NULL if the file
|
|
cannot be read.
|
|
|
|
**/
|
|
VOID
|
|
ExpandPartitionShortFormDevicePath (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **DevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BlockIoHandleCount;
|
|
EFI_HANDLE *BlockIoBuffer;
|
|
EFI_DEVICE_PATH_PROTOCOL *FullDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath;
|
|
UINTN Index;
|
|
UINTN InstanceNum;
|
|
EFI_DEVICE_PATH_PROTOCOL *CachedDevicePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempNewDevicePath;
|
|
UINTN CachedDevicePathSize;
|
|
BOOLEAN DeviceExist;
|
|
BOOLEAN NeedAdjust;
|
|
EFI_DEVICE_PATH_PROTOCOL *Instance;
|
|
UINTN Size;
|
|
|
|
FullDevicePath = NULL;
|
|
//
|
|
// Check if there is prestore 'HDDP' variable.
|
|
// If exist, search the front path which point to partition node in the variable instants.
|
|
// If fail to find or 'HDDP' not exist, reconnect all and search in all system
|
|
//
|
|
GetVariable2 (L"HDDP", &mHdBootVariablePrivateGuid, (VOID **) &CachedDevicePath, &CachedDevicePathSize);
|
|
|
|
//
|
|
// Delete the invalid 'HDDP' variable.
|
|
//
|
|
if ((CachedDevicePath != NULL) && !IsDevicePathValid (CachedDevicePath, CachedDevicePathSize)) {
|
|
FreePool (CachedDevicePath);
|
|
CachedDevicePath = NULL;
|
|
Status = gRT->SetVariable (
|
|
L"HDDP",
|
|
&mHdBootVariablePrivateGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
if (CachedDevicePath != NULL) {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
DeviceExist = FALSE;
|
|
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 (MatchPartitionDevicePathNode (Instance, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) {
|
|
//
|
|
// 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)) {
|
|
DeviceExist = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Come here means the first instance is not matched
|
|
//
|
|
NeedAdjust = TRUE;
|
|
FreePool(Instance);
|
|
} while (TempNewDevicePath != NULL);
|
|
|
|
if (DeviceExist) {
|
|
//
|
|
// Find the matched device path.
|
|
// Append the file path information from the boot option and return the fully expanded device path.
|
|
//
|
|
FullDevicePath = AppendDevicePath (Instance, NextDevicePathNode (*DevicePath));
|
|
|
|
//
|
|
// Adjust the 'HDDP' instances sequence if the matched one is not first one.
|
|
//
|
|
if (NeedAdjust) {
|
|
//
|
|
// First delete the matched instance.
|
|
//
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = EfiBootManagerDelPartMatchInstance (CachedDevicePath, Instance);
|
|
FreePool (TempNewDevicePath);
|
|
|
|
//
|
|
// Second, append the remaining path after the matched instance
|
|
//
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = AppendDevicePathInstance (Instance, CachedDevicePath );
|
|
FreePool (TempNewDevicePath);
|
|
//
|
|
// 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",
|
|
&mHdBootVariablePrivateGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
GetDevicePathSize (CachedDevicePath),
|
|
CachedDevicePath
|
|
);
|
|
}
|
|
|
|
FreePool (Instance);
|
|
FreePool (CachedDevicePath);
|
|
FreePool (*DevicePath);
|
|
*DevicePath = FullDevicePath;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 ();
|
|
BdsLibConnectAll();
|
|
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++) {
|
|
|
|
Status = gBS->HandleProtocol (BlockIoBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID *) &BlockIoDevicePath);
|
|
if (EFI_ERROR (Status) || BlockIoDevicePath == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if (MatchPartitionDevicePathNode (BlockIoDevicePath, (HARDDRIVE_DEVICE_PATH *) *DevicePath)) {
|
|
//
|
|
// Find the matched partition device path
|
|
//
|
|
FullDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (*DevicePath));
|
|
|
|
//
|
|
// Save the matched partition device path in 'HDDP' variable
|
|
//
|
|
if (CachedDevicePath != NULL) {
|
|
//
|
|
// Save the matched partition device path as first instance of 'HDDP' variable
|
|
//
|
|
if (EfiBootManagerMatchDevicePaths (CachedDevicePath, BlockIoDevicePath)) {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = EfiBootManagerDelPartMatchInstance (CachedDevicePath, BlockIoDevicePath);
|
|
FreePool(TempNewDevicePath);
|
|
}
|
|
|
|
if (CachedDevicePath != NULL) {
|
|
TempNewDevicePath = CachedDevicePath;
|
|
CachedDevicePath = AppendDevicePathInstance (BlockIoDevicePath, CachedDevicePath);
|
|
FreePool(TempNewDevicePath);
|
|
} else {
|
|
CachedDevicePath = DuplicateDevicePath (BlockIoDevicePath);
|
|
}
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
InstanceNum = 0;
|
|
ASSERT (CachedDevicePath != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (CachedDevicePath == NULL) {
|
|
break;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
TempNewDevicePath = CachedDevicePath;
|
|
while (!IsDevicePathEnd (TempNewDevicePath)) {
|
|
TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
|
|
//
|
|
// Parse one instance
|
|
//
|
|
while (!IsDevicePathEndType (TempNewDevicePath)) {
|
|
TempNewDevicePath = NextDevicePathNode (TempNewDevicePath);
|
|
}
|
|
InstanceNum++;
|
|
//
|
|
// If the CachedDevicePath variable contain too much instance, only remain 12 instances.
|
|
//
|
|
if (InstanceNum >= 12) {
|
|
SetDevicePathEndNode (TempNewDevicePath);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
CachedDevicePath = DuplicateDevicePath (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",
|
|
&mHdBootVariablePrivateGuid,
|
|
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_NON_VOLATILE,
|
|
GetDevicePathSize (CachedDevicePath),
|
|
CachedDevicePath
|
|
);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (CachedDevicePath != NULL) {
|
|
FreePool (CachedDevicePath);
|
|
}
|
|
if (BlockIoBuffer != NULL) {
|
|
FreePool (BlockIoBuffer);
|
|
}
|
|
FreeAndSet ((VOID **) DevicePath, FullDevicePath);
|
|
}
|
|
|
|
/**
|
|
Algorithm follows the UEFI Spec chapter 3.4 Boot Mechanisms.
|
|
|
|
@param DevicePath Device Path to a bootable device
|
|
|
|
@return The bootable media handle. If the media on the DevicePath is not bootable, NULL will return.
|
|
|
|
**/
|
|
EFI_HANDLE
|
|
GetBootableDeviceHandle (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DEVICE_PATH_PROTOCOL *UpdatedDevicePath;
|
|
EFI_HANDLE Handle;
|
|
EFI_BLOCK_IO_PROTOCOL *BlockIo;
|
|
VOID *Buffer;
|
|
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
|
|
UINTN Size;
|
|
UINTN TempSize;
|
|
EFI_HANDLE ReturnHandle;
|
|
EFI_HANDLE *SimpleFileSystemHandles;
|
|
UINTN NumberSimpleFileSystemHandles;
|
|
UINTN Index;
|
|
EFI_IMAGE_DOS_HEADER DosHeader;
|
|
EFI_IMAGE_OPTIONAL_HEADER_UNION HdrData;
|
|
EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr;
|
|
|
|
ReturnHandle = NULL;
|
|
UpdatedDevicePath = DevicePath;
|
|
|
|
//
|
|
// Check whether the device is connected
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Skip the case that the boot option point to a simple file protocol which does not consume block Io protocol,
|
|
//
|
|
Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Fail to find the proper BlockIo and simple file protocol, maybe because device not present, we need to connect it firstly
|
|
//
|
|
UpdatedDevicePath = DevicePath;
|
|
Status = gBS->LocateDevicePath (&gEfiDevicePathProtocolGuid, &UpdatedDevicePath, &Handle);
|
|
gBS->ConnectController (Handle, NULL, NULL, TRUE);
|
|
}
|
|
} else {
|
|
//
|
|
// For removable device boot option, its contained device path only point 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);
|
|
//
|
|
// Get BlockIo protocol and check removable attribute
|
|
//
|
|
Status = gBS->HandleProtocol (Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);
|
|
//
|
|
// 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.
|
|
//
|
|
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
|
|
//
|
|
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)) {
|
|
//
|
|
// Load the default boot file \EFI\BOOT\boot{machinename}.EFI from removable Media
|
|
// machinename is ia32, ia64, x64, ...
|
|
//
|
|
Hdr.Union = &HdrData;
|
|
Status = GetImageHeader (
|
|
SimpleFileSystemHandles[Index],
|
|
EFI_REMOVABLE_MEDIA_FILE_NAME,
|
|
&DosHeader,
|
|
Hdr
|
|
);
|
|
if (!EFI_ERROR (Status) && EFI_IMAGE_MACHINE_TYPE_SUPPORTED (Hdr.Pe32->FileHeader.Machine) &&
|
|
(Hdr.Pe32->OptionalHeader.Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)
|
|
) {
|
|
ReturnHandle = SimpleFileSystemHandles[Index];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SimpleFileSystemHandles != NULL) {
|
|
FreePool(SimpleFileSystemHandles);
|
|
}
|
|
|
|
return ReturnHandle;
|
|
}
|
|
|
|
/**
|
|
Get the image file buffer data and buffer size by its device path.
|
|
|
|
@param FilePath On input, a pointer to an allocated buffer that contains the
|
|
file device path.
|
|
On output the device path pointer could be modified to point to
|
|
a new allocated buffer that contains the full device path.
|
|
It could be caused by either short-form device path expanding,
|
|
or default boot file path appending.
|
|
@param FileSize A pointer to the size of the file buffer.
|
|
|
|
@retval NULL The file can't be found.
|
|
@retval other The file buffer. The caller is responsible to free memory.
|
|
**/
|
|
VOID *
|
|
LoadEfiBootOption (
|
|
IN OUT EFI_DEVICE_PATH_PROTOCOL **FilePath,
|
|
OUT UINTN *FileSize
|
|
)
|
|
{
|
|
EFI_HANDLE Handle;
|
|
VOID *FileBuffer;
|
|
UINT32 AuthenticationStatus;
|
|
EFI_DEVICE_PATH_PROTOCOL *Node;
|
|
|
|
ASSERT ((FilePath != NULL) && (*FilePath != NULL) && (FileSize != NULL));
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if ((FilePath == NULL) || (*FilePath == NULL) || (FileSize == NULL)) {
|
|
FileSize = NULL;
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
EfiBootManagerConnectDevicePath (*FilePath, NULL);
|
|
|
|
*FileSize = 0;
|
|
FileBuffer = NULL;
|
|
//
|
|
// Expand the short-form device path to full device path
|
|
//
|
|
// if (FeaturePcdGet (PcdShortformBootSupport)) {
|
|
|
|
if ((DevicePathType (*FilePath) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (*FilePath) == MEDIA_HARDDRIVE_DP)) {
|
|
//
|
|
// Expand the Harddrive device path
|
|
//
|
|
ExpandPartitionShortFormDevicePath (FilePath);
|
|
if (*FilePath == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
} else {
|
|
for (Node = *FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
|
|
if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
|
|
((DevicePathSubType (Node) == MSG_USB_CLASS_DP) ||
|
|
(DevicePathSubType (Node) == MSG_USB_WWID_DP))) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!IsDevicePathEnd (Node)) {
|
|
//
|
|
// Expand the USB WWID/Class device path
|
|
//
|
|
FileBuffer = ExpandUsbShortFormDevicePath (FilePath, FileSize);
|
|
if (FileBuffer == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
// }
|
|
|
|
//
|
|
// Fix up the boot option path if it points to a FV in memory map style of device path
|
|
//
|
|
if (IsMemmapFvFilePath (*FilePath)) {
|
|
FixupMemmapFvFilePath (FilePath);
|
|
if (*FilePath == NULL) {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (FileBuffer == NULL) {
|
|
FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus);
|
|
}
|
|
|
|
//
|
|
// If we didn't find an image directly, we need to try as if it is a removable device boot option
|
|
// and load the image according to the default boot behavior.
|
|
//
|
|
if (FileBuffer == NULL) {
|
|
//
|
|
// check if there is a bootable media could be found in this device path,
|
|
// and get the bootable media handle
|
|
//
|
|
Handle = GetBootableDeviceHandle (*FilePath);
|
|
if (Handle != NULL) {
|
|
//
|
|
// Load the default boot file \EFI\BOOT\boot{machinename}.EFI from the media
|
|
// machinename is ia32, ia64, x64, ...
|
|
//
|
|
FreeAndSet ((VOID **) FilePath, FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME));
|
|
ASSERT (*FilePath != NULL);
|
|
FileBuffer = GetFileBufferByFilePath (TRUE, *FilePath, FileSize, &AuthenticationStatus);
|
|
}
|
|
}
|
|
|
|
if (FileBuffer == NULL) {
|
|
FreeAndSet ((VOID **) FilePath, NULL);
|
|
}
|
|
|
|
return FileBuffer;
|
|
}
|
|
|
|
/**
|
|
Return the controller name.
|
|
|
|
@param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
|
|
@param ControllerHandle The handle of a controller that the driver specified by DriverBindingHandle is managing.
|
|
This handle specifies the controller whose name is to be returned.
|
|
@param ChildHandle The handle of the child controller to retrieve the name of. This is an
|
|
optional parameter that may be NULL. It will be NULL for device drivers.
|
|
It will also be NULL for bus drivers that attempt to retrieve the name
|
|
of the bus controller. It will not be NULL for a bus driver that attempts
|
|
to retrieve the name of a child controller.
|
|
|
|
@return A pointer to the Unicode string to return. This Unicode string is the name of the controller
|
|
specified by ControllerHandle and ChildHandle.
|
|
**/
|
|
CHAR16 *
|
|
BmGetControllerName (
|
|
IN EFI_HANDLE DriverHealthHandle,
|
|
IN EFI_HANDLE ControllerHandle,
|
|
IN EFI_HANDLE ChildHandle
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *ControllerName;
|
|
CHAR8 *LanguageVariable;
|
|
CHAR8 *BestLanguage;
|
|
BOOLEAN Iso639Language;
|
|
EFI_COMPONENT_NAME_PROTOCOL *ComponentName;
|
|
|
|
ControllerName = NULL;
|
|
|
|
//
|
|
// Locate Component Name (2) protocol on the driver binging handle.
|
|
//
|
|
Iso639Language = FALSE;
|
|
Status = gBS->HandleProtocol (
|
|
DriverHealthHandle,
|
|
&gEfiComponentName2ProtocolGuid,
|
|
(VOID **) &ComponentName
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = gBS->HandleProtocol (
|
|
DriverHealthHandle,
|
|
&gEfiComponentNameProtocolGuid,
|
|
(VOID **) &ComponentName
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
Iso639Language = TRUE;
|
|
}
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
GetEfiGlobalVariable2 (Iso639Language ? L"Lang" : L"PlatformLang", (VOID**)&LanguageVariable, NULL);
|
|
BestLanguage = GetBestLanguage(
|
|
ComponentName->SupportedLanguages,
|
|
Iso639Language,
|
|
(LanguageVariable != NULL) ? LanguageVariable : "",
|
|
Iso639Language ? "eng" : "en-US",
|
|
NULL
|
|
);
|
|
if (LanguageVariable != NULL) {
|
|
FreePool (LanguageVariable);
|
|
}
|
|
|
|
Status = ComponentName->GetControllerName (
|
|
ComponentName,
|
|
ControllerHandle,
|
|
ChildHandle,
|
|
BestLanguage,
|
|
&ControllerName
|
|
);
|
|
}
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
return AllocateCopyPool (StrSize (ControllerName), ControllerName);
|
|
} else {
|
|
return ConvertDevicePathToText (
|
|
DevicePathFromHandle (ChildHandle != NULL ? ChildHandle : ControllerHandle),
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Display a set of messages returned by the GetHealthStatus () service of the EFI Driver Health Protocol
|
|
|
|
@param DriverHealthInfo Pointer to the Driver Health information entry.
|
|
**/
|
|
VOID
|
|
BmDisplayMessages (
|
|
IN EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo
|
|
)
|
|
{
|
|
UINTN Index;
|
|
EFI_STRING String;
|
|
CHAR16 *ControllerName;
|
|
|
|
if (DriverHealthInfo->MessageList == NULL ||
|
|
DriverHealthInfo->MessageList[0].HiiHandle == NULL) {
|
|
return;
|
|
}
|
|
|
|
ControllerName = BmGetControllerName (
|
|
DriverHealthInfo->DriverHealthHandle,
|
|
DriverHealthInfo->ControllerHandle,
|
|
DriverHealthInfo->ChildHandle
|
|
);
|
|
|
|
DEBUG ((DEBUG_INFO, "Controller: %s\n", ControllerName));
|
|
Print (L"Controller: %s\n", ControllerName);
|
|
for (Index = 0; DriverHealthInfo->MessageList[Index].HiiHandle != NULL; Index++) {
|
|
String = HiiGetString (
|
|
DriverHealthInfo->MessageList[Index].HiiHandle,
|
|
DriverHealthInfo->MessageList[Index].StringId,
|
|
NULL
|
|
);
|
|
if (String != NULL) {
|
|
Print (L" %s\n", String);
|
|
DEBUG ((DEBUG_INFO, " %s\n", String));
|
|
FreePool (String);
|
|
}
|
|
}
|
|
|
|
if (ControllerName != NULL) {
|
|
FreePool (ControllerName);
|
|
}
|
|
}
|
|
|
|
/**
|
|
The repair notify function.
|
|
@param Value A value between 0 and Limit that identifies the current progress
|
|
of the repair operation.
|
|
@param Limit The maximum value of Value for the current repair operation.
|
|
If Limit is 0, then the completion progress is indeterminate.
|
|
For example, a driver that wants to specify progress in percent
|
|
would use a Limit value of 100.
|
|
|
|
@retval EFI_SUCCESS Successfully return from the notify function.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
BmRepairNotify (
|
|
IN UINTN Value,
|
|
IN UINTN Limit
|
|
)
|
|
{
|
|
DEBUG ((DEBUG_INFO, "[BDS]RepairNotify: %d/%d\n", Value, Limit));
|
|
Print (L"[BDS]RepairNotify: %d/%d\n", Value, Limit);
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Collect the Driver Health status of a single controller.
|
|
|
|
@param DriverHealthInfo A pointer to the array containing all of the platform driver health information.
|
|
@param Count Return the updated array count.
|
|
@param DriverHealthHandle The handle on which the Driver Health protocol instance is retrieved.
|
|
@param ControllerHandle The handle of the controller..
|
|
@param ChildHandle The handle of the child controller to retrieve the health
|
|
status on. This is an optional parameter that may be NULL.
|
|
|
|
@retval Status The status returned from GetHealthStatus.
|
|
@retval EFI_ABORTED The health status is healthy so no further query is needed.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
BmGetSingleControllerHealthStatus (
|
|
IN OUT EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO **DriverHealthInfo,
|
|
IN OUT UINTN *Count,
|
|
IN EFI_HANDLE DriverHealthHandle,
|
|
IN EFI_HANDLE ControllerHandle, OPTIONAL
|
|
IN EFI_HANDLE ChildHandle OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_DRIVER_HEALTH_PROTOCOL *DriverHealth;
|
|
EFI_DRIVER_HEALTH_HII_MESSAGE *MessageList;
|
|
EFI_HII_HANDLE FormHiiHandle;
|
|
EFI_DRIVER_HEALTH_STATUS HealthStatus;
|
|
|
|
ASSERT (DriverHealthHandle != NULL);
|
|
//
|
|
// Retrieve the Driver Health Protocol from DriverHandle
|
|
//
|
|
Status = gBS->HandleProtocol (
|
|
DriverHealthHandle,
|
|
&gEfiDriverHealthProtocolGuid,
|
|
(VOID **) &DriverHealth
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
|
|
if (ControllerHandle == NULL) {
|
|
//
|
|
// If ControllerHandle is NULL, the return the cumulative health status of the driver
|
|
//
|
|
Status = DriverHealth->GetHealthStatus (DriverHealth, NULL, NULL, &HealthStatus, NULL, NULL);
|
|
if (!EFI_ERROR (Status) && HealthStatus == EfiDriverHealthStatusHealthy) {
|
|
*DriverHealthInfo = ReallocatePool (
|
|
(*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
|
|
(*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
|
|
*DriverHealthInfo
|
|
);
|
|
ASSERT (*DriverHealthInfo != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (*DriverHealthInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
(*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
|
|
(*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
|
|
(*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
|
|
|
|
*Count = *Count + 1;
|
|
|
|
Status = EFI_ABORTED;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
MessageList = NULL;
|
|
FormHiiHandle = NULL;
|
|
|
|
//
|
|
// Collect the health status with the optional HII message list
|
|
//
|
|
Status = DriverHealth->GetHealthStatus (DriverHealth, ControllerHandle, ChildHandle, &HealthStatus, &MessageList, &FormHiiHandle);
|
|
if (!EFI_ERROR (Status)) {
|
|
*DriverHealthInfo = ReallocatePool (
|
|
(*Count) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
|
|
(*Count + 1) * sizeof (EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO),
|
|
*DriverHealthInfo
|
|
);
|
|
ASSERT (*DriverHealthInfo != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (*DriverHealthInfo == NULL) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
(*DriverHealthInfo)[*Count].DriverHealth = DriverHealth;
|
|
(*DriverHealthInfo)[*Count].DriverHealthHandle = DriverHealthHandle;
|
|
(*DriverHealthInfo)[*Count].ControllerHandle = ControllerHandle;
|
|
(*DriverHealthInfo)[*Count].ChildHandle = ChildHandle;
|
|
(*DriverHealthInfo)[*Count].HiiHandle = FormHiiHandle;
|
|
(*DriverHealthInfo)[*Count].MessageList = MessageList;
|
|
(*DriverHealthInfo)[*Count].HealthStatus = HealthStatus;
|
|
|
|
*Count = *Count + 1;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Return all the Driver Health information.
|
|
|
|
When the cumulative health status of all the controllers managed by the
|
|
driver who produces the EFI_DRIVER_HEALTH_PROTOCOL is healthy, only one
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry is created for such
|
|
EFI_DRIVER_HEALTH_PROTOCOL instance.
|
|
Otherwise, every controller creates one EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO
|
|
entry. Additionally every child controller creates one
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO entry if the driver is a bus driver.
|
|
|
|
@param Count Return the count of the Driver Health information.
|
|
|
|
@retval NULL No Driver Health information is returned.
|
|
@retval !NULL Pointer to the Driver Health information array.
|
|
**/
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *
|
|
EFIAPI
|
|
EfiBootManagerGetDriverHealthInfo (
|
|
UINTN *Count
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN NumHandles;
|
|
EFI_HANDLE *DriverHealthHandles;
|
|
EFI_DRIVER_HEALTH_STATUS HealthStatus;
|
|
UINTN DriverHealthIndex;
|
|
EFI_HANDLE *Handles;
|
|
UINTN HandleCount;
|
|
UINTN ControllerIndex;
|
|
UINTN ChildIndex;
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
|
|
|
|
//
|
|
// Initialize local variables
|
|
//
|
|
*Count = 0;
|
|
DriverHealthInfo = NULL;
|
|
Handles = NULL;
|
|
DriverHealthHandles = NULL;
|
|
NumHandles = 0;
|
|
HandleCount = 0;
|
|
|
|
HealthStatus = EfiDriverHealthStatusHealthy;
|
|
|
|
Status = gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiDriverHealthProtocolGuid,
|
|
NULL,
|
|
&NumHandles,
|
|
&DriverHealthHandles
|
|
);
|
|
|
|
if (Status == EFI_NOT_FOUND || NumHandles == 0 || DriverHealthHandles == NULL) { // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
//
|
|
// If there are no Driver Health Protocols handles, then return EFI_NOT_FOUND
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Check the health status of all controllers in the platform
|
|
// Start by looping through all the Driver Health Protocol handles in the handle database
|
|
//
|
|
for (DriverHealthIndex = 0; DriverHealthIndex < NumHandles; DriverHealthIndex++) {
|
|
//
|
|
// Get the cumulative health status of the driver
|
|
//
|
|
Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], NULL, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// See if the list of all handles in the handle database has been retrieved
|
|
//
|
|
//
|
|
if (Handles == NULL) {
|
|
//
|
|
// Retrieve the list of all handles from the handle database
|
|
//
|
|
Status = gBS->LocateHandleBuffer (
|
|
AllHandles,
|
|
NULL,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
//
|
|
// Loop through all the controller handles in the handle database
|
|
//
|
|
for (ControllerIndex = 0; ControllerIndex < HandleCount; ControllerIndex++) {
|
|
Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Loop through all the child handles in the handle database
|
|
//
|
|
for (ChildIndex = 0; ChildIndex < HandleCount; ChildIndex++) {
|
|
Status = BmGetSingleControllerHealthStatus (&DriverHealthInfo, Count, DriverHealthHandles[DriverHealthIndex], Handles[ControllerIndex], Handles[ChildIndex]);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Status = EFI_SUCCESS;
|
|
|
|
if (Handles != NULL) {
|
|
FreePool (Handles);
|
|
}
|
|
if (DriverHealthHandles != NULL) {
|
|
FreePool (DriverHealthHandles);
|
|
}
|
|
|
|
return DriverHealthInfo;
|
|
}
|
|
|
|
/**
|
|
Free the Driver Health information array.
|
|
|
|
@param DriverHealthInfo Pointer to array of the Driver Health information.
|
|
@param Count Count of the array.
|
|
|
|
@retval EFI_SUCCESS The array is freed.
|
|
@retval EFI_INVALID_PARAMETER The array is NULL.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EfiBootManagerFreeDriverHealthInfo (
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo,
|
|
UINTN Count
|
|
)
|
|
{
|
|
UINTN Index;
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
if (DriverHealthInfo[Index].MessageList != NULL) {
|
|
FreePool (DriverHealthInfo[Index].MessageList);
|
|
}
|
|
}
|
|
return gBS->FreePool (DriverHealthInfo);
|
|
}
|
|
|
|
/**
|
|
Repair all the controllers according to the Driver Health status queried.
|
|
**/
|
|
VOID
|
|
BmRepairAllControllers (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MANAGER_DRIVER_HEALTH_INFO *DriverHealthInfo;
|
|
EFI_DRIVER_HEALTH_STATUS HealthStatus;
|
|
UINTN Count;
|
|
UINTN Index;
|
|
BOOLEAN RepairRequired;
|
|
BOOLEAN ConfigurationRequired;
|
|
BOOLEAN ReconnectRequired;
|
|
BOOLEAN RebootRequired;
|
|
EFI_HII_HANDLE *HiiHandles;
|
|
EFI_FORM_BROWSER2_PROTOCOL *FormBrowser2;
|
|
|
|
Status = gBS->LocateProtocol (&gEfiFormBrowser2ProtocolGuid, NULL, (VOID **) &FormBrowser2);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
do {
|
|
RepairRequired = FALSE;
|
|
ConfigurationRequired = FALSE;
|
|
|
|
//
|
|
// Deal with Repair Required
|
|
//
|
|
DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusConfigurationRequired) {
|
|
ConfigurationRequired = TRUE;
|
|
}
|
|
|
|
if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRepairRequired) {
|
|
RepairRequired = TRUE;
|
|
|
|
BmDisplayMessages (&DriverHealthInfo[Index]);
|
|
|
|
Status = DriverHealthInfo[Index].DriverHealth->Repair (
|
|
DriverHealthInfo[Index].DriverHealth,
|
|
DriverHealthInfo[Index].ControllerHandle,
|
|
DriverHealthInfo[Index].ChildHandle,
|
|
BmRepairNotify
|
|
);
|
|
if (!EFI_ERROR (Status) && !ConfigurationRequired) {
|
|
Status = DriverHealthInfo[Index].DriverHealth->GetHealthStatus (
|
|
DriverHealthInfo[Index].DriverHealth,
|
|
DriverHealthInfo[Index].ControllerHandle,
|
|
DriverHealthInfo[Index].ChildHandle,
|
|
&HealthStatus,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (!EFI_ERROR (Status) && (HealthStatus == EfiDriverHealthStatusConfigurationRequired)) {
|
|
ConfigurationRequired = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ConfigurationRequired) {
|
|
HiiHandles = HiiGetHiiHandles (NULL);
|
|
if (HiiHandles != NULL) {
|
|
for (Index = 0; HiiHandles[Index] != NULL; Index++) {
|
|
#if 0
|
|
Status = FormBrowser2->SendForm (
|
|
FormBrowser2,
|
|
&HiiHandles[Index],
|
|
1,
|
|
PcdGetPtr (PcdDriverHealthConfigureForm),
|
|
0,
|
|
NULL,
|
|
NULL
|
|
);
|
|
#endif
|
|
if (!EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
}
|
|
FreePool (HiiHandles);
|
|
}
|
|
}
|
|
|
|
EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
|
|
} while (RepairRequired || ConfigurationRequired);
|
|
|
|
RebootRequired = FALSE;
|
|
ReconnectRequired = FALSE;
|
|
DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
|
|
BmDisplayMessages (&DriverHealthInfo[Index]);
|
|
|
|
if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusReconnectRequired) {
|
|
Status = gBS->DisconnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL);
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Disconnect failed. Need to promote reconnect to a reboot.
|
|
//
|
|
RebootRequired = TRUE;
|
|
} else {
|
|
gBS->ConnectController (DriverHealthInfo[Index].ControllerHandle, NULL, NULL, TRUE);
|
|
ReconnectRequired = TRUE;
|
|
}
|
|
}
|
|
|
|
if (DriverHealthInfo[Index].HealthStatus == EfiDriverHealthStatusRebootRequired) {
|
|
RebootRequired = TRUE;
|
|
}
|
|
}
|
|
EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
|
|
|
|
|
|
if (ReconnectRequired) {
|
|
BmRepairAllControllers ();
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
CHAR16 *ControllerName;
|
|
|
|
DriverHealthInfo = EfiBootManagerGetDriverHealthInfo (&Count);
|
|
for (Index = 0; Index < Count; Index++) {
|
|
ControllerName = BmGetControllerName (
|
|
DriverHealthInfo[Index].DriverHealthHandle,
|
|
DriverHealthInfo[Index].ControllerHandle,
|
|
DriverHealthInfo[Index].ChildHandle
|
|
);
|
|
DEBUG ((
|
|
DEBUG_INFO,
|
|
"%02d: %s - %s\n",
|
|
Index,
|
|
ControllerName,
|
|
mHealthStatusText[DriverHealthInfo[Index].HealthStatus]
|
|
));
|
|
if (ControllerName != NULL) {
|
|
FreePool (ControllerName);
|
|
}
|
|
}
|
|
EfiBootManagerFreeDriverHealthInfo (DriverHealthInfo, Count);
|
|
);
|
|
|
|
if (RebootRequired) {
|
|
DEBUG ((DEBUG_INFO, "[BDS] One of the Driver Health instances requires rebooting.\n"));
|
|
gRT->ResetSystem (EfiResetWarm, EFI_SUCCESS, 0, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Attempt to boot the EFI boot option. This routine sets L"BootCurent" and
|
|
also signals the EFI ready to boot event. If the device path for the option
|
|
starts with a BBS device path a legacy boot is attempted via the registered
|
|
gLegacyBoot function. Short form device paths are also supported via this
|
|
rountine. A device path starting with MEDIA_HARDDRIVE_DP, MSG_USB_WWID_DP,
|
|
MSG_USB_CLASS_DP gets expaned out to find the first device that matches.
|
|
If the BootOption Device Path fails the removable media boot algorithm
|
|
is attempted (\EFI\BOOTIA32.EFI, \EFI\BOOTX64.EFI,... only one file type
|
|
is tried per processor type)
|
|
|
|
@param BootOption Boot Option to try and boot.
|
|
On return, BootOption->Status contains the boot status.
|
|
EFI_SUCCESS BootOption was booted
|
|
EFI_UNSUPPORTED A BBS device path was found with no valid callback
|
|
registered via EfiBootManagerInitialize().
|
|
EFI_NOT_FOUND The BootOption was not found on the system
|
|
!EFI_SUCCESS BootOption failed with this error status
|
|
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
EfiBootManagerBoot (
|
|
IN EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE ImageHandle;
|
|
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo;
|
|
UINT16 Uint16;
|
|
UINTN OptionNumber;
|
|
UINTN OriginalOptionNumber;
|
|
EFI_DEVICE_PATH_PROTOCOL *FilePath;
|
|
EFI_DEVICE_PATH_PROTOCOL *Node;
|
|
EFI_HANDLE FvHandle;
|
|
VOID *FileBuffer;
|
|
UINTN FileSize;
|
|
EFI_BOOT_LOGO_PROTOCOL *BootLogo;
|
|
// EFI_EVENT LegacyBootEvent;
|
|
// EFI_GUID *TempGuid; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
// BOOLEAN IsEqual; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
LIST_ENTRY List;
|
|
CHAR16 OptionName[20];
|
|
BDS_COMMON_OPTION *Option;
|
|
|
|
if (BootOption == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (BootOption->FilePath == NULL) {
|
|
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 = FindBootOptionInVariable (BootOption);
|
|
if (OptionNumber == LoadOptionNumberUnassigned) {
|
|
Status = GetFreeOptionNumber (L"BootOrder", &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 ((DEBUG_ERROR, "[Bds] Failed to create Boot#### for a temporary boot - %r!\n", Status));
|
|
BootOption->Status = Status;
|
|
return ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 2. Update system variables: remove BootNext, set BootCurrent
|
|
//
|
|
|
|
//
|
|
// 2.1. Remove BootNext
|
|
// To prevent loops, the boot manager deletes BootNext before transferring control to the
|
|
// preselected boot option.
|
|
//
|
|
if (BootOption->BootNext) {
|
|
Status = gRT->SetVariable (
|
|
L"BootNext",
|
|
&gEfiGlobalVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Deleting variable with current variable implementation should succeed unless it doesn't exist.
|
|
//
|
|
ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);
|
|
}
|
|
//
|
|
// 2.2. Set BootCurrent
|
|
//
|
|
Uint16 = (UINT16) OptionNumber;
|
|
SetVariableAndReportStatusCodeOnError (
|
|
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.
|
|
//
|
|
// IsEqual = FALSE; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
Node = BootOption->FilePath;
|
|
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
|
|
#if 0
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
TempGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node);
|
|
if (TempGuid != NULL) {
|
|
IsEqual = CompareGuid(TempGuid, PcdGetPtr (PcdBootManagerMenuFile));
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
if (!EFI_ERROR (Status) && IsEqual) { // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
if (!EFI_ERROR (Status) ) {
|
|
|
|
DEBUG ((DEBUG_INFO, "[Bds] Booting Boot Manager Menu.\n"));
|
|
StopHotkeyService (NULL, NULL);
|
|
} else {
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
|
|
EfiSignalEventReadyToBoot();
|
|
//
|
|
// 4. Repair system through DriverHealth protocol
|
|
//
|
|
BmRepairAllControllers ();
|
|
}
|
|
#endif
|
|
TriggerCpReadyToBootBefore ();
|
|
//
|
|
// PostCode = 0x2E, Last Chipset initial before boot to OS
|
|
//
|
|
POST_CODE (BDS_READY_TO_BOOT_EVENT);
|
|
//
|
|
// Signal the EVT_SIGNAL_READY_TO_BOOT event
|
|
//
|
|
EfiSignalEventReadyToBoot();
|
|
TriggerCpReadyToBootAfter ();
|
|
PERF_START_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
|
|
|
|
//
|
|
// 5. Load EFI boot option to ImageHandle
|
|
//
|
|
ImageHandle = NULL;
|
|
if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
|
|
Status = EFI_NOT_FOUND;
|
|
FilePath = DuplicateDevicePath (BootOption->FilePath);
|
|
FileBuffer = LoadEfiBootOption (&FilePath, &FileSize);
|
|
if (FileBuffer != NULL) {
|
|
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
|
|
|
|
Status = gBS->LoadImage (
|
|
TRUE,
|
|
gImageHandle,
|
|
FilePath,
|
|
FileBuffer,
|
|
FileSize,
|
|
&ImageHandle
|
|
);
|
|
FreePool (FileBuffer);
|
|
FreePool (FilePath);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Report Status Code to indicate that the failure to load boot option
|
|
//
|
|
REPORT_STATUS_CODE (
|
|
EFI_ERROR_CODE | EFI_ERROR_MINOR,
|
|
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)
|
|
);
|
|
BootOption->Status = Status;
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 6. Adjust the different type memory page number just before booting
|
|
// and save the updated info into the variable for next boot to use
|
|
//
|
|
if ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT) {
|
|
if (PcdGetBool (PcdResetOnMemoryTypeInformationChange)) {
|
|
SetMemoryTypeInformationVariable ();
|
|
}
|
|
}
|
|
|
|
DEBUG_CODE_BEGIN();
|
|
if (BootOption->Description == NULL) {
|
|
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting from unknown device path\n"));
|
|
} else {
|
|
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "[Bds]Booting %s\n", BootOption->Description));
|
|
}
|
|
DEBUG_CODE_END();
|
|
|
|
//
|
|
// Check to see if we should legacy BOOT. If yes then do the legacy boot
|
|
// Write boot to OS performance data for Legacy boot
|
|
//
|
|
if ((DevicePathType (BootOption->FilePath) == BBS_DEVICE_PATH) && (DevicePathSubType (BootOption->FilePath) == BBS_BBS_DP)) {
|
|
InitializeListHead (&List);
|
|
UnicodeSPrint (OptionName, sizeof (OptionName), L"Boot%04x", OptionNumber);
|
|
Option = BdsLibVariableToOption (&List, OptionName);
|
|
if (Option != NULL) {
|
|
BdsLibDoLegacyBoot(Option);
|
|
}
|
|
#if 0
|
|
if (mEfiBootManagerLegacyBoot != 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,
|
|
WriteBootToOsPerformanceData,
|
|
NULL,
|
|
&LegacyBootEvent
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
);
|
|
|
|
mEfiBootManagerLegacyBoot (BootOption);
|
|
} else {
|
|
BootOption->Status = EFI_UNSUPPORTED;
|
|
}
|
|
#endif
|
|
|
|
PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Provide the image with its load options
|
|
//
|
|
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
ImageInfo->LoadOptionsSize = BootOption->OptionalDataSize;
|
|
ImageInfo->LoadOptions = BootOption->OptionalData;
|
|
|
|
//
|
|
// Clean to NULL because the image is loaded directly from the firmwares boot manager.
|
|
//
|
|
ImageInfo->ParentHandle = NULL;
|
|
|
|
//
|
|
// Before calling the image, enable the Watchdog Timer for 5 minutes period
|
|
//
|
|
gBS->SetWatchdogTimer (5 * 60, 0x0000, 0x00, NULL);
|
|
|
|
//
|
|
// Write boot to OS performance data for UEFI boot
|
|
//
|
|
// PERF_CODE (
|
|
// WriteBootToOsPerformanceData (NULL,NULL);
|
|
// );
|
|
|
|
REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderStart));
|
|
|
|
Status = gBS->StartImage (ImageHandle, &BootOption->ExitDataSize, &BootOption->ExitData);
|
|
DEBUG ((DEBUG_INFO | DEBUG_LOAD, "Image Return Status = %r\n", Status));
|
|
BootOption->Status = Status;
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Report Status Code to indicate that boot failure
|
|
//
|
|
REPORT_STATUS_CODE (
|
|
EFI_ERROR_CODE | EFI_ERROR_MINOR,
|
|
(EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_FAILED)
|
|
);
|
|
}
|
|
PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);
|
|
|
|
//
|
|
// Clear the Watchdog Timer after the image returns
|
|
//
|
|
gBS->SetWatchdogTimer (0x0000, 0x0000, 0x0000, NULL);
|
|
|
|
//
|
|
// Set Logo status invalid after trying one boot option
|
|
//
|
|
BootLogo = NULL;
|
|
Status = gBS->LocateProtocol (&gEfiBootLogoProtocolGuid, NULL, (VOID **) &BootLogo);
|
|
if (!EFI_ERROR (Status) && (BootLogo != NULL)) {
|
|
Status = BootLogo->SetBootLogo (BootLogo, NULL, 0, 0, 0, 0);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
//
|
|
// Clear Boot Current
|
|
//
|
|
Status = gRT->SetVariable (
|
|
L"BootCurrent",
|
|
&gEfiGlobalVariableGuid,
|
|
0,
|
|
0,
|
|
NULL
|
|
);
|
|
//
|
|
// Deleting variable with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
#if 0
|
|
/**
|
|
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
|
|
MatchPartitionDevicePathNode (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *BlockIoDevicePath,
|
|
IN HARDDRIVE_DEVICE_PATH *HardDriveDevicePath
|
|
)
|
|
{
|
|
HARDDRIVE_DEVICE_PATH *TmpHdPath;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
BOOLEAN Match;
|
|
EFI_DEVICE_PATH_PROTOCOL *BlockIoHdDevicePathNode;
|
|
|
|
if ((BlockIoDevicePath == NULL) || (HardDriveDevicePath == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make PreviousDevicePath == the device path node before the end node
|
|
//
|
|
DevicePath = BlockIoDevicePath;
|
|
BlockIoHdDevicePathNode = NULL;
|
|
|
|
//
|
|
// find the partition device path node
|
|
//
|
|
while (!IsDevicePathEnd (DevicePath)) {
|
|
if ((DevicePathType (DevicePath) == MEDIA_DEVICE_PATH) &&
|
|
(DevicePathSubType (DevicePath) == MEDIA_HARDDRIVE_DP)
|
|
) {
|
|
BlockIoHdDevicePathNode = DevicePath;
|
|
break;
|
|
}
|
|
|
|
DevicePath = NextDevicePathNode (DevicePath);
|
|
}
|
|
|
|
if (BlockIoHdDevicePathNode == NULL) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// See if the harddrive device path in blockio matches the orig Hard Drive Node
|
|
//
|
|
TmpHdPath = (HARDDRIVE_DEVICE_PATH *) BlockIoHdDevicePathNode;
|
|
Match = FALSE;
|
|
|
|
//
|
|
// Check for the match
|
|
//
|
|
if ((TmpHdPath->MBRType == HardDriveDevicePath->MBRType) &&
|
|
(TmpHdPath->SignatureType == HardDriveDevicePath->SignatureType)) {
|
|
switch (TmpHdPath->SignatureType) {
|
|
case SIGNATURE_TYPE_GUID:
|
|
Match = CompareGuid ((EFI_GUID *)TmpHdPath->Signature, (EFI_GUID *)HardDriveDevicePath->Signature);
|
|
break;
|
|
case SIGNATURE_TYPE_MBR:
|
|
Match = (BOOLEAN) (*((UINT32 *) (&(TmpHdPath->Signature[0]))) == ReadUnaligned32((UINT32 *)(&(HardDriveDevicePath->Signature[0]))));
|
|
break;
|
|
default:
|
|
Match = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return Match;
|
|
}
|
|
|
|
/**
|
|
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 *
|
|
EFIAPI
|
|
EfiBootManagerEnumerateBootOptions (
|
|
UINTN *BootOptionCount
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
UINT16 NonBlockNumber;
|
|
UINTN HandleCount;
|
|
EFI_HANDLE *Handles;
|
|
EFI_BLOCK_IO_PROTOCOL *BlkIo;
|
|
UINTN Removable;
|
|
UINTN Index;
|
|
UINTN FunctionIndex;
|
|
CHAR16 *Temp;
|
|
CHAR16 *DescriptionPtr;
|
|
CHAR16 Description[30];
|
|
|
|
ASSERT (BootOptionCount != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (BootOptionCount == NULL) {
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
*BootOptionCount = 0;
|
|
BootOptions = NULL;
|
|
|
|
//
|
|
// Parse removable block io followed by fixed block io
|
|
//
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiBlockIoProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
|
|
for (Removable = 0; Removable < 2; Removable++) {
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
Handles[Index],
|
|
&gEfiBlockIoProtocolGuid,
|
|
(VOID **) &BlkIo
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip the logical partitions
|
|
//
|
|
if (BlkIo->Media->LogicalPartition) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Skip the fixed block io then the removable block io
|
|
//
|
|
if (BlkIo->Media->RemovableMedia == ((Removable == 0) ? FALSE : TRUE)) {
|
|
continue;
|
|
}
|
|
|
|
DescriptionPtr = NULL;
|
|
for (FunctionIndex = 0; FunctionIndex < sizeof (mGetBootDescription) / sizeof (mGetBootDescription[0]); FunctionIndex++) {
|
|
DescriptionPtr = mGetBootDescription[FunctionIndex] (Handles[Index]);
|
|
if (DescriptionPtr != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DescriptionPtr == NULL) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Avoid description confusion between UEFI & Legacy boot option by adding "UEFI " prefix
|
|
//
|
|
Temp = AllocatePool (StrSize (DescriptionPtr) + sizeof (mUefiPrefix));
|
|
ASSERT (Temp != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (Temp == NULL){
|
|
return NULL;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
StrCpy (Temp, mUefiPrefix);
|
|
StrCat (Temp, DescriptionPtr);
|
|
FreePool (DescriptionPtr);
|
|
DescriptionPtr = Temp;
|
|
|
|
BootOptions = ReallocatePool (
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
|
|
BootOptions
|
|
);
|
|
ASSERT (BootOptions != NULL);
|
|
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
&BootOptions[(*BootOptionCount)++],
|
|
LoadOptionNumberUnassigned,
|
|
LoadOptionTypeBoot,
|
|
LOAD_OPTION_ACTIVE,
|
|
DescriptionPtr,
|
|
DevicePathFromHandle (Handles[Index]),
|
|
NULL,
|
|
0
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
FreePool (DescriptionPtr);
|
|
}
|
|
}
|
|
|
|
if (HandleCount != 0) {
|
|
FreePool (Handles);
|
|
}
|
|
|
|
//
|
|
// Parse simple file system not based on block io
|
|
//
|
|
NonBlockNumber = 0;
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiSimpleFileSystemProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
Status = gBS->HandleProtocol (
|
|
Handles[Index],
|
|
&gEfiBlockIoProtocolGuid,
|
|
(VOID **) &BlkIo
|
|
);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Skip if the file system handle supports a BlkIo protocol, which we've handled in above
|
|
//
|
|
continue;
|
|
}
|
|
UnicodeSPrint (Description, sizeof (Description), NonBlockNumber > 0 ? L"%s %d" : L"%s", L"UEFI Non-Block Boot Device", NonBlockNumber);
|
|
|
|
BootOptions = ReallocatePool (
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
|
|
BootOptions
|
|
);
|
|
ASSERT (BootOptions != NULL);
|
|
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
&BootOptions[(*BootOptionCount)++],
|
|
LoadOptionNumberUnassigned,
|
|
LoadOptionTypeBoot,
|
|
LOAD_OPTION_ACTIVE,
|
|
Description,
|
|
DevicePathFromHandle (Handles[Index]),
|
|
NULL,
|
|
0
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
if (HandleCount != 0) {
|
|
FreePool (Handles);
|
|
}
|
|
|
|
//
|
|
// Parse load file, assuming UEFI Network boot option
|
|
//
|
|
gBS->LocateHandleBuffer (
|
|
ByProtocol,
|
|
&gEfiLoadFileProtocolGuid,
|
|
NULL,
|
|
&HandleCount,
|
|
&Handles
|
|
);
|
|
for (Index = 0; Index < HandleCount; Index++) {
|
|
|
|
UnicodeSPrint (Description, sizeof (Description), Index > 0 ? L"%s %d" : L"%s", L"UEFI Network", Index);
|
|
|
|
BootOptions = ReallocatePool (
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount),
|
|
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION) * (*BootOptionCount + 1),
|
|
BootOptions
|
|
);
|
|
ASSERT (BootOptions != NULL);
|
|
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
&BootOptions[(*BootOptionCount)++],
|
|
LoadOptionNumberUnassigned,
|
|
LoadOptionTypeBoot,
|
|
LOAD_OPTION_ACTIVE,
|
|
Description,
|
|
DevicePathFromHandle (Handles[Index]),
|
|
NULL,
|
|
0
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
|
|
if (HandleCount != 0) {
|
|
FreePool (Handles);
|
|
}
|
|
|
|
return BootOptions;
|
|
}
|
|
|
|
/**
|
|
The function enumerates all boot options, creates them and registers them in the BootOrder variable.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
EfiBootManagerRefreshAllBootOption (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *NvBootOptions;
|
|
UINTN NvBootOptionCount;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
UINTN BootOptionCount;
|
|
UINTN Index;
|
|
|
|
BootOptions = NULL; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
NvBootOptions = NULL; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
|
|
//
|
|
// Optionally refresh the legacy boot option
|
|
//
|
|
if (mEfiBootManagerRefreshLegacyBootOption != NULL) {
|
|
mEfiBootManagerRefreshLegacyBootOption ();
|
|
}
|
|
|
|
BootOptions = EfiBootManagerEnumerateBootOptions (&BootOptionCount);
|
|
NvBootOptions = EfiBootManagerGetLoadOptions (&NvBootOptionCount, LoadOptionTypeBoot);
|
|
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (BootOptions == NULL && NvBootOptions == NULL) {
|
|
return;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
//
|
|
// 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), &mAutoCreateBootOptionGuid);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (BootOptions[Index].OptionalData == NULL) {
|
|
goto EXIT;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
BootOptions[Index].OptionalDataSize = sizeof (EFI_GUID);
|
|
}
|
|
|
|
//
|
|
// Remove invalid EFI boot options from NV
|
|
//
|
|
for (Index = 0; Index < NvBootOptionCount; Index++) {
|
|
if (((DevicePathType (NvBootOptions[Index].FilePath) != BBS_DEVICE_PATH) ||
|
|
(DevicePathSubType (NvBootOptions[Index].FilePath) != BBS_BBS_DP)
|
|
) &&
|
|
(NvBootOptions[Index].OptionalDataSize == sizeof (EFI_GUID)) &&
|
|
CompareGuid ((EFI_GUID *) NvBootOptions[Index].OptionalData, &mAutoCreateBootOptionGuid)
|
|
) {
|
|
//
|
|
// Only check those added by BDS
|
|
// so that the boot options added by end-user or OS installer won't be deleted
|
|
//
|
|
if (EfiBootManagerFindLoadOption (&NvBootOptions[Index], BootOptions, BootOptionCount) == (UINTN) -1) {
|
|
Status = EfiBootManagerDeleteLoadOptionVariable (NvBootOptions[Index].OptionNumber, LoadOptionTypeBoot);
|
|
//
|
|
// Deleting variable with current variable implementation shouldn't fail.
|
|
//
|
|
ASSERT_EFI_ERROR (Status);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add new EFI boot options to NV
|
|
//
|
|
for (Index = 0; Index < BootOptionCount; Index++) {
|
|
if (EfiBootManagerFindLoadOption (&BootOptions[Index], NvBootOptions, NvBootOptionCount) == (UINTN) -1) {
|
|
EfiBootManagerAddLoadOptionVariable (&BootOptions[Index], (UINTN) -1);
|
|
//
|
|
// Try best to add the boot options so continue upon failure.
|
|
//
|
|
}
|
|
}
|
|
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
EXIT:
|
|
|
|
if (BootOptions != NULL) {
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
}
|
|
|
|
if (NvBootOptions != NULL) {
|
|
EfiBootManagerFreeLoadOptions (NvBootOptions, NvBootOptionCount);
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
}
|
|
|
|
|
|
/**
|
|
For a bootable Device path, return its boot type.
|
|
|
|
@param DevicePath The bootable device Path to check
|
|
|
|
@retval AcpiFloppyBoot If given device path contains ACPI_DEVICE_PATH type device path node
|
|
which HID is floppy device.
|
|
@retval MessageAtapiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
|
|
and its last device path node's subtype is MSG_ATAPI_DP.
|
|
@retval MessageSataBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
|
|
and its last device path node's subtype is MSG_SATA_DP.
|
|
@retval MessageScsiBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
|
|
and its last device path node's subtype is MSG_SCSI_DP.
|
|
@retval MessageUsbBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
|
|
and its last device path node's subtype is MSG_USB_DP.
|
|
@retval MessageNetworkBoot If given device path contains MESSAGING_DEVICE_PATH type device path node
|
|
and its last device path node's subtype is MSG_MAC_ADDR_DP, MSG_VLAN_DP,
|
|
MSG_IPv4_DP or MSG_IPv6_DP.
|
|
@retval UnsupportedBoot If tiven device path doesn't match the above condition, it's not supported.
|
|
|
|
**/
|
|
BOOT_TYPE
|
|
BootTypeFromDevicePath (
|
|
IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
|
|
)
|
|
{
|
|
EFI_DEVICE_PATH_PROTOCOL *Node;
|
|
EFI_DEVICE_PATH_PROTOCOL *NextNode;
|
|
|
|
ASSERT (DevicePath != NULL);
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
if (DevicePath == NULL) {
|
|
return MiscBoot;
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
for (Node = DevicePath; !IsDevicePathEndType (Node); Node = NextDevicePathNode (Node)) {
|
|
switch (DevicePathType (Node)) {
|
|
|
|
case ACPI_DEVICE_PATH:
|
|
if (EISA_ID_TO_NUM (((ACPI_HID_DEVICE_PATH *) Node)->HID) == 0x0604) {
|
|
return AcpiFloppyBoot;
|
|
}
|
|
break;
|
|
|
|
case HARDWARE_DEVICE_PATH:
|
|
if (DevicePathSubType (Node) == HW_CONTROLLER_DP) {
|
|
return HardwareDeviceBoot;
|
|
}
|
|
break;
|
|
|
|
case MESSAGING_DEVICE_PATH:
|
|
//
|
|
// Skip LUN device node
|
|
//
|
|
NextNode = Node;
|
|
do {
|
|
NextNode = NextDevicePathNode (NextNode);
|
|
} while (
|
|
(DevicePathType (NextNode) == MESSAGING_DEVICE_PATH) &&
|
|
(DevicePathSubType(NextNode) == MSG_DEVICE_LOGICAL_UNIT_DP)
|
|
);
|
|
|
|
//
|
|
// If the device path not only point to driver device, it is not a messaging device path,
|
|
//
|
|
if (!IsDevicePathEndType (NextNode)) {
|
|
break;
|
|
}
|
|
|
|
switch (DevicePathSubType (Node)) {
|
|
case MSG_ATAPI_DP:
|
|
return MessageAtapiBoot;
|
|
break;
|
|
|
|
case MSG_SATA_DP:
|
|
return MessageSataBoot;
|
|
break;
|
|
|
|
case MSG_USB_DP:
|
|
return MessageUsbBoot;
|
|
break;
|
|
|
|
case MSG_SCSI_DP:
|
|
return MessageScsiBoot;
|
|
break;
|
|
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0005
|
|
//
|
|
case MSG_NVME_NAMESPACE_DP:
|
|
return MessageNvmeBoot;
|
|
break;
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
|
|
case MSG_MAC_ADDR_DP:
|
|
case MSG_VLAN_DP:
|
|
case MSG_IPv4_DP:
|
|
case MSG_IPv6_DP:
|
|
return MessageNetworkBoot;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MiscBoot;
|
|
}
|
|
|
|
/**
|
|
This function is called to create the boot option for the Boot Manager Menu.
|
|
|
|
The Boot Manager Menu is shown after successfully booting a boot option.
|
|
Assume the BootManagerMenuFile is in the same FV as the module links to this library.
|
|
|
|
@param BootOption Return the boot option of the Boot Manager Menu
|
|
|
|
@retval EFI_SUCCESS Successfully register the Boot Manager Menu.
|
|
@retval Status Return status of gRT->SetVariable (). BootOption still points
|
|
to the Boot Manager Menu even the Status is not EFI_SUCCESS.
|
|
**/
|
|
EFI_STATUS
|
|
RegisterBootManagerMenu (
|
|
OUT EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
CHAR16 *Description;
|
|
UINTN DescriptionLength;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
EFI_LOADED_IMAGE_PROTOCOL *LoadedImage;
|
|
MEDIA_FW_VOL_FILEPATH_DEVICE_PATH FileNode;
|
|
|
|
Status = GetSectionFromFv (
|
|
PcdGetPtr (PcdBootManagerMenuFile),
|
|
EFI_SECTION_USER_INTERFACE,
|
|
0,
|
|
(VOID **) &Description,
|
|
&DescriptionLength
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
Description = NULL;
|
|
}
|
|
|
|
EfiInitializeFwVolDevicepathNode (&FileNode, PcdGetPtr (PcdBootManagerMenuFile));
|
|
Status = gBS->HandleProtocol (
|
|
gImageHandle,
|
|
&gEfiLoadedImageProtocolGuid,
|
|
(VOID **) &LoadedImage
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
DevicePath = AppendDevicePathNode (
|
|
DevicePathFromHandle (LoadedImage->DeviceHandle),
|
|
(EFI_DEVICE_PATH_PROTOCOL *) &FileNode
|
|
);
|
|
ASSERT (DevicePath != NULL);
|
|
|
|
Status = EfiBootManagerInitializeLoadOption (
|
|
BootOption,
|
|
LoadOptionNumberUnassigned,
|
|
LoadOptionTypeBoot,
|
|
LOAD_OPTION_CATEGORY_APP | LOAD_OPTION_ACTIVE | LOAD_OPTION_HIDDEN,
|
|
(Description != NULL) ? Description : L"Boot Manager Menu",
|
|
DevicePath,
|
|
NULL,
|
|
0
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
FreePool (DevicePath);
|
|
if (Description != NULL) {
|
|
FreePool (Description);
|
|
}
|
|
|
|
DEBUG_CODE (
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
UINTN BootOptionCount;
|
|
|
|
BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
|
|
ASSERT (EfiBootManagerFindLoadOption (BootOption, BootOptions, BootOptionCount) == -1);
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
);
|
|
|
|
return EfiBootManagerAddLoadOptionVariable (BootOption, 0);
|
|
}
|
|
|
|
/**
|
|
Return the boot option corresponding to the Boot Manager Menu.
|
|
It may automatically create one if the boot option hasn't been created yet.
|
|
|
|
@param BootOption Return the Boot Manager Menu.
|
|
|
|
@retval EFI_SUCCESS The Boot Manager Menu is successfully returned.
|
|
@retval Status Return status of gRT->SetVariable (). BootOption still points
|
|
to the Boot Manager Menu even the Status is not EFI_SUCCESS.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
EfiBootManagerGetBootManagerMenu (
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN BootOptionCount;
|
|
EFI_BOOT_MANAGER_LOAD_OPTION *BootOptions;
|
|
UINTN Index;
|
|
EFI_DEVICE_PATH_PROTOCOL *Node;
|
|
EFI_HANDLE FvHandle;
|
|
EFI_GUID *TempGuid; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
BOOLEAN IsEqual; // ROYAL_PARK_OVERRIDE: RoyalParkOverrideContent - RPCO-0031
|
|
|
|
BootOptions = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot);
|
|
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideBegin - RPCO-0031
|
|
//
|
|
for (Index = 0; Index < BootOptionCount; Index++) {
|
|
IsEqual = FALSE;
|
|
Node = BootOptions[Index].FilePath;
|
|
Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &Node, &FvHandle);
|
|
if (!EFI_ERROR (Status)) {
|
|
TempGuid = EfiGetNameGuidFromFwVolDevicePathNode ((CONST MEDIA_FW_VOL_FILEPATH_DEVICE_PATH *) Node);
|
|
if (TempGuid != NULL) {
|
|
IsEqual = CompareGuid (TempGuid, PcdGetPtr (PcdBootManagerMenuFile));
|
|
}
|
|
if (IsEqual) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// ROYAL_PARK_OVERRIDE: RoyalParkOverrideEnd
|
|
//
|
|
EfiBootManagerFreeLoadOptions (BootOptions, BootOptionCount);
|
|
|
|
//
|
|
// Automatically create the Boot#### for Boot Manager Menu when not found.
|
|
//
|
|
if (Index == BootOptionCount) {
|
|
return RegisterBootManagerMenu (BootOption);
|
|
} else {
|
|
return EFI_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|