]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
ed8fbaa77069ba6366810a5595a0036036166cc0
[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 (&gEfiLoadFile2ProtocolGuid, &DevicePath,
68 &Handle);
69 if (EFI_ERROR (Status)) {
70 return FALSE;
71 }
72
73 //
74 // Check whether the existing instance is one that we installed during
75 // a previous invocation.
76 //
77 if (Handle == mInitrdLoadFile2Handle) {
78 return FALSE;
79 }
80 return TRUE;
81 }
82
83 STATIC
84 EFI_STATUS
85 EFIAPI
86 InitrdLoadFile2 (
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
92 )
93 {
94 if (BootPolicy) {
95 return EFI_UNSUPPORTED;
96 }
97
98 if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
99 return EFI_INVALID_PARAMETER;
100 }
101
102 if (FilePath->Type != END_DEVICE_PATH_TYPE ||
103 FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE ||
104 mInitrdFileSize == 0) {
105 return EFI_NOT_FOUND;
106 }
107
108 if (Buffer == NULL || *BufferSize < mInitrdFileSize) {
109 *BufferSize = mInitrdFileSize;
110 return EFI_BUFFER_TOO_SMALL;
111 }
112
113 ASSERT (mInitrdFileAddress != 0);
114
115 gBS->CopyMem (Buffer, (VOID *)(UINTN)mInitrdFileAddress, mInitrdFileSize);
116 *BufferSize = mInitrdFileSize;
117 return EFI_SUCCESS;
118 }
119
120 STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
121 InitrdLoadFile2,
122 };
123
124 STATIC
125 EFI_STATUS
126 UninstallLoadFile2Protocol (
127 VOID
128 )
129 {
130 EFI_STATUS Status;
131
132 if (mInitrdLoadFile2Handle != NULL) {
133 Status = gBS->UninstallMultipleProtocolInterfaces (mInitrdLoadFile2Handle,
134 &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
135 &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
136 NULL);
137 if (!EFI_ERROR (Status)) {
138 mInitrdLoadFile2Handle = NULL;
139 }
140 }
141 return Status;
142 }
143
144 STATIC
145 VOID
146 FreeInitrdFile (
147 VOID
148 )
149 {
150 if (mInitrdFileSize != 0) {
151 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
152 mInitrdFileSize = 0;
153 }
154 }
155
156 STATIC
157 EFI_STATUS
158 CacheInitrdFile (
159 IN SHELL_FILE_HANDLE FileHandle
160 )
161 {
162 EFI_STATUS Status;
163 UINT64 FileSize;
164 UINTN ReadSize;
165
166 Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
167 if (EFI_ERROR (Status)) {
168 return Status;
169 }
170
171 if (FileSize == 0 || FileSize > MAX_UINTN) {
172 return EFI_UNSUPPORTED;
173 }
174
175 Status = gBS->AllocatePages (AllocateAnyPages, EfiLoaderData,
176 EFI_SIZE_TO_PAGES ((UINTN)FileSize), &mInitrdFileAddress);
177 if (EFI_ERROR (Status)) {
178 return Status;
179 }
180
181 ReadSize = (UINTN)FileSize;
182 Status = gEfiShellProtocol->ReadFile (FileHandle, &ReadSize,
183 (VOID *)(UINTN)mInitrdFileAddress);
184 if (EFI_ERROR (Status) || ReadSize < FileSize) {
185 DEBUG ((DEBUG_WARN, "%a: failed to read initrd file - %r 0x%lx 0x%lx\n",
186 __FUNCTION__, Status, (UINT64)ReadSize, FileSize));
187 goto FreeMemory;
188 }
189
190 if (mInitrdLoadFile2Handle == NULL) {
191 Status = gBS->InstallMultipleProtocolInterfaces (&mInitrdLoadFile2Handle,
192 &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
193 &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
194 NULL);
195 ASSERT_EFI_ERROR (Status);
196 }
197
198 mInitrdFileSize = FileSize;
199 return EFI_SUCCESS;
200
201 FreeMemory:
202 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
203 return Status;
204 }
205
206 /**
207 Function for 'initrd' command.
208
209 @param[in] ImageHandle Handle to the Image (NULL if Internal).
210 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
211 **/
212 STATIC
213 SHELL_STATUS
214 EFIAPI
215 RunInitrd (
216 IN EFI_HANDLE ImageHandle,
217 IN EFI_SYSTEM_TABLE *SystemTable
218 )
219 {
220 EFI_STATUS Status;
221 LIST_ENTRY *Package;
222 CHAR16 *ProblemParam;
223 CONST CHAR16 *Param;
224 CHAR16 *Filename;
225 SHELL_STATUS ShellStatus;
226 SHELL_FILE_HANDLE FileHandle;
227
228 ProblemParam = NULL;
229 ShellStatus = SHELL_SUCCESS;
230
231 Status = ShellInitialize ();
232 ASSERT_EFI_ERROR (Status);
233
234 //
235 // parse the command line
236 //
237 Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE);
238 if (EFI_ERROR (Status)) {
239 if (Status == EFI_VOLUME_CORRUPTED && ProblemParam != NULL) {
240 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_PROBLEM),
241 mLinuxInitrdShellCommandHiiHandle, L"initrd", ProblemParam);
242 FreePool (ProblemParam);
243 ShellStatus = SHELL_INVALID_PARAMETER;
244 } else {
245 ASSERT(FALSE);
246 }
247 } else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
248 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_ALREADY_INSTALLED),
249 mLinuxInitrdShellCommandHiiHandle, L"initrd");
250 ShellStatus = SHELL_UNSUPPORTED;
251 } else {
252 if (ShellCommandLineGetCount (Package) > 2) {
253 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_MANY),
254 mLinuxInitrdShellCommandHiiHandle, L"initrd");
255 ShellStatus = SHELL_INVALID_PARAMETER;
256 } else if (ShellCommandLineGetCount (Package) < 2) {
257 if (ShellCommandLineGetFlag (Package, L"-u")) {
258 FreeInitrdFile ();
259 UninstallLoadFile2Protocol ();
260 } else {
261 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),
262 mLinuxInitrdShellCommandHiiHandle, L"initrd");
263 ShellStatus = SHELL_INVALID_PARAMETER;
264 }
265 } else {
266 Param = ShellCommandLineGetRawValue (Package, 1);
267 ASSERT (Param != NULL);
268
269 Filename = ShellFindFilePath (Param);
270 if (Filename == NULL) {
271 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FIND_FAIL),
272 mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);
273 ShellStatus = SHELL_NOT_FOUND;
274 } else {
275 Status = ShellOpenFileByName (Filename, &FileHandle,
276 EFI_FILE_MODE_READ, 0);
277 if (!EFI_ERROR (Status)) {
278 FreeInitrdFile ();
279 Status = CacheInitrdFile (FileHandle);
280 ShellCloseFile (&FileHandle);
281 }
282 if (EFI_ERROR (Status)) {
283 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_FILE_OPEN_FAIL),
284 mLinuxInitrdShellCommandHiiHandle, L"initrd", Param);
285 ShellStatus = SHELL_NOT_FOUND;
286 }
287 FreePool (Filename);
288 }
289 }
290 }
291 return ShellStatus;
292 }
293
294
295 /**
296 This is the shell command handler function pointer callback type. This
297 function handles the command when it is invoked in the shell.
298
299 @param[in] This The instance of the
300 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
301 @param[in] SystemTable The pointer to the system table.
302 @param[in] ShellParameters The parameters associated with the command.
303 @param[in] Shell The instance of the shell protocol used in
304 the context of processing this command.
305
306 @return EFI_SUCCESS the operation was successful
307 @return other the operation failed.
308 **/
309 SHELL_STATUS
310 EFIAPI
311 LinuxInitrdCommandHandler (
312 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
313 IN EFI_SYSTEM_TABLE *SystemTable,
314 IN EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters,
315 IN EFI_SHELL_PROTOCOL *Shell
316 )
317 {
318 gEfiShellParametersProtocol = ShellParameters;
319 gEfiShellProtocol = Shell;
320
321 return RunInitrd (gImageHandle, SystemTable);
322 }
323
324 /**
325 This is the command help handler function pointer callback type. This
326 function is responsible for displaying help information for the associated
327 command.
328
329 @param[in] This The instance of the
330 EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL.
331 @param[in] Language The pointer to the language string to use.
332
333 @return string Pool allocated help string, must be freed
334 by caller
335 **/
336 STATIC
337 CHAR16 *
338 EFIAPI
339 LinuxInitrdGetHelp (
340 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
341 IN CONST CHAR8 *Language
342 )
343 {
344 return HiiGetString (mLinuxInitrdShellCommandHiiHandle,
345 STRING_TOKEN (STR_GET_HELP_INITRD), Language);
346 }
347
348 STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
349 L"initrd",
350 LinuxInitrdCommandHandler,
351 LinuxInitrdGetHelp
352 };
353
354 /**
355 Retrieve HII package list from ImageHandle and publish to HII database.
356
357 @param ImageHandle The image handle of the process.
358
359 @return HII handle.
360 **/
361 STATIC
362 EFI_HII_HANDLE
363 InitializeHiiPackage (
364 EFI_HANDLE ImageHandle
365 )
366 {
367 EFI_STATUS Status;
368 EFI_HII_PACKAGE_LIST_HEADER *PackageList;
369 EFI_HII_HANDLE HiiHandle;
370
371 //
372 // Retrieve HII package list from ImageHandle
373 //
374 Status = gBS->OpenProtocol (ImageHandle, &gEfiHiiPackageListProtocolGuid,
375 (VOID **)&PackageList, ImageHandle, NULL,
376 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
377 ASSERT_EFI_ERROR (Status);
378 if (EFI_ERROR (Status)) {
379 return NULL;
380 }
381
382 //
383 // Publish HII package list to HII Database.
384 //
385 Status = gHiiDatabase->NewPackageList (gHiiDatabase, PackageList, NULL,
386 &HiiHandle);
387 ASSERT_EFI_ERROR (Status);
388 if (EFI_ERROR (Status)) {
389 return NULL;
390 }
391 return HiiHandle;
392 }
393
394 /**
395 Entry point of Linux Initrd dynamic UEFI Shell command.
396
397 Produce the DynamicCommand protocol to handle "initrd" command.
398
399 @param ImageHandle The image handle of the process.
400 @param SystemTable The EFI System Table pointer.
401
402 @retval EFI_SUCCESS Initrd command is executed successfully.
403 @retval EFI_ABORTED HII package was failed to initialize.
404 @retval others Other errors when executing Initrd command.
405 **/
406 EFI_STATUS
407 EFIAPI
408 LinuxInitrdDynamicShellCommandEntryPoint (
409 IN EFI_HANDLE ImageHandle,
410 IN EFI_SYSTEM_TABLE *SystemTable
411 )
412 {
413 EFI_STATUS Status;
414
415 mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
416 if (mLinuxInitrdShellCommandHiiHandle == NULL) {
417 return EFI_ABORTED;
418 }
419
420 Status = gBS->InstallProtocolInterface (&ImageHandle,
421 &gEfiShellDynamicCommandProtocolGuid,
422 EFI_NATIVE_INTERFACE,
423 &mLinuxInitrdDynamicCommand);
424 ASSERT_EFI_ERROR (Status);
425 return Status;
426 }
427
428 /**
429 Unload the dynamic UEFI Shell command.
430
431 @param ImageHandle The image handle of the process.
432
433 @retval EFI_SUCCESS The image is unloaded.
434 @retval Others Failed to unload the image.
435 **/
436 EFI_STATUS
437 EFIAPI
438 LinuxInitrdDynamicShellCommandUnload (
439 IN EFI_HANDLE ImageHandle
440 )
441 {
442 EFI_STATUS Status;
443
444 FreeInitrdFile ();
445
446 Status = UninstallLoadFile2Protocol ();
447 if (EFI_ERROR (Status)) {
448 return Status;
449 }
450
451 Status = gBS->UninstallProtocolInterface (ImageHandle,
452 &gEfiShellDynamicCommandProtocolGuid,
453 &mLinuxInitrdDynamicCommand);
454 if (EFI_ERROR (Status)) {
455 return Status;
456 }
457
458 HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
459 return EFI_SUCCESS;
460 }