From f343ae2127b4be4d154f5594475c84b4db6d455f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 14:13:03 +0100 Subject: [PATCH 01/14] OvmfPkg: add GUID for the QEMU kernel loader fs media device path In an upcoming patch, we will introduce a separate DXE driver that exposes the virtual SimpleFileSystem implementation that carries the kernel and initrd passed via the QEMU command line, and a separate library that consumes it, to be incorporated into the boot manager. Since the GUID used for the SimpleFileSystem implementation's device path will no longer be for internal use only, create a well defined GUID to identify the media device path. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h | 18 ++++++++++++++++++ OvmfPkg/OvmfPkg.dec | 1 + 2 files changed, 19 insertions(+) create mode 100644 OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h diff --git a/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h new file mode 100644 index 000000000000..225c3c494613 --- /dev/null +++ b/OvmfPkg/Include/Guid/QemuKernelLoaderFsMedia.h @@ -0,0 +1,18 @@ +/** @file + GUID definition for the QEMU LoaderFs media device path, containing the + kernel, initrd and command line as file objects + + Copyright (c) 2020, Arm, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__ +#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID_H__ + +#define QEMU_KERNEL_LOADER_FS_MEDIA_GUID \ + {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}} + +extern EFI_GUID gQemuKernelLoaderFsMediaGuid; + +#endif diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 43d16372a270..a21b279d140a 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -87,6 +87,7 @@ gEfiLegacyBiosGuid = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}} gEfiLegacyDevOrderVariableGuid = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}} gLinuxEfiInitrdMediaGuid = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}} + gQemuKernelLoaderFsMediaGuid = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}} [Ppis] # PPI whose presence in the PPI database signals that the TPM base address From cbf087bd694866f5f4d293ad867d33f483a324fa Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 14:58:14 +0100 Subject: [PATCH 02/14] 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 Reviewed-by: Laszlo Ersek --- .../QemuKernelLoaderFsDxe.c | 979 ++++++++++++++++++ .../QemuKernelLoaderFsDxe.inf | 48 + 2 files changed, 1027 insertions(+) create mode 100644 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c create mode 100644 OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf 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 From 94f0c83965919c279981d2bbe019210532fb6f8f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 17:04:01 +0100 Subject: [PATCH 03/14] OvmfPkg: introduce QemuLoadImageLib library class Introduce the QemuLoadImageLib library class that we will instantiate to load the kernel image passed via the QEMU command line using the standard LoadImage boot service. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- OvmfPkg/Include/Library/QemuLoadImageLib.h | 84 ++++++++++++++++++++++ OvmfPkg/OvmfPkg.dec | 5 ++ 2 files changed, 89 insertions(+) create mode 100644 OvmfPkg/Include/Library/QemuLoadImageLib.h diff --git a/OvmfPkg/Include/Library/QemuLoadImageLib.h b/OvmfPkg/Include/Library/QemuLoadImageLib.h new file mode 100644 index 000000000000..746b74f73c40 --- /dev/null +++ b/OvmfPkg/Include/Library/QemuLoadImageLib.h @@ -0,0 +1,84 @@ +/** @file + Load a kernel image and command line passed to QEMU via + the command line + + Copyright (C) 2020, Arm, Limited. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef QEMU_LOAD_IMAGE_LIB_H__ +#define QEMU_LOAD_IMAGE_LIB_H__ + +#include +#include + +#include + +/** + Download the kernel, the initial ramdisk, and the kernel command line from + QEMU's fw_cfg. The kernel will be instructed via its command line to load + the initrd from the same Simple FileSystem where the kernel was loaded from. + + @param[out] ImageHandle The image handle that was allocated for + loading the image + + @retval EFI_SUCCESS The image was loaded successfully. + @retval EFI_NOT_FOUND Kernel image was not found. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_PROTOCOL_ERROR Unterminated kernel command line. + @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call + returned EFI_SECURITY_VIOLATION, and the image + was unloaded again. + + @return Error codes from any of the underlying + functions. +**/ +EFI_STATUS +EFIAPI +QemuLoadKernelImage ( + OUT EFI_HANDLE *ImageHandle + ); + +/** + Transfer control to a kernel image loaded with QemuLoadKernelImage () + + @param[in,out] ImageHandle Handle of image to be started. May assume a + different value on return if the image was + reloaded. + + @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle + or the image has already been initialized with + StartImage + @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the + image should not be started. + + @return Error codes returned by the started image. + On success, the function doesn't return. +**/ +EFI_STATUS +EFIAPI +QemuStartKernelImage ( + IN OUT EFI_HANDLE *ImageHandle + ); + +/** + Unloads an image loaded with QemuLoadKernelImage (). + + @param ImageHandle Handle that identifies the image to be + unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_UNSUPPORTED The image has been started, and does not + support unload. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + + @return Exit code from the image's unload function. +**/ +EFI_STATUS +EFIAPI +QemuUnloadKernelImage ( + IN EFI_HANDLE ImageHandle + ); + +#endif diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index a21b279d140a..055caaa43041 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -58,6 +58,11 @@ # QemuBootOrderLib|Include/Library/QemuBootOrderLib.h + ## @libraryclass Load a kernel image and command line passed to QEMU via + # the command line + # + QemuLoadImageLib|Include/Library/QemuLoadImageLib.h + ## @libraryclass Serialize (and deserialize) variables # SerializeVariablesLib|Include/Library/SerializeVariablesLib.h From 1acafb149a8d5196458d21467af838657739534f Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 17:26:54 +0100 Subject: [PATCH 04/14] OvmfPkg: provide a generic implementation of QemuLoadImageLib Implement QemuLoadImageLib, and make it load the image provided by the QEMU_EFI_LOADER_FS_MEDIA_GUID/kernel device path that we implemented in a preceding patch in a separate DXE driver, using only the standard LoadImage and StartImage boot services. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../GenericQemuLoadImageLib.c | 276 ++++++++++++++++++ .../GenericQemuLoadImageLib.inf | 38 +++ 2 files changed, 314 insertions(+) create mode 100644 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c create mode 100644 OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c new file mode 100644 index 000000000000..f7f9a205f99d --- /dev/null +++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c @@ -0,0 +1,276 @@ +/** @file + Generic implementation of QemuLoadImageLib library class interface. + + Copyright (c) 2020, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma pack (1) +typedef struct { + EFI_DEVICE_PATH_PROTOCOL FilePathHeader; + CHAR16 FilePath[ARRAY_SIZE (L"kernel")]; +} KERNEL_FILE_DEVPATH; + +typedef struct { + VENDOR_DEVICE_PATH VenMediaNode; + KERNEL_FILE_DEVPATH FileNode; + EFI_DEVICE_PATH_PROTOCOL EndNode; +} KERNEL_VENMEDIA_FILE_DEVPATH; +#pragma pack () + +STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = { + { + { + MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, + { sizeof (VENDOR_DEVICE_PATH) } + }, + QEMU_KERNEL_LOADER_FS_MEDIA_GUID + }, { + { + MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP, + { sizeof (KERNEL_FILE_DEVPATH) } + }, + L"kernel", + }, { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL) } + } +}; + +/** + Download the kernel, the initial ramdisk, and the kernel command line from + QEMU's fw_cfg. The kernel will be instructed via its command line to load + the initrd from the same Simple FileSystem where the kernel was loaded from. + + @param[out] ImageHandle The image handle that was allocated for + loading the image + + @retval EFI_SUCCESS The image was loaded successfully. + @retval EFI_NOT_FOUND Kernel image was not found. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_PROTOCOL_ERROR Unterminated kernel command line. + @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call + returned EFI_SECURITY_VIOLATION, and the image + was unloaded again. + + @return Error codes from any of the underlying + functions. +**/ +EFI_STATUS +EFIAPI +QemuLoadKernelImage ( + OUT EFI_HANDLE *ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE KernelImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; + UINTN CommandLineSize; + CHAR8 *CommandLine; + UINTN InitrdSize; + + // + // Load the image. This should call back into the QEMU EFI loader file system. + // + Status = gBS->LoadImage ( + FALSE, // BootPolicy: exact match required + gImageHandle, // ParentImageHandle + (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath, + NULL, // SourceBuffer + 0, // SourceSize + &KernelImageHandle + ); + switch (Status) { + case EFI_SUCCESS: + break; + + case EFI_SECURITY_VIOLATION: + // + // In this case, the image was loaded but failed to authenticate. + // + Status = EFI_ACCESS_DENIED; + goto UnloadImage; + + default: + DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); + return Status; + } + + // + // Construct the kernel command line. + // + Status = gBS->OpenProtocol ( + KernelImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&KernelLoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); + CommandLineSize = (UINTN)QemuFwCfgRead32 (); + + if (CommandLineSize == 0) { + KernelLoadedImage->LoadOptionsSize = 0; + } else { + CommandLine = AllocatePool (CommandLineSize); + if (CommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UnloadImage; + } + + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); + QemuFwCfgReadBytes (CommandLineSize, CommandLine); + + // + // Verify NUL-termination of the command line. + // + if (CommandLine[CommandLineSize - 1] != '\0') { + DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n", + __FUNCTION__)); + Status = EFI_PROTOCOL_ERROR; + goto FreeCommandLine; + } + + // + // Drop the terminating NUL, convert to UTF-16. + // + KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2; + } + + QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); + InitrdSize = (UINTN)QemuFwCfgRead32 (); + + if (InitrdSize > 0) { + // + // Append ' initrd=initrd' in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2; + } + + if (KernelLoadedImage->LoadOptionsSize == 0) { + KernelLoadedImage->LoadOptions = NULL; + } else { + // + // NUL-terminate in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += 2; + + KernelLoadedImage->LoadOptions = AllocatePool ( + KernelLoadedImage->LoadOptionsSize); + if (KernelLoadedImage->LoadOptions == NULL) { + KernelLoadedImage->LoadOptionsSize = 0; + Status = EFI_OUT_OF_RESOURCES; + goto FreeCommandLine; + } + + UnicodeSPrintAsciiFormat ( + KernelLoadedImage->LoadOptions, + KernelLoadedImage->LoadOptionsSize, + "%a%a", + (CommandLineSize == 0) ? "" : CommandLine, + (InitrdSize == 0) ? "" : " initrd=initrd" + ); + DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, + (CHAR16 *)KernelLoadedImage->LoadOptions)); + } + + *ImageHandle = KernelImageHandle; + return EFI_SUCCESS; + +FreeCommandLine: + if (CommandLineSize > 0) { + FreePool (CommandLine); + } +UnloadImage: + gBS->UnloadImage (KernelImageHandle); + + return Status; +} + +/** + Transfer control to a kernel image loaded with QemuLoadKernelImage () + + @param[in,out] ImageHandle Handle of image to be started. May assume a + different value on return if the image was + reloaded. + + @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle + or the image has already been initialized with + StartImage + @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the + image should not be started. + + @return Error codes returned by the started image +**/ +EFI_STATUS +EFIAPI +QemuStartKernelImage ( + IN OUT EFI_HANDLE *ImageHandle + ) +{ + return gBS->StartImage ( + *ImageHandle, + NULL, // ExitDataSize + NULL // ExitData + ); +} + +/** + Unloads an image loaded with QemuLoadKernelImage (). + + @param ImageHandle Handle that identifies the image to be + unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_UNSUPPORTED The image has been started, and does not + support unload. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + + @return Exit code from the image's unload function. +**/ +EFI_STATUS +EFIAPI +QemuUnloadKernelImage ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&KernelLoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + if (KernelLoadedImage->LoadOptions != NULL) { + FreePool (KernelLoadedImage->LoadOptions); + KernelLoadedImage->LoadOptions = NULL; + } + KernelLoadedImage->LoadOptionsSize = 0; + + return gBS->UnloadImage (ImageHandle); +} diff --git a/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf new file mode 100644 index 000000000000..b262cb926a4d --- /dev/null +++ b/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf @@ -0,0 +1,38 @@ +## @file +# Generic implementation of QemuLoadImageLib library class interface. +# +# Copyright (c) 2020, ARM Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = GenericQemuLoadImageLib + FILE_GUID = 9e3e28da-c7b5-4f85-841a-84e6a9a1f1a0 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = QemuLoadImageLib|DXE_DRIVER + +[Sources] + GenericQemuLoadImageLib.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + DebugLib + MemoryAllocationLib + PrintLib + QemuFwCfgLib + UefiBootServicesTableLib + +[Protocols] + gEfiDevicePathProtocolGuid + gEfiLoadedImageProtocolGuid + +[Guids] + gQemuKernelLoaderFsMediaGuid From b29d37e7f3181dfb741bd3472c97f5df130ab4d8 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 17:30:08 +0100 Subject: [PATCH 05/14] ArmVirtPkg: incorporate the new QEMU kernel loader driver and library Add the QEMU loader DXE driver and client library to the build for our QEMU targeted implementations in ArmVirtPkg. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- ArmVirtPkg/ArmVirtQemu.dsc | 2 ++ ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc | 1 + ArmVirtPkg/ArmVirtQemuKernel.dsc | 2 ++ 3 files changed, 5 insertions(+) diff --git a/ArmVirtPkg/ArmVirtQemu.dsc b/ArmVirtPkg/ArmVirtQemu.dsc index 5e5f71e7fe8a..8c77fc46427b 100644 --- a/ArmVirtPkg/ArmVirtQemu.dsc +++ b/ArmVirtPkg/ArmVirtQemu.dsc @@ -58,6 +58,7 @@ VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf + QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf ArmPlatformLib|ArmPlatformPkg/Library/ArmPlatformLibNull/ArmPlatformLibNull.inf @@ -427,6 +428,7 @@ NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf } + OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf # # Networking stack diff --git a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc index 2a17583593ef..aaba0b1c8840 100644 --- a/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc +++ b/ArmVirtPkg/ArmVirtQemuFvMain.fdf.inc @@ -114,6 +114,7 @@ READ_LOCK_STATUS = TRUE INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf INF MdeModulePkg/Application/UiApp/UiApp.inf + INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf # # Networking stack diff --git a/ArmVirtPkg/ArmVirtQemuKernel.dsc b/ArmVirtPkg/ArmVirtQemuKernel.dsc index 37e19dfbd0db..4d82a77213ec 100644 --- a/ArmVirtPkg/ArmVirtQemuKernel.dsc +++ b/ArmVirtPkg/ArmVirtQemuKernel.dsc @@ -56,6 +56,7 @@ VirtioMmioDeviceLib|OvmfPkg/Library/VirtioMmioDeviceLib/VirtioMmioDeviceLib.inf QemuFwCfgLib|ArmVirtPkg/Library/QemuFwCfgLib/QemuFwCfgLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/BaseQemuFwCfgS3LibNull.inf + QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf ArmVirtMemInfoLib|ArmVirtPkg/Library/QemuVirtMemInfoLib/QemuVirtMemInfoLib.inf @@ -362,6 +363,7 @@ NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf } + OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf # # Networking stack From 163bb62a1fa559bb56caf63c9a8439fea5dfa684 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 17:32:35 +0100 Subject: [PATCH 06/14] ArmVirtPkg/PlatformBootManagerLib: switch to separate QEMU loader Drop the QEMU loader file system implementation inside this library, and switch to the separate QemuLoadImageLib library and the associated driver to expose the kernel and initrd passed via the QEMU command line. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../PlatformBootManagerLib.inf | 9 +- .../PlatformBootManagerLib/QemuKernel.c | 1061 +---------------- 2 files changed, 7 insertions(+), 1063 deletions(-) diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf index 6fe0a1bb122b..11f52e019bc3 100644 --- a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -44,9 +44,8 @@ MemoryAllocationLib PcdLib PlatformBmPrintScLib - PrintLib QemuBootOrderLib - QemuFwCfgLib + QemuLoadImageLib ReportStatusCodeLib UefiBootManagerLib UefiBootServicesTableLib @@ -64,19 +63,13 @@ gEfiMdePkgTokenSpaceGuid.PcdPlatformBootTimeOut [Guids] - gEfiFileInfoGuid - gEfiFileSystemInfoGuid - gEfiFileSystemVolumeLabelInfoIdGuid gEfiEndOfDxeEventGroupGuid gEfiGlobalVariableGuid gRootBridgesConnectedEventGroupGuid gUefiShellFileGuid [Protocols] - gEfiDevicePathProtocolGuid gEfiFirmwareVolume2ProtocolGuid gEfiGraphicsOutputProtocolGuid - gEfiLoadedImageProtocolGuid gEfiPciRootBridgeIoProtocolGuid - gEfiSimpleFileSystemProtocolGuid gVirtioDeviceProtocolGuid diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c index d3851fd75fa5..e9c110a0ed75 100644 --- a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c @@ -9,887 +9,11 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ -#include -#include -#include -#include -#include +#include #include -#include -#include -#include #include "PlatformBm.h" -// -// Static data that hosts the fw_cfg blobs and serves file requests. -// -typedef enum { - KernelBlobTypeKernel, - KernelBlobTypeInitrd, - KernelBlobTypeCommandLine, - KernelBlobTypeMax -} KERNEL_BLOB_TYPE; - -typedef struct { - FIRMWARE_CONFIG_ITEM CONST SizeKey; - FIRMWARE_CONFIG_ITEM CONST DataKey; - CONST CHAR16 * CONST Name; - UINT32 Size; - UINT8 *Data; -} KERNEL_BLOB; - -STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = { - { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" }, - { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" }, - { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" } -}; - -STATIC UINT64 mTotalBlobBytes; - -// -// Device path for the handle that incorporates our "EFI stub filesystem". The -// GUID is arbitrary and need not be standardized or advertized. -// -#pragma pack(1) -typedef struct { - VENDOR_DEVICE_PATH VenHwNode; - EFI_DEVICE_PATH_PROTOCOL EndNode; -} SINGLE_VENHW_NODE_DEVPATH; -#pragma pack() - -STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = { - { - { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } }, - { - 0xb0fae7e7, 0x6b07, 0x49d0, - { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d } - } - }, - - { - END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, - { sizeof (EFI_DEVICE_PATH_PROTOCOL) } - } -}; - -// -// The "file in the EFI stub filesystem" abstraction. -// -STATIC EFI_TIME mInitTime; - -#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E') - -typedef struct { - UINT64 Signature; // Carries STUB_FILE_SIG. - - KERNEL_BLOB_TYPE BlobType; // Index into mKernelBlob. KernelBlobTypeMax - // denotes the root directory of the filesystem. - - UINT64 Position; // Byte position for regular files; - // next directory entry to return for the root - // directory. - - EFI_FILE_PROTOCOL File; // Standard protocol interface. -} STUB_FILE; - -#define STUB_FILE_FROM_FILE(FilePointer) \ - CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG) - -// -// Tentative definition of the file protocol template. The initializer -// (external definition) will be provided later. -// -STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate; - - -// -// Protocol member functions for File. -// - -/** - Opens a new file relative to the source file's location. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is - the file handle to the source location. This would - typically be an open handle to a directory. - - @param[out] NewHandle A pointer to the location to return the opened handle - for the new file. - - @param[in] FileName The Null-terminated string of the name of the file to - be opened. The file name may contain the following - path modifiers: "\", ".", and "..". - - @param[in] OpenMode The mode to open the file. The only valid - combinations that the file may be opened with are: - Read, Read/Write, or Create/Read/Write. - - @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case - these are the attribute bits for the newly created - file. - - @retval EFI_SUCCESS The file was opened. - @retval EFI_NOT_FOUND The specified file could not be found on the - device. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_MEDIA_CHANGED The device has a different medium in it or the - medium is no longer supported. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a - file for write when the media is - write-protected. - @retval EFI_ACCESS_DENIED The service denied access to the file. - @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the - file. - @retval EFI_VOLUME_FULL The volume is full. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileOpen ( - IN EFI_FILE_PROTOCOL *This, - OUT EFI_FILE_PROTOCOL **NewHandle, - IN CHAR16 *FileName, - IN UINT64 OpenMode, - IN UINT64 Attributes - ) -{ - CONST STUB_FILE *StubFile; - UINTN BlobType; - STUB_FILE *NewStubFile; - - // - // We're read-only. - // - switch (OpenMode) { - case EFI_FILE_MODE_READ: - break; - - case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE: - case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE: - return EFI_WRITE_PROTECTED; - - default: - return EFI_INVALID_PARAMETER; - } - - // - // Only the root directory supports opening files in it. - // - StubFile = STUB_FILE_FROM_FILE (This); - if (StubFile->BlobType != KernelBlobTypeMax) { - return EFI_UNSUPPORTED; - } - - // - // Locate the file. - // - for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) { - if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) { - break; - } - } - if (BlobType == KernelBlobTypeMax) { - return EFI_NOT_FOUND; - } - - // - // Found it. - // - NewStubFile = AllocatePool (sizeof *NewStubFile); - if (NewStubFile == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - NewStubFile->Signature = STUB_FILE_SIG; - NewStubFile->BlobType = (KERNEL_BLOB_TYPE)BlobType; - NewStubFile->Position = 0; - CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate, - sizeof mEfiFileProtocolTemplate); - *NewHandle = &NewStubFile->File; - - return EFI_SUCCESS; -} - - -/** - Closes a specified file handle. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file - handle to close. - - @retval EFI_SUCCESS The file was closed. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileClose ( - IN EFI_FILE_PROTOCOL *This - ) -{ - FreePool (STUB_FILE_FROM_FILE (This)); - return EFI_SUCCESS; -} - - -/** - Close and delete the file handle. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the - handle to the file to delete. - - @retval EFI_SUCCESS The file was closed and deleted, and the - handle was closed. - @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not - deleted. - -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileDelete ( - IN EFI_FILE_PROTOCOL *This - ) -{ - FreePool (STUB_FILE_FROM_FILE (This)); - return EFI_WARN_DELETE_FAILURE; -} - - -/** - Helper function that formats an EFI_FILE_INFO structure into the - user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including - KernelBlobTypeMax, which stands for the root directory). - - The interface follows the EFI_FILE_GET_INFO -- and for directories, the - EFI_FILE_READ -- interfaces. - - @param[in] BlobType The KERNEL_BLOB_TYPE value identifying the fw_cfg - blob backing the STUB_FILE that information is - being requested about. If BlobType equals - KernelBlobTypeMax, then information will be - provided about the root directory of the - filesystem. - - @param[in,out] BufferSize On input, the size of Buffer. On output, the - amount of data returned in Buffer. In both cases, - the size is measured in bytes. - - @param[out] Buffer A pointer to the data buffer to return. The - buffer's type is EFI_FILE_INFO. - - @retval EFI_SUCCESS The information was returned. - @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the - EFI_FILE_INFO structure. BufferSize has been - updated with the size needed to complete the - request. -**/ -STATIC -EFI_STATUS -ConvertKernelBlobTypeToFileInfo ( - IN KERNEL_BLOB_TYPE BlobType, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer - ) -{ - CONST CHAR16 *Name; - UINT64 FileSize; - UINT64 Attribute; - - UINTN NameSize; - UINTN FileInfoSize; - EFI_FILE_INFO *FileInfo; - UINTN OriginalBufferSize; - - if (BlobType == KernelBlobTypeMax) { - // - // getting file info about the root directory - // - Name = L"\\"; - FileSize = KernelBlobTypeMax; - Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; - } else { - CONST KERNEL_BLOB *Blob; - - Blob = &mKernelBlob[BlobType]; - Name = Blob->Name; - FileSize = Blob->Size; - Attribute = EFI_FILE_READ_ONLY; - } - - NameSize = (StrLen(Name) + 1) * 2; - FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize; - ASSERT (FileInfoSize >= sizeof *FileInfo); - - OriginalBufferSize = *BufferSize; - *BufferSize = FileInfoSize; - if (OriginalBufferSize < *BufferSize) { - return EFI_BUFFER_TOO_SMALL; - } - - FileInfo = (EFI_FILE_INFO *)Buffer; - FileInfo->Size = FileInfoSize; - FileInfo->FileSize = FileSize; - FileInfo->PhysicalSize = FileSize; - FileInfo->Attribute = Attribute; - - CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime); - CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime); - CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime); - CopyMem (FileInfo->FileName, Name, NameSize); - - return EFI_SUCCESS; -} - - -/** - Reads data from a file, or continues scanning a directory. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that - is the file handle to read data from. - - @param[in,out] BufferSize On input, the size of the Buffer. On output, the - amount of data returned in Buffer. In both cases, - the size is measured in bytes. If the read goes - beyond the end of the file, the read length is - truncated to the end of the file. - - If This is a directory, the function reads the - directory entry at the current position and - returns the entry (as EFI_FILE_INFO) in Buffer. If - there are no more directory entries, the - BufferSize is set to zero on output. - - @param[out] Buffer The buffer into which the data is read. - - @retval EFI_SUCCESS Data was read. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted - file. - @retval EFI_DEVICE_ERROR On entry, the current file position is beyond - the end of the file. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the - current directory entry as a EFI_FILE_INFO - structure. BufferSize has been updated with the - size needed to complete the request, and the - directory position has not been advanced. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileRead ( - IN EFI_FILE_PROTOCOL *This, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer - ) -{ - STUB_FILE *StubFile; - CONST KERNEL_BLOB *Blob; - UINT64 Left; - - StubFile = STUB_FILE_FROM_FILE (This); - - // - // Scanning the root directory? - // - if (StubFile->BlobType == KernelBlobTypeMax) { - EFI_STATUS Status; - - if (StubFile->Position == KernelBlobTypeMax) { - // - // Scanning complete. - // - *BufferSize = 0; - return EFI_SUCCESS; - } - - Status = ConvertKernelBlobTypeToFileInfo ( - (KERNEL_BLOB_TYPE)StubFile->Position, - BufferSize, - Buffer); - if (EFI_ERROR (Status)) { - return Status; - } - - ++StubFile->Position; - return EFI_SUCCESS; - } - - // - // Reading a file. - // - Blob = &mKernelBlob[StubFile->BlobType]; - if (StubFile->Position > Blob->Size) { - return EFI_DEVICE_ERROR; - } - - Left = Blob->Size - StubFile->Position; - if (*BufferSize > Left) { - *BufferSize = (UINTN)Left; - } - if (Blob->Data != NULL) { - CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize); - } - StubFile->Position += *BufferSize; - return EFI_SUCCESS; -} - - -/** - Writes data to a file. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that - is the file handle to write data to. - - @param[in,out] BufferSize On input, the size of the Buffer. On output, the - amount of data actually written. In both cases, - the size is measured in bytes. - - @param[in] Buffer The buffer of data to write. - - @retval EFI_SUCCESS Data was written. - @retval EFI_UNSUPPORTED Writes to open directory files are not - supported. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_WRITE_PROTECTED The file or medium is write-protected. - @retval EFI_ACCESS_DENIED The file was opened read only. - @retval EFI_VOLUME_FULL The volume is full. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileWrite ( - IN EFI_FILE_PROTOCOL *This, - IN OUT UINTN *BufferSize, - IN VOID *Buffer - ) -{ - STUB_FILE *StubFile; - - StubFile = STUB_FILE_FROM_FILE (This); - return (StubFile->BlobType == KernelBlobTypeMax) ? - EFI_UNSUPPORTED : - EFI_WRITE_PROTECTED; -} - - -/** - Returns a file's current position. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the - file handle to get the current position on. - - @param[out] Position The address to return the file's current position - value. - - @retval EFI_SUCCESS The position was returned. - @retval EFI_UNSUPPORTED The request is not valid on open directories. - @retval EFI_DEVICE_ERROR An attempt was made to get the position from a - deleted file. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileGetPosition ( - IN EFI_FILE_PROTOCOL *This, - OUT UINT64 *Position - ) -{ - STUB_FILE *StubFile; - - StubFile = STUB_FILE_FROM_FILE (This); - if (StubFile->BlobType == KernelBlobTypeMax) { - return EFI_UNSUPPORTED; - } - - *Position = StubFile->Position; - return EFI_SUCCESS; -} - - -/** - Sets a file's current position. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the - file handle to set the requested position on. - - @param[in] Position The byte position from the start of the file to set. For - regular files, MAX_UINT64 means "seek to end". For - directories, zero means "rewind directory scan". - - @retval EFI_SUCCESS The position was set. - @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open - directories. - @retval EFI_DEVICE_ERROR An attempt was made to set the position of a - deleted file. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileSetPosition ( - IN EFI_FILE_PROTOCOL *This, - IN UINT64 Position - ) -{ - STUB_FILE *StubFile; - KERNEL_BLOB *Blob; - - StubFile = STUB_FILE_FROM_FILE (This); - - if (StubFile->BlobType == KernelBlobTypeMax) { - if (Position == 0) { - // - // rewinding a directory scan is allowed - // - StubFile->Position = 0; - return EFI_SUCCESS; - } - return EFI_UNSUPPORTED; - } - - // - // regular file seek - // - Blob = &mKernelBlob[StubFile->BlobType]; - if (Position == MAX_UINT64) { - // - // seek to end - // - StubFile->Position = Blob->Size; - } else { - // - // absolute seek from beginning -- seeking past the end is allowed - // - StubFile->Position = Position; - } - return EFI_SUCCESS; -} - - -/** - Returns information about a file. - - @param[in] This A pointer to the EFI_FILE_PROTOCOL instance - that is the file handle the requested - information is for. - - @param[in] InformationType The type identifier GUID for the information - being requested. The following information - types are supported, storing the - corresponding structures in Buffer: - - - gEfiFileInfoGuid: EFI_FILE_INFO - - - gEfiFileSystemInfoGuid: - EFI_FILE_SYSTEM_INFO - - - gEfiFileSystemVolumeLabelInfoIdGuid: - EFI_FILE_SYSTEM_VOLUME_LABEL - - @param[in,out] BufferSize On input, the size of Buffer. On output, the - amount of data returned in Buffer. In both - cases, the size is measured in bytes. - - @param[out] Buffer A pointer to the data buffer to return. The - buffer's type is indicated by - InformationType. - - @retval EFI_SUCCESS The information was returned. - @retval EFI_UNSUPPORTED The InformationType is not known. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the - information structure requested by - InformationType. BufferSize has been updated - with the size needed to complete the request. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileGetInfo ( - IN EFI_FILE_PROTOCOL *This, - IN EFI_GUID *InformationType, - IN OUT UINTN *BufferSize, - OUT VOID *Buffer - ) -{ - CONST STUB_FILE *StubFile; - UINTN OriginalBufferSize; - - StubFile = STUB_FILE_FROM_FILE (This); - - if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { - return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize, - Buffer); - } - - OriginalBufferSize = *BufferSize; - - if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { - EFI_FILE_SYSTEM_INFO *FileSystemInfo; - - *BufferSize = sizeof *FileSystemInfo; - if (OriginalBufferSize < *BufferSize) { - return EFI_BUFFER_TOO_SMALL; - } - - FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; - FileSystemInfo->Size = sizeof *FileSystemInfo; - FileSystemInfo->ReadOnly = TRUE; - FileSystemInfo->VolumeSize = mTotalBlobBytes; - FileSystemInfo->FreeSpace = 0; - FileSystemInfo->BlockSize = 1; - FileSystemInfo->VolumeLabel[0] = L'\0'; - - return EFI_SUCCESS; - } - - if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { - EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel; - - *BufferSize = sizeof *FileSystemVolumeLabel; - if (OriginalBufferSize < *BufferSize) { - return EFI_BUFFER_TOO_SMALL; - } - - FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; - FileSystemVolumeLabel->VolumeLabel[0] = L'\0'; - - return EFI_SUCCESS; - } - - return EFI_UNSUPPORTED; -} - - -/** - Sets information about a file. - - @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that - is the file handle the information is for. - - @param[in] InformationType The type identifier for the information being - set. - - @param[in] BufferSize The size, in bytes, of Buffer. - - @param[in] Buffer A pointer to the data buffer to write. The - buffer's type is indicated by InformationType. - - @retval EFI_SUCCESS The information was set. - @retval EFI_UNSUPPORTED The InformationType is not known. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the - media is read-only. - @retval EFI_WRITE_PROTECTED InformationType is - EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media - is read only. - @retval EFI_WRITE_PROTECTED InformationType is - EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media - is read-only. - @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file - to a file that is already present. - @retval EFI_ACCESS_DENIED An attempt is being made to change the - EFI_FILE_DIRECTORY Attribute. - @retval EFI_ACCESS_DENIED An attempt is being made to change the size of - a directory. - @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the - file was opened read-only and an attempt is - being made to modify a field other than - Attribute. - @retval EFI_VOLUME_FULL The volume is full. - @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type - indicated by InformationType. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileSetInfo ( - IN EFI_FILE_PROTOCOL *This, - IN EFI_GUID *InformationType, - IN UINTN BufferSize, - IN VOID *Buffer - ) -{ - return EFI_WRITE_PROTECTED; -} - - -/** - Flushes all modified data associated with a file to a device. - - @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the - file handle to flush. - - @retval EFI_SUCCESS The data was flushed. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_WRITE_PROTECTED The file or medium is write-protected. - @retval EFI_ACCESS_DENIED The file was opened read-only. - @retval EFI_VOLUME_FULL The volume is full. -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileFlush ( - IN EFI_FILE_PROTOCOL *This - ) -{ - return EFI_WRITE_PROTECTED; -} - -// -// External definition of the file protocol template. -// -STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = { - EFI_FILE_PROTOCOL_REVISION, // revision 1 - StubFileOpen, - StubFileClose, - StubFileDelete, - StubFileRead, - StubFileWrite, - StubFileGetPosition, - StubFileSetPosition, - StubFileGetInfo, - StubFileSetInfo, - StubFileFlush, - NULL, // OpenEx, revision 2 - NULL, // ReadEx, revision 2 - NULL, // WriteEx, revision 2 - NULL // FlushEx, revision 2 -}; - - -// -// Protocol member functions for SimpleFileSystem. -// - -/** - Open the root directory on a volume. - - @param[in] This A pointer to the volume to open the root directory on. - - @param[out] Root A pointer to the location to return the opened file handle - for the root directory in. - - @retval EFI_SUCCESS The device was opened. - @retval EFI_UNSUPPORTED This volume does not support the requested file - system type. - @retval EFI_NO_MEDIA The device has no medium. - @retval EFI_DEVICE_ERROR The device reported an error. - @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. - @retval EFI_ACCESS_DENIED The service denied access to the file. - @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of - resources. - @retval EFI_MEDIA_CHANGED The device has a different medium in it or the - medium is no longer supported. Any existing - file handles for this volume are no longer - valid. To access the files on the new medium, - the volume must be reopened with OpenVolume(). -**/ -STATIC -EFI_STATUS -EFIAPI -StubFileSystemOpenVolume ( - IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, - OUT EFI_FILE_PROTOCOL **Root - ) -{ - STUB_FILE *StubFile; - - StubFile = AllocatePool (sizeof *StubFile); - if (StubFile == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - StubFile->Signature = STUB_FILE_SIG; - StubFile->BlobType = KernelBlobTypeMax; - StubFile->Position = 0; - CopyMem (&StubFile->File, &mEfiFileProtocolTemplate, - sizeof mEfiFileProtocolTemplate); - *Root = &StubFile->File; - - return EFI_SUCCESS; -} - -STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = { - EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, - StubFileSystemOpenVolume -}; - - -// -// Utility functions. -// - -/** - Populate a blob in mKernelBlob. - - param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is - to be filled from fw_cfg. - - @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a - size of zero for the blob, then Blob->Data has - been left unchanged. - - @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data. -**/ -STATIC -EFI_STATUS -FetchBlob ( - IN OUT KERNEL_BLOB *Blob - ) -{ - UINT32 Left; - - // - // Read blob size. - // - QemuFwCfgSelectItem (Blob->SizeKey); - Blob->Size = QemuFwCfgRead32 (); - if (Blob->Size == 0) { - return EFI_SUCCESS; - } - - // - // Read blob. - // - Blob->Data = AllocatePool (Blob->Size); - if (Blob->Data == NULL) { - DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n", - __FUNCTION__, (INT64)Blob->Size, Blob->Name)); - return EFI_OUT_OF_RESOURCES; - } - - DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__, - (INT64)Blob->Size, Blob->Name)); - QemuFwCfgSelectItem (Blob->DataKey); - - Left = Blob->Size; - do { - UINT32 Chunk; - - Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB; - QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left)); - Left -= Chunk; - DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n", - __FUNCTION__, (INT64)Left, Blob->Name)); - } while (Left > 0); - return EFI_SUCCESS; -} - - // // The entry point of the feature. // @@ -916,159 +40,14 @@ TryRunningQemuKernel ( VOID ) { - UINTN BlobType; - KERNEL_BLOB *CurrentBlob; - KERNEL_BLOB *KernelBlob, *InitrdBlob, *CommandLineBlob; EFI_STATUS Status; - EFI_HANDLE FileSystemHandle; - EFI_DEVICE_PATH_PROTOCOL *KernelDevicePath; EFI_HANDLE KernelImageHandle; - EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; - Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */); + Status = QemuLoadKernelImage (&KernelImageHandle); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status)); return Status; } - // - // Fetch all blobs. - // - for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) { - CurrentBlob = &mKernelBlob[BlobType]; - Status = FetchBlob (CurrentBlob); - if (EFI_ERROR (Status)) { - goto FreeBlobs; - } - mTotalBlobBytes += CurrentBlob->Size; - } - KernelBlob = &mKernelBlob[KernelBlobTypeKernel]; - InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd]; - CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine]; - - if (KernelBlob->Data == NULL) { - Status = EFI_NOT_FOUND; - goto FreeBlobs; - } - - // - // Create a new handle with a single VenHw() node device path protocol on it, - // plus a custom SimpleFileSystem protocol on it. - // - FileSystemHandle = NULL; - Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle, - &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, - &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, - NULL); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n", - __FUNCTION__, Status)); - goto FreeBlobs; - } - - // - // Create a device path for the kernel image to be loaded from that will call - // back into our file system. - // - KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name); - if (KernelDevicePath == NULL) { - DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n", - __FUNCTION__)); - Status = EFI_OUT_OF_RESOURCES; - goto UninstallProtocols; - } - - // - // Load the image. This should call back into our file system. - // - Status = gBS->LoadImage ( - FALSE, // BootPolicy: exact match required - gImageHandle, // ParentImageHandle - KernelDevicePath, - NULL, // SourceBuffer - 0, // SourceSize - &KernelImageHandle - ); - if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); - if (Status != EFI_SECURITY_VIOLATION) { - goto FreeKernelDevicePath; - } - // - // From the resource allocation perspective, EFI_SECURITY_VIOLATION means - // "success", so we must roll back the image loading. - // - goto UnloadKernelImage; - } - - // - // Construct the kernel command line. - // - Status = gBS->OpenProtocol ( - KernelImageHandle, - &gEfiLoadedImageProtocolGuid, - (VOID **)&KernelLoadedImage, - gImageHandle, // AgentHandle - NULL, // ControllerHandle - EFI_OPEN_PROTOCOL_GET_PROTOCOL - ); - ASSERT_EFI_ERROR (Status); - - if (CommandLineBlob->Data == NULL) { - KernelLoadedImage->LoadOptionsSize = 0; - } else { - // - // Verify NUL-termination of the command line. - // - if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') { - DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n", - __FUNCTION__)); - Status = EFI_PROTOCOL_ERROR; - goto UnloadKernelImage; - } - - // - // Drop the terminating NUL, convert to UTF-16. - // - KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2; - } - - if (InitrdBlob->Data != NULL) { - // - // Append ' initrd=' in UTF-16. - // - KernelLoadedImage->LoadOptionsSize += - (8 + StrLen(InitrdBlob->Name)) * 2; - } - - if (KernelLoadedImage->LoadOptionsSize == 0) { - KernelLoadedImage->LoadOptions = NULL; - } else { - // - // NUL-terminate in UTF-16. - // - KernelLoadedImage->LoadOptionsSize += 2; - - KernelLoadedImage->LoadOptions = AllocatePool ( - KernelLoadedImage->LoadOptionsSize); - if (KernelLoadedImage->LoadOptions == NULL) { - KernelLoadedImage->LoadOptionsSize = 0; - Status = EFI_OUT_OF_RESOURCES; - goto UnloadKernelImage; - } - - UnicodeSPrintAsciiFormat ( - KernelLoadedImage->LoadOptions, - KernelLoadedImage->LoadOptionsSize, - "%a%a%s", - (CommandLineBlob->Data == NULL) ? "" : (CHAR8 *)CommandLineBlob->Data, - (InitrdBlob->Data == NULL) ? "" : " initrd=", - (InitrdBlob->Data == NULL) ? L"" : InitrdBlob->Name - ); - DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, - (CHAR16 *)KernelLoadedImage->LoadOptions)); - } - // // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event. // @@ -1080,41 +59,13 @@ TryRunningQemuKernel ( // // Start the image. // - Status = gBS->StartImage ( - KernelImageHandle, - NULL, // ExitDataSize - NULL // ExitData - ); + Status = QemuStartKernelImage (&KernelImageHandle); if (EFI_ERROR (Status)) { - DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status)); - } - - if (KernelLoadedImage->LoadOptions != NULL) { - FreePool (KernelLoadedImage->LoadOptions); + DEBUG ((DEBUG_ERROR, "%a: QemuStartKernelImage(): %r\n", __FUNCTION__, + Status)); } - KernelLoadedImage->LoadOptionsSize = 0; - -UnloadKernelImage: - gBS->UnloadImage (KernelImageHandle); - -FreeKernelDevicePath: - FreePool (KernelDevicePath); -UninstallProtocols: - gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle, - &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, - &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, - NULL); - -FreeBlobs: - while (BlobType > 0) { - CurrentBlob = &mKernelBlob[--BlobType]; - if (CurrentBlob->Data != NULL) { - FreePool (CurrentBlob->Data); - CurrentBlob->Size = 0; - CurrentBlob->Data = NULL; - } - } + QemuUnloadKernelImage (KernelImageHandle); return Status; } From a65c74444500ab9d8cae50d10eeaae64d50bbf18 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 22:38:50 +0100 Subject: [PATCH 07/14] OvmfPkg/QemuKernelLoaderFsDxe: don't expose kernel command line We have no need for exposing the kernel command line as a file, so remove support for that. Since the remaining blobs (kernel and initrd) are typically much larger than a page, switch to the page based allocator for blobs at the same time. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c index e4539ec2fbe5..dc86a48af378 100644 --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c @@ -30,7 +30,6 @@ typedef enum { KernelBlobTypeKernel, KernelBlobTypeInitrd, - KernelBlobTypeCommandLine, KernelBlobTypeMax } KERNEL_BLOB_TYPE; @@ -45,7 +44,6 @@ typedef struct { STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = { { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" }, { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" }, - { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" } }; STATIC UINT64 mTotalBlobBytes; @@ -865,7 +863,7 @@ FetchBlob ( // // Read blob. // - Blob->Data = AllocatePool (Blob->Size); + Blob->Data = AllocatePages (EFI_SIZE_TO_PAGES ((UINTN)Blob->Size)); if (Blob->Data == NULL) { DEBUG ((DEBUG_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n", __FUNCTION__, (INT64)Blob->Size, Blob->Name)); @@ -969,7 +967,8 @@ QemuKernelLoaderFsDxeEntrypoint ( while (BlobType > 0) { CurrentBlob = &mKernelBlob[--BlobType]; if (CurrentBlob->Data != NULL) { - FreePool (CurrentBlob->Data); + FreePages (CurrentBlob->Data, + EFI_SIZE_TO_PAGES ((UINTN)CurrentBlob->Size)); CurrentBlob->Size = 0; CurrentBlob->Data = NULL; } From 1bb8fc3c5e394126537c61dd00dc4f8c26f93fd1 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Fri, 28 Feb 2020 23:15:22 +0100 Subject: [PATCH 08/14] OvmfPkg/QemuKernelLoaderFsDxe: add support for the kernel setup block On x86, the kernel image consists of a setup block and the actual kernel, and QEMU presents these as separate blobs, whereas on disk (and in terms of PE/COFF image signing), they consist of a single image. So add support to our FS loader driver to expose files via the abstract file system that consist of up to two concatenated blobs, and redefine the kernel file so it consists of the setup and kernel blobs, on every architecture (on non-x86, the setup block is simply 0 bytes and is therefore ignored implicitly) Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../QemuKernelLoaderFsDxe.c | 74 +++++++++++++------ 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c index dc86a48af378..8ccb1983170c 100644 --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c @@ -34,16 +34,29 @@ typedef enum { } KERNEL_BLOB_TYPE; typedef struct { - FIRMWARE_CONFIG_ITEM CONST SizeKey; - FIRMWARE_CONFIG_ITEM CONST DataKey; - CONST CHAR16 * CONST Name; - UINT32 Size; - UINT8 *Data; + CONST CHAR16 Name[8]; + struct { + FIRMWARE_CONFIG_ITEM CONST SizeKey; + FIRMWARE_CONFIG_ITEM CONST DataKey; + UINT32 Size; + } FwCfgItem[2]; + UINT32 Size; + UINT8 *Data; } KERNEL_BLOB; STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = { - { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" }, - { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" }, + { + L"kernel", + { + { QemuFwCfgItemKernelSetupSize, QemuFwCfgItemKernelSetupData, }, + { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, }, + } + }, { + L"initrd", + { + { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, }, + } + } }; STATIC UINT64 mTotalBlobBytes; @@ -850,12 +863,21 @@ FetchBlob ( ) { UINT32 Left; + UINTN Idx; + UINT8 *ChunkData; // // Read blob size. // - QemuFwCfgSelectItem (Blob->SizeKey); - Blob->Size = QemuFwCfgRead32 (); + Blob->Size = 0; + for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) { + if (Blob->FwCfgItem[Idx].SizeKey == 0) { + break; + } + QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].SizeKey); + Blob->FwCfgItem[Idx].Size = QemuFwCfgRead32 (); + Blob->Size += Blob->FwCfgItem[Idx].Size; + } if (Blob->Size == 0) { return EFI_SUCCESS; } @@ -872,18 +894,28 @@ FetchBlob ( DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__, (INT64)Blob->Size, Blob->Name)); - QemuFwCfgSelectItem (Blob->DataKey); - - Left = Blob->Size; - do { - UINT32 Chunk; - - Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB; - QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left)); - Left -= Chunk; - DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n", - __FUNCTION__, (INT64)Left, Blob->Name)); - } while (Left > 0); + + ChunkData = Blob->Data; + for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) { + if (Blob->FwCfgItem[Idx].DataKey == 0) { + break; + } + QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].DataKey); + + Left = Blob->FwCfgItem[Idx].Size; + while (Left > 0) { + UINT32 Chunk; + + Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB; + QemuFwCfgReadBytes (Chunk, ChunkData + Blob->FwCfgItem[Idx].Size - Left); + Left -= Chunk; + DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\" (%d)\n", + __FUNCTION__, (INT64)Left, Blob->Name, (INT32)Idx)); + } + + ChunkData += Blob->FwCfgItem[Idx].Size; + } + return EFI_SUCCESS; } From e23fb0bd2c665bc43dd6f2aaa8d823a620c74f41 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 5 Mar 2020 11:59:57 +0100 Subject: [PATCH 09/14] OvmfPkg: create protocol and GUID header for loaded x86 Linux kernels In preparation of moving the legacy x86 loading to an implementation of the QEMU load image library class, introduce a protocol header and GUID that we will use to identify legacy loaded x86 Linux kernels in the protocol database. Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../Protocol/OvmfLoadedX86LinuxKernel.h | 32 +++++++++++ OvmfPkg/OvmfPkg.dec | 53 ++++++++++--------- 2 files changed, 59 insertions(+), 26 deletions(-) create mode 100644 OvmfPkg/Include/Protocol/OvmfLoadedX86LinuxKernel.h diff --git a/OvmfPkg/Include/Protocol/OvmfLoadedX86LinuxKernel.h b/OvmfPkg/Include/Protocol/OvmfLoadedX86LinuxKernel.h new file mode 100644 index 000000000000..01cfd9d189b4 --- /dev/null +++ b/OvmfPkg/Include/Protocol/OvmfLoadedX86LinuxKernel.h @@ -0,0 +1,32 @@ +/** @file + Protocol/GUID definition to describe a x86 Linux kernel image loaded + into memory. + + Note that this protocol is considered internal ABI, and may be change + structure at any time without regard for backward compatibility. + + Copyright (c) 2020, Arm, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef OVMF_LOADED_X86_LINUX_KERNEL_H__ +#define OVMF_LOADED_X86_LINUX_KERNEL_H__ + +#define OVMF_LOADED_X86_LINUX_KERNEL_PROTOCOL_GUID \ + {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}} + +typedef struct { + VOID *SetupBuf; + VOID *KernelBuf; + CHAR8 *CommandLine; + VOID *InitrdData; + UINTN SetupSize; + UINTN KernelInitialSize; + UINTN InitrdSize; + UINTN CommandLineSize; +} OVMF_LOADED_X86_LINUX_KERNEL; + +extern EFI_GUID gOvmfLoadedX86LinuxKernelProtocolGuid; + +#endif diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index 055caaa43041..913345d49e1f 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -80,38 +80,39 @@ XenPlatformLib|Include/Library/XenPlatformLib.h [Guids] - gUefiOvmfPkgTokenSpaceGuid = {0x93bb96af, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}} - gEfiXenInfoGuid = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}} - gOvmfPkKek1AppPrefixGuid = {0x4e32566d, 0x8e9e, 0x4f52, {0x81, 0xd3, 0x5b, 0xb9, 0x71, 0x5f, 0x97, 0x27}} - gOvmfPlatformConfigGuid = {0x7235c51c, 0x0c80, 0x4cab, {0x87, 0xac, 0x3b, 0x08, 0x4a, 0x63, 0x04, 0xb1}} - gVirtioMmioTransportGuid = {0x837dca9e, 0xe874, 0x4d82, {0xb2, 0x9a, 0x23, 0xfe, 0x0e, 0x23, 0xd1, 0xe2}} - gQemuRamfbGuid = {0x557423a1, 0x63ab, 0x406c, {0xbe, 0x7e, 0x91, 0xcd, 0xbc, 0x08, 0xc4, 0x57}} - gXenBusRootDeviceGuid = {0xa732241f, 0x383d, 0x4d9c, {0x8a, 0xe1, 0x8e, 0x09, 0x83, 0x75, 0x89, 0xd7}} - gRootBridgesConnectedEventGroupGuid = {0x24a2d66f, 0xeedd, 0x4086, {0x90, 0x42, 0xf2, 0x6e, 0x47, 0x97, 0xee, 0x69}} - gMicrosoftVendorGuid = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b}} - gEfiLegacyBiosGuid = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}} - gEfiLegacyDevOrderVariableGuid = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}} - gLinuxEfiInitrdMediaGuid = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}} - gQemuKernelLoaderFsMediaGuid = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}} + gUefiOvmfPkgTokenSpaceGuid = {0x93bb96af, 0xb9f2, 0x4eb8, {0x94, 0x62, 0xe0, 0xba, 0x74, 0x56, 0x42, 0x36}} + gEfiXenInfoGuid = {0xd3b46f3b, 0xd441, 0x1244, {0x9a, 0x12, 0x0, 0x12, 0x27, 0x3f, 0xc1, 0x4d}} + gOvmfPkKek1AppPrefixGuid = {0x4e32566d, 0x8e9e, 0x4f52, {0x81, 0xd3, 0x5b, 0xb9, 0x71, 0x5f, 0x97, 0x27}} + gOvmfPlatformConfigGuid = {0x7235c51c, 0x0c80, 0x4cab, {0x87, 0xac, 0x3b, 0x08, 0x4a, 0x63, 0x04, 0xb1}} + gVirtioMmioTransportGuid = {0x837dca9e, 0xe874, 0x4d82, {0xb2, 0x9a, 0x23, 0xfe, 0x0e, 0x23, 0xd1, 0xe2}} + gQemuRamfbGuid = {0x557423a1, 0x63ab, 0x406c, {0xbe, 0x7e, 0x91, 0xcd, 0xbc, 0x08, 0xc4, 0x57}} + gXenBusRootDeviceGuid = {0xa732241f, 0x383d, 0x4d9c, {0x8a, 0xe1, 0x8e, 0x09, 0x83, 0x75, 0x89, 0xd7}} + gRootBridgesConnectedEventGroupGuid = {0x24a2d66f, 0xeedd, 0x4086, {0x90, 0x42, 0xf2, 0x6e, 0x47, 0x97, 0xee, 0x69}} + gMicrosoftVendorGuid = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b}} + gEfiLegacyBiosGuid = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}} + gEfiLegacyDevOrderVariableGuid = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}} + gLinuxEfiInitrdMediaGuid = {0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68}} + gQemuKernelLoaderFsMediaGuid = {0x1428f772, 0xb64a, 0x441e, {0xb8, 0xc3, 0x9e, 0xbd, 0xd7, 0xf8, 0x93, 0xc7}} [Ppis] # PPI whose presence in the PPI database signals that the TPM base address # has been discovered and recorded - gOvmfTpmDiscoveredPpiGuid = {0xb9a61ad0, 0x2802, 0x41f3, {0xb5, 0x13, 0x96, 0x51, 0xce, 0x6b, 0xd5, 0x75}} + gOvmfTpmDiscoveredPpiGuid = {0xb9a61ad0, 0x2802, 0x41f3, {0xb5, 0x13, 0x96, 0x51, 0xce, 0x6b, 0xd5, 0x75}} [Protocols] - gVirtioDeviceProtocolGuid = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}} - gXenBusProtocolGuid = {0x3d3ca290, 0xb9a5, 0x11e3, {0xb7, 0x5d, 0xb8, 0xac, 0x6f, 0x7d, 0x65, 0xe6}} - gXenIoProtocolGuid = {0x6efac84f, 0x0ab0, 0x4747, {0x81, 0xbe, 0x85, 0x55, 0x62, 0x59, 0x04, 0x49}} - gIoMmuAbsentProtocolGuid = {0xf8775d50, 0x8abd, 0x4adf, {0x92, 0xac, 0x85, 0x3e, 0x51, 0xf6, 0xc8, 0xdc}} - gEfiLegacy8259ProtocolGuid = {0x38321dba, 0x4fe0, 0x4e17, {0x8a, 0xec, 0x41, 0x30, 0x55, 0xea, 0xed, 0xc1}} - gEfiFirmwareVolumeProtocolGuid = {0x389F751F, 0x1838, 0x4388, {0x83, 0x90, 0xcd, 0x81, 0x54, 0xbd, 0x27, 0xf8}} - gEfiIsaAcpiProtocolGuid = {0x64a892dc, 0x5561, 0x4536, {0x92, 0xc7, 0x79, 0x9b, 0xfc, 0x18, 0x33, 0x55}} - gEfiIsaIoProtocolGuid = {0x7ee2bd44, 0x3da0, 0x11d4, {0x9a, 0x38, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d}} - gEfiLegacyBiosProtocolGuid = {0xdb9a1e3d, 0x45cb, 0x4abb, {0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d}} - gEfiLegacyBiosPlatformProtocolGuid = {0x783658a3, 0x4172, 0x4421, {0xa2, 0x99, 0xe0, 0x09, 0x07, 0x9c, 0x0c, 0xb4}} - gEfiLegacyInterruptProtocolGuid = {0x31ce593d, 0x108a, 0x485d, {0xad, 0xb2, 0x78, 0xf2, 0x1f, 0x29, 0x66, 0xbe}} - gEfiVgaMiniPortProtocolGuid = {0xc7735a2f, 0x88f5, 0x4882, {0xae, 0x63, 0xfa, 0xac, 0x8c, 0x8b, 0x86, 0xb3}} + gVirtioDeviceProtocolGuid = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}} + gXenBusProtocolGuid = {0x3d3ca290, 0xb9a5, 0x11e3, {0xb7, 0x5d, 0xb8, 0xac, 0x6f, 0x7d, 0x65, 0xe6}} + gXenIoProtocolGuid = {0x6efac84f, 0x0ab0, 0x4747, {0x81, 0xbe, 0x85, 0x55, 0x62, 0x59, 0x04, 0x49}} + gIoMmuAbsentProtocolGuid = {0xf8775d50, 0x8abd, 0x4adf, {0x92, 0xac, 0x85, 0x3e, 0x51, 0xf6, 0xc8, 0xdc}} + gEfiLegacy8259ProtocolGuid = {0x38321dba, 0x4fe0, 0x4e17, {0x8a, 0xec, 0x41, 0x30, 0x55, 0xea, 0xed, 0xc1}} + gEfiFirmwareVolumeProtocolGuid = {0x389F751F, 0x1838, 0x4388, {0x83, 0x90, 0xcd, 0x81, 0x54, 0xbd, 0x27, 0xf8}} + gEfiIsaAcpiProtocolGuid = {0x64a892dc, 0x5561, 0x4536, {0x92, 0xc7, 0x79, 0x9b, 0xfc, 0x18, 0x33, 0x55}} + gEfiIsaIoProtocolGuid = {0x7ee2bd44, 0x3da0, 0x11d4, {0x9a, 0x38, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d}} + gEfiLegacyBiosProtocolGuid = {0xdb9a1e3d, 0x45cb, 0x4abb, {0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d}} + gEfiLegacyBiosPlatformProtocolGuid = {0x783658a3, 0x4172, 0x4421, {0xa2, 0x99, 0xe0, 0x09, 0x07, 0x9c, 0x0c, 0xb4}} + gEfiLegacyInterruptProtocolGuid = {0x31ce593d, 0x108a, 0x485d, {0xad, 0xb2, 0x78, 0xf2, 0x1f, 0x29, 0x66, 0xbe}} + gEfiVgaMiniPortProtocolGuid = {0xc7735a2f, 0x88f5, 0x4882, {0xae, 0x63, 0xfa, 0xac, 0x8c, 0x8b, 0x86, 0xb3}} + gOvmfLoadedX86LinuxKernelProtocolGuid = {0xa3edc05d, 0xb618, 0x4ff6, {0x95, 0x52, 0x76, 0xd7, 0x88, 0x63, 0x43, 0xc8}} [PcdsFixedAtBuild] gUefiOvmfPkgTokenSpaceGuid.PcdOvmfPeiMemFvBase|0x0|UINT32|0 From c5ecf100ade147e5dffc50831eb19240515c5550 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 29 Feb 2020 01:28:45 +0100 Subject: [PATCH 10/14] OvmfPkg: implement QEMU loader library for X86 with legacy fallback Implement another version of QemuLoadImageLib that uses LoadImage and StartImage, but falls back to the legacy Linux loader code if that fails. The logic in the legacy fallback routines is identical to the current QEMU linux loader for X64 and IA32. Note the use of the OVMF_LOADED_X86_LINUX_KERNEL protocol for the legacy loaded image: this makes it possible to expose the LoadImage/StartImage abstraction for the legacy loader, using the EFI paradigm of identifying a loaded image solely by a handle. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../X86QemuLoadImageLib/X86QemuLoadImageLib.c | 567 ++++++++++++++++++ .../X86QemuLoadImageLib.inf | 42 ++ 2 files changed, 609 insertions(+) create mode 100644 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c create mode 100644 OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c new file mode 100644 index 000000000000..c5bd6862b265 --- /dev/null +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c @@ -0,0 +1,567 @@ +/** @file + X86 specific implementation of QemuLoadImageLib library class interface + with support for loading mixed mode images and non-EFI stub images + + Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.
+ Copyright (c) 2020, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#pragma pack (1) +typedef struct { + EFI_DEVICE_PATH_PROTOCOL FilePathHeader; + CHAR16 FilePath[ARRAY_SIZE (L"kernel")]; +} KERNEL_FILE_DEVPATH; + +typedef struct { + VENDOR_DEVICE_PATH VenMediaNode; + KERNEL_FILE_DEVPATH FileNode; + EFI_DEVICE_PATH_PROTOCOL EndNode; +} KERNEL_VENMEDIA_FILE_DEVPATH; +#pragma pack () + +STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = { + { + { + MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, + { sizeof (VENDOR_DEVICE_PATH) } + }, + QEMU_KERNEL_LOADER_FS_MEDIA_GUID + }, { + { + MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP, + { sizeof (KERNEL_FILE_DEVPATH) } + }, + L"kernel", + }, { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL) } + } +}; + +STATIC +VOID +FreeLegacyImage ( + IN OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage + ) +{ + if (LoadedImage->SetupBuf != NULL) { + FreePages (LoadedImage->SetupBuf, + EFI_SIZE_TO_PAGES (LoadedImage->SetupSize)); + } + if (LoadedImage->KernelBuf != NULL) { + FreePages (LoadedImage->KernelBuf, + EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)); + } + if (LoadedImage->CommandLine != NULL) { + FreePages (LoadedImage->CommandLine, + EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize)); + } + if (LoadedImage->InitrdData != NULL) { + FreePages (LoadedImage->InitrdData, + EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize)); + } +} + +STATIC +EFI_STATUS +QemuLoadLegacyImage ( + OUT EFI_HANDLE *ImageHandle + ) +{ + EFI_STATUS Status; + UINTN KernelSize; + UINTN SetupSize; + OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage; + + QemuFwCfgSelectItem (QemuFwCfgItemKernelSize); + KernelSize = (UINTN)QemuFwCfgRead32 (); + + QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize); + SetupSize = (UINTN)QemuFwCfgRead32 (); + + if (KernelSize == 0 || SetupSize == 0) { + DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n")); + return EFI_NOT_FOUND; + } + + LoadedImage = AllocateZeroPool (sizeof (*LoadedImage)); + if (LoadedImage == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + LoadedImage->SetupSize = SetupSize; + LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages ( + EFI_SIZE_TO_PAGES (LoadedImage->SetupSize)); + if (LoadedImage->SetupBuf == NULL) { + DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n")); + Status = EFI_OUT_OF_RESOURCES; + goto FreeImageDesc; + } + + DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize)); + DEBUG ((DEBUG_INFO, "Reading kernel setup image ...")); + QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData); + QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf); + DEBUG ((DEBUG_INFO, " [done]\n")); + + Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf, + LoadedImage->SetupSize); + if (EFI_ERROR (Status)) { + goto FreeImage; + } + + Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf); + if (EFI_ERROR (Status)) { + goto FreeImage; + } + + LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize ( + LoadedImage->SetupBuf, KernelSize); + if (LoadedImage->KernelInitialSize == 0) { + Status = EFI_UNSUPPORTED; + goto FreeImage; + } + + LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages ( + LoadedImage->SetupBuf, + EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize) + ); + if (LoadedImage->KernelBuf == NULL) { + DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n")); + Status = EFI_OUT_OF_RESOURCES; + goto FreeImage; + } + + DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize)); + DEBUG ((DEBUG_INFO, "Reading kernel image ...")); + QemuFwCfgSelectItem (QemuFwCfgItemKernelData); + QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf); + DEBUG ((DEBUG_INFO, " [done]\n")); + + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); + LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead32 (); + + if (LoadedImage->CommandLineSize > 0) { + LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages ( + EFI_SIZE_TO_PAGES ( + LoadedImage->CommandLineSize)); + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); + QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine); + } + + Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf, + LoadedImage->CommandLine); + if (EFI_ERROR (Status)) { + goto FreeImage; + } + + QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); + LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 (); + + if (LoadedImage->InitrdSize > 0) { + LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages ( + LoadedImage->SetupBuf, + EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize)); + DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n", + (UINT32)LoadedImage->InitrdSize)); + DEBUG ((DEBUG_INFO, "Reading initrd image ...")); + QemuFwCfgSelectItem (QemuFwCfgItemInitrdData); + QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData); + DEBUG ((DEBUG_INFO, " [done]\n")); + } + + Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData, + LoadedImage->InitrdSize); + if (EFI_ERROR (Status)) { + goto FreeImage; + } + + *ImageHandle = NULL; + Status = gBS->InstallProtocolInterface (ImageHandle, + &gOvmfLoadedX86LinuxKernelProtocolGuid, EFI_NATIVE_INTERFACE, + LoadedImage); + if (EFI_ERROR (Status)) { + goto FreeImage; + } + return EFI_SUCCESS; + +FreeImage: + FreeLegacyImage (LoadedImage); +FreeImageDesc: + FreePool (LoadedImage); + return Status; +} + +STATIC +EFI_STATUS +QemuStartLegacyImage ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage; + + Status = gBS->OpenProtocol ( + ImageHandle, + &gOvmfLoadedX86LinuxKernelProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf); +} + +STATIC +EFI_STATUS +QemuUnloadLegacyImage ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_STATUS Status; + OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage; + + Status = gBS->OpenProtocol ( + ImageHandle, + &gOvmfLoadedX86LinuxKernelProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + Status = gBS->UninstallProtocolInterface (ImageHandle, + &gOvmfLoadedX86LinuxKernelProtocolGuid, LoadedImage); + ASSERT_EFI_ERROR (Status); + + FreeLegacyImage (LoadedImage); + FreePool (LoadedImage); + return EFI_SUCCESS; +} + +/** + Download the kernel, the initial ramdisk, and the kernel command line from + QEMU's fw_cfg. The kernel will be instructed via its command line to load + the initrd from the same Simple FileSystem where the kernel was loaded from. + + @param[out] ImageHandle The image handle that was allocated for + loading the image + + @retval EFI_SUCCESS The image was loaded successfully. + @retval EFI_NOT_FOUND Kernel image was not found. + @retval EFI_OUT_OF_RESOURCES Memory allocation failed. + @retval EFI_PROTOCOL_ERROR Unterminated kernel command line. + + @return Error codes from any of the underlying + functions. +**/ +EFI_STATUS +EFIAPI +QemuLoadKernelImage ( + OUT EFI_HANDLE *ImageHandle + ) +{ + EFI_STATUS Status; + EFI_HANDLE KernelImageHandle; + EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; + UINTN CommandLineSize; + CHAR8 *CommandLine; + UINTN InitrdSize; + + // + // Load the image. This should call back into the QEMU EFI loader file system. + // + Status = gBS->LoadImage ( + FALSE, // BootPolicy: exact match required + gImageHandle, // ParentImageHandle + (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath, + NULL, // SourceBuffer + 0, // SourceSize + &KernelImageHandle + ); + switch (Status) { + case EFI_SUCCESS: + break; + + case EFI_NOT_FOUND: + // + // The image does not exist - no -kernel image was supplied via the + // command line so no point in invoking the legacy fallback + // + return EFI_NOT_FOUND; + + case EFI_SECURITY_VIOLATION: + // + // We are running with UEFI secure boot enabled, and the image failed to + // authenticate. For compatibility reasons, we fall back to the legacy + // loader in this case. Since the image has been loaded, we need to unload + // it before proceeding + // + gBS->UnloadImage (KernelImageHandle); + // + // Fall through + // + case EFI_UNSUPPORTED: + // + // The image is not natively supported or cross-type supported. Let's try + // loading it using the loader that parses the bzImage metadata directly. + // + Status = QemuLoadLegacyImage (&KernelImageHandle); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__, + Status)); + return Status; + } + *ImageHandle = KernelImageHandle; + return EFI_SUCCESS; + + default: + DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); + return Status; + } + + // + // Construct the kernel command line. + // + Status = gBS->OpenProtocol ( + KernelImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&KernelLoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + ASSERT_EFI_ERROR (Status); + + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); + CommandLineSize = (UINTN)QemuFwCfgRead32 (); + + if (CommandLineSize == 0) { + KernelLoadedImage->LoadOptionsSize = 0; + } else { + CommandLine = AllocatePool (CommandLineSize); + if (CommandLine == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto UnloadImage; + } + + QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); + QemuFwCfgReadBytes (CommandLineSize, CommandLine); + + // + // Verify NUL-termination of the command line. + // + if (CommandLine[CommandLineSize - 1] != '\0') { + DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n", + __FUNCTION__)); + Status = EFI_PROTOCOL_ERROR; + goto FreeCommandLine; + } + + // + // Drop the terminating NUL, convert to UTF-16. + // + KernelLoadedImage->LoadOptionsSize = (CommandLineSize - 1) * 2; + } + + QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); + InitrdSize = (UINTN)QemuFwCfgRead32 (); + + if (InitrdSize > 0) { + // + // Append ' initrd=initrd' in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2; + } + + if (KernelLoadedImage->LoadOptionsSize == 0) { + KernelLoadedImage->LoadOptions = NULL; + } else { + // + // NUL-terminate in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += 2; + + KernelLoadedImage->LoadOptions = AllocatePool ( + KernelLoadedImage->LoadOptionsSize); + if (KernelLoadedImage->LoadOptions == NULL) { + KernelLoadedImage->LoadOptionsSize = 0; + Status = EFI_OUT_OF_RESOURCES; + goto FreeCommandLine; + } + + UnicodeSPrintAsciiFormat ( + KernelLoadedImage->LoadOptions, + KernelLoadedImage->LoadOptionsSize, + "%a%a", + (CommandLineSize == 0) ? "" : CommandLine, + (InitrdSize == 0) ? "" : " initrd=initrd" + ); + DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, + (CHAR16 *)KernelLoadedImage->LoadOptions)); + } + + *ImageHandle = KernelImageHandle; + return EFI_SUCCESS; + +FreeCommandLine: + if (CommandLineSize > 0) { + FreePool (CommandLine); + } +UnloadImage: + gBS->UnloadImage (KernelImageHandle); + + return Status; +} + +/** + Transfer control to a kernel image loaded with QemuLoadKernelImage () + + @param[in,out] ImageHandle Handle of image to be started. May assume a + different value on return if the image was + reloaded. + + @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle + or the image has already been initialized with + StartImage + @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the + image should not be started. + + @return Error codes returned by the started image +**/ +EFI_STATUS +EFIAPI +QemuStartKernelImage ( + IN OUT EFI_HANDLE *ImageHandle + ) +{ + EFI_STATUS Status; + OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage; + EFI_HANDLE KernelImageHandle; + + Status = gBS->OpenProtocol ( + *ImageHandle, + &gOvmfLoadedX86LinuxKernelProtocolGuid, + (VOID **)&LoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (!EFI_ERROR (Status)) { + return QemuStartLegacyImage (*ImageHandle); + } + + Status = gBS->StartImage ( + *ImageHandle, + NULL, // ExitDataSize + NULL // ExitData + ); +#ifdef MDE_CPU_IA32 + if (Status == EFI_UNSUPPORTED) { + // + // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while + // we are expecting a IA32 one, and the StartImage () boot service is unable + // to handle it, either because the image does not have the special .compat + // PE/COFF section that Linux specifies for mixed mode capable images, or + // because we are running without the support code for that. So load the + // image again, using the legacy loader, and unload the normally loaded + // image before starting the legacy one. + // + Status = QemuLoadLegacyImage (&KernelImageHandle); + if (EFI_ERROR (Status)) { + // + // Note: no change to (*ImageHandle), the caller will release it. + // + return Status; + } + // + // Swap in the legacy-loaded image. + // + QemuUnloadKernelImage (*ImageHandle); + *ImageHandle = KernelImageHandle; + return QemuStartLegacyImage (KernelImageHandle); + } +#endif + return Status; +} + +/** + Unloads an image loaded with QemuLoadKernelImage (). + + @param ImageHandle Handle that identifies the image to be + unloaded. + + @retval EFI_SUCCESS The image has been unloaded. + @retval EFI_UNSUPPORTED The image has been started, and does not + support unload. + @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle. + + @return Exit code from the image's unload function. +**/ +EFI_STATUS +EFIAPI +QemuUnloadKernelImage ( + IN EFI_HANDLE ImageHandle + ) +{ + EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage; + EFI_STATUS Status; + + Status = gBS->OpenProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&KernelLoadedImage, + gImageHandle, // AgentHandle + NULL, // ControllerHandle + EFI_OPEN_PROTOCOL_GET_PROTOCOL + ); + if (Status == EFI_UNSUPPORTED) { + // + // The handle exists but does not have an instance of the standard loaded + // image protocol installed on it. Attempt to unload it as a legacy image + // instead. + // + return QemuUnloadLegacyImage (ImageHandle); + } + + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + // + // We are unloading a normal, non-legacy loaded image, either on behalf of + // an external caller, or called from QemuStartKernelImage() on IA32, while + // switching from the normal to the legacy method to load and start a X64 + // image. + // + if (KernelLoadedImage->LoadOptions != NULL) { + FreePool (KernelLoadedImage->LoadOptions); + KernelLoadedImage->LoadOptions = NULL; + } + KernelLoadedImage->LoadOptionsSize = 0; + + return gBS->UnloadImage (ImageHandle); +} diff --git a/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf new file mode 100644 index 000000000000..e1615badd2ba --- /dev/null +++ b/OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf @@ -0,0 +1,42 @@ +## @file +# X86 specific implementation of QemuLoadImageLib library class interface +# with support for loading mixed mode images and non-EFI stub images +# +# Copyright (c) 2020, ARM Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = X86QemuLoadImageLib + FILE_GUID = 2304df80-e21d-4170-9c3c-113c878f7ac0 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = QemuLoadImageLib|DXE_DRIVER + +[Sources] + X86QemuLoadImageLib.c + +[Packages] + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + DebugLib + MemoryAllocationLib + LoadLinuxLib + PrintLib + QemuFwCfgLib + ReportStatusCodeLib + UefiBootServicesTableLib + +[Protocols] + gEfiDevicePathProtocolGuid + gEfiLoadedImageProtocolGuid + gOvmfLoadedX86LinuxKernelProtocolGuid + +[Guids] + gQemuKernelLoaderFsMediaGuid From 62ba8a7fe044ffad9e9144125baaa87a5b45988c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 29 Feb 2020 10:31:34 +0100 Subject: [PATCH 11/14] OvmfPkg: add new QEMU kernel image loader components Add the components that expose the QEMU abstract loader file system so that we can switch over our PlatformBmLib over to it in a subsequent patch. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- OvmfPkg/OvmfPkgIa32.dsc | 2 ++ OvmfPkg/OvmfPkgIa32.fdf | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 2 ++ OvmfPkg/OvmfPkgIa32X64.fdf | 1 + OvmfPkg/OvmfPkgX64.dsc | 2 ++ OvmfPkg/OvmfPkgX64.fdf | 1 + 6 files changed, 9 insertions(+) diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 53f503b3ed36..ec21d2f3f6cb 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -363,6 +363,7 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf + QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf @@ -716,6 +717,7 @@ NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf !endif } + OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf OvmfPkg/Virtio10Dxe/Virtio10.inf OvmfPkg/VirtioBlkDxe/VirtioBlk.inf diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index 56e3ab3df25a..0e98a4dbd24a 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -243,6 +243,7 @@ INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf INF MdeModulePkg/Application/UiApp/UiApp.inf +INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf INF MdeModulePkg/Universal/PrintDxe/PrintDxe.inf INF MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index d907465752a6..3c80a18c6086 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -367,6 +367,7 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf + QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf @@ -728,6 +729,7 @@ NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf !endif } + OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf OvmfPkg/Virtio10Dxe/Virtio10.inf OvmfPkg/VirtioBlkDxe/VirtioBlk.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index add188723f3e..1dd431d72113 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -244,6 +244,7 @@ INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf INF MdeModulePkg/Application/UiApp/UiApp.inf +INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf INF MdeModulePkg/Universal/PrintDxe/PrintDxe.inf INF MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 0c6582a9ed37..63f1f935f4f3 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -367,6 +367,7 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf + QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf @@ -726,6 +727,7 @@ NULL|OvmfPkg/Csm/LegacyBootMaintUiLib/LegacyBootMaintUiLib.inf !endif } + OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf OvmfPkg/VirtioPciDeviceDxe/VirtioPciDeviceDxe.inf OvmfPkg/Virtio10Dxe/Virtio10.inf OvmfPkg/VirtioBlkDxe/VirtioBlk.inf diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index add188723f3e..1dd431d72113 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -244,6 +244,7 @@ INF MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf INF MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf INF MdeModulePkg/Universal/BdsDxe/BdsDxe.inf INF MdeModulePkg/Application/UiApp/UiApp.inf +INF OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf INF MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf INF MdeModulePkg/Universal/PrintDxe/PrintDxe.inf INF MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf From 657e3d496a0e3430f53cc604a6e8bba9124b6c3c Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 29 Feb 2020 10:33:21 +0100 Subject: [PATCH 12/14] OvmfPkg/PlatformBootManagerLib: switch to QemuLoadImageLib Replace the open coded sequence to load Linux on x86 with a short and generic sequence invoking QemuLoadImageLib, which can be provided by a generic version that only supports the LoadImage and StartImage boot services, and one that incorporates the entire legacy loading sequence as well. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../PlatformBootManagerLib.inf | 2 +- .../PlatformBootManagerLib/QemuKernel.c | 144 ++---------------- 2 files changed, 14 insertions(+), 132 deletions(-) diff --git a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf index c479f113b92b..e470b9a6a3e5 100644 --- a/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf +++ b/OvmfPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -49,7 +49,7 @@ NvVarsFileLib QemuFwCfgLib QemuFwCfgS3Lib - LoadLinuxLib + QemuLoadImageLib QemuBootOrderLib ReportStatusCodeLib UefiLib diff --git a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c index ddfef925edd3..c6255921779e 100644 --- a/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c +++ b/OvmfPkg/Library/PlatformBootManagerLib/QemuKernel.c @@ -9,11 +9,8 @@ #include #include -#include -#include -#include +#include #include -#include #include @@ -23,120 +20,11 @@ TryRunningQemuKernel ( ) { EFI_STATUS Status; - UINTN KernelSize; - UINTN KernelInitialSize; - VOID *KernelBuf; - UINTN SetupSize; - VOID *SetupBuf; - UINTN CommandLineSize; - CHAR8 *CommandLine; - UINTN InitrdSize; - VOID* InitrdData; + EFI_HANDLE KernelImageHandle; - SetupBuf = NULL; - SetupSize = 0; - KernelBuf = NULL; - KernelInitialSize = 0; - CommandLine = NULL; - CommandLineSize = 0; - InitrdData = NULL; - InitrdSize = 0; - - if (!QemuFwCfgIsAvailable ()) { - return EFI_NOT_FOUND; - } - - QemuFwCfgSelectItem (QemuFwCfgItemKernelSize); - KernelSize = (UINTN) QemuFwCfgRead64 (); - - QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize); - SetupSize = (UINTN) QemuFwCfgRead64 (); - - if (KernelSize == 0 || SetupSize == 0) { - DEBUG ((EFI_D_INFO, "qemu -kernel was not used.\n")); - return EFI_NOT_FOUND; - } - - SetupBuf = LoadLinuxAllocateKernelSetupPages (EFI_SIZE_TO_PAGES (SetupSize)); - if (SetupBuf == NULL) { - DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel setup!\n")); - return EFI_OUT_OF_RESOURCES; - } - - DEBUG ((EFI_D_INFO, "Setup size: 0x%x\n", (UINT32) SetupSize)); - DEBUG ((EFI_D_INFO, "Reading kernel setup image ...")); - QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData); - QemuFwCfgReadBytes (SetupSize, SetupBuf); - DEBUG ((EFI_D_INFO, " [done]\n")); - - Status = LoadLinuxCheckKernelSetup (SetupBuf, SetupSize); + Status = QemuLoadKernelImage (&KernelImageHandle); if (EFI_ERROR (Status)) { - goto FreeAndReturn; - } - - Status = LoadLinuxInitializeKernelSetup (SetupBuf); - if (EFI_ERROR (Status)) { - goto FreeAndReturn; - } - - KernelInitialSize = LoadLinuxGetKernelSize (SetupBuf, KernelSize); - if (KernelInitialSize == 0) { - Status = EFI_UNSUPPORTED; - goto FreeAndReturn; - } - - KernelBuf = LoadLinuxAllocateKernelPages ( - SetupBuf, - EFI_SIZE_TO_PAGES (KernelInitialSize)); - if (KernelBuf == NULL) { - DEBUG ((EFI_D_ERROR, "Unable to allocate memory for kernel!\n")); - Status = EFI_OUT_OF_RESOURCES; - goto FreeAndReturn; - } - - DEBUG ((EFI_D_INFO, "Kernel size: 0x%x\n", (UINT32) KernelSize)); - DEBUG ((EFI_D_INFO, "Reading kernel image ...")); - QemuFwCfgSelectItem (QemuFwCfgItemKernelData); - QemuFwCfgReadBytes (KernelSize, KernelBuf); - DEBUG ((EFI_D_INFO, " [done]\n")); - - QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize); - CommandLineSize = (UINTN) QemuFwCfgRead64 (); - - if (CommandLineSize > 0) { - CommandLine = LoadLinuxAllocateCommandLinePages ( - EFI_SIZE_TO_PAGES (CommandLineSize)); - QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData); - QemuFwCfgReadBytes (CommandLineSize, CommandLine); - } else { - CommandLine = NULL; - } - - Status = LoadLinuxSetCommandLine (SetupBuf, CommandLine); - if (EFI_ERROR (Status)) { - goto FreeAndReturn; - } - - QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize); - InitrdSize = (UINTN) QemuFwCfgRead64 (); - - if (InitrdSize > 0) { - InitrdData = LoadLinuxAllocateInitrdPages ( - SetupBuf, - EFI_SIZE_TO_PAGES (InitrdSize) - ); - DEBUG ((EFI_D_INFO, "Initrd size: 0x%x\n", (UINT32) InitrdSize)); - DEBUG ((EFI_D_INFO, "Reading initrd image ...")); - QemuFwCfgSelectItem (QemuFwCfgItemInitrdData); - QemuFwCfgReadBytes (InitrdSize, InitrdData); - DEBUG ((EFI_D_INFO, " [done]\n")); - } else { - InitrdData = NULL; - } - - Status = LoadLinuxSetInitrd (SetupBuf, InitrdData, InitrdSize); - if (EFI_ERROR (Status)) { - goto FreeAndReturn; + return Status; } // @@ -147,22 +35,16 @@ TryRunningQemuKernel ( REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT)); - Status = LoadLinux (KernelBuf, SetupBuf); - -FreeAndReturn: - if (SetupBuf != NULL) { - FreePages (SetupBuf, EFI_SIZE_TO_PAGES (SetupSize)); - } - if (KernelBuf != NULL) { - FreePages (KernelBuf, EFI_SIZE_TO_PAGES (KernelInitialSize)); - } - if (CommandLine != NULL) { - FreePages (CommandLine, EFI_SIZE_TO_PAGES (CommandLineSize)); - } - if (InitrdData != NULL) { - FreePages (InitrdData, EFI_SIZE_TO_PAGES (InitrdSize)); + // + // Start the image. + // + Status = QemuStartKernelImage (&KernelImageHandle); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: QemuStartKernelImage(): %r\n", __FUNCTION__, + Status)); } + QemuUnloadKernelImage (KernelImageHandle); + return Status; } - From e1797cf3525fa0f7a00a82bab9075dcf9e19c6a4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 29 Feb 2020 13:59:52 +0100 Subject: [PATCH 13/14] OvmfPkg/QemuKernelLoaderFsDxe: add support for new Linux initrd device path Linux v5.7 will introduce a new method to load the initial ramdisk (initrd) from the loader, using the LoadFile2 protocol installed on a special vendor GUIDed media device path. Add support for this to our QEMU command line kernel/initrd loader. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- .../QemuKernelLoaderFsDxe.c | 79 +++++++++++++++++++ .../QemuKernelLoaderFsDxe.inf | 2 + 2 files changed, 81 insertions(+) diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c index 8ccb1983170c..869549f164f0 100644 --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c @@ -13,15 +13,18 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include #include #include +#include #include // @@ -84,6 +87,19 @@ STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mFileSystemDevicePath = { } }; +STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mInitrdDevicePath = { + { + { + MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, + { sizeof (VENDOR_DEVICE_PATH) } + }, + LINUX_EFI_INITRD_MEDIA_GUID + }, { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL) } + } +}; + // // The "file in the EFI stub filesystem" abstraction. // @@ -839,6 +855,48 @@ STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = { StubFileSystemOpenVolume }; +STATIC +EFI_STATUS +EFIAPI +InitrdLoadFile2 ( + IN EFI_LOAD_FILE2_PROTOCOL *This, + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN BOOLEAN BootPolicy, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer OPTIONAL + ) +{ + CONST KERNEL_BLOB *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd]; + + ASSERT (InitrdBlob->Size > 0); + + if (BootPolicy) { + return EFI_UNSUPPORTED; + } + + if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) { + return EFI_INVALID_PARAMETER; + } + + if (FilePath->Type != END_DEVICE_PATH_TYPE || + FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) { + return EFI_NOT_FOUND; + } + + if (Buffer == NULL || *BufferSize < InitrdBlob->Size) { + *BufferSize = InitrdBlob->Size; + return EFI_BUFFER_TOO_SMALL; + } + + CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size); + + *BufferSize = InitrdBlob->Size; + return EFI_SUCCESS; +} + +STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = { + InitrdLoadFile2, +}; // // Utility functions. @@ -949,6 +1007,7 @@ QemuKernelLoaderFsDxeEntrypoint ( KERNEL_BLOB *KernelBlob; EFI_STATUS Status; EFI_HANDLE FileSystemHandle; + EFI_HANDLE InitrdLoadFile2Handle; if (!QemuFwCfgIsAvailable ()) { return EFI_NOT_FOUND; @@ -993,8 +1052,28 @@ QemuKernelLoaderFsDxeEntrypoint ( goto FreeBlobs; } + if (KernelBlob[KernelBlobTypeInitrd].Size > 0) { + InitrdLoadFile2Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle, + &gEfiDevicePathProtocolGuid, &mInitrdDevicePath, + &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n", + __FUNCTION__, Status)); + goto UninstallFileSystemHandle; + } + } + return EFI_SUCCESS; +UninstallFileSystemHandle: + Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle, + &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, + &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, + NULL); + ASSERT_EFI_ERROR (Status); + FreeBlobs: while (BlobType > 0) { CurrentBlob = &mKernelBlob[--BlobType]; diff --git a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf index 5ba88063def0..7b35adb8e034 100644 --- a/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf +++ b/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf @@ -28,6 +28,7 @@ BaseLib BaseMemoryLib DebugLib + DevicePathLib MemoryAllocationLib QemuFwCfgLib UefiBootServicesTableLib @@ -42,6 +43,7 @@ [Protocols] gEfiDevicePathProtocolGuid ## PRODUCES + gEfiLoadFile2ProtocolGuid ## PRODUCES gEfiSimpleFileSystemProtocolGuid ## PRODUCES [Depex] From dce71a3af7fef4df0b30ae80508b4b67906e8d9a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Sat, 29 Feb 2020 10:42:43 +0100 Subject: [PATCH 14/14] OvmfPkg: use generic QEMU image loader for secure boot enabled builds The QemuLoadImageLib implementation we currently use for all OVMF builds copies the behavior of the QEMU loader code that precedes it, which is to disregard UEFI secure boot policies entirely when it comes to loading kernel images that have been specified on the QEMU command line. This behavior deviates from ArmVirtQemu based builds, which do take UEFI secure boot policies into account, and refuse to load images from the command line that cannot be authenticated. The disparity was originally due to the fact that the QEMU command line kernel loader did not use LoadImage and StartImage at all, but this changed recently, and now, there are only a couple of reasons left to stick with the legacy loader: - it permits loading images that lack a valid PE/COFF header, - it permits loading X64 kernels on IA32 firmware running on a X64 capable system. Since every non-authentic PE/COFF image can trivially be converted into an image that lacks a valid PE/COFF header, the former case can simply not be supported in a UEFI secure boot context. The latter case is highly theoretical, given that one could easily switch to native X64 firmware in a VM scenario. That leaves us with little justification to use the legacy loader at all when UEFI secure boot policies are in effect, so let's switch to the generic loader for UEFI secure boot enabled builds. Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=2566 Signed-off-by: Ard Biesheuvel Reviewed-by: Laszlo Ersek --- OvmfPkg/OvmfPkgIa32.dsc | 4 ++++ OvmfPkg/OvmfPkgIa32X64.dsc | 4 ++++ OvmfPkg/OvmfPkgX64.dsc | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index ec21d2f3f6cb..8916255df4df 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -363,7 +363,11 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf +!if $(SECURE_BOOT_ENABLE) == TRUE + QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf +!else QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf +!endif !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 3c80a18c6086..342ff96cc279 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -367,7 +367,11 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf +!if $(SECURE_BOOT_ENABLE) == TRUE + QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf +!else QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf +!endif !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 63f1f935f4f3..1fb2de5e0121 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -367,7 +367,11 @@ PciLib|OvmfPkg/Library/DxePciLibI440FxQ35/DxePciLibI440FxQ35.inf MpInitLib|UefiCpuPkg/Library/MpInitLib/DxeMpInitLib.inf QemuFwCfgS3Lib|OvmfPkg/Library/QemuFwCfgS3Lib/DxeQemuFwCfgS3LibFwCfg.inf +!if $(SECURE_BOOT_ENABLE) == TRUE + QemuLoadImageLib|OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.inf +!else QemuLoadImageLib|OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.inf +!endif !if $(TPM_ENABLE) == TRUE Tpm12DeviceLib|SecurityPkg/Library/Tpm12DeviceLibTcg/Tpm12DeviceLibTcg.inf Tpm2DeviceLib|SecurityPkg/Library/Tpm2DeviceLibTcg2/Tpm2DeviceLibTcg2.inf