]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
MdeModulePkg/UefiBootManagerLib: Fix small LoadOptionToVariable leak
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmLoadOption.c
index 6fcd23b6aba337adb26922a0b3f606ddf88eef08..7bf96646c690e2606fe77b7c8333855fe0bc8a2c 100644 (file)
@@ -1,8 +1,8 @@
 /** @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
+Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+(C) Copyright 2015-2018 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
@@ -19,14 +19,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 +90,7 @@ BmGetFreeOptionNumber (
   OUT UINT16                            *FreeOptionNumber\r
   )\r
 {\r
-  \r
+\r
   UINTN         OptionNumber;\r
   UINTN         Index;\r
   UINT16        *OptionOrder;\r
@@ -96,7 +98,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 +110,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 +125,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 +156,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 +170,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 +203,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 +238,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 +332,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 +345,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 +362,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 +402,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
@@ -494,7 +517,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 +571,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 +590,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 +605,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 +671,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 +709,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 +719,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 +782,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
-  return (BOOLEAN) (Index == VariableNameLen);\r
+  if (OptionNumber != NULL) {\r
+    *OptionNumber = LocalOptionNumber;\r
+  }\r
+\r
+  return TRUE;\r
 }\r
 \r
 /**\r
@@ -807,7 +865,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 +885,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 +934,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 +978,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 +1053,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
@@ -989,8 +1105,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 +1125,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 +1153,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 +1189,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,7 +1233,7 @@ 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
+      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
@@ -1115,10 +1243,12 @@ BmIsLoadOptionPeHeaderValid (
         //   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 +1259,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 +1361,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 +1384,71 @@ 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
+      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