X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=MdeModulePkg%2FLibrary%2FUefiBootManagerLib%2FBmLoadOption.c;h=de9dde2d3d27e774bb340eb8469e1c1e97f3cbff;hb=e3917e22e769898bb0d08d0112e768437f1ff9fb;hp=6fcd23b6aba337adb26922a0b3f606ddf88eef08;hpb=418e8cd9240af2249be0cb1d1e38b81b603218e4;p=mirror_edk2.git diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c index 6fcd23b6ab..de9dde2d3d 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c @@ -1,15 +1,9 @@ /** @file Load option library functions which relate with creating and processing load options. -Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.
-(C) Copyright 2015 Hewlett Packard Enterprise Development LP
-This program and the accompanying materials -are licensed and made available under the terms and conditions of the BSD License -which accompanies this distribution. The full text of the license may be found at -http://opensource.org/licenses/bsd-license.php - -THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, -WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.
+(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP
+SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -19,14 +13,16 @@ GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mBmLoadOptionName[] = { L"Driver", L"SysPrep", - L"Boot" + 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 + EFI_BOOT_ORDER_VARIABLE_NAME, + NULL // PlatformRecovery#### doesn't have associated *Order variable }; /** @@ -88,7 +84,7 @@ BmGetFreeOptionNumber ( OUT UINT16 *FreeOptionNumber ) { - + UINTN OptionNumber; UINTN Index; UINT16 *OptionOrder; @@ -96,7 +92,7 @@ BmGetFreeOptionNumber ( UINT16 *BootNext; ASSERT (FreeOptionNumber != NULL); - ASSERT (LoadOptionType == LoadOptionTypeDriver || + ASSERT (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeBoot || LoadOptionType == LoadOptionTypeSysPrep); @@ -108,9 +104,9 @@ BmGetFreeOptionNumber ( GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL); } - for (OptionNumber = 0; + for (OptionNumber = 0; OptionNumber < OptionOrderSize / sizeof (UINT16) - + ((BootNext != NULL) ? 1 : 0); + + ((BootNext != NULL) ? 1 : 0); OptionNumber++ ) { // @@ -123,10 +119,10 @@ BmGetFreeOptionNumber ( } // - // We didn't find it in the ****Order array and it doesn't equal to BootNext + // 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)) && + if ((Index == OptionOrderSize / sizeof (UINT16)) && ((BootNext == NULL) || (OptionNumber != *BootNext)) ) { break; @@ -154,8 +150,9 @@ BmGetFreeOptionNumber ( } /** - Create the Boot####, Driver####, SysPrep####, variable from the load option. - + 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. @@ -167,12 +164,14 @@ 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) || @@ -198,12 +197,12 @@ EfiBootManagerLoadOptionToVariable ( 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 +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) @@ -233,14 +232,28 @@ structure. 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; + } - return gRT->SetVariable ( - OptionName, - &gEfiGlobalVariableGuid, - VariableAttributes, - VariableSize, - Variable - ); + Status = gRT->SetVariable ( + OptionName, + &gEfiGlobalVariableGuid, + VariableAttributes, + VariableSize, + Variable + ); + FreePool (Variable); + + return Status; } /** @@ -313,7 +326,11 @@ BmAddOptionNumberToOrderVariable ( 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. + @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. @@ -322,14 +339,14 @@ BmAddOptionNumberToOrderVariable ( 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. - @retval EFI_STATUS Return the status of gRT->SetVariable (). + @return Status codes of gRT->SetVariable (). **/ EFI_STATUS EFIAPI EfiBootManagerAddLoadOptionVariable ( - IN EFI_BOOT_MANAGER_LOAD_OPTION *Option, - IN UINTN Position + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option, + IN UINTN Position ) { EFI_STATUS Status; @@ -339,7 +356,7 @@ EfiBootManagerAddLoadOptionVariable ( return EFI_INVALID_PARAMETER; } - if (Option->OptionType != LoadOptionTypeDriver && + if (Option->OptionType != LoadOptionTypeDriver && Option->OptionType != LoadOptionTypeSysPrep && Option->OptionType != LoadOptionTypeBoot ) { @@ -379,7 +396,7 @@ EfiBootManagerAddLoadOptionVariable ( } /** - Sort the load option. The DriverOrder or BootOrder will be re-created to + Sort the load option. The DriverOrder or BootOrder will be re-created to reflect the new order. @param OptionType Load option type @@ -459,7 +476,7 @@ EfiBootManagerInitializeLoadOption ( IN UINT32 Attributes, IN CHAR16 *Description, IN EFI_DEVICE_PATH_PROTOCOL *FilePath, - IN UINT8 *OptionalData, OPTIONAL + IN UINT8 *OptionalData OPTIONAL, IN UINT32 OptionalDataSize ) { @@ -494,7 +511,7 @@ EfiBootManagerInitializeLoadOption ( /** Return the index of the load option in the load option array. - The function consider two load options are equal when the + 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. @@ -548,18 +565,17 @@ EfiBootManagerDeleteLoadOptionVariable ( { UINT16 *OptionOrder; UINTN OptionOrderSize; - EFI_STATUS Status; UINTN Index; + CHAR16 OptionName[BM_OPTION_NAME_LEN]; if (((UINT32) OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) { return EFI_INVALID_PARAMETER; } - Status = EFI_NOT_FOUND; - if (OptionType == LoadOptionTypeDriver || OptionType == LoadOptionTypeSysPrep || OptionType == LoadOptionTypeBoot) { // - // If the associated *Order exists, just remove the reference in *Order. + // 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)); @@ -568,13 +584,13 @@ EfiBootManagerDeleteLoadOptionVariable ( if (OptionOrder[Index] == OptionNumber) { OptionOrderSize -= sizeof (UINT16); CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16)); - Status = gRT->SetVariable ( - mBmLoadOptionOrderName[OptionType], - &gEfiGlobalVariableGuid, - EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, - OptionOrderSize, - OptionOrder - ); + gRT->SetVariable ( + mBmLoadOptionOrderName[OptionType], + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + OptionOrderSize, + OptionOrder + ); break; } } @@ -583,21 +599,31 @@ EfiBootManagerDeleteLoadOptionVariable ( } } - return Status; + // + // 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 + 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, + @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. @@ -639,12 +665,12 @@ BmGetDevicePathSizeEx ( } /** - 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 + 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. + Unicode string specified by String. If String is NULL, then ASSERT(). If String is not aligned on a 16-bit boundary, then ASSERT(). @@ -677,7 +703,8 @@ BmStrSizeEx ( } /** - Validate the Boot####, Driver####, SysPrep#### variable (VendorGuid/Name) + Validate the Boot####, Driver####, SysPrep#### and PlatformRecovery#### + variable (VendorGuid/Name) @param Variable The variable data. @param VariableSize The variable size. @@ -686,7 +713,7 @@ BmStrSizeEx ( @retval FALSE The variable data is corrupted. **/ -BOOLEAN +BOOLEAN BmValidateOption ( UINT8 *Variable, UINTN VariableSize @@ -749,46 +776,71 @@ BmValidateOption ( @retval FALSE The variable name is NOT valid. **/ BOOLEAN -BmIsValidLoadOptionVariableName ( +EFIAPI +EfiBootManagerIsValidLoadOptionVariableName ( IN CHAR16 *VariableName, - OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType, - OUT UINT16 *OptionNumber + 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; } - for (Index = 0; Index < sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0]); Index++) { - if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[Index])) && - (StrnCmp (VariableName, mBmLoadOptionName[Index], VariableNameLen - 4) == 0) + // + // 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 (Index == sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0])) { + if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) { return FALSE; } - *OptionType = (EFI_BOOT_MANAGER_LOAD_OPTION_TYPE) Index; - *OptionNumber = 0; + // + // 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 { - *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10; + LocalOptionNumber = (UINT16) Uint + LocalOptionNumber * 0x10; } } + if (Index != VariableNameLen) { + return FALSE; + } + + if (OptionType != NULL) { + *OptionType = LocalOptionType; + } + + if (OptionNumber != NULL) { + *OptionNumber = LocalOptionNumber; + } - return (BOOLEAN) (Index == VariableNameLen); + return TRUE; } /** @@ -807,7 +859,7 @@ EFIAPI EfiBootManagerVariableToLoadOptionEx ( IN CHAR16 *VariableName, IN EFI_GUID *VendorGuid, - IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option + IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option ) { EFI_STATUS Status; @@ -827,7 +879,7 @@ EfiBootManagerVariableToLoadOptionEx ( return EFI_INVALID_PARAMETER; } - if (!BmIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) { + if (!EfiBootManagerIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) { return EFI_INVALID_PARAMETER; } @@ -876,7 +928,7 @@ EfiBootManagerVariableToLoadOptionEx ( FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr; VariablePtr += FilePathSize; - OptionalDataSize = (UINT32) (VariableSize - (UINTN) (VariablePtr - Variable)); + OptionalDataSize = (UINT32) (VariableSize - ((UINTN) VariablePtr - (UINTN) Variable)); if (OptionalDataSize == 0) { OptionalData = NULL; } else { @@ -920,10 +972,66 @@ EfiBootManagerVariableToLoadOption ( 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. + #### 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. @@ -939,16 +1047,18 @@ EfiBootManagerGetLoadOptions ( 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; + 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) { // @@ -971,7 +1081,7 @@ EfiBootManagerGetLoadOptions ( 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)); + 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); @@ -989,8 +1099,16 @@ EfiBootManagerGetLoadOptions ( *OptionCount = OptionIndex; } - } else { - return NULL; + } 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; @@ -1001,8 +1119,8 @@ EfiBootManagerGetLoadOptions ( @param LoadOption Pointer to boot option to Free. - @return EFI_SUCCESS BootOption was freed - @return EFI_NOT_FOUND BootOption == NULL + @return EFI_SUCCESS BootOption was freed + @return EFI_NOT_FOUND BootOption == NULL **/ EFI_STATUS @@ -1029,14 +1147,14 @@ EfiBootManagerFreeLoadOption ( } /** - Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by + 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 + @return EFI_SUCCESS BootOption was freed + @return EFI_NOT_FOUND BootOption == NULL **/ EFI_STATUS @@ -1065,6 +1183,10 @@ EfiBootManagerFreeLoadOptions ( 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. @@ -1105,20 +1227,20 @@ BmIsLoadOptionPeHeaderValid ( // 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) && - EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeHeader->Pe32.FileHeader.Machine) - ) { + 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 == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) || + 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 == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) || + (Type == LoadOptionTypePlatformRecovery && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ) { return TRUE; } @@ -1129,12 +1251,97 @@ BmIsLoadOptionPeHeaderValid ( 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, + @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. @@ -1146,7 +1353,8 @@ EfiBootManagerProcessLoadOption ( ) { EFI_STATUS Status; - EFI_DEVICE_PATH_PROTOCOL *FilePath; + EFI_DEVICE_PATH_PROTOCOL *PreFullPath; + EFI_DEVICE_PATH_PROTOCOL *CurFullPath; EFI_HANDLE ImageHandle; EFI_LOADED_IMAGE_PROTOCOL *ImageInfo; VOID *FileBuffer; @@ -1168,64 +1376,81 @@ EfiBootManagerProcessLoadOption ( return EFI_SUCCESS; } - Status = EFI_INVALID_PARAMETER; - // // Load and start the load option. // DEBUG (( - DEBUG_INFO | DEBUG_LOAD, "Process Load Option (%s%04x) ...\n", - mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber + DEBUG_INFO | DEBUG_LOAD, "Process %s%04x (%s) ...\n", + mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, + LoadOption->Description )); ImageHandle = NULL; - FileBuffer = BmGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize); - DEBUG_CODE ( - if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) { - DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: ")); - BmPrintDp (LoadOption->FilePath); - DEBUG ((EFI_D_INFO, " -> ")); - BmPrintDp (FilePath); - DEBUG ((EFI_D_INFO, "\n")); + 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; } - ); - if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) { Status = gBS->LoadImage ( FALSE, gImageHandle, - FilePath, + CurFullPath, FileBuffer, FileSize, &ImageHandle ); - } - if (FilePath != NULL) { - FreePool (FilePath); - } - if (FileBuffer != NULL) { FreePool (FileBuffer); - } - if (!EFI_ERROR (Status)) { - Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo); - ASSERT_EFI_ERROR (Status); + 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); + 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, "Load Option (%s%04x) Return Status = %r\n", - mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status + 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); + // + // 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;