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