]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
OvmfPkg: Add IntelTdx.h in OvmfPkg/Include/IndustryStandard
[mirror_edk2.git] / OvmfPkg / LinuxInitrdDynamicShellCommand / LinuxInitrdDynamicShellCommand.c
1 /** @file
2 Provides 'initrd' dynamic UEFI shell command to load a Linux initrd
3 via its GUIDed vendor media path
4
5 Copyright (c) 2020, Arm, Ltd. All rights reserved.<BR>
6
7 SPDX-License-Identifier: BSD-2-Clause-Patent
8 **/
9
10 #include <Uefi.h>
11
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>
19
20 #include <Guid/LinuxEfiInitrdMedia.h>
21
22 #include <Protocol/DevicePath.h>
23 #include <Protocol/HiiPackageList.h>
24 #include <Protocol/LoadFile2.h>
25 #include <Protocol/ShellDynamicCommand.h>
26
27 #pragma pack (1)
28 typedef struct {
29 VENDOR_DEVICE_PATH VenMediaNode;
30 EFI_DEVICE_PATH_PROTOCOL EndNode;
31 } SINGLE_NODE_VENDOR_MEDIA_DEVPATH;
32 #pragma pack ()
33
34 STATIC EFI_HII_HANDLE mLinuxInitrdShellCommandHiiHandle;
35 STATIC EFI_PHYSICAL_ADDRESS mInitrdFileAddress;
36 STATIC UINTN mInitrdFileSize;
37 STATIC EFI_HANDLE mInitrdLoadFile2Handle;
38
39 STATIC CONST SHELL_PARAM_ITEM ParamList[] = {
40 { L"-u", TypeFlag },
41 { NULL, TypeMax }
42 };
43
44 STATIC CONST SINGLE_NODE_VENDOR_MEDIA_DEVPATH mInitrdDevicePath = {
45 {
46 {
47 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,{ sizeof (VENDOR_DEVICE_PATH) }
48 },
49 LINUX_EFI_INITRD_MEDIA_GUID
50 },{
51 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
52 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
53 }
54 };
55
56 STATIC
57 BOOLEAN
58 IsOtherInitrdDevicePathAlreadyInstalled (
59 VOID
60 )
61 {
62 EFI_STATUS Status;
63 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
64 EFI_HANDLE Handle;
65
66 DevicePath = (EFI_DEVICE_PATH_PROTOCOL *)&mInitrdDevicePath;
67 Status = gBS->LocateDevicePath (
68 &gEfiLoadFile2ProtocolGuid,
69 &DevicePath,
70 &Handle
71 );
72 if (EFI_ERROR (Status)) {
73 return FALSE;
74 }
75
76 //
77 // Check whether the existing instance is one that we installed during
78 // a previous invocation.
79 //
80 if (Handle == mInitrdLoadFile2Handle) {
81 return FALSE;
82 }
83
84 return TRUE;
85 }
86
87 STATIC
88 EFI_STATUS
89 EFIAPI
90 InitrdLoadFile2 (
91 IN EFI_LOAD_FILE2_PROTOCOL *This,
92 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
93 IN BOOLEAN BootPolicy,
94 IN OUT UINTN *BufferSize,
95 OUT VOID *Buffer OPTIONAL
96 )
97 {
98 if (BootPolicy) {
99 return EFI_UNSUPPORTED;
100 }
101
102 if ((BufferSize == NULL) || !IsDevicePathValid (FilePath, 0)) {
103 return EFI_INVALID_PARAMETER;
104 }
105
106 if ((FilePath->Type != END_DEVICE_PATH_TYPE) ||
107 (FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) ||
108 (mInitrdFileSize == 0))
109 {
110 return EFI_NOT_FOUND;
111 }
112
113 if ((Buffer == NULL) || (*BufferSize < mInitrdFileSize)) {
114 *BufferSize = mInitrdFileSize;
115 return EFI_BUFFER_TOO_SMALL;
116 }
117
118 ASSERT (mInitrdFileAddress != 0);
119
120 gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);
121 *BufferSize = mInitrdFileSize;
122 return EFI_SUCCESS;
123 }
124
125 STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
126 InitrdLoadFile2,
127 };
128
129 STATIC
130 EFI_STATUS
131 UninstallLoadFile2Protocol (
132 VOID
133 )
134 {
135 EFI_STATUS Status;
136
137 if (mInitrdLoadFile2Handle != NULL) {
138 Status = gBS->UninstallMultipleProtocolInterfaces (
139 mInitrdLoadFile2Handle,
140 &gEfiDevicePathProtocolGuid,
141 &mInitrdDevicePath,
142 &gEfiLoadFile2ProtocolGuid,
143 &mInitrdLoadFile2,
144 NULL
145 );
146 if (!EFI_ERROR (Status)) {
147 mInitrdLoadFile2Handle = NULL;
148 }
149
150 return Status;
151 }
152
153 return EFI_SUCCESS;
154 }
155
156 STATIC
157 VOID
158 FreeInitrdFile (
159 VOID
160 )
161 {
162 if (mInitrdFileSize != 0) {
163 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
164 mInitrdFileSize = 0;
165 }
166 }
167
168 STATIC
169 EFI_STATUS
170 CacheInitrdFile (
171 IN SHELL_FILE_HANDLE FileHandle
172 )
173 {
174 EFI_STATUS Status;
175 UINT64 FileSize;
176 UINTN ReadSize;
177
178 Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
179 if (EFI_ERROR (Status)) {
180 return Status;
181 }
182
183 if ((FileSize == 0) || (FileSize > MAX_UINTN)) {
184 return EFI_UNSUPPORTED;
185 }
186
187 Status = gBS->AllocatePages (
188 AllocateAnyPages,
189 EfiLoaderData,
190 EFI_SIZE_TO_PAGES ((UINTN)FileSize),
191 &mInitrdFileAddress
192 );
193 if (EFI_ERROR (Status)) {
194 return Status;
195 }
196
197 ReadSize = (UINTN)FileSize;
198 Status = gEfiShellProtocol->ReadFile (
199 FileHandle,
200 &ReadSize,
201 (VOID *)(UINTN)mInitrdFileAddress
202 );
203 if (EFI_ERROR (Status) || (ReadSize < FileSize)) {
204 DEBUG ((
205 DEBUG_WARN,
206 "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
207 __FUNCTION__,
208 Status,
209 (UINT64)ReadSize,
210 FileSize
211 ));
212 goto FreeMemory;
213 }
214
215 if (mInitrdLoadFile2Handle == NULL) {
216 Status = gBS->InstallMultipleProtocolInterfaces (
217 &mInitrdLoadFile2Handle,
218 &gEfiDevicePathProtocolGuid,
219 &mInitrdDevicePath,
220 &gEfiLoadFile2ProtocolGuid,
221 &mInitrdLoadFile2,
222 NULL
223 );
224 ASSERT_EFI_ERROR (Status);
225 }
226
227 mInitrdFileSize = (UINTN)FileSize;
228 return EFI_SUCCESS;
229
230 FreeMemory:
231 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
232 return Status;
233 }
234
235 /**
236 Function for 'initrd' command.
237
238 @param[in] ImageHandle Handle to the Image (NULL if Internal).
239 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
240 **/
241 STATIC
242 SHELL_STATUS
243 EFIAPI
244 RunInitrd (
245 IN EFI_HANDLE ImageHandle,
246 IN EFI_SYSTEM_TABLE *SystemTable
247 )
248 {
249 EFI_STATUS Status;
250 LIST_ENTRY *Package;
251 CHAR16 *ProblemParam;
252 CONST CHAR16 *Param;
253 CHAR16 *Filename;
254 SHELL_STATUS ShellStatus;
255 SHELL_FILE_HANDLE FileHandle;
256
257 ProblemParam = NULL;
258 ShellStatus = SHELL_SUCCESS;
259
260 Status = ShellInitialize ();
261 ASSERT_EFI_ERROR (Status);
262
263 //
264 // parse the command line
265 //
266 Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
267 if (EFI_ERROR (Status)) {
268 if ((Status == EFI_VOLUME_CORRUPTED) && (ProblemParam != NULL)) {
269 ShellPrintHiiEx (
270 -1,
271 -1,
272 NULL,
273 STRING_TOKEN (STR_GEN_PROBLEM),
274 mLinuxInitrdShellCommandHiiHandle,
275 L"initrd",
276 ProblemParam
277 );
278 FreePool (ProblemParam);
279 ShellStatus = SHELL_INVALID_PARAMETER;
280 } else {
281 ASSERT (FALSE);
282 }
283 } else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
284 ShellPrintHiiEx (
285 -1,
286 -1,
287 NULL,
288 STRING_TOKEN (STR_GEN_ALREADY_INSTALLED),
289 mLinuxInitrdShellCommandHiiHandle,
290 L"initrd"
291 );
292 ShellStatus = SHELL_UNSUPPORTED;
293 } else {
294 if (ShellCommandLineGetCount (Package) > 2) {
295 ShellPrintHiiEx (
296 -1,
297 -1,
298 NULL,
299 STRING_TOKEN (STR_GEN_TOO_MANY),
300 mLinuxInitrdShellCommandHiiHandle,
301 L"initrd"
302 );
303 ShellStatus = SHELL_INVALID_PARAMETER;
304 } else if (ShellCommandLineGetCount (Package) < 2) {
305 if (ShellCommandLineGetFlag (Package, L"-u")) {
306 FreeInitrdFile ();
307 UninstallLoadFile2Protocol ();
308 } else {
309 ShellPrintHiiEx (
310 -1,
311 -1,
312 NULL,
313 STRING_TOKEN (STR_GEN_TOO_FEW),
314 mLinuxInitrdShellCommandHiiHandle,
315 L"initrd"
316 );
317 ShellStatus = SHELL_INVALID_PARAMETER;
318 }
319 } else {
320 Param = ShellCommandLineGetRawValue (Package, 1);
321 ASSERT (Param != NULL);
322
323 Filename = ShellFindFilePath (Param);
324 if (Filename == NULL) {
325 ShellPrintHiiEx (
326 -1,
327 -1,
328 NULL,
329 STRING_TOKEN (STR_GEN_FIND_FAIL),
330 mLinuxInitrdShellCommandHiiHandle,
331 L"initrd",
332 Param
333 );
334 ShellStatus = SHELL_NOT_FOUND;
335 } else {
336 Status = ShellOpenFileByName (
337 Filename,
338 &FileHandle,
339 EFI_FILE_MODE_READ,
340 0
341 );
342 if (!EFI_ERROR (Status)) {
343 FreeInitrdFile ();
344 Status = CacheInitrdFile (FileHandle);
345 ShellCloseFile (&FileHandle);
346 }
347
348 if (EFI_ERROR (Status)) {
349 ShellPrintHiiEx (
350 -1,
351 -1,
352 NULL,
353 STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
354 mLinuxInitrdShellCommandHiiHandle,
355 L"initrd",
356 Param
357 );
358 ShellStatus = SHELL_NOT_FOUND;
359 }
360
361 FreePool (Filename);
362 }
363 }
364 }
365
366 return ShellStatus;
367 }
368
369 /**
370 This is the shell command handler function pointer callback type. This
371 function handles the command when it is invoked in the shell.
372
373 @param[in] This The instance of the
374 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
375 @param[in] SystemTable The pointer to the system table.
376 @param[in] ShellParameters The parameters associated with the command.
377 @param[in] Shell The instance of the shell protocol used in
378 the context of processing this command.
379
380 @return EFI_SUCCESS the operation was successful
381 @return other the operation failed.
382 **/
383 SHELL_STATUS
384 EFIAPI
385 LinuxInitrdCommandHandler (
386 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
387 IN EFI_SYSTEM_TABLE *SystemTable,
388 IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
389 IN EFI_SHELL_PROTOCOL *Shell
390 )
391 {
392 gEfiShellParametersProtocol = ShellParameters;
393 gEfiShellProtocol = Shell;
394
395 return RunInitrd (gImageHandle, SystemTable);
396 }
397
398 /**
399 This is the command help handler function pointer callback type. This
400 function is responsible for displaying help information for the associated
401 command.
402
403 @param[in] This The instance of the
404 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
405 @param[in] Language The pointer to the language string to use.
406
407 @return string Pool allocated help string, must be freed
408 by caller
409 **/
410 STATIC
411 CHAR16 *
412 EFIAPI
413 LinuxInitrdGetHelp (
414 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
415 IN CONST CHAR8 *Language
416 )
417 {
418 return HiiGetString (
419 mLinuxInitrdShellCommandHiiHandle,
420 STRING_TOKEN (STR_GET_HELP_INITRD),
421 Language
422 );
423 }
424
425 STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
426 L"initrd",
427 LinuxInitrdCommandHandler,
428 LinuxInitrdGetHelp
429 };
430
431 /**
432 Retrieve HII package list from ImageHandle and publish to HII database.
433
434 @param ImageHandle The image handle of the process.
435
436 @return HII handle.
437 **/
438 STATIC
439 EFI_HII_HANDLE
440 InitializeHiiPackage (
441 EFI_HANDLE ImageHandle
442 )
443 {
444 EFI_STATUS Status;
445 EFI_HII_PACKAGE_LIST_HEADER *PackageList;
446 EFI_HII_HANDLE HiiHandle;
447
448 //
449 // Retrieve HII package list from ImageHandle
450 //
451 Status = gBS->OpenProtocol (
452 ImageHandle,
453 &gEfiHiiPackageListProtocolGuid,
454 (VOID **)&PackageList,
455 ImageHandle,
456 NULL,
457 EFI_OPEN_PROTOCOL_GET_PROTOCOL
458 );
459 ASSERT_EFI_ERROR (Status);
460 if (EFI_ERROR (Status)) {
461 return NULL;
462 }
463
464 //
465 // Publish HII package list to HII Database.
466 //
467 Status = gHiiDatabase->NewPackageList (
468 gHiiDatabase,
469 PackageList,
470 NULL,
471 &HiiHandle
472 );
473 ASSERT_EFI_ERROR (Status);
474 if (EFI_ERROR (Status)) {
475 return NULL;
476 }
477
478 return HiiHandle;
479 }
480
481 /**
482 Entry point of Linux Initrd dynamic UEFI Shell command.
483
484 Produce the DynamicCommand protocol to handle "initrd" command.
485
486 @param ImageHandle The image handle of the process.
487 @param SystemTable The EFI System Table pointer.
488
489 @retval EFI_SUCCESS Initrd command is executed successfully.
490 @retval EFI_ABORTED HII package was failed to initialize.
491 @retval others Other errors when executing Initrd command.
492 **/
493 EFI_STATUS
494 EFIAPI
495 LinuxInitrdDynamicShellCommandEntryPoint (
496 IN EFI_HANDLE ImageHandle,
497 IN EFI_SYSTEM_TABLE *SystemTable
498 )
499 {
500 EFI_STATUS Status;
501
502 mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
503 if (mLinuxInitrdShellCommandHiiHandle == NULL) {
504 return EFI_ABORTED;
505 }
506
507 Status = gBS->InstallProtocolInterface (
508 &ImageHandle,
509 &gEfiShellDynamicCommandProtocolGuid,
510 EFI_NATIVE_INTERFACE,
511 &mLinuxInitrdDynamicCommand
512 );
513 ASSERT_EFI_ERROR (Status);
514 return Status;
515 }
516
517 /**
518 Unload the dynamic UEFI Shell command.
519
520 @param ImageHandle The image handle of the process.
521
522 @retval EFI_SUCCESS The image is unloaded.
523 @retval Others Failed to unload the image.
524 **/
525 EFI_STATUS
526 EFIAPI
527 LinuxInitrdDynamicShellCommandUnload (
528 IN EFI_HANDLE ImageHandle
529 )
530 {
531 EFI_STATUS Status;
532
533 FreeInitrdFile ();
534
535 Status = UninstallLoadFile2Protocol ();
536 if (EFI_ERROR (Status)) {
537 return Status;
538 }
539
540 Status = gBS->UninstallProtocolInterface (
541 ImageHandle,
542 &gEfiShellDynamicCommandProtocolGuid,
543 &mLinuxInitrdDynamicCommand
544 );
545 if (EFI_ERROR (Status)) {
546 return Status;
547 }
548
549 HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
550 return EFI_SUCCESS;
551 }