From 8d6203223ab18b541d0f0a510c20988de45e3026 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Thu, 5 May 2016 15:12:09 +0200 Subject: [PATCH] ArmVirtPkg: duplicate PlatformIntelBdsLib to PlatformBootManagerLib Create a copy of PlatformIntelBdsLib under the name PlatformBootManagerLib, with the following initial changes: - replace PlatformBdsLib references with PlatformBootManagerLib in comments, - replace "IntelBdsPlatform" with "PlatformBm" in file names and their references, - generate a new FILE_GUID. PlatformBootManagerLib will be linked into the BDS driver from MdeModulePkg. This patch parallels OvmfPkg commit 305418818959. Cc: Ard Biesheuvel Cc: Ruiyu Ni Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Laszlo Ersek Reviewed-by: Ard Biesheuvel Tested-by: Ard Biesheuvel Reviewed-by: Ruiyu Ni --- .../PlatformBootManagerLib/PlatformBm.c | 491 ++++++++ .../PlatformBootManagerLib/PlatformBm.h | 57 + .../PlatformBootManagerLib.inf | 81 ++ .../PlatformBootManagerLib/QemuKernel.c | 1115 +++++++++++++++++ 4 files changed, 1744 insertions(+) create mode 100644 ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c create mode 100644 ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h create mode 100644 ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf create mode 100644 ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c new file mode 100644 index 0000000000..f841d7c1b5 --- /dev/null +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.c @@ -0,0 +1,491 @@ +/** @file + Implementation for PlatformBootManagerLib library class interfaces. + + Copyright (C) 2015-2016, Red Hat, Inc. + Copyright (c) 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.
+ + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PlatformBm.h" + +#define DP_NODE_LEN(Type) { (UINT8)sizeof (Type), (UINT8)(sizeof (Type) >> 8) } + + +#pragma pack (1) +typedef struct { + VENDOR_DEVICE_PATH SerialDxe; + UART_DEVICE_PATH Uart; + VENDOR_DEFINED_DEVICE_PATH TermType; + EFI_DEVICE_PATH_PROTOCOL End; +} PLATFORM_SERIAL_CONSOLE; +#pragma pack () + +#define SERIAL_DXE_FILE_GUID { \ + 0xD3987D4B, 0x971A, 0x435F, \ + { 0x8C, 0xAF, 0x49, 0x67, 0xEB, 0x62, 0x72, 0x41 } \ + } + +STATIC PLATFORM_SERIAL_CONSOLE mSerialConsole = { + // + // VENDOR_DEVICE_PATH SerialDxe + // + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, DP_NODE_LEN (VENDOR_DEVICE_PATH) }, + SERIAL_DXE_FILE_GUID + }, + + // + // UART_DEVICE_PATH Uart + // + { + { MESSAGING_DEVICE_PATH, MSG_UART_DP, DP_NODE_LEN (UART_DEVICE_PATH) }, + 0, // Reserved + FixedPcdGet64 (PcdUartDefaultBaudRate), // BaudRate + FixedPcdGet8 (PcdUartDefaultDataBits), // DataBits + FixedPcdGet8 (PcdUartDefaultParity), // Parity + FixedPcdGet8 (PcdUartDefaultStopBits) // StopBits + }, + + // + // VENDOR_DEFINED_DEVICE_PATH TermType + // + { + { + MESSAGING_DEVICE_PATH, MSG_VENDOR_DP, + DP_NODE_LEN (VENDOR_DEFINED_DEVICE_PATH) + } + // + // Guid to be filled in dynamically + // + }, + + // + // EFI_DEVICE_PATH_PROTOCOL End + // + { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) + } +}; + + +#pragma pack (1) +typedef struct { + USB_CLASS_DEVICE_PATH Keyboard; + EFI_DEVICE_PATH_PROTOCOL End; +} PLATFORM_USB_KEYBOARD; +#pragma pack () + +STATIC PLATFORM_USB_KEYBOARD mUsbKeyboard = { + // + // USB_CLASS_DEVICE_PATH Keyboard + // + { + { + MESSAGING_DEVICE_PATH, MSG_USB_CLASS_DP, + DP_NODE_LEN (USB_CLASS_DEVICE_PATH) + }, + 0xFFFF, // VendorId: any + 0xFFFF, // ProductId: any + 3, // DeviceClass: HID + 1, // DeviceSubClass: boot + 1 // DeviceProtocol: keyboard + }, + + // + // EFI_DEVICE_PATH_PROTOCOL End + // + { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + DP_NODE_LEN (EFI_DEVICE_PATH_PROTOCOL) + } +}; + +// +// BDS Platform Functions +// +/** + Platform Bds init. Include the platform firmware vendor, revision + and so crc check. + +**/ +VOID +EFIAPI +PlatformBdsInit ( + VOID + ) +{ + // + // Signal EndOfDxe PI Event + // + EfiEventGroupSignal (&gEfiEndOfDxeEventGroupGuid); +} + + +/** + Check if the handle satisfies a particular condition. + + @param[in] Handle The handle to check. + @param[in] ReportText A caller-allocated string passed in for reporting + purposes. It must never be NULL. + + @retval TRUE The condition is satisfied. + @retval FALSE Otherwise. This includes the case when the condition could not + be fully evaluated due to an error. +**/ +typedef +BOOLEAN +(EFIAPI *FILTER_FUNCTION) ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ); + + +/** + Process a handle. + + @param[in] Handle The handle to process. + @param[in] ReportText A caller-allocated string passed in for reporting + purposes. It must never be NULL. +**/ +typedef +VOID +(EFIAPI *CALLBACK_FUNCTION) ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ); + +/** + Locate all handles that carry the specified protocol, filter them with a + callback function, and pass each handle that passes the filter to another + callback. + + @param[in] ProtocolGuid The protocol to look for. + + @param[in] Filter The filter function to pass each handle to. If this + parameter is NULL, then all handles are processed. + + @param[in] Process The callback function to pass each handle to that + clears the filter. +**/ +STATIC +VOID +FilterAndProcess ( + IN EFI_GUID *ProtocolGuid, + IN FILTER_FUNCTION Filter OPTIONAL, + IN CALLBACK_FUNCTION Process + ) +{ + EFI_STATUS Status; + EFI_HANDLE *Handles; + UINTN NoHandles; + UINTN Idx; + + Status = gBS->LocateHandleBuffer (ByProtocol, ProtocolGuid, + NULL /* SearchKey */, &NoHandles, &Handles); + if (EFI_ERROR (Status)) { + // + // This is not an error, just an informative condition. + // + DEBUG ((EFI_D_VERBOSE, "%a: %g: %r\n", __FUNCTION__, ProtocolGuid, + Status)); + return; + } + + ASSERT (NoHandles > 0); + for (Idx = 0; Idx < NoHandles; ++Idx) { + CHAR16 *DevicePathText; + STATIC CHAR16 Fallback[] = L""; + + // + // The ConvertDevicePathToText() function handles NULL input transparently. + // + DevicePathText = ConvertDevicePathToText ( + DevicePathFromHandle (Handles[Idx]), + FALSE, // DisplayOnly + FALSE // AllowShortcuts + ); + if (DevicePathText == NULL) { + DevicePathText = Fallback; + } + + if (Filter == NULL || Filter (Handles[Idx], DevicePathText)) { + Process (Handles[Idx], DevicePathText); + } + + if (DevicePathText != Fallback) { + FreePool (DevicePathText); + } + } + gBS->FreePool (Handles); +} + + +/** + This FILTER_FUNCTION checks if a handle corresponds to a PCI display device. +**/ +STATIC +BOOLEAN +EFIAPI +IsPciDisplay ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + EFI_PCI_IO_PROTOCOL *PciIo; + PCI_TYPE00 Pci; + + Status = gBS->HandleProtocol (Handle, &gEfiPciIoProtocolGuid, + (VOID**)&PciIo); + if (EFI_ERROR (Status)) { + // + // This is not an error worth reporting. + // + return FALSE; + } + + Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint32, 0 /* Offset */, + sizeof Pci / sizeof (UINT32), &Pci); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: %s: %r\n", __FUNCTION__, ReportText, Status)); + return FALSE; + } + + return IS_PCI_DISPLAY (&Pci); +} + + +/** + This CALLBACK_FUNCTION attempts to connect a handle non-recursively, asking + the matching driver to produce all first-level child handles. +**/ +STATIC +VOID +EFIAPI +Connect ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + + Status = gBS->ConnectController ( + Handle, // ControllerHandle + NULL, // DriverImageHandle + NULL, // RemainingDevicePath -- produce all children + FALSE // Recursive + ); + DEBUG ((EFI_ERROR (Status) ? EFI_D_ERROR : EFI_D_VERBOSE, "%a: %s: %r\n", + __FUNCTION__, ReportText, Status)); +} + + +/** + This CALLBACK_FUNCTION retrieves the EFI_DEVICE_PATH_PROTOCOL from the + handle, and adds it to ConOut and ErrOut. +**/ +STATIC +VOID +EFIAPI +AddOutput ( + IN EFI_HANDLE Handle, + IN CONST CHAR16 *ReportText + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + + DevicePath = DevicePathFromHandle (Handle); + if (DevicePath == NULL) { + DEBUG ((EFI_D_ERROR, "%a: %s: handle %p: device path not found\n", + __FUNCTION__, ReportText, Handle)); + return; + } + + Status = BdsLibUpdateConsoleVariable (L"ConOut", DevicePath, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: %s: adding to ConOut: %r\n", __FUNCTION__, + ReportText, Status)); + return; + } + + Status = BdsLibUpdateConsoleVariable (L"ErrOut", DevicePath, NULL); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: %s: adding to ErrOut: %r\n", __FUNCTION__, + ReportText, Status)); + return; + } + + DEBUG ((EFI_D_VERBOSE, "%a: %s: added to ConOut and ErrOut\n", __FUNCTION__, + ReportText)); +} + + +/** + The function will execute with as the platform policy, current policy + is driven by boot mode. IBV/OEM can customize this code for their specific + policy action. + + @param DriverOptionList The header of the driver option link list + @param BootOptionList The header of the boot option link list + @param ProcessCapsules A pointer to ProcessCapsules() + @param BaseMemoryTest A pointer to BaseMemoryTest() + +**/ +VOID +EFIAPI +PlatformBdsPolicyBehavior ( + IN LIST_ENTRY *DriverOptionList, + IN LIST_ENTRY *BootOptionList, + IN PROCESS_CAPSULES ProcessCapsules, + IN BASEM_MEMORY_TEST BaseMemoryTest + ) +{ + // + // Locate the PCI root bridges and make the PCI bus driver connect each, + // non-recursively. This will produce a number of child handles with PciIo on + // them. + // + FilterAndProcess (&gEfiPciRootBridgeIoProtocolGuid, NULL, Connect); + + // + // Signal the ACPI platform driver that it can download QEMU ACPI tables. + // + EfiEventGroupSignal (&gRootBridgesConnectedEventGroupGuid); + + // + // Find all display class PCI devices (using the handles from the previous + // step), and connect them non-recursively. This should produce a number of + // child handles with GOPs on them. + // + FilterAndProcess (&gEfiPciIoProtocolGuid, IsPciDisplay, Connect); + + // + // Now add the device path of all handles with GOP on them to ConOut and + // ErrOut. + // + FilterAndProcess (&gEfiGraphicsOutputProtocolGuid, NULL, AddOutput); + + // + // Add the hardcoded short-form USB keyboard device path to ConIn. + // + BdsLibUpdateConsoleVariable (L"ConIn", + (EFI_DEVICE_PATH_PROTOCOL *)&mUsbKeyboard, NULL); + + // + // Add the hardcoded serial console device path to ConIn, ConOut, ErrOut. + // + CopyGuid (&mSerialConsole.TermType.Guid, + PcdGetPtr (PcdTerminalTypeGuidBuffer)); + BdsLibUpdateConsoleVariable (L"ConIn", + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); + BdsLibUpdateConsoleVariable (L"ConOut", + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); + BdsLibUpdateConsoleVariable (L"ErrOut", + (EFI_DEVICE_PATH_PROTOCOL *)&mSerialConsole, NULL); + + // + // Connect the consoles based on the above variables. + // + BdsLibConnectAllDefaultConsoles (); + + // + // Show the splash screen. + // + EnableQuietBoot (PcdGetPtr (PcdLogoFile)); + + // + // Connect the rest of the devices. + // + BdsLibConnectAll (); + + // + // Process QEMU's -kernel command line option. Note that the kernel booted + // this way should receive ACPI tables, which is why we connect all devices + // first (see above) -- PCI enumeration blocks ACPI table installation, if + // there is a PCI host. + // + TryRunningQemuKernel (); + + BdsLibEnumerateAllBootOption (BootOptionList); + SetBootOrderFromQemu (BootOptionList); + // + // The BootOrder variable may have changed, reload the in-memory list with + // it. + // + BdsLibBuildOptionFromVar (BootOptionList, L"BootOrder"); + + PlatformBdsEnterFrontPage (GetFrontPageTimeoutFromQemu(), TRUE); +} + +/** + Hook point after a boot attempt succeeds. We don't expect a boot option to + return, so the UEFI 2.0 specification defines that you will default to an + interactive mode and stop processing the BootOrder list in this case. This + is also a platform implementation and can be customized by IBV/OEM. + + @param Option Pointer to Boot Option that succeeded to boot. + +**/ +VOID +EFIAPI +PlatformBdsBootSuccess ( + IN BDS_COMMON_OPTION *Option + ) +{ +} + +/** + Hook point after a boot attempt fails. + + @param Option Pointer to Boot Option that failed to boot. + @param Status Status returned from failed boot. + @param ExitData Exit data returned from failed boot. + @param ExitDataSize Exit data size returned from failed boot. + +**/ +VOID +EFIAPI +PlatformBdsBootFail ( + IN BDS_COMMON_OPTION *Option, + IN EFI_STATUS Status, + IN CHAR16 *ExitData, + IN UINTN ExitDataSize + ) +{ +} + +/** + This function locks platform flash that is not allowed to be updated during normal boot path. + The flash layout is platform specific. +**/ +VOID +EFIAPI +PlatformBdsLockNonUpdatableFlash ( + VOID + ) +{ + return; +} diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h new file mode 100644 index 0000000000..410c309cfc --- /dev/null +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBm.h @@ -0,0 +1,57 @@ +/** @file + Head file for BDS Platform specific code + + Copyright (C) 2015-2016, Red Hat, Inc. + Copyright (c) 2004 - 2008, Intel Corporation. All rights reserved.
+ + 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. + +**/ + +#ifndef _PLATFORM_BM_H_ +#define _PLATFORM_BM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +VOID +PlatformBdsEnterFrontPage ( + IN UINT16 TimeoutDefault, + 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 // _PLATFORM_BM_H_ diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf new file mode 100644 index 0000000000..1ad4c6f220 --- /dev/null +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf @@ -0,0 +1,81 @@ +## @file +# Implementation for PlatformBootManagerLib library class interfaces. +# +# Copyright (C) 2015-2016, Red Hat, Inc. +# Copyright (c) 2014, ARM Ltd. All rights reserved.
+# Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.
+# +# 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. +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = PlatformIntelBdsLib + FILE_GUID = 469184E8-FADA-41E4-8823-012CA19B40D4 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + LIBRARY_CLASS = PlatformBdsLib|DXE_DRIVER + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ARM AARCH64 +# + +[Sources] + PlatformBm.c + QemuKernel.c + +[Packages] + IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec + MdeModulePkg/MdeModulePkg.dec + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + ArmVirtPkg/ArmVirtPkg.dec + +[LibraryClasses] + BaseLib + BaseMemoryLib + DebugLib + DevicePathLib + GenericBdsLib + MemoryAllocationLib + PcdLib + PrintLib + QemuBootOrderLib + QemuFwCfgLib + UefiBootServicesTableLib + UefiLib + UefiRuntimeServicesTableLib + +[FixedPcd] + gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdLogoFile + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultBaudRate + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultDataBits + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultParity + gEfiMdePkgTokenSpaceGuid.PcdUartDefaultStopBits + +[Pcd] + gArmVirtTokenSpaceGuid.PcdTerminalTypeGuidBuffer + +[Guids] + gEfiFileInfoGuid + gEfiFileSystemInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + gEfiEndOfDxeEventGroupGuid + gRootBridgesConnectedEventGroupGuid + +[Protocols] + gEfiDevicePathProtocolGuid + gEfiGraphicsOutputProtocolGuid + gEfiLoadedImageProtocolGuid + gEfiPciRootBridgeIoProtocolGuid + gEfiSimpleFileSystemProtocolGuid diff --git a/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c new file mode 100644 index 0000000000..ac47d21e71 --- /dev/null +++ b/ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c @@ -0,0 +1,1115 @@ +/** @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-2016, 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 +#include +#include +#include +#include +#include +#include +#include + +#include "PlatformBm.h" + +// +// Static data that hosts the fw_cfg blobs and serves file requests. +// +typedef enum { + KernelBlobTypeKernel, + KernelBlobTypeInitrd, + KernelBlobTypeCommandLine, + KernelBlobTypeMax +} KERNEL_BLOB_TYPE; + +typedef struct { + FIRMWARE_CONFIG_ITEM CONST SizeKey; + FIRMWARE_CONFIG_ITEM CONST DataKey; + CONST CHAR16 * CONST Name; + UINT32 Size; + UINT8 *Data; +} KERNEL_BLOB; + +STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = { + { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" }, + { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" }, + { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" } +}; + +STATIC UINT64 mTotalBlobBytes; + +// +// Device path for the handle that incorporates our "EFI stub filesystem". The +// GUID is arbitrary and need not be standardized or advertized. +// +#pragma pack(1) +typedef struct { + VENDOR_DEVICE_PATH VenHwNode; + EFI_DEVICE_PATH_PROTOCOL EndNode; +} SINGLE_VENHW_NODE_DEVPATH; +#pragma pack() + +STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = { + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } }, + { + 0xb0fae7e7, 0x6b07, 0x49d0, + { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d } + } + }, + + { + END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, + { sizeof (EFI_DEVICE_PATH_PROTOCOL) } + } +}; + +// +// The "file in the EFI stub filesystem" abstraction. +// +STATIC EFI_TIME mInitTime; + +#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E') + +typedef struct { + UINT64 Signature; // Carries STUB_FILE_SIG. + + KERNEL_BLOB_TYPE BlobType; // Index into mKernelBlob. KernelBlobTypeMax + // denotes the root directory of the filesystem. + + UINT64 Position; // Byte position for regular files; + // next directory entry to return for the root + // directory. + + EFI_FILE_PROTOCOL File; // Standard protocol interface. +} STUB_FILE; + +#define STUB_FILE_FROM_FILE(FilePointer) \ + CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG) + +// +// Tentative definition of the file protocol template. The initializer +// (external definition) will be provided later. +// +STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate; + + +// +// Protocol member functions for File. +// + +/** + Opens a new file relative to the source file's location. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to the source location. This would + typically be an open handle to a directory. + + @param[out] NewHandle A pointer to the location to return the opened handle + for the new file. + + @param[in] FileName The Null-terminated string of the name of the file to + be opened. The file name may contain the following + path modifiers: "\", ".", and "..". + + @param[in] OpenMode The mode to open the file. The only valid + combinations that the file may be opened with are: + Read, Read/Write, or Create/Read/Write. + + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case + these are the attribute bits for the newly created + file. + + @retval EFI_SUCCESS The file was opened. + @retval EFI_NOT_FOUND The specified file could not be found on the + device. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a + file for write when the media is + write-protected. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the + file. + @retval EFI_VOLUME_FULL The volume is full. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileOpen ( + IN EFI_FILE_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + CONST STUB_FILE *StubFile; + UINTN BlobType; + STUB_FILE *NewStubFile; + + // + // We're read-only. + // + switch (OpenMode) { + case EFI_FILE_MODE_READ: + break; + + case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE: + case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE: + return EFI_WRITE_PROTECTED; + + default: + return EFI_INVALID_PARAMETER; + } + + // + // Only the root directory supports opening files in it. + // + StubFile = STUB_FILE_FROM_FILE (This); + if (StubFile->BlobType != KernelBlobTypeMax) { + return EFI_UNSUPPORTED; + } + + // + // Locate the file. + // + for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) { + if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) { + break; + } + } + if (BlobType == KernelBlobTypeMax) { + return EFI_NOT_FOUND; + } + + // + // Found it. + // + NewStubFile = AllocatePool (sizeof *NewStubFile); + if (NewStubFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + NewStubFile->Signature = STUB_FILE_SIG; + NewStubFile->BlobType = (KERNEL_BLOB_TYPE)BlobType; + NewStubFile->Position = 0; + CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate, + sizeof mEfiFileProtocolTemplate); + *NewHandle = &NewStubFile->File; + + return EFI_SUCCESS; +} + + +/** + Closes a specified file handle. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileClose ( + IN EFI_FILE_PROTOCOL *This + ) +{ + FreePool (STUB_FILE_FROM_FILE (This)); + return EFI_SUCCESS; +} + + +/** + Close and delete the file handle. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the + handle to the file to delete. + + @retval EFI_SUCCESS The file was closed and deleted, and the + handle was closed. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not + deleted. + +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileDelete ( + IN EFI_FILE_PROTOCOL *This + ) +{ + FreePool (STUB_FILE_FROM_FILE (This)); + return EFI_WARN_DELETE_FAILURE; +} + + +/** + Helper function that formats an EFI_FILE_INFO structure into the + user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including + KernelBlobTypeMax, which stands for the root directory). + + The interface follows the EFI_FILE_GET_INFO -- and for directories, the + EFI_FILE_READ -- interfaces. + + @param[in] BlobType The KERNEL_BLOB_TYPE value identifying the fw_cfg + blob backing the STUB_FILE that information is + being requested about. If BlobType equals + KernelBlobTypeMax, then information will be + provided about the root directory of the + filesystem. + + @param[in,out] BufferSize On input, the size of Buffer. On output, the + amount of data returned in Buffer. In both cases, + the size is measured in bytes. + + @param[out] Buffer A pointer to the data buffer to return. The + buffer's type is EFI_FILE_INFO. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the + EFI_FILE_INFO structure. BufferSize has been + updated with the size needed to complete the + request. +**/ +STATIC +EFI_STATUS +ConvertKernelBlobTypeToFileInfo ( + IN KERNEL_BLOB_TYPE BlobType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + CONST CHAR16 *Name; + UINT64 FileSize; + UINT64 Attribute; + + UINTN NameSize; + UINTN FileInfoSize; + EFI_FILE_INFO *FileInfo; + UINTN OriginalBufferSize; + + if (BlobType == KernelBlobTypeMax) { + // + // getting file info about the root directory + // + Name = L"\\"; + FileSize = KernelBlobTypeMax; + Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; + } else { + CONST KERNEL_BLOB *Blob; + + Blob = &mKernelBlob[BlobType]; + Name = Blob->Name; + FileSize = Blob->Size; + Attribute = EFI_FILE_READ_ONLY; + } + + NameSize = (StrLen(Name) + 1) * 2; + FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize; + ASSERT (FileInfoSize >= sizeof *FileInfo); + + OriginalBufferSize = *BufferSize; + *BufferSize = FileInfoSize; + if (OriginalBufferSize < *BufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + FileInfo = (EFI_FILE_INFO *)Buffer; + FileInfo->Size = FileInfoSize; + FileInfo->FileSize = FileSize; + FileInfo->PhysicalSize = FileSize; + FileInfo->Attribute = Attribute; + + CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime); + CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime); + CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime); + CopyMem (FileInfo->FileName, Name, NameSize); + + return EFI_SUCCESS; +} + + +/** + Reads data from a file, or continues scanning a directory. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to read data from. + + @param[in,out] BufferSize On input, the size of the Buffer. On output, the + amount of data returned in Buffer. In both cases, + the size is measured in bytes. If the read goes + beyond the end of the file, the read length is + truncated to the end of the file. + + If This is a directory, the function reads the + directory entry at the current position and + returns the entry (as EFI_FILE_INFO) in Buffer. If + there are no more directory entries, the + BufferSize is set to zero on output. + + @param[out] Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS Data was read. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted + file. + @retval EFI_DEVICE_ERROR On entry, the current file position is beyond + the end of the file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the + current directory entry as a EFI_FILE_INFO + structure. BufferSize has been updated with the + size needed to complete the request, and the + directory position has not been advanced. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileRead ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + STUB_FILE *StubFile; + CONST KERNEL_BLOB *Blob; + UINT64 Left; + + StubFile = STUB_FILE_FROM_FILE (This); + + // + // Scanning the root directory? + // + if (StubFile->BlobType == KernelBlobTypeMax) { + EFI_STATUS Status; + + if (StubFile->Position == KernelBlobTypeMax) { + // + // Scanning complete. + // + *BufferSize = 0; + return EFI_SUCCESS; + } + + Status = ConvertKernelBlobTypeToFileInfo ( + (KERNEL_BLOB_TYPE)StubFile->Position, + BufferSize, + Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + ++StubFile->Position; + return EFI_SUCCESS; + } + + // + // Reading a file. + // + Blob = &mKernelBlob[StubFile->BlobType]; + if (StubFile->Position > Blob->Size) { + return EFI_DEVICE_ERROR; + } + + Left = Blob->Size - StubFile->Position; + if (*BufferSize > Left) { + *BufferSize = (UINTN)Left; + } + if (Blob->Data != NULL) { + CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize); + } + StubFile->Position += *BufferSize; + return EFI_SUCCESS; +} + + +/** + Writes data to a file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + + @param[in,out] BufferSize On input, the size of the Buffer. On output, the + amount of data actually written. In both cases, + the size is measured in bytes. + + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS Data was written. + @retval EFI_UNSUPPORTED Writes to open directory files are not + supported. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read only. + @retval EFI_VOLUME_FULL The volume is full. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileWrite ( + IN EFI_FILE_PROTOCOL *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + STUB_FILE *StubFile; + + StubFile = STUB_FILE_FROM_FILE (This); + return (StubFile->BlobType == KernelBlobTypeMax) ? + EFI_UNSUPPORTED : + EFI_WRITE_PROTECTED; +} + + +/** + Returns a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to get the current position on. + + @param[out] Position The address to return the file's current position + value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_UNSUPPORTED The request is not valid on open directories. + @retval EFI_DEVICE_ERROR An attempt was made to get the position from a + deleted file. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileGetPosition ( + IN EFI_FILE_PROTOCOL *This, + OUT UINT64 *Position + ) +{ + STUB_FILE *StubFile; + + StubFile = STUB_FILE_FROM_FILE (This); + if (StubFile->BlobType == KernelBlobTypeMax) { + return EFI_UNSUPPORTED; + } + + *Position = StubFile->Position; + return EFI_SUCCESS; +} + + +/** + Sets a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to set the requested position on. + + @param[in] Position The byte position from the start of the file to set. For + regular files, MAX_UINT64 means "seek to end". For + directories, zero means "rewind directory scan". + + @retval EFI_SUCCESS The position was set. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + @retval EFI_DEVICE_ERROR An attempt was made to set the position of a + deleted file. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileSetPosition ( + IN EFI_FILE_PROTOCOL *This, + IN UINT64 Position + ) +{ + STUB_FILE *StubFile; + KERNEL_BLOB *Blob; + + StubFile = STUB_FILE_FROM_FILE (This); + + if (StubFile->BlobType == KernelBlobTypeMax) { + if (Position == 0) { + // + // rewinding a directory scan is allowed + // + StubFile->Position = 0; + return EFI_SUCCESS; + } + return EFI_UNSUPPORTED; + } + + // + // regular file seek + // + Blob = &mKernelBlob[StubFile->BlobType]; + if (Position == MAX_UINT64) { + // + // seek to end + // + StubFile->Position = Blob->Size; + } else { + // + // absolute seek from beginning -- seeking past the end is allowed + // + StubFile->Position = Position; + } + return EFI_SUCCESS; +} + + +/** + Returns information about a file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance + that is the file handle the requested + information is for. + + @param[in] InformationType The type identifier GUID for the information + being requested. The following information + types are supported, storing the + corresponding structures in Buffer: + + - gEfiFileInfoGuid: EFI_FILE_INFO + + - gEfiFileSystemInfoGuid: + EFI_FILE_SYSTEM_INFO + + - gEfiFileSystemVolumeLabelInfoIdGuid: + EFI_FILE_SYSTEM_VOLUME_LABEL + + @param[in,out] BufferSize On input, the size of Buffer. On output, the + amount of data returned in Buffer. In both + cases, the size is measured in bytes. + + @param[out] Buffer A pointer to the data buffer to return. The + buffer's type is indicated by + InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the + information structure requested by + InformationType. BufferSize has been updated + with the size needed to complete the request. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileGetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + CONST STUB_FILE *StubFile; + UINTN OriginalBufferSize; + + StubFile = STUB_FILE_FROM_FILE (This); + + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize, + Buffer); + } + + OriginalBufferSize = *BufferSize; + + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + EFI_FILE_SYSTEM_INFO *FileSystemInfo; + + *BufferSize = sizeof *FileSystemInfo; + if (OriginalBufferSize < *BufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer; + FileSystemInfo->Size = sizeof *FileSystemInfo; + FileSystemInfo->ReadOnly = TRUE; + FileSystemInfo->VolumeSize = mTotalBlobBytes; + FileSystemInfo->FreeSpace = 0; + FileSystemInfo->BlockSize = 1; + FileSystemInfo->VolumeLabel[0] = L'\0'; + + return EFI_SUCCESS; + } + + if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel; + + *BufferSize = sizeof *FileSystemVolumeLabel; + if (OriginalBufferSize < *BufferSize) { + return EFI_BUFFER_TOO_SMALL; + } + + FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer; + FileSystemVolumeLabel->VolumeLabel[0] = L'\0'; + + return EFI_SUCCESS; + } + + return EFI_UNSUPPORTED; +} + + +/** + Sets information about a file. + + @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + + @param[in] InformationType The type identifier for the information being + set. + + @param[in] BufferSize The size, in bytes, of Buffer. + + @param[in] Buffer A pointer to the data buffer to write. The + buffer's type is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the + media is read-only. + @retval EFI_WRITE_PROTECTED InformationType is + EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media + is read only. + @retval EFI_WRITE_PROTECTED InformationType is + EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media + is read-only. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED An attempt is being made to change the size of + a directory. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the + file was opened read-only and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_VOLUME_FULL The volume is full. + @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type + indicated by InformationType. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileSetInfo ( + IN EFI_FILE_PROTOCOL *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + return EFI_WRITE_PROTECTED; +} + + +/** + Flushes all modified data associated with a file to a device. + + @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the + file handle to flush. + + @retval EFI_SUCCESS The data was flushed. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_WRITE_PROTECTED The file or medium is write-protected. + @retval EFI_ACCESS_DENIED The file was opened read-only. + @retval EFI_VOLUME_FULL The volume is full. +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileFlush ( + IN EFI_FILE_PROTOCOL *This + ) +{ + return EFI_WRITE_PROTECTED; +} + +// +// External definition of the file protocol template. +// +STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = { + EFI_FILE_PROTOCOL_REVISION, // revision 1 + StubFileOpen, + StubFileClose, + StubFileDelete, + StubFileRead, + StubFileWrite, + StubFileGetPosition, + StubFileSetPosition, + StubFileGetInfo, + StubFileSetInfo, + StubFileFlush, + NULL, // OpenEx, revision 2 + NULL, // ReadEx, revision 2 + NULL, // WriteEx, revision 2 + NULL // FlushEx, revision 2 +}; + + +// +// Protocol member functions for SimpleFileSystem. +// + +/** + Open the root directory on a volume. + + @param[in] This A pointer to the volume to open the root directory on. + + @param[out] Root A pointer to the location to return the opened file handle + for the root directory in. + + @retval EFI_SUCCESS The device was opened. + @retval EFI_UNSUPPORTED This volume does not support the requested file + system type. + @retval EFI_NO_MEDIA The device has no medium. + @retval EFI_DEVICE_ERROR The device reported an error. + @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted. + @retval EFI_ACCESS_DENIED The service denied access to the file. + @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of + resources. + @retval EFI_MEDIA_CHANGED The device has a different medium in it or the + medium is no longer supported. Any existing + file handles for this volume are no longer + valid. To access the files on the new medium, + the volume must be reopened with OpenVolume(). +**/ +STATIC +EFI_STATUS +EFIAPI +StubFileSystemOpenVolume ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE_PROTOCOL **Root + ) +{ + STUB_FILE *StubFile; + + StubFile = AllocatePool (sizeof *StubFile); + if (StubFile == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + StubFile->Signature = STUB_FILE_SIG; + StubFile->BlobType = KernelBlobTypeMax; + StubFile->Position = 0; + CopyMem (&StubFile->File, &mEfiFileProtocolTemplate, + sizeof mEfiFileProtocolTemplate); + *Root = &StubFile->File; + + return EFI_SUCCESS; +} + +STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + StubFileSystemOpenVolume +}; + + +// +// Utility functions. +// + +/** + Populate a blob in mKernelBlob. + + param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is + to be filled from fw_cfg. + + @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a + size of zero for the blob, then Blob->Data has + been left unchanged. + + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data. +**/ +STATIC +EFI_STATUS +FetchBlob ( + IN OUT KERNEL_BLOB *Blob + ) +{ + UINT32 Left; + + // + // Read blob size. + // + QemuFwCfgSelectItem (Blob->SizeKey); + Blob->Size = QemuFwCfgRead32 (); + if (Blob->Size == 0) { + return EFI_SUCCESS; + } + + // + // Read blob. + // + Blob->Data = AllocatePool (Blob->Size); + if (Blob->Data == NULL) { + DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n", + __FUNCTION__, (INT64)Blob->Size, Blob->Name)); + return EFI_OUT_OF_RESOURCES; + } + + DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__, + (INT64)Blob->Size, Blob->Name)); + QemuFwCfgSelectItem (Blob->DataKey); + + Left = Blob->Size; + do { + UINT32 Chunk; + + Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB; + QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left)); + Left -= Chunk; + DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n", + __FUNCTION__, (INT64)Left, Blob->Name)); + } while (Left > 0); + return EFI_SUCCESS; +} + + +// +// The entry point of the feature. +// + +/** + 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]; + + if (KernelBlob->Data == NULL) { + Status = EFI_NOT_FOUND; + goto FreeBlobs; + } + + // + // Create a new handle with a single VenHw() node device path protocol on it, + // plus a custom SimpleFileSystem protocol on it. + // + FileSystemHandle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle, + &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath, + &gEfiSimpleFileSystemProtocolGuid, &mFileSystem, + NULL); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n", + __FUNCTION__, Status)); + goto FreeBlobs; + } + + // + // Create a device path for the kernel image to be loaded from that will call + // back into our file system. + // + KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name); + if (KernelDevicePath == NULL) { + DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n", + __FUNCTION__)); + Status = EFI_OUT_OF_RESOURCES; + goto UninstallProtocols; + } + + // + // Load the image. This should call back into our file system. + // + Status = gBS->LoadImage ( + FALSE, // BootPolicy: exact match required + gImageHandle, // ParentImageHandle + KernelDevicePath, + NULL, // SourceBuffer + 0, // SourceSize + &KernelImageHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status)); + 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=' in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += + (8 + StrLen(InitrdBlob->Name)) * 2; + } + + if (KernelLoadedImage->LoadOptionsSize == 0) { + KernelLoadedImage->LoadOptions = NULL; + } else { + // + // NUL-terminate in UTF-16. + // + KernelLoadedImage->LoadOptionsSize += 2; + + KernelLoadedImage->LoadOptions = AllocatePool ( + KernelLoadedImage->LoadOptionsSize); + if (KernelLoadedImage->LoadOptions == NULL) { + KernelLoadedImage->LoadOptionsSize = 0; + Status = EFI_OUT_OF_RESOURCES; + goto UnloadKernelImage; + } + + UnicodeSPrintAsciiFormat ( + KernelLoadedImage->LoadOptions, + KernelLoadedImage->LoadOptionsSize, + "%a%a%s", + (CommandLineBlob->Data == NULL) ? "" : (CHAR8 *)CommandLineBlob->Data, + (InitrdBlob->Data == NULL) ? "" : " initrd=", + (InitrdBlob->Data == NULL) ? L"" : InitrdBlob->Name + ); + DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__, + (CHAR16 *)KernelLoadedImage->LoadOptions)); + } + + // + // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event. + // + EfiSignalEventReadyToBoot(); + + // + // 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; +} -- 2.39.2