]> git.proxmox.com Git - mirror_edk2.git/commitdiff
OvmfPkg: export abstract QEMU blob filesystem in standalone driver
authorArd Biesheuvel <ard.biesheuvel@linaro.org>
Fri, 28 Feb 2020 13:58:14 +0000 (14:58 +0100)
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Thu, 5 Mar 2020 19:45:05 +0000 (19:45 +0000)
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.

Since we are using the GetTime() runtime service in a DXE_DRIVER type
module, we need to DEPEX explicitly on gEfiRealTimeClockArchProtocolGuid.

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

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