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