diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand')
3 files changed, 566 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c b/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c new file mode 100644 index 00000000..9b98c1e2 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c @@ -0,0 +1,461 @@ +/** @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 +BOOLEAN +IsOtherInitrdDevicePathAlreadyInstalled ( + VOID + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_HANDLE Handle; + + DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mInitrdDevicePath; + Status = gBS->LocateDevicePath (&gEfiLoadFile2ProtocolGuid, &DevicePath, + &Handle); + if (EFI_ERROR (Status)) { + return FALSE; + } + + // + // Check whether the existing instance is one that we installed during + // a previous invocation. + // + if (Handle == mInitrdLoadFile2Handle) { + return FALSE; + } + return TRUE; +} + +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; + } + return EFI_SUCCESS; +} + +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 = (UINTN)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 (IsOtherInitrdDevicePathAlreadyInstalled ()) { + ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_ALREADY_INSTALLED), + mLinuxInitrdShellCommandHiiHandle, L"initrd"); + ShellStatus = SHELL_UNSUPPORTED; + } 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/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf b/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.inf new file mode 100644 index 00000000..bb399b1f --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/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/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni b/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni new file mode 100644 index 00000000..4b6b1285 --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.uni @@ -0,0 +1,52 @@ +// /**
+//
+// 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_ALREADY_INSTALLED #language en-US "%H%s%N: Linux initrd already provided by platform\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"
+" 3. Exposing an initrd using this command is only supported if no initrd is\r\n"
+" already being exposed by another driver on the platform.\r\n"
|