[edk2] [PATCH] MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic

Ruiyu Ni posted 1 patch 7 years, 6 months ago
Failed in applying to current master (apply log)
MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c   | 470 ++++++++++++---------
.../Library/UefiBootManagerLib/BmLoadOption.c      | 173 ++++++--
.../Library/UefiBootManagerLib/InternalBm.h        | 100 +++--
3 files changed, 475 insertions(+), 268 deletions(-)
[edk2] [PATCH] MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic
Posted by Ruiyu Ni 7 years, 6 months ago
Old implementation only finds first matched full device path for a
given short-form device path.
The patch adds internal function BmGetNextLoadOptionBuffer() to finds
all matched full device path for a given short-form device path.
There are 6 kinds of device paths. Some of them match to multiple
load options, some of them don't.

1. Media device path:
   Returns multiple load options: The media device path may point
   to a physical BlockIo which contains multiple logic partitions,
   each logic partitions contains \EFI\BOOT\BOOT${ARCH}.EFI.

2. Short-form hard-drive device path:
   Returns one load option because the partition signature is unique.

3. Short-form file-path device path:
   Returns multiple load options: There are multiple SimpleFileSystem
   instances and each contains the same file.

4. Short-form URI device path:
   Returns multiple load options: There are multiple LoadFile
   instances and each can boot.

5. Short-form USB device path:
   Returns multiple load options: There are multiple UsbIo instances
   and each contains the boot-able file.

6. FV device path, device path pointing to SimpleFileSystem, device
   path pointing to LoadFile
   Returns one load option.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Feng Tian <feng.tian@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jeff Fan <jeff.fan@intel.com>
---
 MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c   | 470 ++++++++++++---------
 .../Library/UefiBootManagerLib/BmLoadOption.c      | 173 ++++++--
 .../Library/UefiBootManagerLib/InternalBm.h        | 100 +++--
 3 files changed, 475 insertions(+), 268 deletions(-)

diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
index 8a3a402..aa79c90 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
@@ -131,21 +131,16 @@ BmFindBootOptionInVariable (
 }
 
 /**
-  Get the file buffer using a Memory Mapped Device Path.
-
+  Return the correct FV file path.
   FV address may change across reboot. This routine promises the FV file device path is right.
 
   @param  FilePath     The Memory Mapped Device Path to get the file buffer.
-  @param  FullPath     Receive the updated FV Device Path pointint to the file.
-  @param  FileSize     Receive the file buffer size.
 
-  @return  The file buffer.
+  @return  The updated FV Device Path pointint to the file.
 **/
-VOID *
-BmGetFileBufferByFvFilePath (
-  IN EFI_DEVICE_PATH_PROTOCOL      *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL     **FullPath,
-  OUT UINTN                        *FileSize
+EFI_DEVICE_PATH_PROTOCOL *
+BmAdjustFvFilePath (
+  IN EFI_DEVICE_PATH_PROTOCOL      *FilePath
   )
 {
   EFI_STATUS                    Status;
@@ -153,11 +148,10 @@ BmGetFileBufferByFvFilePath (
   EFI_DEVICE_PATH_PROTOCOL      *FvFileNode;
   EFI_HANDLE                    FvHandle;
   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
-  UINT32                        AuthenticationStatus;
   UINTN                         FvHandleCount;
   EFI_HANDLE                    *FvHandles;
   EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;
-  VOID                          *FileBuffer;
+  EFI_DEVICE_PATH_PROTOCOL      *FullPath;
 
   //
   // Get the file buffer by using the exactly FilePath.
@@ -165,11 +159,7 @@ BmGetFileBufferByFvFilePath (
   FvFileNode = FilePath;
   Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
   if (!EFI_ERROR (Status)) {
-    FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer != NULL) {
-      *FullPath = DuplicateDevicePath (FilePath);
-    }
-    return FileBuffer;
+    return DuplicateDevicePath (FilePath);
   }
 
   //
@@ -190,11 +180,10 @@ BmGetFileBufferByFvFilePath (
          (VOID **) &LoadedImage
          );
   NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
-  FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
+  FullPath = BmAdjustFvFilePath (NewDevicePath);
   FreePool (NewDevicePath);
-
-  if (FileBuffer != NULL) {
-    return FileBuffer;
+  if (FullPath != NULL) {
+    return FullPath;
   }
 
   //
@@ -207,22 +196,25 @@ BmGetFileBufferByFvFilePath (
          &FvHandleCount,
          &FvHandles
          );
-  for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) {
+  for (Index = 0; Index < FvHandleCount; Index++) {
     if (FvHandles[Index] == LoadedImage->DeviceHandle) {
       //
-      // Skip current FV
+      // Skip current FV, it was handed in first step.
       //
       continue;
     }
     NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
-    FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
+    FullPath = BmAdjustFvFilePath (NewDevicePath);
     FreePool (NewDevicePath);
+    if (FullPath != NULL) {
+      break;
+    }
   }
   
   if (FvHandles != NULL) {
     FreePool (FvHandles);
   }
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
@@ -555,46 +547,64 @@ BmFindUsbDevice (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
   @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandUsbDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL  **FullPath,
-  OUT UINTN                     *FileSize,
-  IN EFI_DEVICE_PATH_PROTOCOL   *ShortformNode
+  IN  EFI_DEVICE_PATH_PROTOCOL  *FullPath,
+  IN  EFI_DEVICE_PATH_PROTOCOL  *ShortformNode
   )
 {
   UINTN                             ParentDevicePathSize;
   EFI_DEVICE_PATH_PROTOCOL          *RemainingDevicePath;
-  EFI_DEVICE_PATH_PROTOCOL          *FullDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL          *NextFullPath;
   EFI_HANDLE                        *Handles;
   UINTN                             HandleCount;
   UINTN                             Index;
-  VOID                              *FileBuffer;
+  BOOLEAN                           GetNext;
 
+  NextFullPath = NULL;
+  GetNext = (BOOLEAN)(FullPath == NULL);
   ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;
   RemainingDevicePath = NextDevicePathNode (ShortformNode);
-  FileBuffer = NULL;
   Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
 
-  for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {
-    FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
-    FileBuffer = EfiBootManagerGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);
-    FreePool (FullDevicePath);
+  for (Index = 0; Index < HandleCount; Index++) {
+    FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
+    if (FilePath == NULL) {
+      //
+      // Out of memory.
+      //
+      continue;
+    }
+    NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);
+    FreePool (FilePath);
+    if (NextFullPath == NULL) {
+      //
+      // No BlockIo or SimpleFileSystem under FilePath.
+      //
+      continue;
+    }
+    if (GetNext) {
+      break;
+    } else {
+      GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+      FreePool (NextFullPath);
+      NextFullPath = NULL;
+    }
   }
 
   if (Handles != NULL) {
     FreePool (Handles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -602,18 +612,16 @@ BmExpandUsbDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandFileDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL    *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL    **FullPath,
-  OUT UINTN                       *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL    *FullPath
   )
 {
   EFI_STATUS                      Status;
@@ -622,9 +630,8 @@ BmExpandFileDevicePath (
   EFI_HANDLE                      *Handles;
   EFI_BLOCK_IO_PROTOCOL           *BlockIo;
   UINTN                           MediaType;
-  EFI_DEVICE_PATH_PROTOCOL        *FullDevicePath;
-  VOID                            *FileBuffer;
-  UINT32                          AuthenticationStatus;
+  EFI_DEVICE_PATH_PROTOCOL        *NextFullPath;
+  BOOLEAN                         GetNext;
   
   EfiBootManagerConnectAll ();
   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles);
@@ -633,6 +640,8 @@ BmExpandFileDevicePath (
     Handles = NULL;
   }
 
+  GetNext = (BOOLEAN)(FullPath == NULL);
+  NextFullPath = NULL;
   //
   // Enumerate all removable media devices followed by all fixed media devices,
   //   followed by media devices which don't layer on block io.
@@ -647,24 +656,26 @@ BmExpandFileDevicePath (
           (MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||
           (MediaType == 2 && BlockIo == NULL)
           ) {
-        FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
-        FileBuffer = GetFileBufferByFilePath (TRUE, FullDevicePath, FileSize, &AuthenticationStatus);
-        if (FileBuffer != NULL) {
-          *FullPath = FullDevicePath;
-          FreePool (Handles);
-          return FileBuffer;
+        NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
+        if (GetNext) {
+          break;
+        } else {
+          GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+          FreePool (NextFullPath);
+          NextFullPath = NULL;
         }
-        FreePool (FullDevicePath);
       }
     }
+    if (NextFullPath != NULL) {
+      break;
+    }
   }
 
   if (Handles != NULL) {
     FreePool (Handles);
   }
 
-  *FullPath = NULL;
-  return NULL;
+  return NextFullPath;
 }
 
 /**
@@ -672,25 +683,25 @@ BmExpandFileDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandUriDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL    *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL    **FullPath,
-  OUT UINTN                       *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL    *FullPath
   )
 {
   EFI_STATUS                      Status;
   UINTN                           Index;
   UINTN                           HandleCount;
   EFI_HANDLE                      *Handles;
-  VOID                            *FileBuffer;
+  EFI_DEVICE_PATH_PROTOCOL        *NextFullPath;
+  EFI_DEVICE_PATH_PROTOCOL        *RamDiskDevicePath;
+  BOOLEAN                         GetNext;
 
   EfiBootManagerConnectAll ();
   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles);
@@ -699,11 +710,29 @@ BmExpandUriDevicePath (
     Handles = NULL;
   }
 
-  FileBuffer = NULL;
+  NextFullPath = NULL;
+  GetNext = (BOOLEAN)(FullPath == NULL);
   for (Index = 0; Index < HandleCount; Index++) {
-    FileBuffer = BmGetFileBufferFromLoadFile (Handles[Index], FilePath, FullPath, FileSize);
-    if (FileBuffer != NULL) {
+    NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);
+
+    if (NextFullPath == NULL) {
+      continue;
+    }
+
+    if (GetNext) {
       break;
+    } else {
+      GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+      //
+      // Free the resource occupied by the RAM disk.
+      //
+      RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);
+      if (RamDiskDevicePath != NULL) {
+        BmDestroyRamDisk (RamDiskDevicePath);
+        FreePool (RamDiskDevicePath);
+      }
+      FreePool (NextFullPath);
+      NextFullPath = NULL;
     }
   }
 
@@ -711,7 +740,7 @@ BmExpandUriDevicePath (
     FreePool (Handles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -781,35 +810,28 @@ BmCachePartitionDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The full device path pointing to the load option.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandPartitionDevicePath (
-  IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL  **FullPath,
-  OUT UINTN                     *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath
   )
 {
   EFI_STATUS                Status;
   UINTN                     BlockIoHandleCount;
   EFI_HANDLE                *BlockIoBuffer;
-  VOID                      *FileBuffer;
   EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;
   UINTN                     Index;
   EFI_DEVICE_PATH_PROTOCOL  *CachedDevicePath;
   EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *FullPath;
   UINTN                     CachedDevicePathSize;
   BOOLEAN                   NeedAdjust;
   EFI_DEVICE_PATH_PROTOCOL  *Instance;
   UINTN                     Size;
 
-  FileBuffer = NULL;
   //
   // Check if there is prestore 'HDDP' variable.
   // If exist, search the front path which point to partition node in the variable instants.
@@ -833,6 +855,7 @@ BmExpandPartitionDevicePath (
     ASSERT_EFI_ERROR (Status);
   }
 
+  FullPath = NULL;
   if (CachedDevicePath != NULL) {
     TempNewDevicePath = CachedDevicePath;
     NeedAdjust = FALSE;
@@ -851,10 +874,20 @@ BmExpandPartitionDevicePath (
         Status = EfiBootManagerConnectDevicePath (Instance, NULL);
         if (!EFI_ERROR (Status)) {
           TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));
-          FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
+          //
+          // TempDevicePath = ACPI()/PCI()/ATA()/Partition()
+          // or             = ACPI()/PCI()/ATA()/Partition()/.../A.EFI
+          //
+          // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),
+          // it may expand to two potienal full paths (nested partition, rarely happen):
+          //   1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI
+          //   2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI
+          // For simplicity, only #1 is returned.
+          //
+          FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
           FreePool (TempDevicePath);
 
-          if (FileBuffer != NULL) {
+          if (FullPath != NULL) {
             //
             // Adjust the 'HDDP' instances sequence if the matched one is not first one.
             //
@@ -875,7 +908,7 @@ BmExpandPartitionDevicePath (
 
             FreePool (Instance);
             FreePool (CachedDevicePath);
-            return FileBuffer;
+            return FullPath;
           }
         }
       }
@@ -911,10 +944,10 @@ BmExpandPartitionDevicePath (
       // Find the matched partition device path
       //
       TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
-      FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
+      FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
       FreePool (TempDevicePath);
 
-      if (FileBuffer != NULL) {
+      if (FullPath != NULL) {
         BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
 
         //
@@ -940,7 +973,7 @@ BmExpandPartitionDevicePath (
   if (BlockIoBuffer != NULL) {
     FreePool (BlockIoBuffer);
   }
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
@@ -948,16 +981,16 @@ BmExpandPartitionDevicePath (
   by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
 
   @param DevicePath  The media device path pointing to a BlockIo or SimpleFileSystem instance.
-  @param FullPath    Return the full device path pointing to the load option.
-  @param FileSize    Return the size of the load option.
+  @param FullPath    The full path returned by the routine in last call.
+                     Set to NULL in first call.
 
-  @return  The load option buffer.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandMediaDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FullPath
   )
 {
   EFI_STATUS                          Status;
@@ -965,14 +998,15 @@ BmExpandMediaDevicePath (
   EFI_BLOCK_IO_PROTOCOL               *BlockIo;
   VOID                                *Buffer;
   EFI_DEVICE_PATH_PROTOCOL            *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL            *NextFullPath;
   UINTN                               Size;
   UINTN                               TempSize;
   EFI_HANDLE                          *SimpleFileSystemHandles;
   UINTN                               NumberSimpleFileSystemHandles;
   UINTN                               Index;
-  VOID                                *FileBuffer;
-  UINT32                              AuthenticationStatus;
+  BOOLEAN                             GetNext;
 
+  GetNext = (BOOLEAN)(FullPath == NULL);
   //
   // Check whether the device is connected
   //
@@ -981,14 +1015,16 @@ BmExpandMediaDevicePath (
   if (!EFI_ERROR (Status)) {
     ASSERT (IsDevicePathEnd (TempDevicePath));
 
-    TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
-    FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer == NULL) {
-      FreePool (TempDevicePath);
-      TempDevicePath = NULL;
+    NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
+    //
+    // For device path pointing to simple file system, it only expands to one full path.
+    //
+    if (GetNext) {
+      return NextFullPath;
+    } else {
+      FreePool (NextFullPath);
+      return NULL;
     }
-    *FullPath = TempDevicePath;
-    return FileBuffer;
   }
 
   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle);
@@ -1025,8 +1061,7 @@ BmExpandMediaDevicePath (
   //
   // Detect the the default boot file from removable Media
   //
-  FileBuffer = NULL;
-  *FullPath = NULL;
+  NextFullPath = NULL;
   Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
   gBS->LocateHandleBuffer (
          ByProtocol,
@@ -1045,13 +1080,14 @@ BmExpandMediaDevicePath (
     // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
     //
     if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
-      TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
-      FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
-      if (FileBuffer != NULL) {
-        *FullPath = TempDevicePath;
+      NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
+      if (GetNext) {
         break;
+      } else {
+        GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+        FreePool (NextFullPath);
+        NextFullPath = NULL;
       }
-      FreePool (TempDevicePath);
     }
   }
 
@@ -1059,7 +1095,7 @@ BmExpandMediaDevicePath (
     FreePool (SimpleFileSystemHandles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -1098,17 +1134,14 @@ BmMatchHttpBootDevicePath (
   Get the file buffer from the file system produced by Load File instance.
 
   @param LoadFileHandle The handle of LoadFile instance.
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
   @param RamDiskHandle  Return the RAM Disk handle.
 
-  @return  The load option buffer.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
-BmGetFileBufferFromLoadFileSystem (
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandNetworkFileSystem (
   IN  EFI_HANDLE                      LoadFileHandle,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize,
   OUT EFI_HANDLE                      *RamDiskHandle
   )
 {
@@ -1138,6 +1171,9 @@ BmGetFileBufferFromLoadFileSystem (
     if (!EFI_ERROR (Status) &&
         (Handle == LoadFileHandle) &&
         (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {
+      //
+      // Find the BlockIo instance populated from the LoadFile.
+      //
       Handle = Handles[Index];
       break;
     }
@@ -1154,13 +1190,16 @@ BmGetFileBufferFromLoadFileSystem (
   *RamDiskHandle = Handle;
 
   if (Handle != NULL) {
-    return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), FullPath, FileSize);
+    //
+    // Re-use BmExpandMediaDevicePath() to get the full device path of load option.
+    // But assume only one SimpleFileSystem can be found under the BlockIo.
+    //
+    return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), NULL);
   } else {
     return NULL;
   }
 }
 
-
 /**
   Return the RAM Disk device path created by LoadFile.
 
@@ -1274,27 +1313,21 @@ BmDestroyRamDisk (
 
   @param LoadFileHandle The specified Load File instance.
   @param FilePath       The file path which will pass to LoadFile().
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
 
-  @return  The load option buffer or NULL if fails.
+  @return  The full device path pointing to the load option buffer.
 **/
-VOID *
-BmGetFileBufferFromLoadFile (
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
   IN  EFI_HANDLE                      LoadFileHandle,
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
   )
 {
   EFI_STATUS                          Status;
   EFI_LOAD_FILE_PROTOCOL              *LoadFile;
   VOID                                *FileBuffer;
-  BOOLEAN                             LoadFileSystem;
   EFI_HANDLE                          RamDiskHandle;
   UINTN                               BufferSize;
-
-  *FileSize = 0;
+  EFI_DEVICE_PATH_PROTOCOL            *FullPath;
 
   Status = gBS->OpenProtocol (
                   LoadFileHandle,
@@ -1313,52 +1346,60 @@ BmGetFileBufferFromLoadFile (
     return NULL;
   }
 
-  LoadFileSystem = (BOOLEAN) (Status == EFI_WARN_FILE_SYSTEM);
-  FileBuffer = LoadFileSystem ? AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)) : AllocatePool (BufferSize);
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    //
+    // The load option buffer is directly returned by LoadFile.
+    //
+    return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
+  }
+
+  //
+  // The load option resides in a RAM disk.
+  //
+  FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
   if (FileBuffer == NULL) {
     return NULL;
   }
 
   Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
   if (EFI_ERROR (Status)) {
-    if (LoadFileSystem) {
-      FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
-    } else {
-      FreePool (FileBuffer);
-    }
+    FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
     return NULL;
   }
 
-  if (LoadFileSystem) {
-    FileBuffer = BmGetFileBufferFromLoadFileSystem (LoadFileHandle, FullPath, FileSize, &RamDiskHandle);
-    if (FileBuffer == NULL) {
-      //
-      // If there is no bootable executable in the populated
-      //
-      BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
-    }
-  } else {
-    *FileSize = BufferSize;
-    *FullPath = DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
+  FullPath = BmExpandNetworkFileSystem (LoadFileHandle, &RamDiskHandle);
+  if (FullPath == NULL) {
+    //
+    // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
+    //
+    BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
   }
 
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
-  Get the file buffer from all the Load File instances.
+  Return the full device path pointing to the load option.
+
+  FilePath may:
+  1. Exactly matches to a LoadFile instance.
+  2. Cannot match to any LoadFile instance. Wide match is required.
+  In either case, the routine may return:
+  1. A copy of FilePath when FilePath matches to a LoadFile instance and
+     the LoadFile returns a load option buffer.
+  2. A new device path with IP and URI information updated when wide match
+     happens.
+  3. A new device path pointing to a load option in RAM disk.
+  In either case, only one full device path is returned for a specified
+  FilePath.
 
   @param FilePath    The media device path pointing to a LoadFile instance.
-  @param FullPath    Return the full device path pointing to the load option.
-  @param FileSize    Return the size of the load option.
 
   @return  The load option buffer.
 **/
-VOID *
-BmGetFileBufferFromLoadFiles (
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFiles (
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
   )
 {
   EFI_STATUS                      Status;
@@ -1411,7 +1452,7 @@ BmGetFileBufferFromLoadFiles (
     return NULL;
   }
 
-  return BmGetFileBufferFromLoadFile (Handle, FilePath, FullPath, FileSize);
+  return BmExpandLoadFile (Handle, FilePath);
 }
 
 /**
@@ -1434,19 +1475,37 @@ EfiBootManagerGetLoadOptionBuffer (
   OUT UINTN                             *FileSize
   )
 {
+  *FullPath = NULL;
+
+  EfiBootManagerConnectDevicePath (FilePath, NULL);
+  return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, FullPath, FileSize);
+}
+
+/**
+  Get the next possible full path pointing to the load option.
+  The routine doesn't guarantee the returned full path points to an existing
+  file, and it also doesn't guarantee the existing file is a valid load option.
+  BmGetNextLoadOptionBuffer() guarantees.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  )
+{
   EFI_HANDLE                      Handle;
-  VOID                            *FileBuffer;
-  UINT32                          AuthenticationStatus;
   EFI_DEVICE_PATH_PROTOCOL        *Node;
   EFI_STATUS                      Status;
 
-  ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));
-
-  EfiBootManagerConnectDevicePath (FilePath, NULL);
-
-  *FullPath  = NULL;
-  *FileSize  = 0;
-  FileBuffer = NULL;
+  ASSERT (FilePath != NULL);
 
   //
   // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI
@@ -1458,7 +1517,7 @@ EfiBootManagerGetLoadOptionBuffer (
   }
 
   if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
-    return BmExpandMediaDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandMediaDevicePath (FilePath, FullPath);
   }
 
   //
@@ -1469,19 +1528,23 @@ EfiBootManagerGetLoadOptionBuffer (
     //
     // Expand the Harddrive device path
     //
-    return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);
+    if (FullPath == NULL) {
+      return BmExpandPartitionDevicePath (FilePath);
+    } else {
+      return NULL;
+    }
   } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
              (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {
     //
     // Expand the File-path device path
     //
-    return BmExpandFileDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandFileDevicePath (FilePath, FullPath);
   } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&
              (DevicePathSubType (FilePath) == MSG_URI_DP)) {
     //
     // Expand the URI device path
     //
-    return BmExpandUriDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandUriDevicePath (FilePath, FullPath);
   } else {
     for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
       if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) &&
@@ -1490,45 +1553,50 @@ EfiBootManagerGetLoadOptionBuffer (
       }
     }
 
+    //
+    // Expand the USB WWID/Class device path
+    //
     if (!IsDevicePathEnd (Node)) {
-      //
-      // Expand the USB WWID/Class device path
-      //
-      FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
-      if ((FileBuffer == NULL) && (FilePath == Node)) {
+      if (FilePath == Node) {
         //
         // Boot Option device path starts with USB Class or USB WWID device path.
         // For Boot Option device path which doesn't begin with the USB Class or
         // USB WWID device path, it's not needed to connect again here.
         //
         BmConnectUsbShortFormDevicePath (FilePath);
-        FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
       }
-      return FileBuffer;
+      return BmExpandUsbDevicePath (FilePath, FullPath, Node);
     }
   }
 
   //
-  // Get file buffer from FV file path.
+  // For the below cases, FilePath only expands to one Full path.
+  // So just handle the case when FullPath == NULL.
+  //
+  if (FullPath != NULL) {
+    return NULL;
+  }
+
+  //
+  // Load option resides in FV.
   //
   if (BmIsFvFilePath (FilePath)) {
-    return BmGetFileBufferByFvFilePath (FilePath, FullPath, FileSize);
+    return BmAdjustFvFilePath (FilePath);
   }
 
   //
-  // Get file buffer from simple file system.
+  // Load option resides in Simple File System.
   //
   Node   = FilePath;
   Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
   if (!EFI_ERROR (Status)) {
-    FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer != NULL) {
-      *FullPath = DuplicateDevicePath (FilePath);
-    }
-    return FileBuffer;
+    return DuplicateDevicePath (FilePath);
   }
 
-  return BmGetFileBufferFromLoadFiles (FilePath, FullPath, FileSize);
+  //
+  // Last chance to try: Load option may be loaded through LoadFile.
+  //
+  return BmExpandLoadFiles (FilePath);
 }
 
 /**
@@ -1686,21 +1754,13 @@ EfiBootManagerBoot (
   ImageHandle       = NULL;
   RamDiskDevicePath = NULL;
   if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
-    Status     = EFI_NOT_FOUND;
-    FileBuffer = EfiBootManagerGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);
+    Status   = EFI_NOT_FOUND;
+    FilePath = NULL;
+    EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);
+    FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, BootOption->FilePath, &FilePath, &FileSize);
     if (FileBuffer != NULL) {
       RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
-    }
-    DEBUG_CODE (
-      if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
-        DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
-        BmPrintDp (BootOption->FilePath);
-        DEBUG ((EFI_D_INFO, " -> "));
-        BmPrintDp (FilePath);
-        DEBUG ((EFI_D_INFO, "\n"));
-      }
-    );
-    if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {
+
       REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
       Status = gBS->LoadImage (
                       TRUE,
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
index 116cf28..b0a3505 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
@@ -1167,6 +1167,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.
 
@@ -1217,7 +1221,8 @@ BmIsLoadOptionPeHeaderValid (
         //   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) ||
@@ -1233,6 +1238,91 @@ BmIsLoadOptionPeHeaderValid (
 }
 
 /**
+  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.
@@ -1249,7 +1339,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;
@@ -1271,8 +1362,6 @@ EfiBootManagerProcessLoadOption (
     return EFI_SUCCESS;
   }
 
-  Status = EFI_INVALID_PARAMETER;
-
   //
   // Load and start the load option.
   //
@@ -1282,54 +1371,62 @@ EfiBootManagerProcessLoadOption (
     LoadOption->Description
     ));
   ImageHandle = NULL;
-  FileBuffer = EfiBootManagerGetLoadOptionBuffer (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)) {
+      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, "%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;
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
index 8d7215a..ef09050 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
+++ b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
@@ -310,23 +310,6 @@ BmSetVariableAndReportStatusCodeOnError (
   );
 
 /**
-  Return whether the PE header of the load option is valid or not.
-
-  @param[in] Type       The load option type.
-  @param[in] FileBuffer The PE file buffer of the load option.
-  @param[in] FileSize   The size of the load option file.
-
-  @retval TRUE  The PE header of the load option is valid.
-  @retval FALSE The PE header of the load option is not valid.
-**/
-BOOLEAN
-BmIsLoadOptionPeHeaderValid (
-  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
-  IN VOID                              *FileBuffer,
-  IN UINTN                             FileSize
-  );
-
-/**
   Function compares a device path data structure to that of all the nodes of a
   second device path instance.
 
@@ -425,16 +408,83 @@ BmMakeBootOptionDescriptionUnique (
 
   @param LoadFileHandle The specified Load File instance.
   @param FilePath       The file path which will pass to LoadFile().
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
 
-  @return  The load option buffer or NULL if fails.
+  @return  The full device path pointing to the load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
+  IN  EFI_HANDLE                      LoadFileHandle,
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
+  );
+
+/**
+  Return the RAM Disk device path created by LoadFile.
+
+  @param FilePath  The source file path.
+
+  @return Callee-to-free RAM Disk device path
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetRamDiskDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+  );
+
+/**
+  Destroy the RAM Disk.
+
+  The destroy operation includes to call RamDisk.Unregister to
+  unregister the RAM DISK from RAM DISK driver, free the memory
+  allocated for the RAM Disk.
+
+  @param RamDiskDevicePath    RAM Disk device path.
+**/
+VOID
+BmDestroyRamDisk (
+  IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+  );
+
+/**
+  Get the next possible full path pointing to the load option.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  );
+
+/**
+  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 *
-BmGetFileBufferFromLoadFile (
-  EFI_HANDLE                          LoadFileHandle,
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+BmGetNextLoadOptionBuffer (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  OUT EFI_DEVICE_PATH_PROTOCOL          **FullPath,
+  OUT UINTN                             *FileSize
   );
 #endif // _INTERNAL_BM_H_
-- 
2.9.0.windows.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel
Re: [edk2] [PATCH] MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic
Posted by Tian, Feng 7 years, 6 months ago
Reviewed-by: Feng Tian <feng.tian@intel.com>

Thanks
Feng

-----Original Message-----
From: Ni, Ruiyu 
Sent: Thursday, April 6, 2017 5:45 PM
To: edk2-devel@lists.01.org
Cc: Tian, Feng <feng.tian@intel.com>; Dong, Eric <eric.dong@intel.com>; Fan, Jeff <jeff.fan@intel.com>
Subject: [PATCH] MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic

Old implementation only finds first matched full device path for a given short-form device path.
The patch adds internal function BmGetNextLoadOptionBuffer() to finds all matched full device path for a given short-form device path.
There are 6 kinds of device paths. Some of them match to multiple load options, some of them don't.

1. Media device path:
   Returns multiple load options: The media device path may point
   to a physical BlockIo which contains multiple logic partitions,
   each logic partitions contains \EFI\BOOT\BOOT${ARCH}.EFI.

2. Short-form hard-drive device path:
   Returns one load option because the partition signature is unique.

3. Short-form file-path device path:
   Returns multiple load options: There are multiple SimpleFileSystem
   instances and each contains the same file.

4. Short-form URI device path:
   Returns multiple load options: There are multiple LoadFile
   instances and each can boot.

5. Short-form USB device path:
   Returns multiple load options: There are multiple UsbIo instances
   and each contains the boot-able file.

6. FV device path, device path pointing to SimpleFileSystem, device
   path pointing to LoadFile
   Returns one load option.

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Feng Tian <feng.tian@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Jeff Fan <jeff.fan@intel.com>
---
 MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c   | 470 ++++++++++++---------
 .../Library/UefiBootManagerLib/BmLoadOption.c      | 173 ++++++--
 .../Library/UefiBootManagerLib/InternalBm.h        | 100 +++--
 3 files changed, 475 insertions(+), 268 deletions(-)

diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
index 8a3a402..aa79c90 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
@@ -131,21 +131,16 @@ BmFindBootOptionInVariable (  }
 
 /**
-  Get the file buffer using a Memory Mapped Device Path.
-
+  Return the correct FV file path.
   FV address may change across reboot. This routine promises the FV file device path is right.
 
   @param  FilePath     The Memory Mapped Device Path to get the file buffer.
-  @param  FullPath     Receive the updated FV Device Path pointint to the file.
-  @param  FileSize     Receive the file buffer size.
 
-  @return  The file buffer.
+  @return  The updated FV Device Path pointint to the file.
 **/
-VOID *
-BmGetFileBufferByFvFilePath (
-  IN EFI_DEVICE_PATH_PROTOCOL      *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL     **FullPath,
-  OUT UINTN                        *FileSize
+EFI_DEVICE_PATH_PROTOCOL *
+BmAdjustFvFilePath (
+  IN EFI_DEVICE_PATH_PROTOCOL      *FilePath
   )
 {
   EFI_STATUS                    Status;
@@ -153,11 +148,10 @@ BmGetFileBufferByFvFilePath (
   EFI_DEVICE_PATH_PROTOCOL      *FvFileNode;
   EFI_HANDLE                    FvHandle;
   EFI_LOADED_IMAGE_PROTOCOL     *LoadedImage;
-  UINT32                        AuthenticationStatus;
   UINTN                         FvHandleCount;
   EFI_HANDLE                    *FvHandles;
   EFI_DEVICE_PATH_PROTOCOL      *NewDevicePath;
-  VOID                          *FileBuffer;
+  EFI_DEVICE_PATH_PROTOCOL      *FullPath;
 
   //
   // Get the file buffer by using the exactly FilePath.
@@ -165,11 +159,7 @@ BmGetFileBufferByFvFilePath (
   FvFileNode = FilePath;
   Status = gBS->LocateDevicePath (&gEfiFirmwareVolume2ProtocolGuid, &FvFileNode, &FvHandle);
   if (!EFI_ERROR (Status)) {
-    FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer != NULL) {
-      *FullPath = DuplicateDevicePath (FilePath);
-    }
-    return FileBuffer;
+    return DuplicateDevicePath (FilePath);
   }
 
   //
@@ -190,11 +180,10 @@ BmGetFileBufferByFvFilePath (
          (VOID **) &LoadedImage
          );
   NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (LoadedImage->DeviceHandle), FvFileNode);
-  FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
+  FullPath = BmAdjustFvFilePath (NewDevicePath);
   FreePool (NewDevicePath);
-
-  if (FileBuffer != NULL) {
-    return FileBuffer;
+  if (FullPath != NULL) {
+    return FullPath;
   }
 
   //
@@ -207,22 +196,25 @@ BmGetFileBufferByFvFilePath (
          &FvHandleCount,
          &FvHandles
          );
-  for (Index = 0; (Index < FvHandleCount) && (FileBuffer == NULL); Index++) {
+  for (Index = 0; Index < FvHandleCount; Index++) {
     if (FvHandles[Index] == LoadedImage->DeviceHandle) {
       //
-      // Skip current FV
+      // Skip current FV, it was handed in first step.
       //
       continue;
     }
     NewDevicePath = AppendDevicePathNode (DevicePathFromHandle (FvHandles[Index]), FvFileNode);
-    FileBuffer = BmGetFileBufferByFvFilePath (NewDevicePath, FullPath, FileSize);
+    FullPath = BmAdjustFvFilePath (NewDevicePath);
     FreePool (NewDevicePath);
+    if (FullPath != NULL) {
+      break;
+    }
   }
   
   if (FvHandles != NULL) {
     FreePool (FvHandles);
   }
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
@@ -555,46 +547,64 @@ BmFindUsbDevice (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
   @param ShortformNode Pointer to the USB short-form device path node in the FilePath buffer.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandUsbDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL  **FullPath,
-  OUT UINTN                     *FileSize,
-  IN EFI_DEVICE_PATH_PROTOCOL   *ShortformNode
+  IN  EFI_DEVICE_PATH_PROTOCOL  *FullPath,  IN  
+ EFI_DEVICE_PATH_PROTOCOL  *ShortformNode
   )
 {
   UINTN                             ParentDevicePathSize;
   EFI_DEVICE_PATH_PROTOCOL          *RemainingDevicePath;
-  EFI_DEVICE_PATH_PROTOCOL          *FullDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL          *NextFullPath;
   EFI_HANDLE                        *Handles;
   UINTN                             HandleCount;
   UINTN                             Index;
-  VOID                              *FileBuffer;
+  BOOLEAN                           GetNext;
 
+  NextFullPath = NULL;
+  GetNext = (BOOLEAN)(FullPath == NULL);
   ParentDevicePathSize = (UINTN) ShortformNode - (UINTN) FilePath;
   RemainingDevicePath = NextDevicePathNode (ShortformNode);
-  FileBuffer = NULL;
   Handles = BmFindUsbDevice (FilePath, ParentDevicePathSize, &HandleCount);
 
-  for (Index = 0; (Index < HandleCount) && (FileBuffer == NULL); Index++) {
-    FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
-    FileBuffer = EfiBootManagerGetLoadOptionBuffer (FullDevicePath, FullPath, FileSize);
-    FreePool (FullDevicePath);
+  for (Index = 0; Index < HandleCount; Index++) {
+    FilePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), RemainingDevicePath);
+    if (FilePath == NULL) {
+      //
+      // Out of memory.
+      //
+      continue;
+    }
+    NextFullPath = BmGetNextLoadOptionDevicePath (FilePath, NULL);
+    FreePool (FilePath);
+    if (NextFullPath == NULL) {
+      //
+      // No BlockIo or SimpleFileSystem under FilePath.
+      //
+      continue;
+    }
+    if (GetNext) {
+      break;
+    } else {
+      GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+      FreePool (NextFullPath);
+      NextFullPath = NULL;
+    }
   }
 
   if (Handles != NULL) {
     FreePool (Handles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -602,18 +612,16 @@ BmExpandUsbDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandFileDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL    *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL    **FullPath,
-  OUT UINTN                       *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL    *FullPath
   )
 {
   EFI_STATUS                      Status;
@@ -622,9 +630,8 @@ BmExpandFileDevicePath (
   EFI_HANDLE                      *Handles;
   EFI_BLOCK_IO_PROTOCOL           *BlockIo;
   UINTN                           MediaType;
-  EFI_DEVICE_PATH_PROTOCOL        *FullDevicePath;
-  VOID                            *FileBuffer;
-  UINT32                          AuthenticationStatus;
+  EFI_DEVICE_PATH_PROTOCOL        *NextFullPath;
+  BOOLEAN                         GetNext;
   
   EfiBootManagerConnectAll ();
   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiSimpleFileSystemProtocolGuid, NULL, &HandleCount, &Handles); @@ -633,6 +640,8 @@ BmExpandFileDevicePath (
     Handles = NULL;
   }
 
+  GetNext = (BOOLEAN)(FullPath == NULL);  NextFullPath = NULL;
   //
   // Enumerate all removable media devices followed by all fixed media devices,
   //   followed by media devices which don't layer on block io.
@@ -647,24 +656,26 @@ BmExpandFileDevicePath (
           (MediaType == 1 && BlockIo != NULL && !BlockIo->Media->RemovableMedia) ||
           (MediaType == 2 && BlockIo == NULL)
           ) {
-        FullDevicePath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
-        FileBuffer = GetFileBufferByFilePath (TRUE, FullDevicePath, FileSize, &AuthenticationStatus);
-        if (FileBuffer != NULL) {
-          *FullPath = FullDevicePath;
-          FreePool (Handles);
-          return FileBuffer;
+        NextFullPath = AppendDevicePath (DevicePathFromHandle (Handles[Index]), FilePath);
+        if (GetNext) {
+          break;
+        } else {
+          GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+          FreePool (NextFullPath);
+          NextFullPath = NULL;
         }
-        FreePool (FullDevicePath);
       }
     }
+    if (NextFullPath != NULL) {
+      break;
+    }
   }
 
   if (Handles != NULL) {
     FreePool (Handles);
   }
 
-  *FullPath = NULL;
-  return NULL;
+  return NextFullPath;
 }
 
 /**
@@ -672,25 +683,25 @@ BmExpandFileDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
+  @param FullPath      The full path returned by the routine in last call.
+                       Set to NULL in first call.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandUriDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL    *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL    **FullPath,
-  OUT UINTN                       *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL    *FullPath
   )
 {
   EFI_STATUS                      Status;
   UINTN                           Index;
   UINTN                           HandleCount;
   EFI_HANDLE                      *Handles;
-  VOID                            *FileBuffer;
+  EFI_DEVICE_PATH_PROTOCOL        *NextFullPath;
+  EFI_DEVICE_PATH_PROTOCOL        *RamDiskDevicePath;
+  BOOLEAN                         GetNext;
 
   EfiBootManagerConnectAll ();
   Status = gBS->LocateHandleBuffer (ByProtocol, &gEfiLoadFileProtocolGuid, NULL, &HandleCount, &Handles); @@ -699,11 +710,29 @@ BmExpandUriDevicePath (
     Handles = NULL;
   }
 
-  FileBuffer = NULL;
+  NextFullPath = NULL;
+  GetNext = (BOOLEAN)(FullPath == NULL);
   for (Index = 0; Index < HandleCount; Index++) {
-    FileBuffer = BmGetFileBufferFromLoadFile (Handles[Index], FilePath, FullPath, FileSize);
-    if (FileBuffer != NULL) {
+    NextFullPath = BmExpandLoadFile (Handles[Index], FilePath);
+
+    if (NextFullPath == NULL) {
+      continue;
+    }
+
+    if (GetNext) {
       break;
+    } else {
+      GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+      //
+      // Free the resource occupied by the RAM disk.
+      //
+      RamDiskDevicePath = BmGetRamDiskDevicePath (NextFullPath);
+      if (RamDiskDevicePath != NULL) {
+        BmDestroyRamDisk (RamDiskDevicePath);
+        FreePool (RamDiskDevicePath);
+      }
+      FreePool (NextFullPath);
+      NextFullPath = NULL;
     }
   }
 
@@ -711,7 +740,7 @@ BmExpandUriDevicePath (
     FreePool (Handles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -781,35 +810,28 @@ BmCachePartitionDevicePath (
 
   @param FilePath      The device path pointing to a load option.
                        It could be a short-form device path.
-  @param FullPath      Return the full device path of the load option after
-                       short-form device path expanding.
-                       Caller is responsible to free it.
-  @param FileSize      Return the load option size.
 
-  @return The load option buffer. Caller is responsible to free the memory.
+  @return The full device path pointing to the load option.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandPartitionDevicePath (
-  IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL  **FullPath,
-  OUT UINTN                     *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL  *FilePath
   )
 {
   EFI_STATUS                Status;
   UINTN                     BlockIoHandleCount;
   EFI_HANDLE                *BlockIoBuffer;
-  VOID                      *FileBuffer;
   EFI_DEVICE_PATH_PROTOCOL  *BlockIoDevicePath;
   UINTN                     Index;
   EFI_DEVICE_PATH_PROTOCOL  *CachedDevicePath;
   EFI_DEVICE_PATH_PROTOCOL  *TempNewDevicePath;
   EFI_DEVICE_PATH_PROTOCOL  *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL  *FullPath;
   UINTN                     CachedDevicePathSize;
   BOOLEAN                   NeedAdjust;
   EFI_DEVICE_PATH_PROTOCOL  *Instance;
   UINTN                     Size;
 
-  FileBuffer = NULL;
   //
   // Check if there is prestore 'HDDP' variable.
   // If exist, search the front path which point to partition node in the variable instants.
@@ -833,6 +855,7 @@ BmExpandPartitionDevicePath (
     ASSERT_EFI_ERROR (Status);
   }
 
+  FullPath = NULL;
   if (CachedDevicePath != NULL) {
     TempNewDevicePath = CachedDevicePath;
     NeedAdjust = FALSE;
@@ -851,10 +874,20 @@ BmExpandPartitionDevicePath (
         Status = EfiBootManagerConnectDevicePath (Instance, NULL);
         if (!EFI_ERROR (Status)) {
           TempDevicePath = AppendDevicePath (Instance, NextDevicePathNode (FilePath));
-          FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
+          //
+          // TempDevicePath = ACPI()/PCI()/ATA()/Partition()
+          // or             = ACPI()/PCI()/ATA()/Partition()/.../A.EFI
+          //
+          // When TempDevicePath = ACPI()/PCI()/ATA()/Partition(),
+          // it may expand to two potienal full paths (nested partition, rarely happen):
+          //   1. ACPI()/PCI()/ATA()/Partition()/Partition(A1)/EFI/BootX64.EFI
+          //   2. ACPI()/PCI()/ATA()/Partition()/Partition(A2)/EFI/BootX64.EFI
+          // For simplicity, only #1 is returned.
+          //
+          FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, 
+ NULL);
           FreePool (TempDevicePath);
 
-          if (FileBuffer != NULL) {
+          if (FullPath != NULL) {
             //
             // Adjust the 'HDDP' instances sequence if the matched one is not first one.
             //
@@ -875,7 +908,7 @@ BmExpandPartitionDevicePath (
 
             FreePool (Instance);
             FreePool (CachedDevicePath);
-            return FileBuffer;
+            return FullPath;
           }
         }
       }
@@ -911,10 +944,10 @@ BmExpandPartitionDevicePath (
       // Find the matched partition device path
       //
       TempDevicePath = AppendDevicePath (BlockIoDevicePath, NextDevicePathNode (FilePath));
-      FileBuffer = EfiBootManagerGetLoadOptionBuffer (TempDevicePath, FullPath, FileSize);
+      FullPath = BmGetNextLoadOptionDevicePath (TempDevicePath, NULL);
       FreePool (TempDevicePath);
 
-      if (FileBuffer != NULL) {
+      if (FullPath != NULL) {
         BmCachePartitionDevicePath (&CachedDevicePath, BlockIoDevicePath);
 
         //
@@ -940,7 +973,7 @@ BmExpandPartitionDevicePath (
   if (BlockIoBuffer != NULL) {
     FreePool (BlockIoBuffer);
   }
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
@@ -948,16 +981,16 @@ BmExpandPartitionDevicePath (
   by appending EFI_REMOVABLE_MEDIA_FILE_NAME.
 
   @param DevicePath  The media device path pointing to a BlockIo or SimpleFileSystem instance.
-  @param FullPath    Return the full device path pointing to the load option.
-  @param FileSize    Return the size of the load option.
+  @param FullPath    The full path returned by the routine in last call.
+                     Set to NULL in first call.
 
-  @return  The load option buffer.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
+EFI_DEVICE_PATH_PROTOCOL *
 BmExpandMediaDevicePath (
   IN  EFI_DEVICE_PATH_PROTOCOL        *DevicePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FullPath
   )
 {
   EFI_STATUS                          Status;
@@ -965,14 +998,15 @@ BmExpandMediaDevicePath (
   EFI_BLOCK_IO_PROTOCOL               *BlockIo;
   VOID                                *Buffer;
   EFI_DEVICE_PATH_PROTOCOL            *TempDevicePath;
+  EFI_DEVICE_PATH_PROTOCOL            *NextFullPath;
   UINTN                               Size;
   UINTN                               TempSize;
   EFI_HANDLE                          *SimpleFileSystemHandles;
   UINTN                               NumberSimpleFileSystemHandles;
   UINTN                               Index;
-  VOID                                *FileBuffer;
-  UINT32                              AuthenticationStatus;
+  BOOLEAN                             GetNext;
 
+  GetNext = (BOOLEAN)(FullPath == NULL);
   //
   // Check whether the device is connected
   //
@@ -981,14 +1015,16 @@ BmExpandMediaDevicePath (
   if (!EFI_ERROR (Status)) {
     ASSERT (IsDevicePathEnd (TempDevicePath));
 
-    TempDevicePath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
-    FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer == NULL) {
-      FreePool (TempDevicePath);
-      TempDevicePath = NULL;
+    NextFullPath = FileDevicePath (Handle, EFI_REMOVABLE_MEDIA_FILE_NAME);
+    //
+    // For device path pointing to simple file system, it only expands to one full path.
+    //
+    if (GetNext) {
+      return NextFullPath;
+    } else {
+      FreePool (NextFullPath);
+      return NULL;
     }
-    *FullPath = TempDevicePath;
-    return FileBuffer;
   }
 
   Status = gBS->LocateDevicePath (&gEfiBlockIoProtocolGuid, &TempDevicePath, &Handle); @@ -1025,8 +1061,7 @@ BmExpandMediaDevicePath (
   //
   // Detect the the default boot file from removable Media
   //
-  FileBuffer = NULL;
-  *FullPath = NULL;
+  NextFullPath = NULL;
   Size = GetDevicePathSize (DevicePath) - END_DEVICE_PATH_LENGTH;
   gBS->LocateHandleBuffer (
          ByProtocol,
@@ -1045,13 +1080,14 @@ BmExpandMediaDevicePath (
     // Check whether the device path of boot option is part of the SimpleFileSystem handle's device path
     //
     if ((Size <= TempSize) && (CompareMem (TempDevicePath, DevicePath, Size) == 0)) {
-      TempDevicePath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
-      FileBuffer = GetFileBufferByFilePath (TRUE, TempDevicePath, FileSize, &AuthenticationStatus);
-      if (FileBuffer != NULL) {
-        *FullPath = TempDevicePath;
+      NextFullPath = FileDevicePath (SimpleFileSystemHandles[Index], EFI_REMOVABLE_MEDIA_FILE_NAME);
+      if (GetNext) {
         break;
+      } else {
+        GetNext = (BOOLEAN)(CompareMem (NextFullPath, FullPath, GetDevicePathSize (NextFullPath)) == 0);
+        FreePool (NextFullPath);
+        NextFullPath = NULL;
       }
-      FreePool (TempDevicePath);
     }
   }
 
@@ -1059,7 +1095,7 @@ BmExpandMediaDevicePath (
     FreePool (SimpleFileSystemHandles);
   }
 
-  return FileBuffer;
+  return NextFullPath;
 }
 
 /**
@@ -1098,17 +1134,14 @@ BmMatchHttpBootDevicePath (
   Get the file buffer from the file system produced by Load File instance.
 
   @param LoadFileHandle The handle of LoadFile instance.
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
   @param RamDiskHandle  Return the RAM Disk handle.
 
-  @return  The load option buffer.
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
 **/
-VOID *
-BmGetFileBufferFromLoadFileSystem (
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandNetworkFileSystem (
   IN  EFI_HANDLE                      LoadFileHandle,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize,
   OUT EFI_HANDLE                      *RamDiskHandle
   )
 {
@@ -1138,6 +1171,9 @@ BmGetFileBufferFromLoadFileSystem (
     if (!EFI_ERROR (Status) &&
         (Handle == LoadFileHandle) &&
         (DevicePathType (Node) == MEDIA_DEVICE_PATH) && (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)) {
+      //
+      // Find the BlockIo instance populated from the LoadFile.
+      //
       Handle = Handles[Index];
       break;
     }
@@ -1154,13 +1190,16 @@ BmGetFileBufferFromLoadFileSystem (
   *RamDiskHandle = Handle;
 
   if (Handle != NULL) {
-    return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), FullPath, FileSize);
+    //
+    // Re-use BmExpandMediaDevicePath() to get the full device path of load option.
+    // But assume only one SimpleFileSystem can be found under the BlockIo.
+    //
+    return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), 
+ NULL);
   } else {
     return NULL;
   }
 }
 
-
 /**
   Return the RAM Disk device path created by LoadFile.
 
@@ -1274,27 +1313,21 @@ BmDestroyRamDisk (
 
   @param LoadFileHandle The specified Load File instance.
   @param FilePath       The file path which will pass to LoadFile().
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
 
-  @return  The load option buffer or NULL if fails.
+  @return  The full device path pointing to the load option buffer.
 **/
-VOID *
-BmGetFileBufferFromLoadFile (
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
   IN  EFI_HANDLE                      LoadFileHandle,
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
   )
 {
   EFI_STATUS                          Status;
   EFI_LOAD_FILE_PROTOCOL              *LoadFile;
   VOID                                *FileBuffer;
-  BOOLEAN                             LoadFileSystem;
   EFI_HANDLE                          RamDiskHandle;
   UINTN                               BufferSize;
-
-  *FileSize = 0;
+  EFI_DEVICE_PATH_PROTOCOL            *FullPath;
 
   Status = gBS->OpenProtocol (
                   LoadFileHandle,
@@ -1313,52 +1346,60 @@ BmGetFileBufferFromLoadFile (
     return NULL;
   }
 
-  LoadFileSystem = (BOOLEAN) (Status == EFI_WARN_FILE_SYSTEM);
-  FileBuffer = LoadFileSystem ? AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize)) : AllocatePool (BufferSize);
+  if (Status == EFI_BUFFER_TOO_SMALL) {
+    //
+    // The load option buffer is directly returned by LoadFile.
+    //
+    return DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));  
+ }
+
+  //
+  // The load option resides in a RAM disk.
+  //
+  FileBuffer = AllocateReservedPages (EFI_SIZE_TO_PAGES (BufferSize));
   if (FileBuffer == NULL) {
     return NULL;
   }
 
   Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);
   if (EFI_ERROR (Status)) {
-    if (LoadFileSystem) {
-      FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
-    } else {
-      FreePool (FileBuffer);
-    }
+    FreePages (FileBuffer, EFI_SIZE_TO_PAGES (BufferSize));
     return NULL;
   }
 
-  if (LoadFileSystem) {
-    FileBuffer = BmGetFileBufferFromLoadFileSystem (LoadFileHandle, FullPath, FileSize, &RamDiskHandle);
-    if (FileBuffer == NULL) {
-      //
-      // If there is no bootable executable in the populated
-      //
-      BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
-    }
-  } else {
-    *FileSize = BufferSize;
-    *FullPath = DuplicateDevicePath (DevicePathFromHandle (LoadFileHandle));
+  FullPath = BmExpandNetworkFileSystem (LoadFileHandle, 
+ &RamDiskHandle);  if (FullPath == NULL) {
+    //
+    // Free the memory occupied by the RAM disk if there is no BlockIo or SimpleFileSystem instance.
+    //
+    BmDestroyRamDisk (DevicePathFromHandle (RamDiskHandle));
   }
 
-  return FileBuffer;
+  return FullPath;
 }
 
 /**
-  Get the file buffer from all the Load File instances.
+  Return the full device path pointing to the load option.
+
+  FilePath may:
+  1. Exactly matches to a LoadFile instance.
+  2. Cannot match to any LoadFile instance. Wide match is required.
+  In either case, the routine may return:
+  1. A copy of FilePath when FilePath matches to a LoadFile instance and
+     the LoadFile returns a load option buffer.
+  2. A new device path with IP and URI information updated when wide match
+     happens.
+  3. A new device path pointing to a load option in RAM disk.
+  In either case, only one full device path is returned for a specified  
+ FilePath.
 
   @param FilePath    The media device path pointing to a LoadFile instance.
-  @param FullPath    Return the full device path pointing to the load option.
-  @param FileSize    Return the size of the load option.
 
   @return  The load option buffer.
 **/
-VOID *
-BmGetFileBufferFromLoadFiles (
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFiles (
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
   )
 {
   EFI_STATUS                      Status;
@@ -1411,7 +1452,7 @@ BmGetFileBufferFromLoadFiles (
     return NULL;
   }
 
-  return BmGetFileBufferFromLoadFile (Handle, FilePath, FullPath, FileSize);
+  return BmExpandLoadFile (Handle, FilePath);
 }
 
 /**
@@ -1434,19 +1475,37 @@ EfiBootManagerGetLoadOptionBuffer (
   OUT UINTN                             *FileSize
   )
 {
+  *FullPath = NULL;
+
+  EfiBootManagerConnectDevicePath (FilePath, NULL);
+  return BmGetNextLoadOptionBuffer (LoadOptionTypeMax, FilePath, 
+FullPath, FileSize); }
+
+/**
+  Get the next possible full path pointing to the load option.
+  The routine doesn't guarantee the returned full path points to an 
+existing
+  file, and it also doesn't guarantee the existing file is a valid load option.
+  BmGetNextLoadOptionBuffer() guarantees.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  )
+{
   EFI_HANDLE                      Handle;
-  VOID                            *FileBuffer;
-  UINT32                          AuthenticationStatus;
   EFI_DEVICE_PATH_PROTOCOL        *Node;
   EFI_STATUS                      Status;
 
-  ASSERT ((FilePath != NULL) && (FullPath != NULL) && (FileSize != NULL));
-
-  EfiBootManagerConnectDevicePath (FilePath, NULL);
-
-  *FullPath  = NULL;
-  *FileSize  = 0;
-  FileBuffer = NULL;
+  ASSERT (FilePath != NULL);
 
   //
   // Boot from media device by adding a default file name \EFI\BOOT\BOOT{machine type short-name}.EFI @@ -1458,7 +1517,7 @@ EfiBootManagerGetLoadOptionBuffer (
   }
 
   if (!EFI_ERROR (Status) && IsDevicePathEnd (Node)) {
-    return BmExpandMediaDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandMediaDevicePath (FilePath, FullPath);
   }
 
   //
@@ -1469,19 +1528,23 @@ EfiBootManagerGetLoadOptionBuffer (
     //
     // Expand the Harddrive device path
     //
-    return BmExpandPartitionDevicePath (FilePath, FullPath, FileSize);
+    if (FullPath == NULL) {
+      return BmExpandPartitionDevicePath (FilePath);
+    } else {
+      return NULL;
+    }
   } else if ((DevicePathType (FilePath) == MEDIA_DEVICE_PATH) &&
              (DevicePathSubType (FilePath) == MEDIA_FILEPATH_DP)) {
     //
     // Expand the File-path device path
     //
-    return BmExpandFileDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandFileDevicePath (FilePath, FullPath);
   } else if ((DevicePathType (FilePath) == MESSAGING_DEVICE_PATH) &&
              (DevicePathSubType (FilePath) == MSG_URI_DP)) {
     //
     // Expand the URI device path
     //
-    return BmExpandUriDevicePath (FilePath, FullPath, FileSize);
+    return BmExpandUriDevicePath (FilePath, FullPath);
   } else {
     for (Node = FilePath; !IsDevicePathEnd (Node); Node = NextDevicePathNode (Node)) {
       if ((DevicePathType (Node) == MESSAGING_DEVICE_PATH) && @@ -1490,45 +1553,50 @@ EfiBootManagerGetLoadOptionBuffer (
       }
     }
 
+    //
+    // Expand the USB WWID/Class device path
+    //
     if (!IsDevicePathEnd (Node)) {
-      //
-      // Expand the USB WWID/Class device path
-      //
-      FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
-      if ((FileBuffer == NULL) && (FilePath == Node)) {
+      if (FilePath == Node) {
         //
         // Boot Option device path starts with USB Class or USB WWID device path.
         // For Boot Option device path which doesn't begin with the USB Class or
         // USB WWID device path, it's not needed to connect again here.
         //
         BmConnectUsbShortFormDevicePath (FilePath);
-        FileBuffer = BmExpandUsbDevicePath (FilePath, FullPath, FileSize, Node);
       }
-      return FileBuffer;
+      return BmExpandUsbDevicePath (FilePath, FullPath, Node);
     }
   }
 
   //
-  // Get file buffer from FV file path.
+  // For the below cases, FilePath only expands to one Full path.
+  // So just handle the case when FullPath == NULL.
+  //
+  if (FullPath != NULL) {
+    return NULL;
+  }
+
+  //
+  // Load option resides in FV.
   //
   if (BmIsFvFilePath (FilePath)) {
-    return BmGetFileBufferByFvFilePath (FilePath, FullPath, FileSize);
+    return BmAdjustFvFilePath (FilePath);
   }
 
   //
-  // Get file buffer from simple file system.
+  // Load option resides in Simple File System.
   //
   Node   = FilePath;
   Status = gBS->LocateDevicePath (&gEfiSimpleFileSystemProtocolGuid, &Node, &Handle);
   if (!EFI_ERROR (Status)) {
-    FileBuffer = GetFileBufferByFilePath (TRUE, FilePath, FileSize, &AuthenticationStatus);
-    if (FileBuffer != NULL) {
-      *FullPath = DuplicateDevicePath (FilePath);
-    }
-    return FileBuffer;
+    return DuplicateDevicePath (FilePath);
   }
 
-  return BmGetFileBufferFromLoadFiles (FilePath, FullPath, FileSize);
+  //
+  // Last chance to try: Load option may be loaded through LoadFile.
+  //
+  return BmExpandLoadFiles (FilePath);
 }
 
 /**
@@ -1686,21 +1754,13 @@ EfiBootManagerBoot (
   ImageHandle       = NULL;
   RamDiskDevicePath = NULL;
   if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {
-    Status     = EFI_NOT_FOUND;
-    FileBuffer = EfiBootManagerGetLoadOptionBuffer (BootOption->FilePath, &FilePath, &FileSize);
+    Status   = EFI_NOT_FOUND;
+    FilePath = NULL;
+    EfiBootManagerConnectDevicePath (BootOption->FilePath, NULL);
+    FileBuffer = BmGetNextLoadOptionBuffer (LoadOptionTypeBoot, 
+ BootOption->FilePath, &FilePath, &FileSize);
     if (FileBuffer != NULL) {
       RamDiskDevicePath = BmGetRamDiskDevicePath (FilePath);
-    }
-    DEBUG_CODE (
-      if (FileBuffer != NULL && CompareMem (BootOption->FilePath, FilePath, GetDevicePathSize (FilePath)) != 0) {
-        DEBUG ((EFI_D_INFO, "[Bds] DevicePath expand: "));
-        BmPrintDp (BootOption->FilePath);
-        DEBUG ((EFI_D_INFO, " -> "));
-        BmPrintDp (FilePath);
-        DEBUG ((EFI_D_INFO, "\n"));
-      }
-    );
-    if (BmIsLoadOptionPeHeaderValid (BootOption->OptionType, FileBuffer, FileSize)) {
+
       REPORT_STATUS_CODE (EFI_PROGRESS_CODE, PcdGet32 (PcdProgressCodeOsLoaderLoad));
       Status = gBS->LoadImage (
                       TRUE,
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
index 116cf28..b0a3505 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
@@ -1167,6 +1167,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.
 
@@ -1217,7 +1221,8 @@ BmIsLoadOptionPeHeaderValid (
         //   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) || @@ -1233,6 +1238,91 @@ BmIsLoadOptionPeHeaderValid (  }
 
 /**
+  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.
@@ -1249,7 +1339,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;
@@ -1271,8 +1362,6 @@ EfiBootManagerProcessLoadOption (
     return EFI_SUCCESS;
   }
 
-  Status = EFI_INVALID_PARAMETER;
-
   //
   // Load and start the load option.
   //
@@ -1282,54 +1371,62 @@ EfiBootManagerProcessLoadOption (
     LoadOption->Description
     ));
   ImageHandle = NULL;
-  FileBuffer = EfiBootManagerGetLoadOptionBuffer (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)) {
+      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, "%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;
diff --git a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
index 8d7215a..ef09050 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
+++ b/MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h
@@ -310,23 +310,6 @@ BmSetVariableAndReportStatusCodeOnError (
   );
 
 /**
-  Return whether the PE header of the load option is valid or not.
-
-  @param[in] Type       The load option type.
-  @param[in] FileBuffer The PE file buffer of the load option.
-  @param[in] FileSize   The size of the load option file.
-
-  @retval TRUE  The PE header of the load option is valid.
-  @retval FALSE The PE header of the load option is not valid.
-**/
-BOOLEAN
-BmIsLoadOptionPeHeaderValid (
-  IN EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
-  IN VOID                              *FileBuffer,
-  IN UINTN                             FileSize
-  );
-
-/**
   Function compares a device path data structure to that of all the nodes of a
   second device path instance.
 
@@ -425,16 +408,83 @@ BmMakeBootOptionDescriptionUnique (
 
   @param LoadFileHandle The specified Load File instance.
   @param FilePath       The file path which will pass to LoadFile().
-  @param FullPath       Return the full device path pointing to the load option.
-  @param FileSize       Return the size of the load option.
 
-  @return  The load option buffer or NULL if fails.
+  @return  The full device path pointing to the load option buffer.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmExpandLoadFile (
+  IN  EFI_HANDLE                      LoadFileHandle,
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath
+  );
+
+/**
+  Return the RAM Disk device path created by LoadFile.
+
+  @param FilePath  The source file path.
+
+  @return Callee-to-free RAM Disk device path **/ 
+EFI_DEVICE_PATH_PROTOCOL * BmGetRamDiskDevicePath (
+  IN EFI_DEVICE_PATH_PROTOCOL *FilePath
+  );
+
+/**
+  Destroy the RAM Disk.
+
+  The destroy operation includes to call RamDisk.Unregister to  
+ unregister the RAM DISK from RAM DISK driver, free the memory  
+ allocated for the RAM Disk.
+
+  @param RamDiskDevicePath    RAM Disk device path.
+**/
+VOID
+BmDestroyRamDisk (
+  IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath
+  );
+
+/**
+  Get the next possible full path pointing to the load option.
+
+  @param FilePath  The device path pointing to a load option.
+                   It could be a short-form device path.
+  @param FullPath  The full path returned by the routine in last call.
+                   Set to NULL in first call.
+
+  @return The next possible full path pointing to the load option.
+          Caller is responsible to free the memory.
+**/
+EFI_DEVICE_PATH_PROTOCOL *
+BmGetNextLoadOptionDevicePath (
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FullPath
+  );
+
+/**
+  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 *
-BmGetFileBufferFromLoadFile (
-  EFI_HANDLE                          LoadFileHandle,
-  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,
-  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,
-  OUT UINTN                           *FileSize
+BmGetNextLoadOptionBuffer (
+  IN  EFI_BOOT_MANAGER_LOAD_OPTION_TYPE Type,
+  IN  EFI_DEVICE_PATH_PROTOCOL          *FilePath,
+  OUT EFI_DEVICE_PATH_PROTOCOL          **FullPath,
+  OUT UINTN                             *FileSize
   );
 #endif // _INTERNAL_BM_H_
--
2.9.0.windows.1

_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel