summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/QemuKernelLoaderFsDxe
diff options
context:
space:
mode:
authorArd Biesheuvel <ard.biesheuvel@linaro.org>2020-02-28 14:58:14 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2020-03-05 19:45:05 +0000
commit57f9b7f89e0e7226c69830e3e9b178c275f9140a (patch)
tree3629aa759f00db0fd8c0e913af4a70313002e2c0 /OvmfPkg/QemuKernelLoaderFsDxe
parent6ae2d31ca250ed1041f260f37b1b3761cbeab8ba (diff)
downloadedk2-57f9b7f89e0e7226c69830e3e9b178c275f9140a.tar.gz
edk2-57f9b7f89e0e7226c69830e3e9b178c275f9140a.tar.bz2
edk2-57f9b7f89e0e7226c69830e3e9b178c275f9140a.zip
OvmfPkg: export abstract QEMU blob filesystem in standalone driver
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>
Diffstat (limited to 'OvmfPkg/QemuKernelLoaderFsDxe')
-rw-r--r--OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c979
-rw-r--r--OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf48
2 files changed, 1027 insertions, 0 deletions
diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
new file mode 100644
index 0000000000..e4539ec2fb
--- /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 files 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 0000000000..5ba88063de
--- /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 files 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
+ QemuFwCfgLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiRuntimeServicesTableLib
+
+[Guids]
+ gEfiFileInfoGuid
+ gEfiFileSystemInfoGuid
+ gEfiFileSystemVolumeLabelInfoIdGuid
+ gQemuKernelLoaderFsMediaGuid
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## PRODUCES
+ gEfiSimpleFileSystemProtocolGuid ## PRODUCES
+
+[Depex]
+ gEfiRealTimeClockArchProtocolGuid