public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
* [RFC PATCH 1/1] OvmfPkg: add 'initrd' shell command to expose Linux initrd via device path
@ 2020-02-11 18:03 Ard Biesheuvel
  2020-02-12 11:24 ` Laszlo Ersek
  0 siblings, 1 reply; 7+ messages in thread
From: Ard Biesheuvel @ 2020-02-11 18:03 UTC (permalink / raw)
  To: devel; +Cc: lersek, leif, philmd, Ard Biesheuvel

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 <ard.biesheuvel@arm.com>
---
Note that the vendor media device path initrd loading method is still
under review on the Linux side, so this is for discussion only for now.

 ArmVirtPkg/ArmVirt.dsc.inc                                                |   1 +
 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.c   | 269 ++++++++++++++++++++
 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.inf |  49 ++++
 OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni |  37 +++
 OvmfPkg/OvmfPkg.dec                                                       |   1 +
 OvmfPkg/OvmfPkgIa32.dsc                                                   |   1 +
 OvmfPkg/OvmfPkgIa32X64.dsc                                                |   1 +
 OvmfPkg/OvmfPkgX64.dsc                                                    |   1 +
 8 files changed, 360 insertions(+)

diff --git a/ArmVirtPkg/ArmVirt.dsc.inc b/ArmVirtPkg/ArmVirt.dsc.inc
index 10037c938eb8..0570eba6ed0c 100644
--- a/ArmVirtPkg/ArmVirt.dsc.inc
+++ b/ArmVirtPkg/ArmVirt.dsc.inc
@@ -382,6 +382,7 @@ [Components.common]
   ShellPkg/Application/Shell/Shell.inf {
     <LibraryClasses>
       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 000000000000..cfa4526df6d8
--- /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.<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/ShellCommandLib.h>
+#include <Library/ShellLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+
+#include <Protocol/DevicePath.h>
+#include <Protocol/LoadFile2.h>
+
+#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"<unspecified>";
+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
+  )
+{
+  UINTN                     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) {
+            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) {
+      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 000000000000..ed8a0ca85e85
--- /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.<BR>
+#
+#  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 000000000000..d4fe798b1ea2
--- /dev/null
+++ b/OvmfPkg/Library/LinuxInitrdShellCommandLib/LinuxInitrdShellCommandLib.uni
@@ -0,0 +1,37 @@
+// /**
+//
+// Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
+// 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 <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"
diff --git a/OvmfPkg/OvmfPkg.dec b/OvmfPkg/OvmfPkg.dec
index d5fee805ef4a..437b1d248895 100644
--- a/OvmfPkg/OvmfPkg.dec
+++ b/OvmfPkg/OvmfPkg.dec
@@ -86,6 +86,7 @@ [Guids]
   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 9a60eb8fe2b0..3ffaa9b3388f 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -817,6 +817,7 @@ [Components]
   ShellPkg/Application/Shell/Shell.inf {
     <LibraryClasses>
       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 1d1480b50b02..691ba204a6ac 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -830,6 +830,7 @@ [Components.X64]
   ShellPkg/Application/Shell/Shell.inf {
     <LibraryClasses>
       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 c287a436f8ec..8df5390de132 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -828,6 +828,7 @@ [Components]
   ShellPkg/Application/Shell/Shell.inf {
     <LibraryClasses>
       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
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2020-02-14 14:48 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2020-02-11 18:03 [RFC PATCH 1/1] OvmfPkg: add 'initrd' shell command to expose Linux initrd via device path Ard Biesheuvel
2020-02-12 11:24 ` Laszlo Ersek
2020-02-12 14:21   ` [edk2-devel] " Ni, Ray
2020-02-13 23:14     ` Laszlo Ersek
2020-02-14  0:55       ` Ni, Ray
2020-02-14 14:17         ` Laszlo Ersek
2020-02-14 14:48           ` Ard Biesheuvel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox