|
@@ -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;
|
|
|
+}
|