public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images
@ 2020-03-02  7:29 Ard Biesheuvel
  2020-03-02  7:29 ` [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
                   ` (12 more replies)
  0 siblings, 13 replies; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel, Arvind Sankar

On ArmVirtQemu, we require the kernel passed via the QEMU -kernel option
to have a PE/COFF header and an EFI stub, so that it can be loaded and
started using the LoadImage and StartImage boot services, respectively.
This means that, on builds that enable secure boot or measured boot, the
kernel image gets authenticated and/or measured as well.

On X86, for historical reasons, we never use LoadImage or StartImage, which
means that:
- kernel images are never authenticated or measured,
- calling Exit() from within the boot stub will attempt tp terminate the
  calling image, which is likely to end badly.

So instead, split and generalize the code that exists today for ArmVirtQemu,
and wire it up for x86 so that LoadImage and StartImage are used unless
there is a true need for the special Linux boot protocol.

The first 6 patches are only intended to be a refactoring of the existing
code, and should not result in any functional changes for either ArmVirtQemu
or OVMF.

Patch #12 adds the new Linux specific initrd loadfile2 protocol that aims
to simplify initrd loading from Linux when booting via the PE stub.

Patch #13 is optional, and disables the Linux loader fallback on builds that
have secure boot enabled.

Code can be found here:
https://github.com/ardbiesheuvel/edk2/tree/ovmf-loadimage-startimage-v1

Cc: Arvind Sankar <nivedita@alum.mit.edu>

Ard Biesheuvel (13):
  OvmfPkg: add GUID for the QEMU kernel loader fs media device path
  OvmfPkg: export abstract QEMU blob filesystem in standalone driver
  OvmfPkg: introduce QemuLoadImageLib library class
  OvmfPkg: provide a generic implementation of QemuLoadImageLib
  ArmVirtPkg: incorporate the new QEMU kernel loader driver and library
  ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader
  OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line
  OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block
  OvmfPkg: implement QEMU loader library for X86 with legacy fallback
  OvmfPkg: add new QEMU kernel image loader components
  OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib
  OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device
    path
  OvmfPkg: use generic QEMU image loader for secure boot enabled builds

 ArmVirtPkg/ArmVirtQemu.dsc                    |    2 +
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc          |    1 +
 ArmVirtPkg/ArmVirtQemuKernel.dsc              |    2 +
 .../PlatformBootManagerLib.inf                |    8 +-
 .../PlatformBootManagerLib/QemuKernel.c       | 1052 +----------------
 .../Include/Guid/QemuKernelLoaderFsMedia.h    |   18 +
 OvmfPkg/Include/Library/QemuLoadImageLib.h    |   78 ++
 .../GenericQemuLoadImageLib.c                 |  253 ++++
 .../GenericQemuLoadImageLib.inf               |   39 +
 .../PlatformBootManagerLib.inf                |    2 +-
 .../PlatformBootManagerLib/QemuKernel.c       |  157 +--
 .../X86QemuLoadImageLib/X86QemuLoadImageLib.c |  562 +++++++++
 .../X86QemuLoadImageLib.inf                   |   42 +
 OvmfPkg/OvmfPkg.dec                           |    7 +
 OvmfPkg/OvmfPkgIa32.dsc                       |    6 +
 OvmfPkg/OvmfPkgIa32.fdf                       |    1 +
 OvmfPkg/OvmfPkgIa32X64.dsc                    |    6 +
 OvmfPkg/OvmfPkgIa32X64.fdf                    |    1 +
 OvmfPkg/OvmfPkgX64.dsc                        |    6 +
 OvmfPkg/OvmfPkgX64.fdf                        |    1 +
 .../QemuKernelLoaderFsDxe.c                   |  362 +++---
 .../QemuKernelLoaderFsDxe.inf                 |   50 +
 22 files changed, 1268 insertions(+), 1388 deletions(-)
 create mode 100644 OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h
 create mode 100644 OvmfPkg/Include/Library/QemuLoadImageLib.h
 create mode 100644 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
 create mode 100644 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
 create mode 100644 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
 create mode 100644 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
 copy ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c => OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c (77%)
 create mode 100644 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf

-- 
2.17.1


^ permalink raw reply	[flat|nested] 33+ messages in thread

* [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 13:22   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Ard Biesheuvel
                   ` (11 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

In an upcoming patch, we will introduce a separate DXE driver that
exposes the virtual SimpleFileSystem implementation that carries the
kernel and initrd passed via the QEMU command line, and a separate
library that consumes it, to be incorporated into the boot manager.

Since the GUID used for the SimpleFileSystem implementation's device
path will no longer be for internal use only, create a well defined
GUID to identify the media device path.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h | 18 ++++++++++++++++++
 OvmfPkg/OvmfPkg.dec                            |  1 +
 2 files changed, 19 insertions(+)

diff --git a/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h
new file mode 100644
index 000000000000..225c3c494613
--- /dev/null
+++ b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h
@@ -0,0 +1,18 @@
+/** @file
+  GUID definition for the QEMU LoaderFs media device path, containing the
+  kernel, initrd and command line as file objects
+
+  Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__
+#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__
+
+#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \
+  {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
+
+extern EFI_GUID gQemuKernelLoaderFsMediaGuid;
+
+#endif
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 6849a79cd8b0..d88778600517 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -87,6 +87,7 @@ [Guids]
   gEfiLegacyBiosGuid                  = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}}
   gEfiLegacyDevOrderVariableGuid      = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}}
   gLinuxEfiInitrdMediaGuid            = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}
+  gQemuKernelLoaderFsMediaGuid        = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
 
 [Protocols]
   gVirtioDeviceProtocolGuid           = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
  2020-03-02  7:29 ` [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 13:45   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class Ard Biesheuvel
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Expose the existing implementation of an abstract filesystem exposing
the blobs passed to QEMU via the command line via a standalone DXE
driver.

Notable difference with the original code is the switch to a new vendor
GUIDed media device path, as opposed to a vendor GUID hardware device
path, which is not entirely appropriate for pure software constructs.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 979 ++++++++++++++++++++
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  48 +
 2 files changed, 1027 insertions(+)

diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
new file mode 100644
index 000000000000..efecbd817da1
--- /dev/null
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
@@ -0,0 +1,979 @@
+/** @file
+  DXE driver to expose the 'kernel', 'initrd' and 'cmdline' blobs
+  provided by QEMU as file in an abstract file system
+
+  Copyright (C) 2014-2016, Red Hat, Inc.
+  Copyright (C) 2020, Arm, Limited.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <PiDxe.h>
+
+#include <Guid/FileInfo.h>
+#include <Guid/FileSystemInfo.h>
+#include <Guid/FileSystemVolumeLabelInfo.h>
+#include <Guid/QemuKernelLoaderFsMedia.h>
+#include <Library/BaseLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/SimpleFileSystem.h>
+
+//
+// Static data that hosts the fw_cfg blobs and serves file requests.
+//
+typedef enum {
+  KernelBlobTypeKernel,
+  KernelBlobTypeInitrd,
+  KernelBlobTypeCommandLine,
+  KernelBlobTypeMax
+} KERNEL_BLOB_TYPE;
+
+typedef struct {
+  FIRMWARE_CONFIG_ITEM CONST SizeKey;
+  FIRMWARE_CONFIG_ITEM CONST DataKey;
+  CONST CHAR16 *       CONST Name;
+  UINT32                     Size;
+  UINT8                      *Data;
+} KERNEL_BLOB;
+
+STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
+  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
+  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
+  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
+};
+
+STATIC UINT64 mTotalBlobBytes;
+
+//
+// Device path for the handle that incorporates our "EFI stub filesystem".
+//
+#pragma pack (1)
+typedef struct {
+  VENDOR_DEVICE_PATH       VenMediaNode;
+  EFI_DEVICE_PATH_PROTOCOL EndNode;
+} SINGLE_VENMEDIA_NODE_DEVPATH;
+#pragma pack ()
+
+STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = {
+  {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+      { sizeof (VENDOR_DEVICE_PATH) }
+    },
+    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
+  }, {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+  }
+};
+
+//
+// The "file in the EFI stub filesystem" abstraction.
+//
+STATIC EFI_TIME mInitTime;
+
+#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')
+
+typedef struct {
+  UINT64            Signature; // Carries STUB_FILE_SIG.
+
+  KERNEL_BLOB_TYPE  BlobType;  // Index into mKernelBlob. KernelBlobTypeMax
+                               // denotes the root directory of the filesystem.
+
+  UINT64            Position;  // Byte position for regular files;
+                               // next directory entry to return for the root
+                               // directory.
+
+  EFI_FILE_PROTOCOL File;      // Standard protocol interface.
+} STUB_FILE;
+
+#define STUB_FILE_FROM_FILE(FilePointer) \
+        CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG)
+
+//
+// Tentative definition of the file protocol template. The initializer
+// (external definition) will be provided later.
+//
+STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate;
+
+
+//
+// Protocol member functions for File.
+//
+
+/**
+  Opens a new file relative to the source file's location.
+
+  @param[in]  This        A pointer to the EFI_FILE_PROTOCOL instance that is
+                          the file handle to the source location. This would
+                          typically be an open handle to a directory.
+
+  @param[out] NewHandle   A pointer to the location to return the opened handle
+                          for the new file.
+
+  @param[in]  FileName    The Null-terminated string of the name of the file to
+                          be opened. The file name may contain the following
+                          path modifiers: "\", ".", and "..".
+
+  @param[in]  OpenMode    The mode to open the file. The only valid
+                          combinations that the file may be opened with are:
+                          Read, Read/Write, or Create/Read/Write.
+
+  @param[in]  Attributes  Only valid for EFI_FILE_MODE_CREATE, in which case
+                          these are the attribute bits for the newly created
+                          file.
+
+  @retval EFI_SUCCESS           The file was opened.
+  @retval EFI_NOT_FOUND         The specified file could not be found on the
+                                device.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
+                                medium is no longer supported.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a
+                                file for write when the media is
+                                write-protected.
+  @retval EFI_ACCESS_DENIED     The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
+                                file.
+  @retval EFI_VOLUME_FULL       The volume is full.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileOpen (
+  IN EFI_FILE_PROTOCOL  *This,
+  OUT EFI_FILE_PROTOCOL **NewHandle,
+  IN CHAR16             *FileName,
+  IN UINT64             OpenMode,
+  IN UINT64             Attributes
+  )
+{
+  CONST STUB_FILE *StubFile;
+  UINTN           BlobType;
+  STUB_FILE       *NewStubFile;
+
+  //
+  // We're read-only.
+  //
+  switch (OpenMode) {
+    case EFI_FILE_MODE_READ:
+      break;
+
+    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
+    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
+      return EFI_WRITE_PROTECTED;
+
+    default:
+      return EFI_INVALID_PARAMETER;
+  }
+
+  //
+  // Only the root directory supports opening files in it.
+  //
+  StubFile = STUB_FILE_FROM_FILE (This);
+  if (StubFile->BlobType != KernelBlobTypeMax) {
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // Locate the file.
+  //
+  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
+    if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {
+      break;
+    }
+  }
+  if (BlobType == KernelBlobTypeMax) {
+    return EFI_NOT_FOUND;
+  }
+
+  //
+  // Found it.
+  //
+  NewStubFile = AllocatePool (sizeof *NewStubFile);
+  if (NewStubFile == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  NewStubFile->Signature = STUB_FILE_SIG;
+  NewStubFile->BlobType  = (KERNEL_BLOB_TYPE)BlobType;
+  NewStubFile->Position  = 0;
+  CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate,
+    sizeof mEfiFileProtocolTemplate);
+  *NewHandle = &NewStubFile->File;
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Closes a specified file handle.
+
+  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
+                   handle to close.
+
+  @retval EFI_SUCCESS  The file was closed.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileClose (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  FreePool (STUB_FILE_FROM_FILE (This));
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Close and delete the file handle.
+
+  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
+                   handle to the file to delete.
+
+  @retval EFI_SUCCESS              The file was closed and deleted, and the
+                                   handle was closed.
+  @retval EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not
+                                   deleted.
+
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileDelete (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  FreePool (STUB_FILE_FROM_FILE (This));
+  return EFI_WARN_DELETE_FAILURE;
+}
+
+
+/**
+  Helper function that formats an EFI_FILE_INFO structure into the
+  user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including
+  KernelBlobTypeMax, which stands for the root directory).
+
+  The interface follows the EFI_FILE_GET_INFO -- and for directories, the
+  EFI_FILE_READ -- interfaces.
+
+  @param[in]     BlobType     The KERNEL_BLOB_TYPE value identifying the fw_cfg
+                              blob backing the STUB_FILE that information is
+                              being requested about. If BlobType equals
+                              KernelBlobTypeMax, then information will be
+                              provided about the root directory of the
+                              filesystem.
+
+  @param[in,out] BufferSize  On input, the size of Buffer. On output, the
+                             amount of data returned in Buffer. In both cases,
+                             the size is measured in bytes.
+
+  @param[out]    Buffer      A pointer to the data buffer to return. The
+                             buffer's type is EFI_FILE_INFO.
+
+  @retval EFI_SUCCESS           The information was returned.
+  @retval EFI_BUFFER_TOO_SMALL  BufferSize is too small to store the
+                                EFI_FILE_INFO structure. BufferSize has been
+                                updated with the size needed to complete the
+                                request.
+**/
+STATIC
+EFI_STATUS
+ConvertKernelBlobTypeToFileInfo (
+  IN KERNEL_BLOB_TYPE BlobType,
+  IN OUT UINTN        *BufferSize,
+  OUT VOID            *Buffer
+  )
+{
+  CONST CHAR16  *Name;
+  UINT64        FileSize;
+  UINT64        Attribute;
+
+  UINTN         NameSize;
+  UINTN         FileInfoSize;
+  EFI_FILE_INFO *FileInfo;
+  UINTN         OriginalBufferSize;
+
+  if (BlobType == KernelBlobTypeMax) {
+    //
+    // getting file info about the root directory
+    //
+    Name      = L"\\";
+    FileSize  = KernelBlobTypeMax;
+    Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
+  } else {
+    CONST KERNEL_BLOB *Blob;
+
+    Blob      = &mKernelBlob[BlobType];
+    Name      = Blob->Name;
+    FileSize  = Blob->Size;
+    Attribute = EFI_FILE_READ_ONLY;
+  }
+
+  NameSize     = (StrLen(Name) + 1) * 2;
+  FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;
+  ASSERT (FileInfoSize >= sizeof *FileInfo);
+
+  OriginalBufferSize = *BufferSize;
+  *BufferSize        = FileInfoSize;
+  if (OriginalBufferSize < *BufferSize) {
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  FileInfo               = (EFI_FILE_INFO *)Buffer;
+  FileInfo->Size         = FileInfoSize;
+  FileInfo->FileSize     = FileSize;
+  FileInfo->PhysicalSize = FileSize;
+  FileInfo->Attribute    = Attribute;
+
+  CopyMem (&FileInfo->CreateTime,       &mInitTime, sizeof mInitTime);
+  CopyMem (&FileInfo->LastAccessTime,   &mInitTime, sizeof mInitTime);
+  CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);
+  CopyMem (FileInfo->FileName,          Name,       NameSize);
+
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Reads data from a file, or continues scanning a directory.
+
+  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
+                             is the file handle to read data from.
+
+  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
+                             amount of data returned in Buffer. In both cases,
+                             the size is measured in bytes. If the read goes
+                             beyond the end of the file, the read length is
+                             truncated to the end of the file.
+
+                             If This is a directory, the function reads the
+                             directory entry at the current position and
+                             returns the entry (as EFI_FILE_INFO) in Buffer. If
+                             there are no more directory entries, the
+                             BufferSize is set to zero on output.
+
+  @param[out]    Buffer      The buffer into which the data is read.
+
+  @retval EFI_SUCCESS           Data was read.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_DEVICE_ERROR      An attempt was made to read from a deleted
+                                file.
+  @retval EFI_DEVICE_ERROR      On entry, the current file position is beyond
+                                the end of the file.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
+                                current directory entry as a EFI_FILE_INFO
+                                structure. BufferSize has been updated with the
+                                size needed to complete the request, and the
+                                directory position has not been advanced.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileRead (
+  IN EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN         *BufferSize,
+  OUT VOID             *Buffer
+  )
+{
+  STUB_FILE         *StubFile;
+  CONST KERNEL_BLOB *Blob;
+  UINT64            Left;
+
+  StubFile = STUB_FILE_FROM_FILE (This);
+
+  //
+  // Scanning the root directory?
+  //
+  if (StubFile->BlobType == KernelBlobTypeMax) {
+    EFI_STATUS Status;
+
+    if (StubFile->Position == KernelBlobTypeMax) {
+      //
+      // Scanning complete.
+      //
+      *BufferSize = 0;
+      return EFI_SUCCESS;
+    }
+
+    Status = ConvertKernelBlobTypeToFileInfo (
+               (KERNEL_BLOB_TYPE)StubFile->Position,
+               BufferSize,
+               Buffer);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+
+    ++StubFile->Position;
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Reading a file.
+  //
+  Blob = &mKernelBlob[StubFile->BlobType];
+  if (StubFile->Position > Blob->Size) {
+    return EFI_DEVICE_ERROR;
+  }
+
+  Left = Blob->Size - StubFile->Position;
+  if (*BufferSize > Left) {
+    *BufferSize = (UINTN)Left;
+  }
+  if (Blob->Data != NULL) {
+    CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
+  }
+  StubFile->Position += *BufferSize;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Writes data to a file.
+
+  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
+                             is the file handle to write data to.
+
+  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
+                             amount of data actually written. In both cases,
+                             the size is measured in bytes.
+
+  @param[in]     Buffer      The buffer of data to write.
+
+  @retval EFI_SUCCESS           Data was written.
+  @retval EFI_UNSUPPORTED       Writes to open directory files are not
+                                supported.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_DEVICE_ERROR      An attempt was made to write to a deleted file.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
+  @retval EFI_ACCESS_DENIED     The file was opened read only.
+  @retval EFI_VOLUME_FULL       The volume is full.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileWrite (
+  IN EFI_FILE_PROTOCOL *This,
+  IN OUT UINTN         *BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  STUB_FILE *StubFile;
+
+  StubFile = STUB_FILE_FROM_FILE (This);
+  return (StubFile->BlobType == KernelBlobTypeMax) ?
+         EFI_UNSUPPORTED :
+         EFI_WRITE_PROTECTED;
+}
+
+
+/**
+  Returns a file's current position.
+
+  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is the
+                        file handle to get the current position on.
+
+  @param[out] Position  The address to return the file's current position
+                        value.
+
+  @retval EFI_SUCCESS      The position was returned.
+  @retval EFI_UNSUPPORTED  The request is not valid on open directories.
+  @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
+                           deleted file.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileGetPosition (
+  IN EFI_FILE_PROTOCOL *This,
+  OUT UINT64           *Position
+  )
+{
+  STUB_FILE *StubFile;
+
+  StubFile = STUB_FILE_FROM_FILE (This);
+  if (StubFile->BlobType == KernelBlobTypeMax) {
+    return EFI_UNSUPPORTED;
+  }
+
+  *Position = StubFile->Position;
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Sets a file's current position.
+
+  @param[in] This      A pointer to the EFI_FILE_PROTOCOL instance that is the
+                       file handle to set the requested position on.
+
+  @param[in] Position  The byte position from the start of the file to set. For
+                       regular files, MAX_UINT64 means "seek to end". For
+                       directories, zero means "rewind directory scan".
+
+  @retval EFI_SUCCESS       The position was set.
+  @retval EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
+                            directories.
+  @retval EFI_DEVICE_ERROR  An attempt was made to set the position of a
+                            deleted file.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileSetPosition (
+  IN EFI_FILE_PROTOCOL *This,
+  IN UINT64            Position
+  )
+{
+  STUB_FILE   *StubFile;
+  KERNEL_BLOB *Blob;
+
+  StubFile = STUB_FILE_FROM_FILE (This);
+
+  if (StubFile->BlobType == KernelBlobTypeMax) {
+    if (Position == 0) {
+      //
+      // rewinding a directory scan is allowed
+      //
+      StubFile->Position = 0;
+      return EFI_SUCCESS;
+    }
+    return EFI_UNSUPPORTED;
+  }
+
+  //
+  // regular file seek
+  //
+  Blob = &mKernelBlob[StubFile->BlobType];
+  if (Position == MAX_UINT64) {
+    //
+    // seek to end
+    //
+    StubFile->Position = Blob->Size;
+  } else {
+    //
+    // absolute seek from beginning -- seeking past the end is allowed
+    //
+    StubFile->Position = Position;
+  }
+  return EFI_SUCCESS;
+}
+
+
+/**
+  Returns information about a file.
+
+  @param[in]     This             A pointer to the EFI_FILE_PROTOCOL instance
+                                  that is the file handle the requested
+                                  information is for.
+
+  @param[in]     InformationType  The type identifier GUID for the information
+                                  being requested. The following information
+                                  types are supported, storing the
+                                  corresponding structures in Buffer:
+
+                                  - gEfiFileInfoGuid: EFI_FILE_INFO
+
+                                  - gEfiFileSystemInfoGuid:
+                                    EFI_FILE_SYSTEM_INFO
+
+                                  - gEfiFileSystemVolumeLabelInfoIdGuid:
+                                    EFI_FILE_SYSTEM_VOLUME_LABEL
+
+  @param[in,out] BufferSize       On input, the size of Buffer. On output, the
+                                  amount of data returned in Buffer. In both
+                                  cases, the size is measured in bytes.
+
+  @param[out]    Buffer           A pointer to the data buffer to return. The
+                                  buffer's type is indicated by
+                                  InformationType.
+
+  @retval EFI_SUCCESS           The information was returned.
+  @retval EFI_UNSUPPORTED       The InformationType is not known.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
+                                information structure requested by
+                                InformationType. BufferSize has been updated
+                                with the size needed to complete the request.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileGetInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN EFI_GUID          *InformationType,
+  IN OUT UINTN         *BufferSize,
+  OUT VOID             *Buffer
+  )
+{
+  CONST STUB_FILE *StubFile;
+  UINTN           OriginalBufferSize;
+
+  StubFile = STUB_FILE_FROM_FILE (This);
+
+  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
+    return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
+             Buffer);
+  }
+
+  OriginalBufferSize = *BufferSize;
+
+  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
+    EFI_FILE_SYSTEM_INFO *FileSystemInfo;
+
+    *BufferSize = sizeof *FileSystemInfo;
+    if (OriginalBufferSize < *BufferSize) {
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    FileSystemInfo                 = (EFI_FILE_SYSTEM_INFO *)Buffer;
+    FileSystemInfo->Size           = sizeof *FileSystemInfo;
+    FileSystemInfo->ReadOnly       = TRUE;
+    FileSystemInfo->VolumeSize     = mTotalBlobBytes;
+    FileSystemInfo->FreeSpace      = 0;
+    FileSystemInfo->BlockSize      = 1;
+    FileSystemInfo->VolumeLabel[0] = L'\0';
+
+    return EFI_SUCCESS;
+  }
+
+  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
+    EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
+
+    *BufferSize = sizeof *FileSystemVolumeLabel;
+    if (OriginalBufferSize < *BufferSize) {
+      return EFI_BUFFER_TOO_SMALL;
+    }
+
+    FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
+    FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
+
+    return EFI_SUCCESS;
+  }
+
+  return EFI_UNSUPPORTED;
+}
+
+
+/**
+  Sets information about a file.
+
+  @param[in] File             A pointer to the EFI_FILE_PROTOCOL instance that
+                              is the file handle the information is for.
+
+  @param[in] InformationType  The type identifier for the information being
+                              set.
+
+  @param[in] BufferSize       The size, in bytes, of Buffer.
+
+  @param[in] Buffer           A pointer to the data buffer to write. The
+                              buffer's type is indicated by InformationType.
+
+  @retval EFI_SUCCESS           The information was set.
+  @retval EFI_UNSUPPORTED       The InformationType is not known.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED   InformationType is EFI_FILE_INFO_ID and the
+                                media is read-only.
+  @retval EFI_WRITE_PROTECTED   InformationType is
+                                EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media
+                                is read only.
+  @retval EFI_WRITE_PROTECTED   InformationType is
+                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media
+                                is read-only.
+  @retval EFI_ACCESS_DENIED     An attempt is made to change the name of a file
+                                to a file that is already present.
+  @retval EFI_ACCESS_DENIED     An attempt is being made to change the
+                                EFI_FILE_DIRECTORY Attribute.
+  @retval EFI_ACCESS_DENIED     An attempt is being made to change the size of
+                                a directory.
+  @retval EFI_ACCESS_DENIED     InformationType is EFI_FILE_INFO_ID and the
+                                file was opened read-only and an attempt is
+                                being made to modify a field other than
+                                Attribute.
+  @retval EFI_VOLUME_FULL       The volume is full.
+  @retval EFI_BAD_BUFFER_SIZE   BufferSize is smaller than the size of the type
+                                indicated by InformationType.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileSetInfo (
+  IN EFI_FILE_PROTOCOL *This,
+  IN EFI_GUID          *InformationType,
+  IN UINTN             BufferSize,
+  IN VOID              *Buffer
+  )
+{
+  return EFI_WRITE_PROTECTED;
+}
+
+
+/**
+  Flushes all modified data associated with a file to a device.
+
+  @param [in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
+                    file handle to flush.
+
+  @retval EFI_SUCCESS           The data was flushed.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
+  @retval EFI_ACCESS_DENIED     The file was opened read-only.
+  @retval EFI_VOLUME_FULL       The volume is full.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileFlush (
+  IN EFI_FILE_PROTOCOL *This
+  )
+{
+  return EFI_WRITE_PROTECTED;
+}
+
+//
+// External definition of the file protocol template.
+//
+STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
+  EFI_FILE_PROTOCOL_REVISION, // revision 1
+  StubFileOpen,
+  StubFileClose,
+  StubFileDelete,
+  StubFileRead,
+  StubFileWrite,
+  StubFileGetPosition,
+  StubFileSetPosition,
+  StubFileGetInfo,
+  StubFileSetInfo,
+  StubFileFlush,
+  NULL,                       // OpenEx, revision 2
+  NULL,                       // ReadEx, revision 2
+  NULL,                       // WriteEx, revision 2
+  NULL                        // FlushEx, revision 2
+};
+
+
+//
+// Protocol member functions for SimpleFileSystem.
+//
+
+/**
+  Open the root directory on a volume.
+
+  @param[in]  This  A pointer to the volume to open the root directory on.
+
+  @param[out] Root  A pointer to the location to return the opened file handle
+                    for the root directory in.
+
+  @retval EFI_SUCCESS           The device was opened.
+  @retval EFI_UNSUPPORTED       This volume does not support the requested file
+                                system type.
+  @retval EFI_NO_MEDIA          The device has no medium.
+  @retval EFI_DEVICE_ERROR      The device reported an error.
+  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
+  @retval EFI_ACCESS_DENIED     The service denied access to the file.
+  @retval EFI_OUT_OF_RESOURCES  The volume was not opened due to lack of
+                                resources.
+  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
+                                medium is no longer supported. Any existing
+                                file handles for this volume are no longer
+                                valid. To access the files on the new medium,
+                                the volume must be reopened with OpenVolume().
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+StubFileSystemOpenVolume (
+  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
+  OUT EFI_FILE_PROTOCOL              **Root
+  )
+{
+  STUB_FILE *StubFile;
+
+  StubFile = AllocatePool (sizeof *StubFile);
+  if (StubFile == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  StubFile->Signature = STUB_FILE_SIG;
+  StubFile->BlobType  = KernelBlobTypeMax;
+  StubFile->Position  = 0;
+  CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
+    sizeof mEfiFileProtocolTemplate);
+  *Root = &StubFile->File;
+
+  return EFI_SUCCESS;
+}
+
+STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
+  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
+  StubFileSystemOpenVolume
+};
+
+
+//
+// Utility functions.
+//
+
+/**
+  Populate a blob in mKernelBlob.
+
+  param[in,out] Blob  Pointer to the KERNEL_BLOB element in mKernelBlob that is
+                      to be filled from fw_cfg.
+
+  @retval EFI_SUCCESS           Blob has been populated. If fw_cfg reported a
+                                size of zero for the blob, then Blob->Data has
+                                been left unchanged.
+
+  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for Blob->Data.
+**/
+STATIC
+EFI_STATUS
+FetchBlob (
+  IN OUT KERNEL_BLOB *Blob
+  )
+{
+  UINT32 Left;
+
+  //
+  // Read blob size.
+  //
+  QemuFwCfgSelectItem (Blob->SizeKey);
+  Blob->Size = QemuFwCfgRead32 ();
+  if (Blob->Size == 0) {
+    return EFI_SUCCESS;
+  }
+
+  //
+  // Read blob.
+  //
+  Blob->Data = AllocatePool (Blob->Size);
+  if (Blob->Data == NULL) {
+    DEBUG ((DEBUG_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
+      __FUNCTION__, (INT64)Blob->Size, Blob->Name));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
+    (INT64)Blob->Size, Blob->Name));
+  QemuFwCfgSelectItem (Blob->DataKey);
+
+  Left = Blob->Size;
+  do {
+    UINT32 Chunk;
+
+    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
+    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
+    Left -= Chunk;
+    DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
+      __FUNCTION__, (INT64)Left, Blob->Name));
+  } while (Left > 0);
+  return EFI_SUCCESS;
+}
+
+
+//
+// The entry point of the feature.
+//
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
+  image files.
+
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions. On success, the function doesn't
+                                return.
+**/
+EFI_STATUS
+EFIAPI
+QemuKernelLoaderFsDxeEntrypoint (
+  IN EFI_HANDLE       ImageHandle,
+  IN EFI_SYSTEM_TABLE *SystemTable
+  )
+{
+  UINTN                     BlobType;
+  KERNEL_BLOB               *CurrentBlob;
+  KERNEL_BLOB               *KernelBlob;
+  EFI_STATUS                Status;
+  EFI_HANDLE                FileSystemHandle;
+
+  if (!QemuFwCfgIsAvailable ()) {
+    return EFI_NOT_FOUND;
+  }
+
+  Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+
+  //
+  // Fetch all blobs.
+  //
+  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
+    CurrentBlob = &mKernelBlob[BlobType];
+    Status = FetchBlob (CurrentBlob);
+    if (EFI_ERROR (Status)) {
+      goto FreeBlobs;
+    }
+    mTotalBlobBytes += CurrentBlob->Size;
+  }
+  KernelBlob      = &mKernelBlob[KernelBlobTypeKernel];
+
+  if (KernelBlob->Data == NULL) {
+    Status = EFI_NOT_FOUND;
+    goto FreeBlobs;
+  }
+
+  //
+  // Create a new handle with a single VenMedia() node device path protocol on
+  // it, plus a custom SimpleFileSystem protocol on it.
+  //
+  FileSystemHandle = NULL;
+  Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
+                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
+                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
+                  NULL);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
+      __FUNCTION__, Status));
+    goto FreeBlobs;
+  }
+
+  return EFI_SUCCESS;
+
+FreeBlobs:
+  while (BlobType > 0) {
+    CurrentBlob = &mKernelBlob[--BlobType];
+    if (CurrentBlob->Data != NULL) {
+      FreePool (CurrentBlob->Data);
+      CurrentBlob->Size = 0;
+      CurrentBlob->Data = NULL;
+    }
+  }
+
+  return Status;
+}
diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
new file mode 100644
index 000000000000..f4b50c265027
--- /dev/null
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
@@ -0,0 +1,48 @@
+##  @file
+#  DXE driver to expose the 'kernel', 'initrd' and 'cmdline' blobs
+#  provided by QEMU as file in an abstract file system
+#
+#  Copyright (C) 2014-2016, Red Hat, Inc.
+#  Copyright (C) 2020, Arm, Limited.
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+##
+
+[Defines]
+  INF_VERSION                    = 1.27
+  BASE_NAME                      = QemuKernelLoaderFsDxe
+  FILE_GUID                      = 806040ca-dad9-4978-a3b4-2d2ab0c8a48f
+  MODULE_TYPE                    = DXE_DRIVER
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = QemuKernelLoaderFsDxeEntrypoint
+
+[Sources]
+  QemuKernelLoaderFsDxe.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  BaseLib
+  BaseMemoryLib
+  DebugLib
+  MemoryAllocationLib
+  UefiBootServicesTableLib
+  QemuFwCfgLib
+  UefiDriverEntryPoint
+  UefiRuntimeServicesTableLib
+
+[Guids]
+  gEfiFileInfoGuid
+  gEfiFileSystemInfoGuid
+  gEfiFileSystemVolumeLabelInfoIdGuid
+  gQemuKernelLoaderFsMediaGuid
+
+[Protocols]
+  gEfiDevicePathProtocolGuid                ## PRODUCES
+  gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
+
+[Depex]
+  gEfiRealTimeClockArchProtocolGuid
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
  2020-03-02  7:29 ` [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
  2020-03-02  7:29 ` [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 14:07   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib Ard Biesheuvel
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Introduce the QemuLoadImageLib library class that we will instantiate
to load the kernel image passed via the QEMU command line using the
standard LoadImage boot service.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/Include/Library/QemuLoadImageLib.h | 78 ++++++++++++++++++++
 OvmfPkg/OvmfPkg.dec                        |  5 ++
 2 files changed, 83 insertions(+)

diff --git a/OvmfPkg/Include/Library/QemuLoadImageLib.h b/OvmfPkg/Include/Library/QemuLoadImageLib.h
new file mode 100644
index 000000000000..304853096593
--- /dev/null
+++ b/OvmfPkg/Include/Library/QemuLoadImageLib.h
@@ -0,0 +1,78 @@
+/** @file
+  Load a kernel image and command line passed to QEMU via
+  the command line
+
+  Copyright (C) 2020, Arm, Limited.
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#ifndef QEMU_LOAD_IMAGE_LIB_H__
+#define QEMU_LOAD_IMAGE_LIB_H__
+
+#include <Uefi/UefiBaseType.h>
+#include <Base.h>
+
+#include <Protocol/LoadedImage.h>
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. The kernel will be instructed via its command line to load
+  the initrd from the same Simple FileSystem.
+
+  @param[out] ImageHandle       The image handle that was allocated for
+                                loading the image
+
+  @retval EFI_SUCCESS           The image was loaded successfully.
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions.
+**/
+EFI_STATUS
+EFIAPI
+QemuLoadKernelImage (
+  OUT EFI_HANDLE          *ImageHandle
+  );
+
+/**
+  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
+
+  @param[in]  ImageHandle         Handle of image to be started.
+
+  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
+                                  or the image has already been initialized with
+                                  StartImage
+  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
+                                  image should not be started.
+
+  @return                         Error codes returned by the started image.
+                                  On success, the function doesn't return.
+**/
+EFI_STATUS
+EFIAPI
+QemuStartKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  );
+
+/**
+  Unloads an image loaded with QemuLoadKernelImage ().
+
+  @param  ImageHandle             Handle that identifies the image to be
+                                  unloaded.
+
+  @retval EFI_SUCCESS             The image has been unloaded.
+  @retval EFI_UNSUPPORTED         The image has been started, and does not
+                                  support unload.
+  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+QemuUnloadKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  );
+
+#endif
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index d88778600517..26f977bad795 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -58,6 +58,11 @@ [LibraryClasses]
   #
   QemuBootOrderLib|Include/Library/QemuBootOrderLib.h
 
+  ##  @libraryclass  Load a kernel image and command line passed to QEMU via
+  #                  the command line
+  #
+  QemuLoadImageLib|Include/Library/QemuLoadImageLib.h
+
   ##  @libraryclass  Serialize (and deserialize) variables
   #
   SerializeVariablesLib|Include/Library/SerializeVariablesLib.h
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (2 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 17:12   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Ard Biesheuvel
                   ` (8 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

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>
---
 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c   | 253 ++++++++++++++++++++
 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf |  39 +++
 2 files changed, 292 insertions(+)

diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
new file mode 100644
index 000000000000..c48c7a88dd91
--- /dev/null
+++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
@@ -0,0 +1,253 @@
+/**  @file
+  Generic implementation of QemuLoadImageLib library class interface.
+
+  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Guid/QemuKernelLoaderFsMedia.h>
+#include <Library/DebugLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+
+#pragma pack (1)
+typedef struct {
+  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;
+  CHAR16                    FilePath[sizeof (L"kernel")];
+} KERNEL_FILE_DEVPATH;
+
+typedef struct {
+  VENDOR_DEVICE_PATH        VenMediaNode;
+  KERNEL_FILE_DEVPATH       FileNode;
+  EFI_DEVICE_PATH_PROTOCOL  EndNode;
+} KERNEL_VENMEDIA_FILE_DEVPATH;
+#pragma pack ()
+
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
+  {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+      { sizeof (VENDOR_DEVICE_PATH) }
+    },
+    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
+  }, {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
+      { sizeof (KERNEL_FILE_DEVPATH) }
+    },
+    L"kernel",
+  }, {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+  }
+};
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. The kernel will be instructed via its command line to load
+  the initrd from the same Simple FileSystem.
+
+  @param[out] ImageHandle       The image handle that was allocated for
+                                loading the image
+  @param[out] LoadedImage       The loaded image protocol that was installed
+                                on ImageHandle by the LoadImage boot service.
+
+  @retval EFI_SUCCESS           The image was loaded successfully.
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions.
+**/
+EFI_STATUS
+EFIAPI
+QemuLoadKernelImage (
+  OUT EFI_HANDLE                  *ImageHandle
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HANDLE                KernelImageHandle;
+  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+  UINTN                     CommandLineSize;
+  CHAR8                     *CommandLine;
+  UINTN                     InitrdSize;
+
+  //
+  // Load the image. This should call back into the QEMU EFI loader file system.
+  //
+  Status = gBS->LoadImage (
+                  FALSE,                    // BootPolicy: exact match required
+                  gImageHandle,             // ParentImageHandle
+                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
+                  NULL,                     // SourceBuffer
+                  0,                        // SourceSize
+                  &KernelImageHandle
+                  );
+  if (EFI_ERROR (Status)) {
+    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+
+  //
+  // Construct the kernel command line.
+  //
+  Status = gBS->OpenProtocol (
+                  KernelImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **)&KernelLoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
+
+  if (CommandLineSize == 0) {
+    KernelLoadedImage->LoadOptionsSize = 0;
+  } else {
+    CommandLine = AllocatePool (CommandLineSize);
+    ASSERT (CommandLine != NULL);
+
+    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
+
+    //
+    // Verify NUL-termination of the command line.
+    //
+    if (CommandLine[CommandLineSize - 1] != '\0') {
+      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
+        __FUNCTION__));
+      Status = EFI_PROTOCOL_ERROR;
+      goto FreeCommandLine;
+    }
+
+    //
+    // Drop the terminating NUL, convert to UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+  InitrdSize = (UINTN) QemuFwCfgRead64 ();
+
+  if (InitrdSize > 0) {
+    //
+    // Append ' initrd=initrd' in UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
+  }
+
+  if (KernelLoadedImage->LoadOptionsSize == 0) {
+    KernelLoadedImage->LoadOptions = NULL;
+  } else {
+    //
+    // NUL-terminate in UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize += 2;
+
+    KernelLoadedImage->LoadOptions = AllocatePool (
+                                       KernelLoadedImage->LoadOptionsSize);
+    if (KernelLoadedImage->LoadOptions == NULL) {
+      KernelLoadedImage->LoadOptionsSize = 0;
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeCommandLine;
+    }
+
+    UnicodeSPrintAsciiFormat (
+      KernelLoadedImage->LoadOptions,
+      KernelLoadedImage->LoadOptionsSize,
+      "%a%a",
+      (CommandLineSize == 0) ?  "" : CommandLine,
+      (InitrdSize == 0)      ?  "" : " initrd=initrd"
+      );
+    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
+      (CHAR16 *)KernelLoadedImage->LoadOptions));
+  }
+
+  *ImageHandle = KernelImageHandle;
+  return EFI_SUCCESS;
+
+FreeCommandLine:
+  FreePool (CommandLine);
+  gBS->UnloadImage (KernelImageHandle);
+
+  return Status;
+}
+
+/**
+  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
+
+  @param[in]  ImageHandle         Handle of image to be started.
+
+  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
+                                  or the image has already been initialized with
+                                  StartImage
+  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
+                                  image should not be started.
+
+  @return                         Error codes returned by the started image
+**/
+EFI_STATUS
+EFIAPI
+QemuStartKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  )
+{
+  return gBS->StartImage (
+                ImageHandle,
+                NULL,              // ExitDataSize
+                NULL               // ExitData
+                );
+}
+
+/**
+  Unloads an image loaded with QemuLoadKernelImage ().
+
+  @param  ImageHandle             Handle that identifies the image to be
+                                  unloaded.
+
+  @retval EFI_SUCCESS             The image has been unloaded.
+  @retval EFI_UNSUPPORTED         The image has been started, and does not
+                                  support unload.
+  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+QemuUnloadKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  )
+{
+  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;
+  EFI_STATUS                  Status;
+
+  Status = gBS->OpenProtocol (
+                  ImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **)&KernelLoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (KernelLoadedImage->LoadOptions != NULL) {
+    FreePool (KernelLoadedImage->LoadOptions);
+    KernelLoadedImage->LoadOptions = NULL;
+  }
+  KernelLoadedImage->LoadOptionsSize = 0;
+
+  return gBS->UnloadImage (ImageHandle);
+}
diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
new file mode 100644
index 000000000000..cbd40e6290e0
--- /dev/null
+++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
@@ -0,0 +1,39 @@
+## @file
+#  Generic implementation of QemuLoadImageLib library class interface.
+#
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.27
+  BASE_NAME                      = GenericQemuLoadImageLib
+  FILE_GUID                      = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER
+
+[Sources]
+  GenericQemuLoadImageLib.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
+  PrintLib
+  QemuFwCfgLib
+  ReportStatusCodeLib
+  UefiBootServicesTableLib
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiLoadedImageProtocolGuid
+
+[Guids]
+  gQemuKernelLoaderFsMediaGuid
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (3 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 17:15   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Ard Biesheuvel
                   ` (7 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Add the QEMU loader DXE driver and client library to the build for
our QEMU targeted implementations in ArmVirtPkg.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 ArmVirtPkg/ArmVirtQemu.dsc           | 2 ++
 ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 1 +
 ArmVirtPkg/ArmVirtQemuKernel.dsc     | 2 ++
 3 files changed, 5 insertions(+)

diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
index 7ae6702ac1f0..d037d68f90c7 100644
--- a/ArmVirtPkg/ArmVirtQemu.dsc
+++ b/ArmVirtPkg/ArmVirtQemu.dsc
@@ -56,6 +56,7 @@ [LibraryClasses.common]
   VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf
   QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf
+  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
 
   ArmPlatformLib|ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.inf
 
@@ -371,6 +372,7 @@ [Components.common]
       NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
       NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
   }
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 
   #
   # Networking stack
diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
index bfa380815f1a..9cb8621ffece 100644
--- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
+++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
@@ -114,6 +114,7 @@ [FV.FvMain]
   INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
   INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
   INF MdeModulePkg/Application/UiApp/UiApp.inf
+  INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 
   #
   # Networking stack
diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
index 3b0f04967a4b..dfd06587c735 100644
--- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
+++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
@@ -56,6 +56,7 @@ [LibraryClasses.common]
   VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf
   QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf
+  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
 
   ArmVirtMemInfoLib|ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf
 
@@ -355,6 +356,7 @@ [Components.common]
       NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
       NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
   }
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 
   #
   # Networking stack
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (4 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 17:26   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line Ard Biesheuvel
                   ` (6 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Drop the QEMU loader file system implementation inside this library,
and switch to the separate QemuLoadImageLib library and the associated
driver to expose the kernel and initrd passed via the QEMU command line.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |    8 +-
 ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c               | 1052 +-------------------
 2 files changed, 6 insertions(+), 1054 deletions(-)

diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
index a9d4888d4377..e80f444aec64 100644
--- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -46,7 +46,7 @@ [LibraryClasses]
   PlatformBmPrintScLib
   PrintLib
   QemuBootOrderLib
-  QemuFwCfgLib
+  QemuLoadImageLib
   ReportStatusCodeLib
   UefiBootManagerLib
   UefiBootServicesTableLib
@@ -64,18 +64,12 @@ [Pcd]
   gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
 
 [Guids]
-  gEfiFileInfoGuid
-  gEfiFileSystemInfoGuid
-  gEfiFileSystemVolumeLabelInfoIdGuid
   gEfiEndOfDxeEventGroupGuid
   gRootBridgesConnectedEventGroupGuid
   gUefiShellFileGuid
 
 [Protocols]
-  gEfiDevicePathProtocolGuid
   gEfiFirmwareVolume2ProtocolGuid
   gEfiGraphicsOutputProtocolGuid
-  gEfiLoadedImageProtocolGuid
   gEfiPciRootBridgeIoProtocolGuid
-  gEfiSimpleFileSystemProtocolGuid
   gVirtioDeviceProtocolGuid
diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
index d3851fd75fa5..c305927e249b 100644
--- a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
+++ b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
@@ -9,887 +9,11 @@
   SPDX-License-Identifier: BSD-2-Clause-Patent
 **/
 
