]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
MdeModulePkg: Change OPTIONAL keyword usage style
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmLoadOption.c
index 6fcd23b6aba337adb26922a0b3f606ddf88eef08..de9dde2d3d27e774bb340eb8469e1c1e97f3cbff 100644 (file)
@@ -1,15 +1,9 @@
 /** @file\r
   Load option library functions which relate with creating and processing load options.\r
 \r
-Copyright (c) 2011 - 2015, Intel Corporation. All rights reserved.<BR>\r
-(C) Copyright 2015 Hewlett Packard Enterprise Development LP<BR>\r
-This program and the accompanying materials\r
-are licensed and made available under the terms and conditions of the BSD License\r
-which accompanies this distribution.  The full text of the license may be found at\r
-http://opensource.org/licenses/bsd-license.php\r
-\r
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+Copyright (c) 2011 - 2019, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2015-2018 Hewlett Packard Enterprise Development LP<BR>\r
+SPDX-License-Identifier: BSD-2-Clause-Patent\r
 \r
 **/\r
 \r
@@ -19,14 +13,16 @@ GLOBAL_REMOVE_IF_UNREFERENCED
   CHAR16 *mBmLoadOptionName[] = {\r
     L"Driver",\r
     L"SysPrep",\r
-    L"Boot"\r
+    L"Boot",\r
+    L"PlatformRecovery"\r
   };\r
 \r
 GLOBAL_REMOVE_IF_UNREFERENCED\r
   CHAR16 *mBmLoadOptionOrderName[] = {\r
     EFI_DRIVER_ORDER_VARIABLE_NAME,\r
     EFI_SYS_PREP_ORDER_VARIABLE_NAME,\r
-    EFI_BOOT_ORDER_VARIABLE_NAME\r
+    EFI_BOOT_ORDER_VARIABLE_NAME,\r
+    NULL  // PlatformRecovery#### doesn't have associated *Order variable\r
   };\r
 \r
 /**\r
@@ -88,7 +84,7 @@ BmGetFreeOptionNumber (
   OUT UINT16                            *FreeOptionNumber\r
   )\r
 {\r
-  \r
+\r
   UINTN         OptionNumber;\r
   UINTN         Index;\r
   UINT16        *OptionOrder;\r
@@ -96,7 +92,7 @@ BmGetFreeOptionNumber (
   UINT16        *BootNext;\r
 \r
   ASSERT (FreeOptionNumber != NULL);\r
-  ASSERT (LoadOptionType == LoadOptionTypeDriver || \r
+  ASSERT (LoadOptionType == LoadOptionTypeDriver ||\r
           LoadOptionType == LoadOptionTypeBoot ||\r
           LoadOptionType == LoadOptionTypeSysPrep);\r
 \r
@@ -108,9 +104,9 @@ BmGetFreeOptionNumber (
     GetEfiGlobalVariable2 (L"BootNext", (VOID**) &BootNext, NULL);\r
   }\r
 \r
-  for (OptionNumber = 0; \r
+  for (OptionNumber = 0;\r
        OptionNumber < OptionOrderSize / sizeof (UINT16)\r
-                    + ((BootNext != NULL) ? 1 : 0); \r
+                    + ((BootNext != NULL) ? 1 : 0);\r
        OptionNumber++\r
        ) {\r
     //\r
@@ -123,10 +119,10 @@ BmGetFreeOptionNumber (
     }\r
 \r
     //\r
-    // We didn't find it in the ****Order array and it doesn't equal to BootNext \r
+    // We didn't find it in the ****Order array and it doesn't equal to BootNext\r
     // Otherwise, OptionNumber equals to OptionOrderSize / sizeof (UINT16) + 1\r
     //\r
-    if ((Index == OptionOrderSize / sizeof (UINT16)) && \r
+    if ((Index == OptionOrderSize / sizeof (UINT16)) &&\r
         ((BootNext == NULL) || (OptionNumber != *BootNext))\r
         ) {\r
       break;\r
@@ -154,8 +150,9 @@ BmGetFreeOptionNumber (
 }\r
 \r
 /**\r
-  Create the Boot####, Driver####, SysPrep####, variable from the load option.\r
-  \r
+  Create the Boot####, Driver####, SysPrep####, PlatformRecovery#### variable\r
+  from the load option.\r
+\r
   @param  LoadOption      Pointer to the load option.\r
 \r
   @retval EFI_SUCCESS     The variable was created.\r
@@ -167,12 +164,14 @@ EfiBootManagerLoadOptionToVariable (
   IN CONST EFI_BOOT_MANAGER_LOAD_OPTION     *Option\r
   )\r
 {\r
+  EFI_STATUS                       Status;\r
   UINTN                            VariableSize;\r
   UINT8                            *Variable;\r
   UINT8                            *Ptr;\r
   CHAR16                           OptionName[BM_OPTION_NAME_LEN];\r
   CHAR16                           *Description;\r
   CHAR16                           NullChar;\r
+  EDKII_VARIABLE_LOCK_PROTOCOL     *VariableLock;\r
   UINT32                           VariableAttributes;\r
 \r
   if ((Option->OptionNumber == LoadOptionNumberUnassigned) ||\r
@@ -198,12 +197,12 @@ EfiBootManagerLoadOptionToVariable (
   EFI_DEVICE_PATH_PROTOCOL    FilePathList[];\r
   UINT8                       OptionalData[];\r
 TODO: FilePathList[] IS:\r
-A packed array of UEFI device paths.  The first element of the \r
-array is a device path that describes the device and location of the \r
-Image for this load option.  The FilePathList[0] is specific \r
-to the device type.  Other device paths may optionally exist in the \r
-FilePathList, but their usage is OSV specific. Each element \r
-in the array is variable length, and ends at the device path end \r
+A packed array of UEFI device paths.  The first element of the\r
+array is a device path that describes the device and location of the\r
+Image for this load option.  The FilePathList[0] is specific\r
+to the device type.  Other device paths may optionally exist in the\r
+FilePathList, but their usage is OSV specific. Each element\r
+in the array is variable length, and ends at the device path end\r
 structure.\r
   */\r
   VariableSize = sizeof (Option->Attributes)\r
