From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f47.google.com (mail-wr1-f47.google.com [209.85.221.47]) by mx.groups.io with SMTP id smtpd.web11.9066.1583315565353674655 for ; Wed, 04 Mar 2020 01:52:45 -0800 Authentication-Results: mx.groups.io; dkim=pass header.i=@linaro.org header.s=google header.b=wGmjn/IY; spf=pass (domain: linaro.org, ip: 209.85.221.47, mailfrom: ard.biesheuvel@linaro.org) Received: by mail-wr1-f47.google.com with SMTP id r7so1577004wro.2 for ; Wed, 04 Mar 2020 01:52:45 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=zDszE8yf8xFtkzNeW2pO9EK5CHlY+Z6lOwZvuyJWILs=; b=wGmjn/IYVeyBr7i+bFH2DZAZywwA1fRgVKeW/LZPG+MUlVNfhkWZd1t8B3dAmHBo3W t50alO+rXXcJurw0/SjgZBWQ9zzl0dDADcO2SeSYyv5Ho0w3Ka37NwIBFbfkhfpPbf2Q /thOJCjOlIwW+L9E4ECTCiS21RGq+z3DIc0Ak7c+TWXu7T9vMNl0JsSBfQaCgduwc+// GwD+DnTTBESr02GzUFMnjrHi0GDSnlFkfW3vVArijpQEH64f0DFHoQMOJGjRs7dUKiq4 /tNdNtop0yWu06YolDxbyOSyQJbcrPV4NLzxvrXmJN3f1zVxHFqwLqhDfwXHmVQWojiX T3Wg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=zDszE8yf8xFtkzNeW2pO9EK5CHlY+Z6lOwZvuyJWILs=; b=AUxmUHEbP56xsofAPsTS68l0MOhji/uLwNbcWGDgxfWfiLTbmApDgdhC3RhWXsbZLA 0MBbIHmeNkOlEh8N2IBncCGn+uJLFE0MunaoZVPP9iuMtsllF2+/PgifrrsnwiTo+NWA u02MQ94E5p8jO5X8rO3aohodYh4WvaSt6yaNzyGGjHZm5X7ZHQsSSvCQFCS6EEU+TEeJ vea92xDZvM1hISYOF7YWjO6SlIrluiULI3mcldwtIYHO4rrzoc9drdYDK19Td3b8qvAQ puQGsLprbNgKQY7UqhA4tZMrc3r5RX5F11IBdXWnqMl1qWh+7W5q5VRw9hlGhgY+46KK VSMQ== X-Gm-Message-State: ANhLgQ3kYIovM62ZKWkzi1VRCCJs8WNlipfDK990AoN6vk0OcOMUF3Py CBiiL5BSI0eZV37+Zep9KHovYI9U110IZA== X-Google-Smtp-Source: ADFU+vvhsFg1qpyVL9H8yoSETdnKjsHJOZW1Ois3SlA1gz1jpPTXAYUI+mNoSXFBOfpdoEsa1VjHjw== X-Received: by 2002:adf:90cd:: with SMTP id i71mr3049368wri.63.1583315562654; Wed, 04 Mar 2020 01:52:42 -0800 (PST) Return-Path: Received: from e123331-lin.home ([2a01:cb1d:112:6f00:816e:ff0d:fb69:f613]) by smtp.gmail.com with ESMTPSA id v16sm20781095wrp.84.2020.03.04.01.52.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 04 Mar 2020 01:52:41 -0800 (PST) From: "Ard Biesheuvel" To: devel@edk2.groups.io Cc: lersek@redhat.com, Ard Biesheuvel Subject: [PATCH v2 02/14] OvmfPkg: export abstract QEMU blob filesystem in standalone driver Date: Wed, 4 Mar 2020 10:52:21 +0100 Message-Id: <20200304095233.21046-3-ard.biesheuvel@linaro.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200304095233.21046-1-ard.biesheuvel@linaro.org> References: <20200304095233.21046-1-ard.biesheuvel@linaro.org> 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 Reviewed-by: Laszlo Ersek --- 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..e4539ec2fbe5 --- /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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// 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..5ba88063def0 --- /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 -- 2.17.1