]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/LinuxInitrdDynamicShellCommand/LinuxInitrdDynamicShellCommand.c
OvmfPkg/LinuxInitrdDynamicShellCommand: fix uninitialized status return
[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 return Status;
141 }
142 return EFI_SUCCESS;
143 }
144
145 STATIC
146 VOID
147 FreeInitrdFile (
148 VOID
149 )
150 {
151 if (mInitrdFileSize != 0) {
152 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES (mInitrdFileSize));
153 mInitrdFileSize = 0;
154 }
155 }
156
157 STATIC
158 EFI_STATUS
159 CacheInitrdFile (
160 IN SHELL_FILE_HANDLE FileHandle
161 )
162 {
163 EFI_STATUS Status;
164 UINT64 FileSize;
165 UINTN ReadSize;
166
167 Status = gEfiShellProtocol->GetFileSize (FileHandle, &FileSize);
168 if (EFI_ERROR (Status)) {
169 return Status;
170 }
171
172 if (FileSize == 0 || FileSize > MAX_UINTN) {
173 return EFI_UNSUPPORTED;
174 }
175
176 Status = gBS->AllocatePages (AllocateAnyPages, EfiLoaderData,
177 EFI_SIZE_TO_PAGES ((UINTN)FileSize), &mInitrdFileAddress);
178 if (EFI_ERROR (Status)) {
179 return Status;
180 }
181
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));
188 goto FreeMemory;
189 }
190
191 if (mInitrdLoadFile2Handle == NULL) {
192 Status = gBS->InstallMultipleProtocolInterfaces (&mInitrdLoadFile2Handle,
193 &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
194 &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
195 NULL);
196 ASSERT_EFI_ERROR (Status);
197 }
198
199 mInitrdFileSize = FileSize;
200 return EFI_SUCCESS;
201
202 FreeMemory:
203 gBS->FreePages (mInitrdFileAddress, EFI_SIZE_TO_PAGES ((UINTN)FileSize));
204 return Status;
205 }
206
207 /**
208 Function for 'initrd' command.
209
210 @param[in] ImageHandle Handle to the Image (NULL if Internal).
211 @param[in] SystemTable Pointer to the System Table (NULL if Internal).
212 **/
213 STATIC
214 SHELL_STATUS
215 EFIAPI
216 RunInitrd (
217 IN EFI_HANDLE ImageHandle,
218 IN EFI_SYSTEM_TABLE *SystemTable
219 )
220 {
221 EFI_STATUS Status;
222 LIST_ENTRY *Package;
223 CHAR16 *ProblemParam;
224 CONST CHAR16 *Param;
225 CHAR16 *Filename;
226 SHELL_STATUS ShellStatus;
227 SHELL_FILE_HANDLE FileHandle;
228
229 ProblemParam = NULL;
230 ShellStatus = SHELL_SUCCESS;
231
232 Status = ShellInitialize ();
233 ASSERT_EFI_ERROR (Status);
234
235 //
236 // parse the command line
237 //
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;
245 } else {
246 ASSERT(FALSE);
247 }
248 } else if (IsOtherInitrdDevicePathAlreadyInstalled ()) {
249 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_ALREADY_INSTALLED),
250 mLinuxInitrdShellCommandHiiHandle, L"initrd");
251 ShellStatus = SHELL_UNSUPPORTED;
252 } else {
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")) {
259 FreeInitrdFile ();
260 UninstallLoadFile2Protocol ();
261 } else {
262 ShellPrintHiiEx (-1, -1, NULL, STRING_TOKEN (STR_GEN_TOO_FEW),
263 mLinuxInitrdShellCommandHiiHandle, L"initrd");
264 ShellStatus = SHELL_INVALID_PARAMETER;
265 }
266 } else {
267 Param = ShellCommandLineGetRawValue (Package, 1);
268 ASSERT (Param != NULL);
269
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;
275 } else {
276 Status = ShellOpenFileByName (Filename, &FileHandle,
277 EFI_FILE_MODE_READ, 0);
278 if (!EFI_ERROR (Status)) {
279 FreeInitrdFile ();
280 Status = CacheInitrdFile (FileHandle);
281 ShellCloseFile (&FileHandle);
282 }
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;
287 }
288 FreePool (Filename);
289 }
290 }
291 }
292 return ShellStatus;
293 }
294
295
296 /**
297 This is the shell command handler function pointer callback type. This
298 function handles the command when it is invoked in the shell.
299
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.
306
307 @return EFI_SUCCESS the operation was successful
308 @return other the operation failed.
309 **/
310 SHELL_STATUS
311 EFIAPI
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
317 )
318 {
319 gEfiShellParametersProtocol = ShellParameters;
320 gEfiShellProtocol = Shell;
321
322 return RunInitrd (gImageHandle, SystemTable);
323 }
324
325 /**
326 This is the command help handler function pointer callback type. This
327 function is responsible for displaying help information for the associated
328 command.
329
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.
333
334 @return string Pool allocated help string, must be freed
335 by caller
336 **/
337 STATIC
338 CHAR16 *
339 EFIAPI
340 LinuxInitrdGetHelp (
341 IN EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL *This,
342 IN CONST CHAR8 *Language
343 )
344 {
345 return HiiGetString (mLinuxInitrdShellCommandHiiHandle,
346 STRING_TOKEN (STR_GET_HELP_INITRD), Language);
347 }
348
349 STATIC EFI_SHELL_DYNAMIC_COMMAND_PROTOCOL mLinuxInitrdDynamicCommand = {
350 L"initrd",
351 LinuxInitrdCommandHandler,
352 LinuxInitrdGetHelp
353 };
354
355 /**
356 Retrieve HII package list from ImageHandle and publish to HII database.
357
358 @param ImageHandle The image handle of the process.
359
360 @return HII handle.
361 **/
362 STATIC
363 EFI_HII_HANDLE
364 InitializeHiiPackage (
365 EFI_HANDLE ImageHandle
366 )
367 {
368 EFI_STATUS Status;
369 EFI_HII_PACKAGE_LIST_HEADER *PackageList;
370 EFI_HII_HANDLE HiiHandle;
371
372 //
373 // Retrieve HII package list from ImageHandle
374 //
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)) {
380 return NULL;
381 }
382
383 //
384 // Publish HII package list to HII Database.
385 //
386 Status = gHiiDatabase->NewPackageList (gHiiDatabase, PackageList, NULL,
387 &HiiHandle);
388 ASSERT_EFI_ERROR (Status);
389 if (EFI_ERROR (Status)) {
390 return NULL;
391 }
392 return HiiHandle;
393 }
394
395 /**
396 Entry point of Linux Initrd dynamic UEFI Shell command.
397
398 Produce the DynamicCommand protocol to handle "initrd" command.
399
400 @param ImageHandle The image handle of the process.
401 @param SystemTable The EFI System Table pointer.
402
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.
406 **/
407 EFI_STATUS
408 EFIAPI
409 LinuxInitrdDynamicShellCommandEntryPoint (
410 IN EFI_HANDLE ImageHandle,
411 IN EFI_SYSTEM_TABLE *SystemTable
412 )
413 {
414 EFI_STATUS Status;
415
416 mLinuxInitrdShellCommandHiiHandle = InitializeHiiPackage (ImageHandle);
417 if (mLinuxInitrdShellCommandHiiHandle == NULL) {
418 return EFI_ABORTED;
419 }
420
421 Status = gBS->InstallProtocolInterface (&ImageHandle,
422 &gEfiShellDynamicCommandProtocolGuid,
423 EFI_NATIVE_INTERFACE,
424 &mLinuxInitrdDynamicCommand);
425 ASSERT_EFI_ERROR (Status);
426 return Status;
427 }
428
429 /**
430 Unload the dynamic UEFI Shell command.
431
432 @param ImageHandle The image handle of the process.
433
434 @retval EFI_SUCCESS The image is unloaded.
435 @retval Others Failed to unload the image.
436 **/
437 EFI_STATUS
438 EFIAPI
439 LinuxInitrdDynamicShellCommandUnload (
440 IN EFI_HANDLE ImageHandle
441 )
442 {
443 EFI_STATUS Status;
444
445 FreeInitrdFile ();
446
447 Status = UninstallLoadFile2Protocol ();
448 if (EFI_ERROR (Status)) {
449 return Status;
450 }
451
452 Status = gBS->UninstallProtocolInterface (ImageHandle,
453 &gEfiShellDynamicCommandProtocolGuid,
454 &mLinuxInitrdDynamicCommand);
455 if (EFI_ERROR (Status)) {
456 return Status;
457 }
458
459 HiiRemovePackages (mLinuxInitrdShellCommandHiiHandle);
460 return EFI_SUCCESS;
461 }