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