From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- .../QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c | 1094 ++++++++++++++++++++ .../QemuKernelLoaderFsDxe.inf | 50 + 2 files changed, 1144 insertions(+) create mode 100644 src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c create mode 100644 src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf (limited to 'src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe') diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c new file mode 100644 index 00000000..bf105840 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c @@ -0,0 +1,1094 @@ +/** @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 +#include +#include +#include + +// +// Static data that hosts the fw_cfg blobs and serves file requests. +// +typedef enum { + KernelBlobTypeKernel, + KernelBlobTypeInitrd, + KernelBlobTypeMax +} KERNEL_BLOB_TYPE; + +typedef struct { + 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] = { + { + L"kernel", + { + { QemuFwCfgItemKernelSetupSize, QemuFwCfgItemKernelSetupData, }, + { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, }, + } + }, { + L"initrd", + { + { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, }, + } + } +}; + +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) } + } +}; + +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. +// +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) + +// +// Protocol member functions for File. +// + +/** + Opens a new file relative to the source file's location. + + (Forward declaration.) + + @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 + ); + +/** + 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 +}; + +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; +} + + +// +// 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 +}; + +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. +// + +/** + 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; + UINTN Idx; + UINT8 *ChunkData; + + // + // Read blob size. + // + 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; + } + + // + // Read blob. + // + 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)); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__, + (INT64)Blob->Size, Blob->Name)); + + 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; +} + + +// +// 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; + EFI_HANDLE InitrdLoadFile2Handle; + + 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; + } + + 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]; + if (CurrentBlob->Data != NULL) { + FreePages (CurrentBlob->Data, + EFI_SIZE_TO_PAGES ((UINTN)CurrentBlob->Size)); + CurrentBlob->Size = 0; + CurrentBlob->Data = NULL; + } + } + + return Status; +} diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf b/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf new file mode 100644 index 00000000..f26052be --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.inf @@ -0,0 +1,50 @@ +## @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 + DevicePathLib + MemoryAllocationLib + QemuFwCfgLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gQemuKernelLoaderFsMediaGuid + +[Protocols] + gEfiDevicePathProtocolGuid ## PRODUCES + gEfiLoadFile2ProtocolGuid ## PRODUCES + gEfiSimpleFileSystemProtocolGuid ## PRODUCES + +[Depex] + gEfiRealTimeClockArchProtocolGuid -- cgit v1.2.3