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