summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c')
-rw-r--r--src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c1469
1 files changed, 1469 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
new file mode 100644
index 00000000..dd17ee43
--- /dev/null
+++ b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
@@ -0,0 +1,1469 @@
+/** @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"
+
+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_LOCK_PROTOCOL *VariableLock;
+ 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 (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);
+ if (!EFI_ERROR (Status)) {
+ Status = VariableLock->RequestToLock (VariableLock, OptionName, &gEfiGlobalVariableGuid);
+ 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 ((EFI_D_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;
+ }
+
+#ifdef VBOX
+ /*
+ * Check for Fat/Universal EFI binaries provided by older macOS versions
+ * (Mountain Lion and older).
+ *
+ * @todo More checks here? (VBoxPeCoffLib will do more thorough checks
+ * when the image is actually loaded).
+ */
+ if (*(UINT32 *)FileBuffer == 0x0ef1fab9)
+ return TRUE;
+#endif
+
+ //
+ // 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;
+}