summaryrefslogtreecommitdiff
path: root/ArmPlatformPkg
diff options
context:
space:
mode:
Diffstat (limited to 'ArmPlatformPkg')
-rw-r--r--ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.c5
-rw-r--r--ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.h22
-rw-r--r--ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/PlatformIntelBdsLib.inf10
-rw-r--r--ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/QemuKernel.c1103
4 files changed, 1140 insertions, 0 deletions
diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.c b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.c
index f5d82edfff..6fea2b0aeb 100644
--- a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.c
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.c
@@ -305,6 +305,11 @@ PlatformBdsPolicyBehavior (
Status = PlatformBdsConnectConsole ();
ASSERT_EFI_ERROR (Status);
+ //
+ // Process QEMU's -kernel command line option
+ //
+ TryRunningQemuKernel ();
+
BdsLibConnectAll ();
BdsLibEnumerateAllBootOption (BootOptionList);
diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.h b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.h
index c50bda2f09..cfc496d292 100644
--- a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.h
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/IntelBdsPlatform.h
@@ -38,4 +38,26 @@ PlatformBdsEnterFrontPage (
IN BOOLEAN ConnectAllHappened
);
+/**
+ 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, and load and start the kernel from it.
+
+ The kernel will be instructed via its command line to load the initrd from
+ the same Simple FileSystem.
+
+ @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
+TryRunningQemuKernel (
+ VOID
+ );
+
#endif // _INTEL_BDS_PLATFORM_H
diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/PlatformIntelBdsLib.inf b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/PlatformIntelBdsLib.inf
index efd50f3816..8db2ad57c0 100644
--- a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/PlatformIntelBdsLib.inf
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/PlatformIntelBdsLib.inf
@@ -33,6 +33,7 @@
[Sources]
IntelBdsPlatform.c
IntelBdsPlatform.h
+ QemuKernel.c
[Packages]
ArmPkg/ArmPkg.dec
@@ -53,9 +54,15 @@
PcdLib
GenericBdsLib
QemuBootOrderLib
+ QemuFwCfgLib
+ PrintLib
+ UefiRuntimeServicesTableLib
[Guids]
gArmGlobalVariableGuid
+ gEfiFileInfoGuid
+ gEfiFileSystemInfoGuid
+ gEfiFileSystemVolumeLabelInfoIdGuid
[Pcd]
gArmPlatformTokenSpaceGuid.PcdDefaultConInPaths
@@ -65,3 +72,6 @@
[Protocols]
gEfiDevicePathFromTextProtocolGuid
gEfiDevicePathToTextProtocolGuid
+ gEfiDevicePathProtocolGuid
+ gEfiLoadedImageProtocolGuid
+ gEfiSimpleFileSystemProtocolGuid
diff --git a/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/QemuKernel.c b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/QemuKernel.c
new file mode 100644
index 0000000000..abcac777eb
--- /dev/null
+++ b/ArmPlatformPkg/ArmVirtualizationPkg/Library/PlatformIntelBdsLib/QemuKernel.c
@@ -0,0 +1,1103 @@
+/** @file
+ Try to load an EFI-stubbed ARM Linux kernel from QEMU's fw_cfg.
+
+ This implementation differs from OvmfPkg/Library/LoadLinuxLib. An EFI
+ stub in the subject kernel is a hard requirement here.
+
+ Copyright (C) 2014, Red Hat, Inc.
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+**/
+
+#include <Guid/FileInfo.h>
+#include <Guid/FileSystemInfo.h>
+#include <Guid/FileSystemVolumeLabelInfo.h>
+#include <Library/PrintLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadedImage.h>
+#include <Protocol/SimpleFileSystem.h>
+
+#include "IntelBdsPlatform.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 (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.
+//
+
+/**
+ 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, and load and start the kernel from it.
+
+ The kernel will be instructed via its command line to load the initrd from
+ the same Simple FileSystem.
+
+ @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
+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 */);
+ 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];
+
+ //
+ // 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));
+ goto FreeKernelDevicePath;
+ }
+
+ //
+ // 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=<name>' 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));
+ }
+
+ //
+ // Start the image.
+ //
+ Status = gBS->StartImage (
+ KernelImageHandle,
+ NULL, // ExitDataSize
+ NULL // ExitData
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
+ }
+
+ if (KernelLoadedImage->LoadOptions != NULL) {
+ FreePool (KernelLoadedImage->LoadOptions);
+ }
+ 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;
+ }
+ }
+
+ return Status;
+}