From ac8f3ddf03c4fcda186a42918a234733153e42eb Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Tue, 11 Feb 2020 18:45:59 +0100 Subject: [PATCH] OvmfPkg: add 'initrd' shell command to expose Linux initrd via device path Add a new 'initrd' command to the UEFI Shell that allows any file that is accessible to the shell to be registered as the initrd that is returned when Linux's EFI stub loader invokes the LoadFile2 protocol on its special vendor media device path. Signed-off-by: Ard Biesheuvel --- ArmVirtPkg/ArmVirt.dsc.inc | 1 + .../LinuxInitrdShellCommandLib.c | 269 ++++++++++++++++++ .../LinuxInitrdShellCommandLib.inf | 49 ++++ .../LinuxInitrdShellCommandLib.uni | 37 +++ OvmfPkg/OvmfPkg.dec | 1 + OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgX64.dsc | 1 + 8 files changed, 360 insertions(+) create mode 100644 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.c create mode 100644 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf create mode 100644 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc index 10037c938eb82..0570eba6ed0c5 100644 --- a/ArmVirtPkg/ArmVirt.dsc.inc +++ b/ArmVirtPkg/ArmVirt.dsc.inc @@ -382,6 +382,7 @@ ShellPkg/Application/Shell/Shell.inf { ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf + NULL|OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf diff --git a/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.c b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.c new file mode 100644 index 0000000000000..af5a0a56ea772 --- /dev/null +++ b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.c @@ -0,0 +1,269 @@ +/** @file + Provides initrd command to load a Linux initrd via its GUIDed vendor media + path + + Copyright (c) 2020, Arm, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#pragma pack(1) +typedef struct { + VENDOR_DEVICE_PATH VenMediaNode; + EFI_DEVICE_PATH_PROTOCOL EndNode; +} SINGLE_NODE_VENDOR_MEDIA_DEVPATH; +#pragma pack() + +STATIC CONST CHAR16 mFileName[] = L""; +STATIC EFI_HII_HANDLE gLinuxInitrdShellCommandHiiHandle; +STATIC SHELL_FILE_HANDLE mInitrdFileHandle; +STATIC EFI_HANDLE mInitrdLoadFile2Handle; + +STATIC CONST SHELL_PARAM_ITEM ParamList[] = { + {L"-u", TypeFlag}, + {NULL, TypeMax} + }; + +/** + Get the filename to get help text from if not using HII. + + @retval The filename. +**/ +STATIC +CONST CHAR16* +EFIAPI +ShellCommandGetManFileNameInitrd ( + VOID + ) +{ + return mFileName; +} + +STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = { + { + { + MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } + }, + { + // LINUX_EFI_INITRD_MEDIA_GUID + 0x5568e427, 0x68fc, 0x4f3d, + { 0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68 } + } + }, + + { + 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, + IN VOID *Buffer OPTIONAL + ) +{ + UINT64 InitrdSize; + EFI_STATUS Status; + + 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 || + mInitrdFileHandle == NULL) { + return EFI_NOT_FOUND; + } + + Status = gEfiShellProtocol->GetFileSize (mInitrdFileHandle, &InitrdSize); + ASSERT_EFI_ERROR(Status); + + if (Buffer == NULL || *BufferSize < InitrdSize) { + *BufferSize = InitrdSize; + return EFI_BUFFER_TOO_SMALL; + } + + return gEfiShellProtocol->ReadFile (mInitrdFileHandle, BufferSize, Buffer); +} + +STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = { + InitrdLoadFile2, +}; + +/** + 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). +**/ +SHELL_STATUS +EFIAPI +ShellCommandRunInitrd ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + LIST_ENTRY *Package; + CHAR16 *ProblemParam; + CONST CHAR16 *Param; + CONST CHAR16 *Filename; + SHELL_STATUS ShellStatus; + + 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), + gLinuxInitrdShellCommandHiiHandle, 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), + gLinuxInitrdShellCommandHiiHandle, L"initrd"); + ShellStatus = SHELL_INVALID_PARAMETER; + } else if (ShellCommandLineGetCount (Package) < 2) { + if (ShellCommandLineGetFlag(Package, L"-u")) { + if (mInitrdFileHandle != NULL) { + ShellCloseFile (&mInitrdFileHandle); + mInitrdFileHandle = NULL; + } + if (mInitrdLoadFile2Handle != NULL) { + gBS->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle, + &gEfiDevicePathProtocolGuid, &mInitrdDevicePath, + &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2, + NULL); + mInitrdLoadFile2Handle = NULL; + } + } else { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW), + gLinuxInitrdShellCommandHiiHandle, 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), + gLinuxInitrdShellCommandHiiHandle, L"initrd", Param); + ShellStatus = SHELL_NOT_FOUND; + } else { + if (mInitrdFileHandle != NULL) { + ShellCloseFile (&mInitrdFileHandle); + mInitrdFileHandle = NULL; + } + Status = ShellOpenFileByName (Filename, &mInitrdFileHandle, + EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL), + gLinuxInitrdShellCommandHiiHandle, L"initrd", Param); + ShellStatus = SHELL_NOT_FOUND; + } + if (mInitrdLoadFile2Handle == NULL) { + Status = gBS->InstallMultipleProtocolInterfaces ( + &mInitrdLoadFile2Handle, + &gEfiDevicePathProtocolGuid, &mInitrdDevicePath, + &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2, + NULL); + ASSERT_EFI_ERROR (Status); + } + } + } + } + + return ShellStatus; +} + +/** + Constructor for the 'initrd' UEFI Shell command library + + @param ImageHandle the image handle of the process + @param SystemTable the EFI System Table pointer + + @retval EFI_SUCCESS the shell command handlers were installed sucessfully + @retval EFI_UNSUPPORTED the shell level required was not found. +**/ +EFI_STATUS +EFIAPI +LinuxInitrdShellCommandLibConstructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + gLinuxInitrdShellCommandHiiHandle = HiiAddPackages (&gShellInitrdHiiGuid, + gImageHandle, LinuxInitrdShellCommandLibStrings, NULL); + if (gLinuxInitrdShellCommandHiiHandle == NULL) { + return EFI_DEVICE_ERROR; + } + + ShellCommandRegisterCommandName (L"initrd", ShellCommandRunInitrd, + ShellCommandGetManFileNameInitrd, 0, L"initrd", TRUE, + gLinuxInitrdShellCommandHiiHandle, STRING_TOKEN(STR_GET_HELP_INITRD)); + + return EFI_SUCCESS; +} + +/** + Destructor for the library. free any resources. + + @param ImageHandle The image handle of the process. + @param SystemTable The EFI System Table pointer. + + @retval EFI_SUCCESS Always returned. +**/ +EFI_STATUS +EFIAPI +LinuxInitrdShellCommandLibDestructor ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + if (gLinuxInitrdShellCommandHiiHandle != NULL) { + HiiRemovePackages (gLinuxInitrdShellCommandHiiHandle); + } + + if (mInitrdLoadFile2Handle != NULL) { + gBS->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle, + &gEfiDevicePathProtocolGuid, &mInitrdDevicePath, + &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2, + NULL); + } + return EFI_SUCCESS; +} diff --git a/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf new file mode 100644 index 0000000000000..ed8a0ca85e85b --- /dev/null +++ b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf @@ -0,0 +1,49 @@ +## @file +# Provides initrd command to load a Linux initrd via its GUIDed vendor media +# path +# +# Copyright (c) 2020, Arm, Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = LinuxInitrdShellCommandLib + FILE_GUID = 2f30da26-f51b-4b6f-85c4-31873c281bca + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + LIBRARY_CLASS = NULL|UEFI_APPLICATION UEFI_DRIVER + CONSTRUCTOR = LinuxInitrdShellCommandLibConstructor + DESTRUCTOR = LinuxInitrdShellCommandLibDestructor + +# +# VALID_ARCHITECTURES = IA32 X64 ARM AARCH64 +# + +[Sources.common] + LinuxInitrdShellCommandLib.c + LinuxInitrdShellCommandLib.uni + +[Packages] + MdePkg/MdePkg.dec + ShellPkg/ShellPkg.dec + MdeModulePkg/MdeModulePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + DebugLib + DevicePathLib + HiiLib + MemoryAllocationLib + ShellCommandLib + ShellLib + UefiBootServicesTableLib + +[Protocols] + gEfiLoadFile2ProtocolGuid ## SOMETIMES_PRODUCES + +[Guids] + gShellInitrdHiiGuid ## SOMETIMES_CONSUMES ## HII diff --git a/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni new file mode 100644 index 0000000000000..d4fe798b1ea2f --- /dev/null +++ b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni @@ -0,0 +1,37 @@ +// /** +// +// Copyright (c) 2020, Arm, Ltd. All rights reserved.
+// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// Module Name: +// +// LinuxInitrdShellCommandLib.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 vol 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 \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" diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec index d5fee805ef4a1..437b1d2488953 100644 --- a/OvmfPkg/OvmfPkg.dec +++ b/OvmfPkg/OvmfPkg.dec @@ -86,6 +86,7 @@ gMicrosoftVendorGuid = {0x77fa9abd, 0x0359, 0x4d32, {0xbd, 0x60, 0x28, 0xf4, 0xe7, 0x8f, 0x78, 0x4b}} gEfiLegacyBiosGuid = {0x2E3044AC, 0x879F, 0x490F, {0x97, 0x60, 0xBB, 0xDF, 0xAF, 0x69, 0x5F, 0x50}} gEfiLegacyDevOrderVariableGuid = {0xa56074db, 0x65fe, 0x45f7, {0xbd, 0x21, 0x2d, 0x2b, 0xdd, 0x8e, 0x96, 0x52}} + gShellInitrdHiiGuid = {0xeda423e2, 0xf434, 0x4cdd, {0x8d, 0x5d, 0xb5, 0xb0, 0xc7, 0x2c, 0x56, 0xd9}} [Protocols] gVirtioDeviceProtocolGuid = {0xfa920010, 0x6785, 0x4941, {0xb6, 0xec, 0x49, 0x8c, 0x57, 0x9f, 0x16, 0x0a}} diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 9a60eb8fe2b07..3ffaa9b3388f6 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -817,6 +817,7 @@ ShellPkg/Application/Shell/Shell.inf { ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf + NULL|OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 1d1480b50b029..691ba204a6ac1 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -830,6 +830,7 @@ ShellPkg/Application/Shell/Shell.inf { ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf + NULL|OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index c287a436f8ec3..8df5390de132d 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -828,6 +828,7 @@ ShellPkg/Application/Shell/Shell.inf { ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf + NULL|OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf