summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/LinuxInitrdDynamicShellCommand
diff options
context:
space:
mode:
Diffstat (limited to 'OvmfPkg/LinuxInitrdDynamicShellCommand')
-rw-r--r--OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c429
-rw-r--r--OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf53
-rw-r--r--OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni49
3 files changed, 531 insertions, 0 deletions
diff --git a/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
new file mode 100644
index 0000000000..47ed26b50d
--- /dev/null
+++ b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
@@ -0,0 +1,429 @@
+/** @file
+ Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
+ via its GUIDed vendor media path
+
+ Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
+
+ SPDX-License-Identifier: BSD-2-Clause-Patent
+**/
+
+#include <Uefi.h>
+
+#include <Library/DebugLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/HiiLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/ShellLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiHiiServicesLib.h>
+
+#include <Guid/LinuxEfiInitrdMedia.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/HiiPackageList.h>
+#include <Protocol/LoadFile2.h>
+#include <Protocol/ShellDynamicCommand.h>
+
+#pragma pack (1)
+typedef struct {
+ VENDOR_DEVICE_PATH VenMediaNode;
+ EFI_DEVICE_PATH_PROTOCOL EndNode;
+} SINGLE_NODE_VENDOR_MEDIA_DEVPATH;
+#pragma pack ()
+
+STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle;
+STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress;
+STATIC UINTN mInitrdFileSize;
+STATIC EFI_HANDLE mInitrdLoadFile2Handle;
+
+STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
+ {L"-u", TypeFlag},
+ {NULL, TypeMax}
+ };
+
+STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = {
+ {
+ {
+ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) }
+ },
+ LINUX_EFI_INITRD_MEDIA_GUID
+ }, {
+ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
+ }
+};
+
+STATIC
+EFI_STATUS
+EFIAPI
+InitrdLoadFile2 (
+ IN EFI_LOAD_FILE2_PROTOCOL *This,
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
+ IN BOOLEAN BootPolicy,
+ IN OUT UINTN *BufferSize,
+ OUT VOID *Buffer OPTIONAL
+ )
+{
+ if (BootPolicy) {
+ return EFI_UNSUPPORTED;
+ }
+
+ if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (FilePath->Type != END_DEVICE_PATH_TYPE ||
+ FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE ||
+ mInitrdFileSize == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ if (Buffer == NULL || *BufferSize < mInitrdFileSize) {
+ *BufferSize = mInitrdFileSize;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ ASSERT (mInitrdFileAddress != 0);
+
+ gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);
+ *BufferSize = mInitrdFileSize;
+ return EFI_SUCCESS;
+}
+
+STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
+ InitrdLoadFile2,
+};
+
+STATIC
+EFI_STATUS
+UninstallLoadFile2Protocol (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+
+ if (mInitrdLoadFile2Handle != NULL) {
+ Status = gBS->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle,
+ &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
+ &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
+ NULL);
+ if (!EFI_ERROR (Status)) {
+ mInitrdLoadFile2Handle = NULL;
+ }
+ }
+ return Status;
+}
+
+STATIC
+VOID
+FreeInitrdFile (
+ VOID
+ )
+{
+ if (mInitrdFileSize != 0) {
+ gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
+ mInitrdFileSize = 0;
+ }
+}
+
+STATIC
+EFI_STATUS
+CacheInitrdFile (
+ IN SHELL_FILE_HANDLE FileHandle
+ )
+{
+ EFI_STATUS Status;
+ UINT64 FileSize;
+ UINTN ReadSize;
+
+ Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ if (FileSize == 0 || FileSize > MAX_UINTN) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Status = gBS->AllocatePages (AllocateAnyPages, EfiLoaderData,
+ EFI_SIZE_TO_PAGES ((UINTN)FileSize), &mInitrdFileAddress);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ ReadSize = (UINTN)FileSize;
+ Status = gEfiShellProtocol->ReadFile (FileHandle, &ReadSize,
+ (VOID *)(UINTN)mInitrdFileAddress);
+ if (EFI_ERROR (Status) || ReadSize < FileSize) {
+ DEBUG ((DEBUG_WARN, "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
+ __FUNCTION__, Status, (UINT64)ReadSize, FileSize));
+ goto FreeMemory;
+ }
+
+ if (mInitrdLoadFile2Handle == NULL) {
+ Status = gBS->InstallMultipleProtocolInterfaces (&mInitrdLoadFile2Handle,
+ &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
+ &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
+ NULL);
+ ASSERT_EFI_ERROR (Status);
+ }
+
+ mInitrdFileSize = FileSize;
+ return EFI_SUCCESS;
+
+FreeMemory:
+ gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
+ return Status;
+}
+
+/**
+ Function for 'initrd' command.
+
+ @param[in] ImageHandle Handle to the Image (NULL if Internal).
+ @param[in] SystemTable Pointer to the System Table (NULL if Internal).
+**/
+STATIC
+SHELL_STATUS
+EFIAPI
+RunInitrd (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ LIST_ENTRY *Package;
+ CHAR16 *ProblemParam;
+ CONST CHAR16 *Param;
+ CHAR16 *Filename;
+ SHELL_STATUS ShellStatus;
+ SHELL_FILE_HANDLE FileHandle;
+
+ ProblemParam = NULL;
+ ShellStatus = SHELL_SUCCESS;
+
+ Status = ShellInitialize ();
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // parse the command line
+ //
+ Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
+ if (EFI_ERROR (Status)) {
+ if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM),
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", ProblemParam);
+ FreePool (ProblemParam);
+ ShellStatus = SHELL_INVALID_PARAMETER;
+ } else {
+ ASSERT(FALSE);
+ }
+ } else {
+ if (ShellCommandLineGetCount (Package) > 2) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),
+ mLinuxInitrdShellCommandHiiHandle, L"initrd");
+ ShellStatus = SHELL_INVALID_PARAMETER;
+ } else if (ShellCommandLineGetCount (Package) < 2) {
+ if (ShellCommandLineGetFlag (Package, L"-u")) {
+ FreeInitrdFile ();
+ UninstallLoadFile2Protocol ();
+ } else {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),
+ mLinuxInitrdShellCommandHiiHandle, L"initrd");
+ ShellStatus = SHELL_INVALID_PARAMETER;
+ }
+ } else {
+ Param = ShellCommandLineGetRawValue (Package, 1);
+ ASSERT (Param != NULL);
+
+ Filename = ShellFindFilePath (Param);
+ if (Filename == NULL) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FIND_FAIL),
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);
+ ShellStatus = SHELL_NOT_FOUND;
+ } else {
+ Status = ShellOpenFileByName (Filename, &FileHandle,
+ EFI_FILE_MODE_READ, 0);
+ if (!EFI_ERROR (Status)) {
+ FreeInitrdFile ();
+ Status = CacheInitrdFile (FileHandle);
+ ShellCloseFile (&FileHandle);
+ }
+ if (EFI_ERROR (Status)) {
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);
+ ShellStatus = SHELL_NOT_FOUND;
+ }
+ FreePool (Filename);
+ }
+ }
+ }
+ return ShellStatus;
+}
+
+
+/**
+ This is the shell command handler function pointer callback type. This
+ function handles the command when it is invoked in the shell.
+
+ @param[in] This The instance of the
+ EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
+ @param[in] SystemTable The pointer to the system table.
+ @param[in] ShellParameters The parameters associated with the command.
+ @param[in] Shell The instance of the shell protocol used in
+ the context of processing this command.
+
+ @return EFI_SUCCESS the operation was successful
+ @return other the operation failed.
+**/
+SHELL_STATUS
+EFIAPI
+LinuxInitrdCommandHandler (
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
+ IN EFI_SYSTEM_TABLE *SystemTable,
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
+ IN EFI_SHELL_PROTOCOL *Shell
+ )
+{
+ gEfiShellParametersProtocol = ShellParameters;
+ gEfiShellProtocol = Shell;
+
+ return RunInitrd (gImageHandle, SystemTable);
+}
+
+/**
+ This is the command help handler function pointer callback type. This
+ function is responsible for displaying help information for the associated
+ command.
+
+ @param[in] This The instance of the
+ EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
+ @param[in] Language The pointer to the language string to use.
+
+ @return string Pool allocated help string, must be freed
+ by caller
+**/
+STATIC
+CHAR16 *
+EFIAPI
+LinuxInitrdGetHelp (
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
+ IN CONST CHAR8 *Language
+ )
+{
+ return HiiGetString (mLinuxInitrdShellCommandHiiHandle,
+ STRING_TOKEN (STR_GET_HELP_INITRD), Language);
+}
+
+STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
+ L"initrd",
+ LinuxInitrdCommandHandler,
+ LinuxInitrdGetHelp
+};
+
+/**
+ Retrieve HII package list from ImageHandle and publish to HII database.
+
+ @param ImageHandle The image handle of the process.
+
+ @return HII handle.
+**/
+STATIC
+EFI_HII_HANDLE
+InitializeHiiPackage (
+ EFI_HANDLE ImageHandle
+ )
+{
+ EFI_STATUS Status;
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;
+ EFI_HII_HANDLE HiiHandle;
+
+ //
+ // Retrieve HII package list from ImageHandle
+ //
+ Status = gBS->OpenProtocol (ImageHandle, &gEfiHiiPackageListProtocolGuid,
+ (VOID **)&PackageList, ImageHandle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+
+ //
+ // Publish HII package list to HII Database.
+ //
+ Status = gHiiDatabase->NewPackageList (gHiiDatabase, PackageList, NULL,
+ &HiiHandle);
+ ASSERT_EFI_ERROR (Status);
+ if (EFI_ERROR (Status)) {
+ return NULL;
+ }
+ return HiiHandle;
+}
+
+/**
+ Entry point of Linux Initrd dynamic UEFI Shell command.
+
+ Produce the DynamicCommand protocol to handle "initrd" command.
+
+ @param ImageHandle The image handle of the process.
+ @param SystemTable The EFI System Table pointer.
+
+ @retval EFI_SUCCESS Initrd command is executed successfully.
+ @retval EFI_ABORTED HII package was failed to initialize.
+ @retval others Other errors when executing Initrd command.
+**/
+EFI_STATUS
+EFIAPI
+LinuxInitrdDynamicShellCommandEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+
+ mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
+ if (mLinuxInitrdShellCommandHiiHandle == NULL) {
+ return EFI_ABORTED;
+ }
+
+ Status = gBS->InstallProtocolInterface (&ImageHandle,
+ &gEfiShellDynamicCommandProtocolGuid,
+ EFI_NATIVE_INTERFACE,
+ &mLinuxInitrdDynamicCommand);
+ ASSERT_EFI_ERROR (Status);
+ return Status;
+}
+
+/**
+ Unload the dynamic UEFI Shell command.
+
+ @param ImageHandle The image handle of the process.
+
+ @retval EFI_SUCCESS The image is unloaded.
+ @retval Others Failed to unload the image.
+**/
+EFI_STATUS
+EFIAPI
+LinuxInitrdDynamicShellCommandUnload (
+ IN EFI_HANDLE ImageHandle
+)
+{
+ EFI_STATUS Status;
+
+ FreeInitrdFile ();
+
+ Status = UninstallLoadFile2Protocol ();
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = gBS->UninstallProtocolInterface (ImageHandle,
+ &gEfiShellDynamicCommandProtocolGuid,
+ &mLinuxInitrdDynamicCommand);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
+ return EFI_SUCCESS;
+}
diff --git a/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
new file mode 100644
index 0000000000..6da6ef6d78
--- /dev/null
+++ b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf
@@ -0,0 +1,53 @@
+## @file
+# Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
+# via its GUIDed vendor media path
+#
+# Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
+#
+# SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+#
+##
+
+[Defines]
+ INF_VERSION = 1.27
+ BASE_NAME = LinuxInitrdDynamicShellCommand
+ FILE_GUID = 2f30da26-f51b-4b6f-85c4-31873c281bca
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+ ENTRY_POINT = LinuxInitrdDynamicShellCommandEntryPoint
+ UNLOAD_IMAGE = LinuxInitrdDynamicShellCommandUnload
+ UEFI_HII_RESOURCE_SECTION = TRUE
+
+#
+# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 EBC
+#
+
+[Sources.common]
+ LinuxInitrdDynamicShellCommand.c
+ LinuxInitrdDynamicShellCommand.uni
+
+[Packages]
+ MdePkg/MdePkg.dec
+ ShellPkg/ShellPkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ DebugLib
+ DevicePathLib
+ HiiLib
+ MemoryAllocationLib
+ ShellLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+ UefiHiiServicesLib
+
+[Protocols]
+ gEfiDevicePathProtocolGuid ## SOMETIMES_PRODUCES
+ gEfiHiiPackageListProtocolGuid ## CONSUMES
+ gEfiLoadFile2ProtocolGuid ## SOMETIMES_PRODUCES
+ gEfiShellDynamicCommandProtocolGuid ## PRODUCES
+
+[DEPEX]
+ TRUE
diff --git a/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni
new file mode 100644
index 0000000000..a88fa6e364
--- /dev/null
+++ b/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni
@@ -0,0 +1,49 @@
+// /**
+//
+// Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
+// SPDX-License-Identifier: BSD-2-Clause-Patent
+//
+// Module Name:
+//
+// LinuxInitrdDynamicShellCommand.uni
+//
+// Abstract:
+//
+// String definitions for 'initrd' UEFI Shell command
+//
+// **/
+
+/=#
+
+#langdef en-US "english"
+
+#string STR_GEN_PROBLEM #language en-US "%H%s%N: Unknown flag - '%H%s%N'\r\n"
+#string STR_GEN_TOO_MANY #language en-US "%H%s%N: Too many arguments.\r\n"
+#string STR_GEN_TOO_FEW #language en-US "%H%s%N: Too few arguments.\r\n"
+#string STR_GEN_FIND_FAIL #language en-US "%H%s%N: File not found - '%H%s%N'\r\n"
+#string STR_GEN_FILE_OPEN_FAIL #language en-US "%H%s%N: Cannot open file - '%H%s%N'\r\n"
+
+#string STR_GET_HELP_INITRD #language en-US ""
+".TH initrd 0 "Registers or unregisters a file as Linux initrd."\r\n"
+".SH NAME\r\n"
+"Registers or unregisters a file as Linux initrd.\r\n"
+".SH SYNOPSIS\r\n"
+" \r\n"
+"initrd <FileName>\r\n"
+"initrd -u\r\n"
+".SH OPTIONS\r\n"
+" \r\n"
+" FileName - Specifies a file to register as initrd.\r\n"
+" -u - Unregisters any previously registered initrd files.\r\n"
+".SH DESCRIPTION\r\n"
+" \r\n"
+"NOTES:\r\n"
+" 1. Only a single file can be loaded as initrd at any given time. Using the\r\n"
+" command twice with a <FileName> option will result in the first file to\r\n"
+" be unloaded again, regardless of whether the second invocation succeeded\r\n"
+" or not.\r\n"
+" 2. The initrd is not unloaded when the shell exits, and will remain active\r\n"
+" until it is unloaded again by a different invocation of the shell.\r\n"
+" Consumers of the LoadFile2 protocol on the LINUX_EFI_INITRD_MEDIA_GUID\r\n"
+" device path that are started via means other than the shell will be able\r\n"
+" to locate the protocol and invoke it.\r\n"