]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
MdeModulePkg/Bds: Fix a boot hang due to Ram Disk boot support
[mirror_edk2.git] / MdeModulePkg / Library / UefiBootManagerLib / BmBoot.c
index f6c68458abb11f3de8fcd34917167962fdf750d6..2eb8971ba0f6b709bdbc6967458be152aff57b2a 100644 (file)
@@ -15,6 +15,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 \r
 #include "InternalBm.h"\r
 \r
+EFI_RAM_DISK_PROTOCOL                        *mRamDisk                  = NULL;\r
+\r
 EFI_BOOT_MANAGER_REFRESH_LEGACY_BOOT_OPTION  mBmRefreshLegacyBootOption = NULL;\r
 EFI_BOOT_MANAGER_LEGACY_BOOT                 mBmLegacyBoot              = NULL;\r
 \r
@@ -688,7 +690,6 @@ BmExpandUriDevicePath (
   UINTN                           Index;\r
   UINTN                           HandleCount;\r
   EFI_HANDLE                      *Handles;\r
-  EFI_LOAD_FILE_PROTOCOL          *LoadFile;\r
   VOID                            *FileBuffer;\r
 \r
   EfiBootManagerConnectAll ();\r
@@ -698,37 +699,10 @@ BmExpandUriDevicePath (
     Handles = NULL;\r
   }\r
 \r
-  FileBuffer = NULL;\r
-\r
   for (Index = 0; Index < HandleCount; Index++) {\r
-    Status = gBS->HandleProtocol (Handles[Index], &gEfiLoadFileProtocolGuid, (VOID *) &LoadFile);\r
-    ASSERT_EFI_ERROR (Status);\r
-\r
-    FileBuffer = NULL;\r
-    Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, FileSize, FileBuffer);\r
-    if (Status == EFI_BUFFER_TOO_SMALL) {\r
-      FileBuffer = AllocatePool (*FileSize);\r
-      if (FileBuffer == NULL) {\r
-        break;\r
-      }\r
-      Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, FileSize, FileBuffer);\r
-    }\r
-\r
-    if (!EFI_ERROR (Status)) {\r
-      //\r
-      // LoadFile() returns a file buffer mapping to a file system.\r
-      //\r
-      if (Status == EFI_WARN_FILE_SYSTEM) {\r
-        return BmGetFileBufferFromLoadFileFileSystem (Handles[Index], FullPath, FileSize);\r
-      }\r
-\r
-      ASSERT (Status == EFI_SUCCESS);\r
-      *FullPath = DuplicateDevicePath (DevicePathFromHandle (Handles[Index]));\r
-      break;\r
-    }\r
-\r
+    FileBuffer = BmGetFileBufferFromLoadFile (Handles[Index], FilePath, FullPath, FileSize);\r
     if (FileBuffer != NULL) {\r
-      FreePool (FileBuffer);\r
+      break;\r
     }\r
   }\r
 \r
@@ -1123,14 +1097,16 @@ BmMatchHttpBootDevicePath (
   @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
 **/\r
 VOID *\r
-BmGetFileBufferFromLoadFileFileSystem (\r
+BmGetFileBufferFromLoadFileSystem (\r
   IN  EFI_HANDLE                      LoadFileHandle,\r
   OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,\r
-  OUT UINTN                           *FileSize\r
+  OUT UINTN                           *FileSize,\r
+  OUT EFI_HANDLE                      *RamDiskHandle\r
   )\r
 {\r
   EFI_STATUS                      Status;\r
@@ -1168,15 +1144,206 @@ BmGetFileBufferFromLoadFileFileSystem (
     FreePool (Handles);\r
   }\r
 \r
-  if (Index != HandleCount) {\r
+  if (Index == HandleCount) {\r
+    Handle = NULL;\r
+  }\r
+\r
+  *RamDiskHandle = Handle;\r
+\r
+  if (Handle != NULL) {\r
     return BmExpandMediaDevicePath (DevicePathFromHandle (Handle), FullPath, FileSize);\r
   } else {\r
     return NULL;\r
   }\r
 }\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
+  EFI_STATUS                  Status;\r
+  EFI_DEVICE_PATH_PROTOCOL    *RamDiskDevicePath;\r
+  EFI_DEVICE_PATH_PROTOCOL    *Node;\r
+  EFI_HANDLE                  Handle;\r
+\r
+  Node = FilePath;\r
+  Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &Node, &Handle);\r
+  if (!EFI_ERROR (Status) &&\r
+      (DevicePathType (Node) == MEDIA_DEVICE_PATH) &&\r
+      (DevicePathSubType (Node) == MEDIA_RAM_DISK_DP)\r
+      ) {\r
+\r
+    //\r
+    // Construct the device path pointing to RAM Disk\r
+    //\r
+    Node = NextDevicePathNode (Node);\r
+    RamDiskDevicePath = DuplicateDevicePath (FilePath);\r
+    ASSERT (RamDiskDevicePath != NULL);\r
+    SetDevicePathEndNode ((VOID *) ((UINTN) RamDiskDevicePath + ((UINTN) Node - (UINTN) FilePath)));\r
+    return RamDiskDevicePath;\r
+  }\r
+\r
+  return NULL;\r
+}\r
+\r
+/**\r
+  Return the buffer and buffer size occupied by the RAM Disk.\r
+\r
+  @param RamDiskDevicePath  RAM Disk device path.\r
+  @param RamDiskSizeInPages Return RAM Disk size in pages.\r
+\r
+  @retval RAM Disk buffer.\r
+**/\r
+VOID *\r
+BmGetRamDiskMemoryInfo (\r
+  IN EFI_DEVICE_PATH_PROTOCOL *RamDiskDevicePath,\r
+  OUT UINTN                   *RamDiskSizeInPages\r
+  )\r
+{\r
+\r
+  EFI_STATUS                  Status;\r
+  EFI_HANDLE                  Handle;\r
+  UINT64                      StartingAddr;\r
+  UINT64                      EndingAddr;\r
+\r
+  ASSERT (RamDiskDevicePath != NULL);\r
+\r
+  *RamDiskSizeInPages = 0;\r
+\r
+  //\r
+  // Get the buffer occupied by RAM Disk.\r
+  //\r
+  Status = gBS->LocateDevicePath (&gEfiLoadFileProtocolGuid, &RamDiskDevicePath, &Handle);\r
+  ASSERT_EFI_ERROR (Status);\r
+  ASSERT ((DevicePathType (RamDiskDevicePath) == MEDIA_DEVICE_PATH) &&\r
+          (DevicePathSubType (RamDiskDevicePath) == MEDIA_RAM_DISK_DP));\r
+  StartingAddr = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->StartingAddr);\r
+  EndingAddr   = ReadUnaligned64 ((UINT64 *) ((MEDIA_RAM_DISK_DEVICE_PATH *) RamDiskDevicePath)->EndingAddr);\r
+  *RamDiskSizeInPages = EFI_SIZE_TO_PAGES ((UINTN) (EndingAddr - StartingAddr + 1));\r
+  return (VOID *) (UINTN) StartingAddr;\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
+  EFI_STATUS                  Status;\r
+  VOID                        *RamDiskBuffer;\r
+  UINTN                       RamDiskSizeInPages;\r
+\r
+  ASSERT (RamDiskDevicePath != NULL);\r
+\r
+  RamDiskBuffer = BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);\r
+\r
+  //\r
+  // Destroy RAM Disk.\r
+  //\r
+  if (mRamDisk == NULL) {\r
+    Status = gBS->LocateProtocol (&gEfiRamDiskProtocolGuid, NULL, (VOID *) &mRamDisk);\r
+    ASSERT_EFI_ERROR (Status);\r
+  }\r
+  Status = mRamDisk->Unregister (RamDiskDevicePath);\r
+  ASSERT_EFI_ERROR (Status);\r
+  FreePages (RamDiskBuffer, RamDiskSizeInPages);\r
+}\r
+\r
+/**\r
+  Get the file buffer from the specified Load File instance.\r
+\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
+**/\r
+VOID *\r
+BmGetFileBufferFromLoadFile (\r
+  IN  EFI_HANDLE                      LoadFileHandle,\r
+  IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,\r
+  OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,\r
+  OUT UINTN                           *FileSize\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
+\r
+  Status = gBS->OpenProtocol (\r
+                  LoadFileHandle,\r
+                  &gEfiLoadFileProtocolGuid,\r
+                  (VOID **) &LoadFile,\r
+                  gImageHandle,\r
+                  NULL,\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  FileBuffer = NULL;\r
+  BufferSize = 0;\r
+  Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
+  if ((Status != EFI_WARN_FILE_SYSTEM) && (Status != EFI_BUFFER_TOO_SMALL)) {\r
+    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 (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
+    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
+  }\r
+\r
+  return FileBuffer;\r
+}\r
+\r
 /**\r
-  Get the file buffer from Load File instance.\r
+  Get the file buffer from all the Load File instances.\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
@@ -1185,7 +1352,7 @@ BmGetFileBufferFromLoadFileFileSystem (
   @return  The load option buffer.\r
 **/\r
 VOID *\r
-BmGetFileBufferFromLoadFile (\r
+BmGetFileBufferFromLoadFiles (\r
   IN  EFI_DEVICE_PATH_PROTOCOL        *FilePath,\r
   OUT EFI_DEVICE_PATH_PROTOCOL        **FullPath,\r
   OUT UINTN                           *FileSize\r
@@ -1193,13 +1360,10 @@ BmGetFileBufferFromLoadFile (
 {\r
   EFI_STATUS                      Status;\r
   EFI_HANDLE                      Handle;\r
-  VOID                            *FileBuffer;\r
   EFI_HANDLE                      *Handles;\r
   UINTN                           HandleCount;\r
   UINTN                           Index;\r
   EFI_DEVICE_PATH_PROTOCOL        *Node;\r
-  EFI_LOAD_FILE_PROTOCOL          *LoadFile;\r
-  UINTN                           BufferSize;\r
 \r
   //\r
   // Get file buffer from load file instance.\r
@@ -1244,41 +1408,7 @@ BmGetFileBufferFromLoadFile (
     return NULL;\r
   }\r
 \r
-  Status = gBS->HandleProtocol (Handle, &gEfiLoadFileProtocolGuid, (VOID **) &LoadFile);\r
-  ASSERT_EFI_ERROR (Status);\r
-\r
-  BufferSize = 0;\r
-  FileBuffer = NULL;\r
-  Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
-  if (Status == EFI_BUFFER_TOO_SMALL) {\r
-    FileBuffer = AllocatePool (BufferSize);\r
-    if (FileBuffer != NULL) {\r
-      Status = EFI_SUCCESS;\r
-    }\r
-  }\r
-\r
-  if (!EFI_ERROR (Status)) {\r
-    Status = LoadFile->LoadFile (LoadFile, FilePath, TRUE, &BufferSize, FileBuffer);\r
-  }\r
-\r
-  if (!EFI_ERROR (Status)) {\r
-    //\r
-    // LoadFile() returns a file buffer mapping to a file system.\r
-    //\r
-    if (Status == EFI_WARN_FILE_SYSTEM) {\r
-      return BmGetFileBufferFromLoadFileFileSystem (Handle, FullPath, FileSize);\r
-    }\r
-\r
-    ASSERT (Status == EFI_SUCCESS);\r
-    //\r
-    // LoadFile () may cause the device path of the Handle be updated.\r
-    //\r
-    *FullPath = DuplicateDevicePath (DevicePathFromHandle (Handle));\r
-    *FileSize = BufferSize;\r
-    return FileBuffer;\r
-  } else {\r
-    return NULL;\r
-  }\r
+  return BmGetFileBufferFromLoadFile (Handle, FilePath, FullPath, FileSize);\r
 }\r
 \r
 /**\r
@@ -1394,7 +1524,7 @@ BmGetLoadOptionBuffer (
     return FileBuffer;\r
   }\r
 \r
-  return BmGetFileBufferFromLoadFile (FilePath, FullPath, FileSize);\r
+  return BmGetFileBufferFromLoadFiles (FilePath, FullPath, FileSize);\r
 }\r
 \r
 /**\r
@@ -1431,11 +1561,13 @@ EfiBootManagerBoot (
   UINTN                     OriginalOptionNumber;\r
   EFI_DEVICE_PATH_PROTOCOL  *FilePath;\r
   EFI_DEVICE_PATH_PROTOCOL  *Node;\r
+  EFI_DEVICE_PATH_PROTOCOL  *RamDiskDevicePath;\r
   EFI_HANDLE                FvHandle;\r
   VOID                      *FileBuffer;\r
   UINTN                     FileSize;\r
   EFI_BOOT_LOGO_PROTOCOL    *BootLogo;\r
   EFI_EVENT                 LegacyBootEvent;\r
+  UINTN                     RamDiskSizeInPages;\r
 \r
   if (BootOption == NULL) {\r
     return;\r
@@ -1511,10 +1643,14 @@ EfiBootManagerBoot (
   //\r
   // 5. Load EFI boot option to ImageHandle\r
   //\r
-  ImageHandle = NULL;\r
+  ImageHandle       = NULL;\r
+  RamDiskDevicePath = NULL;\r
   if (DevicePathType (BootOption->FilePath) != BBS_DEVICE_PATH) {\r
     Status     = EFI_NOT_FOUND;\r
     FileBuffer = BmGetLoadOptionBuffer (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
@@ -1551,6 +1687,13 @@ EfiBootManagerBoot (
         (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_EC_BOOT_OPTION_LOAD_ERROR)\r
         );\r
       BootOption->Status = Status;\r
+      //\r
+      // Destroy the RAM disk\r
+      //\r
+      if (RamDiskDevicePath != NULL) {\r
+        BmDestroyRamDisk (RamDiskDevicePath);\r
+        FreePool (RamDiskDevicePath);\r
+      }\r
       return;\r
     }\r
   }\r
@@ -1559,8 +1702,14 @@ EfiBootManagerBoot (
   // 6. Adjust the different type memory page number just before booting\r
   //    and save the updated info into the variable for next boot to use\r
   //\r
+  if (RamDiskDevicePath == NULL) {\r
+    RamDiskSizeInPages = 0;\r
+  } else {\r
+    BmGetRamDiskMemoryInfo (RamDiskDevicePath, &RamDiskSizeInPages);\r
+  }\r
   BmSetMemoryTypeInformationVariable (\r
-    (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT)\r
+    (BOOLEAN) ((BootOption->Attributes & LOAD_OPTION_CATEGORY) == LOAD_OPTION_CATEGORY_BOOT),\r
+    RamDiskSizeInPages\r
     );\r
 \r
   DEBUG_CODE_BEGIN();\r
@@ -1646,6 +1795,14 @@ EfiBootManagerBoot (
   }\r
   PERF_END_EX (gImageHandle, "BdsAttempt", NULL, 0, (UINT32) OptionNumber);\r
 \r
+  //\r
+  // Destroy the RAM disk\r
+  //\r
+  if (RamDiskDevicePath != NULL) {\r
+    BmDestroyRamDisk (RamDiskDevicePath);\r
+    FreePool (RamDiskDevicePath);\r
+  }\r
+\r
   //\r
   // Clear the Watchdog Timer after the image returns\r
   //\r