--- /dev/null
+/** @file\r
+ Provides 'initrd' dynamic UEFI shell command to load a Linux initrd\r
+ via its GUIDed vendor media path\r
+\r
+ Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <Uefi.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/DevicePathLib.h>\r
+#include <Library/HiiLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/ShellLib.h>\r
+#include <Library/UefiBootServicesTableLib.h>\r
+#include <Library/UefiHiiServicesLib.h>\r
+\r
+#include <Guid/LinuxEfiInitrdMedia.h>\r
+\r
+#include <Protocol/DevicePath.h>\r
+#include <Protocol/HiiPackageList.h>\r
+#include <Protocol/LoadFile2.h>\r
+#include <Protocol/ShellDynamicCommand.h>\r
+\r
+#pragma pack (1)\r
+typedef struct {\r
+ VENDOR_DEVICE_PATH VenMediaNode;\r
+ EFI_DEVICE_PATH_PROTOCOL EndNode;\r
+} SINGLE_NODE_VENDOR_MEDIA_DEVPATH;\r
+#pragma pack ()\r
+\r
+STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle;\r
+STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress;\r
+STATIC UINTN mInitrdFileSize;\r
+STATIC EFI_HANDLE mInitrdLoadFile2Handle;\r
+\r
+STATIC CONST SHELL_PARAM_ITEM ParamList[] = {\r
+ {L"-u", TypeFlag},\r
+ {NULL, TypeMax}\r
+ };\r
+\r
+STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = {\r
+ {\r
+ {\r
+ MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) }\r
+ },\r
+ LINUX_EFI_INITRD_MEDIA_GUID\r
+ }, {\r
+ END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
+ { sizeof (EFI_DEVICE_PATH_PROTOCOL) }\r
+ }\r
+};\r
+\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+InitrdLoadFile2 (\r
+ IN EFI_LOAD_FILE2_PROTOCOL *This,\r
+ IN EFI_DEVICE_PATH_PROTOCOL *FilePath,\r
+ IN BOOLEAN BootPolicy,\r
+ IN OUT UINTN *BufferSize,\r
+ OUT VOID *Buffer OPTIONAL\r
+ )\r
+{\r
+ if (BootPolicy) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {\r
+ return EFI_INVALID_PARAMETER;\r
+ }\r
+\r
+ if (FilePath->Type != END_DEVICE_PATH_TYPE ||\r
+ FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE ||\r
+ mInitrdFileSize == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if (Buffer == NULL || *BufferSize < mInitrdFileSize) {\r
+ *BufferSize = mInitrdFileSize;\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ ASSERT (mInitrdFileAddress != 0);\r
+\r
+ gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);\r
+ *BufferSize = mInitrdFileSize;\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {\r
+ InitrdLoadFile2,\r
+};\r
+\r
+STATIC\r
+EFI_STATUS\r
+UninstallLoadFile2Protocol (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ if (mInitrdLoadFile2Handle != NULL) {\r
+ Status = gBS->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle,\r
+ &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,\r
+ &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,\r
+ NULL);\r
+ if (!EFI_ERROR (Status)) {\r
+ mInitrdLoadFile2Handle = NULL;\r
+ }\r
+ }\r
+ return Status;\r
+}\r
+\r
+STATIC\r
+VOID\r
+FreeInitrdFile (\r
+ VOID\r
+ )\r
+{\r
+ if (mInitrdFileSize != 0) {\r
+ gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));\r
+ mInitrdFileSize = 0;\r
+ }\r
+}\r
+\r
+STATIC\r
+EFI_STATUS\r
+CacheInitrdFile (\r
+ IN SHELL_FILE_HANDLE FileHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 FileSize;\r
+ UINTN ReadSize;\r
+\r
+ Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ if (FileSize == 0 || FileSize > MAX_UINTN) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ Status = gBS->AllocatePages (AllocateAnyPages, EfiLoaderData,\r
+ EFI_SIZE_TO_PAGES ((UINTN)FileSize), &mInitrdFileAddress);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ ReadSize = (UINTN)FileSize;\r
+ Status = gEfiShellProtocol->ReadFile (FileHandle, &ReadSize,\r
+ (VOID *)(UINTN)mInitrdFileAddress);\r
+ if (EFI_ERROR (Status) || ReadSize < FileSize) {\r
+ DEBUG ((DEBUG_WARN, "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",\r
+ __FUNCTION__, Status, (UINT64)ReadSize, FileSize));\r
+ goto FreeMemory;\r
+ }\r
+\r
+ if (mInitrdLoadFile2Handle == NULL) {\r
+ Status = gBS->InstallMultipleProtocolInterfaces (&mInitrdLoadFile2Handle,\r
+ &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,\r
+ &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,\r
+ NULL);\r
+ ASSERT_EFI_ERROR (Status);\r
+ }\r
+\r
+ mInitrdFileSize = FileSize;\r
+ return EFI_SUCCESS;\r
+\r
+FreeMemory:\r
+ gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Function for 'initrd' command.\r
+\r
+ @param[in] ImageHandle Handle to the Image (NULL if Internal).\r
+ @param[in] SystemTable Pointer to the System Table (NULL if Internal).\r
+**/\r
+STATIC\r
+SHELL_STATUS\r
+EFIAPI\r
+RunInitrd (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Package;\r
+ CHAR16 *ProblemParam;\r
+ CONST CHAR16 *Param;\r
+ CHAR16 *Filename;\r
+ SHELL_STATUS ShellStatus;\r
+ SHELL_FILE_HANDLE FileHandle;\r
+\r
+ ProblemParam = NULL;\r
+ ShellStatus = SHELL_SUCCESS;\r
+\r
+ Status = ShellInitialize ();\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // parse the command line\r
+ //\r
+ Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM),\r
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", ProblemParam);\r
+ FreePool (ProblemParam);\r
+ ShellStatus = SHELL_INVALID_PARAMETER;\r
+ } else {\r
+ ASSERT(FALSE);\r
+ }\r
+ } else {\r
+ if (ShellCommandLineGetCount (Package) > 2) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),\r
+ mLinuxInitrdShellCommandHiiHandle, L"initrd");\r
+ ShellStatus = SHELL_INVALID_PARAMETER;\r
+ } else if (ShellCommandLineGetCount (Package) < 2) {\r
+ if (ShellCommandLineGetFlag (Package, L"-u")) {\r
+ FreeInitrdFile ();\r
+ UninstallLoadFile2Protocol ();\r
+ } else {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),\r
+ mLinuxInitrdShellCommandHiiHandle, L"initrd");\r
+ ShellStatus = SHELL_INVALID_PARAMETER;\r
+ }\r
+ } else {\r
+ Param = ShellCommandLineGetRawValue (Package, 1);\r
+ ASSERT (Param != NULL);\r
+\r
+ Filename = ShellFindFilePath (Param);\r
+ if (Filename == NULL) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FIND_FAIL),\r
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);\r
+ ShellStatus = SHELL_NOT_FOUND;\r
+ } else {\r
+ Status = ShellOpenFileByName (Filename, &FileHandle,\r
+ EFI_FILE_MODE_READ, 0);\r
+ if (!EFI_ERROR (Status)) {\r
+ FreeInitrdFile ();\r
+ Status = CacheInitrdFile (FileHandle);\r
+ ShellCloseFile (&FileHandle);\r
+ }\r
+ if (EFI_ERROR (Status)) {\r
+ ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),\r
+ mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);\r
+ ShellStatus = SHELL_NOT_FOUND;\r
+ }\r
+ FreePool (Filename);\r
+ }\r
+ }\r
+ }\r
+ return ShellStatus;\r
+}\r
+\r
+\r
+/**\r
+ This is the shell command handler function pointer callback type. This\r
+ function handles the command when it is invoked in the shell.\r
+\r
+ @param[in] This The instance of the\r
+ EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.\r
+ @param[in] SystemTable The pointer to the system table.\r
+ @param[in] ShellParameters The parameters associated with the command.\r
+ @param[in] Shell The instance of the shell protocol used in\r
+ the context of processing this command.\r
+\r
+ @return EFI_SUCCESS the operation was successful\r
+ @return other the operation failed.\r
+**/\r
+SHELL_STATUS\r
+EFIAPI\r
+LinuxInitrdCommandHandler (\r
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,\r
+ IN EFI_SYSTEM_TABLE *SystemTable,\r
+ IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,\r
+ IN EFI_SHELL_PROTOCOL *Shell\r
+ )\r
+{\r
+ gEfiShellParametersProtocol = ShellParameters;\r
+ gEfiShellProtocol = Shell;\r
+\r
+ return RunInitrd (gImageHandle, SystemTable);\r
+}\r
+\r
+/**\r
+ This is the command help handler function pointer callback type. This\r
+ function is responsible for displaying help information for the associated\r
+ command.\r
+\r
+ @param[in] This The instance of the\r
+ EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.\r
+ @param[in] Language The pointer to the language string to use.\r
+\r
+ @return string Pool allocated help string, must be freed\r
+ by caller\r
+**/\r
+STATIC\r
+CHAR16 *\r
+EFIAPI\r
+LinuxInitrdGetHelp (\r
+ IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,\r
+ IN CONST CHAR8 *Language\r
+ )\r
+{\r
+ return HiiGetString (mLinuxInitrdShellCommandHiiHandle,\r
+ STRING_TOKEN (STR_GET_HELP_INITRD), Language);\r
+}\r
+\r
+STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {\r
+ L"initrd",\r
+ LinuxInitrdCommandHandler,\r
+ LinuxInitrdGetHelp\r
+};\r
+\r
+/**\r
+ Retrieve HII package list from ImageHandle and publish to HII database.\r
+\r
+ @param ImageHandle The image handle of the process.\r
+\r
+ @return HII handle.\r
+**/\r
+STATIC\r
+EFI_HII_HANDLE\r
+InitializeHiiPackage (\r
+ EFI_HANDLE ImageHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HII_PACKAGE_LIST_HEADER *PackageList;\r
+ EFI_HII_HANDLE HiiHandle;\r
+\r
+ //\r
+ // Retrieve HII package list from ImageHandle\r
+ //\r
+ Status = gBS->OpenProtocol (ImageHandle, &gEfiHiiPackageListProtocolGuid,\r
+ (VOID **)&PackageList, ImageHandle, NULL,\r
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Publish HII package list to HII Database.\r
+ //\r
+ Status = gHiiDatabase->NewPackageList (gHiiDatabase, PackageList, NULL,\r
+ &HiiHandle);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return NULL;\r
+ }\r
+ return HiiHandle;\r
+}\r
+\r
+/**\r
+ Entry point of Linux Initrd dynamic UEFI Shell command.\r
+\r
+ Produce the DynamicCommand protocol to handle "initrd" command.\r
+\r
+ @param ImageHandle The image handle of the process.\r
+ @param SystemTable The EFI System Table pointer.\r
+\r
+ @retval EFI_SUCCESS Initrd command is executed successfully.\r
+ @retval EFI_ABORTED HII package was failed to initialize.\r
+ @retval others Other errors when executing Initrd command.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LinuxInitrdDynamicShellCommandEntryPoint (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);\r
+ if (mLinuxInitrdShellCommandHiiHandle == NULL) {\r
+ return EFI_ABORTED;\r
+ }\r
+\r
+ Status = gBS->InstallProtocolInterface (&ImageHandle,\r
+ &gEfiShellDynamicCommandProtocolGuid,\r
+ EFI_NATIVE_INTERFACE,\r
+ &mLinuxInitrdDynamicCommand);\r
+ ASSERT_EFI_ERROR (Status);\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Unload the dynamic UEFI Shell command.\r
+\r
+ @param ImageHandle The image handle of the process.\r
+\r
+ @retval EFI_SUCCESS The image is unloaded.\r
+ @retval Others Failed to unload the image.\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+LinuxInitrdDynamicShellCommandUnload (\r
+ IN EFI_HANDLE ImageHandle\r
+)\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ FreeInitrdFile ();\r
+\r
+ Status = UninstallLoadFile2Protocol ();\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->UninstallProtocolInterface (ImageHandle,\r
+ &gEfiShellDynamicCommandProtocolGuid,\r
+ &mLinuxInitrdDynamicCommand);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);\r
+ return EFI_SUCCESS;\r
+}\r