]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: implement QEMU loader library for X86 with legacy fallback
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Sat, 29 Feb 2020 00:28:45 +0000 (01:28 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 5 Mar 2020 19:45:05 +0000 (19:45 +0000)
Implement another version of QemuLoadImageLib that uses LoadImage and
StartImage, but falls back to the legacy Linux loader code if that
fails. The logic in the legacy fallback routines is identical to the
current QEMU linux loader for X64 and IA32.

Note the use of the OVMF_LOADED_X86_LINUX_KERNEL protocol for the legacy
loaded image: this makes it possible to expose the LoadImage/StartImage
abstraction for the legacy loader, using the EFI paradigm of identifying
a loaded image solely by a handle.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Laszlo Ersek <lersek@redhat.com>
OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c [new file with mode: 0644]
OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf [new file with mode: 0644]

diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
new file mode 100644 (file)
index 0000000..c5bd686
--- /dev/null
@@ -0,0 +1,567 @@
+/**  @file\r
+  X86 specific implementation of QemuLoadImageLib library class interface\r
+  with support for loading mixed mode images and non-EFI stub images\r
+\r
+  Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>\r
+  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>\r
+\r
+  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Guid/QemuKernelLoaderFsMedia.h>\r
+#include <Library/DebugLib.h>\r
+#include <Library/LoadLinuxLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PrintLib.h>\r
+#include <Library/QemuFwCfgLib.h>\r
+#include <Library/QemuLoadImageLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/LoadedImage.h>\r
+#include <Protocol/OvmfLoadedX86LinuxKernel.h>\r
+\r
+#pragma pack (1)\r
+typedef struct {\r
+  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;\r
+  CHAR16                    FilePath[ARRAY_SIZE (L"kernel")];\r
+} KERNEL_FILE_DEVPATH;\r
+\r
+typedef struct {\r
+  VENDOR_DEVICE_PATH        VenMediaNode;\r
+  KERNEL_FILE_DEVPATH       FileNode;\r
+  EFI_DEVICE_PATH_PROTOCOL  EndNode;\r
+} KERNEL_VENMEDIA_FILE_DEVPATH;\r
+#pragma pack ()\r
+\r
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {\r
+  {\r
+    {\r
+      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,\r
+      { sizeof (VENDOR_DEVICE_PATH) }\r
+    },\r
+    QEMU_KERNEL_LOADER_FS_MEDIA_GUID\r
+  }, {\r
+    {\r
+      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,\r
+      { sizeof (KERNEL_FILE_DEVPATH) }\r
+    },\r
+    L"kernel",\r
+  }, {\r
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }\r
+  }\r
+};\r
+\r
+STATIC\r
+VOID\r
+FreeLegacyImage (\r
+  IN  OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage\r
+  )\r
+{\r
+  if (LoadedImage->SetupBuf != NULL) {\r
+    FreePages (LoadedImage->SetupBuf,\r
+      EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));\r
+  }\r
+  if (LoadedImage->KernelBuf != NULL) {\r
+    FreePages (LoadedImage->KernelBuf,\r
+      EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));\r
+  }\r
+  if (LoadedImage->CommandLine != NULL) {\r
+    FreePages (LoadedImage->CommandLine,\r
+      EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));\r
+  }\r
+  if (LoadedImage->InitrdData != NULL) {\r
+    FreePages (LoadedImage->InitrdData,\r
+      EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));\r
+  }\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+QemuLoadLegacyImage (\r
+  OUT EFI_HANDLE                  *ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS                      Status;\r
+  UINTN                           KernelSize;\r
+  UINTN                           SetupSize;\r
+  OVMF_LOADED_X86_LINUX_KERNEL    *LoadedImage;\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);\r
+  KernelSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);\r
+  SetupSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  if (KernelSize == 0 || SetupSize == 0) {\r
+    DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  LoadedImage = AllocateZeroPool (sizeof (*LoadedImage));\r
+  if (LoadedImage == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+\r
+  LoadedImage->SetupSize = SetupSize;\r
+  LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (\r
+                            EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));\r
+  if (LoadedImage->SetupBuf == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeImageDesc;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));\r
+  DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));\r
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);\r
+  QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);\r
+  DEBUG ((DEBUG_INFO, " [done]\n"));\r
+\r
+  Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,\r
+             LoadedImage->SetupSize);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeImage;\r
+  }\r
+\r
+  Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeImage;\r
+  }\r
+\r
+  LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (\r
+                                     LoadedImage->SetupBuf, KernelSize);\r
+  if (LoadedImage->KernelInitialSize == 0) {\r
+    Status = EFI_UNSUPPORTED;\r
+    goto FreeImage;\r
+  }\r
+\r
+  LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (\r
+                             LoadedImage->SetupBuf,\r
+                             EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)\r
+                             );\r
+  if (LoadedImage->KernelBuf == NULL) {\r
+    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto FreeImage;\r
+  }\r
+\r
+  DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));\r
+  DEBUG ((DEBUG_INFO, "Reading kernel image ..."));\r
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);\r
+  QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);\r
+  DEBUG ((DEBUG_INFO, " [done]\n"));\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);\r
+  LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  if (LoadedImage->CommandLineSize > 0) {\r
+    LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (\r
+                                 EFI_SIZE_TO_PAGES (\r
+                                   LoadedImage->CommandLineSize));\r
+    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);\r
+    QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);\r
+  }\r
+\r
+  Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,\r
+             LoadedImage->CommandLine);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeImage;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);\r
+  LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  if (LoadedImage->InitrdSize > 0) {\r
+    LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (\r
+                                LoadedImage->SetupBuf,\r
+                                EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));\r
+    DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",\r
+      (UINT32)LoadedImage->InitrdSize));\r
+    DEBUG ((DEBUG_INFO, "Reading initrd image ..."));\r
+    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);\r
+    QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);\r
+    DEBUG ((DEBUG_INFO, " [done]\n"));\r
+  }\r
+\r
+  Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,\r
+             LoadedImage->InitrdSize);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeImage;\r
+  }\r
+\r
+  *ImageHandle = NULL;\r
+  Status = gBS->InstallProtocolInterface (ImageHandle,\r
+                  &gOvmfLoadedX86LinuxKernelProtocolGuid, EFI_NATIVE_INTERFACE,\r
+                  LoadedImage);\r
+  if (EFI_ERROR (Status)) {\r
+    goto FreeImage;\r
+  }\r
+  return EFI_SUCCESS;\r
+\r
+FreeImage:\r
+  FreeLegacyImage (LoadedImage);\r
+FreeImageDesc:\r
+  FreePool (LoadedImage);\r
+  return Status;\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+QemuStartLegacyImage (\r
+  IN  EFI_HANDLE                ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  OVMF_LOADED_X86_LINUX_KERNEL  *LoadedImage;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ImageHandle,\r
+                  &gOvmfLoadedX86LinuxKernelProtocolGuid,\r
+                  (VOID **)&LoadedImage,\r
+                  gImageHandle,                  // AgentHandle\r
+                  NULL,                          // ControllerHandle\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf);\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+QemuUnloadLegacyImage (\r
+  IN  EFI_HANDLE          ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  OVMF_LOADED_X86_LINUX_KERNEL  *LoadedImage;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ImageHandle,\r
+                  &gOvmfLoadedX86LinuxKernelProtocolGuid,\r
+                  (VOID **)&LoadedImage,\r
+                  gImageHandle,                  // AgentHandle\r
+                  NULL,                          // ControllerHandle\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = gBS->UninstallProtocolInterface (ImageHandle,\r
+                  &gOvmfLoadedX86LinuxKernelProtocolGuid, LoadedImage);\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  FreeLegacyImage (LoadedImage);\r
+  FreePool (LoadedImage);\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Download the kernel, the initial ramdisk, and the kernel command line from\r
+  QEMU's fw_cfg. The kernel will be instructed via its command line to load\r
+  the initrd from the same Simple FileSystem where the kernel was loaded from.\r
+\r
+  @param[out] ImageHandle       The image handle that was allocated for\r
+                                loading the image\r
+\r
+  @retval EFI_SUCCESS           The image was loaded successfully.\r
+  @retval EFI_NOT_FOUND         Kernel image was not found.\r
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.\r
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.\r
+\r
+  @return                       Error codes from any of the underlying\r
+                                functions.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuLoadKernelImage (\r
+  OUT EFI_HANDLE            *ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS                Status;\r
+  EFI_HANDLE                KernelImageHandle;\r
+  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;\r
+  UINTN                     CommandLineSize;\r
+  CHAR8                     *CommandLine;\r
+  UINTN                     InitrdSize;\r
+\r
+  //\r
+  // Load the image. This should call back into the QEMU EFI loader file system.\r
+  //\r
+  Status = gBS->LoadImage (\r
+                  FALSE,                    // BootPolicy: exact match required\r
+                  gImageHandle,             // ParentImageHandle\r
+                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,\r
+                  NULL,                     // SourceBuffer\r
+                  0,                        // SourceSize\r
+                  &KernelImageHandle\r
+                  );\r
+  switch (Status) {\r
+  case EFI_SUCCESS:\r
+    break;\r
+\r
+  case EFI_NOT_FOUND:\r
+    //\r
+    // The image does not exist - no -kernel image was supplied via the\r
+    // command line so no point in invoking the legacy fallback\r
+    //\r
+    return EFI_NOT_FOUND;\r
+\r
+  case EFI_SECURITY_VIOLATION:\r
+    //\r
+    // We are running with UEFI secure boot enabled, and the image failed to\r
+    // authenticate. For compatibility reasons, we fall back to the legacy\r
+    // loader in this case. Since the image has been loaded, we need to unload\r
+    // it before proceeding\r
+    //\r
+    gBS->UnloadImage (KernelImageHandle);\r
+    //\r
+    // Fall through\r
+    //\r
+  case EFI_UNSUPPORTED:\r
+    //\r
+    // The image is not natively supported or cross-type supported. Let's try\r
+    // loading it using the loader that parses the bzImage metadata directly.\r
+    //\r
+    Status = QemuLoadLegacyImage (&KernelImageHandle);\r
+    if (EFI_ERROR (Status)) {\r
+      DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,\r
+        Status));\r
+      return Status;\r
+    }\r
+    *ImageHandle = KernelImageHandle;\r
+    return EFI_SUCCESS;\r
+\r
+  default:\r
+    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Construct the kernel command line.\r
+  //\r
+  Status = gBS->OpenProtocol (\r
+                  KernelImageHandle,\r
+                  &gEfiLoadedImageProtocolGuid,\r
+                  (VOID **)&KernelLoadedImage,\r
+                  gImageHandle,                  // AgentHandle\r
+                  NULL,                          // ControllerHandle\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  ASSERT_EFI_ERROR (Status);\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);\r
+  CommandLineSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  if (CommandLineSize == 0) {\r
+    KernelLoadedImage->LoadOptionsSize = 0;\r
+  } else {\r
+    CommandLine = AllocatePool (CommandLineSize);\r
+    if (CommandLine == NULL) {\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto UnloadImage;\r
+    }\r
+\r
+    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);\r
+    QemuFwCfgReadBytes (CommandLineSize, CommandLine);\r
+\r
+    //\r
+    // Verify NUL-termination of the command line.\r
+    //\r
+    if (CommandLine[CommandLineSize - 1] != '\0') {\r
+      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",\r
+        __FUNCTION__));\r
+      Status = EFI_PROTOCOL_ERROR;\r
+      goto FreeCommandLine;\r
+    }\r
+\r
+    //\r
+    // Drop the terminating NUL, convert to UTF-16.\r
+    //\r
+    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;\r
+  }\r
+\r
+  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);\r
+  InitrdSize = (UINTN)QemuFwCfgRead32 ();\r
+\r
+  if (InitrdSize > 0) {\r
+    //\r
+    // Append ' initrd=initrd' in UTF-16.\r
+    //\r
+    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;\r
+  }\r
+\r
+  if (KernelLoadedImage->LoadOptionsSize == 0) {\r
+    KernelLoadedImage->LoadOptions = NULL;\r
+  } else {\r
+    //\r
+    // NUL-terminate in UTF-16.\r
+    //\r
+    KernelLoadedImage->LoadOptionsSize += 2;\r
+\r
+    KernelLoadedImage->LoadOptions = AllocatePool (\r
+                                       KernelLoadedImage->LoadOptionsSize);\r
+    if (KernelLoadedImage->LoadOptions == NULL) {\r
+      KernelLoadedImage->LoadOptionsSize = 0;\r
+      Status = EFI_OUT_OF_RESOURCES;\r
+      goto FreeCommandLine;\r
+    }\r
+\r
+    UnicodeSPrintAsciiFormat (\r
+      KernelLoadedImage->LoadOptions,\r
+      KernelLoadedImage->LoadOptionsSize,\r
+      "%a%a",\r
+      (CommandLineSize == 0) ?  "" : CommandLine,\r
+      (InitrdSize == 0)      ?  "" : " initrd=initrd"\r
+      );\r
+    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,\r
+      (CHAR16 *)KernelLoadedImage->LoadOptions));\r
+  }\r
+\r
+  *ImageHandle = KernelImageHandle;\r
+  return EFI_SUCCESS;\r
+\r
+FreeCommandLine:\r
+  if (CommandLineSize > 0) {\r
+    FreePool (CommandLine);\r
+  }\r
+UnloadImage:\r
+  gBS->UnloadImage (KernelImageHandle);\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Transfer control to a kernel image loaded with QemuLoadKernelImage ()\r
+\r
+  @param[in,out]  ImageHandle     Handle of image to be started. May assume a\r
+                                  different value on return if the image was\r
+                                  reloaded.\r
+\r
+  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle\r
+                                  or the image has already been initialized with\r
+                                  StartImage\r
+  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the\r
+                                  image should not be started.\r
+\r
+  @return                         Error codes returned by the started image\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuStartKernelImage (\r
+  IN  OUT EFI_HANDLE            *ImageHandle\r
+  )\r
+{\r
+  EFI_STATUS                    Status;\r
+  OVMF_LOADED_X86_LINUX_KERNEL  *LoadedImage;\r
+  EFI_HANDLE                    KernelImageHandle;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  *ImageHandle,\r
+                  &gOvmfLoadedX86LinuxKernelProtocolGuid,\r
+                  (VOID **)&LoadedImage,\r
+                  gImageHandle,                  // AgentHandle\r
+                  NULL,                          // ControllerHandle\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (!EFI_ERROR (Status)) {\r
+    return QemuStartLegacyImage (*ImageHandle);\r
+  }\r
+\r
+  Status = gBS->StartImage (\r
+                  *ImageHandle,\r
+                  NULL,              // ExitDataSize\r
+                  NULL               // ExitData\r
+                  );\r
+#ifdef MDE_CPU_IA32\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    //\r
+    // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while\r
+    // we are expecting a IA32 one, and the StartImage () boot service is unable\r
+    // to handle it, either because the image does not have the special .compat\r
+    // PE/COFF section that Linux specifies for mixed mode capable images, or\r
+    // because we are running without the support code for that. So load the\r
+    // image again, using the legacy loader, and unload the normally loaded\r
+    // image before starting the legacy one.\r
+    //\r
+    Status = QemuLoadLegacyImage (&KernelImageHandle);\r
+    if (EFI_ERROR (Status)) {\r
+      //\r
+      // Note: no change to (*ImageHandle), the caller will release it.\r
+      //\r
+      return Status;\r
+    }\r
+    //\r
+    // Swap in the legacy-loaded image.\r
+    //\r
+    QemuUnloadKernelImage (*ImageHandle);\r
+    *ImageHandle = KernelImageHandle;\r
+    return QemuStartLegacyImage (KernelImageHandle);\r
+  }\r
+#endif\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Unloads an image loaded with QemuLoadKernelImage ().\r
+\r
+  @param  ImageHandle             Handle that identifies the image to be\r
+                                  unloaded.\r
+\r
+  @retval EFI_SUCCESS             The image has been unloaded.\r
+  @retval EFI_UNSUPPORTED         The image has been started, and does not\r
+                                  support unload.\r
+  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.\r
+\r
+  @return                         Exit code from the image's unload function.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+QemuUnloadKernelImage (\r
+  IN  EFI_HANDLE          ImageHandle\r
+  )\r
+{\r
+  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;\r
+  EFI_STATUS                  Status;\r
+\r
+  Status = gBS->OpenProtocol (\r
+                  ImageHandle,\r
+                  &gEfiLoadedImageProtocolGuid,\r
+                  (VOID **)&KernelLoadedImage,\r
+                  gImageHandle,                  // AgentHandle\r
+                  NULL,                          // ControllerHandle\r
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
+                  );\r
+  if (Status == EFI_UNSUPPORTED) {\r
+    //\r
+    // The handle exists but does not have an instance of the standard loaded\r
+    // image protocol installed on it. Attempt to unload it as a legacy image\r
+    // instead.\r
+    //\r
+    return QemuUnloadLegacyImage (ImageHandle);\r
+  }\r
+\r
+  if (EFI_ERROR (Status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  //\r
+  // We are unloading a normal, non-legacy loaded image, either on behalf of\r
+  // an external caller, or called from QemuStartKernelImage() on IA32, while\r
+  // switching from the normal to the legacy method to load and start a X64\r
+  // image.\r
+  //\r
+  if (KernelLoadedImage->LoadOptions != NULL) {\r
+    FreePool (KernelLoadedImage->LoadOptions);\r
+    KernelLoadedImage->LoadOptions = NULL;\r
+  }\r
+  KernelLoadedImage->LoadOptionsSize = 0;\r
+\r
+  return gBS->UnloadImage (ImageHandle);\r
+}\r
diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
new file mode 100644 (file)
index 0000000..e1615ba
--- /dev/null
@@ -0,0 +1,42 @@
+## @file\r
+#  X86 specific implementation of QemuLoadImageLib library class interface\r
+#  with support for loading mixed mode images and non-EFI stub images\r
+#\r
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>\r
+#\r
+#  SPDX-License-Identifier: BSD-2-Clause-Patent\r
+#\r
+##\r
+\r
+[Defines]\r
+  INF_VERSION                    = 1.27\r
+  BASE_NAME                      = X86QemuLoadImageLib\r
+  FILE_GUID                      = 2304df80-e21d-4170-9c3c-113c878f7ac0\r
+  MODULE_TYPE                    = BASE\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER\r
+\r
+[Sources]\r
+  X86QemuLoadImageLib.c\r
+\r
+[Packages]\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  MdePkg/MdePkg.dec\r
+  OvmfPkg/OvmfPkg.dec\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  LoadLinuxLib\r
+  PrintLib\r
+  QemuFwCfgLib\r
+  ReportStatusCodeLib\r
+  UefiBootServicesTableLib\r
+\r
+[Protocols]\r
+  gEfiDevicePathProtocolGuid\r
+  gEfiLoadedImageProtocolGuid\r
+  gOvmfLoadedX86LinuxKernelProtocolGuid\r
+\r
+[Guids]\r
+  gQemuKernelLoaderFsMediaGuid\r