@@ -233,14 +232,28 @@ structure.
   UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[Option->OptionType], Option->OptionNumber);\r
 \r
   VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE;\r
+  if (Option->OptionType == LoadOptionTypePlatformRecovery) {\r
+    //\r
+    // Lock the PlatformRecovery####\r
+    //\r
+    Status = gBS->LocateProtocol (&gEdkiiVariableLockProtocolGuid, NULL, (VOID **) &VariableLock);\r
+    if (!EFI_ERROR (Status)) {\r
+      Status = VariableLock->RequestToLock (VariableLock, OptionName, &gEfiGlobalVariableGuid);\r
+      ASSERT_EFI_ERROR (Status);\r
+    }\r
+    VariableAttributes = EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS;\r
+  }\r
 \r
-  return gRT->SetVariable (\r
-                OptionName,\r
-                &gEfiGlobalVariableGuid,\r
-                VariableAttributes,\r
-                VariableSize,\r
-                Variable\r
-                );\r
+  Status = gRT->SetVariable (\r
+                  OptionName,\r
+                  &gEfiGlobalVariableGuid,\r
+                  VariableAttributes,\r
+                  VariableSize,\r
+                  Variable\r
+                  );\r
+  FreePool (Variable);\r
+\r
+  return Status;\r
 }\r
 \r
 /**\r
@@ -313,7 +326,11 @@ BmAddOptionNumberToOrderVariable (
   This function will register the new Boot####, Driver#### or SysPrep#### option.\r
   After the *#### is updated, the *Order will also be updated.\r
 \r
-  @param  Option            Pointer to load option to add.\r
+  @param  Option            Pointer to load option to add. If on input\r
+                            Option->OptionNumber is LoadOptionNumberUnassigned,\r
+                            then on output Option->OptionNumber is updated to\r
+                            the number of the new Boot####,\r
+                            Driver#### or SysPrep#### option.\r
   @param  Position          Position of the new load option to put in the ****Order variable.\r
 \r
   @retval EFI_SUCCESS           The *#### have been successfully registered.\r
@@ -322,14 +339,14 @@ BmAddOptionNumberToOrderVariable (
                                 Note: this API only adds new load option, no replacement support.\r
   @retval EFI_OUT_OF_RESOURCES  There is no free option number that can be used when the\r
                                 option number specified in the Option is LoadOptionNumberUnassigned.\r
-  @retval EFI_STATUS            Return the status of gRT->SetVariable ().\r
+  @return                       Status codes of gRT->SetVariable ().\r
 \r
 **/\r
 EFI_STATUS\r
 EFIAPI\r
 EfiBootManagerAddLoadOptionVariable (\r
-  IN EFI_BOOT_MANAGER_LOAD_OPTION *Option,\r
-  IN UINTN                        Position\r
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option,\r
+  IN     UINTN                        Position\r
   )\r
 {\r
   EFI_STATUS                      Status;\r
@@ -339,7 +356,7 @@ EfiBootManagerAddLoadOptionVariable (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (Option->OptionType != LoadOptionTypeDriver && \r
+  if (Option->OptionType != LoadOptionTypeDriver &&\r
       Option->OptionType != LoadOptionTypeSysPrep &&\r
       Option->OptionType != LoadOptionTypeBoot\r
       ) {\r
@@ -379,7 +396,7 @@ EfiBootManagerAddLoadOptionVariable (
 }\r
 \r
 /**\r
-  Sort the load option. The DriverOrder or BootOrder will be re-created to \r
+  Sort the load option. The DriverOrder or BootOrder will be re-created to\r
   reflect the new order.\r
 \r
   @param OptionType             Load option type\r
@@ -459,7 +476,7 @@ EfiBootManagerInitializeLoadOption (
   IN  UINT32                            Attributes,\r
   IN  CHAR16                            *Description,\r
   IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,\r
-  IN  UINT8                             *OptionalData,   OPTIONAL\r
+  IN  UINT8                             *OptionalData    OPTIONAL,\r
   IN  UINT32                            OptionalDataSize\r
   )\r
 {\r
@@ -494,7 +511,7 @@ EfiBootManagerInitializeLoadOption (
 /**\r
   Return the index of the load option in the load option array.\r
 \r
-  The function consider two load options are equal when the \r
+  The function consider two load options are equal when the\r
   OptionType, Attributes, Description, FilePath and OptionalData are equal.\r
 \r
   @param Key    Pointer to the load option to be found.\r
@@ -548,18 +565,17 @@ EfiBootManagerDeleteLoadOptionVariable (
 {\r
   UINT16                            *OptionOrder;\r
   UINTN                             OptionOrderSize;\r
-  EFI_STATUS                        Status;\r
   UINTN                             Index;\r
+  CHAR16                            OptionName[BM_OPTION_NAME_LEN];\r
 \r
   if (((UINT32) OptionType >= LoadOptionTypeMax) || (OptionNumber >= LoadOptionNumberMax)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  Status = EFI_NOT_FOUND;\r
-\r
   if (OptionType == LoadOptionTypeDriver || OptionType == LoadOptionTypeSysPrep || OptionType == LoadOptionTypeBoot) {\r
     //\r
-    // If the associated *Order exists, just remove the reference in *Order.\r
+    // If the associated *Order exists, firstly remove the reference in *Order for\r
+    //  Driver####, SysPrep#### and Boot####.\r
     //\r
     GetEfiGlobalVariable2 (mBmLoadOptionOrderName[OptionType], (VOID **) &OptionOrder, &OptionOrderSize);\r
     ASSERT ((OptionOrder != NULL && OptionOrderSize != 0) || (OptionOrder == NULL && OptionOrderSize == 0));\r
@@ -568,13 +584,13 @@ EfiBootManagerDeleteLoadOptionVariable (
       if (OptionOrder[Index] == OptionNumber) {\r
         OptionOrderSize -= sizeof (UINT16);\r
         CopyMem (&OptionOrder[Index], &OptionOrder[Index + 1], OptionOrderSize - Index * sizeof (UINT16));\r
-        Status = gRT->SetVariable (\r
-          mBmLoadOptionOrderName[OptionType],\r
-          &gEfiGlobalVariableGuid,\r
-          EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
-          OptionOrderSize,\r
-          OptionOrder\r
-          );\r
+        gRT->SetVariable (\r
+               mBmLoadOptionOrderName[OptionType],\r
+               &gEfiGlobalVariableGuid,\r
+               EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+               OptionOrderSize,\r
+               OptionOrder\r
+               );\r
         break;\r
       }\r
     }\r
@@ -583,21 +599,31 @@ EfiBootManagerDeleteLoadOptionVariable (
     }\r
   }\r
 \r
-  return Status;\r
+  //\r
+  // Remove the Driver####, SysPrep####, Boot#### or PlatformRecovery#### itself.\r
+  //\r
+  UnicodeSPrint (OptionName, sizeof (OptionName), L"%s%04x", mBmLoadOptionName[OptionType], OptionNumber);\r
+  return gRT->SetVariable (\r
+                OptionName,\r
+                &gEfiGlobalVariableGuid,\r
+                0,\r
+                0,\r
+                NULL\r
+                );\r
 }\r
 \r
 /**\r
   Returns the size of a device path in bytes.\r
 \r
-  This function returns the size, in bytes, of the device path data structure \r
-  specified by DevicePath including the end of device path node. If DevicePath \r
+  This function returns the size, in bytes, of the device path data structure\r
+  specified by DevicePath including the end of device path node. If DevicePath\r
   is NULL, then 0 is returned. If the length of the device path is bigger than\r
   MaxSize, also return 0 to indicate this is an invalidate device path.\r
 \r
   @param  DevicePath         A pointer to a device path data structure.\r
-  @param  MaxSize            Max valid device path size. If big than this size, \r
+  @param  MaxSize            Max valid device path size. If big than this size,\r
                              return error.\r
-  \r
+\r
   @retval 0                  An invalid device path.\r
   @retval Others             The size of a device path in bytes.\r
 \r
@@ -639,12 +665,12 @@ BmGetDevicePathSizeEx (
 }\r
 \r
 /**\r
-  Returns the length of a Null-terminated Unicode string. If the length is \r
-  bigger than MaxStringLen, return length 0 to indicate that this is an \r
+  Returns the length of a Null-terminated Unicode string. If the length is\r
+  bigger than MaxStringLen, return length 0 to indicate that this is an\r
   invalidate string.\r
 \r
   This function returns the number of Unicode characters in the Null-terminated\r
-  Unicode string specified by String. \r
+  Unicode string specified by String.\r
 \r
   If String is NULL, then ASSERT().\r
   If String is not aligned on a 16-bit boundary, then ASSERT().\r
@@ -677,7 +703,8 @@ BmStrSizeEx (
 }\r
 \r
 /**\r
-  Validate the Boot####, Driver####, SysPrep#### variable (VendorGuid/Name)\r
+  Validate the Boot####, Driver####, SysPrep#### and PlatformRecovery####\r
+  variable (VendorGuid/Name)\r
 \r
   @param  Variable              The variable data.\r
   @param  VariableSize          The variable size.\r
@@ -686,7 +713,7 @@ BmStrSizeEx (
   @retval FALSE                 The variable data is corrupted.\r
 \r
 **/\r
-BOOLEAN \r
+BOOLEAN\r
 BmValidateOption (\r
   UINT8                     *Variable,\r
   UINTN                     VariableSize\r
@@ -749,46 +776,71 @@ BmValidateOption (
   @retval FALSE The variable name is NOT valid.\r
 **/\r
 BOOLEAN\r
-BmIsValidLoadOptionVariableName (\r
+EFIAPI\r
+EfiBootManagerIsValidLoadOptionVariableName (\r
   IN CHAR16                             *VariableName,\r
-  OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType,\r
-  OUT UINT16                            *OptionNumber\r
+  OUT EFI_BOOT_MANAGER_LOAD_OPTION_TYPE *OptionType   OPTIONAL,\r
+  OUT UINT16                            *OptionNumber OPTIONAL\r
   )\r
 {\r
   UINTN                             VariableNameLen;\r
   UINTN                             Index;\r
   UINTN                             Uint;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE LocalOptionType;\r
+  UINT16                            LocalOptionNumber;\r
+\r
+  if (VariableName == NULL) {\r
+    return FALSE;\r
+  }\r
 \r
   VariableNameLen = StrLen (VariableName);\r
 \r
+  //\r
+  // Return FALSE when the variable name length is too small.\r
+  //\r
   if (VariableNameLen <= 4) {\r
     return FALSE;\r
   }\r
 \r
-  for (Index = 0; Index < sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0]); Index++) {\r
-    if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[Index])) &&\r
-        (StrnCmp (VariableName, mBmLoadOptionName[Index], VariableNameLen - 4) == 0)\r
+  //\r
+  // Return FALSE when the variable name doesn't start with Driver/SysPrep/Boot/PlatformRecovery.\r
+  //\r
+  for (LocalOptionType = 0; LocalOptionType < ARRAY_SIZE (mBmLoadOptionName); LocalOptionType++) {\r
+    if ((VariableNameLen - 4 == StrLen (mBmLoadOptionName[LocalOptionType])) &&\r
+        (StrnCmp (VariableName, mBmLoadOptionName[LocalOptionType], VariableNameLen - 4) == 0)\r
         ) {\r
       break;\r
     }\r
   }\r
-\r
-  if (Index == sizeof (mBmLoadOptionName) / sizeof (mBmLoadOptionName[0])) {\r
+  if (LocalOptionType == ARRAY_SIZE (mBmLoadOptionName)) {\r
     return FALSE;\r
   }\r
 \r
-  *OptionType = (EFI_BOOT_MANAGER_LOAD_OPTION_TYPE) Index;\r
-  *OptionNumber = 0;\r
+  //\r
+  // Return FALSE when the last four characters are not hex digits.\r
+  //\r
+  LocalOptionNumber = 0;\r
   for (Index = VariableNameLen - 4; Index < VariableNameLen; Index++) {\r
     Uint = BmCharToUint (VariableName[Index]);\r
     if (Uint == -1) {\r
       break;\r
     } else {\r
-      *OptionNumber = (UINT16) Uint + *OptionNumber * 0x10;\r
+      LocalOptionNumber = (UINT16) Uint + LocalOptionNumber * 0x10;\r
     }\r
   }\r
+  if (Index != VariableNameLen) {\r
+    return FALSE;\r
+  }\r
+\r
+  if (OptionType != NULL) {\r
+    *OptionType = LocalOptionType;\r
+  }\r
+\r
+  if (OptionNumber != NULL) {\r
+    *OptionNumber = LocalOptionNumber;\r
+  }\r
 \r
-  return (BOOLEAN) (Index == VariableNameLen);\r
+  return TRUE;\r
 }\r
 \r
 /**\r
@@ -807,7 +859,7 @@ EFIAPI
 EfiBootManagerVariableToLoadOptionEx (\r
   IN CHAR16                           *VariableName,\r
   IN EFI_GUID                         *VendorGuid,\r
-  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option  \r
+  IN OUT EFI_BOOT_MANAGER_LOAD_OPTION *Option\r
   )\r
 {\r
   EFI_STATUS                         Status;\r
@@ -827,7 +879,7 @@ EfiBootManagerVariableToLoadOptionEx (
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
-  if (!BmIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) {\r
+  if (!EfiBootManagerIsValidLoadOptionVariableName (VariableName, &OptionType, &OptionNumber)) {\r
     return EFI_INVALID_PARAMETER;\r
   }\r
 \r
@@ -876,7 +928,7 @@ EfiBootManagerVariableToLoadOptionEx (
   FilePath = (EFI_DEVICE_PATH_PROTOCOL *) VariablePtr;\r
   VariablePtr += FilePathSize;\r
 \r
-  OptionalDataSize = (UINT32) (VariableSize - (UINTN) (VariablePtr - Variable));\r
+  OptionalDataSize = (UINT32) (VariableSize - ((UINTN) VariablePtr - (UINTN) Variable));\r
   if (OptionalDataSize == 0) {\r
     OptionalData = NULL;\r
   } else {\r
@@ -920,10 +972,66 @@ EfiBootManagerVariableToLoadOption (
   return EfiBootManagerVariableToLoadOptionEx (VariableName, &gEfiGlobalVariableGuid, Option);\r
 }\r
 \r
+typedef struct {\r
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;\r
+  EFI_GUID                          *Guid;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION      *Options;\r
+  UINTN                             OptionCount;\r
+} BM_COLLECT_LOAD_OPTIONS_PARAM;\r
+\r
+/**\r
+  Visitor function to collect the Platform Recovery load options or OS Recovery\r
+  load options from NV storage.\r
+\r
+  @param Name    Variable name.\r
+  @param Guid    Variable GUID.\r
+  @param Context The same context passed to BmForEachVariable.\r
+**/\r
+VOID\r
+BmCollectLoadOptions (\r
+  IN CHAR16               *Name,\r
+  IN EFI_GUID             *Guid,\r
+  IN VOID                 *Context\r
+  )\r
+{\r
+  EFI_STATUS                        Status;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE OptionType;\r
+  UINT16                            OptionNumber;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION      Option;\r
+  UINTN                             Index;\r
+  BM_COLLECT_LOAD_OPTIONS_PARAM     *Param;\r
+\r
+  Param = (BM_COLLECT_LOAD_OPTIONS_PARAM *) Context;\r
+\r
+  if (CompareGuid (Guid, Param->Guid) && (\r
+      Param->OptionType == LoadOptionTypePlatformRecovery &&\r
+      EfiBootManagerIsValidLoadOptionVariableName (Name, &OptionType, &OptionNumber) &&\r
+      OptionType == LoadOptionTypePlatformRecovery\r
+     )) {\r
+    Status = EfiBootManagerVariableToLoadOptionEx (Name, Guid, &Option);\r
+    if (!EFI_ERROR (Status)) {\r
+      for (Index = 0; Index < Param->OptionCount; Index++) {\r
+        if (Param->Options[Index].OptionNumber > Option.OptionNumber) {\r
+          break;\r
+        }\r
+      }\r
+      Param->Options = ReallocatePool (\r
+                         Param->OptionCount * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),\r
+                         (Param->OptionCount + 1) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION),\r
+                         Param->Options\r
+                         );\r
+      ASSERT (Param->Options != NULL);\r
+      CopyMem (&Param->Options[Index + 1], &Param->Options[Index], (Param->OptionCount - Index) * sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+      CopyMem (&Param->Options[Index], &Option, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+      Param->OptionCount++;\r
+    }\r
+  }\r
+}\r
+\r
 /**\r
   Returns an array of load options based on the EFI variable\r
   L"BootOrder"/L"DriverOrder" and the L"Boot####"/L"Driver####" variables impled by it.\r
-  #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry. \r
+  #### is the hex value of the UINT16 in each BootOrder/DriverOrder entry.\r
 \r
   @param  LoadOptionCount   Returns number of entries in the array.\r
   @param  LoadOptionType    The type of the load option.\r
@@ -939,16 +1047,18 @@ EfiBootManagerGetLoadOptions (
   IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE  LoadOptionType\r
   )\r
 {\r
-  EFI_STATUS                   Status;\r
-  UINT16                       *OptionOrder;\r
-  UINTN                        OptionOrderSize;\r
-  UINTN                        Index;\r
-  UINTN                        OptionIndex;\r
-  EFI_BOOT_MANAGER_LOAD_OPTION *Options;\r
-  CHAR16                       OptionName[BM_OPTION_NAME_LEN];\r
-  UINT16                       OptionNumber;\r
+  EFI_STATUS                    Status;\r
+  UINT16                        *OptionOrder;\r
+  UINTN                         OptionOrderSize;\r
+  UINTN                         Index;\r
+  UINTN                         OptionIndex;\r
+  EFI_BOOT_MANAGER_LOAD_OPTION  *Options;\r
+  CHAR16                        OptionName[BM_OPTION_NAME_LEN];\r
+  UINT16                        OptionNumber;\r
+  BM_COLLECT_LOAD_OPTIONS_PARAM Param;\r
 \r
   *OptionCount = 0;\r
+  Options      = NULL;\r
 \r
   if (LoadOptionType == LoadOptionTypeDriver || LoadOptionType == LoadOptionTypeSysPrep || LoadOptionType == LoadOptionTypeBoot) {\r
     //\r
@@ -971,7 +1081,7 @@ EfiBootManagerGetLoadOptions (
 \r
       Status = EfiBootManagerVariableToLoadOption (OptionName, &Options[OptionIndex]);\r
       if (EFI_ERROR (Status)) {\r
-        DEBUG ((EFI_D_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));\r
+        DEBUG ((DEBUG_INFO, "[Bds] %s doesn't exist - Update ****Order variable to remove the reference!!", OptionName));\r
         EfiBootManagerDeleteLoadOptionVariable (OptionNumber, LoadOptionType);\r
       } else {\r
         ASSERT (Options[OptionIndex].OptionNumber == OptionNumber);\r
@@ -989,8 +1099,16 @@ EfiBootManagerGetLoadOptions (
       *OptionCount = OptionIndex;\r
     }\r
 \r
-  } else {\r
-    return NULL;\r
+  } else if (LoadOptionType == LoadOptionTypePlatformRecovery) {\r
+    Param.OptionType = LoadOptionTypePlatformRecovery;\r
+    Param.Options = NULL;\r
+    Param.OptionCount = 0;\r
+    Param.Guid = &gEfiGlobalVariableGuid;\r
+\r
+    BmForEachVariable (BmCollectLoadOptions, (VOID *) &Param);\r
+\r
+    *OptionCount = Param.OptionCount;\r
+    Options = Param.Options;\r
   }\r
 \r
   return Options;\r
@@ -1001,8 +1119,8 @@ EfiBootManagerGetLoadOptions (
 \r
   @param  LoadOption   Pointer to boot option to Free.\r
 \r
-  @return EFI_SUCCESS   BootOption was freed \r
-  @return EFI_NOT_FOUND BootOption == NULL \r
+  @return EFI_SUCCESS   BootOption was freed\r
+  @return EFI_NOT_FOUND BootOption == NULL\r
 \r
 **/\r
 EFI_STATUS\r
@@ -1029,14 +1147,14 @@ EfiBootManagerFreeLoadOption (
 }\r
 \r
 /**\r
-  Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by \r
+  Free an EFI_BOOT_MANGER_LOAD_OPTION array that was allocated by\r
   EfiBootManagerGetLoadOptions().\r
 \r
   @param  Option       Pointer to boot option array to free.\r
   @param  OptionCount  Number of array entries in BootOption\r
 \r
-  @return EFI_SUCCESS   BootOption was freed \r
-  @return EFI_NOT_FOUND BootOption == NULL \r
+  @return EFI_SUCCESS   BootOption was freed\r
+  @return EFI_NOT_FOUND BootOption == NULL\r
 \r
 **/\r
 EFI_STATUS\r
@@ -1065,6 +1183,10 @@ EfiBootManagerFreeLoadOptions (
   Return whether the PE header of the load option is valid or not.\r
 \r
   @param[in] Type       The load option type.\r
+                        It's used to check whether the load option is valid.\r
+                        When it's LoadOptionTypeMax, the routine only guarantees\r
+                        the load option is a valid PE image but doesn't guarantee\r
+                        the PE's subsystem type is valid.\r
   @param[in] FileBuffer The PE file buffer of the load option.\r
   @param[in] FileSize   The size of the load option file.\r
 \r
@@ -1105,20 +1227,20 @@ BmIsLoadOptionPeHeaderValid (
       // Check PE32 or PE32+ magic, and machine type\r
       //\r
       OptionalHeader = (EFI_IMAGE_OPTIONAL_HEADER32 *) &PeHeader->Pe32.OptionalHeader;\r
-      if ((OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC || \r
-           OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) &&\r
-          EFI_IMAGE_MACHINE_TYPE_SUPPORTED (PeHeader->Pe32.FileHeader.Machine)\r
-          ) {\r
+      if (OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC ||\r
+          OptionalHeader->Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) {\r
         //\r
         // Check the Subsystem:\r
         //   Driver#### must be of type BootServiceDriver or RuntimeDriver\r
         //   SysPrep####, Boot####, OsRecovery####, PlatformRecovery#### must be of type Application\r
         //\r
         Subsystem = OptionalHeader->Subsystem;\r
-        if ((Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||\r
+        if ((Type == LoadOptionTypeMax) ||\r
+            (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) ||\r
             (Type == LoadOptionTypeDriver && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) ||\r
             (Type == LoadOptionTypeSysPrep && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||\r
-            (Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)\r
+            (Type == LoadOptionTypeBoot && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION) ||\r
+            (Type == LoadOptionTypePlatformRecovery && Subsystem == EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION)\r
             ) {\r
           return TRUE;\r
         }\r
@@ -1129,12 +1251,97 @@ BmIsLoadOptionPeHeaderValid (
   return FALSE;\r
 }\r
 \r
+/**\r
+  Return the next matched load option buffer.\r
+  The routine keeps calling BmGetNextLoadOptionDevicePath() until a valid\r
+  load option is read.\r
+\r
+  @param Type      The load option type.\r
+                   It's used to check whether the load option is valid.\r
+                   When it's LoadOptionTypeMax, the routine only guarantees\r
+                   the load option is a valid PE image but doesn't guarantee\r
+                   the PE's subsystem type is valid.\r
+  @param FilePath  The device path pointing to a load option.\r
+                   It could be a short-form device path.\r
+  @param FullPath  Return the next full device path of the load option after\r
+                   short-form device path expanding.\r
+                   Caller is responsible to free it.\r
+                   NULL to return the first matched full device path.\r
+  @param FileSize  Return the load option size.\r
+\r
+  @return The load option buffer. Caller is responsible to free the memory.\r
+**/\r
+VOID *\r
+BmGetNextLoadOptionBuffer (\r
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL          **FullPath,\r
+  OUT UINTN                             *FileSize\r
+  )\r
+{\r
+  VOID                                  *FileBuffer;\r
+  EFI_DEVICE_PATH_PROTOCOL              *PreFullPath;\r
+  EFI_DEVICE_PATH_PROTOCOL              *CurFullPath;\r
+  UINTN                                 LocalFileSize;\r
+  UINT32                                AuthenticationStatus;\r
+  EFI_DEVICE_PATH_PROTOCOL              *RamDiskDevicePath;\r
+\r
+  LocalFileSize = 0;\r
+  FileBuffer  = NULL;\r
+  CurFullPath = *FullPath;\r
+  do {\r
+    PreFullPath = CurFullPath;\r
+    CurFullPath = BmGetNextLoadOptionDevicePath (FilePath, CurFullPath);\r
+    //\r
+    // Only free the full path created *inside* this routine\r
+    //\r
+    if ((PreFullPath != NULL) && (PreFullPath != *FullPath)) {\r
+      FreePool (PreFullPath);\r
+    }\r
+    if (CurFullPath == NULL) {\r
+      break;\r
+    }\r
+    FileBuffer = GetFileBufferByFilePath (TRUE, CurFullPath, &LocalFileSize, &AuthenticationStatus);\r
+    if ((FileBuffer != NULL) && !BmIsLoadOptionPeHeaderValid (Type, FileBuffer, LocalFileSize)) {\r
+      //\r
+      // Free the RAM disk file system if the load option is invalid.\r
+      //\r
+      RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);\r
+      if (RamDiskDevicePath != NULL) {\r
+        BmDestroyRamDisk (RamDiskDevicePath);\r
+        FreePool (RamDiskDevicePath);\r
+      }\r
+\r
+      //\r
+      // Free the invalid load option buffer.\r
+      //\r
+      FreePool (FileBuffer);\r
+      FileBuffer = NULL;\r
+    }\r
+  } while (FileBuffer == NULL);\r
+\r
+  if (FileBuffer == NULL) {\r
+    CurFullPath = NULL;\r
+    LocalFileSize = 0;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "[Bds] Expand "));\r
+  BmPrintDp (FilePath);\r
+  DEBUG ((DEBUG_INFO, " -> "));\r
+  BmPrintDp (CurFullPath);\r
+  DEBUG ((DEBUG_INFO, "\n"));\r
+\r
+  *FullPath = CurFullPath;\r
+  *FileSize = LocalFileSize;\r
+  return FileBuffer;\r
+}\r
+\r
 /**\r
   Process (load and execute) the load option.\r
 \r
   @param LoadOption  Pointer to the load option.\r
 \r
-  @retval EFI_INVALID_PARAMETER  The load option type is invalid, \r
+  @retval EFI_INVALID_PARAMETER  The load option type is invalid,\r
                                  or the load option file path doesn't point to a valid file.\r
   @retval EFI_UNSUPPORTED        The load option type is of LoadOptionTypeBoot.\r
   @retval EFI_SUCCESS            The load option is inactive, or successfully loaded and executed.\r
@@ -1146,7 +1353,8 @@ EfiBootManagerProcessLoadOption (
   )\r
 {\r
   EFI_STATUS                        Status;\r
-  EFI_DEVICE_PATH_PROTOCOL          *FilePath;\r
+  EFI_DEVICE_PATH_PROTOCOL          *PreFullPath;\r
+  EFI_DEVICE_PATH_PROTOCOL          *CurFullPath;\r
   EFI_HANDLE                        ImageHandle;\r
   EFI_LOADED_IMAGE_PROTOCOL         *ImageInfo;\r
   VOID                              *FileBuffer;\r
@@ -1168,64 +1376,81 @@ EfiBootManagerProcessLoadOption (
     return EFI_SUCCESS;\r
   }\r
 \r
-  Status = EFI_INVALID_PARAMETER;\r
-\r
   //\r
   // Load and start the load option.\r
   //\r
   DEBUG ((\r
-    DEBUG_INFO | DEBUG_LOAD, "Process Load Option (%s%04x) ...\n",\r
-    mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber\r
+    DEBUG_INFO | DEBUG_LOAD, "Process %s%04x (%s) ...\n",\r
+    mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber,\r
+    LoadOption->Description\r
     ));\r
   ImageHandle = NULL;\r
-  FileBuffer = BmGetLoadOptionBuffer (LoadOption->FilePath, &FilePath, &FileSize);\r
-  DEBUG_CODE (\r
-    if (FileBuffer != NULL && CompareMem (LoadOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {\r
-      DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));\r
-      BmPrintDp (LoadOption->FilePath);\r
-      DEBUG ((EFI_D_INFO, " -> "));\r
-      BmPrintDp (FilePath);\r
-      DEBUG ((EFI_D_INFO, "\n"));\r
+  CurFullPath = NULL;\r
+  EfiBootManagerConnectDevicePath (LoadOption->FilePath, NULL);\r
+\r
+  //\r
+  // while() loop is to keep starting next matched load option if the PlatformRecovery#### returns failure status.\r
+  //\r
+  while (TRUE) {\r
+    Status      = EFI_INVALID_PARAMETER;\r
+    PreFullPath = CurFullPath;\r
+    FileBuffer  = BmGetNextLoadOptionBuffer (LoadOption->OptionType, LoadOption->FilePath, &CurFullPath, &FileSize);\r
+    if (PreFullPath != NULL) {\r
+      FreePool (PreFullPath);\r
+    }\r
+    if (FileBuffer == NULL) {\r
+      break;\r
     }\r
-  );\r
-  if (BmIsLoadOptionPeHeaderValid (LoadOption->OptionType, FileBuffer, FileSize)) {\r
     Status = gBS->LoadImage (\r
                     FALSE,\r
                     gImageHandle,\r
-                    FilePath,\r
+                    CurFullPath,\r
                     FileBuffer,\r
                     FileSize,\r
                     &ImageHandle\r
                     );\r
-  }\r
-  if (FilePath != NULL) {\r
-    FreePool (FilePath);\r
-  }\r
-  if (FileBuffer != NULL) {\r
     FreePool (FileBuffer);\r
-  }\r
 \r
-  if (!EFI_ERROR (Status)) {\r
-    Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **) &ImageInfo);\r
-    ASSERT_EFI_ERROR (Status);\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // With EFI_SECURITY_VIOLATION retval, the Image was loaded and an ImageHandle was created\r
+      // with a valid EFI_LOADED_IMAGE_PROTOCOL, but the image can not be started right now.\r
+      // If the caller doesn't have the option to defer the execution of an image, we should\r
+      // unload image for the EFI_SECURITY_VIOLATION to avoid resource leak.\r
+      //\r
+      if (Status == EFI_SECURITY_VIOLATION) {\r
+        gBS->UnloadImage (ImageHandle);\r
+      }\r
+    } else {\r
+      Status = gBS->HandleProtocol (ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&ImageInfo);\r
+      ASSERT_EFI_ERROR (Status);\r
 \r
-    ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;\r
-    ImageInfo->LoadOptions = LoadOption->OptionalData;\r
-    //\r
-    // Before calling the image, enable the Watchdog Timer for the 5-minute period\r
-    //\r
-    gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);\r
+      ImageInfo->LoadOptionsSize = LoadOption->OptionalDataSize;\r
+      ImageInfo->LoadOptions = LoadOption->OptionalData;\r
+      //\r
+      // Before calling the image, enable the Watchdog Timer for the 5-minute period\r
+      //\r
+      gBS->SetWatchdogTimer (5 * 60, 0, 0, NULL);\r
 \r
-    LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);\r
-    DEBUG ((\r
-      DEBUG_INFO | DEBUG_LOAD, "Load Option (%s%04x) Return Status = %r\n",\r
-      mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status\r
+      LoadOption->Status = gBS->StartImage (ImageHandle, &LoadOption->ExitDataSize, &LoadOption->ExitData);\r
+      DEBUG ((\r
+        DEBUG_INFO | DEBUG_LOAD, "%s%04x Return Status = %r\n",\r
+        mBmLoadOptionName[LoadOption->OptionType], LoadOption->OptionNumber, LoadOption->Status\r
       ));\r
 \r
-    //\r
-    // Clear the Watchdog Timer after the image returns\r
-    //\r
-    gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
+      //\r
+      // Clear the Watchdog Timer after the image returns\r
+      //\r
+      gBS->SetWatchdogTimer (0, 0, 0, NULL);\r
+\r
+      if ((LoadOption->OptionType != LoadOptionTypePlatformRecovery) || (LoadOption->Status == EFI_SUCCESS)) {\r
+        break;\r
+      }\r
+    }\r
+  }\r
+\r
+  if (CurFullPath != NULL) {\r
+    FreePool (CurFullPath);\r
   }\r
 \r
   return Status;\r