-#include <Guid/FileInfo.h>
-#include <Guid/FileSystemInfo.h>
-#include <Guid/FileSystemVolumeLabelInfo.h>
-#include <Library/PrintLib.h>
-#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuLoadImageLib.h>
 #include <Library/ReportStatusCodeLib.h>
-#include <Protocol/DevicePath.h>
-#include <Protocol/LoadedImage.h>
-#include <Protocol/SimpleFileSystem.h>
 
 #include "PlatformBm.h"
 
-//
-// Static data that hosts the fw_cfg blobs and serves file requests.
-//
-typedef enum {
-  KernelBlobTypeKernel,
-  KernelBlobTypeInitrd,
-  KernelBlobTypeCommandLine,
-  KernelBlobTypeMax
-} KERNEL_BLOB_TYPE;
-
-typedef struct {
-  FIRMWARE_CONFIG_ITEM CONST SizeKey;
-  FIRMWARE_CONFIG_ITEM CONST DataKey;
-  CONST CHAR16 *       CONST Name;
-  UINT32                     Size;
-  UINT8                      *Data;
-} KERNEL_BLOB;
-
-STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
-  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
-  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
-  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
-};
-
-STATIC UINT64 mTotalBlobBytes;
-
-//
-// Device path for the handle that incorporates our "EFI stub filesystem". The
-// GUID is arbitrary and need not be standardized or advertized.
-//
-#pragma pack(1)
-typedef struct {
-  VENDOR_DEVICE_PATH       VenHwNode;
-  EFI_DEVICE_PATH_PROTOCOL EndNode;
-} SINGLE_VENHW_NODE_DEVPATH;
-#pragma pack()
-
-STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = {
-  {
-    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } },
-    {
-      0xb0fae7e7, 0x6b07, 0x49d0,
-      { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d }
-    }
-  },
-
-  {
-    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
-    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
-  }
-};
-
-//
-// The "file in the EFI stub filesystem" abstraction.
-//
-STATIC EFI_TIME mInitTime;
-
-#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')
-
-typedef struct {
-  UINT64            Signature; // Carries STUB_FILE_SIG.
-
-  KERNEL_BLOB_TYPE  BlobType;  // Index into mKernelBlob. KernelBlobTypeMax
-                               // denotes the root directory of the filesystem.
-
-  UINT64            Position;  // Byte position for regular files;
-                               // next directory entry to return for the root
-                               // directory.
-
-  EFI_FILE_PROTOCOL File;      // Standard protocol interface.
-} STUB_FILE;
-
-#define STUB_FILE_FROM_FILE(FilePointer) \
-        CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG)
-
-//
-// Tentative definition of the file protocol template. The initializer
-// (external definition) will be provided later.
-//
-STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate;
-
-
-//
-// Protocol member functions for File.
-//
-
-/**
-  Opens a new file relative to the source file's location.
-
-  @param[in]  This        A pointer to the EFI_FILE_PROTOCOL instance that is
-                          the file handle to the source location. This would
-                          typically be an open handle to a directory.
-
-  @param[out] NewHandle   A pointer to the location to return the opened handle
-                          for the new file.
-
-  @param[in]  FileName    The Null-terminated string of the name of the file to
-                          be opened. The file name may contain the following
-                          path modifiers: "\", ".", and "..".
-
-  @param[in]  OpenMode    The mode to open the file. The only valid
-                          combinations that the file may be opened with are:
-                          Read, Read/Write, or Create/Read/Write.
-
-  @param[in]  Attributes  Only valid for EFI_FILE_MODE_CREATE, in which case
-                          these are the attribute bits for the newly created
-                          file.
-
-  @retval EFI_SUCCESS           The file was opened.
-  @retval EFI_NOT_FOUND         The specified file could not be found on the
-                                device.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
-                                medium is no longer supported.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a
-                                file for write when the media is
-                                write-protected.
-  @retval EFI_ACCESS_DENIED     The service denied access to the file.
-  @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
-                                file.
-  @retval EFI_VOLUME_FULL       The volume is full.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileOpen (
-  IN EFI_FILE_PROTOCOL  *This,
-  OUT EFI_FILE_PROTOCOL **NewHandle,
-  IN CHAR16             *FileName,
-  IN UINT64             OpenMode,
-  IN UINT64             Attributes
-  )
-{
-  CONST STUB_FILE *StubFile;
-  UINTN           BlobType;
-  STUB_FILE       *NewStubFile;
-
-  //
-  // We're read-only.
-  //
-  switch (OpenMode) {
-    case EFI_FILE_MODE_READ:
-      break;
-
-    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
-    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
-      return EFI_WRITE_PROTECTED;
-
-    default:
-      return EFI_INVALID_PARAMETER;
-  }
-
-  //
-  // Only the root directory supports opening files in it.
-  //
-  StubFile = STUB_FILE_FROM_FILE (This);
-  if (StubFile->BlobType != KernelBlobTypeMax) {
-    return EFI_UNSUPPORTED;
-  }
-
-  //
-  // Locate the file.
-  //
-  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
-    if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {
-      break;
-    }
-  }
-  if (BlobType == KernelBlobTypeMax) {
-    return EFI_NOT_FOUND;
-  }
-
-  //
-  // Found it.
-  //
-  NewStubFile = AllocatePool (sizeof *NewStubFile);
-  if (NewStubFile == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  NewStubFile->Signature = STUB_FILE_SIG;
-  NewStubFile->BlobType  = (KERNEL_BLOB_TYPE)BlobType;
-  NewStubFile->Position  = 0;
-  CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate,
-    sizeof mEfiFileProtocolTemplate);
-  *NewHandle = &NewStubFile->File;
-
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Closes a specified file handle.
-
-  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
-                   handle to close.
-
-  @retval EFI_SUCCESS  The file was closed.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileClose (
-  IN EFI_FILE_PROTOCOL *This
-  )
-{
-  FreePool (STUB_FILE_FROM_FILE (This));
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Close and delete the file handle.
-
-  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
-                   handle to the file to delete.
-
-  @retval EFI_SUCCESS              The file was closed and deleted, and the
-                                   handle was closed.
-  @retval EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not
-                                   deleted.
-
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileDelete (
-  IN EFI_FILE_PROTOCOL *This
-  )
-{
-  FreePool (STUB_FILE_FROM_FILE (This));
-  return EFI_WARN_DELETE_FAILURE;
-}
-
-
-/**
-  Helper function that formats an EFI_FILE_INFO structure into the
-  user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including
-  KernelBlobTypeMax, which stands for the root directory).
-
-  The interface follows the EFI_FILE_GET_INFO -- and for directories, the
-  EFI_FILE_READ -- interfaces.
-
-  @param[in]     BlobType     The KERNEL_BLOB_TYPE value identifying the fw_cfg
-                              blob backing the STUB_FILE that information is
-                              being requested about. If BlobType equals
-                              KernelBlobTypeMax, then information will be
-                              provided about the root directory of the
-                              filesystem.
-
-  @param[in,out] BufferSize  On input, the size of Buffer. On output, the
-                             amount of data returned in Buffer. In both cases,
-                             the size is measured in bytes.
-
-  @param[out]    Buffer      A pointer to the data buffer to return. The
-                             buffer's type is EFI_FILE_INFO.
-
-  @retval EFI_SUCCESS           The information was returned.
-  @retval EFI_BUFFER_TOO_SMALL  BufferSize is too small to store the
-                                EFI_FILE_INFO structure. BufferSize has been
-                                updated with the size needed to complete the
-                                request.
-**/
-STATIC
-EFI_STATUS
-ConvertKernelBlobTypeToFileInfo (
-  IN KERNEL_BLOB_TYPE BlobType,
-  IN OUT UINTN        *BufferSize,
-  OUT VOID            *Buffer
-  )
-{
-  CONST CHAR16  *Name;
-  UINT64        FileSize;
-  UINT64        Attribute;
-
-  UINTN         NameSize;
-  UINTN         FileInfoSize;
-  EFI_FILE_INFO *FileInfo;
-  UINTN         OriginalBufferSize;
-
-  if (BlobType == KernelBlobTypeMax) {
-    //
-    // getting file info about the root directory
-    //
-    Name      = L"\\";
-    FileSize  = KernelBlobTypeMax;
-    Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
-  } else {
-    CONST KERNEL_BLOB *Blob;
-
-    Blob      = &mKernelBlob[BlobType];
-    Name      = Blob->Name;
-    FileSize  = Blob->Size;
-    Attribute = EFI_FILE_READ_ONLY;
-  }
-
-  NameSize     = (StrLen(Name) + 1) * 2;
-  FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;
-  ASSERT (FileInfoSize >= sizeof *FileInfo);
-
-  OriginalBufferSize = *BufferSize;
-  *BufferSize        = FileInfoSize;
-  if (OriginalBufferSize < *BufferSize) {
-    return EFI_BUFFER_TOO_SMALL;
-  }
-
-  FileInfo               = (EFI_FILE_INFO *)Buffer;
-  FileInfo->Size         = FileInfoSize;
-  FileInfo->FileSize     = FileSize;
-  FileInfo->PhysicalSize = FileSize;
-  FileInfo->Attribute    = Attribute;
-
-  CopyMem (&FileInfo->CreateTime,       &mInitTime, sizeof mInitTime);
-  CopyMem (&FileInfo->LastAccessTime,   &mInitTime, sizeof mInitTime);
-  CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);
-  CopyMem (FileInfo->FileName,          Name,       NameSize);
-
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Reads data from a file, or continues scanning a directory.
-
-  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
-                             is the file handle to read data from.
-
-  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
-                             amount of data returned in Buffer. In both cases,
-                             the size is measured in bytes. If the read goes
-                             beyond the end of the file, the read length is
-                             truncated to the end of the file.
-
-                             If This is a directory, the function reads the
-                             directory entry at the current position and
-                             returns the entry (as EFI_FILE_INFO) in Buffer. If
-                             there are no more directory entries, the
-                             BufferSize is set to zero on output.
-
-  @param[out]    Buffer      The buffer into which the data is read.
-
-  @retval EFI_SUCCESS           Data was read.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_DEVICE_ERROR      An attempt was made to read from a deleted
-                                file.
-  @retval EFI_DEVICE_ERROR      On entry, the current file position is beyond
-                                the end of the file.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
-                                current directory entry as a EFI_FILE_INFO
-                                structure. BufferSize has been updated with the
-                                size needed to complete the request, and the
-                                directory position has not been advanced.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileRead (
-  IN EFI_FILE_PROTOCOL *This,
-  IN OUT UINTN         *BufferSize,
-  OUT VOID             *Buffer
-  )
-{
-  STUB_FILE         *StubFile;
-  CONST KERNEL_BLOB *Blob;
-  UINT64            Left;
-
-  StubFile = STUB_FILE_FROM_FILE (This);
-
-  //
-  // Scanning the root directory?
-  //
-  if (StubFile->BlobType == KernelBlobTypeMax) {
-    EFI_STATUS Status;
-
-    if (StubFile->Position == KernelBlobTypeMax) {
-      //
-      // Scanning complete.
-      //
-      *BufferSize = 0;
-      return EFI_SUCCESS;
-    }
-
-    Status = ConvertKernelBlobTypeToFileInfo (
-               (KERNEL_BLOB_TYPE)StubFile->Position,
-               BufferSize,
-               Buffer);
-    if (EFI_ERROR (Status)) {
-      return Status;
-    }
-
-    ++StubFile->Position;
-    return EFI_SUCCESS;
-  }
-
-  //
-  // Reading a file.
-  //
-  Blob = &mKernelBlob[StubFile->BlobType];
-  if (StubFile->Position > Blob->Size) {
-    return EFI_DEVICE_ERROR;
-  }
-
-  Left = Blob->Size - StubFile->Position;
-  if (*BufferSize > Left) {
-    *BufferSize = (UINTN)Left;
-  }
-  if (Blob->Data != NULL) {
-    CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
-  }
-  StubFile->Position += *BufferSize;
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Writes data to a file.
-
-  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
-                             is the file handle to write data to.
-
-  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
-                             amount of data actually written. In both cases,
-                             the size is measured in bytes.
-
-  @param[in]     Buffer      The buffer of data to write.
-
-  @retval EFI_SUCCESS           Data was written.
-  @retval EFI_UNSUPPORTED       Writes to open directory files are not
-                                supported.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_DEVICE_ERROR      An attempt was made to write to a deleted file.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
-  @retval EFI_ACCESS_DENIED     The file was opened read only.
-  @retval EFI_VOLUME_FULL       The volume is full.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileWrite (
-  IN EFI_FILE_PROTOCOL *This,
-  IN OUT UINTN         *BufferSize,
-  IN VOID              *Buffer
-  )
-{
-  STUB_FILE *StubFile;
-
-  StubFile = STUB_FILE_FROM_FILE (This);
-  return (StubFile->BlobType == KernelBlobTypeMax) ?
-         EFI_UNSUPPORTED :
-         EFI_WRITE_PROTECTED;
-}
-
-
-/**
-  Returns a file's current position.
-
-  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is the
-                        file handle to get the current position on.
-
-  @param[out] Position  The address to return the file's current position
-                        value.
-
-  @retval EFI_SUCCESS      The position was returned.
-  @retval EFI_UNSUPPORTED  The request is not valid on open directories.
-  @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
-                           deleted file.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileGetPosition (
-  IN EFI_FILE_PROTOCOL *This,
-  OUT UINT64           *Position
-  )
-{
-  STUB_FILE *StubFile;
-
-  StubFile = STUB_FILE_FROM_FILE (This);
-  if (StubFile->BlobType == KernelBlobTypeMax) {
-    return EFI_UNSUPPORTED;
-  }
-
-  *Position = StubFile->Position;
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Sets a file's current position.
-
-  @param[in] This      A pointer to the EFI_FILE_PROTOCOL instance that is the
-                       file handle to set the requested position on.
-
-  @param[in] Position  The byte position from the start of the file to set. For
-                       regular files, MAX_UINT64 means "seek to end". For
-                       directories, zero means "rewind directory scan".
-
-  @retval EFI_SUCCESS       The position was set.
-  @retval EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
-                            directories.
-  @retval EFI_DEVICE_ERROR  An attempt was made to set the position of a
-                            deleted file.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileSetPosition (
-  IN EFI_FILE_PROTOCOL *This,
-  IN UINT64            Position
-  )
-{
-  STUB_FILE   *StubFile;
-  KERNEL_BLOB *Blob;
-
-  StubFile = STUB_FILE_FROM_FILE (This);
-
-  if (StubFile->BlobType == KernelBlobTypeMax) {
-    if (Position == 0) {
-      //
-      // rewinding a directory scan is allowed
-      //
-      StubFile->Position = 0;
-      return EFI_SUCCESS;
-    }
-    return EFI_UNSUPPORTED;
-  }
-
-  //
-  // regular file seek
-  //
-  Blob = &mKernelBlob[StubFile->BlobType];
-  if (Position == MAX_UINT64) {
-    //
-    // seek to end
-    //
-    StubFile->Position = Blob->Size;
-  } else {
-    //
-    // absolute seek from beginning -- seeking past the end is allowed
-    //
-    StubFile->Position = Position;
-  }
-  return EFI_SUCCESS;
-}
-
-
-/**
-  Returns information about a file.
-
-  @param[in]     This             A pointer to the EFI_FILE_PROTOCOL instance
-                                  that is the file handle the requested
-                                  information is for.
-
-  @param[in]     InformationType  The type identifier GUID for the information
-                                  being requested. The following information
-                                  types are supported, storing the
-                                  corresponding structures in Buffer:
-
-                                  - gEfiFileInfoGuid: EFI_FILE_INFO
-
-                                  - gEfiFileSystemInfoGuid:
-                                    EFI_FILE_SYSTEM_INFO
-
-                                  - gEfiFileSystemVolumeLabelInfoIdGuid:
-                                    EFI_FILE_SYSTEM_VOLUME_LABEL
-
-  @param[in,out] BufferSize       On input, the size of Buffer. On output, the
-                                  amount of data returned in Buffer. In both
-                                  cases, the size is measured in bytes.
-
-  @param[out]    Buffer           A pointer to the data buffer to return. The
-                                  buffer's type is indicated by
-                                  InformationType.
-
-  @retval EFI_SUCCESS           The information was returned.
-  @retval EFI_UNSUPPORTED       The InformationType is not known.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
-                                information structure requested by
-                                InformationType. BufferSize has been updated
-                                with the size needed to complete the request.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileGetInfo (
-  IN EFI_FILE_PROTOCOL *This,
-  IN EFI_GUID          *InformationType,
-  IN OUT UINTN         *BufferSize,
-  OUT VOID             *Buffer
-  )
-{
-  CONST STUB_FILE *StubFile;
-  UINTN           OriginalBufferSize;
-
-  StubFile = STUB_FILE_FROM_FILE (This);
-
-  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
-    return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
-             Buffer);
-  }
-
-  OriginalBufferSize = *BufferSize;
-
-  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
-    EFI_FILE_SYSTEM_INFO *FileSystemInfo;
-
-    *BufferSize = sizeof *FileSystemInfo;
-    if (OriginalBufferSize < *BufferSize) {
-      return EFI_BUFFER_TOO_SMALL;
-    }
-
-    FileSystemInfo                 = (EFI_FILE_SYSTEM_INFO *)Buffer;
-    FileSystemInfo->Size           = sizeof *FileSystemInfo;
-    FileSystemInfo->ReadOnly       = TRUE;
-    FileSystemInfo->VolumeSize     = mTotalBlobBytes;
-    FileSystemInfo->FreeSpace      = 0;
-    FileSystemInfo->BlockSize      = 1;
-    FileSystemInfo->VolumeLabel[0] = L'\0';
-
-    return EFI_SUCCESS;
-  }
-
-  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
-    EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
-
-    *BufferSize = sizeof *FileSystemVolumeLabel;
-    if (OriginalBufferSize < *BufferSize) {
-      return EFI_BUFFER_TOO_SMALL;
-    }
-
-    FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
-    FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
-
-    return EFI_SUCCESS;
-  }
-
-  return EFI_UNSUPPORTED;
-}
-
-
-/**
-  Sets information about a file.
-
-  @param[in] File             A pointer to the EFI_FILE_PROTOCOL instance that
-                              is the file handle the information is for.
-
-  @param[in] InformationType  The type identifier for the information being
-                              set.
-
-  @param[in] BufferSize       The size, in bytes, of Buffer.
-
-  @param[in] Buffer           A pointer to the data buffer to write. The
-                              buffer's type is indicated by InformationType.
-
-  @retval EFI_SUCCESS           The information was set.
-  @retval EFI_UNSUPPORTED       The InformationType is not known.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_WRITE_PROTECTED   InformationType is EFI_FILE_INFO_ID and the
-                                media is read-only.
-  @retval EFI_WRITE_PROTECTED   InformationType is
-                                EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media
-                                is read only.
-  @retval EFI_WRITE_PROTECTED   InformationType is
-                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media
-                                is read-only.
-  @retval EFI_ACCESS_DENIED     An attempt is made to change the name of a file
-                                to a file that is already present.
-  @retval EFI_ACCESS_DENIED     An attempt is being made to change the
-                                EFI_FILE_DIRECTORY Attribute.
-  @retval EFI_ACCESS_DENIED     An attempt is being made to change the size of
-                                a directory.
-  @retval EFI_ACCESS_DENIED     InformationType is EFI_FILE_INFO_ID and the
-                                file was opened read-only and an attempt is
-                                being made to modify a field other than
-                                Attribute.
-  @retval EFI_VOLUME_FULL       The volume is full.
-  @retval EFI_BAD_BUFFER_SIZE   BufferSize is smaller than the size of the type
-                                indicated by InformationType.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileSetInfo (
-  IN EFI_FILE_PROTOCOL *This,
-  IN EFI_GUID          *InformationType,
-  IN UINTN             BufferSize,
-  IN VOID              *Buffer
-  )
-{
-  return EFI_WRITE_PROTECTED;
-}
-
-
-/**
-  Flushes all modified data associated with a file to a device.
-
-  @param [in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
-                    file handle to flush.
-
-  @retval EFI_SUCCESS           The data was flushed.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
-  @retval EFI_ACCESS_DENIED     The file was opened read-only.
-  @retval EFI_VOLUME_FULL       The volume is full.
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileFlush (
-  IN EFI_FILE_PROTOCOL *This
-  )
-{
-  return EFI_WRITE_PROTECTED;
-}
-
-//
-// External definition of the file protocol template.
-//
-STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
-  EFI_FILE_PROTOCOL_REVISION, // revision 1
-  StubFileOpen,
-  StubFileClose,
-  StubFileDelete,
-  StubFileRead,
-  StubFileWrite,
-  StubFileGetPosition,
-  StubFileSetPosition,
-  StubFileGetInfo,
-  StubFileSetInfo,
-  StubFileFlush,
-  NULL,                       // OpenEx, revision 2
-  NULL,                       // ReadEx, revision 2
-  NULL,                       // WriteEx, revision 2
-  NULL                        // FlushEx, revision 2
-};
-
-
-//
-// Protocol member functions for SimpleFileSystem.
-//
-
-/**
-  Open the root directory on a volume.
-
-  @param[in]  This  A pointer to the volume to open the root directory on.
-
-  @param[out] Root  A pointer to the location to return the opened file handle
-                    for the root directory in.
-
-  @retval EFI_SUCCESS           The device was opened.
-  @retval EFI_UNSUPPORTED       This volume does not support the requested file
-                                system type.
-  @retval EFI_NO_MEDIA          The device has no medium.
-  @retval EFI_DEVICE_ERROR      The device reported an error.
-  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
-  @retval EFI_ACCESS_DENIED     The service denied access to the file.
-  @retval EFI_OUT_OF_RESOURCES  The volume was not opened due to lack of
-                                resources.
-  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
-                                medium is no longer supported. Any existing
-                                file handles for this volume are no longer
-                                valid. To access the files on the new medium,
-                                the volume must be reopened with OpenVolume().
-**/
-STATIC
-EFI_STATUS
-EFIAPI
-StubFileSystemOpenVolume (
-  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
-  OUT EFI_FILE_PROTOCOL              **Root
-  )
-{
-  STUB_FILE *StubFile;
-
-  StubFile = AllocatePool (sizeof *StubFile);
-  if (StubFile == NULL) {
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  StubFile->Signature = STUB_FILE_SIG;
-  StubFile->BlobType  = KernelBlobTypeMax;
-  StubFile->Position  = 0;
-  CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
-    sizeof mEfiFileProtocolTemplate);
-  *Root = &StubFile->File;
-
-  return EFI_SUCCESS;
-}
-
-STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
-  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
-  StubFileSystemOpenVolume
-};
-
-
-//
-// Utility functions.
-//
-
-/**
-  Populate a blob in mKernelBlob.
-
-  param[in,out] Blob  Pointer to the KERNEL_BLOB element in mKernelBlob that is
-                      to be filled from fw_cfg.
-
-  @retval EFI_SUCCESS           Blob has been populated. If fw_cfg reported a
-                                size of zero for the blob, then Blob->Data has
-                                been left unchanged.
-
-  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for Blob->Data.
-**/
-STATIC
-EFI_STATUS
-FetchBlob (
-  IN OUT KERNEL_BLOB *Blob
-  )
-{
-  UINT32 Left;
-
-  //
-  // Read blob size.
-  //
-  QemuFwCfgSelectItem (Blob->SizeKey);
-  Blob->Size = QemuFwCfgRead32 ();
-  if (Blob->Size == 0) {
-    return EFI_SUCCESS;
-  }
-
-  //
-  // Read blob.
-  //
-  Blob->Data = AllocatePool (Blob->Size);
-  if (Blob->Data == NULL) {
-    DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
-      __FUNCTION__, (INT64)Blob->Size, Blob->Name));
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
-    (INT64)Blob->Size, Blob->Name));
-  QemuFwCfgSelectItem (Blob->DataKey);
-
-  Left = Blob->Size;
-  do {
-    UINT32 Chunk;
-
-    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
-    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
-    Left -= Chunk;
-    DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
-      __FUNCTION__, (INT64)Left, Blob->Name));
-  } while (Left > 0);
-  return EFI_SUCCESS;
-}
-
-
 //
 // The entry point of the feature.
 //
@@ -916,83 +40,13 @@ TryRunningQemuKernel (
   VOID
   )
 {
-  UINTN                     BlobType;
-  KERNEL_BLOB               *CurrentBlob;
-  KERNEL_BLOB               *KernelBlob, *InitrdBlob, *CommandLineBlob;
   EFI_STATUS                Status;
-  EFI_HANDLE                FileSystemHandle;
-  EFI_DEVICE_PATH_PROTOCOL  *KernelDevicePath;
   EFI_HANDLE                KernelImageHandle;
-  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
 
-  Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
+  Status = QemuLoadKernelImage (&KernelImageHandle);
   if (EFI_ERROR (Status)) {
-    DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
-    return Status;
-  }
-
-  //
-  // Fetch all blobs.
-  //
-  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
-    CurrentBlob = &mKernelBlob[BlobType];
-    Status = FetchBlob (CurrentBlob);
-    if (EFI_ERROR (Status)) {
-      goto FreeBlobs;
-    }
-    mTotalBlobBytes += CurrentBlob->Size;
-  }
-  KernelBlob      = &mKernelBlob[KernelBlobTypeKernel];
-  InitrdBlob      = &mKernelBlob[KernelBlobTypeInitrd];
-  CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];
-
-  if (KernelBlob->Data == NULL) {
-    Status = EFI_NOT_FOUND;
-    goto FreeBlobs;
-  }
-
-  //
-  // Create a new handle with a single VenHw() node device path protocol on it,
-  // plus a custom SimpleFileSystem protocol on it.
-  //
-  FileSystemHandle = NULL;
-  Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
-                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
-                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
-                  NULL);
-  if (EFI_ERROR (Status)) {
-    DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
-      __FUNCTION__, Status));
-    goto FreeBlobs;
-  }
-
-  //
-  // Create a device path for the kernel image to be loaded from that will call
-  // back into our file system.
-  //
-  KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);
-  if (KernelDevicePath == NULL) {
-    DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",
-      __FUNCTION__));
-    Status = EFI_OUT_OF_RESOURCES;
-    goto UninstallProtocols;
-  }
-
-  //
-  // Load the image. This should call back into our file system.
-  //
-  Status = gBS->LoadImage (
-                  FALSE,             // BootPolicy: exact match required
-                  gImageHandle,      // ParentImageHandle
-                  KernelDevicePath,
-                  NULL,              // SourceBuffer
-                  0,                 // SourceSize
-                  &KernelImageHandle
-                  );
-  if (EFI_ERROR (Status)) {
-    DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
     if (Status != EFI_SECURITY_VIOLATION) {
-      goto FreeKernelDevicePath;
+      return Status;
     }
     //
     // From the resource allocation perspective, EFI_SECURITY_VIOLATION means
@@ -1001,74 +55,6 @@ TryRunningQemuKernel (
     goto UnloadKernelImage;
   }
 
-  //
-  // Construct the kernel command line.
-  //
-  Status = gBS->OpenProtocol (
-                  KernelImageHandle,
-                  &gEfiLoadedImageProtocolGuid,
-                  (VOID **)&KernelLoadedImage,
-                  gImageHandle,                  // AgentHandle
-                  NULL,                          // ControllerHandle
-                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
-                  );
-  ASSERT_EFI_ERROR (Status);
-
-  if (CommandLineBlob->Data == NULL) {
-    KernelLoadedImage->LoadOptionsSize = 0;
-  } else {
-    //
-    // Verify NUL-termination of the command line.
-    //
-    if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {
-      DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",
-        __FUNCTION__));
-      Status = EFI_PROTOCOL_ERROR;
-      goto UnloadKernelImage;
-    }
-
-    //
-    // Drop the terminating NUL, convert to UTF-16.
-    //
-    KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;
-  }
-
-  if (InitrdBlob->Data != NULL) {
-    //
-    // Append ' initrd=<name>' in UTF-16.
-    //
-    KernelLoadedImage->LoadOptionsSize +=
-                                        (8 + StrLen(InitrdBlob->Name)) * 2;
-  }
-
-  if (KernelLoadedImage->LoadOptionsSize == 0) {
-    KernelLoadedImage->LoadOptions = NULL;
-  } else {
-    //
-    // NUL-terminate in UTF-16.
-    //
-    KernelLoadedImage->LoadOptionsSize += 2;
-
-    KernelLoadedImage->LoadOptions = AllocatePool (
-                                       KernelLoadedImage->LoadOptionsSize);
-    if (KernelLoadedImage->LoadOptions == NULL) {
-      KernelLoadedImage->LoadOptionsSize = 0;
-      Status = EFI_OUT_OF_RESOURCES;
-      goto UnloadKernelImage;
-    }
-
-    UnicodeSPrintAsciiFormat (
-      KernelLoadedImage->LoadOptions,
-      KernelLoadedImage->LoadOptionsSize,
-      "%a%a%s",
-      (CommandLineBlob->Data == NULL) ?  "" : (CHAR8 *)CommandLineBlob->Data,
-      (InitrdBlob->Data      == NULL) ?  "" : " initrd=",
-      (InitrdBlob->Data      == NULL) ? L"" : InitrdBlob->Name
-      );
-    DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
-      (CHAR16 *)KernelLoadedImage->LoadOptions));
-  }
-
   //
   // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
   //
@@ -1080,41 +66,13 @@ TryRunningQemuKernel (
   //
   // Start the image.
   //
-  Status = gBS->StartImage (
-                KernelImageHandle,
-                NULL,              // ExitDataSize
-                NULL               // ExitData
-                );
+  Status = QemuStartKernelImage (KernelImageHandle);
   if (EFI_ERROR (Status)) {
     DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
   }
 
-  if (KernelLoadedImage->LoadOptions != NULL) {
-    FreePool (KernelLoadedImage->LoadOptions);
-  }
-  KernelLoadedImage->LoadOptionsSize = 0;
-
 UnloadKernelImage:
-  gBS->UnloadImage (KernelImageHandle);
-
-FreeKernelDevicePath:
-  FreePool (KernelDevicePath);
-
-UninstallProtocols:
-  gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
-         &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
-         &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
-         NULL);
-
-FreeBlobs:
-  while (BlobType > 0) {
-    CurrentBlob = &mKernelBlob[--BlobType];
-    if (CurrentBlob->Data != NULL) {
-      FreePool (CurrentBlob->Data);
-      CurrentBlob->Size = 0;
-      CurrentBlob->Data = NULL;
-    }
-  }
+  QemuUnloadKernelImage (KernelImageHandle);
 
   return Status;
 }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (5 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 17:31   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block Ard Biesheuvel
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

We have no need for exposing the kernel command line as a file,
so remove support for that. Since the remaining blobs (kernel
and initrd) are typically much larger than a page, switch to
the page based allocator for blobs at the same time.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
index efecbd817da1..b8d64e2781fc 100644
--- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
@@ -30,7 +30,6 @@
 typedef enum {
   KernelBlobTypeKernel,
   KernelBlobTypeInitrd,
-  KernelBlobTypeCommandLine,
   KernelBlobTypeMax
 } KERNEL_BLOB_TYPE;
 
@@ -45,7 +44,6 @@ typedef struct {
 STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
   { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
   { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
-  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
 };
 
 STATIC UINT64 mTotalBlobBytes;
@@ -865,7 +863,7 @@ FetchBlob (
   //
   // Read blob.
   //
-  Blob->Data = AllocatePool (Blob->Size);
+  Blob->Data = AllocatePages (EFI_SIZE_TO_PAGES (Blob->Size));
   if (Blob->Data == NULL) {
     DEBUG ((DEBUG_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
       __FUNCTION__, (INT64)Blob->Size, Blob->Name));
@@ -969,7 +967,7 @@ QemuKernelLoaderFsDxeEntrypoint (
   while (BlobType > 0) {
     CurrentBlob = &mKernelBlob[--BlobType];
     if (CurrentBlob->Data != NULL) {
-      FreePool (CurrentBlob->Data);
+      FreePages (CurrentBlob->Data, EFI_SIZE_TO_PAGES (CurrentBlob->Size));
       CurrentBlob->Size = 0;
       CurrentBlob->Data = NULL;
     }
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (6 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-02 17:58   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Ard Biesheuvel
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

On x86, the kernel image consists of a setup block and the actual kernel,
and QEMU presents these as separate blobs, whereas on disk (and in terms
of PE/COFF image signing), they consist of a single image.

So add support to our FS loader driver to expose files via the abstract
file system that consist of up to two concatenated blobs, and redefine
the kernel file so it consists of the setup and kernel blobs, on every
architecture (on non-x86, the setup block is simply 0 bytes and is
therefore ignored implicitly)

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 70 ++++++++++++++------
 1 file changed, 49 insertions(+), 21 deletions(-)

diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
index b8d64e2781fc..77d8fedb738a 100644
--- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
@@ -34,16 +34,29 @@ typedef enum {
 } KERNEL_BLOB_TYPE;
 
 typedef struct {
-  FIRMWARE_CONFIG_ITEM CONST SizeKey;
-  FIRMWARE_CONFIG_ITEM CONST DataKey;
-  CONST CHAR16 *       CONST Name;
-  UINT32                     Size;
-  UINT8                      *Data;
+  CONST CHAR16                  Name[8];
+  struct {
+    FIRMWARE_CONFIG_ITEM CONST  SizeKey;
+    FIRMWARE_CONFIG_ITEM CONST  DataKey;
+    UINT32                      Size;
+  }                             FwCfgItem[2];
+  UINT32                        Size;
+  UINT8                         *Data;
 } KERNEL_BLOB;
 
 STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
-  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
-  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
+  {
+    L"kernel",
+    {
+      { QemuFwCfgItemKernelSetupSize, QemuFwCfgItemKernelSetupData, },
+      { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      },
+    }
+  }, {
+    L"initrd",
+    {
+      { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      },
+    }
+  }
 };
 
 STATIC UINT64 mTotalBlobBytes;
@@ -850,12 +863,20 @@ FetchBlob (
   )
 {
   UINT32 Left;
+  UINTN  Idx;
+  UINT8  *ChunkData;
 
   //
   // Read blob size.
   //
-  QemuFwCfgSelectItem (Blob->SizeKey);
-  Blob->Size = QemuFwCfgRead32 ();
+  Blob->Size = 0;
+  for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
+    if (Blob->FwCfgItem[Idx].SizeKey == 0) {
+      break;
+    }
+    QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].SizeKey);
+    Blob->Size += Blob->FwCfgItem[Idx].Size = QemuFwCfgRead32 ();
+  }
   if (Blob->Size == 0) {
     return EFI_SUCCESS;
   }
@@ -872,18 +893,25 @@ FetchBlob (
 
   DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
     (INT64)Blob->Size, Blob->Name));
-  QemuFwCfgSelectItem (Blob->DataKey);
-
-  Left = Blob->Size;
-  do {
-    UINT32 Chunk;
-
-    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
-    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
-    Left -= Chunk;
-    DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
-      __FUNCTION__, (INT64)Left, Blob->Name));
-  } while (Left > 0);
+
+  ChunkData = Blob->Data;
+  for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
+    QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].DataKey);
+
+    Left = Blob->FwCfgItem[Idx].Size;
+    do {
+      UINT32 Chunk;
+
+      Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
+      QemuFwCfgReadBytes (Chunk, ChunkData + Blob->FwCfgItem[Idx].Size - Left);
+      Left -= Chunk;
+      DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\" (%d)\n",
+        __FUNCTION__, (INT64)Left, Blob->Name, Idx));
+    } while (Left > 0);
+
+    ChunkData += Blob->FwCfgItem[Idx].Size;
+  }
+
   return EFI_SUCCESS;
 }
 
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (7 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-03  9:45   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components Ard Biesheuvel
                   ` (3 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

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 a LoadedImage pseudo-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 loaded image solely by a handle. The pseudo-protocol
record type and the use of CR() is to get DEBUG coverage for the code
that deals with these handles.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c   | 562 ++++++++++++++++++++
 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf |  42 ++
 OvmfPkg/OvmfPkg.dec                                         |   1 +
 3 files changed, 605 insertions(+)

diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
new file mode 100644
index 000000000000..a1ced417d1cc
--- /dev/null
+++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
@@ -0,0 +1,562 @@
+/**  @file
+  X86 specific implementation of QemuLoadImageLib library class interface
+  with support for loading mixed mode images and non-EFI stub images
+
+  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+
+  SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Guid/QemuKernelLoaderFsMedia.h>
+#include <Library/DebugLib.h>
+#include <Library/LoadLinuxLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuLoadImageLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+
+#pragma pack (1)
+typedef struct {
+  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;
+  CHAR16                    FilePath[sizeof (L"kernel")];
+} KERNEL_FILE_DEVPATH;
+
+typedef struct {
+  VENDOR_DEVICE_PATH        VenMediaNode;
+  KERNEL_FILE_DEVPATH       FileNode;
+  EFI_DEVICE_PATH_PROTOCOL  EndNode;
+} KERNEL_VENMEDIA_FILE_DEVPATH;
+#pragma pack ()
+
+STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
+  {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+      { sizeof (VENDOR_DEVICE_PATH) }
+    },
+    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
+  }, {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
+      { sizeof (KERNEL_FILE_DEVPATH) }
+    },
+    L"kernel",
+  }, {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+  }
+};
+
+typedef struct {
+  VOID    *SetupBuf;
+  VOID    *KernelBuf;
+  CHAR8   *CommandLine;
+  VOID    *InitrdData;
+  UINTN   SetupSize;
+  UINTN   KernelInitialSize;
+  UINTN   InitrdSize;
+  UINTN   CommandLineSize;
+} QEMU_LEGACY_LOADED_IMAGE;
+
+#define QEMU_LEGACY_LOADED_IMAGE_SIG \
+          SIGNATURE_64 ('Q', 'L', 'O', 'A', 'D', 'I', 'M', 'G')
+
+typedef struct {
+  UINT64                    Signature;
+  QEMU_LEGACY_LOADED_IMAGE  LoadedImage;
+} QEMU_LEGACY_LOADED_IMAGE_REC;
+
+#define QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE(ImagePointer) \
+        CR (ImagePointer, QEMU_LEGACY_LOADED_IMAGE_REC, LoadedImage, \
+          QEMU_LEGACY_LOADED_IMAGE_SIG)
+
+STATIC
+VOID
+FreeLegacyImage (
+  IN  QEMU_LEGACY_LOADED_IMAGE *LoadedImage
+  )
+{
+  if (LoadedImage->SetupBuf != NULL) {
+    FreePages (LoadedImage->SetupBuf,
+      EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
+  }
+  if (LoadedImage->KernelBuf != NULL) {
+    FreePages (LoadedImage->KernelBuf,
+      EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));
+  }
+  if (LoadedImage->CommandLine != NULL) {
+    FreePages (LoadedImage->CommandLine,
+      EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));
+  }
+  if (LoadedImage->InitrdData != NULL) {
+    FreePages (LoadedImage->InitrdData,
+      EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
+  }
+}
+
+STATIC
+EFI_STATUS
+QemuLoadLegacyImage (
+  OUT EFI_HANDLE                  *ImageHandle
+  )
+{
+  EFI_STATUS                      Status;
+  UINTN                           KernelSize;
+  UINTN                           SetupSize;
+  QEMU_LEGACY_LOADED_IMAGE_REC    *LoadedImageRec;
+  QEMU_LEGACY_LOADED_IMAGE        *LoadedImage;
+
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
+  KernelSize = (UINTN)QemuFwCfgRead64 ();
+
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
+  SetupSize = (UINTN)QemuFwCfgRead64 ();
+
+  if (KernelSize == 0 || SetupSize == 0) {
+    DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
+    return EFI_NOT_FOUND;
+  }
+
+  LoadedImageRec = AllocateZeroPool (sizeof (*LoadedImageRec));
+  if (LoadedImageRec == NULL) {
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  LoadedImageRec->Signature = QEMU_LEGACY_LOADED_IMAGE_SIG;
+  LoadedImage = &LoadedImageRec->LoadedImage;
+
+  LoadedImage->SetupSize = SetupSize;
+  LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (
+                            EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
+  if (LoadedImage->SetupBuf == NULL) {
+    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));
+    return EFI_OUT_OF_RESOURCES;
+  }
+
+  DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));
+  DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
+  QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);
+  DEBUG ((DEBUG_INFO, " [done]\n"));
+
+  Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,
+             LoadedImage->SetupSize);
+  if (EFI_ERROR (Status)) {
+    goto FreeAndReturn;
+  }
+
+  Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
+  if (EFI_ERROR (Status)) {
+    goto FreeAndReturn;
+  }
+
+  LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
+                                     LoadedImage->SetupBuf, KernelSize);
+  if (LoadedImage->KernelInitialSize == 0) {
+    Status = EFI_UNSUPPORTED;
+    goto FreeAndReturn;
+  }
+
+  LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
+                             LoadedImage->SetupBuf,
+                             EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
+                             );
+  if (LoadedImage->KernelBuf == NULL) {
+    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
+    Status = EFI_OUT_OF_RESOURCES;
+    goto FreeAndReturn;
+  }
+
+  DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));
+  DEBUG ((DEBUG_INFO, "Reading kernel image ..."));
+  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
+  QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);
+  DEBUG ((DEBUG_INFO, " [done]\n"));
+
+  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+  LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead64 ();
+
+  if (LoadedImage->CommandLineSize > 0) {
+    LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
+                                 EFI_SIZE_TO_PAGES (
+                                   LoadedImage->CommandLineSize));
+    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+    QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
+  } else {
+    LoadedImage->CommandLine = NULL;
+  }
+
+  Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
+             LoadedImage->CommandLine);
+  if (EFI_ERROR (Status)) {
+    goto FreeAndReturn;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+  LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead64 ();
+
+  if (LoadedImage->InitrdSize > 0) {
+    LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
+                                LoadedImage->SetupBuf,
+                                EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
+    DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",
+      (UINT32)LoadedImage->InitrdSize));
+    DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
+    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
+    QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
+    DEBUG ((DEBUG_INFO, " [done]\n"));
+  } else {
+    LoadedImage->InitrdData = NULL;
+  }
+
+  Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
+             LoadedImage->InitrdSize);
+  if (EFI_ERROR (Status)) {
+    goto FreeAndReturn;
+  }
+
+  Status = gBS->InstallProtocolInterface (ImageHandle,
+                  &gX86QemuKernelLoadedImageGuid, EFI_NATIVE_INTERFACE,
+                  LoadedImage);
+  if (EFI_ERROR (Status)) {
+    goto FreeAndReturn;
+  }
+  return EFI_SUCCESS;
+
+FreeAndReturn:
+  FreeLegacyImage (LoadedImage);
+  FreePool (LoadedImageRec);
+  return Status;
+}
+
+STATIC
+EFI_STATUS
+QemuStartLegacyImage (
+  IN  EFI_HANDLE                ImageHandle
+  )
+{
+  EFI_STATUS                    Status;
+  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
+  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
+
+  Status = gBS->OpenProtocol (ImageHandle,
+                  &gX86QemuKernelLoadedImageGuid,
+                  (VOID **)&LoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
+
+  return LoadLinux (LoadedImageRec->LoadedImage.KernelBuf,
+                    LoadedImageRec->LoadedImage.SetupBuf);
+}
+
+STATIC
+EFI_STATUS
+QemuUnloadLegacyImage (
+  IN  EFI_HANDLE          ImageHandle
+  )
+{
+  EFI_STATUS                    Status;
+  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
+  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
+
+  Status = gBS->OpenProtocol (ImageHandle,
+                  &gX86QemuKernelLoadedImageGuid,
+                  (VOID **)&LoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (EFI_ERROR (Status)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
+
+  FreeLegacyImage (&LoadedImageRec->LoadedImage);
+  FreePool (LoadedImageRec);
+  return EFI_SUCCESS;
+}
+
+/**
+  Download the kernel, the initial ramdisk, and the kernel command line from
+  QEMU's fw_cfg. The kernel will be instructed via its command line to load
+  the initrd from the same Simple FileSystem.
+
+  @param[out] ImageHandle       The image handle that was allocated for
+                                loading the image
+  @param[out] LoadedImage       The loaded image protocol that was installed
+                                on ImageHandle by the LoadImage boot service.
+
+  @retval EFI_SUCCESS           The image was loaded successfully.
+  @retval EFI_NOT_FOUND         Kernel image was not found.
+  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
+  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
+
+  @return                       Error codes from any of the underlying
+                                functions.
+**/
+EFI_STATUS
+EFIAPI
+QemuLoadKernelImage (
+  OUT EFI_HANDLE            *ImageHandle
+  )
+{
+  EFI_STATUS                Status;
+  EFI_HANDLE                KernelImageHandle;
+  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
+  UINTN                     CommandLineSize;
+  CHAR8                     *CommandLine;
+  UINTN                     InitrdSize;
+
+  //
+  // Load the image. This should call back into the QEMU EFI loader file system.
+  //
+  Status = gBS->LoadImage (
+                  FALSE,                    // BootPolicy: exact match required
+                  gImageHandle,             // ParentImageHandle
+                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
+                  NULL,                     // SourceBuffer
+                  0,                        // SourceSize
+                  &KernelImageHandle
+                  );
+  switch (Status) {
+  case EFI_SUCCESS:
+    break;
+
+  case EFI_NOT_FOUND:
+    //
+    // The image does not exist - no -kernel image was supplied via the
+    // command line so no point in invoking the legacy fallback
+    //
+    return EFI_NOT_FOUND;
+
+  case EFI_SECURITY_VIOLATION:
+    //
+    // We are running with UEFI secure boot enabled, and the image failed to
+    // authenticate. For compatibility reasons, we fall back to the legacy
+    // loader in this case. Since the image has been loaded, we need to unload
+    // it before proceeding
+    //
+    gBS->UnloadImage (KernelImageHandle);
+    //
+    // Fall through
+    //
+  case EFI_UNSUPPORTED:
+    //
+    // The image is not natively supported or cross-type supported. Let's try
+    // loading it using the loader that parses the bzImage metadata directly.
+    //
+    KernelImageHandle = NULL;
+    Status = QemuLoadLegacyImage (&KernelImageHandle);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
+        Status));
+      return Status;
+    }
+    *ImageHandle = KernelImageHandle;
+    return EFI_SUCCESS;
+
+  default:
+    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
+    return Status;
+  }
+
+  //
+  // Construct the kernel command line.
+  //
+  Status = gBS->OpenProtocol (
+                  KernelImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **)&KernelLoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  ASSERT_EFI_ERROR (Status);
+
+  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
+  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
+
+  if (CommandLineSize == 0) {
+    KernelLoadedImage->LoadOptionsSize = 0;
+  } else {
+    CommandLine = AllocatePool (CommandLineSize);
+    ASSERT (CommandLine != NULL);
+
+    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
+    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
+
+    //
+    // Verify NUL-termination of the command line.
+    //
+    if (CommandLine[CommandLineSize - 1] != '\0') {
+      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
+        __FUNCTION__));
+      Status = EFI_PROTOCOL_ERROR;
+      goto FreeCommandLine;
+    }
+
+    //
+    // Drop the terminating NUL, convert to UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;
+  }
+
+  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
+  InitrdSize = (UINTN) QemuFwCfgRead64 ();
+
+  if (InitrdSize > 0) {
+    //
+    // Append ' initrd=initrd' in UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
+  }
+
+  if (KernelLoadedImage->LoadOptionsSize == 0) {
+    KernelLoadedImage->LoadOptions = NULL;
+  } else {
+    //
+    // NUL-terminate in UTF-16.
+    //
+    KernelLoadedImage->LoadOptionsSize += 2;
+
+    KernelLoadedImage->LoadOptions = AllocatePool (
+                                       KernelLoadedImage->LoadOptionsSize);
+    if (KernelLoadedImage->LoadOptions == NULL) {
+      KernelLoadedImage->LoadOptionsSize = 0;
+      Status = EFI_OUT_OF_RESOURCES;
+      goto FreeCommandLine;
+    }
+
+    UnicodeSPrintAsciiFormat (
+      KernelLoadedImage->LoadOptions,
+      KernelLoadedImage->LoadOptionsSize,
+      "%a%a",
+      (CommandLineSize == 0) ?  "" : CommandLine,
+      (InitrdSize == 0)      ?  "" : " initrd=initrd"
+      );
+    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
+      (CHAR16 *)KernelLoadedImage->LoadOptions));
+  }
+
+  *ImageHandle = KernelImageHandle;
+  return EFI_SUCCESS;
+
+FreeCommandLine:
+  FreePool (CommandLine);
+  gBS->UnloadImage (KernelImageHandle);
+
+  return Status;
+}
+
+/**
+  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
+
+  @param[in]  ImageHandle         Handle of image to be started.
+
+  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
+                                  or the image has already been initialized with
+                                  StartImage
+  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
+                                  image should not be started.
+
+  @return                         Error codes returned by the started image
+**/
+EFI_STATUS
+EFIAPI
+QemuStartKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  )
+{
+  EFI_STATUS                Status;
+
+  Status = gBS->StartImage (
+                  ImageHandle,
+                  NULL,              // ExitDataSize
+                  NULL               // ExitData
+                  );
+
+  switch (Status) {
+#ifdef MDE_CPU_IA32
+  case EFI_UNSUPPORTED:
+    //
+    // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
+    // we are expecting a IA32 one, and the StartImage () boot service is unable
+    // to handle it, either because the image does not have the special .compat
+    // PE/COFF section that Linux specifies for mixed mode capable images, or
+    // because we are running without the support code for that. So do a legacy
+    // load instead, but do it first so we can reuse the same handle. Then,
+    // unload the normally loaded image.
+    //
+    Status = QemuLoadLegacyImage (&ImageHandle);
+    if (EFI_ERROR (Status)) {
+      return Status;
+    }
+    QemuUnloadKernelImage (ImageHandle);
+    //
+    // Fall through
+    //
+#endif
+  case EFI_INVALID_PARAMETER:
+    return QemuStartLegacyImage (ImageHandle);
+  default:
+    break;
+  }
+  return Status;
+}
+
+/**
+  Unloads an image loaded with QemuLoadKernelImage ().
+
+  @param  ImageHandle             Handle that identifies the image to be
+                                  unloaded.
+
+  @retval EFI_SUCCESS             The image has been unloaded.
+  @retval EFI_UNSUPPORTED         The image has been started, and does not
+                                  support unload.
+  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
+
+**/
+EFI_STATUS
+EFIAPI
+QemuUnloadKernelImage (
+  IN  EFI_HANDLE          ImageHandle
+  )
+{
+  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;
+  EFI_STATUS                  Status;
+
+  Status = gBS->OpenProtocol (
+                  ImageHandle,
+                  &gEfiLoadedImageProtocolGuid,
+                  (VOID **)&KernelLoadedImage,
+                  gImageHandle,                  // AgentHandle
+                  NULL,                          // ControllerHandle
+                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
+                  );
+  if (Status == EFI_UNSUPPORTED) {
+    return QemuUnloadLegacyImage (ImageHandle);
+  } else if (EFI_ERROR (Status)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (KernelLoadedImage->LoadOptions != NULL) {
+    FreePool (KernelLoadedImage->LoadOptions);
+    KernelLoadedImage->LoadOptions = NULL;
+  }
+  KernelLoadedImage->LoadOptionsSize = 0;
+
+  return gBS->UnloadImage (ImageHandle);
+}
diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
new file mode 100644
index 000000000000..4208f5da3b31
--- /dev/null
+++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
@@ -0,0 +1,42 @@
+## @file
+#  X86 specific implementation of QemuLoadImageLib library class interface
+#  with support for loading mixed mode images and non-EFI stub images
+#
+#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
+#
+#  SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  INF_VERSION                    = 1.27
+  BASE_NAME                      = X86QemuLoadImageLib
+  FILE_GUID                      = 2304df80-e21d-4170-9c3c-113c878f7ac0
+  MODULE_TYPE                    = BASE
+  VERSION_STRING                 = 1.0
+  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER
+
+[Sources]
+  X86QemuLoadImageLib.c
+
+[Packages]
+  MdeModulePkg/MdeModulePkg.dec
+  MdePkg/MdePkg.dec
+  OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+  DebugLib
+  MemoryAllocationLib
+  LoadLinuxLib
+  PrintLib
+  QemuFwCfgLib
+  ReportStatusCodeLib
+  UefiBootServicesTableLib
+
+[Protocols]
+  gEfiDevicePathProtocolGuid
+  gEfiLoadedImageProtocolGuid
+
+[Guids]
+  gQemuKernelLoaderFsMediaGuid
+  gX86QemuKernelLoadedImageGuid
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index 26f977bad795..e66af38f4290 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -93,6 +93,7 @@ [Guids]
   gEfiLegacyDevOrderVariableGuid      = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}}
   gLinuxEfiInitrdMediaGuid            = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}
   gQemuKernelLoaderFsMediaGuid        = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
+  gX86QemuKernelLoadedImageGuid       = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}}
 
 [Protocols]
   gVirtioDeviceProtocolGuid           = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (8 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-03  9:47   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Ard Biesheuvel
                   ` (2 subsequent siblings)
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Add the components that expose the QEMU abstract loader file system so
that we can switch over our PlatformBmLib over to it in a subsequent
patch.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/OvmfPkgIa32.dsc    | 2 ++
 OvmfPkg/OvmfPkgIa32.fdf    | 1 +
 OvmfPkg/OvmfPkgIa32X64.dsc | 2 ++
 OvmfPkg/OvmfPkgIa32X64.fdf | 1 +
 OvmfPkg/OvmfPkgX64.dsc     | 2 ++
 OvmfPkg/OvmfPkgX64.fdf     | 1 +
 6 files changed, 9 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 8d91903f8b4e..2cc924a6986a 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -361,6 +361,7 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
@@ -711,6 +712,7 @@ [Components]
       NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
 !endif
   }
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
   OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
   OvmfPkg/Virtio10Dxe/Virtio10.inf
   OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index f57de4a26f92..61287bd51f84 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -242,6 +242,7 @@ [FV.DXEFV]
 INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
 INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
 INF  MdeModulePkg/Application/UiApp/UiApp.inf
+INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
 INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
 INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 842b4a028913..21d1f156973b 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -365,6 +365,7 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
@@ -723,6 +724,7 @@ [Components.X64]
       NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
 !endif
   }
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
   OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
   OvmfPkg/Virtio10Dxe/Virtio10.inf
   OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 69c133ec08d5..7b770f8fa424 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -243,6 +243,7 @@ [FV.DXEFV]
 INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
 INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
 INF  MdeModulePkg/Application/UiApp/UiApp.inf
+INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
 INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
 INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 0b1e45d1f15a..f3d0f18db7e2 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -365,6 +365,7 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
@@ -721,6 +722,7 @@ [Components]
       NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
 !endif
   }
+  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
   OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
   OvmfPkg/Virtio10Dxe/Virtio10.inf
   OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index 69c133ec08d5..7b770f8fa424 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -243,6 +243,7 @@ [FV.DXEFV]
 INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
 INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
 INF  MdeModulePkg/Application/UiApp/UiApp.inf
+INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
 INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
 INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
 INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (9 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-03  9:52   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Ard Biesheuvel
  2020-03-02  7:29 ` [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds Ard Biesheuvel
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Replace the open coded sequence to load Linux on x86 with a short and
generic sequence invoking QemuLoadImageLib, which can be provided by
a generic version that only supports the LoadImage and StartImage boot
services, and one that incorporates the entire legacy loading sequence
as well.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |   2 +-
 OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c               | 157 +++-----------------
 2 files changed, 24 insertions(+), 135 deletions(-)

diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
index f89cce187942..40ac5dd7f9d5 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
+++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
@@ -48,7 +48,7 @@ [LibraryClasses]
   NvVarsFileLib
   QemuFwCfgLib
   QemuFwCfgS3Lib
-  LoadLinuxLib
+  QemuLoadImageLib
   QemuBootOrderLib
   ReportStatusCodeLib
   UefiLib
diff --git a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
index ddfef925edd3..a15b48d360d2 100644
--- a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
+++ b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
@@ -9,11 +9,8 @@
 
 #include <Library/BaseLib.h>
 #include <Library/DebugLib.h>
-#include <Library/LoadLinuxLib.h>
-#include <Library/MemoryAllocationLib.h>
-#include <Library/QemuFwCfgLib.h>
+#include <Library/QemuLoadImageLib.h>
 #include <Library/ReportStatusCodeLib.h>
-#include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiLib.h>
 
 
@@ -23,146 +20,38 @@ TryRunningQemuKernel (
   )
 {
   EFI_STATUS                Status;
-  UINTN                     KernelSize;
-  UINTN                     KernelInitialSize;
-  VOID                      *KernelBuf;
-  UINTN                     SetupSize;
-  VOID                      *SetupBuf;
-  UINTN                     CommandLineSize;
-  CHAR8                     *CommandLine;
-  UINTN                     InitrdSize;
-  VOID*                     InitrdData;
-
-  SetupBuf = NULL;
-  SetupSize = 0;
-  KernelBuf = NULL;
-  KernelInitialSize = 0;
-  CommandLine = NULL;
-  CommandLineSize = 0;
-  InitrdData = NULL;
-  InitrdSize = 0;
-
-  if (!QemuFwCfgIsAvailable ()) {
-    return EFI_NOT_FOUND;
-  }
-
-  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
-  KernelSize = (UINTN) QemuFwCfgRead64 ();
-
-  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
-  SetupSize = (UINTN) QemuFwCfgRead64 ();
-
-  if (KernelSize == 0 || SetupSize == 0) {
-    DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n"));
-    return EFI_NOT_FOUND;
-  }
-
-  SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize));
-  if (SetupBuf == NULL) {
-    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n"));
-    return EFI_OUT_OF_RESOURCES;
-  }
-
-  DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize));
-  DEBUG ((EFI_D_INFO, "Reading kernel setup image ..."));
-  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
-  QemuFwCfgReadBytes (SetupSize, SetupBuf);
-  DEBUG ((EFI_D_INFO, " [done]\n"));
-
-  Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize);
-  if (EFI_ERROR (Status)) {
-    goto FreeAndReturn;
-  }
-
-  Status = LoadLinuxInitializeKernelSetup (SetupBuf);
-  if (EFI_ERROR (Status)) {
-    goto FreeAndReturn;
-  }
-
-  KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize);
-  if (KernelInitialSize == 0) {
-    Status = EFI_UNSUPPORTED;
-    goto FreeAndReturn;
-  }
-
-  KernelBuf = LoadLinuxAllocateKernelPages (
-                SetupBuf,
-                EFI_SIZE_TO_PAGES (KernelInitialSize));
-  if (KernelBuf == NULL) {
-    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n"));
-    Status = EFI_OUT_OF_RESOURCES;
-    goto FreeAndReturn;
-  }
-
-  DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize));
-  DEBUG ((EFI_D_INFO, "Reading kernel image ..."));
-  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
-  QemuFwCfgReadBytes (KernelSize, KernelBuf);
-  DEBUG ((EFI_D_INFO, " [done]\n"));
-
-  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
-  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
-
-  if (CommandLineSize > 0) {
-    CommandLine = LoadLinuxAllocateCommandLinePages (
-                    EFI_SIZE_TO_PAGES (CommandLineSize));
-    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
-    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
-  } else {
-    CommandLine = NULL;
-  }
-
-  Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine);
-  if (EFI_ERROR (Status)) {
-    goto FreeAndReturn;
-  }
-
-  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
-  InitrdSize = (UINTN) QemuFwCfgRead64 ();
-
-  if (InitrdSize > 0) {
-    InitrdData = LoadLinuxAllocateInitrdPages (
-                   SetupBuf,
-                   EFI_SIZE_TO_PAGES (InitrdSize)
-                   );
-    DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize));
-    DEBUG ((EFI_D_INFO, "Reading initrd image ..."));
-    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
-    QemuFwCfgReadBytes (InitrdSize, InitrdData);
-    DEBUG ((EFI_D_INFO, " [done]\n"));
-  } else {
-    InitrdData = NULL;
-  }
-
-  Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize);
-  if (EFI_ERROR (Status)) {
-    goto FreeAndReturn;
+  EFI_HANDLE                KernelImageHandle;
+
+  Status = QemuLoadKernelImage (&KernelImageHandle);
+  if (EFI_ERROR (Status)) {
+    if (Status != EFI_SECURITY_VIOLATION) {
+      return Status;
+    }
+    //
+    // From the resource allocation perspective, EFI_SECURITY_VIOLATION means
+    // "success", so we must roll back the image loading.
+    //
+    goto UnloadKernelImage;
   }
 
   //
-  // Signal the EVT_SIGNAL_READY_TO_BOOT event
+  // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
   //
   EfiSignalEventReadyToBoot();
 
   REPORT_STATUS_CODE (EFI_PROGRESS_CODE,
     (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
 
-  Status = LoadLinux (KernelBuf, SetupBuf);
+  //
+  // Start the image.
+  //
+  Status = QemuStartKernelImage (KernelImageHandle);
+  if (EFI_ERROR (Status)) {
+    DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
+  }
 
-FreeAndReturn:
-  if (SetupBuf != NULL) {
-    FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize));
-  }
-  if (KernelBuf != NULL) {
-    FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize));
-  }
-  if (CommandLine != NULL) {
-    FreePages (CommandLine, EFI_SIZE_TO_PAGES (CommandLineSize));
-  }
-  if (InitrdData != NULL) {
-    FreePages (InitrdData, EFI_SIZE_TO_PAGES (InitrdSize));
-  }
+UnloadKernelImage:
+  QemuUnloadKernelImage (KernelImageHandle);
 
   return Status;
 }
-
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (10 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-03 10:10   ` [edk2-devel] " Laszlo Ersek
  2020-03-02  7:29 ` [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds Ard Biesheuvel
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

Linux v5.7 will introduce a new method to load the initial ramdisk
(initrd) from the loader, using the LoadFile2 protocol installed on a
special vendor GUIDed media device path.

Add support for this to our QEMU command line kernel/initrd loader.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 79 ++++++++++++++++++++
 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  2 +
 2 files changed, 81 insertions(+)

diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
index 77d8fedb738a..415946edf22a 100644
--- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
@@ -13,15 +13,18 @@
 #include <Guid/FileInfo.h>
 #include <Guid/FileSystemInfo.h>
 #include <Guid/FileSystemVolumeLabelInfo.h>
+#include <Guid/LinuxEfiInitrdMedia.h>
 #include <Guid/QemuKernelLoaderFsMedia.h>
 #include <Library/BaseLib.h>
 #include <Library/BaseMemoryLib.h>
 #include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
 #include <Library/MemoryAllocationLib.h>
 #include <Library/QemuFwCfgLib.h>
 #include <Library/UefiBootServicesTableLib.h>
 #include <Library/UefiRuntimeServicesTableLib.h>
 #include <Protocol/DevicePath.h>
+#include <Protocol/LoadFile2.h>
 #include <Protocol/SimpleFileSystem.h>
 
 //
@@ -84,6 +87,19 @@ STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = {
   }
 };
 
+STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = {
+  {
+    {
+      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
+      { sizeof (VENDOR_DEVICE_PATH) }
+    },
+    LINUX_EFI_INITRD_MEDIA_GUID
+  }, {
+    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+  }
+};
+
 //
 // The "file in the EFI stub filesystem" abstraction.
 //
@@ -839,6 +855,48 @@ STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
   StubFileSystemOpenVolume
 };
 
+STATIC
+EFI_STATUS
+EFIAPI
+InitrdLoadFile2 (
+  IN EFI_LOAD_FILE2_PROTOCOL          *This,
+  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
+  IN BOOLEAN                          BootPolicy,
+  IN OUT UINTN                        *BufferSize,
+  IN VOID                             *Buffer OPTIONAL
+  )
+{
+  CONST KERNEL_BLOB   *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];
+
+  ASSERT (InitrdBlob->Size > 0);
+
+  if (BootPolicy) {
+    return EFI_UNSUPPORTED;
+  }
+
+  if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
+    return EFI_INVALID_PARAMETER;
+  }
+
+  if (FilePath->Type != END_DEVICE_PATH_TYPE ||
+      FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) {
+    return EFI_NOT_FOUND;
+  }
+
+  if (Buffer == NULL || *BufferSize < InitrdBlob->Size) {
+    *BufferSize = InitrdBlob->Size;
+    return EFI_BUFFER_TOO_SMALL;
+  }
+
+  CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size);
+
+  *BufferSize = InitrdBlob->Size;
+  return EFI_SUCCESS;
+}
+
+STATIC CONST EFI_LOAD_FILE2_PROTOCOL     mInitrdLoadFile2 = {
+  InitrdLoadFile2,
+};
 
 //
 // Utility functions.
@@ -945,6 +1003,7 @@ QemuKernelLoaderFsDxeEntrypoint (
   KERNEL_BLOB               *KernelBlob;
   EFI_STATUS                Status;
   EFI_HANDLE                FileSystemHandle;
+  EFI_HANDLE                InitrdLoadFile2Handle;
 
   if (!QemuFwCfgIsAvailable ()) {
     return EFI_NOT_FOUND;
@@ -989,8 +1048,28 @@ QemuKernelLoaderFsDxeEntrypoint (
     goto FreeBlobs;
   }
 
+  if (KernelBlob[KernelBlobTypeInitrd].Size > 0) {
+    InitrdLoadFile2Handle = NULL;
+    Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle,
+                    &gEfiDevicePathProtocolGuid,  &mInitrdDevicePath,
+                    &gEfiLoadFile2ProtocolGuid,   &mInitrdLoadFile2,
+                    NULL);
+    if (EFI_ERROR (Status)) {
+      DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
+        __FUNCTION__, Status));
+      goto UninstallFileSystemHandle;
+    }
+  }
+
   return EFI_SUCCESS;
 
+UninstallFileSystemHandle:
+  Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
+                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
+                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
+                  NULL);
+  ASSERT_EFI_ERROR (Status);
+
 FreeBlobs:
   while (BlobType > 0) {
     CurrentBlob = &mKernelBlob[--BlobType];
diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
index f4b50c265027..737f9b87a7c7 100644
--- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
+++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
@@ -28,6 +28,7 @@ [LibraryClasses]
   BaseLib
   BaseMemoryLib
   DebugLib
+  DevicePathLib
   MemoryAllocationLib
   UefiBootServicesTableLib
   QemuFwCfgLib
@@ -42,6 +43,7 @@ [Guids]
 
 [Protocols]
   gEfiDevicePathProtocolGuid                ## PRODUCES
+  gEfiLoadFile2ProtocolGuid                 ## PRODUCES
   gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
 
 [Depex]
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds
  2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
                   ` (11 preceding siblings ...)
  2020-03-02  7:29 ` [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Ard Biesheuvel
@ 2020-03-02  7:29 ` Ard Biesheuvel
  2020-03-03 10:13   ` [edk2-devel] " Laszlo Ersek
  12 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-02  7:29 UTC (permalink / raw)
  To: devel; +Cc: lersek, Ard Biesheuvel

The QemuLoadImageLib implementation we currently use for all OVMF
builds copies the behavior of the QEMU loader code that precedes it,
which is to disregard UEFI secure boot policies entirely when it comes
to loading kernel images that have been specified on the QEMU command
line. This behavior deviates from ArmVirtQemu based builds, which do
take UEFI secure boot policies into account, and refuse to load images
from the command line that cannot be authenticated.

The disparity was originally due to the fact that the QEMU command line
kernel loader did not use LoadImage and StartImage at all, but this
changed recently, and now, there are only a couple of reasons left to
stick with the legacy loader:
- it permits loading images that lack a valid PE/COFF header,
- it permits loading X64 kernels on IA32 firmware running on a X64
  capable system.

Since every non-authentic PE/COFF image can trivially be converted into
an image that lacks a valid PE/COFF header, the former case can simply
not be supported in a UEFI secure boot context. The latter case is highly
theoretical, given that one could easily switch to native X64 firmware in
a VM scenario.

That leaves us with little justification to use the legacy loader at all
when UEFI secure boot policies are in effect, so let's switch to the
generic loader for UEFI secure boot enabled builds.

Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
---
 OvmfPkg/OvmfPkgIa32.dsc    | 4 ++++
 OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++
 OvmfPkg/OvmfPkgX64.dsc     | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 2cc924a6986a..eceddb71948f 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -361,7 +361,11 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
+!else
   QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
+!endif
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 21d1f156973b..8bdf2e692b00 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -365,7 +365,11 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
+!else
   QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
+!endif
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index f3d0f18db7e2..bc0a3e438d2a 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -365,7 +365,11 @@ [LibraryClasses.common.DXE_DRIVER]
   PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
   MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
   QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
+!if $(SECURE_BOOT_ENABLE) == TRUE
+  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
+!else
   QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
+!endif
 !if $(TPM2_ENABLE) == TRUE
   Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
 !endif
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path
  2020-03-02  7:29 ` [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
@ 2020-03-02 13:22   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 13:22 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> In an upcoming patch, we will introduce a separate DXE driver that
> exposes the virtual SimpleFileSystem implementation that carries the
> kernel and initrd passed via the QEMU command line, and a separate
> library that consumes it, to be incorporated into the boot manager.
> 
> Since the GUID used for the SimpleFileSystem implementation's device
> path will no longer be for internal use only, create a well defined
> GUID to identify the media device path.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h | 18 ++++++++++++++++++
>  OvmfPkg/OvmfPkg.dec                            |  1 +
>  2 files changed, 19 insertions(+)
> 
> diff --git a/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h
> new file mode 100644
> index 000000000000..225c3c494613
> --- /dev/null
> +++ b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h
> @@ -0,0 +1,18 @@
> +/** @file
> +  GUID definition for the QEMU LoaderFs media device path, containing the
> +  kernel, initrd and command line as file objects
> +
> +  Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#ifndef QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__
> +#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__
> +
> +#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \
> +  {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
> +
> +extern EFI_GUID gQemuKernelLoaderFsMediaGuid;
> +
> +#endif
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 6849a79cd8b0..d88778600517 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -87,6 +87,7 @@ [Guids]
>    gEfiLegacyBiosGuid                  = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}}
>    gEfiLegacyDevOrderVariableGuid      = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}}
>    gLinuxEfiInitrdMediaGuid            = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}
> +  gQemuKernelLoaderFsMediaGuid        = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
>  
>  [Protocols]
>    gVirtioDeviceProtocolGuid           = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver
  2020-03-02  7:29 ` [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Ard Biesheuvel
@ 2020-03-02 13:45   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 13:45 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Expose the existing implementation of an abstract filesystem exposing
> the blobs passed to QEMU via the command line via a standalone DXE
> driver.

"git show --find-copies-harder" works wonders while reviewing this patch :)

> 
> Notable difference with the original code is the switch to a new vendor
> GUIDed media device path, as opposed to a vendor GUID hardware device
> path, which is not entirely appropriate for pure software constructs.

Good point!

> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 979 ++++++++++++++++++++
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  48 +
>  2 files changed, 1027 insertions(+)
> 
> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> new file mode 100644
> index 000000000000..efecbd817da1
> --- /dev/null
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> @@ -0,0 +1,979 @@
> +/** @file
> +  DXE driver to expose the 'kernel', 'initrd' and 'cmdline' blobs
> +  provided by QEMU as file in an abstract file system

(1) s/file/files/

[...]

> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> new file mode 100644
> index 000000000000..f4b50c265027
> --- /dev/null
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> @@ -0,0 +1,48 @@
> +##  @file
> +#  DXE driver to expose the 'kernel', 'initrd' and 'cmdline' blobs
> +#  provided by QEMU as file in an abstract file system

(2) same as (1)

> +#
> +#  Copyright (C) 2014-2016, Red Hat, Inc.
> +#  Copyright (C) 2020, Arm, Limited.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.27
> +  BASE_NAME                      = QemuKernelLoaderFsDxe
> +  FILE_GUID                      = 806040ca-dad9-4978-a3b4-2d2ab0c8a48f
> +  MODULE_TYPE                    = DXE_DRIVER
> +  VERSION_STRING                 = 1.0
> +  ENTRY_POINT                    = QemuKernelLoaderFsDxeEntrypoint
> +
> +[Sources]
> +  QemuKernelLoaderFsDxe.c
> +
> +[Packages]
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +  OvmfPkg/OvmfPkg.dec
> +
> +[LibraryClasses]
> +  BaseLib
> +  BaseMemoryLib
> +  DebugLib
> +  MemoryAllocationLib
> +  UefiBootServicesTableLib
> +  QemuFwCfgLib

(3) slight disorder

> +  UefiDriverEntryPoint
> +  UefiRuntimeServicesTableLib
> +
> +[Guids]
> +  gEfiFileInfoGuid
> +  gEfiFileSystemInfoGuid
> +  gEfiFileSystemVolumeLabelInfoIdGuid
> +  gQemuKernelLoaderFsMediaGuid
> +
> +[Protocols]
> +  gEfiDevicePathProtocolGuid                ## PRODUCES
> +  gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
> +
> +[Depex]
> +  gEfiRealTimeClockArchProtocolGuid
> 

Good point about the depex; per PI vol 2, 12.6 Real Time Clock
Architectural Protocol, we need this for gRT->GetTime(). The previous
implementation can tage GetTime for granted, because it is linked into
BdsDxe, which ensures that all the architectural protocols have been
installed.

(4) Can you please add a short note about this (the depex) in the commit
message?

With (1) to (4) addressed:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thanks!
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class
  2020-03-02  7:29 ` [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class Ard Biesheuvel
@ 2020-03-02 14:07   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 14:07 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Introduce the QemuLoadImageLib library class that we will instantiate
> to load the kernel image passed via the QEMU command line using the
> standard LoadImage boot service.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/Include/Library/QemuLoadImageLib.h | 78 ++++++++++++++++++++
>  OvmfPkg/OvmfPkg.dec                        |  5 ++
>  2 files changed, 83 insertions(+)
> 
> diff --git a/OvmfPkg/Include/Library/QemuLoadImageLib.h b/OvmfPkg/Include/Library/QemuLoadImageLib.h
> new file mode 100644
> index 000000000000..304853096593
> --- /dev/null
> +++ b/OvmfPkg/Include/Library/QemuLoadImageLib.h
> @@ -0,0 +1,78 @@
> +/** @file
> +  Load a kernel image and command line passed to QEMU via
> +  the command line
> +
> +  Copyright (C) 2020, Arm, Limited.
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#ifndef QEMU_LOAD_IMAGE_LIB_H__
> +#define QEMU_LOAD_IMAGE_LIB_H__
> +
> +#include <Uefi/UefiBaseType.h>
> +#include <Base.h>
> +
> +#include <Protocol/LoadedImage.h>
> +
> +/**
> +  Download the kernel, the initial ramdisk, and the kernel command line from
> +  QEMU's fw_cfg. The kernel will be instructed via its command line to load
> +  the initrd from the same Simple FileSystem.

(1) ... "where the kernel was loaded from".

> +
> +  @param[out] ImageHandle       The image handle that was allocated for
> +                                loading the image
> +
> +  @retval EFI_SUCCESS           The image was loaded successfully.
> +  @retval EFI_NOT_FOUND         Kernel image was not found.
> +  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
> +  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
> +
> +  @return                       Error codes from any of the underlying
> +                                functions.
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuLoadKernelImage (
> +  OUT EFI_HANDLE          *ImageHandle
> +  );
> +
> +/**
> +  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
> +
> +  @param[in]  ImageHandle         Handle of image to be started.
> +
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
> +                                  or the image has already been initialized with
> +                                  StartImage
> +  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
> +                                  image should not be started.
> +
> +  @return                         Error codes returned by the started image.
> +                                  On success, the function doesn't return.
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuStartKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  );
> +
> +/**
> +  Unloads an image loaded with QemuLoadKernelImage ().
> +
> +  @param  ImageHandle             Handle that identifies the image to be
> +                                  unloaded.
> +
> +  @retval EFI_SUCCESS             The image has been unloaded.
> +  @retval EFI_UNSUPPORTED         The image has been started, and does not
> +                                  support unload.
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.

(2) Please append:

@return  Exit code from the image’s unload function.

> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuUnloadKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  );
> +
> +#endif
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index d88778600517..26f977bad795 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -58,6 +58,11 @@ [LibraryClasses]
>    #
>    QemuBootOrderLib|Include/Library/QemuBootOrderLib.h
>  
> +  ##  @libraryclass  Load a kernel image and command line passed to QEMU via
> +  #                  the command line
> +  #
> +  QemuLoadImageLib|Include/Library/QemuLoadImageLib.h
> +
>    ##  @libraryclass  Serialize (and deserialize) variables
>    #
>    SerializeVariablesLib|Include/Library/SerializeVariablesLib.h
> 

With (1) and (2) updated:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib
  2020-03-02  7:29 ` [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib Ard Biesheuvel
@ 2020-03-02 17:12   ` Laszlo Ersek
  2020-03-03  7:36     ` Laszlo Ersek
  0 siblings, 1 reply; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 17:12 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> 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>
> ---
>  OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c   | 253 ++++++++++++++++++++
>  OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf |  39 +++
>  2 files changed, 292 insertions(+)
> 
> diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
> new file mode 100644
> index 000000000000..c48c7a88dd91
> --- /dev/null
> +++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
> @@ -0,0 +1,253 @@
> +/**  @file
> +  Generic implementation of QemuLoadImageLib library class interface.
> +
> +  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Uefi.h>
> +
> +#include <Guid/QemuKernelLoaderFsMedia.h>
> +#include <Library/DebugLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/QemuFwCfgLib.h>

(1) I think it would be nicer if, in this patch, we didn't access fw_cfg
at all. The filesystem already exposes "cmdline", we could use that.

... Except, I can see you are removing that in patch #7... OK, I guess.

> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/DevicePath.h>
> +#include <Protocol/LoadedImage.h>
> +
> +#pragma pack (1)
> +typedef struct {
> +  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;
> +  CHAR16                    FilePath[sizeof (L"kernel")];
> +} KERNEL_FILE_DEVPATH;

(2) The idea is very nice, but the size of the FilePath array is larger
than necessary. L"kernel" is an array of CHAR16, sizeof returns number
of bytes, and the element type of FilePath is CHAR16; so we end up
allocating doubly.

Please use instead:

  CHAR16                    FilePath[ARRAY_SIZE (L"kernel")];

(Please #include <Base.h> for this, too.)

> +
> +typedef struct {
> +  VENDOR_DEVICE_PATH        VenMediaNode;
> +  KERNEL_FILE_DEVPATH       FileNode;
> +  EFI_DEVICE_PATH_PROTOCOL  EndNode;
> +} KERNEL_VENMEDIA_FILE_DEVPATH;
> +#pragma pack ()
> +
> +STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
> +  {
> +    {
> +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
> +      { sizeof (VENDOR_DEVICE_PATH) }
> +    },
> +    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
> +  }, {
> +    {
> +      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
> +      { sizeof (KERNEL_FILE_DEVPATH) }
> +    },
> +    L"kernel",
> +  }, {
> +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> +  }
> +};
> +
> +/**
> +  Download the kernel, the initial ramdisk, and the kernel command line from
> +  QEMU's fw_cfg. The kernel will be instructed via its command line to load
> +  the initrd from the same Simple FileSystem.

(3) Please sync the comments here with the lib class header, according
to my requests for the lib class header (two updates).

> +
> +  @param[out] ImageHandle       The image handle that was allocated for
> +                                loading the image
> +  @param[out] LoadedImage       The loaded image protocol that was installed
> +                                on ImageHandle by the LoadImage boot service.
> +
> +  @retval EFI_SUCCESS           The image was loaded successfully.
> +  @retval EFI_NOT_FOUND         Kernel image was not found.
> +  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
> +  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
> +
> +  @return                       Error codes from any of the underlying
> +                                functions.
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuLoadKernelImage (
> +  OUT EFI_HANDLE                  *ImageHandle
> +  )
> +{
> +  EFI_STATUS                Status;
> +  EFI_HANDLE                KernelImageHandle;
> +  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
> +  UINTN                     CommandLineSize;
> +  CHAR8                     *CommandLine;
> +  UINTN                     InitrdSize;
> +
> +  //
> +  // Load the image. This should call back into the QEMU EFI loader file system.
> +  //
> +  Status = gBS->LoadImage (
> +                  FALSE,                    // BootPolicy: exact match required
> +                  gImageHandle,             // ParentImageHandle
> +                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
> +                  NULL,                     // SourceBuffer
> +                  0,                        // SourceSize
> +                  &KernelImageHandle
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
> +    return Status;
> +  }

(4) OK, so we got a bit of a complication here, with regard to
EFI_SECURITY_VIOLATION.

Per spec, when gBS->LoadImage() returns EFI_SECURITY_VIOLATION, the
image will have been loaded. If we don't release it with
gBS->UnloadImage(), then it will be leaked.

Now, there are two ways to treat this issue, given this library class:


(4a) Handle EFI_SECURITY_VIOLATION internally to the library; that is,
do not allow the caller to explicitly override the security violation
(IOW don't allow the caller to do something with the output ImageHandle
*despite* the security violation).

In this case:

- Catch EFI_SECURITY_VIOLATION here explicitly,
- set status to EFI_ACCESS_DENIED,
- set (*ImageHandle) to NULL,
- introduce an additional label just above gBS->UnloadImage(),
- jump to that label from here,
- update the leading comments in both the lib class header and in this
lib instance to state that EFI_SECURITY_VIOLATION is caught internally,
and EFI_ACCESS_DENIED is returned instead, always.


(4b) Expose the generality of gBS->LoadImage() through this library
interface:

- document the special behavior of EFI_SECURITY_VIOLATION in the lib
class header (necessitating the caller to recognize that error code, and
then to unload the kernel image with QemuUnloadKernelImage()),

- actually implement the special handling for EFI_SECURITY_VIOLATION here.

I would strongly suggest (4a).

Now, I realize that (4a) requires the removal of the special handling of
EFI_SECURITY_VIOLATION in patch #6. I still think that's preferable,
because even if we propagate EFI_SECURITY_VIOLATION out of this
function, the bad security status of the ImageHandle that we output,
will not be the *only* thing amiss.

Namely, we will not have set up the command line. And so the caller will
be left with two choices:

- release the image at once,
- "trust" the image nonetheless... but what about the command line, then?

In other words, the way the patch set looks right now, we allow
EFI_SECURITY_VIOLATION to get out of the function, but it's not really
useful to the caller. Given that we're turning this boundary into an
actual lib class API, I think we should either keep
EFI_SECURITY_VIOLATION internal -- (4a) -- or pass it out full-featured
-- (4b) --. And then I think (4a) is much better here (for simplicity).

> +
> +  //
> +  // Construct the kernel command line.
> +  //
> +  Status = gBS->OpenProtocol (
> +                  KernelImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&KernelLoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> +  CommandLineSize = (UINTN) QemuFwCfgRead64 ();

(5) This should be QemuFwCfgRead32(). See FetchBlob() the original code:

   Blob->Size = QemuFwCfgRead32 ();

> +
> +  if (CommandLineSize == 0) {
> +    KernelLoadedImage->LoadOptionsSize = 0;
> +  } else {
> +    CommandLine = AllocatePool (CommandLineSize);
> +    ASSERT (CommandLine != NULL);

(6) It's not very difficult to jump to the end of the function here.
PLease use an explicit check and a goto, for releasing KernelImageHandle.

> +
> +    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> +    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
> +
> +    //
> +    // Verify NUL-termination of the command line.
> +    //
> +    if (CommandLine[CommandLineSize - 1] != '\0') {
> +      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
> +        __FUNCTION__));
> +      Status = EFI_PROTOCOL_ERROR;
> +      goto FreeCommandLine;
> +    }
> +
> +    //
> +    // Drop the terminating NUL, convert to UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;
> +  }
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> +  InitrdSize = (UINTN) QemuFwCfgRead64 ();

(7) Should be QemuFwCfgRead32().

> +
> +  if (InitrdSize > 0) {
> +    //
> +    // Append ' initrd=initrd' in UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
> +  }
> +
> +  if (KernelLoadedImage->LoadOptionsSize == 0) {
> +    KernelLoadedImage->LoadOptions = NULL;
> +  } else {
> +    //
> +    // NUL-terminate in UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize += 2;
> +
> +    KernelLoadedImage->LoadOptions = AllocatePool (
> +                                       KernelLoadedImage->LoadOptionsSize);
> +    if (KernelLoadedImage->LoadOptions == NULL) {
> +      KernelLoadedImage->LoadOptionsSize = 0;
> +      Status = EFI_OUT_OF_RESOURCES;
> +      goto FreeCommandLine;

It's possible that we don't have a kernel commandline via fw_cfg, but
we're still appending " initrd=initrd" because we do have an initial
ramdisk. In that case, CommandLine will not have been assigned, and the
FreePool() in the end will blow up.

(8) So this "goto" is correct, but the final FreePool() should be
conditional on (CommandLineSize > 0).

> +    }
> +
> +    UnicodeSPrintAsciiFormat (
> +      KernelLoadedImage->LoadOptions,
> +      KernelLoadedImage->LoadOptionsSize,
> +      "%a%a",
> +      (CommandLineSize == 0) ?  "" : CommandLine,
> +      (InitrdSize == 0)      ?  "" : " initrd=initrd"
> +      );
> +    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
> +      (CHAR16 *)KernelLoadedImage->LoadOptions));
> +  }
> +
> +  *ImageHandle = KernelImageHandle;
> +  return EFI_SUCCESS;
> +
> +FreeCommandLine:
> +  FreePool (CommandLine);
> +  gBS->UnloadImage (KernelImageHandle);
> +
> +  return Status;
> +}
> +
> +/**
> +  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
> +
> +  @param[in]  ImageHandle         Handle of image to be started.
> +
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
> +                                  or the image has already been initialized with
> +                                  StartImage
> +  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
> +                                  image should not be started.
> +
> +  @return                         Error codes returned by the started image
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuStartKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  )
> +{
> +  return gBS->StartImage (
> +                ImageHandle,
> +                NULL,              // ExitDataSize
> +                NULL               // ExitData
> +                );
> +}
> +
> +/**
> +  Unloads an image loaded with QemuLoadKernelImage ().
> +
> +  @param  ImageHandle             Handle that identifies the image to be
> +                                  unloaded.
> +
> +  @retval EFI_SUCCESS             The image has been unloaded.
> +  @retval EFI_UNSUPPORTED         The image has been started, and does not
> +                                  support unload.
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuUnloadKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  )
> +{
> +  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;
> +  EFI_STATUS                  Status;
> +
> +  Status = gBS->OpenProtocol (
> +                  ImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&KernelLoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (KernelLoadedImage->LoadOptions != NULL) {
> +    FreePool (KernelLoadedImage->LoadOptions);
> +    KernelLoadedImage->LoadOptions = NULL;
> +  }
> +  KernelLoadedImage->LoadOptionsSize = 0;
> +
> +  return gBS->UnloadImage (ImageHandle);
> +}

Seems OK.

> diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
> new file mode 100644
> index 000000000000..cbd40e6290e0
> --- /dev/null
> +++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
> @@ -0,0 +1,39 @@
> +## @file
> +#  Generic implementation of QemuLoadImageLib library class interface.
> +#
> +#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.27
> +  BASE_NAME                      = GenericQemuLoadImageLib
> +  FILE_GUID                      = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER
> +
> +[Sources]
> +  GenericQemuLoadImageLib.c
> +
> +[Packages]
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +  OvmfPkg/OvmfPkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  MemoryAllocationLib
> +  PrintLib
> +  QemuFwCfgLib
> +  ReportStatusCodeLib

OK, so from patch #6, it's clear that EfiSignalEventReadyToBoot() and
REPORT_STATUS_CODE() remain in the original spot.

(9) That looks good; but then we don't need ReportStatusCodeLib here
(the header file is not included either, anyway).

> +  UefiBootServicesTableLib
> +
> +[Protocols]
> +  gEfiDevicePathProtocolGuid

Not strictly needed by this patch, but in order to match the #include
directives, it seems OK.

> +  gEfiLoadedImageProtocolGuid
> +
> +[Guids]
> +  gQemuKernelLoaderFsMediaGuid
> 

Same -- OK.

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library
  2020-03-02  7:29 ` [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Ard Biesheuvel
@ 2020-03-02 17:15   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 17:15 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Add the QEMU loader DXE driver and client library to the build for
> our QEMU targeted implementations in ArmVirtPkg.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  ArmVirtPkg/ArmVirtQemu.dsc           | 2 ++
>  ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 1 +
>  ArmVirtPkg/ArmVirtQemuKernel.dsc     | 2 ++
>  3 files changed, 5 insertions(+)
> 
> diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc
> index 7ae6702ac1f0..d037d68f90c7 100644
> --- a/ArmVirtPkg/ArmVirtQemu.dsc
> +++ b/ArmVirtPkg/ArmVirtQemu.dsc
> @@ -56,6 +56,7 @@ [LibraryClasses.common]
>    VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf
>    QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf
> +  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
>  
>    ArmPlatformLib|ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.inf
>  
> @@ -371,6 +372,7 @@ [Components.common]
>        NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
>        NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
>    }
> +  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  
>    #
>    # Networking stack
> diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> index bfa380815f1a..9cb8621ffece 100644
> --- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> +++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc
> @@ -114,6 +114,7 @@ [FV.FvMain]
>    INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
>    INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
>    INF MdeModulePkg/Application/UiApp/UiApp.inf
> +  INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  
>    #
>    # Networking stack
> diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> index 3b0f04967a4b..dfd06587c735 100644
> --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc
> +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc
> @@ -56,6 +56,7 @@ [LibraryClasses.common]
>    VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf
>    QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf
> +  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
>  
>    ArmVirtMemInfoLib|ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf
>  
> @@ -355,6 +356,7 @@ [Components.common]
>        NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
>        NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
>    }
> +  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  
>    #
>    # Networking stack
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader
  2020-03-02  7:29 ` [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Ard Biesheuvel
@ 2020-03-02 17:26   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 17:26 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Drop the QEMU loader file system implementation inside this library,
> and switch to the separate QemuLoadImageLib library and the associated
> driver to expose the kernel and initrd passed via the QEMU command line.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |    8 +-
>  ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c               | 1052 +-------------------
>  2 files changed, 6 insertions(+), 1054 deletions(-)
> 
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> index a9d4888d4377..e80f444aec64 100644
> --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> @@ -46,7 +46,7 @@ [LibraryClasses]
>    PlatformBmPrintScLib
>    PrintLib
>    QemuBootOrderLib
> -  QemuFwCfgLib
> +  QemuLoadImageLib
>    ReportStatusCodeLib
>    UefiBootManagerLib
>    UefiBootServicesTableLib

(1) I think we can remove at least PrintLib. (You do remove the #include
for "PrintLib.h".)

> @@ -64,18 +64,12 @@ [Pcd]
>    gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut
>  
>  [Guids]
> -  gEfiFileInfoGuid
> -  gEfiFileSystemInfoGuid
> -  gEfiFileSystemVolumeLabelInfoIdGuid
>    gEfiEndOfDxeEventGroupGuid
>    gRootBridgesConnectedEventGroupGuid
>    gUefiShellFileGuid
>  
>  [Protocols]
> -  gEfiDevicePathProtocolGuid
>    gEfiFirmwareVolume2ProtocolGuid
>    gEfiGraphicsOutputProtocolGuid
> -  gEfiLoadedImageProtocolGuid
>    gEfiPciRootBridgeIoProtocolGuid
> -  gEfiSimpleFileSystemProtocolGuid
>    gVirtioDeviceProtocolGuid
> diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
> index d3851fd75fa5..c305927e249b 100644
> --- a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
> +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
> @@ -9,887 +9,11 @@
>    SPDX-License-Identifier: BSD-2-Clause-Patent
>  **/
>  
> -#include <Guid/FileInfo.h>
> -#include <Guid/FileSystemInfo.h>
> -#include <Guid/FileSystemVolumeLabelInfo.h>
> -#include <Library/PrintLib.h>
> -#include <Library/QemuFwCfgLib.h>
> +#include <Library/QemuLoadImageLib.h>
>  #include <Library/ReportStatusCodeLib.h>
> -#include <Protocol/DevicePath.h>
> -#include <Protocol/LoadedImage.h>
> -#include <Protocol/SimpleFileSystem.h>
>  
>  #include "PlatformBm.h"
>  
> -//
> -// Static data that hosts the fw_cfg blobs and serves file requests.
> -//
> -typedef enum {
> -  KernelBlobTypeKernel,
> -  KernelBlobTypeInitrd,
> -  KernelBlobTypeCommandLine,
> -  KernelBlobTypeMax
> -} KERNEL_BLOB_TYPE;
> -
> -typedef struct {
> -  FIRMWARE_CONFIG_ITEM CONST SizeKey;
> -  FIRMWARE_CONFIG_ITEM CONST DataKey;
> -  CONST CHAR16 *       CONST Name;
> -  UINT32                     Size;
> -  UINT8                      *Data;
> -} KERNEL_BLOB;
> -
> -STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
> -  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
> -  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
> -  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
> -};
> -
> -STATIC UINT64 mTotalBlobBytes;
> -
> -//
> -// Device path for the handle that incorporates our "EFI stub filesystem". The
> -// GUID is arbitrary and need not be standardized or advertized.
> -//
> -#pragma pack(1)
> -typedef struct {
> -  VENDOR_DEVICE_PATH       VenHwNode;
> -  EFI_DEVICE_PATH_PROTOCOL EndNode;
> -} SINGLE_VENHW_NODE_DEVPATH;
> -#pragma pack()
> -
> -STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = {
> -  {
> -    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } },
> -    {
> -      0xb0fae7e7, 0x6b07, 0x49d0,
> -      { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d }
> -    }
> -  },
> -
> -  {
> -    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> -    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> -  }
> -};
> -
> -//
> -// The "file in the EFI stub filesystem" abstraction.
> -//
> -STATIC EFI_TIME mInitTime;
> -
> -#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')
> -
> -typedef struct {
> -  UINT64            Signature; // Carries STUB_FILE_SIG.
> -
> -  KERNEL_BLOB_TYPE  BlobType;  // Index into mKernelBlob. KernelBlobTypeMax
> -                               // denotes the root directory of the filesystem.
> -
> -  UINT64            Position;  // Byte position for regular files;
> -                               // next directory entry to return for the root
> -                               // directory.
> -
> -  EFI_FILE_PROTOCOL File;      // Standard protocol interface.
> -} STUB_FILE;
> -
> -#define STUB_FILE_FROM_FILE(FilePointer) \
> -        CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG)
> -
> -//
> -// Tentative definition of the file protocol template. The initializer
> -// (external definition) will be provided later.
> -//
> -STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate;
> -
> -
> -//
> -// Protocol member functions for File.
> -//
> -
> -/**
> -  Opens a new file relative to the source file's location.
> -
> -  @param[in]  This        A pointer to the EFI_FILE_PROTOCOL instance that is
> -                          the file handle to the source location. This would
> -                          typically be an open handle to a directory.
> -
> -  @param[out] NewHandle   A pointer to the location to return the opened handle
> -                          for the new file.
> -
> -  @param[in]  FileName    The Null-terminated string of the name of the file to
> -                          be opened. The file name may contain the following
> -                          path modifiers: "\", ".", and "..".
> -
> -  @param[in]  OpenMode    The mode to open the file. The only valid
> -                          combinations that the file may be opened with are:
> -                          Read, Read/Write, or Create/Read/Write.
> -
> -  @param[in]  Attributes  Only valid for EFI_FILE_MODE_CREATE, in which case
> -                          these are the attribute bits for the newly created
> -                          file.
> -
> -  @retval EFI_SUCCESS           The file was opened.
> -  @retval EFI_NOT_FOUND         The specified file could not be found on the
> -                                device.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
> -                                medium is no longer supported.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a
> -                                file for write when the media is
> -                                write-protected.
> -  @retval EFI_ACCESS_DENIED     The service denied access to the file.
> -  @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
> -                                file.
> -  @retval EFI_VOLUME_FULL       The volume is full.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileOpen (
> -  IN EFI_FILE_PROTOCOL  *This,
> -  OUT EFI_FILE_PROTOCOL **NewHandle,
> -  IN CHAR16             *FileName,
> -  IN UINT64             OpenMode,
> -  IN UINT64             Attributes
> -  )
> -{
> -  CONST STUB_FILE *StubFile;
> -  UINTN           BlobType;
> -  STUB_FILE       *NewStubFile;
> -
> -  //
> -  // We're read-only.
> -  //
> -  switch (OpenMode) {
> -    case EFI_FILE_MODE_READ:
> -      break;
> -
> -    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
> -    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
> -      return EFI_WRITE_PROTECTED;
> -
> -    default:
> -      return EFI_INVALID_PARAMETER;
> -  }
> -
> -  //
> -  // Only the root directory supports opening files in it.
> -  //
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -  if (StubFile->BlobType != KernelBlobTypeMax) {
> -    return EFI_UNSUPPORTED;
> -  }
> -
> -  //
> -  // Locate the file.
> -  //
> -  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
> -    if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {
> -      break;
> -    }
> -  }
> -  if (BlobType == KernelBlobTypeMax) {
> -    return EFI_NOT_FOUND;
> -  }
> -
> -  //
> -  // Found it.
> -  //
> -  NewStubFile = AllocatePool (sizeof *NewStubFile);
> -  if (NewStubFile == NULL) {
> -    return EFI_OUT_OF_RESOURCES;
> -  }
> -
> -  NewStubFile->Signature = STUB_FILE_SIG;
> -  NewStubFile->BlobType  = (KERNEL_BLOB_TYPE)BlobType;
> -  NewStubFile->Position  = 0;
> -  CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate,
> -    sizeof mEfiFileProtocolTemplate);
> -  *NewHandle = &NewStubFile->File;
> -
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Closes a specified file handle.
> -
> -  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
> -                   handle to close.
> -
> -  @retval EFI_SUCCESS  The file was closed.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileClose (
> -  IN EFI_FILE_PROTOCOL *This
> -  )
> -{
> -  FreePool (STUB_FILE_FROM_FILE (This));
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Close and delete the file handle.
> -
> -  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
> -                   handle to the file to delete.
> -
> -  @retval EFI_SUCCESS              The file was closed and deleted, and the
> -                                   handle was closed.
> -  @retval EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not
> -                                   deleted.
> -
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileDelete (
> -  IN EFI_FILE_PROTOCOL *This
> -  )
> -{
> -  FreePool (STUB_FILE_FROM_FILE (This));
> -  return EFI_WARN_DELETE_FAILURE;
> -}
> -
> -
> -/**
> -  Helper function that formats an EFI_FILE_INFO structure into the
> -  user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including
> -  KernelBlobTypeMax, which stands for the root directory).
> -
> -  The interface follows the EFI_FILE_GET_INFO -- and for directories, the
> -  EFI_FILE_READ -- interfaces.
> -
> -  @param[in]     BlobType     The KERNEL_BLOB_TYPE value identifying the fw_cfg
> -                              blob backing the STUB_FILE that information is
> -                              being requested about. If BlobType equals
> -                              KernelBlobTypeMax, then information will be
> -                              provided about the root directory of the
> -                              filesystem.
> -
> -  @param[in,out] BufferSize  On input, the size of Buffer. On output, the
> -                             amount of data returned in Buffer. In both cases,
> -                             the size is measured in bytes.
> -
> -  @param[out]    Buffer      A pointer to the data buffer to return. The
> -                             buffer's type is EFI_FILE_INFO.
> -
> -  @retval EFI_SUCCESS           The information was returned.
> -  @retval EFI_BUFFER_TOO_SMALL  BufferSize is too small to store the
> -                                EFI_FILE_INFO structure. BufferSize has been
> -                                updated with the size needed to complete the
> -                                request.
> -**/
> -STATIC
> -EFI_STATUS
> -ConvertKernelBlobTypeToFileInfo (
> -  IN KERNEL_BLOB_TYPE BlobType,
> -  IN OUT UINTN        *BufferSize,
> -  OUT VOID            *Buffer
> -  )
> -{
> -  CONST CHAR16  *Name;
> -  UINT64        FileSize;
> -  UINT64        Attribute;
> -
> -  UINTN         NameSize;
> -  UINTN         FileInfoSize;
> -  EFI_FILE_INFO *FileInfo;
> -  UINTN         OriginalBufferSize;
> -
> -  if (BlobType == KernelBlobTypeMax) {
> -    //
> -    // getting file info about the root directory
> -    //
> -    Name      = L"\\";
> -    FileSize  = KernelBlobTypeMax;
> -    Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
> -  } else {
> -    CONST KERNEL_BLOB *Blob;
> -
> -    Blob      = &mKernelBlob[BlobType];
> -    Name      = Blob->Name;
> -    FileSize  = Blob->Size;
> -    Attribute = EFI_FILE_READ_ONLY;
> -  }
> -
> -  NameSize     = (StrLen(Name) + 1) * 2;
> -  FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;
> -  ASSERT (FileInfoSize >= sizeof *FileInfo);
> -
> -  OriginalBufferSize = *BufferSize;
> -  *BufferSize        = FileInfoSize;
> -  if (OriginalBufferSize < *BufferSize) {
> -    return EFI_BUFFER_TOO_SMALL;
> -  }
> -
> -  FileInfo               = (EFI_FILE_INFO *)Buffer;
> -  FileInfo->Size         = FileInfoSize;
> -  FileInfo->FileSize     = FileSize;
> -  FileInfo->PhysicalSize = FileSize;
> -  FileInfo->Attribute    = Attribute;
> -
> -  CopyMem (&FileInfo->CreateTime,       &mInitTime, sizeof mInitTime);
> -  CopyMem (&FileInfo->LastAccessTime,   &mInitTime, sizeof mInitTime);
> -  CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);
> -  CopyMem (FileInfo->FileName,          Name,       NameSize);
> -
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Reads data from a file, or continues scanning a directory.
> -
> -  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
> -                             is the file handle to read data from.
> -
> -  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
> -                             amount of data returned in Buffer. In both cases,
> -                             the size is measured in bytes. If the read goes
> -                             beyond the end of the file, the read length is
> -                             truncated to the end of the file.
> -
> -                             If This is a directory, the function reads the
> -                             directory entry at the current position and
> -                             returns the entry (as EFI_FILE_INFO) in Buffer. If
> -                             there are no more directory entries, the
> -                             BufferSize is set to zero on output.
> -
> -  @param[out]    Buffer      The buffer into which the data is read.
> -
> -  @retval EFI_SUCCESS           Data was read.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_DEVICE_ERROR      An attempt was made to read from a deleted
> -                                file.
> -  @retval EFI_DEVICE_ERROR      On entry, the current file position is beyond
> -                                the end of the file.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
> -                                current directory entry as a EFI_FILE_INFO
> -                                structure. BufferSize has been updated with the
> -                                size needed to complete the request, and the
> -                                directory position has not been advanced.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileRead (
> -  IN EFI_FILE_PROTOCOL *This,
> -  IN OUT UINTN         *BufferSize,
> -  OUT VOID             *Buffer
> -  )
> -{
> -  STUB_FILE         *StubFile;
> -  CONST KERNEL_BLOB *Blob;
> -  UINT64            Left;
> -
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -
> -  //
> -  // Scanning the root directory?
> -  //
> -  if (StubFile->BlobType == KernelBlobTypeMax) {
> -    EFI_STATUS Status;
> -
> -    if (StubFile->Position == KernelBlobTypeMax) {
> -      //
> -      // Scanning complete.
> -      //
> -      *BufferSize = 0;
> -      return EFI_SUCCESS;
> -    }
> -
> -    Status = ConvertKernelBlobTypeToFileInfo (
> -               (KERNEL_BLOB_TYPE)StubFile->Position,
> -               BufferSize,
> -               Buffer);
> -    if (EFI_ERROR (Status)) {
> -      return Status;
> -    }
> -
> -    ++StubFile->Position;
> -    return EFI_SUCCESS;
> -  }
> -
> -  //
> -  // Reading a file.
> -  //
> -  Blob = &mKernelBlob[StubFile->BlobType];
> -  if (StubFile->Position > Blob->Size) {
> -    return EFI_DEVICE_ERROR;
> -  }
> -
> -  Left = Blob->Size - StubFile->Position;
> -  if (*BufferSize > Left) {
> -    *BufferSize = (UINTN)Left;
> -  }
> -  if (Blob->Data != NULL) {
> -    CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
> -  }
> -  StubFile->Position += *BufferSize;
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Writes data to a file.
> -
> -  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
> -                             is the file handle to write data to.
> -
> -  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
> -                             amount of data actually written. In both cases,
> -                             the size is measured in bytes.
> -
> -  @param[in]     Buffer      The buffer of data to write.
> -
> -  @retval EFI_SUCCESS           Data was written.
> -  @retval EFI_UNSUPPORTED       Writes to open directory files are not
> -                                supported.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_DEVICE_ERROR      An attempt was made to write to a deleted file.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
> -  @retval EFI_ACCESS_DENIED     The file was opened read only.
> -  @retval EFI_VOLUME_FULL       The volume is full.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileWrite (
> -  IN EFI_FILE_PROTOCOL *This,
> -  IN OUT UINTN         *BufferSize,
> -  IN VOID              *Buffer
> -  )
> -{
> -  STUB_FILE *StubFile;
> -
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -  return (StubFile->BlobType == KernelBlobTypeMax) ?
> -         EFI_UNSUPPORTED :
> -         EFI_WRITE_PROTECTED;
> -}
> -
> -
> -/**
> -  Returns a file's current position.
> -
> -  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is the
> -                        file handle to get the current position on.
> -
> -  @param[out] Position  The address to return the file's current position
> -                        value.
> -
> -  @retval EFI_SUCCESS      The position was returned.
> -  @retval EFI_UNSUPPORTED  The request is not valid on open directories.
> -  @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
> -                           deleted file.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileGetPosition (
> -  IN EFI_FILE_PROTOCOL *This,
> -  OUT UINT64           *Position
> -  )
> -{
> -  STUB_FILE *StubFile;
> -
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -  if (StubFile->BlobType == KernelBlobTypeMax) {
> -    return EFI_UNSUPPORTED;
> -  }
> -
> -  *Position = StubFile->Position;
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Sets a file's current position.
> -
> -  @param[in] This      A pointer to the EFI_FILE_PROTOCOL instance that is the
> -                       file handle to set the requested position on.
> -
> -  @param[in] Position  The byte position from the start of the file to set. For
> -                       regular files, MAX_UINT64 means "seek to end". For
> -                       directories, zero means "rewind directory scan".
> -
> -  @retval EFI_SUCCESS       The position was set.
> -  @retval EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
> -                            directories.
> -  @retval EFI_DEVICE_ERROR  An attempt was made to set the position of a
> -                            deleted file.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileSetPosition (
> -  IN EFI_FILE_PROTOCOL *This,
> -  IN UINT64            Position
> -  )
> -{
> -  STUB_FILE   *StubFile;
> -  KERNEL_BLOB *Blob;
> -
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -
> -  if (StubFile->BlobType == KernelBlobTypeMax) {
> -    if (Position == 0) {
> -      //
> -      // rewinding a directory scan is allowed
> -      //
> -      StubFile->Position = 0;
> -      return EFI_SUCCESS;
> -    }
> -    return EFI_UNSUPPORTED;
> -  }
> -
> -  //
> -  // regular file seek
> -  //
> -  Blob = &mKernelBlob[StubFile->BlobType];
> -  if (Position == MAX_UINT64) {
> -    //
> -    // seek to end
> -    //
> -    StubFile->Position = Blob->Size;
> -  } else {
> -    //
> -    // absolute seek from beginning -- seeking past the end is allowed
> -    //
> -    StubFile->Position = Position;
> -  }
> -  return EFI_SUCCESS;
> -}
> -
> -
> -/**
> -  Returns information about a file.
> -
> -  @param[in]     This             A pointer to the EFI_FILE_PROTOCOL instance
> -                                  that is the file handle the requested
> -                                  information is for.
> -
> -  @param[in]     InformationType  The type identifier GUID for the information
> -                                  being requested. The following information
> -                                  types are supported, storing the
> -                                  corresponding structures in Buffer:
> -
> -                                  - gEfiFileInfoGuid: EFI_FILE_INFO
> -
> -                                  - gEfiFileSystemInfoGuid:
> -                                    EFI_FILE_SYSTEM_INFO
> -
> -                                  - gEfiFileSystemVolumeLabelInfoIdGuid:
> -                                    EFI_FILE_SYSTEM_VOLUME_LABEL
> -
> -  @param[in,out] BufferSize       On input, the size of Buffer. On output, the
> -                                  amount of data returned in Buffer. In both
> -                                  cases, the size is measured in bytes.
> -
> -  @param[out]    Buffer           A pointer to the data buffer to return. The
> -                                  buffer's type is indicated by
> -                                  InformationType.
> -
> -  @retval EFI_SUCCESS           The information was returned.
> -  @retval EFI_UNSUPPORTED       The InformationType is not known.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
> -                                information structure requested by
> -                                InformationType. BufferSize has been updated
> -                                with the size needed to complete the request.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileGetInfo (
> -  IN EFI_FILE_PROTOCOL *This,
> -  IN EFI_GUID          *InformationType,
> -  IN OUT UINTN         *BufferSize,
> -  OUT VOID             *Buffer
> -  )
> -{
> -  CONST STUB_FILE *StubFile;
> -  UINTN           OriginalBufferSize;
> -
> -  StubFile = STUB_FILE_FROM_FILE (This);
> -
> -  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
> -    return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
> -             Buffer);
> -  }
> -
> -  OriginalBufferSize = *BufferSize;
> -
> -  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
> -    EFI_FILE_SYSTEM_INFO *FileSystemInfo;
> -
> -    *BufferSize = sizeof *FileSystemInfo;
> -    if (OriginalBufferSize < *BufferSize) {
> -      return EFI_BUFFER_TOO_SMALL;
> -    }
> -
> -    FileSystemInfo                 = (EFI_FILE_SYSTEM_INFO *)Buffer;
> -    FileSystemInfo->Size           = sizeof *FileSystemInfo;
> -    FileSystemInfo->ReadOnly       = TRUE;
> -    FileSystemInfo->VolumeSize     = mTotalBlobBytes;
> -    FileSystemInfo->FreeSpace      = 0;
> -    FileSystemInfo->BlockSize      = 1;
> -    FileSystemInfo->VolumeLabel[0] = L'\0';
> -
> -    return EFI_SUCCESS;
> -  }
> -
> -  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
> -    EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
> -
> -    *BufferSize = sizeof *FileSystemVolumeLabel;
> -    if (OriginalBufferSize < *BufferSize) {
> -      return EFI_BUFFER_TOO_SMALL;
> -    }
> -
> -    FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
> -    FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
> -
> -    return EFI_SUCCESS;
> -  }
> -
> -  return EFI_UNSUPPORTED;
> -}
> -
> -
> -/**
> -  Sets information about a file.
> -
> -  @param[in] File             A pointer to the EFI_FILE_PROTOCOL instance that
> -                              is the file handle the information is for.
> -
> -  @param[in] InformationType  The type identifier for the information being
> -                              set.
> -
> -  @param[in] BufferSize       The size, in bytes, of Buffer.
> -
> -  @param[in] Buffer           A pointer to the data buffer to write. The
> -                              buffer's type is indicated by InformationType.
> -
> -  @retval EFI_SUCCESS           The information was set.
> -  @retval EFI_UNSUPPORTED       The InformationType is not known.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_WRITE_PROTECTED   InformationType is EFI_FILE_INFO_ID and the
> -                                media is read-only.
> -  @retval EFI_WRITE_PROTECTED   InformationType is
> -                                EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media
> -                                is read only.
> -  @retval EFI_WRITE_PROTECTED   InformationType is
> -                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media
> -                                is read-only.
> -  @retval EFI_ACCESS_DENIED     An attempt is made to change the name of a file
> -                                to a file that is already present.
> -  @retval EFI_ACCESS_DENIED     An attempt is being made to change the
> -                                EFI_FILE_DIRECTORY Attribute.
> -  @retval EFI_ACCESS_DENIED     An attempt is being made to change the size of
> -                                a directory.
> -  @retval EFI_ACCESS_DENIED     InformationType is EFI_FILE_INFO_ID and the
> -                                file was opened read-only and an attempt is
> -                                being made to modify a field other than
> -                                Attribute.
> -  @retval EFI_VOLUME_FULL       The volume is full.
> -  @retval EFI_BAD_BUFFER_SIZE   BufferSize is smaller than the size of the type
> -                                indicated by InformationType.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileSetInfo (
> -  IN EFI_FILE_PROTOCOL *This,
> -  IN EFI_GUID          *InformationType,
> -  IN UINTN             BufferSize,
> -  IN VOID              *Buffer
> -  )
> -{
> -  return EFI_WRITE_PROTECTED;
> -}
> -
> -
> -/**
> -  Flushes all modified data associated with a file to a device.
> -
> -  @param [in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
> -                    file handle to flush.
> -
> -  @retval EFI_SUCCESS           The data was flushed.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
> -  @retval EFI_ACCESS_DENIED     The file was opened read-only.
> -  @retval EFI_VOLUME_FULL       The volume is full.
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileFlush (
> -  IN EFI_FILE_PROTOCOL *This
> -  )
> -{
> -  return EFI_WRITE_PROTECTED;
> -}
> -
> -//
> -// External definition of the file protocol template.
> -//
> -STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
> -  EFI_FILE_PROTOCOL_REVISION, // revision 1
> -  StubFileOpen,
> -  StubFileClose,
> -  StubFileDelete,
> -  StubFileRead,
> -  StubFileWrite,
> -  StubFileGetPosition,
> -  StubFileSetPosition,
> -  StubFileGetInfo,
> -  StubFileSetInfo,
> -  StubFileFlush,
> -  NULL,                       // OpenEx, revision 2
> -  NULL,                       // ReadEx, revision 2
> -  NULL,                       // WriteEx, revision 2
> -  NULL                        // FlushEx, revision 2
> -};
> -
> -
> -//
> -// Protocol member functions for SimpleFileSystem.
> -//
> -
> -/**
> -  Open the root directory on a volume.
> -
> -  @param[in]  This  A pointer to the volume to open the root directory on.
> -
> -  @param[out] Root  A pointer to the location to return the opened file handle
> -                    for the root directory in.
> -
> -  @retval EFI_SUCCESS           The device was opened.
> -  @retval EFI_UNSUPPORTED       This volume does not support the requested file
> -                                system type.
> -  @retval EFI_NO_MEDIA          The device has no medium.
> -  @retval EFI_DEVICE_ERROR      The device reported an error.
> -  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
> -  @retval EFI_ACCESS_DENIED     The service denied access to the file.
> -  @retval EFI_OUT_OF_RESOURCES  The volume was not opened due to lack of
> -                                resources.
> -  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
> -                                medium is no longer supported. Any existing
> -                                file handles for this volume are no longer
> -                                valid. To access the files on the new medium,
> -                                the volume must be reopened with OpenVolume().
> -**/
> -STATIC
> -EFI_STATUS
> -EFIAPI
> -StubFileSystemOpenVolume (
> -  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
> -  OUT EFI_FILE_PROTOCOL              **Root
> -  )
> -{
> -  STUB_FILE *StubFile;
> -
> -  StubFile = AllocatePool (sizeof *StubFile);
> -  if (StubFile == NULL) {
> -    return EFI_OUT_OF_RESOURCES;
> -  }
> -
> -  StubFile->Signature = STUB_FILE_SIG;
> -  StubFile->BlobType  = KernelBlobTypeMax;
> -  StubFile->Position  = 0;
> -  CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
> -    sizeof mEfiFileProtocolTemplate);
> -  *Root = &StubFile->File;
> -
> -  return EFI_SUCCESS;
> -}
> -
> -STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
> -  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
> -  StubFileSystemOpenVolume
> -};
> -
> -
> -//
> -// Utility functions.
> -//
> -
> -/**
> -  Populate a blob in mKernelBlob.
> -
> -  param[in,out] Blob  Pointer to the KERNEL_BLOB element in mKernelBlob that is
> -                      to be filled from fw_cfg.
> -
> -  @retval EFI_SUCCESS           Blob has been populated. If fw_cfg reported a
> -                                size of zero for the blob, then Blob->Data has
> -                                been left unchanged.
> -
> -  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for Blob->Data.
> -**/
> -STATIC
> -EFI_STATUS
> -FetchBlob (
> -  IN OUT KERNEL_BLOB *Blob
> -  )
> -{
> -  UINT32 Left;
> -
> -  //
> -  // Read blob size.
> -  //
> -  QemuFwCfgSelectItem (Blob->SizeKey);
> -  Blob->Size = QemuFwCfgRead32 ();
> -  if (Blob->Size == 0) {
> -    return EFI_SUCCESS;
> -  }
> -
> -  //
> -  // Read blob.
> -  //
> -  Blob->Data = AllocatePool (Blob->Size);
> -  if (Blob->Data == NULL) {
> -    DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
> -      __FUNCTION__, (INT64)Blob->Size, Blob->Name));
> -    return EFI_OUT_OF_RESOURCES;
> -  }
> -
> -  DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
> -    (INT64)Blob->Size, Blob->Name));
> -  QemuFwCfgSelectItem (Blob->DataKey);
> -
> -  Left = Blob->Size;
> -  do {
> -    UINT32 Chunk;
> -
> -    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
> -    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
> -    Left -= Chunk;
> -    DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
> -      __FUNCTION__, (INT64)Left, Blob->Name));
> -  } while (Left > 0);
> -  return EFI_SUCCESS;
> -}
> -
> -
>  //
>  // The entry point of the feature.
>  //
> @@ -916,83 +40,13 @@ TryRunningQemuKernel (
>    VOID
>    )
>  {
> -  UINTN                     BlobType;
> -  KERNEL_BLOB               *CurrentBlob;
> -  KERNEL_BLOB               *KernelBlob, *InitrdBlob, *CommandLineBlob;
>    EFI_STATUS                Status;
> -  EFI_HANDLE                FileSystemHandle;
> -  EFI_DEVICE_PATH_PROTOCOL  *KernelDevicePath;
>    EFI_HANDLE                KernelImageHandle;
> -  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
>  
> -  Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
> +  Status = QemuLoadKernelImage (&KernelImageHandle);
>    if (EFI_ERROR (Status)) {
> -    DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
> -    return Status;
> -  }
> -
> -  //
> -  // Fetch all blobs.
> -  //
> -  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
> -    CurrentBlob = &mKernelBlob[BlobType];
> -    Status = FetchBlob (CurrentBlob);
> -    if (EFI_ERROR (Status)) {
> -      goto FreeBlobs;
> -    }
> -    mTotalBlobBytes += CurrentBlob->Size;
> -  }
> -  KernelBlob      = &mKernelBlob[KernelBlobTypeKernel];
> -  InitrdBlob      = &mKernelBlob[KernelBlobTypeInitrd];
> -  CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];
> -
> -  if (KernelBlob->Data == NULL) {
> -    Status = EFI_NOT_FOUND;
> -    goto FreeBlobs;
> -  }
> -
> -  //
> -  // Create a new handle with a single VenHw() node device path protocol on it,
> -  // plus a custom SimpleFileSystem protocol on it.
> -  //
> -  FileSystemHandle = NULL;
> -  Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
> -                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
> -                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
> -                  NULL);
> -  if (EFI_ERROR (Status)) {
> -    DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
> -      __FUNCTION__, Status));
> -    goto FreeBlobs;
> -  }
> -
> -  //
> -  // Create a device path for the kernel image to be loaded from that will call
> -  // back into our file system.
> -  //
> -  KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);
> -  if (KernelDevicePath == NULL) {
> -    DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",
> -      __FUNCTION__));
> -    Status = EFI_OUT_OF_RESOURCES;
> -    goto UninstallProtocols;
> -  }
> -
> -  //
> -  // Load the image. This should call back into our file system.
> -  //
> -  Status = gBS->LoadImage (
> -                  FALSE,             // BootPolicy: exact match required
> -                  gImageHandle,      // ParentImageHandle
> -                  KernelDevicePath,
> -                  NULL,              // SourceBuffer
> -                  0,                 // SourceSize
> -                  &KernelImageHandle
> -                  );
> -  if (EFI_ERROR (Status)) {
> -    DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
>      if (Status != EFI_SECURITY_VIOLATION) {

(2) According to my earlier suggestion, this EFI_SECURITY_VIOLATION
check should be moved into the library, and the library should transform
that into EFI_ACCESS_DENIED (with not image handle left allocated).

> -      goto FreeKernelDevicePath;
> +      return Status;
>      }
>      //
>      // From the resource allocation perspective, EFI_SECURITY_VIOLATION means
> @@ -1001,74 +55,6 @@ TryRunningQemuKernel (
>      goto UnloadKernelImage;
>    }
>  
> -  //
> -  // Construct the kernel command line.
> -  //
> -  Status = gBS->OpenProtocol (
> -                  KernelImageHandle,
> -                  &gEfiLoadedImageProtocolGuid,
> -                  (VOID **)&KernelLoadedImage,
> -                  gImageHandle,                  // AgentHandle
> -                  NULL,                          // ControllerHandle
> -                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> -                  );
> -  ASSERT_EFI_ERROR (Status);
> -
> -  if (CommandLineBlob->Data == NULL) {
> -    KernelLoadedImage->LoadOptionsSize = 0;
> -  } else {
> -    //
> -    // Verify NUL-termination of the command line.
> -    //
> -    if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {
> -      DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",
> -        __FUNCTION__));
> -      Status = EFI_PROTOCOL_ERROR;
> -      goto UnloadKernelImage;
> -    }
> -
> -    //
> -    // Drop the terminating NUL, convert to UTF-16.
> -    //
> -    KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;
> -  }
> -
> -  if (InitrdBlob->Data != NULL) {
> -    //
> -    // Append ' initrd=<name>' in UTF-16.
> -    //
> -    KernelLoadedImage->LoadOptionsSize +=
> -                                        (8 + StrLen(InitrdBlob->Name)) * 2;
> -  }
> -
> -  if (KernelLoadedImage->LoadOptionsSize == 0) {
> -    KernelLoadedImage->LoadOptions = NULL;
> -  } else {
> -    //
> -    // NUL-terminate in UTF-16.
> -    //
> -    KernelLoadedImage->LoadOptionsSize += 2;
> -
> -    KernelLoadedImage->LoadOptions = AllocatePool (
> -                                       KernelLoadedImage->LoadOptionsSize);
> -    if (KernelLoadedImage->LoadOptions == NULL) {
> -      KernelLoadedImage->LoadOptionsSize = 0;
> -      Status = EFI_OUT_OF_RESOURCES;
> -      goto UnloadKernelImage;
> -    }
> -
> -    UnicodeSPrintAsciiFormat (
> -      KernelLoadedImage->LoadOptions,
> -      KernelLoadedImage->LoadOptionsSize,
> -      "%a%a%s",
> -      (CommandLineBlob->Data == NULL) ?  "" : (CHAR8 *)CommandLineBlob->Data,
> -      (InitrdBlob->Data      == NULL) ?  "" : " initrd=",
> -      (InitrdBlob->Data      == NULL) ? L"" : InitrdBlob->Name
> -      );
> -    DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
> -      (CHAR16 *)KernelLoadedImage->LoadOptions));
> -  }
> -
>    //
>    // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
>    //
> @@ -1080,41 +66,13 @@ TryRunningQemuKernel (
>    //
>    // Start the image.
>    //
> -  Status = gBS->StartImage (
> -                KernelImageHandle,
> -                NULL,              // ExitDataSize
> -                NULL               // ExitData
> -                );
> +  Status = QemuStartKernelImage (KernelImageHandle);
>    if (EFI_ERROR (Status)) {
>      DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
>    }

(3) Please update the debug message to "QemuStartKernelImage".

>  
> -  if (KernelLoadedImage->LoadOptions != NULL) {
> -    FreePool (KernelLoadedImage->LoadOptions);
> -  }
> -  KernelLoadedImage->LoadOptionsSize = 0;
> -
>  UnloadKernelImage:
> -  gBS->UnloadImage (KernelImageHandle);
> -
> -FreeKernelDevicePath:
> -  FreePool (KernelDevicePath);
> -
> -UninstallProtocols:
> -  gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
> -         &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
> -         &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
> -         NULL);
> -
> -FreeBlobs:
> -  while (BlobType > 0) {
> -    CurrentBlob = &mKernelBlob[--BlobType];
> -    if (CurrentBlob->Data != NULL) {
> -      FreePool (CurrentBlob->Data);
> -      CurrentBlob->Size = 0;
> -      CurrentBlob->Data = NULL;
> -    }
> -  }
> +  QemuUnloadKernelImage (KernelImageHandle);
>  
>    return Status;
>  }
> 

Thanks,
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line
  2020-03-02  7:29 ` [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line Ard Biesheuvel
@ 2020-03-02 17:31   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 17:31 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> We have no need for exposing the kernel command line as a file,
> so remove support for that.

OK

> Since the remaining blobs (kernel
> and initrd) are typically much larger than a page, switch to
> the page based allocator for blobs at the same time.

Not sure why this matters, but I don't mind. However:

> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 6 ++----
>  1 file changed, 2 insertions(+), 4 deletions(-)
> 
> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> index efecbd817da1..b8d64e2781fc 100644
> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> @@ -30,7 +30,6 @@
>  typedef enum {
>    KernelBlobTypeKernel,
>    KernelBlobTypeInitrd,
> -  KernelBlobTypeCommandLine,
>    KernelBlobTypeMax
>  } KERNEL_BLOB_TYPE;
>  
> @@ -45,7 +44,6 @@ typedef struct {
>  STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
>    { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
>    { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
> -  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
>  };
>  
>  STATIC UINT64 mTotalBlobBytes;
> @@ -865,7 +863,7 @@ FetchBlob (
>    //
>    // Read blob.
>    //
> -  Blob->Data = AllocatePool (Blob->Size);
> +  Blob->Data = AllocatePages (EFI_SIZE_TO_PAGES (Blob->Size));

(1) EFI_SIZE_TO_PAGES expects a UINTN, but KERNEL_BLOB.Size is UINT32.
Please cast the argument.

>    if (Blob->Data == NULL) {
>      DEBUG ((DEBUG_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
>        __FUNCTION__, (INT64)Blob->Size, Blob->Name));
> @@ -969,7 +967,7 @@ QemuKernelLoaderFsDxeEntrypoint (
>    while (BlobType > 0) {
>      CurrentBlob = &mKernelBlob[--BlobType];
>      if (CurrentBlob->Data != NULL) {
> -      FreePool (CurrentBlob->Data);
> +      FreePages (CurrentBlob->Data, EFI_SIZE_TO_PAGES (CurrentBlob->Size));

(2) Same as (1).

>        CurrentBlob->Size = 0;
>        CurrentBlob->Data = NULL;
>      }
> 

With (1) and (2) fixed:

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block
  2020-03-02  7:29 ` [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block Ard Biesheuvel
@ 2020-03-02 17:58   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-02 17:58 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> On x86, the kernel image consists of a setup block and the actual kernel,
> and QEMU presents these as separate blobs, whereas on disk (and in terms
> of PE/COFF image signing), they consist of a single image.
> 
> So add support to our FS loader driver to expose files via the abstract
> file system that consist of up to two concatenated blobs, and redefine
> the kernel file so it consists of the setup and kernel blobs, on every
> architecture (on non-x86, the setup block is simply 0 bytes and is
> therefore ignored implicitly)
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 70 ++++++++++++++------
>  1 file changed, 49 insertions(+), 21 deletions(-)
> 
> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> index b8d64e2781fc..77d8fedb738a 100644
> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> @@ -34,16 +34,29 @@ typedef enum {
>  } KERNEL_BLOB_TYPE;
>  
>  typedef struct {
> -  FIRMWARE_CONFIG_ITEM CONST SizeKey;
> -  FIRMWARE_CONFIG_ITEM CONST DataKey;
> -  CONST CHAR16 *       CONST Name;
> -  UINT32                     Size;
> -  UINT8                      *Data;
> +  CONST CHAR16                  Name[8];
> +  struct {
> +    FIRMWARE_CONFIG_ITEM CONST  SizeKey;
> +    FIRMWARE_CONFIG_ITEM CONST  DataKey;
> +    UINT32                      Size;
> +  }                             FwCfgItem[2];
> +  UINT32                        Size;
> +  UINT8                         *Data;
>  } KERNEL_BLOB;
>  
>  STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
> -  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
> -  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
> +  {
> +    L"kernel",
> +    {
> +      { QemuFwCfgItemKernelSetupSize, QemuFwCfgItemKernelSetupData, },
> +      { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      },
> +    }
> +  }, {
> +    L"initrd",
> +    {
> +      { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      },
> +    }
> +  }
>  };
>  
>  STATIC UINT64 mTotalBlobBytes;
> @@ -850,12 +863,20 @@ FetchBlob (
>    )
>  {
>    UINT32 Left;
> +  UINTN  Idx;
> +  UINT8  *ChunkData;
>  
>    //
>    // Read blob size.
>    //
> -  QemuFwCfgSelectItem (Blob->SizeKey);
> -  Blob->Size = QemuFwCfgRead32 ();
> +  Blob->Size = 0;
> +  for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
> +    if (Blob->FwCfgItem[Idx].SizeKey == 0) {
> +      break;
> +    }
> +    QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].SizeKey);
> +    Blob->Size += Blob->FwCfgItem[Idx].Size = QemuFwCfgRead32 ();

(1) Please break up these assignments into two statements.

> +  }
>    if (Blob->Size == 0) {
>      return EFI_SUCCESS;
>    }
> @@ -872,18 +893,25 @@ FetchBlob (
>  
>    DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
>      (INT64)Blob->Size, Blob->Name));
> -  QemuFwCfgSelectItem (Blob->DataKey);
> -
> -  Left = Blob->Size;
> -  do {
> -    UINT32 Chunk;
> -
> -    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
> -    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
> -    Left -= Chunk;
> -    DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
> -      __FUNCTION__, (INT64)Left, Blob->Name));
> -  } while (Left > 0);
> +
> +  ChunkData = Blob->Data;
> +  for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
> +    QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].DataKey);

(2) For the initrd, this will write a zero selector when (Idx==1), if I
understand correctly. We shouldn't do that; please break out of the loop
early, like in the previous loop. (Check either SizeKey or DataKey
against 0.)

> +
> +    Left = Blob->FwCfgItem[Idx].Size;
> +    do {

Previously, the "do" loop was appropriate, because "Left" was guaranteed
positive here. That's no longer true: according to your description, for
non-x86, the setup block has zero size. In that case, we shouldn't enter
the inner loop body at all.

(3) So please turn this into a "while" loop.

> +      UINT32 Chunk;
> +
> +      Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
> +      QemuFwCfgReadBytes (Chunk, ChunkData + Blob->FwCfgItem[Idx].Size - Left);
> +      Left -= Chunk;
> +      DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\" (%d)\n",
> +        __FUNCTION__, (INT64)Left, Blob->Name, Idx));

(4) Idx is a UINTN, we shouldn't log it with "%d". The fully portable
approach is to use %Lu as the format specifier and cast Idx to UINT64.

If we are sure Idx fits into an INT32, then we can stick with %d, but we
should still cast Idx to INT32.

