/** @file | |
Load option library functions which relate with creating and processing load options. | |
Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR> | |
(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP<BR> | |
SPDX-License-Identifier: BSD-2-Clause-Patent | |
**/ | |
#include "InternalBm.h" | |
#include <Library/VariablePolicyHelperLib.h> | |
GLOBAL_REMOVE_IF_UNREFERENCED | |
CHAR16 *mBmLoadOptionName[] = { | |
L"Driver", | |
L"SysPrep", | |
L"Boot", | |
L"PlatformRecovery" | |
}; | |
GLOBAL_REMOVE_IF_UNREFERENCED | |
CHAR16 *mBmLoadOptionOrderName[] = { | |
EFI_DRIVER_ORDER_VARIABLE_NAME, | |
EFI_SYS_PREP_ORDER_VARIABLE_NAME, | |
EFI_BOOT_ORDER_VARIABLE_NAME, | |
NULL // PlatformRecovery#### doesn't have associated *Order variable | |
}; | |
/** | |
Call Visitor function for each variable in variable storage. | |
@param Visitor Visitor function. | |
@param Context The context passed to Visitor function. | |
**/ | |
VOID | |
BmForEachVariable ( | |
BM_VARIABLE_VISITOR Visitor, | |
VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
CHAR16 *Name; | |
EFI_GUID Guid; | |
UINTN NameSize; | |
UINTN NewNameSize; | |
NameSize = sizeof (CHAR16); | |
Name = AllocateZeroPool (NameSize); | |
ASSERT (Name != NULL); | |
while (TRUE) { | |
NewNameSize = NameSize; | |
Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); | |
if (Status == EFI_BUFFER_TOO_SMALL) { | |
Name = ReallocatePool (NameSize, NewNameSize, Name); | |
ASSERT (Name != NULL); | |
Status = gRT->GetNextVariableName (&NewNameSize, Name, &Guid); | |
NameSize = NewNameSize; | |
} | |
if (Status == EFI_NOT_FOUND) { | |
break; | |
} | |
ASSERT_EFI_ERROR (Status); | |
Visitor (Name, &Guid, Context); | |
} | |
FreePool (Name); | |
} | |
/** | |
Get the Option Number that wasn't used. | |
@param LoadOptionType The load option type. | |
@param FreeOptionNumber Return the minimal free option number. | |
@retval EFI_SUCCESS The option number is found and will be returned. | |
@retval EFI_OUT_OF_RESOURCES There is no free option number that can be used. | |
@retval EFI_INVALID_PARAMETER FreeOptionNumber is NULL | |
**/ | |
EFI_STATUS | |
BmGetFreeOptionNumber ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType, | |
OUT UINT16 *FreeOptionNumber | |
) | |
{ | |
UINTN OptionNumber; | |
UINTN Index; | |
UINT16 *OptionOrder; | |
UINTN OptionOrderSize; | |
UINT16 *BootNext; | |
ASSERT (FreeOptionNumber != NULL); | |
ASSERT ( | |
LoadOptionType == LoadOptionTypeDriver || | |
LoadOptionType == LoadOptionTypeBoot || | |
LoadOptionType == LoadOptionTypeSysPrep | |
); | |
GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **)&OptionOrder, &OptionOrderSize); | |
ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0)); | |
BootNext = NULL; | |
if (LoadOptionType == LoadOptionTypeBoot) { | |
GetEfiGlobalVariable2 (L"BootNext", (VOID **)&BootNext, NULL); | |
} | |
for (OptionNumber = 0; | |
OptionNumber < OptionOrderSize / sizeof (UINT16) | |
+ ((BootNext != NULL) ? 1 : 0); | |
OptionNumber++ | |
) | |
{ | |
// | |
// Search in OptionOrder whether the OptionNumber exists | |
// | |
for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { | |
if (OptionNumber == OptionOrder[Index]) { | |
break; | |
} | |
} | |
// | |
// We didn't find it in the ****Order array and it doesn't equal to BootNext | |
// Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1 | |
// | |
if ((Index == OptionOrderSize / sizeof (UINT16)) && | |
((BootNext == NULL) || (OptionNumber != *BootNext)) | |
) | |
{ | |
break; | |
} | |
} | |
if (OptionOrder != NULL) { | |
FreePool (OptionOrder); | |
} | |
if (BootNext != NULL) { | |
FreePool (BootNext); | |
} | |
// | |
// When BootOrder & BootNext conver all numbers in the range [0 ... 0xffff], | |
// OptionNumber equals to 0x10000 which is not valid. | |
// | |
ASSERT (OptionNumber <= 0x10000); | |
if (OptionNumber == 0x10000) { | |
return EFI_OUT_OF_RESOURCES; | |
} else { | |
*FreeOptionNumber = (UINT16)OptionNumber; | |
return EFI_SUCCESS; | |
} | |
} | |
/** | |
Create the Boot####, Driver####, SysPrep####, PlatformRecovery#### variable | |
from the load option. | |
@param LoadOption Pointer to the load option. | |
@retval EFI_SUCCESS The variable was created. | |
@retval Others Error status returned by RT->SetVariable. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerLoadOptionToVariable ( | |
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Option | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN VariableSize; | |
UINT8 *Variable; | |
UINT8 *Ptr; | |
CHAR16 OptionName[BM_OPTION_NAME_LEN]; | |
CHAR16 *Description; | |
CHAR16 NullChar; | |
EDKII_VARIABLE_POLICY_PROTOCOL *VariablePolicy; | |
UINT32 VariableAttributes; | |
if ((Option->OptionNumber == LoadOptionNumberUnassigned) || | |
(Option->FilePath == NULL) || | |
((UINT32)Option->OptionType >= LoadOptionTypeMax) | |
) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Convert NULL description to empty description | |
// | |
NullChar = L'\0'; | |
Description = Option->Description; | |
if (Description == NULL) { | |
Description = &NullChar; | |
} | |
/* | |
UINT32 Attributes; | |
UINT16 FilePathListLength; | |
CHAR16 Description[]; | |
EFI_DEVICE_PATH_PROTOCOL FilePathList[]; | |
UINT8 OptionalData[]; | |
TODO: FilePathList[] IS: | |
A packed array of UEFI device paths. The first element of the | |
array is a device path that describes the device and location of the | |
Image for this load option. The FilePathList[0] is specific | |
to the device type. Other device paths may optionally exist in the | |
FilePathList, but their usage is OSV specific. Each element | |
in the array is variable length, and ends at the device path end | |
structure. | |
*/ | |
VariableSize = sizeof (Option->Attributes) | |
+ sizeof (UINT16) | |
+ StrSize (Description) | |
+ GetDevicePathSize (Option->FilePath) | |
+ Option->OptionalDataSize; | |
Variable = AllocatePool (VariableSize); | |
ASSERT (Variable != NULL); | |
Ptr = Variable; | |
WriteUnaligned32 ((UINT32 *)Ptr, Option->Attributes); | |
Ptr += sizeof (Option->Attributes); | |
WriteUnaligned16 ((UINT16 *)Ptr, (UINT16)GetDevicePathSize (Option->FilePath)); | |
Ptr += sizeof (UINT16); | |
CopyMem (Ptr, Description, StrSize (Description)); | |
Ptr += StrSize (Description); | |
CopyMem (Ptr, Option->FilePath, GetDevicePathSize (Option->FilePath)); | |
Ptr += GetDevicePathSize (Option->FilePath); | |
CopyMem (Ptr, Option->OptionalData, Option->OptionalDataSize); | |
UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[Option->OptionType], Option->OptionNumber); | |
VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE; | |
if (Option->OptionType == LoadOptionTypePlatformRecovery) { | |
// | |
// Lock the PlatformRecovery#### | |
// | |
Status = gBS->LocateProtocol (&gEdkiiVariablePolicyProtocolGuid, NULL, (VOID **)&VariablePolicy); | |
if (!EFI_ERROR (Status)) { | |
Status = RegisterBasicVariablePolicy ( | |
VariablePolicy, | |
&gEfiGlobalVariableGuid, | |
OptionName, | |
VARIABLE_POLICY_NO_MIN_SIZE, | |
VARIABLE_POLICY_NO_MAX_SIZE, | |
VARIABLE_POLICY_NO_MUST_ATTR, | |
VARIABLE_POLICY_NO_CANT_ATTR, | |
VARIABLE_POLICY_TYPE_LOCK_NOW | |
); | |
ASSERT_EFI_ERROR (Status); | |
} | |
VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS; | |
} | |
Status = gRT->SetVariable ( | |
OptionName, | |
&gEfiGlobalVariableGuid, | |
VariableAttributes, | |
VariableSize, | |
Variable | |
); | |
FreePool (Variable); | |
return Status; | |
} | |
/** | |
Update order variable . | |
@param OptionOrderName Order variable name which need to be updated. | |
@param OptionNumber Option number for the new option. | |
@param Position Position of the new load option to put in the ****Order variable. | |
@retval EFI_SUCCESS The boot#### or driver#### have been successfully registered. | |
@retval EFI_ALREADY_STARTED The option number of Option is being used already. | |
@retval EFI_STATUS Return the status of gRT->SetVariable (). | |
**/ | |
EFI_STATUS | |
BmAddOptionNumberToOrderVariable ( | |
IN CHAR16 *OptionOrderName, | |
IN UINT16 OptionNumber, | |
IN UINTN Position | |
) | |
{ | |
EFI_STATUS Status; | |
UINTN Index; | |
UINT16 *OptionOrder; | |
UINT16 *NewOptionOrder; | |
UINTN OptionOrderSize; | |
// | |
// Update the option order variable | |
// | |
GetEfiGlobalVariable2 (OptionOrderName, (VOID **)&OptionOrder, &OptionOrderSize); | |
ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0)); | |
Status = EFI_SUCCESS; | |
for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { | |
if (OptionOrder[Index] == OptionNumber) { | |
Status = EFI_ALREADY_STARTED; | |
break; | |
} | |
} | |
if (!EFI_ERROR (Status)) { | |
Position = MIN (Position, OptionOrderSize / sizeof (UINT16)); | |
NewOptionOrder = AllocatePool (OptionOrderSize + sizeof (UINT16)); | |
ASSERT (NewOptionOrder != NULL); | |
if (OptionOrderSize != 0) { | |
CopyMem (NewOptionOrder, OptionOrder, Position * sizeof (UINT16)); | |
CopyMem (&NewOptionOrder[Position + 1], &OptionOrder[Position], OptionOrderSize - Position * sizeof (UINT16)); | |
} | |
NewOptionOrder[Position] = OptionNumber; | |
Status = gRT->SetVariable ( | |
OptionOrderName, | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
OptionOrderSize + sizeof (UINT16), | |
NewOptionOrder | |
); | |
FreePool (NewOptionOrder); | |
} | |
if (OptionOrder != NULL) { | |
FreePool (OptionOrder); | |
} | |
return Status; | |
} | |
/** | |
This function will register the new Boot####, Driver#### or SysPrep#### option. | |
After the *#### is updated, the *Order will also be updated. | |
@param Option Pointer to load option to add. If on input | |
Option->OptionNumber is LoadOptionNumberUnassigned, | |
then on output Option->OptionNumber is updated to | |
the number of the new Boot####, | |
Driver#### or SysPrep#### option. | |
@param Position Position of the new load option to put in the ****Order variable. | |
@retval EFI_SUCCESS The *#### have been successfully registered. | |
@retval EFI_INVALID_PARAMETER The option number exceeds 0xFFFF. | |
@retval EFI_ALREADY_STARTED The option number of Option is being used already. | |
Note: this API only adds new load option, no replacement support. | |
@retval EFI_OUT_OF_RESOURCES There is no free option number that can be used when the | |
option number specified in the Option is LoadOptionNumberUnassigned. | |
@return Status codes of gRT->SetVariable (). | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerAddLoadOptionVariable ( | |
IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option, | |
IN UINTN Position | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 OptionNumber; | |
if (Option == NULL) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((Option->OptionType != LoadOptionTypeDriver) && | |
(Option->OptionType != LoadOptionTypeSysPrep) && | |
(Option->OptionType != LoadOptionTypeBoot) | |
) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Get the free option number if the option number is unassigned | |
// | |
if (Option->OptionNumber == LoadOptionNumberUnassigned) { | |
Status = BmGetFreeOptionNumber (Option->OptionType, &OptionNumber); | |
if (EFI_ERROR (Status)) { | |
return Status; | |
} | |
Option->OptionNumber = OptionNumber; | |
} | |
if (Option->OptionNumber >= LoadOptionNumberMax) { | |
return EFI_INVALID_PARAMETER; | |
} | |
Status = BmAddOptionNumberToOrderVariable (mBmLoadOptionOrderName[Option->OptionType], (UINT16)Option->OptionNumber, Position); | |
if (!EFI_ERROR (Status)) { | |
// | |
// Save the Boot#### or Driver#### variable | |
// | |
Status = EfiBootManagerLoadOptionToVariable (Option); | |
if (EFI_ERROR (Status)) { | |
// | |
// Remove the #### from *Order variable when the Driver####/SysPrep####/Boot#### cannot be saved. | |
// | |
EfiBootManagerDeleteLoadOptionVariable (Option->OptionNumber, Option->OptionType); | |
} | |
} | |
return Status; | |
} | |
/** | |
Sort the load option. The DriverOrder or BootOrder will be re-created to | |
reflect the new order. | |
@param OptionType Load option type | |
@param CompareFunction The comparator | |
**/ | |
VOID | |
EFIAPI | |
EfiBootManagerSortLoadOptionVariable ( | |
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, | |
SORT_COMPARE CompareFunction | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption; | |
UINTN LoadOptionCount; | |
UINTN Index; | |
UINT16 *OptionOrder; | |
LoadOption = EfiBootManagerGetLoadOptions (&LoadOptionCount, OptionType); | |
// | |
// Insertion sort algorithm | |
// | |
PerformQuickSort ( | |
LoadOption, | |
LoadOptionCount, | |
sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), | |
CompareFunction | |
); | |
// | |
// Create new ****Order variable | |
// | |
OptionOrder = AllocatePool (LoadOptionCount * sizeof (UINT16)); | |
ASSERT (OptionOrder != NULL); | |
for (Index = 0; Index < LoadOptionCount; Index++) { | |
OptionOrder[Index] = (UINT16)LoadOption[Index].OptionNumber; | |
} | |
Status = gRT->SetVariable ( | |
mBmLoadOptionOrderName[OptionType], | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
LoadOptionCount * sizeof (UINT16), | |
OptionOrder | |
); | |
// | |
// Changing the *Order content without increasing its size with current variable implementation shouldn't fail. | |
// | |
ASSERT_EFI_ERROR (Status); | |
FreePool (OptionOrder); | |
EfiBootManagerFreeLoadOptions (LoadOption, LoadOptionCount); | |
} | |
/** | |
Initialize a load option. | |
@param Option Pointer to the load option to be initialized. | |
@param OptionNumber Option number of the load option. | |
@param OptionType Type of the load option. | |
@param Attributes Attributes of the load option. | |
@param Description Description of the load option. | |
@param FilePath Device path of the load option. | |
@param OptionalData Optional data of the load option. | |
@param OptionalDataSize Size of the optional data of the load option. | |
@retval EFI_SUCCESS The load option was initialized successfully. | |
@retval EFI_INVALID_PARAMETER Option, Description or FilePath is NULL. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerInitializeLoadOption ( | |
IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option, | |
IN UINTN OptionNumber, | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType, | |
IN UINT32 Attributes, | |
IN CHAR16 *Description, | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
IN UINT8 *OptionalData OPTIONAL, | |
IN UINT32 OptionalDataSize | |
) | |
{ | |
if ((Option == NULL) || (Description == NULL) || (FilePath == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (((OptionalData != NULL) && (OptionalDataSize == 0)) || | |
((OptionalData == NULL) && (OptionalDataSize != 0))) | |
{ | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((UINT32)OptionType >= LoadOptionTypeMax) { | |
return EFI_INVALID_PARAMETER; | |
} | |
ZeroMem (Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); | |
Option->OptionNumber = OptionNumber; | |
Option->OptionType = OptionType; | |
Option->Attributes = Attributes; | |
Option->Description = AllocateCopyPool (StrSize (Description), Description); | |
Option->FilePath = DuplicateDevicePath (FilePath); | |
if (OptionalData != NULL) { | |
Option->OptionalData = AllocateCopyPool (OptionalDataSize, OptionalData); | |
Option->OptionalDataSize = OptionalDataSize; | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Return the index of the load option in the load option array. | |
The function consider two load options are equal when the | |
OptionType, Attributes, Description, FilePath and OptionalData are equal. | |
@param Key Pointer to the load option to be found. | |
@param Array Pointer to the array of load options to be found. | |
@param Count Number of entries in the Array. | |
@retval -1 Key wasn't found in the Array. | |
@retval 0 ~ Count-1 The index of the Key in the Array. | |
**/ | |
INTN | |
EFIAPI | |
EfiBootManagerFindLoadOption ( | |
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Key, | |
IN CONST EFI_BOOT_MANAGER_LOAD_OPTION *Array, | |
IN UINTN Count | |
) | |
{ | |
UINTN Index; | |
for (Index = 0; Index < Count; Index++) { | |
if ((Key->OptionType == Array[Index].OptionType) && | |
(Key->Attributes == Array[Index].Attributes) && | |
(StrCmp (Key->Description, Array[Index].Description) == 0) && | |
(CompareMem (Key->FilePath, Array[Index].FilePath, GetDevicePathSize (Key->FilePath)) == 0) && | |
(Key->OptionalDataSize == Array[Index].OptionalDataSize) && | |
(CompareMem (Key->OptionalData, Array[Index].OptionalData, Key->OptionalDataSize) == 0)) | |
{ | |
return (INTN)Index; | |
} | |
} | |
return -1; | |
} | |
/** | |
Delete the load option. | |
@param OptionNumber Indicate the option number of load option | |
@param OptionType Indicate the type of load option | |
@retval EFI_INVALID_PARAMETER OptionType or OptionNumber is invalid. | |
@retval EFI_NOT_FOUND The load option cannot be found | |
@retval EFI_SUCCESS The load option was deleted | |
@retval others Status of RT->SetVariable() | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerDeleteLoadOptionVariable ( | |
IN UINTN OptionNumber, | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType | |
) | |
{ | |
UINT16 *OptionOrder; | |
UINTN OptionOrderSize; | |
UINTN Index; | |
CHAR16 OptionName[BM_OPTION_NAME_LEN]; | |
if (((UINT32)OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if ((OptionType == LoadOptionTypeDriver) || (OptionType == LoadOptionTypeSysPrep) || (OptionType == LoadOptionTypeBoot)) { | |
// | |
// If the associated *Order exists, firstly remove the reference in *Order for | |
// Driver####, SysPrep#### and Boot####. | |
// | |
GetEfiGlobalVariable2 (mBmLoadOptionOrderName[OptionType], (VOID **)&OptionOrder, &OptionOrderSize); | |
ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0)); | |
for (Index = 0; Index < OptionOrderSize / sizeof (UINT16); Index++) { | |
if (OptionOrder[Index] == OptionNumber) { | |
OptionOrderSize -= sizeof (UINT16); | |
CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16)); | |
gRT->SetVariable ( | |
mBmLoadOptionOrderName[OptionType], | |
&gEfiGlobalVariableGuid, | |
EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, | |
OptionOrderSize, | |
OptionOrder | |
); | |
break; | |
} | |
} | |
if (OptionOrder != NULL) { | |
FreePool (OptionOrder); | |
} | |
} | |
// | |
// Remove the Driver####, SysPrep####, Boot#### or PlatformRecovery#### itself. | |
// | |
UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[OptionType], OptionNumber); | |
return gRT->SetVariable ( | |
OptionName, | |
&gEfiGlobalVariableGuid, | |
0, | |
0, | |
NULL | |
); | |
} | |
/** | |
Returns the size of a device path in bytes. | |
This function returns the size, in bytes, of the device path data structure | |
specified by DevicePath including the end of device path node. If DevicePath | |
is NULL, then 0 is returned. If the length of the device path is bigger than | |
MaxSize, also return 0 to indicate this is an invalidate device path. | |
@param DevicePath A pointer to a device path data structure. | |
@param MaxSize Max valid device path size. If big than this size, | |
return error. | |
@retval 0 An invalid device path. | |
@retval Others The size of a device path in bytes. | |
**/ | |
UINTN | |
BmGetDevicePathSizeEx ( | |
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, | |
IN UINTN MaxSize | |
) | |
{ | |
UINTN Size; | |
UINTN NodeSize; | |
if (DevicePath == NULL) { | |
return 0; | |
} | |
// | |
// Search for the end of the device path structure | |
// | |
Size = 0; | |
while (!IsDevicePathEnd (DevicePath)) { | |
NodeSize = DevicePathNodeLength (DevicePath); | |
if (NodeSize == 0) { | |
return 0; | |
} | |
Size += NodeSize; | |
if (Size > MaxSize) { | |
return 0; | |
} | |
DevicePath = NextDevicePathNode (DevicePath); | |
} | |
Size += DevicePathNodeLength (DevicePath); | |
if (Size > MaxSize) { | |
return 0; | |
} | |
return Size; | |
} | |
/** | |
Returns the length of a Null-terminated Unicode string. If the length is | |
bigger than MaxStringLen, return length 0 to indicate that this is an | |
invalidate string. | |
This function returns the number of Unicode characters in the Null-terminated | |
Unicode string specified by String. | |
If String is NULL, then ASSERT(). | |
If String is not aligned on a 16-bit boundary, then ASSERT(). | |
@param String A pointer to a Null-terminated Unicode string. | |
@param MaxStringLen Max string len in this string. | |
@retval 0 An invalid string. | |
@retval Others The length of String. | |
**/ | |
UINTN | |
BmStrSizeEx ( | |
IN CONST CHAR16 *String, | |
IN UINTN MaxStringLen | |
) | |
{ | |
UINTN Length; | |
ASSERT (String != NULL && MaxStringLen != 0); | |
ASSERT (((UINTN)String & BIT0) == 0); | |
for (Length = 0; *String != L'\0' && MaxStringLen != Length; String++, Length += 2) { | |
} | |
if ((*String != L'\0') && (MaxStringLen == Length)) { | |
return 0; | |
} | |
return Length + 2; | |
} | |
/** | |
Validate the Boot####, Driver####, SysPrep#### and PlatformRecovery#### | |
variable (VendorGuid/Name) | |
@param Variable The variable data. | |
@param VariableSize The variable size. | |
@retval TRUE The variable data is correct. | |
@retval FALSE The variable data is corrupted. | |
**/ | |
BOOLEAN | |
BmValidateOption ( | |
UINT8 *Variable, | |
UINTN VariableSize | |
) | |
{ | |
UINT16 FilePathSize; | |
EFI_DEVICE_PATH_PROTOCOL *DevicePath; | |
UINTN DescriptionSize; | |
if (VariableSize <= sizeof (UINT16) + sizeof (UINT32)) { | |
return FALSE; | |
} | |
// | |
// Skip the option attribute | |
// | |
Variable += sizeof (UINT32); | |
// | |
// Get the option's device path size | |
// | |
FilePathSize = ReadUnaligned16 ((UINT16 *)Variable); | |
Variable += sizeof (UINT16); | |
// | |
// Get the option's description string size | |
// | |
DescriptionSize = BmStrSizeEx ((CHAR16 *)Variable, VariableSize - sizeof (UINT16) - sizeof (UINT32)); | |
Variable += DescriptionSize; | |
// | |
// Get the option's device path | |
// | |
DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)Variable; | |
// | |
// Validation boot option variable. | |
// | |
if ((FilePathSize == 0) || (DescriptionSize == 0)) { | |
return FALSE; | |
} | |
if (sizeof (UINT32) + sizeof (UINT16) + DescriptionSize + FilePathSize > VariableSize) { | |
return FALSE; | |
} | |
return (BOOLEAN)(BmGetDevicePathSizeEx (DevicePath, FilePathSize) != 0); | |
} | |
/** | |
Check whether the VariableName is a valid load option variable name | |
and return the load option type and option number. | |
@param VariableName The name of the load option variable. | |
@param OptionType Return the load option type. | |
@param OptionNumber Return the load option number. | |
@retval TRUE The variable name is valid; The load option type and | |
load option number is returned. | |
@retval FALSE The variable name is NOT valid. | |
**/ | |
BOOLEAN | |
EFIAPI | |
EfiBootManagerIsValidLoadOptionVariableName ( | |
IN CHAR16 *VariableName, | |
OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType OPTIONAL, | |
OUT UINT16 *OptionNumber OPTIONAL | |
) | |
{ | |
UINTN VariableNameLen; | |
UINTN Index; | |
UINTN Uint; | |
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LocalOptionType; | |
UINT16 LocalOptionNumber; | |
if (VariableName == NULL) { | |
return FALSE; | |
} | |
VariableNameLen = StrLen (VariableName); | |
// | |
// Return FALSE when the variable name length is too small. | |
// | |
if (VariableNameLen <= 4) { | |
return FALSE; | |
} | |
// | |
// Return FALSE when the variable name doesn't start with Driver/SysPrep/Boot/PlatformRecovery. | |
// | |
for (LocalOptionType = 0; LocalOptionType < ARRAY_SIZE (mBmLoadOptionName); LocalOptionType++) { | |
if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[LocalOptionType])) && | |
(StrnCmp (VariableName, mBmLoadOptionName[LocalOptionType], VariableNameLen - 4) == 0) | |
) | |
{ | |
break; | |
} | |
} | |
if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) { | |
return FALSE; | |
} | |
// | |
// Return FALSE when the last four characters are not hex digits. | |
// | |
LocalOptionNumber = 0; | |
for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) { | |
Uint = BmCharToUint (VariableName[Index]); | |
if (Uint == -1) { | |
break; | |
} else { | |
LocalOptionNumber = (UINT16)Uint + LocalOptionNumber * 0x10; | |
} | |
} | |
if (Index != VariableNameLen) { | |
return FALSE; | |
} | |
if (OptionType != NULL) { | |
*OptionType = LocalOptionType; | |
} | |
if (OptionNumber != NULL) { | |
*OptionNumber = LocalOptionNumber; | |
} | |
return TRUE; | |
} | |
/** | |
Build the Boot#### or Driver#### option from the VariableName. | |
@param VariableName Variable name of the load option | |
@param VendorGuid Variable GUID of the load option | |
@param Option Return the load option. | |
@retval EFI_SUCCESS Get the option just been created | |
@retval EFI_NOT_FOUND Failed to get the new option | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerVariableToLoadOptionEx ( | |
IN CHAR16 *VariableName, | |
IN EFI_GUID *VendorGuid, | |
IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option | |
) | |
{ | |
EFI_STATUS Status; | |
UINT32 Attribute; | |
UINT16 FilePathSize; | |
UINT8 *Variable; | |
UINT8 *VariablePtr; | |
UINTN VariableSize; | |
EFI_DEVICE_PATH_PROTOCOL *FilePath; | |
UINT8 *OptionalData; | |
UINT32 OptionalDataSize; | |
CHAR16 *Description; | |
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; | |
UINT16 OptionNumber; | |
if ((VariableName == NULL) || (Option == NULL)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (!EfiBootManagerIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) { | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Read the variable | |
// | |
GetVariable2 (VariableName, VendorGuid, (VOID **)&Variable, &VariableSize); | |
if (Variable == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
// | |
// Validate *#### variable data. | |
// | |
if (!BmValidateOption (Variable, VariableSize)) { | |
FreePool (Variable); | |
return EFI_INVALID_PARAMETER; | |
} | |
// | |
// Get the option attribute | |
// | |
VariablePtr = Variable; | |
Attribute = ReadUnaligned32 ((UINT32 *)VariablePtr); | |
VariablePtr += sizeof (UINT32); | |
// | |
// Get the option's device path size | |
// | |
FilePathSize = ReadUnaligned16 ((UINT16 *)VariablePtr); | |
VariablePtr += sizeof (UINT16); | |
// | |
// Get the option's description string | |
// | |
Description = (CHAR16 *)VariablePtr; | |
// | |
// Get the option's description string size | |
// | |
VariablePtr += StrSize ((CHAR16 *)VariablePtr); | |
// | |
// Get the option's device path | |
// | |
FilePath = (EFI_DEVICE_PATH_PROTOCOL *)VariablePtr; | |
VariablePtr += FilePathSize; | |
OptionalDataSize = (UINT32)(VariableSize - ((UINTN)VariablePtr - (UINTN)Variable)); | |
if (OptionalDataSize == 0) { | |
OptionalData = NULL; | |
} else { | |
OptionalData = VariablePtr; | |
} | |
Status = EfiBootManagerInitializeLoadOption ( | |
Option, | |
OptionNumber, | |
OptionType, | |
Attribute, | |
Description, | |
FilePath, | |
OptionalData, | |
OptionalDataSize | |
); | |
ASSERT_EFI_ERROR (Status); | |
CopyGuid (&Option->VendorGuid, VendorGuid); | |
FreePool (Variable); | |
return Status; | |
} | |
/** | |
Build the Boot#### or Driver#### option from the VariableName. | |
@param VariableName EFI Variable name indicate if it is Boot#### or Driver#### | |
@param Option Return the Boot#### or Driver#### option. | |
@retval EFI_SUCCESS Get the option just been created | |
@retval EFI_NOT_FOUND Failed to get the new option | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerVariableToLoadOption ( | |
IN CHAR16 *VariableName, | |
IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option | |
) | |
{ | |
return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, Option); | |
} | |
typedef struct { | |
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; | |
EFI_GUID *Guid; | |
EFI_BOOT_MANAGER_LOAD_OPTION *Options; | |
UINTN OptionCount; | |
} BM_COLLECT_LOAD_OPTIONS_PARAM; | |
/** | |
Visitor function to collect the Platform Recovery load options or OS Recovery | |
load options from NV storage. | |
@param Name Variable name. | |
@param Guid Variable GUID. | |
@param Context The same context passed to BmForEachVariable. | |
**/ | |
VOID | |
BmCollectLoadOptions ( | |
IN CHAR16 *Name, | |
IN EFI_GUID *Guid, | |
IN VOID *Context | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType; | |
UINT16 OptionNumber; | |
EFI_BOOT_MANAGER_LOAD_OPTION Option; | |
UINTN Index; | |
BM_COLLECT_LOAD_OPTIONS_PARAM *Param; | |
Param = (BM_COLLECT_LOAD_OPTIONS_PARAM *)Context; | |
if (CompareGuid (Guid, Param->Guid) && ( | |
(Param->OptionType == LoadOptionTypePlatformRecovery) && | |
EfiBootManagerIsValidLoadOptionVariableName (Name, &OptionType, &OptionNumber) && | |
(OptionType == LoadOptionTypePlatformRecovery) | |
)) | |
{ | |
Status = EfiBootManagerVariableToLoadOptionEx (Name, Guid, &Option); | |
if (!EFI_ERROR (Status)) { | |
for (Index = 0; Index < Param->OptionCount; Index++) { | |
if (Param->Options[Index].OptionNumber > Option.OptionNumber) { | |
break; | |
} | |
} | |
Param->Options = ReallocatePool ( | |
Param->OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), | |
(Param->OptionCount + 1) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), | |
Param->Options | |
); | |
ASSERT (Param->Options != NULL); | |
CopyMem (&Param->Options[Index + 1], &Param->Options[Index], (Param->OptionCount - Index) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); | |
CopyMem (&Param->Options[Index], &Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); | |
Param->OptionCount++; | |
} | |
} | |
} | |
/** | |
Returns an array of load options based on the EFI variable | |
L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it. | |
#### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. | |
@param LoadOptionCount Returns number of entries in the array. | |
@param LoadOptionType The type of the load option. | |
@retval NULL No load options exist. | |
@retval !NULL Array of load option entries. | |
**/ | |
EFI_BOOT_MANAGER_LOAD_OPTION * | |
EFIAPI | |
EfiBootManagerGetLoadOptions ( | |
OUT UINTN *OptionCount, | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LoadOptionType | |
) | |
{ | |
EFI_STATUS Status; | |
UINT16 *OptionOrder; | |
UINTN OptionOrderSize; | |
UINTN Index; | |
UINTN OptionIndex; | |
EFI_BOOT_MANAGER_LOAD_OPTION *Options; | |
CHAR16 OptionName[BM_OPTION_NAME_LEN]; | |
UINT16 OptionNumber; | |
BM_COLLECT_LOAD_OPTIONS_PARAM Param; | |
*OptionCount = 0; | |
Options = NULL; | |
if ((LoadOptionType == LoadOptionTypeDriver) || (LoadOptionType == LoadOptionTypeSysPrep) || (LoadOptionType == LoadOptionTypeBoot)) { | |
// | |
// Read the BootOrder, or DriverOrder variable. | |
// | |
GetEfiGlobalVariable2 (mBmLoadOptionOrderName[LoadOptionType], (VOID **)&OptionOrder, &OptionOrderSize); | |
if (OptionOrder == NULL) { | |
return NULL; | |
} | |
*OptionCount = OptionOrderSize / sizeof (UINT16); | |
Options = AllocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION)); | |
ASSERT (Options != NULL); | |
OptionIndex = 0; | |
for (Index = 0; Index < *OptionCount; Index++) { | |
OptionNumber = OptionOrder[Index]; | |
UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[LoadOptionType], OptionNumber); | |
Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]); | |
if (EFI_ERROR (Status)) { | |
DEBUG ((DEBUG_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName)); | |
EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType); | |
} else { | |
ASSERT (Options[OptionIndex].OptionNumber == OptionNumber); | |
OptionIndex++; | |
} | |
} | |
if (OptionOrder != NULL) { | |
FreePool (OptionOrder); | |
} | |
if (OptionIndex < *OptionCount) { | |
Options = ReallocatePool (*OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), OptionIndex * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION), Options); | |
ASSERT (Options != NULL); | |
*OptionCount = OptionIndex; | |
} | |
} else if (LoadOptionType == LoadOptionTypePlatformRecovery) { | |
Param.OptionType = LoadOptionTypePlatformRecovery; | |
Param.Options = NULL; | |
Param.OptionCount = 0; | |
Param.Guid = &gEfiGlobalVariableGuid; | |
BmForEachVariable (BmCollectLoadOptions, (VOID *)&Param); | |
*OptionCount = Param.OptionCount; | |
Options = Param.Options; | |
} | |
return Options; | |
} | |
/** | |
Free an EFI_BOOT_MANGER_LOAD_OPTION entry that was allocate by the library. | |
@param LoadOption Pointer to boot option to Free. | |
@return EFI_SUCCESS BootOption was freed | |
@return EFI_NOT_FOUND BootOption == NULL | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerFreeLoadOption ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption | |
) | |
{ | |
if (LoadOption == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
if (LoadOption->Description != NULL) { | |
FreePool (LoadOption->Description); | |
} | |
if (LoadOption->FilePath != NULL) { | |
FreePool (LoadOption->FilePath); | |
} | |
if (LoadOption->OptionalData != NULL) { | |
FreePool (LoadOption->OptionalData); | |
} | |
return EFI_SUCCESS; | |
} | |
/** | |
Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by | |
EfiBootManagerGetLoadOptions(). | |
@param Option Pointer to boot option array to free. | |
@param OptionCount Number of array entries in BootOption | |
@return EFI_SUCCESS BootOption was freed | |
@return EFI_NOT_FOUND BootOption == NULL | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerFreeLoadOptions ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION *Option, | |
IN UINTN OptionCount | |
) | |
{ | |
UINTN Index; | |
if (Option == NULL) { | |
return EFI_NOT_FOUND; | |
} | |
for (Index = 0; Index < OptionCount; Index++) { | |
EfiBootManagerFreeLoadOption (&Option[Index]); | |
} | |
FreePool (Option); | |
return EFI_SUCCESS; | |
} | |
/** | |
Return whether the PE header of the load option is valid or not. | |
@param[in] Type The load option type. | |
It's used to check whether the load option is valid. | |
When it's LoadOptionTypeMax, the routine only guarantees | |
the load option is a valid PE image but doesn't guarantee | |
the PE's subsystem type is valid. | |
@param[in] FileBuffer The PE file buffer of the load option. | |
@param[in] FileSize The size of the load option file. | |
@retval TRUE The PE header of the load option is valid. | |
@retval FALSE The PE header of the load option is not valid. | |
**/ | |
BOOLEAN | |
BmIsLoadOptionPeHeaderValid ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type, | |
IN VOID *FileBuffer, | |
IN UINTN FileSize | |
) | |
{ | |
EFI_IMAGE_DOS_HEADER *DosHeader; | |
EFI_IMAGE_OPTIONAL_HEADER_UNION *PeHeader; | |
EFI_IMAGE_OPTIONAL_HEADER32 *OptionalHeader; | |
UINT16 Subsystem; | |
if ((FileBuffer == NULL) || (FileSize == 0)) { | |
return FALSE; | |
} | |
// | |
// Read dos header | |
// | |
DosHeader = (EFI_IMAGE_DOS_HEADER *)FileBuffer; | |
if ((FileSize >= sizeof (EFI_IMAGE_DOS_HEADER)) && | |
(FileSize > DosHeader->e_lfanew) && (DosHeader->e_magic == EFI_IMAGE_DOS_SIGNATURE) | |
) | |
{ | |
// | |
// Read and check PE signature | |
// | |
PeHeader = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((UINT8 *)FileBuffer + DosHeader->e_lfanew); | |
if ((FileSize >= DosHeader->e_lfanew + sizeof (EFI_IMAGE_OPTIONAL_HEADER_UNION)) && | |
(PeHeader->Pe32.Signature == EFI_IMAGE_NT_SIGNATURE) | |
) | |
{ | |
// | |
// Check PE32 or PE32+ magic, and machine type | |
// | |
OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *)&PeHeader->Pe32.OptionalHeader; | |
if ((OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) || | |
(OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC)) | |
{ | |
// | |
// Check the Subsystem: | |
// Driver#### must be of type BootServiceDriver or RuntimeDriver | |
// SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application | |
// | |
Subsystem = OptionalHeader->Subsystem; | |
if ((Type == LoadOptionTypeMax) || | |
((Type == LoadOptionTypeDriver) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)) || | |
((Type == LoadOptionTypeDriver) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)) || | |
((Type == LoadOptionTypeSysPrep) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) || | |
((Type == LoadOptionTypeBoot) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) || | |
((Type == LoadOptionTypePlatformRecovery) && (Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)) | |
) | |
{ | |
return TRUE; | |
} | |
} | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Return the next matched load option buffer. | |
The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid | |
load option is read. | |
@param Type The load option type. | |
It's used to check whether the load option is valid. | |
When it's LoadOptionTypeMax, the routine only guarantees | |
the load option is a valid PE image but doesn't guarantee | |
the PE's subsystem type is valid. | |
@param FilePath The device path pointing to a load option. | |
It could be a short-form device path. | |
@param FullPath Return the next full device path of the load option after | |
short-form device path expanding. | |
Caller is responsible to free it. | |
NULL to return the first matched full device path. | |
@param FileSize Return the load option size. | |
@return The load option buffer. Caller is responsible to free the memory. | |
**/ | |
VOID * | |
BmGetNextLoadOptionBuffer ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type, | |
IN EFI_DEVICE_PATH_PROTOCOL *FilePath, | |
OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, | |
OUT UINTN *FileSize | |
) | |
{ | |
VOID *FileBuffer; | |
EFI_DEVICE_PATH_PROTOCOL *PreFullPath; | |
EFI_DEVICE_PATH_PROTOCOL *CurFullPath; | |
UINTN LocalFileSize; | |
UINT32 AuthenticationStatus; | |
EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath; | |
LocalFileSize = 0; | |
FileBuffer = NULL; | |
CurFullPath = *FullPath; | |
do { | |
PreFullPath = CurFullPath; | |
CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath); | |
// | |
// Only free the full path created *inside* this routine | |
// | |
if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) { | |
FreePool (PreFullPath); | |
} | |
if (CurFullPath == NULL) { | |
break; | |
} | |
FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus); | |
if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) { | |
// | |
// Free the RAM disk file system if the load option is invalid. | |
// | |
RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath); | |
if (RamDiskDevicePath != NULL) { | |
BmDestroyRamDisk (RamDiskDevicePath); | |
FreePool (RamDiskDevicePath); | |
} | |
// | |
// Free the invalid load option buffer. | |
// | |
FreePool (FileBuffer); | |
FileBuffer = NULL; | |
} | |
} while (FileBuffer == NULL); | |
if (FileBuffer == NULL) { | |
CurFullPath = NULL; | |
LocalFileSize = 0; | |
} | |
DEBUG ((DEBUG_INFO, "[Bds] Expand ")); | |
BmPrintDp (FilePath); | |
DEBUG ((DEBUG_INFO, " -> ")); | |
BmPrintDp (CurFullPath); | |
DEBUG ((DEBUG_INFO, "\n")); | |
*FullPath = CurFullPath; | |
*FileSize = LocalFileSize; | |
return FileBuffer; | |
} | |
/** | |
Process (load and execute) the load option. | |
@param LoadOption Pointer to the load option. | |
@retval EFI_INVALID_PARAMETER The load option type is invalid, | |
or the load option file path doesn't point to a valid file. | |
@retval EFI_UNSUPPORTED The load option type is of LoadOptionTypeBoot. | |
@retval EFI_SUCCESS The load option is inactive, or successfully loaded and executed. | |
**/ | |
EFI_STATUS | |
EFIAPI | |
EfiBootManagerProcessLoadOption ( | |
IN EFI_BOOT_MANAGER_LOAD_OPTION *LoadOption | |
) | |
{ | |
EFI_STATUS Status; | |
EFI_DEVICE_PATH_PROTOCOL *PreFullPath; | |
EFI_DEVICE_PATH_PROTOCOL *CurFullPath; | |
EFI_HANDLE ImageHandle; | |
EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; | |
VOID *FileBuffer; | |
UINTN FileSize; | |
if ((UINT32)LoadOption->OptionType >= LoadOptionTypeMax) { | |
return EFI_INVALID_PARAMETER; | |
} | |
if (LoadOption->OptionType == LoadOptionTypeBoot) { | |
return EFI_UNSUPPORTED; | |
} | |
// | |
// If a load option is not marked as LOAD_OPTION_ACTIVE, | |
// the boot manager will not automatically load the option. | |
// | |
if ((LoadOption->Attributes & LOAD_OPTION_ACTIVE) == 0) { | |
return EFI_SUCCESS; | |
} | |
// | |
// Load and start the load option. | |
// | |
DEBUG (( | |
DEBUG_INFO | DEBUG_LOAD, | |
"Process %s%04x (%s) ...\n", | |
mBmLoadOptionName[LoadOption->OptionType], | |
LoadOption->OptionNumber, | |
LoadOption->Description | |
)); | |
ImageHandle = NULL; | |
CurFullPath = NULL; | |
EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL); | |
// | |
// while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status. | |
// | |
while (TRUE) { | |
Status = EFI_INVALID_PARAMETER; | |
PreFullPath = CurFullPath; | |
FileBuffer = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize); | |
if (PreFullPath != NULL) { | |
FreePool (PreFullPath); | |
} | |
if (FileBuffer == NULL) { | |
break; | |
} | |
Status = gBS->LoadImage ( | |
FALSE, | |
gImageHandle, | |
CurFullPath, | |
FileBuffer, | |
FileSize, | |
&ImageHandle | |
); | |
FreePool (FileBuffer); | |
if (EFI_ERROR (Status)) { | |
// | |
// With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created | |
// with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now. | |
// If the caller doesn't have the option to defer the execution of an image, we should | |
// unload image for the EFI_SECURITY_VIOLATION to avoid resource leak. | |
// | |
if (Status == EFI_SECURITY_VIOLATION) { | |
gBS->UnloadImage (ImageHandle); | |
} | |
} else { | |
Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo); | |
ASSERT_EFI_ERROR (Status); | |
ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize; | |
ImageInfo->LoadOptions = LoadOption->OptionalData; | |
// | |
// Before calling the image, enable the Watchdog Timer for the 5-minute period | |
// | |
gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL); | |
LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData); | |
DEBUG (( | |
DEBUG_INFO | DEBUG_LOAD, | |
"%s%04x Return Status = %r\n", | |
mBmLoadOptionName[LoadOption->OptionType], | |
LoadOption->OptionNumber, | |
LoadOption->Status | |
)); | |
// | |
// Clear the Watchdog Timer after the image returns | |
// | |
gBS->SetWatchdogTimer (0, 0, 0, NULL); | |
if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) { | |
break; | |
} | |
} | |
} | |
if (CurFullPath != NULL) { | |
FreePool (CurFullPath); | |
} | |
return Status; | |
} |