2 Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
3 via its GUIDed vendor media path
5 Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
12 #include <Library/DebugLib.h>
13 #include <Library/DevicePathLib.h>
14 #include <Library/HiiLib.h>
15 #include <Library/MemoryAllocationLib.h>
16 #include <Library/ShellLib.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/UefiHiiServicesLib.h>
20 #include <Guid/LinuxEfiInitrdMedia.h>
22 #include <Protocol/DevicePath.h>
23 #include <Protocol/HiiPackageList.h>
24 #include <Protocol/LoadFile2.h>
25 #include <Protocol/ShellDynamicCommand.h>
29 VENDOR_DEVICE_PATH VenMediaNode
;
30 EFI_DEVICE_PATH_PROTOCOL EndNode
;
31 } SINGLE_NODE_VENDOR_MEDIA_DEVPATH
;
34 STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle
;
35 STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress
;
36 STATIC UINTN mInitrdFileSize
;
37 STATIC EFI_HANDLE mInitrdLoadFile2Handle
;
39 STATIC CONST SHELL_PARAM_ITEM ParamList
[] = {
44 STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath
= {
47 MEDIA_DEVICE_PATH
, MEDIA_VENDOR_DP
, { sizeof (VENDOR_DEVICE_PATH
) }
49 LINUX_EFI_INITRD_MEDIA_GUID
51 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
52 { sizeof (EFI_DEVICE_PATH_PROTOCOL
) }
58 IsOtherInitrdDevicePathAlreadyInstalled (
63 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
66 DevicePath
= (EFI_DEVICE_PATH_PROTOCOL
*)&mInitrdDevicePath
;
67 Status
= gBS
->LocateDevicePath (&gEfiLoadFile2ProtocolGuid
, &DevicePath
,
69 if (EFI_ERROR (Status
)) {
74 // Check whether the existing instance is one that we installed during
75 // a previous invocation.
77 if (Handle
== mInitrdLoadFile2Handle
) {
87 IN EFI_LOAD_FILE2_PROTOCOL
*This
,
88 IN EFI_DEVICE_PATH_PROTOCOL
*FilePath
,
89 IN BOOLEAN BootPolicy
,
90 IN OUT UINTN
*BufferSize
,
91 OUT VOID
*Buffer OPTIONAL
95 return EFI_UNSUPPORTED
;
98 if (BufferSize
== NULL
|| !IsDevicePathValid (FilePath
, 0)) {
99 return EFI_INVALID_PARAMETER
;
102 if (FilePath
->Type
!= END_DEVICE_PATH_TYPE
||
103 FilePath
->SubType
!= END_ENTIRE_DEVICE_PATH_SUBTYPE
||
104 mInitrdFileSize
== 0) {
105 return EFI_NOT_FOUND
;
108 if (Buffer
== NULL
|| *BufferSize
< mInitrdFileSize
) {
109 *BufferSize
= mInitrdFileSize
;
110 return EFI_BUFFER_TOO_SMALL
;
113 ASSERT (mInitrdFileAddress
!= 0);
115 gBS
->CopyMem (Buffer
, (VOID
*)(UINTN
)mInitrdFileAddress
, mInitrdFileSize
);
116 *BufferSize
= mInitrdFileSize
;
120 STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2
= {
126 UninstallLoadFile2Protocol (
132 if (mInitrdLoadFile2Handle
!= NULL
) {
133 Status
= gBS
->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle
,
134 &gEfiDevicePathProtocolGuid
, &mInitrdDevicePath
,
135 &gEfiLoadFile2ProtocolGuid
, &mInitrdLoadFile2
,
137 if (!EFI_ERROR (Status
)) {
138 mInitrdLoadFile2Handle
= NULL
;
151 if (mInitrdFileSize
!= 0) {
152 gBS
->FreePages (mInitrdFileAddress
, EFI_SIZE_TO_PAGES (mInitrdFileSize
));
160 IN SHELL_FILE_HANDLE FileHandle
167 Status
= gEfiShellProtocol
->GetFileSize (FileHandle
, &FileSize
);
168 if (EFI_ERROR (Status
)) {
172 if (FileSize
== 0 || FileSize
> MAX_UINTN
) {
173 return EFI_UNSUPPORTED
;
176 Status
= gBS
->AllocatePages (AllocateAnyPages
, EfiLoaderData
,
177 EFI_SIZE_TO_PAGES ((UINTN
)FileSize
), &mInitrdFileAddress
);
178 if (EFI_ERROR (Status
)) {
182 ReadSize
= (UINTN
)FileSize
;
183 Status
= gEfiShellProtocol
->ReadFile (FileHandle
, &ReadSize
,
184 (VOID
*)(UINTN
)mInitrdFileAddress
);
185 if (EFI_ERROR (Status
) || ReadSize
< FileSize
) {
186 DEBUG ((DEBUG_WARN
, "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
187 __FUNCTION__
, Status
, (UINT64
)ReadSize
, FileSize
));
191 if (mInitrdLoadFile2Handle
== NULL
) {
192 Status
= gBS
->InstallMultipleProtocolInterfaces (&mInitrdLoadFile2Handle
,
193 &gEfiDevicePathProtocolGuid
, &mInitrdDevicePath
,
194 &gEfiLoadFile2ProtocolGuid
, &mInitrdLoadFile2
,
196 ASSERT_EFI_ERROR (Status
);
199 mInitrdFileSize
= FileSize
;
203 gBS
->FreePages (mInitrdFileAddress
, EFI_SIZE_TO_PAGES ((UINTN
)FileSize
));
208 Function for 'initrd' command.
210 @param[in] ImageHandle Handle to the Image (NULL if Internal).
211 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
217 IN EFI_HANDLE ImageHandle
,
218 IN EFI_SYSTEM_TABLE
*SystemTable
223 CHAR16
*ProblemParam
;
226 SHELL_STATUS ShellStatus
;
227 SHELL_FILE_HANDLE FileHandle
;
230 ShellStatus
= SHELL_SUCCESS
;
232 Status
= ShellInitialize ();
233 ASSERT_EFI_ERROR (Status
);
236 // parse the command line
238 Status
= ShellCommandLineParse (ParamList
, &Package
, &ProblemParam
, TRUE
);
239 if (EFI_ERROR (Status
)) {
240 if (Status
== EFI_VOLUME_CORRUPTED
&& ProblemParam
!= NULL
) {
241 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_PROBLEM
),
242 mLinuxInitrdShellCommandHiiHandle
, L
"initrd", ProblemParam
);
243 FreePool (ProblemParam
);
244 ShellStatus
= SHELL_INVALID_PARAMETER
;
248 } else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
249 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_ALREADY_INSTALLED
),
250 mLinuxInitrdShellCommandHiiHandle
, L
"initrd");
251 ShellStatus
= SHELL_UNSUPPORTED
;
253 if (ShellCommandLineGetCount (Package
) > 2) {
254 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_MANY
),
255 mLinuxInitrdShellCommandHiiHandle
, L
"initrd");
256 ShellStatus
= SHELL_INVALID_PARAMETER
;
257 } else if (ShellCommandLineGetCount (Package
) < 2) {
258 if (ShellCommandLineGetFlag (Package
, L
"-u")) {
260 UninstallLoadFile2Protocol ();
262 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_TOO_FEW
),
263 mLinuxInitrdShellCommandHiiHandle
, L
"initrd");
264 ShellStatus
= SHELL_INVALID_PARAMETER
;
267 Param
= ShellCommandLineGetRawValue (Package
, 1);
268 ASSERT (Param
!= NULL
);
270 Filename
= ShellFindFilePath (Param
);
271 if (Filename
== NULL
) {
272 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_FIND_FAIL
),
273 mLinuxInitrdShellCommandHiiHandle
, L
"initrd", Param
);
274 ShellStatus
= SHELL_NOT_FOUND
;
276 Status
= ShellOpenFileByName (Filename
, &FileHandle
,
277 EFI_FILE_MODE_READ
, 0);
278 if (!EFI_ERROR (Status
)) {
280 Status
= CacheInitrdFile (FileHandle
);
281 ShellCloseFile (&FileHandle
);
283 if (EFI_ERROR (Status
)) {
284 ShellPrintHiiEx (-1, -1, NULL
, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL
),
285 mLinuxInitrdShellCommandHiiHandle
, L
"initrd", Param
);
286 ShellStatus
= SHELL_NOT_FOUND
;
297 This is the shell command handler function pointer callback type. This
298 function handles the command when it is invoked in the shell.
300 @param[in] This The instance of the
301 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
302 @param[in] SystemTable The pointer to the system table.
303 @param[in] ShellParameters The parameters associated with the command.
304 @param[in] Shell The instance of the shell protocol used in
305 the context of processing this command.
307 @return EFI_SUCCESS the operation was successful
308 @return other the operation failed.
312 LinuxInitrdCommandHandler (
313 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL
*This
,
314 IN EFI_SYSTEM_TABLE
*SystemTable
,
315 IN EFI_SHELL_PARAMETERS_PROTOCOL
*ShellParameters
,
316 IN EFI_SHELL_PROTOCOL
*Shell
319 gEfiShellParametersProtocol
= ShellParameters
;
320 gEfiShellProtocol
= Shell
;
322 return RunInitrd (gImageHandle
, SystemTable
);
326 This is the command help handler function pointer callback type. This
327 function is responsible for displaying help information for the associated
330 @param[in] This The instance of the
331 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
332 @param[in] Language The pointer to the language string to use.
334 @return string Pool allocated help string, must be freed
341 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL
*This
,
342 IN CONST CHAR8
*Language
345 return HiiGetString (mLinuxInitrdShellCommandHiiHandle
,
346 STRING_TOKEN (STR_GET_HELP_INITRD
), Language
);
349 STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand
= {
351 LinuxInitrdCommandHandler
,
356 Retrieve HII package list from ImageHandle and publish to HII database.
358 @param ImageHandle The image handle of the process.
364 InitializeHiiPackage (
365 EFI_HANDLE ImageHandle
369 EFI_HII_PACKAGE_LIST_HEADER
*PackageList
;
370 EFI_HII_HANDLE HiiHandle
;
373 // Retrieve HII package list from ImageHandle
375 Status
= gBS
->OpenProtocol (ImageHandle
, &gEfiHiiPackageListProtocolGuid
,
376 (VOID
**)&PackageList
, ImageHandle
, NULL
,
377 EFI_OPEN_PROTOCOL_GET_PROTOCOL
);
378 ASSERT_EFI_ERROR (Status
);
379 if (EFI_ERROR (Status
)) {
384 // Publish HII package list to HII Database.
386 Status
= gHiiDatabase
->NewPackageList (gHiiDatabase
, PackageList
, NULL
,
388 ASSERT_EFI_ERROR (Status
);
389 if (EFI_ERROR (Status
)) {
396 Entry point of Linux Initrd dynamic UEFI Shell command.
398 Produce the DynamicCommand protocol to handle "initrd" command.
400 @param ImageHandle The image handle of the process.
401 @param SystemTable The EFI System Table pointer.
403 @retval EFI_SUCCESS Initrd command is executed successfully.
404 @retval EFI_ABORTED HII package was failed to initialize.
405 @retval others Other errors when executing Initrd command.
409 LinuxInitrdDynamicShellCommandEntryPoint (
410 IN EFI_HANDLE ImageHandle
,
411 IN EFI_SYSTEM_TABLE
*SystemTable
416 mLinuxInitrdShellCommandHiiHandle
= InitializeHiiPackage (ImageHandle
);
417 if (mLinuxInitrdShellCommandHiiHandle
== NULL
) {
421 Status
= gBS
->InstallProtocolInterface (&ImageHandle
,
422 &gEfiShellDynamicCommandProtocolGuid
,
423 EFI_NATIVE_INTERFACE
,
424 &mLinuxInitrdDynamicCommand
);
425 ASSERT_EFI_ERROR (Status
);
430 Unload the dynamic UEFI Shell command.
432 @param ImageHandle The image handle of the process.
434 @retval EFI_SUCCESS The image is unloaded.
435 @retval Others Failed to unload the image.
439 LinuxInitrdDynamicShellCommandUnload (
440 IN EFI_HANDLE ImageHandle
447 Status
= UninstallLoadFile2Protocol ();
448 if (EFI_ERROR (Status
)) {
452 Status
= gBS
->UninstallProtocolInterface (ImageHandle
,
453 &gEfiShellDynamicCommandProtocolGuid
,
454 &mLinuxInitrdDynamicCommand
);
455 if (EFI_ERROR (Status
)) {
459 HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle
);