> +    } while (Left > 0);
> +
> +    ChunkData += Blob->FwCfgItem[Idx].Size;
> +  }
> +
>    return EFI_SUCCESS;
>  }
>  
> 

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib
  2020-03-02 17:12   ` [edk2-devel] " Laszlo Ersek
@ 2020-03-03  7:36     ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03  7:36 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 18:12, Laszlo Ersek wrote:
> On 03/02/20 08:29, Ard Biesheuvel wrote:
>> 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>
>> ---
>>  OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c   | 253 ++++++++++++++++++++
>>  OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf |  39 +++
>>  2 files changed, 292 insertions(+)
>>
>> diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
>> new file mode 100644
>> index 000000000000..c48c7a88dd91
>> --- /dev/null
>> +++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
>> @@ -0,0 +1,253 @@
>> +/**  @file
>> +  Generic implementation of QemuLoadImageLib library class interface.
>> +
>> +  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
>> +
>> +  SPDX-License-Identifier: BSD-2-Clause-Patent
>> +**/
>> +
>> +#include <Uefi.h>
>> +
>> +#include <Guid/QemuKernelLoaderFsMedia.h>
>> +#include <Library/DebugLib.h>
>> +#include <Library/MemoryAllocationLib.h>
>> +#include <Library/PrintLib.h>
>> +#include <Library/QemuFwCfgLib.h>
> 
> (1) I think it would be nicer if, in this patch, we didn't access fw_cfg
> at all. The filesystem already exposes "cmdline", we could use that.
> 
> ... Except, I can see you are removing that in patch #7... OK, I guess.
> 
>> +#include <Library/UefiBootServicesTableLib.h>
>> +#include <Protocol/DevicePath.h>
>> +#include <Protocol/LoadedImage.h>

(10) Sorry, just noticed, from comparing the x86 instance: please
include the lib class header here.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback
  2020-03-02  7:29 ` [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Ard Biesheuvel
@ 2020-03-03  9:45   ` Laszlo Ersek
  2020-03-03 10:08     ` Ard Biesheuvel
  0 siblings, 1 reply; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03  9:45 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> 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 a LoadedImage pseudo-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 loaded image solely by a handle. The pseudo-protocol
> record type and the use of CR() is to get DEBUG coverage for the code
> that deals with these handles.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c   | 562 ++++++++++++++++++++
>  OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf |  42 ++
>  OvmfPkg/OvmfPkg.dec                                         |   1 +
>  3 files changed, 605 insertions(+)

(1) "-C 41 --find-copies-harder" displays this lib instance as a
modified version of the generic instance (from patch#4). Please sync
this instance too with those comments (whichever apply).

> diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
> new file mode 100644
> index 000000000000..a1ced417d1cc
> --- /dev/null
> +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
> @@ -0,0 +1,562 @@
> +/**  @file
> +  X86 specific implementation of QemuLoadImageLib library class interface
> +  with support for loading mixed mode images and non-EFI stub images
> +
> +  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> +
> +  SPDX-License-Identifier: BSD-2-Clause-Patent
> +**/
> +
> +#include <Uefi.h>
> +
> +#include <Guid/QemuKernelLoaderFsMedia.h>
> +#include <Library/DebugLib.h>
> +#include <Library/LoadLinuxLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/QemuFwCfgLib.h>
> +#include <Library/QemuLoadImageLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Protocol/DevicePath.h>
> +#include <Protocol/LoadedImage.h>
> +
> +#pragma pack (1)
> +typedef struct {
> +  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;
> +  CHAR16                    FilePath[sizeof (L"kernel")];
> +} KERNEL_FILE_DEVPATH;
> +
> +typedef struct {
> +  VENDOR_DEVICE_PATH        VenMediaNode;
> +  KERNEL_FILE_DEVPATH       FileNode;
> +  EFI_DEVICE_PATH_PROTOCOL  EndNode;
> +} KERNEL_VENMEDIA_FILE_DEVPATH;
> +#pragma pack ()
> +
> +STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
> +  {
> +    {
> +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
> +      { sizeof (VENDOR_DEVICE_PATH) }
> +    },
> +    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
> +  }, {
> +    {
> +      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
> +      { sizeof (KERNEL_FILE_DEVPATH) }
> +    },
> +    L"kernel",
> +  }, {
> +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> +  }
> +};
> +
> +typedef struct {
> +  VOID    *SetupBuf;
> +  VOID    *KernelBuf;
> +  CHAR8   *CommandLine;
> +  VOID    *InitrdData;
> +  UINTN   SetupSize;
> +  UINTN   KernelInitialSize;
> +  UINTN   InitrdSize;
> +  UINTN   CommandLineSize;
> +} QEMU_LEGACY_LOADED_IMAGE;
> +
> +#define QEMU_LEGACY_LOADED_IMAGE_SIG \
> +          SIGNATURE_64 ('Q', 'L', 'O', 'A', 'D', 'I', 'M', 'G')
> +
> +typedef struct {
> +  UINT64                    Signature;
> +  QEMU_LEGACY_LOADED_IMAGE  LoadedImage;
> +} QEMU_LEGACY_LOADED_IMAGE_REC;
> +
> +#define QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE(ImagePointer) \
> +        CR (ImagePointer, QEMU_LEGACY_LOADED_IMAGE_REC, LoadedImage, \
> +          QEMU_LEGACY_LOADED_IMAGE_SIG)
> +

I don't understand:

- why we define two structures here (QEMU_LEGACY_LOADED_IMAGE and
QEMU_LEGACY_LOADED_IMAGE_REC),

- and the related use of a Signature + CR().

These tools help if we have a pre-defined -- e.g., standard -- protocol,
for which we'd like to provide an implementation, and we need to carry
some auxiliary ("private") data for every protocol instance that we
produce. In that case, we embed the pre-defined protocol structure in
our own, and use CR() / Signature etc. to arrive at our own container
structure, from a pointer to the pre-defined protocol. This is all
motivated by the fact that the member functions of the pre-defined
protocol take a "This" pointer to said pre-defined protocol.

But in our case here, this use case doesn't seem to apply. We do not
embed a pre-defined protocol structure; we define a brand new inner
structure, and we introduce a brand new protocol GUID for it. We don't
have any pre-existent function prototype that can only take a pointer to
QEMU_LEGACY_LOADED_IMAGE, from which we'd have to arrive at
QEMU_LEGACY_LOADED_IMAGE_REC.

(2) So that makes me think we should simply drop the macro and the
wrapper structure.

> +STATIC
> +VOID
> +FreeLegacyImage (
> +  IN  QEMU_LEGACY_LOADED_IMAGE *LoadedImage
> +  )
> +{
> +  if (LoadedImage->SetupBuf != NULL) {
> +    FreePages (LoadedImage->SetupBuf,
> +      EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
> +  }
> +  if (LoadedImage->KernelBuf != NULL) {
> +    FreePages (LoadedImage->KernelBuf,
> +      EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));
> +  }
> +  if (LoadedImage->CommandLine != NULL) {
> +    FreePages (LoadedImage->CommandLine,
> +      EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));
> +  }
> +  if (LoadedImage->InitrdData != NULL) {
> +    FreePages (LoadedImage->InitrdData,
> +      EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
> +  }
> +}
> +
> +STATIC
> +EFI_STATUS
> +QemuLoadLegacyImage (
> +  OUT EFI_HANDLE                  *ImageHandle
> +  )
> +{
> +  EFI_STATUS                      Status;
> +  UINTN                           KernelSize;
> +  UINTN                           SetupSize;
> +  QEMU_LEGACY_LOADED_IMAGE_REC    *LoadedImageRec;
> +  QEMU_LEGACY_LOADED_IMAGE        *LoadedImage;

(3) If I'm right to think that this patch incorporates non-trivial code
from TryRunningQemuKernel()
[OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c], then please (a)
note that in the commit message, (b) please preserve the Intel copyright
notice.

> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
> +  KernelSize = (UINTN)QemuFwCfgRead64 ();

Sigh, now I understand where the "64" comes from -- it's a bug in the
original code. QEMU exposes these values with fw_cfg_add_i32(); see
load_image_to_fw_cfg() in "hw/arm/boot.c", and the direct calls in
"hw/i386/x86.c".

We used to get away with Read64 only because edk2 uses little endian,
and fw_cfg is specified to produce zeroes when reading past the end of
an item.

(4) So we should use *32 for reading *all* these items, regardless of
"OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c". (I'm not going to
point out each one below.)

> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
> +  SetupSize = (UINTN)QemuFwCfgRead64 ();
> +
> +  if (KernelSize == 0 || SetupSize == 0) {
> +    DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  LoadedImageRec = AllocateZeroPool (sizeof (*LoadedImageRec));
> +  if (LoadedImageRec == NULL) {
> +    return EFI_OUT_OF_RESOURCES;
> +  }
> +
> +  LoadedImageRec->Signature = QEMU_LEGACY_LOADED_IMAGE_SIG;
> +  LoadedImage = &LoadedImageRec->LoadedImage;
> +
> +  LoadedImage->SetupSize = SetupSize;
> +  LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (
> +                            EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
> +  if (LoadedImage->SetupBuf == NULL) {
> +    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));
> +    return EFI_OUT_OF_RESOURCES;

(5) This leaks "LoadedImageRec".

> +  }
> +
> +  DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));
> +  DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));
> +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
> +  QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);
> +  DEBUG ((DEBUG_INFO, " [done]\n"));
> +
> +  Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,
> +             LoadedImage->SetupSize);
> +  if (EFI_ERROR (Status)) {
> +    goto FreeAndReturn;
> +  }

OK, this seems to release everything correctly.

> +
> +  Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
> +  if (EFI_ERROR (Status)) {
> +    goto FreeAndReturn;
> +  }
> +
> +  LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
> +                                     LoadedImage->SetupBuf, KernelSize);
> +  if (LoadedImage->KernelInitialSize == 0) {
> +    Status = EFI_UNSUPPORTED;
> +    goto FreeAndReturn;
> +  }
> +
> +  LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
> +                             LoadedImage->SetupBuf,
> +                             EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
> +                             );
> +  if (LoadedImage->KernelBuf == NULL) {
> +    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
> +    Status = EFI_OUT_OF_RESOURCES;
> +    goto FreeAndReturn;
> +  }
> +
> +  DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));
> +  DEBUG ((DEBUG_INFO, "Reading kernel image ..."));
> +  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
> +  QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);
> +  DEBUG ((DEBUG_INFO, " [done]\n"));
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> +  LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead64 ();
> +
> +  if (LoadedImage->CommandLineSize > 0) {
> +    LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
> +                                 EFI_SIZE_TO_PAGES (
> +                                   LoadedImage->CommandLineSize));
> +    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> +    QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
> +  } else {
> +    LoadedImage->CommandLine = NULL;

(6) This assignment is superfluous. We allocate (*LoadedImageRec) with
AllocateZeroPool(), which contains (*LoadedImage); and we rely on the
zero-initialization in FreeLegacyImage().

> +  }
> +
> +  Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
> +             LoadedImage->CommandLine);
> +  if (EFI_ERROR (Status)) {
> +    goto FreeAndReturn;
> +  }
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> +  LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead64 ();
> +
> +  if (LoadedImage->InitrdSize > 0) {
> +    LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
> +                                LoadedImage->SetupBuf,
> +                                EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
> +    DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",
> +      (UINT32)LoadedImage->InitrdSize));
> +    DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
> +    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
> +    QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
> +    DEBUG ((DEBUG_INFO, " [done]\n"));
> +  } else {
> +    LoadedImage->InitrdData = NULL;

(7) Same as (6).

> +  }
> +
> +  Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
> +             LoadedImage->InitrdSize);
> +  if (EFI_ERROR (Status)) {
> +    goto FreeAndReturn;
> +  }
> +
> +  Status = gBS->InstallProtocolInterface (ImageHandle,
> +                  &gX86QemuKernelLoadedImageGuid, EFI_NATIVE_INTERFACE,
> +                  LoadedImage);

(8) This indicates that the identifier should actually be a
*ProtocolGuid, with the usual consequences:

- it should be put in [Protocols], not [Guids], in the INF file and the
DEC file,

- we should introduce the protocol related artifacts (the extern
declaration for *ProtocolGuid, the definition of the initializer macro,
and the typedef for the protocol structure) in a new header file under
OvmfPkg/Include/Protocol.

It's OK if the new protocol is not standard and subject to change at any
time -- we only need to state that explicitly in the new header file.

I understand that you might not want to expose so many internals in a
protocol header, but, after all, we do introduce the GUID in the DEC
file too.

> +  if (EFI_ERROR (Status)) {
> +    goto FreeAndReturn;
> +  }
> +  return EFI_SUCCESS;
> +
> +FreeAndReturn:
> +  FreeLegacyImage (LoadedImage);
> +  FreePool (LoadedImageRec);
> +  return Status;
> +}
> +
> +STATIC
> +EFI_STATUS
> +QemuStartLegacyImage (
> +  IN  EFI_HANDLE                ImageHandle
> +  )
> +{
> +  EFI_STATUS                    Status;
> +  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
> +  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
> +
> +  Status = gBS->OpenProtocol (ImageHandle,
> +                  &gX86QemuKernelLoadedImageGuid,
> +                  (VOID **)&LoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return EFI_INVALID_PARAMETER;

(Or you could return "Status" too (up to you); OpenProtocol() returns
EFI_UNSUPPORTED if the protocol is not on the handle.

If you decide to update this, then please keep QemuUnloadLegacyImage()
in sync.)

> +  }
> +
> +  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
> +
> +  return LoadLinux (LoadedImageRec->LoadedImage.KernelBuf,
> +                    LoadedImageRec->LoadedImage.SetupBuf);
> +}
> +
> +STATIC
> +EFI_STATUS
> +QemuUnloadLegacyImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  )
> +{
> +  EFI_STATUS                    Status;
> +  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
> +  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
> +
> +  Status = gBS->OpenProtocol (ImageHandle,
> +                  &gX86QemuKernelLoadedImageGuid,
> +                  (VOID **)&LoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (EFI_ERROR (Status)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
> +
> +  FreeLegacyImage (&LoadedImageRec->LoadedImage);
> +  FreePool (LoadedImageRec);
> +  return EFI_SUCCESS;
> +}

(9) Please uninstall the protocol interface, before freeing it.

> +
> +/**
> +  Download the kernel, the initial ramdisk, and the kernel command line from
> +  QEMU's fw_cfg. The kernel will be instructed via its command line to load
> +  the initrd from the same Simple FileSystem.
> +
> +  @param[out] ImageHandle       The image handle that was allocated for
> +                                loading the image
> +  @param[out] LoadedImage       The loaded image protocol that was installed
> +                                on ImageHandle by the LoadImage boot service.
> +
> +  @retval EFI_SUCCESS           The image was loaded successfully.
> +  @retval EFI_NOT_FOUND         Kernel image was not found.
> +  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
> +  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
> +
> +  @return                       Error codes from any of the underlying
> +                                functions.
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuLoadKernelImage (
> +  OUT EFI_HANDLE            *ImageHandle
> +  )
> +{
> +  EFI_STATUS                Status;
> +  EFI_HANDLE                KernelImageHandle;
> +  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
> +  UINTN                     CommandLineSize;
> +  CHAR8                     *CommandLine;
> +  UINTN                     InitrdSize;
> +
> +  //
> +  // Load the image. This should call back into the QEMU EFI loader file system.
> +  //
> +  Status = gBS->LoadImage (
> +                  FALSE,                    // BootPolicy: exact match required
> +                  gImageHandle,             // ParentImageHandle
> +                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
> +                  NULL,                     // SourceBuffer
> +                  0,                        // SourceSize
> +                  &KernelImageHandle
> +                  );
> +  switch (Status) {
> +  case EFI_SUCCESS:
> +    break;
> +
> +  case EFI_NOT_FOUND:
> +    //
> +    // The image does not exist - no -kernel image was supplied via the
> +    // command line so no point in invoking the legacy fallback
> +    //
> +    return EFI_NOT_FOUND;
> +
> +  case EFI_SECURITY_VIOLATION:
> +    //
> +    // We are running with UEFI secure boot enabled, and the image failed to
> +    // authenticate. For compatibility reasons, we fall back to the legacy
> +    // loader in this case. Since the image has been loaded, we need to unload
> +    // it before proceeding
> +    //
> +    gBS->UnloadImage (KernelImageHandle);
> +    //
> +    // Fall through
> +    //

Yes, good point; this actually supports my request (4a) under patch#4 --
EFI_SECURITY_VIOLATION should be handled internally to the library.

> +  case EFI_UNSUPPORTED:
> +    //
> +    // The image is not natively supported or cross-type supported. Let's try
> +    // loading it using the loader that parses the bzImage metadata directly.
> +    //
> +    KernelImageHandle = NULL;
> +    Status = QemuLoadLegacyImage (&KernelImageHandle);
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
> +        Status));
> +      return Status;
> +    }
> +    *ImageHandle = KernelImageHandle;
> +    return EFI_SUCCESS;
> +
> +  default:
> +    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
> +    return Status;
> +  }
> +
> +  //
> +  // Construct the kernel command line.
> +  //
> +  Status = gBS->OpenProtocol (
> +                  KernelImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&KernelLoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> +  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
> +
> +  if (CommandLineSize == 0) {
> +    KernelLoadedImage->LoadOptionsSize = 0;
> +  } else {
> +    CommandLine = AllocatePool (CommandLineSize);
> +    ASSERT (CommandLine != NULL);
> +
> +    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> +    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
> +
> +    //
> +    // Verify NUL-termination of the command line.
> +    //
> +    if (CommandLine[CommandLineSize - 1] != '\0') {
> +      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
> +        __FUNCTION__));
> +      Status = EFI_PROTOCOL_ERROR;
> +      goto FreeCommandLine;
> +    }
> +
> +    //
> +    // Drop the terminating NUL, convert to UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;
> +  }
> +
> +  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> +  InitrdSize = (UINTN) QemuFwCfgRead64 ();
> +
> +  if (InitrdSize > 0) {
> +    //
> +    // Append ' initrd=initrd' in UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
> +  }
> +
> +  if (KernelLoadedImage->LoadOptionsSize == 0) {
> +    KernelLoadedImage->LoadOptions = NULL;
> +  } else {
> +    //
> +    // NUL-terminate in UTF-16.
> +    //
> +    KernelLoadedImage->LoadOptionsSize += 2;
> +
> +    KernelLoadedImage->LoadOptions = AllocatePool (
> +                                       KernelLoadedImage->LoadOptionsSize);
> +    if (KernelLoadedImage->LoadOptions == NULL) {
> +      KernelLoadedImage->LoadOptionsSize = 0;
> +      Status = EFI_OUT_OF_RESOURCES;
> +      goto FreeCommandLine;
> +    }
> +
> +    UnicodeSPrintAsciiFormat (
> +      KernelLoadedImage->LoadOptions,
> +      KernelLoadedImage->LoadOptionsSize,
> +      "%a%a",
> +      (CommandLineSize == 0) ?  "" : CommandLine,
> +      (InitrdSize == 0)      ?  "" : " initrd=initrd"
> +      );
> +    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
> +      (CHAR16 *)KernelLoadedImage->LoadOptions));
> +  }
> +
> +  *ImageHandle = KernelImageHandle;
> +  return EFI_SUCCESS;
> +
> +FreeCommandLine:
> +  FreePool (CommandLine);
> +  gBS->UnloadImage (KernelImageHandle);
> +
> +  return Status;
> +}
> +
> +/**
> +  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
> +
> +  @param[in]  ImageHandle         Handle of image to be started.
> +
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
> +                                  or the image has already been initialized with
> +                                  StartImage
> +  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
> +                                  image should not be started.
> +
> +  @return                         Error codes returned by the started image
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuStartKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  )
> +{
> +  EFI_STATUS                Status;
> +
> +  Status = gBS->StartImage (
> +                  ImageHandle,
> +                  NULL,              // ExitDataSize
> +                  NULL               // ExitData
> +                  );

(10) I'd like to avoid calling StartImage on a handle we know has not
been created with gBS->LoadImage(). Can you first check for the custom
protocol on the handle, and base QemuStartLegacyImage() on that (rather
than the EFI_INVALID_PARAMETER return value)?


> +
> +  switch (Status) {
> +#ifdef MDE_CPU_IA32
> +  case EFI_UNSUPPORTED:
> +    //
> +    // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
> +    // we are expecting a IA32 one, and the StartImage () boot service is unable
> +    // to handle it, either because the image does not have the special .compat
> +    // PE/COFF section that Linux specifies for mixed mode capable images, or

(11) So does this series depend on your other patch

  [edk2-devel] [PATCH v3 6/6]
  OvmfPkg IA32: add support for loading X64 images

(which is for <https://bugzilla.tianocore.org/show_bug.cgi?id=2564>)?

The topic branch that you reference in the cover letter of this series
seems to be based on those patches.

Should we make <https://bugzilla.tianocore.org/show_bug.cgi?id=2566> --
i.e. the BZ that tracks the present feature -- dependent on TianoCore#2564?

> +    // because we are running without the support code for that. So do a legacy
> +    // load instead, but do it first so we can reuse the same handle. Then,
> +    // unload the normally loaded image.
> +    //
> +    Status = QemuLoadLegacyImage (&ImageHandle);

Hmmm, I'm getting quite confused here. This seems to indicate that:

- even though we do recognize *some* cases when QemuLoadLegacyImage()
needs to be called from QemuLoadKernelImage() -- which makes sense to me --,

- there are *other* cases needing QemuLoadLegacyImage() that we can only
recognize here, in QemuStartKernelImage(), *after* the normal
LoadImage() branch in QemuLoadKernelImage() succeeded.

Is that right?

If so, then I guess the idea here is to first add another protocol on
the existent ImageHandle (our own custom one), before allowing
gBS->UnloadImage() in QemuUnloadKernelImage() to remove the protocol
interfaces produced by gBS->LoadImage(), and thereby destroy the handle.

I have two concerns:

(12) I'd rather like this part of the code to know for sure that our
custom protocol is not present yet on the image handle -- see (10) above,

(13) I'm worried that we are installing custom protocols on a handle
that was first created by LoadImage(), before we pass it to
UnloadImage(). I don't know if, per spec, LoadImage() / UnloadImage()
are allowed to associate such information with the specific image handle
that is *beyond* the protocol interfaces visible on the handle.

Can we modify the logic somehow so that we don't have to silently call
QemuLoadLegacyImage() inside QemuStartKernelImage()? If not, we should
at least document that this is arguably a grey area per UEFI spec.


> +    if (EFI_ERROR (Status)) {
> +      return Status;
> +    }
> +    QemuUnloadKernelImage (ImageHandle);
> +    //
> +    // Fall through
> +    //
> +#endif
> +  case EFI_INVALID_PARAMETER:
> +    return QemuStartLegacyImage (ImageHandle);
> +  default:
> +    break;
> +  }
> +  return Status;
> +}
> +
> +/**
> +  Unloads an image loaded with QemuLoadKernelImage ().
> +
> +  @param  ImageHandle             Handle that identifies the image to be
> +                                  unloaded.
> +
> +  @retval EFI_SUCCESS             The image has been unloaded.
> +  @retval EFI_UNSUPPORTED         The image has been started, and does not
> +                                  support unload.
> +  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +QemuUnloadKernelImage (
> +  IN  EFI_HANDLE          ImageHandle
> +  )
> +{
> +  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;
> +  EFI_STATUS                  Status;
> +
> +  Status = gBS->OpenProtocol (
> +                  ImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&KernelLoadedImage,
> +                  gImageHandle,                  // AgentHandle
> +                  NULL,                          // ControllerHandle
> +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> +                  );
> +  if (Status == EFI_UNSUPPORTED) {
> +    return QemuUnloadLegacyImage (ImageHandle);
> +  } else if (EFI_ERROR (Status)) {

(14) Please don't follow a "return" statement with an "else"; just
unnest the second branch.

> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (KernelLoadedImage->LoadOptions != NULL) {
> +    FreePool (KernelLoadedImage->LoadOptions);
> +    KernelLoadedImage->LoadOptions = NULL;
> +  }
> +  KernelLoadedImage->LoadOptionsSize = 0;
> +
> +  return gBS->UnloadImage (ImageHandle);
> +}

This function has to cope with three situations:

(i) The image is a normal UEFI executable that was loaded by the
LoadImage() branch of QemuLoadKernelImage(). In this case,
OpenProtocol() is supposed to succeed, and so is gBS->UnloadImage(). OK.

(ii) The image was legacy-loaded by QemuLoadKernelImage() -->
QemuLoadLegacyImage(). Meaning, gBS->LoadImage() failed there, or it
returned EFI_SECURITY_VIOLATION, and we unloaded the kernel image right
there, before performing the legacy load. In either case, OpenProtocol()
here will fail with EFI_UNSUPPORTED, and we defer to
QemuUnloadLegacyImage(). OK.

(iii) The third case is when *both* protocol sets are available on the
handle, and we're being called from QemuStartKernelImage(). In this case
we're in the process of "replacing" EFI_LOADED_IMAGE_PROTOCOL (and
EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL) with our custom protocol. The
function takes the same path as (i) in this case, I think, and that
seems right. OK.

(15) This function needs to document (in comments) all three cases
above, please.


> diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> new file mode 100644
> index 000000000000..4208f5da3b31
> --- /dev/null
> +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> @@ -0,0 +1,42 @@
> +## @file
> +#  X86 specific implementation of QemuLoadImageLib library class interface
> +#  with support for loading mixed mode images and non-EFI stub images
> +#
> +#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#
> +##
> +
> +[Defines]
> +  INF_VERSION                    = 1.27
> +  BASE_NAME                      = X86QemuLoadImageLib
> +  FILE_GUID                      = 2304df80-e21d-4170-9c3c-113c878f7ac0
> +  MODULE_TYPE                    = BASE
> +  VERSION_STRING                 = 1.0
> +  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER
> +
> +[Sources]
> +  X86QemuLoadImageLib.c
> +
> +[Packages]
> +  MdeModulePkg/MdeModulePkg.dec
> +  MdePkg/MdePkg.dec
> +  OvmfPkg/OvmfPkg.dec
> +
> +[LibraryClasses]
> +  DebugLib
> +  MemoryAllocationLib
> +  LoadLinuxLib
> +  PrintLib
> +  QemuFwCfgLib
> +  ReportStatusCodeLib
> +  UefiBootServicesTableLib
> +
> +[Protocols]
> +  gEfiDevicePathProtocolGuid
> +  gEfiLoadedImageProtocolGuid
> +
> +[Guids]
> +  gQemuKernelLoaderFsMediaGuid
> +  gX86QemuKernelLoadedImageGuid
> diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> index 26f977bad795..e66af38f4290 100644
> --- a/OvmfPkg/OvmfPkg.dec
> +++ b/OvmfPkg/OvmfPkg.dec
> @@ -93,6 +93,7 @@ [Guids]
>    gEfiLegacyDevOrderVariableGuid      = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}}
>    gLinuxEfiInitrdMediaGuid            = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}
>    gQemuKernelLoaderFsMediaGuid        = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
> +  gX86QemuKernelLoadedImageGuid       = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}}
>  
>  [Protocols]
>    gVirtioDeviceProtocolGuid           = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
> 

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components
  2020-03-02  7:29 ` [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components Ard Biesheuvel
@ 2020-03-03  9:47   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03  9:47 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Add the components that expose the QEMU abstract loader file system so
> that we can switch over our PlatformBmLib over to it in a subsequent
> patch.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/OvmfPkgIa32.dsc    | 2 ++
>  OvmfPkg/OvmfPkgIa32.fdf    | 1 +
>  OvmfPkg/OvmfPkgIa32X64.dsc | 2 ++
>  OvmfPkg/OvmfPkgIa32X64.fdf | 1 +
>  OvmfPkg/OvmfPkgX64.dsc     | 2 ++
>  OvmfPkg/OvmfPkgX64.fdf     | 1 +
>  6 files changed, 9 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index 8d91903f8b4e..2cc924a6986a 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -361,6 +361,7 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> @@ -711,6 +712,7 @@ [Components]
>        NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
>  !endif
>    }
> +  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>    OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
>    OvmfPkg/Virtio10Dxe/Virtio10.inf
>    OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
> diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
> index f57de4a26f92..61287bd51f84 100644
> --- a/OvmfPkg/OvmfPkgIa32.fdf
> +++ b/OvmfPkg/OvmfPkgIa32.fdf
> @@ -242,6 +242,7 @@ [FV.DXEFV]
>  INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
>  INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
>  INF  MdeModulePkg/Application/UiApp/UiApp.inf
> +INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
>  INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>  INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index 842b4a028913..21d1f156973b 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -365,6 +365,7 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> @@ -723,6 +724,7 @@ [Components.X64]
>        NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
>  !endif
>    }
> +  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>    OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
>    OvmfPkg/Virtio10Dxe/Virtio10.inf
>    OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
> diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
> index 69c133ec08d5..7b770f8fa424 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.fdf
> +++ b/OvmfPkg/OvmfPkgIa32X64.fdf
> @@ -243,6 +243,7 @@ [FV.DXEFV]
>  INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
>  INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
>  INF  MdeModulePkg/Application/UiApp/UiApp.inf
> +INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
>  INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>  INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index 0b1e45d1f15a..f3d0f18db7e2 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -365,6 +365,7 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +  QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> @@ -721,6 +722,7 @@ [Components]
>        NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf
>  !endif
>    }
> +  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>    OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf
>    OvmfPkg/Virtio10Dxe/Virtio10.inf
>    OvmfPkg/VirtioBlkDxe/VirtioBlk.inf
> diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
> index 69c133ec08d5..7b770f8fa424 100644
> --- a/OvmfPkg/OvmfPkgX64.fdf
> +++ b/OvmfPkg/OvmfPkgX64.fdf
> @@ -243,6 +243,7 @@ [FV.DXEFV]
>  INF  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
>  INF  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
>  INF  MdeModulePkg/Application/UiApp/UiApp.inf
> +INF  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>  INF  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
>  INF  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
>  INF  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib
  2020-03-02  7:29 ` [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Ard Biesheuvel
@ 2020-03-03  9:52   ` Laszlo Ersek
  2020-03-03  9:53     ` Laszlo Ersek
  0 siblings, 1 reply; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03  9:52 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Replace the open coded sequence to load Linux on x86 with a short and
> generic sequence invoking QemuLoadImageLib, which can be provided by
> a generic version that only supports the LoadImage and StartImage boot
> services, and one that incorporates the entire legacy loading sequence
> as well.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |   2 +-
>  OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c               | 157 +++-----------------
>  2 files changed, 24 insertions(+), 135 deletions(-)
> 
> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> index f89cce187942..40ac5dd7f9d5 100644
> --- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> +++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
> @@ -48,7 +48,7 @@ [LibraryClasses]
>    NvVarsFileLib
>    QemuFwCfgLib
>    QemuFwCfgS3Lib
> -  LoadLinuxLib
> +  QemuLoadImageLib
>    QemuBootOrderLib
>    ReportStatusCodeLib
>    UefiLib
> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
> index ddfef925edd3..a15b48d360d2 100644
> --- a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
> +++ b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
> @@ -9,11 +9,8 @@
>  
>  #include <Library/BaseLib.h>
>  #include <Library/DebugLib.h>
> -#include <Library/LoadLinuxLib.h>
> -#include <Library/MemoryAllocationLib.h>
> -#include <Library/QemuFwCfgLib.h>
> +#include <Library/QemuLoadImageLib.h>
>  #include <Library/ReportStatusCodeLib.h>
> -#include <Library/UefiBootServicesTableLib.h>
>  #include <Library/UefiLib.h>
>  
>  
> @@ -23,146 +20,38 @@ TryRunningQemuKernel (
>    )
>  {
>    EFI_STATUS                Status;
> -  UINTN                     KernelSize;
> -  UINTN                     KernelInitialSize;
> -  VOID                      *KernelBuf;
> -  UINTN                     SetupSize;
> -  VOID                      *SetupBuf;
> -  UINTN                     CommandLineSize;
> -  CHAR8                     *CommandLine;
> -  UINTN                     InitrdSize;
> -  VOID*                     InitrdData;
> -
> -  SetupBuf = NULL;
> -  SetupSize = 0;
> -  KernelBuf = NULL;
> -  KernelInitialSize = 0;
> -  CommandLine = NULL;
> -  CommandLineSize = 0;
> -  InitrdData = NULL;
> -  InitrdSize = 0;
> -
> -  if (!QemuFwCfgIsAvailable ()) {
> -    return EFI_NOT_FOUND;
> -  }
> -
> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
> -  KernelSize = (UINTN) QemuFwCfgRead64 ();
> -
> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
> -  SetupSize = (UINTN) QemuFwCfgRead64 ();
> -
> -  if (KernelSize == 0 || SetupSize == 0) {
> -    DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n"));
> -    return EFI_NOT_FOUND;
> -  }
> -
> -  SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize));
> -  if (SetupBuf == NULL) {
> -    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n"));
> -    return EFI_OUT_OF_RESOURCES;
> -  }
> -
> -  DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize));
> -  DEBUG ((EFI_D_INFO, "Reading kernel setup image ..."));
> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
> -  QemuFwCfgReadBytes (SetupSize, SetupBuf);
> -  DEBUG ((EFI_D_INFO, " [done]\n"));
> -
> -  Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize);
> -  if (EFI_ERROR (Status)) {
> -    goto FreeAndReturn;
> -  }
> -
> -  Status = LoadLinuxInitializeKernelSetup (SetupBuf);
> -  if (EFI_ERROR (Status)) {
> -    goto FreeAndReturn;
> -  }
> -
> -  KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize);
> -  if (KernelInitialSize == 0) {
> -    Status = EFI_UNSUPPORTED;
> -    goto FreeAndReturn;
> -  }
> -
> -  KernelBuf = LoadLinuxAllocateKernelPages (
> -                SetupBuf,
> -                EFI_SIZE_TO_PAGES (KernelInitialSize));
> -  if (KernelBuf == NULL) {
> -    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n"));
> -    Status = EFI_OUT_OF_RESOURCES;
> -    goto FreeAndReturn;
> -  }
> -
> -  DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize));
> -  DEBUG ((EFI_D_INFO, "Reading kernel image ..."));
> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
> -  QemuFwCfgReadBytes (KernelSize, KernelBuf);
> -  DEBUG ((EFI_D_INFO, " [done]\n"));
> -
> -  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> -  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
> -
> -  if (CommandLineSize > 0) {
> -    CommandLine = LoadLinuxAllocateCommandLinePages (
> -                    EFI_SIZE_TO_PAGES (CommandLineSize));
> -    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> -    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
> -  } else {
> -    CommandLine = NULL;
> -  }
> -
> -  Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine);
> -  if (EFI_ERROR (Status)) {
> -    goto FreeAndReturn;
> -  }
> -
> -  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> -  InitrdSize = (UINTN) QemuFwCfgRead64 ();
> -
> -  if (InitrdSize > 0) {
> -    InitrdData = LoadLinuxAllocateInitrdPages (
> -                   SetupBuf,
> -                   EFI_SIZE_TO_PAGES (InitrdSize)
> -                   );
> -    DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize));
> -    DEBUG ((EFI_D_INFO, "Reading initrd image ..."));
> -    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
> -    QemuFwCfgReadBytes (InitrdSize, InitrdData);
> -    DEBUG ((EFI_D_INFO, " [done]\n"));
> -  } else {
> -    InitrdData = NULL;
> -  }
> -
> -  Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize);
> -  if (EFI_ERROR (Status)) {
> -    goto FreeAndReturn;
> +  EFI_HANDLE                KernelImageHandle;
> +
> +  Status = QemuLoadKernelImage (&KernelImageHandle);
> +  if (EFI_ERROR (Status)) {
> +    if (Status != EFI_SECURITY_VIOLATION) {

(1) This special treatment should disappear, once QemuLoadKernelImage()
keeps EFI_SECURITY_VIOLATION inside.

> +      return Status;
> +    }
> +    //
> +    // From the resource allocation perspective, EFI_SECURITY_VIOLATION means
> +    // "success", so we must roll back the image loading.
> +    //
> +    goto UnloadKernelImage;
>    }
>  
>    //
> -  // Signal the EVT_SIGNAL_READY_TO_BOOT event
> +  // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
>    //
>    EfiSignalEventReadyToBoot();
>  
>    REPORT_STATUS_CODE (EFI_PROGRESS_CODE,
>      (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
>  
> -  Status = LoadLinux (KernelBuf, SetupBuf);
> +  //
> +  // Start the image.
> +  //
> +  Status = QemuStartKernelImage (KernelImageHandle);
> +  if (EFI_ERROR (Status)) {
> +    DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
> +  }

(2) Please run "PatchCheck.py" on the series; it will reject "EFI_D_ERROR".

Thanks,
Laszlo


>  
> -FreeAndReturn:
> -  if (SetupBuf != NULL) {
> -    FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize));
> -  }
> -  if (KernelBuf != NULL) {
> -    FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize));
> -  }
> -  if (CommandLine != NULL) {
> -    FreePages (CommandLine, EFI_SIZE_TO_PAGES (CommandLineSize));
> -  }
> -  if (InitrdData != NULL) {
> -    FreePages (InitrdData, EFI_SIZE_TO_PAGES (InitrdSize));
> -  }
> +UnloadKernelImage:
> +  QemuUnloadKernelImage (KernelImageHandle);
>  
>    return Status;
>  }
> -
> 


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib
  2020-03-03  9:52   ` [edk2-devel] " Laszlo Ersek
@ 2020-03-03  9:53     ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03  9:53 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/03/20 10:52, Laszlo Ersek wrote:
> On 03/02/20 08:29, Ard Biesheuvel wrote:
>> Replace the open coded sequence to load Linux on x86 with a short and
>> generic sequence invoking QemuLoadImageLib, which can be provided by
>> a generic version that only supports the LoadImage and StartImage boot
>> services, and one that incorporates the entire legacy loading sequence
>> as well.
>>
>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>> ---
>>  OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf |   2 +-
>>  OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c               | 157 +++-----------------
>>  2 files changed, 24 insertions(+), 135 deletions(-)
>>
>> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> index f89cce187942..40ac5dd7f9d5 100644
>> --- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> +++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf
>> @@ -48,7 +48,7 @@ [LibraryClasses]
>>    NvVarsFileLib
>>    QemuFwCfgLib
>>    QemuFwCfgS3Lib
>> -  LoadLinuxLib
>> +  QemuLoadImageLib
>>    QemuBootOrderLib
>>    ReportStatusCodeLib
>>    UefiLib
>> diff --git a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
>> index ddfef925edd3..a15b48d360d2 100644
>> --- a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
>> +++ b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c
>> @@ -9,11 +9,8 @@
>>  
>>  #include <Library/BaseLib.h>
>>  #include <Library/DebugLib.h>
>> -#include <Library/LoadLinuxLib.h>
>> -#include <Library/MemoryAllocationLib.h>
>> -#include <Library/QemuFwCfgLib.h>
>> +#include <Library/QemuLoadImageLib.h>
>>  #include <Library/ReportStatusCodeLib.h>
>> -#include <Library/UefiBootServicesTableLib.h>
>>  #include <Library/UefiLib.h>
>>  
>>  
>> @@ -23,146 +20,38 @@ TryRunningQemuKernel (
>>    )
>>  {
>>    EFI_STATUS                Status;
>> -  UINTN                     KernelSize;
>> -  UINTN                     KernelInitialSize;
>> -  VOID                      *KernelBuf;
>> -  UINTN                     SetupSize;
>> -  VOID                      *SetupBuf;
>> -  UINTN                     CommandLineSize;
>> -  CHAR8                     *CommandLine;
>> -  UINTN                     InitrdSize;
>> -  VOID*                     InitrdData;
>> -
>> -  SetupBuf = NULL;
>> -  SetupSize = 0;
>> -  KernelBuf = NULL;
>> -  KernelInitialSize = 0;
>> -  CommandLine = NULL;
>> -  CommandLineSize = 0;
>> -  InitrdData = NULL;
>> -  InitrdSize = 0;
>> -
>> -  if (!QemuFwCfgIsAvailable ()) {
>> -    return EFI_NOT_FOUND;
>> -  }
>> -
>> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
>> -  KernelSize = (UINTN) QemuFwCfgRead64 ();
>> -
>> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
>> -  SetupSize = (UINTN) QemuFwCfgRead64 ();
>> -
>> -  if (KernelSize == 0 || SetupSize == 0) {
>> -    DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n"));
>> -    return EFI_NOT_FOUND;
>> -  }
>> -
>> -  SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize));
>> -  if (SetupBuf == NULL) {
>> -    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n"));
>> -    return EFI_OUT_OF_RESOURCES;
>> -  }
>> -
>> -  DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize));
>> -  DEBUG ((EFI_D_INFO, "Reading kernel setup image ..."));
>> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
>> -  QemuFwCfgReadBytes (SetupSize, SetupBuf);
>> -  DEBUG ((EFI_D_INFO, " [done]\n"));
>> -
>> -  Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize);
>> -  if (EFI_ERROR (Status)) {
>> -    goto FreeAndReturn;
>> -  }
>> -
>> -  Status = LoadLinuxInitializeKernelSetup (SetupBuf);
>> -  if (EFI_ERROR (Status)) {
>> -    goto FreeAndReturn;
>> -  }
>> -
>> -  KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize);
>> -  if (KernelInitialSize == 0) {
>> -    Status = EFI_UNSUPPORTED;
>> -    goto FreeAndReturn;
>> -  }
>> -
>> -  KernelBuf = LoadLinuxAllocateKernelPages (
>> -                SetupBuf,
>> -                EFI_SIZE_TO_PAGES (KernelInitialSize));
>> -  if (KernelBuf == NULL) {
>> -    DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n"));
>> -    Status = EFI_OUT_OF_RESOURCES;
>> -    goto FreeAndReturn;
>> -  }
>> -
>> -  DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize));
>> -  DEBUG ((EFI_D_INFO, "Reading kernel image ..."));
>> -  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
>> -  QemuFwCfgReadBytes (KernelSize, KernelBuf);
>> -  DEBUG ((EFI_D_INFO, " [done]\n"));
>> -
>> -  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
>> -  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
>> -
>> -  if (CommandLineSize > 0) {
>> -    CommandLine = LoadLinuxAllocateCommandLinePages (
>> -                    EFI_SIZE_TO_PAGES (CommandLineSize));
>> -    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
>> -    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
>> -  } else {
>> -    CommandLine = NULL;
>> -  }
>> -
>> -  Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine);
>> -  if (EFI_ERROR (Status)) {
>> -    goto FreeAndReturn;
>> -  }
>> -
>> -  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
>> -  InitrdSize = (UINTN) QemuFwCfgRead64 ();
>> -
>> -  if (InitrdSize > 0) {
>> -    InitrdData = LoadLinuxAllocateInitrdPages (
>> -                   SetupBuf,
>> -                   EFI_SIZE_TO_PAGES (InitrdSize)
>> -                   );
>> -    DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize));
>> -    DEBUG ((EFI_D_INFO, "Reading initrd image ..."));
>> -    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
>> -    QemuFwCfgReadBytes (InitrdSize, InitrdData);
>> -    DEBUG ((EFI_D_INFO, " [done]\n"));
>> -  } else {
>> -    InitrdData = NULL;
>> -  }
>> -
>> -  Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize);
>> -  if (EFI_ERROR (Status)) {
>> -    goto FreeAndReturn;
>> +  EFI_HANDLE                KernelImageHandle;
>> +
>> +  Status = QemuLoadKernelImage (&KernelImageHandle);
>> +  if (EFI_ERROR (Status)) {
>> +    if (Status != EFI_SECURITY_VIOLATION) {
> 
> (1) This special treatment should disappear, once QemuLoadKernelImage()
> keeps EFI_SECURITY_VIOLATION inside.
> 
>> +      return Status;
>> +    }
>> +    //
>> +    // From the resource allocation perspective, EFI_SECURITY_VIOLATION means
>> +    // "success", so we must roll back the image loading.
>> +    //
>> +    goto UnloadKernelImage;
>>    }
>>  
>>    //
>> -  // Signal the EVT_SIGNAL_READY_TO_BOOT event
>> +  // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
>>    //
>>    EfiSignalEventReadyToBoot();
>>  
>>    REPORT_STATUS_CODE (EFI_PROGRESS_CODE,
>>      (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));
>>  
>> -  Status = LoadLinux (KernelBuf, SetupBuf);
>> +  //
>> +  // Start the image.
>> +  //
>> +  Status = QemuStartKernelImage (KernelImageHandle);
>> +  if (EFI_ERROR (Status)) {
>> +    DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
>> +  }
> 
> (2) Please run "PatchCheck.py" on the series; it will reject "EFI_D_ERROR".

(3) also, the debug message should likely refer to
QemuStartKernelImage(), not StartImage().

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback
  2020-03-03  9:45   ` [edk2-devel] " Laszlo Ersek
@ 2020-03-03 10:08     ` Ard Biesheuvel
  2020-03-03 11:20       ` Laszlo Ersek
  0 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-03 10:08 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: edk2-devel-groups-io

On Tue, 3 Mar 2020 at 10:45, Laszlo Ersek <lersek@redhat.com> wrote:
>
> On 03/02/20 08:29, Ard Biesheuvel wrote:
> > 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 a LoadedImage pseudo-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 loaded image solely by a handle. The pseudo-protocol
> > record type and the use of CR() is to get DEBUG coverage for the code
> > that deals with these handles.
> >
> > Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> > ---
> >  OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c   | 562 ++++++++++++++++++++
> >  OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf |  42 ++
> >  OvmfPkg/OvmfPkg.dec                                         |   1 +
> >  3 files changed, 605 insertions(+)
>
> (1) "-C 41 --find-copies-harder" displays this lib instance as a
> modified version of the generic instance (from patch#4). Please sync
> this instance too with those comments (whichever apply).
>
> > diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
> > new file mode 100644
> > index 000000000000..a1ced417d1cc
> > --- /dev/null
> > +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
> > @@ -0,0 +1,562 @@
> > +/**  @file
> > +  X86 specific implementation of QemuLoadImageLib library class interface
> > +  with support for loading mixed mode images and non-EFI stub images
> > +
> > +  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> > +
> > +  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +**/
> > +
> > +#include <Uefi.h>
> > +
> > +#include <Guid/QemuKernelLoaderFsMedia.h>
> > +#include <Library/DebugLib.h>
> > +#include <Library/LoadLinuxLib.h>
> > +#include <Library/MemoryAllocationLib.h>
> > +#include <Library/PrintLib.h>
> > +#include <Library/QemuFwCfgLib.h>
> > +#include <Library/QemuLoadImageLib.h>
> > +#include <Library/UefiBootServicesTableLib.h>
> > +#include <Protocol/DevicePath.h>
> > +#include <Protocol/LoadedImage.h>
> > +
> > +#pragma pack (1)
> > +typedef struct {
> > +  EFI_DEVICE_PATH_PROTOCOL  FilePathHeader;
> > +  CHAR16                    FilePath[sizeof (L"kernel")];
> > +} KERNEL_FILE_DEVPATH;
> > +
> > +typedef struct {
> > +  VENDOR_DEVICE_PATH        VenMediaNode;
> > +  KERNEL_FILE_DEVPATH       FileNode;
> > +  EFI_DEVICE_PATH_PROTOCOL  EndNode;
> > +} KERNEL_VENMEDIA_FILE_DEVPATH;
> > +#pragma pack ()
> > +
> > +STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
> > +  {
> > +    {
> > +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
> > +      { sizeof (VENDOR_DEVICE_PATH) }
> > +    },
> > +    QEMU_KERNEL_LOADER_FS_MEDIA_GUID
> > +  }, {
> > +    {
> > +      MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
> > +      { sizeof (KERNEL_FILE_DEVPATH) }
> > +    },
> > +    L"kernel",
> > +  }, {
> > +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> > +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> > +  }
> > +};
> > +
> > +typedef struct {
> > +  VOID    *SetupBuf;
> > +  VOID    *KernelBuf;
> > +  CHAR8   *CommandLine;
> > +  VOID    *InitrdData;
> > +  UINTN   SetupSize;
> > +  UINTN   KernelInitialSize;
> > +  UINTN   InitrdSize;
> > +  UINTN   CommandLineSize;
> > +} QEMU_LEGACY_LOADED_IMAGE;
> > +
> > +#define QEMU_LEGACY_LOADED_IMAGE_SIG \
> > +          SIGNATURE_64 ('Q', 'L', 'O', 'A', 'D', 'I', 'M', 'G')
> > +
> > +typedef struct {
> > +  UINT64                    Signature;
> > +  QEMU_LEGACY_LOADED_IMAGE  LoadedImage;
> > +} QEMU_LEGACY_LOADED_IMAGE_REC;
> > +
> > +#define QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE(ImagePointer) \
> > +        CR (ImagePointer, QEMU_LEGACY_LOADED_IMAGE_REC, LoadedImage, \
> > +          QEMU_LEGACY_LOADED_IMAGE_SIG)
> > +
>
> I don't understand:
>
> - why we define two structures here (QEMU_LEGACY_LOADED_IMAGE and
> QEMU_LEGACY_LOADED_IMAGE_REC),
>
> - and the related use of a Signature + CR().
>
> These tools help if we have a pre-defined -- e.g., standard -- protocol,
> for which we'd like to provide an implementation, and we need to carry
> some auxiliary ("private") data for every protocol instance that we
> produce. In that case, we embed the pre-defined protocol structure in
> our own, and use CR() / Signature etc. to arrive at our own container
> structure, from a pointer to the pre-defined protocol. This is all
> motivated by the fact that the member functions of the pre-defined
> protocol take a "This" pointer to said pre-defined protocol.
>
> But in our case here, this use case doesn't seem to apply. We do not
> embed a pre-defined protocol structure; we define a brand new inner
> structure, and we introduce a brand new protocol GUID for it. We don't
> have any pre-existent function prototype that can only take a pointer to
> QEMU_LEGACY_LOADED_IMAGE, from which we'd have to arrive at
> QEMU_LEGACY_LOADED_IMAGE_REC.
>

OK. I liked the extra check against the signature in debug mode, but
given that there should not be any other code using our GUID for
installing protocols, I guess it is redundant. I'll drop it.

> (2) So that makes me think we should simply drop the macro and the
> wrapper structure.
>

Ack

> > +STATIC
> > +VOID
> > +FreeLegacyImage (
> > +  IN  QEMU_LEGACY_LOADED_IMAGE *LoadedImage
> > +  )
> > +{
> > +  if (LoadedImage->SetupBuf != NULL) {
> > +    FreePages (LoadedImage->SetupBuf,
> > +      EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
> > +  }
> > +  if (LoadedImage->KernelBuf != NULL) {
> > +    FreePages (LoadedImage->KernelBuf,
> > +      EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));
> > +  }
> > +  if (LoadedImage->CommandLine != NULL) {
> > +    FreePages (LoadedImage->CommandLine,
> > +      EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));
> > +  }
> > +  if (LoadedImage->InitrdData != NULL) {
> > +    FreePages (LoadedImage->InitrdData,
> > +      EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
> > +  }
> > +}
> > +
> > +STATIC
> > +EFI_STATUS
> > +QemuLoadLegacyImage (
> > +  OUT EFI_HANDLE                  *ImageHandle
> > +  )
> > +{
> > +  EFI_STATUS                      Status;
> > +  UINTN                           KernelSize;
> > +  UINTN                           SetupSize;
> > +  QEMU_LEGACY_LOADED_IMAGE_REC    *LoadedImageRec;
> > +  QEMU_LEGACY_LOADED_IMAGE        *LoadedImage;
>
> (3) If I'm right to think that this patch incorporates non-trivial code
> from TryRunningQemuKernel()
> [OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c], then please (a)
> note that in the commit message, (b) please preserve the Intel copyright
> notice.
>

OK

> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
> > +  KernelSize = (UINTN)QemuFwCfgRead64 ();
>
> Sigh, now I understand where the "64" comes from -- it's a bug in the
> original code. QEMU exposes these values with fw_cfg_add_i32(); see
> load_image_to_fw_cfg() in "hw/arm/boot.c", and the direct calls in
> "hw/i386/x86.c".
>
> We used to get away with Read64 only because edk2 uses little endian,
> and fw_cfg is specified to produce zeroes when reading past the end of
> an item.
>
> (4) So we should use *32 for reading *all* these items, regardless of
> "OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c". (I'm not going to
> point out each one below.)
>

Sure

> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
> > +  SetupSize = (UINTN)QemuFwCfgRead64 ();
> > +
> > +  if (KernelSize == 0 || SetupSize == 0) {
> > +    DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
> > +    return EFI_NOT_FOUND;
> > +  }
> > +
> > +  LoadedImageRec = AllocateZeroPool (sizeof (*LoadedImageRec));
> > +  if (LoadedImageRec == NULL) {
> > +    return EFI_OUT_OF_RESOURCES;
> > +  }
> > +
> > +  LoadedImageRec->Signature = QEMU_LEGACY_LOADED_IMAGE_SIG;
> > +  LoadedImage = &LoadedImageRec->LoadedImage;
> > +
> > +  LoadedImage->SetupSize = SetupSize;
> > +  LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (
> > +                            EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
> > +  if (LoadedImage->SetupBuf == NULL) {
> > +    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));
> > +    return EFI_OUT_OF_RESOURCES;
>
> (5) This leaks "LoadedImageRec".
>
> > +  }
> > +
> > +  DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));
> > +  DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));
> > +  QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
> > +  QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);
> > +  DEBUG ((DEBUG_INFO, " [done]\n"));
> > +
> > +  Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,
> > +             LoadedImage->SetupSize);
> > +  if (EFI_ERROR (Status)) {
> > +    goto FreeAndReturn;
> > +  }
>
> OK, this seems to release everything correctly.
>
> > +
> > +  Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
> > +  if (EFI_ERROR (Status)) {
> > +    goto FreeAndReturn;
> > +  }
> > +
> > +  LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
> > +                                     LoadedImage->SetupBuf, KernelSize);
> > +  if (LoadedImage->KernelInitialSize == 0) {
> > +    Status = EFI_UNSUPPORTED;
> > +    goto FreeAndReturn;
> > +  }
> > +
> > +  LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
> > +                             LoadedImage->SetupBuf,
> > +                             EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
> > +                             );
> > +  if (LoadedImage->KernelBuf == NULL) {
> > +    DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
> > +    Status = EFI_OUT_OF_RESOURCES;
> > +    goto FreeAndReturn;
> > +  }
> > +
> > +  DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));
> > +  DEBUG ((DEBUG_INFO, "Reading kernel image ..."));
> > +  QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
> > +  QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);
> > +  DEBUG ((DEBUG_INFO, " [done]\n"));
> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> > +  LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead64 ();
> > +
> > +  if (LoadedImage->CommandLineSize > 0) {
> > +    LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
> > +                                 EFI_SIZE_TO_PAGES (
> > +                                   LoadedImage->CommandLineSize));
> > +    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> > +    QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
> > +  } else {
> > +    LoadedImage->CommandLine = NULL;
>
> (6) This assignment is superfluous. We allocate (*LoadedImageRec) with
> AllocateZeroPool(), which contains (*LoadedImage); and we rely on the
> zero-initialization in FreeLegacyImage().
>

Indeed.

> > +  }
> > +
> > +  Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
> > +             LoadedImage->CommandLine);
> > +  if (EFI_ERROR (Status)) {
> > +    goto FreeAndReturn;
> > +  }
> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> > +  LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead64 ();
> > +
> > +  if (LoadedImage->InitrdSize > 0) {
> > +    LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
> > +                                LoadedImage->SetupBuf,
> > +                                EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
> > +    DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",
> > +      (UINT32)LoadedImage->InitrdSize));
> > +    DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
> > +    QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
> > +    QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
> > +    DEBUG ((DEBUG_INFO, " [done]\n"));
> > +  } else {
> > +    LoadedImage->InitrdData = NULL;
>
> (7) Same as (6).
>
> > +  }
> > +
> > +  Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
> > +             LoadedImage->InitrdSize);
> > +  if (EFI_ERROR (Status)) {
> > +    goto FreeAndReturn;
> > +  }
> > +
> > +  Status = gBS->InstallProtocolInterface (ImageHandle,
> > +                  &gX86QemuKernelLoadedImageGuid, EFI_NATIVE_INTERFACE,
> > +                  LoadedImage);
>
> (8) This indicates that the identifier should actually be a
> *ProtocolGuid, with the usual consequences:
>
> - it should be put in [Protocols], not [Guids], in the INF file and the
> DEC file,
>
> - we should introduce the protocol related artifacts (the extern
> declaration for *ProtocolGuid, the definition of the initializer macro,
> and the typedef for the protocol structure) in a new header file under
> OvmfPkg/Include/Protocol.
>
> It's OK if the new protocol is not standard and subject to change at any
> time -- we only need to state that explicitly in the new header file.
>
> I understand that you might not want to expose so many internals in a
> protocol header, but, after all, we do introduce the GUID in the DEC
> file too.
>

Fair enough.

> > +  if (EFI_ERROR (Status)) {
> > +    goto FreeAndReturn;
> > +  }
> > +  return EFI_SUCCESS;
> > +
> > +FreeAndReturn:
> > +  FreeLegacyImage (LoadedImage);
> > +  FreePool (LoadedImageRec);
> > +  return Status;
> > +}
> > +
> > +STATIC
> > +EFI_STATUS
> > +QemuStartLegacyImage (
> > +  IN  EFI_HANDLE                ImageHandle
> > +  )
> > +{
> > +  EFI_STATUS                    Status;
> > +  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
> > +  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
> > +
> > +  Status = gBS->OpenProtocol (ImageHandle,
> > +                  &gX86QemuKernelLoadedImageGuid,
> > +                  (VOID **)&LoadedImage,
> > +                  gImageHandle,                  // AgentHandle
> > +                  NULL,                          // ControllerHandle
> > +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> > +                  );
> > +  if (EFI_ERROR (Status)) {
> > +    return EFI_INVALID_PARAMETER;
>
> (Or you could return "Status" too (up to you); OpenProtocol() returns
> EFI_UNSUPPORTED if the protocol is not on the handle.
>
> If you decide to update this, then please keep QemuUnloadLegacyImage()
> in sync.)
>
> > +  }
> > +
> > +  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
> > +
> > +  return LoadLinux (LoadedImageRec->LoadedImage.KernelBuf,
> > +                    LoadedImageRec->LoadedImage.SetupBuf);
> > +}
> > +
> > +STATIC
> > +EFI_STATUS
> > +QemuUnloadLegacyImage (
> > +  IN  EFI_HANDLE          ImageHandle
> > +  )
> > +{
> > +  EFI_STATUS                    Status;
> > +  QEMU_LEGACY_LOADED_IMAGE      *LoadedImage;
> > +  QEMU_LEGACY_LOADED_IMAGE_REC  *LoadedImageRec;
> > +
> > +  Status = gBS->OpenProtocol (ImageHandle,
> > +                  &gX86QemuKernelLoadedImageGuid,
> > +                  (VOID **)&LoadedImage,
> > +                  gImageHandle,                  // AgentHandle
> > +                  NULL,                          // ControllerHandle
> > +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> > +                  );
> > +  if (EFI_ERROR (Status)) {
> > +    return EFI_INVALID_PARAMETER;
> > +  }
> > +
> > +  LoadedImageRec = QEMU_LEGACY_LOADED_IMAGE_REC_FROM_LOADED_IMAGE (LoadedImage);
> > +
> > +  FreeLegacyImage (&LoadedImageRec->LoadedImage);
> > +  FreePool (LoadedImageRec);
> > +  return EFI_SUCCESS;
> > +}
>
> (9) Please uninstall the protocol interface, before freeing it.
>

Ah yes, good point.

> > +
> > +/**
> > +  Download the kernel, the initial ramdisk, and the kernel command line from
> > +  QEMU's fw_cfg. The kernel will be instructed via its command line to load
> > +  the initrd from the same Simple FileSystem.
> > +
> > +  @param[out] ImageHandle       The image handle that was allocated for
> > +                                loading the image
> > +  @param[out] LoadedImage       The loaded image protocol that was installed
> > +                                on ImageHandle by the LoadImage boot service.
> > +
> > +  @retval EFI_SUCCESS           The image was loaded successfully.
> > +  @retval EFI_NOT_FOUND         Kernel image was not found.
> > +  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
> > +  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
> > +
> > +  @return                       Error codes from any of the underlying
> > +                                functions.
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +QemuLoadKernelImage (
> > +  OUT EFI_HANDLE            *ImageHandle
> > +  )
> > +{
> > +  EFI_STATUS                Status;
> > +  EFI_HANDLE                KernelImageHandle;
> > +  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
> > +  UINTN                     CommandLineSize;
> > +  CHAR8                     *CommandLine;
> > +  UINTN                     InitrdSize;
> > +
> > +  //
> > +  // Load the image. This should call back into the QEMU EFI loader file system.
> > +  //
> > +  Status = gBS->LoadImage (
> > +                  FALSE,                    // BootPolicy: exact match required
> > +                  gImageHandle,             // ParentImageHandle
> > +                  (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
> > +                  NULL,                     // SourceBuffer
> > +                  0,                        // SourceSize
> > +                  &KernelImageHandle
> > +                  );
> > +  switch (Status) {
> > +  case EFI_SUCCESS:
> > +    break;
> > +
> > +  case EFI_NOT_FOUND:
> > +    //
> > +    // The image does not exist - no -kernel image was supplied via the
> > +    // command line so no point in invoking the legacy fallback
> > +    //
> > +    return EFI_NOT_FOUND;
> > +
> > +  case EFI_SECURITY_VIOLATION:
> > +    //
> > +    // We are running with UEFI secure boot enabled, and the image failed to
> > +    // authenticate. For compatibility reasons, we fall back to the legacy
> > +    // loader in this case. Since the image has been loaded, we need to unload
> > +    // it before proceeding
> > +    //
> > +    gBS->UnloadImage (KernelImageHandle);
> > +    //
> > +    // Fall through
> > +    //
>
> Yes, good point; this actually supports my request (4a) under patch#4 --
> EFI_SECURITY_VIOLATION should be handled internally to the library.
>
> > +  case EFI_UNSUPPORTED:
> > +    //
> > +    // The image is not natively supported or cross-type supported. Let's try
> > +    // loading it using the loader that parses the bzImage metadata directly.
> > +    //
> > +    KernelImageHandle = NULL;
> > +    Status = QemuLoadLegacyImage (&KernelImageHandle);
> > +    if (EFI_ERROR (Status)) {
> > +      DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
> > +        Status));
> > +      return Status;
> > +    }
> > +    *ImageHandle = KernelImageHandle;
> > +    return EFI_SUCCESS;
> > +
> > +  default:
> > +    DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
> > +    return Status;
> > +  }
> > +
> > +  //
> > +  // Construct the kernel command line.
> > +  //
> > +  Status = gBS->OpenProtocol (
> > +                  KernelImageHandle,
> > +                  &gEfiLoadedImageProtocolGuid,
> > +                  (VOID **)&KernelLoadedImage,
> > +                  gImageHandle,                  // AgentHandle
> > +                  NULL,                          // ControllerHandle
> > +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> > +                  );
> > +  ASSERT_EFI_ERROR (Status);
> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
> > +  CommandLineSize = (UINTN) QemuFwCfgRead64 ();
> > +
> > +  if (CommandLineSize == 0) {
> > +    KernelLoadedImage->LoadOptionsSize = 0;
> > +  } else {
> > +    CommandLine = AllocatePool (CommandLineSize);
> > +    ASSERT (CommandLine != NULL);
> > +
> > +    QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
> > +    QemuFwCfgReadBytes (CommandLineSize, CommandLine);
> > +
> > +    //
> > +    // Verify NUL-termination of the command line.
> > +    //
> > +    if (CommandLine[CommandLineSize - 1] != '\0') {
> > +      DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
> > +        __FUNCTION__));
> > +      Status = EFI_PROTOCOL_ERROR;
> > +      goto FreeCommandLine;
> > +    }
> > +
> > +    //
> > +    // Drop the terminating NUL, convert to UTF-16.
> > +    //
> > +    KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2;
> > +  }
> > +
> > +  QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
> > +  InitrdSize = (UINTN) QemuFwCfgRead64 ();
> > +
> > +  if (InitrdSize > 0) {
> > +    //
> > +    // Append ' initrd=initrd' in UTF-16.
> > +    //
> > +    KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
> > +  }
> > +
> > +  if (KernelLoadedImage->LoadOptionsSize == 0) {
> > +    KernelLoadedImage->LoadOptions = NULL;
> > +  } else {
> > +    //
> > +    // NUL-terminate in UTF-16.
> > +    //
> > +    KernelLoadedImage->LoadOptionsSize += 2;
> > +
> > +    KernelLoadedImage->LoadOptions = AllocatePool (
> > +                                       KernelLoadedImage->LoadOptionsSize);
> > +    if (KernelLoadedImage->LoadOptions == NULL) {
> > +      KernelLoadedImage->LoadOptionsSize = 0;
> > +      Status = EFI_OUT_OF_RESOURCES;
> > +      goto FreeCommandLine;
> > +    }
> > +
> > +    UnicodeSPrintAsciiFormat (
> > +      KernelLoadedImage->LoadOptions,
> > +      KernelLoadedImage->LoadOptionsSize,
> > +      "%a%a",
> > +      (CommandLineSize == 0) ?  "" : CommandLine,
> > +      (InitrdSize == 0)      ?  "" : " initrd=initrd"
> > +      );
> > +    DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
> > +      (CHAR16 *)KernelLoadedImage->LoadOptions));
> > +  }
> > +
> > +  *ImageHandle = KernelImageHandle;
> > +  return EFI_SUCCESS;
> > +
> > +FreeCommandLine:
> > +  FreePool (CommandLine);
> > +  gBS->UnloadImage (KernelImageHandle);
> > +
> > +  return Status;
> > +}
> > +
> > +/**
> > +  Transfer control to a kernel image loaded with QemuLoadKernelImage ()
> > +
> > +  @param[in]  ImageHandle         Handle of image to be started.
> > +
> > +  @retval EFI_INVALID_PARAMETER   ImageHandle is either an invalid image handle
> > +                                  or the image has already been initialized with
> > +                                  StartImage
> > +  @retval EFI_SECURITY_VIOLATION  The current platform policy specifies that the
> > +                                  image should not be started.
> > +
> > +  @return                         Error codes returned by the started image
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +QemuStartKernelImage (
> > +  IN  EFI_HANDLE          ImageHandle
> > +  )
> > +{
> > +  EFI_STATUS                Status;
> > +
> > +  Status = gBS->StartImage (
> > +                  ImageHandle,
> > +                  NULL,              // ExitDataSize
> > +                  NULL               // ExitData
> > +                  );
>
> (10) I'd like to avoid calling StartImage on a handle we know has not
> been created with gBS->LoadImage(). Can you first check for the custom
> protocol on the handle, and base QemuStartLegacyImage() on that (rather
> than the EFI_INVALID_PARAMETER return value)?
>

OK

>
> > +
> > +  switch (Status) {
> > +#ifdef MDE_CPU_IA32
> > +  case EFI_UNSUPPORTED:
> > +    //
> > +    // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
> > +    // we are expecting a IA32 one, and the StartImage () boot service is unable
> > +    // to handle it, either because the image does not have the special .compat
> > +    // PE/COFF section that Linux specifies for mixed mode capable images, or
>
> (11) So does this series depend on your other patch
>
>   [edk2-devel] [PATCH v3 6/6]
>   OvmfPkg IA32: add support for loading X64 images
>
> (which is for <https://bugzilla.tianocore.org/show_bug.cgi?id=2564>)?
>
> The topic branch that you reference in the cover letter of this series
> seems to be based on those patches.
>
> Should we make <https://bugzilla.tianocore.org/show_bug.cgi?id=2566> --
> i.e. the BZ that tracks the present feature -- dependent on TianoCore#2564?
>
> > +    // because we are running without the support code for that. So do a legacy
> > +    // load instead, but do it first so we can reuse the same handle. Then,
> > +    // unload the normally loaded image.
> > +    //
> > +    Status = QemuLoadLegacyImage (&ImageHandle);
>
> Hmmm, I'm getting quite confused here. This seems to indicate that:
>
> - even though we do recognize *some* cases when QemuLoadLegacyImage()
> needs to be called from QemuLoadKernelImage() -- which makes sense to me --,
>
> - there are *other* cases needing QemuLoadLegacyImage() that we can only
> recognize here, in QemuStartKernelImage(), *after* the normal
> LoadImage() branch in QemuLoadKernelImage() succeeded.
>
> Is that right?
>

Yes. IA32 builds will permit the loading of X64 images, but will not
allow them to be started. So we need to fallback to the legacy
*Load*Image, in the implementation of QemuStartImage

> If so, then I guess the idea here is to first add another protocol on
> the existent ImageHandle (our own custom one), before allowing
> gBS->UnloadImage() in QemuUnloadKernelImage() to remove the protocol
> interfaces produced by gBS->LoadImage(), and thereby destroy the handle.
>
> I have two concerns:
>
> (12) I'd rather like this part of the code to know for sure that our
> custom protocol is not present yet on the image handle -- see (10) above,
>

OK.

> (13) I'm worried that we are installing custom protocols on a handle
> that was first created by LoadImage(), before we pass it to
> UnloadImage(). I don't know if, per spec, LoadImage() / UnloadImage()
> are allowed to associate such information with the specific image handle
> that is *beyond* the protocol interfaces visible on the handle.
>

I'm not sure I follow. Why would it not be allowed to install
additional protocols on that handle? Note that it is rather common for
drivers to install arbitrary protocols on the handle that they receive
via the PE/COFF entry point.


> Can we modify the logic somehow so that we don't have to silently call
> QemuLoadLegacyImage() inside QemuStartKernelImage()? If not, we should
> at least document that this is arguably a grey area per UEFI spec.
>

That would require QemuStartImage() to take a EFI_HANDLE* rather than
a EFI_HANDLE, which is what I was trying to avoid here.

>
> > +    if (EFI_ERROR (Status)) {
> > +      return Status;
> > +    }
> > +    QemuUnloadKernelImage (ImageHandle);
> > +    //
> > +    // Fall through
> > +    //
> > +#endif
> > +  case EFI_INVALID_PARAMETER:
> > +    return QemuStartLegacyImage (ImageHandle);
> > +  default:
> > +    break;
> > +  }
> > +  return Status;
> > +}
> > +
> > +/**
> > +  Unloads an image loaded with QemuLoadKernelImage ().
> > +
> > +  @param  ImageHandle             Handle that identifies the image to be
> > +                                  unloaded.
> > +
> > +  @retval EFI_SUCCESS             The image has been unloaded.
> > +  @retval EFI_UNSUPPORTED         The image has been started, and does not
> > +                                  support unload.
> > +  @retval EFI_INVALID_PARAMETER   ImageHandle is not a valid image handle.
> > +
> > +**/
> > +EFI_STATUS
> > +EFIAPI
> > +QemuUnloadKernelImage (
> > +  IN  EFI_HANDLE          ImageHandle
> > +  )
> > +{
> > +  EFI_LOADED_IMAGE_PROTOCOL   *KernelLoadedImage;
> > +  EFI_STATUS                  Status;
> > +
> > +  Status = gBS->OpenProtocol (
> > +                  ImageHandle,
> > +                  &gEfiLoadedImageProtocolGuid,
> > +                  (VOID **)&KernelLoadedImage,
> > +                  gImageHandle,                  // AgentHandle
> > +                  NULL,                          // ControllerHandle
> > +                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
> > +                  );
> > +  if (Status == EFI_UNSUPPORTED) {
> > +    return QemuUnloadLegacyImage (ImageHandle);
> > +  } else if (EFI_ERROR (Status)) {
>
> (14) Please don't follow a "return" statement with an "else"; just
> unnest the second branch.
>

Sure

> > +    return EFI_INVALID_PARAMETER;
> > +  }
> > +
> > +  if (KernelLoadedImage->LoadOptions != NULL) {
> > +    FreePool (KernelLoadedImage->LoadOptions);
> > +    KernelLoadedImage->LoadOptions = NULL;
> > +  }
> > +  KernelLoadedImage->LoadOptionsSize = 0;
> > +
> > +  return gBS->UnloadImage (ImageHandle);
> > +}
>
> This function has to cope with three situations:
>
> (i) The image is a normal UEFI executable that was loaded by the
> LoadImage() branch of QemuLoadKernelImage(). In this case,
> OpenProtocol() is supposed to succeed, and so is gBS->UnloadImage(). OK.
>
> (ii) The image was legacy-loaded by QemuLoadKernelImage() -->
> QemuLoadLegacyImage(). Meaning, gBS->LoadImage() failed there, or it
> returned EFI_SECURITY_VIOLATION, and we unloaded the kernel image right
> there, before performing the legacy load. In either case, OpenProtocol()
> here will fail with EFI_UNSUPPORTED, and we defer to
> QemuUnloadLegacyImage(). OK.
>
> (iii) The third case is when *both* protocol sets are available on the
> handle, and we're being called from QemuStartKernelImage(). In this case
> we're in the process of "replacing" EFI_LOADED_IMAGE_PROTOCOL (and
> EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL) with our custom protocol. The
> function takes the same path as (i) in this case, I think, and that
> seems right. OK.
>
> (15) This function needs to document (in comments) all three cases
> above, please.
>

OK

>
> > diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> > new file mode 100644
> > index 000000000000..4208f5da3b31
> > --- /dev/null
> > +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> > @@ -0,0 +1,42 @@
> > +## @file
> > +#  X86 specific implementation of QemuLoadImageLib library class interface
> > +#  with support for loading mixed mode images and non-EFI stub images
> > +#
> > +#  Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
> > +#
> > +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> > +#
> > +##
> > +
> > +[Defines]
> > +  INF_VERSION                    = 1.27
> > +  BASE_NAME                      = X86QemuLoadImageLib
> > +  FILE_GUID                      = 2304df80-e21d-4170-9c3c-113c878f7ac0
> > +  MODULE_TYPE                    = BASE
> > +  VERSION_STRING                 = 1.0
> > +  LIBRARY_CLASS                  = QemuLoadImageLib|DXE_DRIVER
> > +
> > +[Sources]
> > +  X86QemuLoadImageLib.c
> > +
> > +[Packages]
> > +  MdeModulePkg/MdeModulePkg.dec
> > +  MdePkg/MdePkg.dec
> > +  OvmfPkg/OvmfPkg.dec
> > +
> > +[LibraryClasses]
> > +  DebugLib
> > +  MemoryAllocationLib
> > +  LoadLinuxLib
> > +  PrintLib
> > +  QemuFwCfgLib
> > +  ReportStatusCodeLib
> > +  UefiBootServicesTableLib
> > +
> > +[Protocols]
> > +  gEfiDevicePathProtocolGuid
> > +  gEfiLoadedImageProtocolGuid
> > +
> > +[Guids]
> > +  gQemuKernelLoaderFsMediaGuid
> > +  gX86QemuKernelLoadedImageGuid
> > diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
> > index 26f977bad795..e66af38f4290 100644
> > --- a/OvmfPkg/OvmfPkg.dec
> > +++ b/OvmfPkg/OvmfPkg.dec
> > @@ -93,6 +93,7 @@ [Guids]
> >    gEfiLegacyDevOrderVariableGuid      = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}}
> >    gLinuxEfiInitrdMediaGuid            = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}}
> >    gQemuKernelLoaderFsMediaGuid        = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}}
> > +  gX86QemuKernelLoadedImageGuid       = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}}
> >
> >  [Protocols]
> >    gVirtioDeviceProtocolGuid           = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}}
> >
>
> Thanks
> Laszlo
>

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path
  2020-03-02  7:29 ` [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Ard Biesheuvel
@ 2020-03-03 10:10   ` Laszlo Ersek
  2020-03-03 10:18     ` Ard Biesheuvel
  0 siblings, 1 reply; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03 10:10 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> Linux v5.7 will introduce a new method to load the initial ramdisk
> (initrd) from the loader, using the LoadFile2 protocol installed on a
> special vendor GUIDed media device path.
> 
> Add support for this to our QEMU command line kernel/initrd loader.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 79 ++++++++++++++++++++
>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  2 +
>  2 files changed, 81 insertions(+)
> 
> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> index 77d8fedb738a..415946edf22a 100644
> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> @@ -13,15 +13,18 @@
>  #include <Guid/FileInfo.h>
>  #include <Guid/FileSystemInfo.h>
>  #include <Guid/FileSystemVolumeLabelInfo.h>
> +#include <Guid/LinuxEfiInitrdMedia.h>
>  #include <Guid/QemuKernelLoaderFsMedia.h>
>  #include <Library/BaseLib.h>
>  #include <Library/BaseMemoryLib.h>
>  #include <Library/DebugLib.h>
> +#include <Library/DevicePathLib.h>
>  #include <Library/MemoryAllocationLib.h>
>  #include <Library/QemuFwCfgLib.h>
>  #include <Library/UefiBootServicesTableLib.h>
>  #include <Library/UefiRuntimeServicesTableLib.h>
>  #include <Protocol/DevicePath.h>
> +#include <Protocol/LoadFile2.h>
>  #include <Protocol/SimpleFileSystem.h>
>  
>  //
> @@ -84,6 +87,19 @@ STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = {
>    }
>  };
>  
> +STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = {
> +  {
> +    {
> +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
> +      { sizeof (VENDOR_DEVICE_PATH) }
> +    },
> +    LINUX_EFI_INITRD_MEDIA_GUID
> +  }, {
> +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> +  }
> +};
> +
>  //
>  // The "file in the EFI stub filesystem" abstraction.
>  //
> @@ -839,6 +855,48 @@ STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
>    StubFileSystemOpenVolume
>  };
>  
> +STATIC
> +EFI_STATUS
> +EFIAPI
> +InitrdLoadFile2 (
> +  IN EFI_LOAD_FILE2_PROTOCOL          *This,
> +  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
> +  IN BOOLEAN                          BootPolicy,
> +  IN OUT UINTN                        *BufferSize,
> +  IN VOID                             *Buffer OPTIONAL
> +  )
> +{
> +  CONST KERNEL_BLOB   *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];
> +
> +  ASSERT (InitrdBlob->Size > 0);
> +
> +  if (BootPolicy) {
> +    return EFI_UNSUPPORTED;
> +  }
> +
> +  if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
> +    return EFI_INVALID_PARAMETER;
> +  }
> +
> +  if (FilePath->Type != END_DEVICE_PATH_TYPE ||
> +      FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) {
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  if (Buffer == NULL || *BufferSize < InitrdBlob->Size) {
> +    *BufferSize = InitrdBlob->Size;
> +    return EFI_BUFFER_TOO_SMALL;
> +  }
> +
> +  CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size);
> +
> +  *BufferSize = InitrdBlob->Size;
> +  return EFI_SUCCESS;
> +}
> +
> +STATIC CONST EFI_LOAD_FILE2_PROTOCOL     mInitrdLoadFile2 = {
> +  InitrdLoadFile2,
> +};
>  
>  //
>  // Utility functions.
> @@ -945,6 +1003,7 @@ QemuKernelLoaderFsDxeEntrypoint (
>    KERNEL_BLOB               *KernelBlob;
>    EFI_STATUS                Status;
>    EFI_HANDLE                FileSystemHandle;
> +  EFI_HANDLE                InitrdLoadFile2Handle;
>  
>    if (!QemuFwCfgIsAvailable ()) {
>      return EFI_NOT_FOUND;
> @@ -989,8 +1048,28 @@ QemuKernelLoaderFsDxeEntrypoint (
>      goto FreeBlobs;
>    }
>  
> +  if (KernelBlob[KernelBlobTypeInitrd].Size > 0) {
> +    InitrdLoadFile2Handle = NULL;
> +    Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle,
> +                    &gEfiDevicePathProtocolGuid,  &mInitrdDevicePath,
> +                    &gEfiLoadFile2ProtocolGuid,   &mInitrdLoadFile2,
> +                    NULL);
> +    if (EFI_ERROR (Status)) {
> +      DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
> +        __FUNCTION__, Status));
> +      goto UninstallFileSystemHandle;
> +    }
> +  }
> +
>    return EFI_SUCCESS;
>  
> +UninstallFileSystemHandle:
> +  Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
> +                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
> +                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
> +                  NULL);
> +  ASSERT_EFI_ERROR (Status);
> +
>  FreeBlobs:
>    while (BlobType > 0) {
>      CurrentBlob = &mKernelBlob[--BlobType];
> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> index f4b50c265027..737f9b87a7c7 100644
> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> @@ -28,6 +28,7 @@ [LibraryClasses]
>    BaseLib
>    BaseMemoryLib
>    DebugLib
> +  DevicePathLib
>    MemoryAllocationLib
>    UefiBootServicesTableLib
>    QemuFwCfgLib
> @@ -42,6 +43,7 @@ [Guids]
>  
>  [Protocols]
>    gEfiDevicePathProtocolGuid                ## PRODUCES
> +  gEfiLoadFile2ProtocolGuid                 ## PRODUCES
>    gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
>  
>  [Depex]
> 

I think this patch conflicts with your other patch:

  [edk2-devel] [PATCH v3 2/6] OvmfPkg: add 'initrd' shell command to
                              expose Linux initrd via device path

There should not be two different handles in the handle database that
carry the same device path (I mean "same device path" by contents, not
by reference).

I believe that it's possible that the command-line specified -kernel /
-initrd are attempted first, but the kernel's stub returns for some
reason. Then the user can still go to the UEFI shell, and use the new
dynamic shell command. In that case I think the device path will cease
being unique (by contents).

I think you can solve this as follows:

- in both modules (this driver, and the dynamic shell command driver),
install a NULL protocol interface with GUID = gEfiCallerIdGuid on the
same handle that carries the device path and LoadFile2

- in both modules, before you touch the handle that carries the device
path, try to locate it first. If it exists but doesn't carry the subject
module's FILE_GUID as a NULL protocol interface, we know the device path
was created by the "other" module, and we can bail.

Just an idea.

Thanks,
Laszlo

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds
  2020-03-02  7:29 ` [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds Ard Biesheuvel
@ 2020-03-03 10:13   ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03 10:13 UTC (permalink / raw)
  To: devel, ard.biesheuvel

On 03/02/20 08:29, Ard Biesheuvel wrote:
> The QemuLoadImageLib implementation we currently use for all OVMF
> builds copies the behavior of the QEMU loader code that precedes it,
> which is to disregard UEFI secure boot policies entirely when it comes
> to loading kernel images that have been specified on the QEMU command
> line. This behavior deviates from ArmVirtQemu based builds, which do
> take UEFI secure boot policies into account, and refuse to load images
> from the command line that cannot be authenticated.
> 
> The disparity was originally due to the fact that the QEMU command line
> kernel loader did not use LoadImage and StartImage at all, but this
> changed recently, and now, there are only a couple of reasons left to
> stick with the legacy loader:
> - it permits loading images that lack a valid PE/COFF header,
> - it permits loading X64 kernels on IA32 firmware running on a X64
>   capable system.
> 
> Since every non-authentic PE/COFF image can trivially be converted into
> an image that lacks a valid PE/COFF header, the former case can simply
> not be supported in a UEFI secure boot context. The latter case is highly
> theoretical, given that one could easily switch to native X64 firmware in
> a VM scenario.
> 
> That leaves us with little justification to use the legacy loader at all
> when UEFI secure boot policies are in effect, so let's switch to the
> generic loader for UEFI secure boot enabled builds.
> 
> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  OvmfPkg/OvmfPkgIa32.dsc    | 4 ++++
>  OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++
>  OvmfPkg/OvmfPkgX64.dsc     | 4 ++++
>  3 files changed, 12 insertions(+)
> 
> diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
> index 2cc924a6986a..eceddb71948f 100644
> --- a/OvmfPkg/OvmfPkgIa32.dsc
> +++ b/OvmfPkg/OvmfPkgIa32.dsc
> @@ -361,7 +361,11 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +!if $(SECURE_BOOT_ENABLE) == TRUE
> +  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
> +!else
>    QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> +!endif
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
> index 21d1f156973b..8bdf2e692b00 100644
> --- a/OvmfPkg/OvmfPkgIa32X64.dsc
> +++ b/OvmfPkg/OvmfPkgIa32X64.dsc
> @@ -365,7 +365,11 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +!if $(SECURE_BOOT_ENABLE) == TRUE
> +  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
> +!else
>    QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> +!endif
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
> index f3d0f18db7e2..bc0a3e438d2a 100644
> --- a/OvmfPkg/OvmfPkgX64.dsc
> +++ b/OvmfPkg/OvmfPkgX64.dsc
> @@ -365,7 +365,11 @@ [LibraryClasses.common.DXE_DRIVER]
>    PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf
>    MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf
>    QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf
> +!if $(SECURE_BOOT_ENABLE) == TRUE
> +  QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf
> +!else
>    QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf
> +!endif
>  !if $(TPM2_ENABLE) == TRUE
>    Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf
>  !endif
> 

Reviewed-by: Laszlo Ersek <lersek@redhat.com>

Thank you!
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path
  2020-03-03 10:10   ` [edk2-devel] " Laszlo Ersek
@ 2020-03-03 10:18     ` Ard Biesheuvel
  2020-03-03 11:27       ` Laszlo Ersek
  0 siblings, 1 reply; 33+ messages in thread
From: Ard Biesheuvel @ 2020-03-03 10:18 UTC (permalink / raw)
  To: Laszlo Ersek; +Cc: edk2-devel-groups-io

On Tue, 3 Mar 2020 at 11:10, Laszlo Ersek <lersek@redhat.com> wrote:
>
> On 03/02/20 08:29, Ard Biesheuvel wrote:
> > Linux v5.7 will introduce a new method to load the initial ramdisk
> > (initrd) from the loader, using the LoadFile2 protocol installed on a
> > special vendor GUIDed media device path.
> >
> > Add support for this to our QEMU command line kernel/initrd loader.
> >
> > Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
> > Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> > ---
> >  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 79 ++++++++++++++++++++
> >  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  2 +
> >  2 files changed, 81 insertions(+)
> >
> > diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> > index 77d8fedb738a..415946edf22a 100644
> > --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> > +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
> > @@ -13,15 +13,18 @@
> >  #include <Guid/FileInfo.h>
> >  #include <Guid/FileSystemInfo.h>
> >  #include <Guid/FileSystemVolumeLabelInfo.h>
> > +#include <Guid/LinuxEfiInitrdMedia.h>
> >  #include <Guid/QemuKernelLoaderFsMedia.h>
> >  #include <Library/BaseLib.h>
> >  #include <Library/BaseMemoryLib.h>
> >  #include <Library/DebugLib.h>
> > +#include <Library/DevicePathLib.h>
> >  #include <Library/MemoryAllocationLib.h>
> >  #include <Library/QemuFwCfgLib.h>
> >  #include <Library/UefiBootServicesTableLib.h>
> >  #include <Library/UefiRuntimeServicesTableLib.h>
> >  #include <Protocol/DevicePath.h>
> > +#include <Protocol/LoadFile2.h>
> >  #include <Protocol/SimpleFileSystem.h>
> >
> >  //
> > @@ -84,6 +87,19 @@ STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = {
> >    }
> >  };
> >
> > +STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = {
> > +  {
> > +    {
> > +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
> > +      { sizeof (VENDOR_DEVICE_PATH) }
> > +    },
> > +    LINUX_EFI_INITRD_MEDIA_GUID
> > +  }, {
> > +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
> > +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
> > +  }
> > +};
> > +
> >  //
> >  // The "file in the EFI stub filesystem" abstraction.
> >  //
> > @@ -839,6 +855,48 @@ STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
> >    StubFileSystemOpenVolume
> >  };
> >
> > +STATIC
> > +EFI_STATUS
> > +EFIAPI
> > +InitrdLoadFile2 (
> > +  IN EFI_LOAD_FILE2_PROTOCOL          *This,
> > +  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
> > +  IN BOOLEAN                          BootPolicy,
> > +  IN OUT UINTN                        *BufferSize,
> > +  IN VOID                             *Buffer OPTIONAL
> > +  )
> > +{
> > +  CONST KERNEL_BLOB   *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];
> > +
> > +  ASSERT (InitrdBlob->Size > 0);
> > +
> > +  if (BootPolicy) {
> > +    return EFI_UNSUPPORTED;
> > +  }
> > +
> > +  if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
> > +    return EFI_INVALID_PARAMETER;
> > +  }
> > +
> > +  if (FilePath->Type != END_DEVICE_PATH_TYPE ||
> > +      FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) {
> > +    return EFI_NOT_FOUND;
> > +  }
> > +
> > +  if (Buffer == NULL || *BufferSize < InitrdBlob->Size) {
> > +    *BufferSize = InitrdBlob->Size;
> > +    return EFI_BUFFER_TOO_SMALL;
> > +  }
> > +
> > +  CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size);
> > +
> > +  *BufferSize = InitrdBlob->Size;
> > +  return EFI_SUCCESS;
> > +}
> > +
> > +STATIC CONST EFI_LOAD_FILE2_PROTOCOL     mInitrdLoadFile2 = {
> > +  InitrdLoadFile2,
> > +};
> >
> >  //
> >  // Utility functions.
> > @@ -945,6 +1003,7 @@ QemuKernelLoaderFsDxeEntrypoint (
> >    KERNEL_BLOB               *KernelBlob;
> >    EFI_STATUS                Status;
> >    EFI_HANDLE                FileSystemHandle;
> > +  EFI_HANDLE                InitrdLoadFile2Handle;
> >
> >    if (!QemuFwCfgIsAvailable ()) {
> >      return EFI_NOT_FOUND;
> > @@ -989,8 +1048,28 @@ QemuKernelLoaderFsDxeEntrypoint (
> >      goto FreeBlobs;
> >    }
> >
> > +  if (KernelBlob[KernelBlobTypeInitrd].Size > 0) {
> > +    InitrdLoadFile2Handle = NULL;
> > +    Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle,
> > +                    &gEfiDevicePathProtocolGuid,  &mInitrdDevicePath,
> > +                    &gEfiLoadFile2ProtocolGuid,   &mInitrdLoadFile2,
> > +                    NULL);
> > +    if (EFI_ERROR (Status)) {
> > +      DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
> > +        __FUNCTION__, Status));
> > +      goto UninstallFileSystemHandle;
> > +    }
> > +  }
> > +
> >    return EFI_SUCCESS;
> >
> > +UninstallFileSystemHandle:
> > +  Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
> > +                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
> > +                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
> > +                  NULL);
> > +  ASSERT_EFI_ERROR (Status);
> > +
> >  FreeBlobs:
> >    while (BlobType > 0) {
> >      CurrentBlob = &mKernelBlob[--BlobType];
> > diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> > index f4b50c265027..737f9b87a7c7 100644
> > --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> > +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
> > @@ -28,6 +28,7 @@ [LibraryClasses]
> >    BaseLib
> >    BaseMemoryLib
> >    DebugLib
> > +  DevicePathLib
> >    MemoryAllocationLib
> >    UefiBootServicesTableLib
> >    QemuFwCfgLib
> > @@ -42,6 +43,7 @@ [Guids]
> >
> >  [Protocols]
> >    gEfiDevicePathProtocolGuid                ## PRODUCES
> > +  gEfiLoadFile2ProtocolGuid                 ## PRODUCES
> >    gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
> >
> >  [Depex]
> >
>
> I think this patch conflicts with your other patch:
>
>   [edk2-devel] [PATCH v3 2/6] OvmfPkg: add 'initrd' shell command to
>                               expose Linux initrd via device path
>
> There should not be two different handles in the handle database that
> carry the same device path (I mean "same device path" by contents, not
> by reference).
>
> I believe that it's possible that the command-line specified -kernel /
> -initrd are attempted first, but the kernel's stub returns for some
> reason. Then the user can still go to the UEFI shell, and use the new
> dynamic shell command. In that case I think the device path will cease
> being unique (by contents).
>
> I think you can solve this as follows:
>
> - in both modules (this driver, and the dynamic shell command driver),
> install a NULL protocol interface with GUID = gEfiCallerIdGuid on the
> same handle that carries the device path and LoadFile2
>
> - in both modules, before you touch the handle that carries the device
> path, try to locate it first. If it exists but doesn't carry the subject
> module's FILE_GUID as a NULL protocol interface, we know the device path
> was created by the "other" module, and we can bail.
>
> Just an idea.
>

Thanks.

I did wonder about the rules regarding duplicates in the device path
space. If the DXE core doesn't catch that, this seems like a gross
oversight to me.

But it raises an interesting point: the idea is that any boot stage
could elect to provide this interface, not just UEFI or the shell
itself, but also shim, grub or whatever executes in between. That
means that, in general, it should probably be the responsibility of
the code that installs the protocol to ensure that it doesn't exist
already.

For this code, it means it could simply install it, since it
guarantees to execute first.

For the initrd implementation, it means we should check whether any
other implementation exists already, which could simply be done [in
that particular case] by locating the protocol and checking the
address against the statically allocated one in the driver.

I am aware that both are DXE drivers, but since the dynamic shell
command is not invoked by any driver that is invoked before EndOfDxe,
I think the above should be sufficient.

^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback
  2020-03-03 10:08     ` Ard Biesheuvel
@ 2020-03-03 11:20       ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03 11:20 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: edk2-devel-groups-io

On 03/03/20 11:08, Ard Biesheuvel wrote:
> On Tue, 3 Mar 2020 at 10:45, Laszlo Ersek <lersek@redhat.com> wrote:

>> (13) I'm worried that we are installing custom protocols on a handle
>> that was first created by LoadImage(), before we pass it to
>> UnloadImage(). I don't know if, per spec, LoadImage() / UnloadImage()
>> are allowed to associate such information with the specific image handle
>> that is *beyond* the protocol interfaces visible on the handle.
>>
> 
> I'm not sure I follow. Why would it not be allowed to install
> additional protocols on that handle? Note that it is rather common for
> drivers to install arbitrary protocols on the handle that they receive
> via the PE/COFF entry point.

Yes, that's correct; however what doesn't happen is that protocols
installed on the image handle *survive* unloading the image with
gBS->UnloadImage().

Put differently, if the image handle was created by LoadImage() -- by
virtue of LoadImage() installing the first protocol interface --, then I
*think* there might be a silent expectation that UnloadImage() will also
uninstall the last protocol interface, thereby releasing the image
handle itself.

>> Can we modify the logic somehow so that we don't have to silently call
>> QemuLoadLegacyImage() inside QemuStartKernelImage()? If not, we should
>> at least document that this is arguably a grey area per UEFI spec.
>>
> 
> That would require QemuStartImage() to take a EFI_HANDLE* rather than
> a EFI_HANDLE,

Yes, I agree.

> which is what I was trying to avoid here.

I perceive this as a lifecycle risk / grey area in the spec.

If we can make it disappear by having QemuStartImage() take an "IN OUT
EFI_HANDLE*", IMO that's a significant improvement, and hopefully not
much additional complexity. I think the underlying use case is intricate
enough for us to let it affect the abstract QemuStartKernelImage()
interface.

Thanks!
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

* Re: [edk2-devel] [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path
  2020-03-03 10:18     ` Ard Biesheuvel
@ 2020-03-03 11:27       ` Laszlo Ersek
  0 siblings, 0 replies; 33+ messages in thread
From: Laszlo Ersek @ 2020-03-03 11:27 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: edk2-devel-groups-io

On 03/03/20 11:18, Ard Biesheuvel wrote:
> On Tue, 3 Mar 2020 at 11:10, Laszlo Ersek <lersek@redhat.com> wrote:
>>
>> On 03/02/20 08:29, Ard Biesheuvel wrote:
>>> Linux v5.7 will introduce a new method to load the initial ramdisk
>>> (initrd) from the loader, using the LoadFile2 protocol installed on a
>>> special vendor GUIDed media device path.
>>>
>>> Add support for this to our QEMU command line kernel/initrd loader.
>>>
>>> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566
>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
>>> ---
>>>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c   | 79 ++++++++++++++++++++
>>>  OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf |  2 +
>>>  2 files changed, 81 insertions(+)
>>>
>>> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
>>> index 77d8fedb738a..415946edf22a 100644
>>> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
>>> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
>>> @@ -13,15 +13,18 @@
>>>  #include <Guid/FileInfo.h>
>>>  #include <Guid/FileSystemInfo.h>
>>>  #include <Guid/FileSystemVolumeLabelInfo.h>
>>> +#include <Guid/LinuxEfiInitrdMedia.h>
>>>  #include <Guid/QemuKernelLoaderFsMedia.h>
>>>  #include <Library/BaseLib.h>
>>>  #include <Library/BaseMemoryLib.h>
>>>  #include <Library/DebugLib.h>
>>> +#include <Library/DevicePathLib.h>
>>>  #include <Library/MemoryAllocationLib.h>
>>>  #include <Library/QemuFwCfgLib.h>
>>>  #include <Library/UefiBootServicesTableLib.h>
>>>  #include <Library/UefiRuntimeServicesTableLib.h>
>>>  #include <Protocol/DevicePath.h>
>>> +#include <Protocol/LoadFile2.h>
>>>  #include <Protocol/SimpleFileSystem.h>
>>>
>>>  //
>>> @@ -84,6 +87,19 @@ STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = {
>>>    }
>>>  };
>>>
>>> +STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = {
>>> +  {
>>> +    {
>>> +      MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
>>> +      { sizeof (VENDOR_DEVICE_PATH) }
>>> +    },
>>> +    LINUX_EFI_INITRD_MEDIA_GUID
>>> +  }, {
>>> +    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
>>> +    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
>>> +  }
>>> +};
>>> +
>>>  //
>>>  // The "file in the EFI stub filesystem" abstraction.
>>>  //
>>> @@ -839,6 +855,48 @@ STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
>>>    StubFileSystemOpenVolume
>>>  };
>>>
>>> +STATIC
>>> +EFI_STATUS
>>> +EFIAPI
>>> +InitrdLoadFile2 (
>>> +  IN EFI_LOAD_FILE2_PROTOCOL          *This,
>>> +  IN EFI_DEVICE_PATH_PROTOCOL         *FilePath,
>>> +  IN BOOLEAN                          BootPolicy,
>>> +  IN OUT UINTN                        *BufferSize,
>>> +  IN VOID                             *Buffer OPTIONAL
>>> +  )
>>> +{
>>> +  CONST KERNEL_BLOB   *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];
>>> +
>>> +  ASSERT (InitrdBlob->Size > 0);
>>> +
>>> +  if (BootPolicy) {
>>> +    return EFI_UNSUPPORTED;
>>> +  }
>>> +
>>> +  if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
>>> +    return EFI_INVALID_PARAMETER;
>>> +  }
>>> +
>>> +  if (FilePath->Type != END_DEVICE_PATH_TYPE ||
>>> +      FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) {
>>> +    return EFI_NOT_FOUND;
>>> +  }
>>> +
>>> +  if (Buffer == NULL || *BufferSize < InitrdBlob->Size) {
>>> +    *BufferSize = InitrdBlob->Size;
>>> +    return EFI_BUFFER_TOO_SMALL;
>>> +  }
>>> +
>>> +  CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size);
>>> +
>>> +  *BufferSize = InitrdBlob->Size;
>>> +  return EFI_SUCCESS;
>>> +}
>>> +
>>> +STATIC CONST EFI_LOAD_FILE2_PROTOCOL     mInitrdLoadFile2 = {
>>> +  InitrdLoadFile2,
>>> +};
>>>
>>>  //
>>>  // Utility functions.
>>> @@ -945,6 +1003,7 @@ QemuKernelLoaderFsDxeEntrypoint (
>>>    KERNEL_BLOB               *KernelBlob;
>>>    EFI_STATUS                Status;
>>>    EFI_HANDLE                FileSystemHandle;
>>> +  EFI_HANDLE                InitrdLoadFile2Handle;
>>>
>>>    if (!QemuFwCfgIsAvailable ()) {
>>>      return EFI_NOT_FOUND;
>>> @@ -989,8 +1048,28 @@ QemuKernelLoaderFsDxeEntrypoint (
>>>      goto FreeBlobs;
>>>    }
>>>
>>> +  if (KernelBlob[KernelBlobTypeInitrd].Size > 0) {
>>> +    InitrdLoadFile2Handle = NULL;
>>> +    Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle,
>>> +                    &gEfiDevicePathProtocolGuid,  &mInitrdDevicePath,
>>> +                    &gEfiLoadFile2ProtocolGuid,   &mInitrdLoadFile2,
>>> +                    NULL);
>>> +    if (EFI_ERROR (Status)) {
>>> +      DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
>>> +        __FUNCTION__, Status));
>>> +      goto UninstallFileSystemHandle;
>>> +    }
>>> +  }
>>> +
>>>    return EFI_SUCCESS;
>>>
>>> +UninstallFileSystemHandle:
>>> +  Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
>>> +                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
>>> +                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
>>> +                  NULL);
>>> +  ASSERT_EFI_ERROR (Status);
>>> +
>>>  FreeBlobs:
>>>    while (BlobType > 0) {
>>>      CurrentBlob = &mKernelBlob[--BlobType];
>>> diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>>> index f4b50c265027..737f9b87a7c7 100644
>>> --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>>> +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf
>>> @@ -28,6 +28,7 @@ [LibraryClasses]
>>>    BaseLib
>>>    BaseMemoryLib
>>>    DebugLib
>>> +  DevicePathLib
>>>    MemoryAllocationLib
>>>    UefiBootServicesTableLib
>>>    QemuFwCfgLib
>>> @@ -42,6 +43,7 @@ [Guids]
>>>
>>>  [Protocols]
>>>    gEfiDevicePathProtocolGuid                ## PRODUCES
>>> +  gEfiLoadFile2ProtocolGuid                 ## PRODUCES
>>>    gEfiSimpleFileSystemProtocolGuid          ## PRODUCES
>>>
>>>  [Depex]
>>>
>>
>> I think this patch conflicts with your other patch:
>>
>>   [edk2-devel] [PATCH v3 2/6] OvmfPkg: add 'initrd' shell command to
>>                               expose Linux initrd via device path
>>
>> There should not be two different handles in the handle database that
>> carry the same device path (I mean "same device path" by contents, not
>> by reference).
>>
>> I believe that it's possible that the command-line specified -kernel /
>> -initrd are attempted first, but the kernel's stub returns for some
>> reason. Then the user can still go to the UEFI shell, and use the new
>> dynamic shell command. In that case I think the device path will cease
>> being unique (by contents).
>>
>> I think you can solve this as follows:
>>
>> - in both modules (this driver, and the dynamic shell command driver),
>> install a NULL protocol interface with GUID = gEfiCallerIdGuid on the
>> same handle that carries the device path and LoadFile2
>>
>> - in both modules, before you touch the handle that carries the device
>> path, try to locate it first. If it exists but doesn't carry the subject
>> module's FILE_GUID as a NULL protocol interface, we know the device path
>> was created by the "other" module, and we can bail.
>>
>> Just an idea.
>>
> 
> Thanks.
> 
> I did wonder about the rules regarding duplicates in the device path
> space. If the DXE core doesn't catch that, this seems like a gross
> oversight to me.
> 
> But it raises an interesting point: the idea is that any boot stage
> could elect to provide this interface, not just UEFI or the shell
> itself, but also shim, grub or whatever executes in between. That
> means that, in general, it should probably be the responsibility of
> the code that installs the protocol to ensure that it doesn't exist
> already.
> 
> For this code, it means it could simply install it, since it
> guarantees to execute first.
> 
> For the initrd implementation, it means we should check whether any
> other implementation exists already, which could simply be done [in
> that particular case] by locating the protocol and checking the
> address against the statically allocated one in the driver.
> 
> I am aware that both are DXE drivers, but since the dynamic shell
> command is not invoked by any driver that is invoked before EndOfDxe,
> I think the above should be sufficient.
> 

Sounds OK to me.

Thanks
Laszlo


^ permalink raw reply	[flat|nested] 33+ messages in thread

end of thread, other threads:[~2020-03-03 11:27 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-03-02  7:29 [PATCH 00/13] Ovmf: use LoadImage/StartImage for loading command line images Ard Biesheuvel
2020-03-02  7:29 ` [PATCH 01/13] OvmfPkg: add GUID for the QEMU kernel loader fs media device path Ard Biesheuvel
2020-03-02 13:22   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 02/13] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Ard Biesheuvel
2020-03-02 13:45   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 03/13] OvmfPkg: introduce QemuLoadImageLib library class Ard Biesheuvel
2020-03-02 14:07   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 04/13] OvmfPkg: provide a generic implementation of QemuLoadImageLib Ard Biesheuvel
2020-03-02 17:12   ` [edk2-devel] " Laszlo Ersek
2020-03-03  7:36     ` Laszlo Ersek
2020-03-02  7:29 ` [PATCH 05/13] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Ard Biesheuvel
2020-03-02 17:15   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 06/13] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Ard Biesheuvel
2020-03-02 17:26   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 07/13] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line Ard Biesheuvel
2020-03-02 17:31   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 08/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block Ard Biesheuvel
2020-03-02 17:58   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 09/13] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Ard Biesheuvel
2020-03-03  9:45   ` [edk2-devel] " Laszlo Ersek
2020-03-03 10:08     ` Ard Biesheuvel
2020-03-03 11:20       ` Laszlo Ersek
2020-03-02  7:29 ` [PATCH 10/13] OvmfPkg: add new QEMU kernel image loader components Ard Biesheuvel
2020-03-03  9:47   ` [edk2-devel] " Laszlo Ersek
2020-03-02  7:29 ` [PATCH 11/13] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Ard Biesheuvel
2020-03-03  9:52   ` [edk2-devel] " Laszlo Ersek
2020-03-03  9:53     ` Laszlo Ersek
2020-03-02  7:29 ` [PATCH 12/13] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Ard Biesheuvel
2020-03-03 10:10   ` [edk2-devel] " Laszlo Ersek
2020-03-03 10:18     ` Ard Biesheuvel
2020-03-03 11:27       ` Laszlo Ersek
2020-03-02  7:29 ` [PATCH 13/13] OvmfPkg: use generic QEMU image loader for secure boot enabled builds Ard Biesheuvel
2020-03-03 10:13   ` [edk2-devel] " Laszlo Ersek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox