]> git.proxmox.com Git - mirror_edk2.git/commitdiff
MdeModulePkg/UefiBootManagerLib: Enhance short-form expanding logic
authorRuiyu Ni <ruiyu.ni@intel.com>
Wed, 29 Mar 2017 02:55:43 +0000 (10:55 +0800)
committerRuiyu Ni <ruiyu.ni@intel.com>
Fri, 7 Apr 2017 03:06:12 +0000 (11:06 +0800)
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>
Reviewed-by: 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
MdeModulePkg/Library/UefiBootManagerLib/BmLoadOption.c
MdeModulePkg/Library/UefiBootManagerLib/InternalBm.h

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