]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: provide a generic implementation of QemuLoadImageLib
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Fri, 28 Feb 2020 16:26:54 +0000 (17:26 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 5 Mar 2020 19:45:05 +0000 (19:45 +0000)
Implement QemuLoadImageLib, and make it load the image provided by the
QEMU_EFI_LOADER_FS_MEDIA_GUID/kernel device path that we implemented
in a preceding patch in a separate DXE driver, using only the standard
LoadImage and StartImage boot services.

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/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c [new file with mode: 0644]
OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf [new file with mode: 0644]

diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
new file mode 100644 (file)
index 0000000..f7f9a20
--- /dev/null
@@ -0,0 +1,276 @@
+/**  @file\r
+  Generic implementation of QemuLoadImageLib library class interface.\r
+\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 <Base.h>\r
+#include <Guid/QemuKernelLoaderFsMedia.h>\r
+#include <Library/DebugLib.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
+\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
+/**\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
+  @retval EFI_ACCESS_DENIED     The underlying LoadImage boot service call\r
+                                returned EFI_SECURITY_VIOLATION, and the image\r
+                                was unloaded again.\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_SECURITY_VIOLATION:\r
+    //\r
+    // In this case, the image was loaded but failed to authenticate.\r
+    //\r
+    Status = EFI_ACCESS_DENIED;\r
+    goto UnloadImage;\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
+  return gBS->StartImage (\r
+                *ImageHandle,\r
+                NULL,              // ExitDataSize\r
+                NULL               // ExitData\r
+                );\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 (EFI_ERROR (Status)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\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/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
new file mode 100644 (file)
index 0000000..b262cb9
--- /dev/null
@@ -0,0 +1,38 @@
+## @file\r
+#  Generic implementation of QemuLoadImageLib library class interface.\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                      = GenericQemuLoadImageLib\r
+  FILE_GUID                      = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0\r
+  MODULE_TYPE                    = BASE\r
+  VERSION_STRING                 = 1.0\r
+  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER\r
+\r
+[Sources]\r
+  GenericQemuLoadImageLib.c\r
+\r
+[Packages]\r
+  MdeModulePkg/MdeModulePkg.dec\r
+  MdePkg/MdePkg.dec\r
+  OvmfPkg/OvmfPkg.dec\r
+\r
+[LibraryClasses]\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  PrintLib\r
+  QemuFwCfgLib\r
+  UefiBootServicesTableLib\r
+\r
+[Protocols]\r
+  gEfiDevicePathProtocolGuid\r
+  gEfiLoadedImageProtocolGuid\r
+\r
+[Guids]\r
+  gQemuKernelLoaderFsMediaGuid\r