]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Library / X86QemuLoadImageLib / X86QemuLoadImageLib.c
1 /** @file
2 X86 specific implementation of QemuLoadImageLib library class interface
3 with support for loading mixed mode images and non-EFI stub images
4
5 Note that this implementation reads the cmdline (and possibly kernel, setup
6 data, and initrd in the legacy boot mode) from fw_cfg directly.
7
8 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
9 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
10
11 SPDX-License-Identifier: BSD-2-Clause-Patent
12 **/
13
14 #include <Uefi.h>
15
16 #include <Guid/QemuKernelLoaderFsMedia.h>
17 #include <Library/DebugLib.h>
18 #include <Library/LoadLinuxLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/PrintLib.h>
21 #include <Library/QemuFwCfgLib.h>
22 #include <Library/QemuLoadImageLib.h>
23 #include <Library/UefiBootServicesTableLib.h>
24 #include <Protocol/DevicePath.h>
25 #include <Protocol/LoadedImage.h>
26 #include <Protocol/OvmfLoadedX86LinuxKernel.h>
27
28 #pragma pack (1)
29 typedef struct {
30 EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
31 CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
32 } KERNEL_FILE_DEVPATH;
33
34 typedef struct {
35 VENDOR_DEVICE_PATH VenMediaNode;
36 KERNEL_FILE_DEVPATH FileNode;
37 EFI_DEVICE_PATH_PROTOCOL EndNode;
38 } KERNEL_VENMEDIA_FILE_DEVPATH;
39 #pragma pack ()
40
41 STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
42 {
43 {
44 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
45 { sizeof (VENDOR_DEVICE_PATH) }
46 },
47 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
48 }, {
49 {
50 MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
51 { sizeof (KERNEL_FILE_DEVPATH) }
52 },
53 L"kernel",
54 }, {
55 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
56 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
57 }
58 };
59
60 STATIC
61 VOID
62 FreeLegacyImage (
63 IN OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage
64 )
65 {
66 if (LoadedImage->SetupBuf != NULL) {
67 FreePages (
68 LoadedImage->SetupBuf,
69 EFI_SIZE_TO_PAGES (LoadedImage->SetupSize)
70 );
71 }
72
73 if (LoadedImage->KernelBuf != NULL) {
74 FreePages (
75 LoadedImage->KernelBuf,
76 EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
77 );
78 }
79
80 if (LoadedImage->CommandLine != NULL) {
81 FreePages (
82 LoadedImage->CommandLine,
83 EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize)
84 );
85 }
86
87 if (LoadedImage->InitrdData != NULL) {
88 FreePages (
89 LoadedImage->InitrdData,
90 EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize)
91 );
92 }
93 }
94
95 STATIC
96 EFI_STATUS
97 QemuLoadLegacyImage (
98 OUT EFI_HANDLE *ImageHandle
99 )
100 {
101 EFI_STATUS Status;
102 UINTN KernelSize;
103 UINTN SetupSize;
104 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
105
106 QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
107 KernelSize = (UINTN)QemuFwCfgRead32 ();
108
109 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
110 SetupSize = (UINTN)QemuFwCfgRead32 ();
111
112 if ((KernelSize == 0) || (SetupSize == 0)) {
113 DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
114 return EFI_NOT_FOUND;
115 }
116
117 LoadedImage = AllocateZeroPool (sizeof (*LoadedImage));
118 if (LoadedImage == NULL) {
119 return EFI_OUT_OF_RESOURCES;
120 }
121
122 LoadedImage->SetupSize = SetupSize;
123 LoadedImage->SetupBuf = LoadLinuxAllocateKernelSetupPages (
124 EFI_SIZE_TO_PAGES (LoadedImage->SetupSize)
125 );
126 if (LoadedImage->SetupBuf == NULL) {
127 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel setup!\n"));
128 Status = EFI_OUT_OF_RESOURCES;
129 goto FreeImageDesc;
130 }
131
132 DEBUG ((DEBUG_INFO, "Setup size: 0x%x\n", (UINT32)LoadedImage->SetupSize));
133 DEBUG ((DEBUG_INFO, "Reading kernel setup image ..."));
134 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData);
135 QemuFwCfgReadBytes (LoadedImage->SetupSize, LoadedImage->SetupBuf);
136 DEBUG ((DEBUG_INFO, " [done]\n"));
137
138 Status = LoadLinuxCheckKernelSetup (
139 LoadedImage->SetupBuf,
140 LoadedImage->SetupSize
141 );
142 if (EFI_ERROR (Status)) {
143 goto FreeImage;
144 }
145
146 Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
147 if (EFI_ERROR (Status)) {
148 goto FreeImage;
149 }
150
151 LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
152 LoadedImage->SetupBuf,
153 KernelSize
154 );
155 if (LoadedImage->KernelInitialSize == 0) {
156 Status = EFI_UNSUPPORTED;
157 goto FreeImage;
158 }
159
160 LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
161 LoadedImage->SetupBuf,
162 EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
163 );
164 if (LoadedImage->KernelBuf == NULL) {
165 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
166 Status = EFI_OUT_OF_RESOURCES;
167 goto FreeImage;
168 }
169
170 DEBUG ((DEBUG_INFO, "Kernel size: 0x%x\n", (UINT32)KernelSize));
171 DEBUG ((DEBUG_INFO, "Reading kernel image ..."));
172 QemuFwCfgSelectItem (QemuFwCfgItemKernelData);
173 QemuFwCfgReadBytes (KernelSize, LoadedImage->KernelBuf);
174 DEBUG ((DEBUG_INFO, " [done]\n"));
175
176 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
177 LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead32 ();
178
179 if (LoadedImage->CommandLineSize > 0) {
180 LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
181 EFI_SIZE_TO_PAGES (
182 LoadedImage->CommandLineSize
183 )
184 );
185 if (LoadedImage->CommandLine == NULL) {
186 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel command line!\n"));
187 Status = EFI_OUT_OF_RESOURCES;
188 goto FreeImage;
189 }
190
191 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
192 QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
193 }
194
195 Status = LoadLinuxSetCommandLine (
196 LoadedImage->SetupBuf,
197 LoadedImage->CommandLine
198 );
199 if (EFI_ERROR (Status)) {
200 goto FreeImage;
201 }
202
203 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
204 LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 ();
205
206 if (LoadedImage->InitrdSize > 0) {
207 LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
208 LoadedImage->SetupBuf,
209 EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize)
210 );
211 if (LoadedImage->InitrdData == NULL) {
212 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for initrd!\n"));
213 Status = EFI_OUT_OF_RESOURCES;
214 goto FreeImage;
215 }
216
217 DEBUG ((
218 DEBUG_INFO,
219 "Initrd size: 0x%x\n",
220 (UINT32)LoadedImage->InitrdSize
221 ));
222 DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
223 QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
224 QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
225 DEBUG ((DEBUG_INFO, " [done]\n"));
226 }
227
228 Status = LoadLinuxSetInitrd (
229 LoadedImage->SetupBuf,
230 LoadedImage->InitrdData,
231 LoadedImage->InitrdSize
232 );
233 if (EFI_ERROR (Status)) {
234 goto FreeImage;
235 }
236
237 *ImageHandle = NULL;
238 Status = gBS->InstallProtocolInterface (
239 ImageHandle,
240 &gOvmfLoadedX86LinuxKernelProtocolGuid,
241 EFI_NATIVE_INTERFACE,
242 LoadedImage
243 );
244 if (EFI_ERROR (Status)) {
245 goto FreeImage;
246 }
247
248 return EFI_SUCCESS;
249
250 FreeImage:
251 FreeLegacyImage (LoadedImage);
252 FreeImageDesc:
253 FreePool (LoadedImage);
254 return Status;
255 }
256
257 STATIC
258 EFI_STATUS
259 QemuStartLegacyImage (
260 IN EFI_HANDLE ImageHandle
261 )
262 {
263 EFI_STATUS Status;
264 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
265
266 Status = gBS->OpenProtocol (
267 ImageHandle,
268 &gOvmfLoadedX86LinuxKernelProtocolGuid,
269 (VOID **)&LoadedImage,
270 gImageHandle, // AgentHandle
271 NULL, // ControllerHandle
272 EFI_OPEN_PROTOCOL_GET_PROTOCOL
273 );
274 if (EFI_ERROR (Status)) {
275 return EFI_INVALID_PARAMETER;
276 }
277
278 return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf);
279 }
280
281 STATIC
282 EFI_STATUS
283 QemuUnloadLegacyImage (
284 IN EFI_HANDLE ImageHandle
285 )
286 {
287 EFI_STATUS Status;
288 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
289
290 Status = gBS->OpenProtocol (
291 ImageHandle,
292 &gOvmfLoadedX86LinuxKernelProtocolGuid,
293 (VOID **)&LoadedImage,
294 gImageHandle, // AgentHandle
295 NULL, // ControllerHandle
296 EFI_OPEN_PROTOCOL_GET_PROTOCOL
297 );
298 if (EFI_ERROR (Status)) {
299 return EFI_INVALID_PARAMETER;
300 }
301
302 Status = gBS->UninstallProtocolInterface (
303 ImageHandle,
304 &gOvmfLoadedX86LinuxKernelProtocolGuid,
305 LoadedImage
306 );
307 ASSERT_EFI_ERROR (Status);
308
309 FreeLegacyImage (LoadedImage);
310 FreePool (LoadedImage);
311 return EFI_SUCCESS;
312 }
313
314 /**
315 Download the kernel, the initial ramdisk, and the kernel command line from
316 QEMU's fw_cfg. The kernel will be instructed via its command line to load
317 the initrd from the same Simple FileSystem where the kernel was loaded from.
318
319 @param[out] ImageHandle The image handle that was allocated for
320 loading the image
321
322 @retval EFI_SUCCESS The image was loaded successfully.
323 @retval EFI_NOT_FOUND Kernel image was not found.
324 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
325 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
326
327 @return Error codes from any of the underlying
328 functions.
329 **/
330 EFI_STATUS
331 EFIAPI
332 QemuLoadKernelImage (
333 OUT EFI_HANDLE *ImageHandle
334 )
335 {
336 EFI_STATUS Status;
337 EFI_HANDLE KernelImageHandle;
338 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
339 UINTN CommandLineSize;
340 CHAR8 *CommandLine;
341 UINTN InitrdSize;
342
343 //
344 // Redundant assignment to work around GCC48/GCC49 limitations.
345 //
346 CommandLine = NULL;
347
348 //
349 // Load the image. This should call back into the QEMU EFI loader file system.
350 //
351 Status = gBS->LoadImage (
352 FALSE, // BootPolicy: exact match required
353 gImageHandle, // ParentImageHandle
354 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
355 NULL, // SourceBuffer
356 0, // SourceSize
357 &KernelImageHandle
358 );
359 switch (Status) {
360 case EFI_SUCCESS:
361 break;
362
363 case EFI_NOT_FOUND:
364 //
365 // The image does not exist - no -kernel image was supplied via the
366 // command line so no point in invoking the legacy fallback
367 //
368 return EFI_NOT_FOUND;
369
370 case EFI_SECURITY_VIOLATION:
371 //
372 // Since the image has been loaded, we need to unload it before proceeding
373 // to the EFI_ACCESS_DENIED case below.
374 //
375 gBS->UnloadImage (KernelImageHandle);
376 //
377 // Fall through
378 //
379 case EFI_ACCESS_DENIED:
380 //
381 // We are running with UEFI secure boot enabled, and the image failed to
382 // authenticate. For compatibility reasons, we fall back to the legacy
383 // loader in this case.
384 //
385 // Fall through
386 //
387 case EFI_UNSUPPORTED:
388 //
389 // The image is not natively supported or cross-type supported. Let's try
390 // loading it using the loader that parses the bzImage metadata directly.
391 //
392 Status = QemuLoadLegacyImage (&KernelImageHandle);
393 if (EFI_ERROR (Status)) {
394 DEBUG ((
395 DEBUG_ERROR,
396 "%a: QemuLoadLegacyImage(): %r\n",
397 __FUNCTION__,
398 Status
399 ));
400 return Status;
401 }
402
403 *ImageHandle = KernelImageHandle;
404 return EFI_SUCCESS;
405
406 default:
407 DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
408 return Status;
409 }
410
411 //
412 // Construct the kernel command line.
413 //
414 Status = gBS->OpenProtocol (
415 KernelImageHandle,
416 &gEfiLoadedImageProtocolGuid,
417 (VOID **)&KernelLoadedImage,
418 gImageHandle, // AgentHandle
419 NULL, // ControllerHandle
420 EFI_OPEN_PROTOCOL_GET_PROTOCOL
421 );
422 ASSERT_EFI_ERROR (Status);
423
424 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
425 CommandLineSize = (UINTN)QemuFwCfgRead32 ();
426
427 if (CommandLineSize == 0) {
428 KernelLoadedImage->LoadOptionsSize = 0;
429 } else {
430 CommandLine = AllocatePool (CommandLineSize);
431 if (CommandLine == NULL) {
432 Status = EFI_OUT_OF_RESOURCES;
433 goto UnloadImage;
434 }
435
436 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
437 QemuFwCfgReadBytes (CommandLineSize, CommandLine);
438
439 //
440 // Verify NUL-termination of the command line.
441 //
442 if (CommandLine[CommandLineSize - 1] != '\0') {
443 DEBUG ((
444 DEBUG_ERROR,
445 "%a: kernel command line is not NUL-terminated\n",
446 __FUNCTION__
447 ));
448 Status = EFI_PROTOCOL_ERROR;
449 goto FreeCommandLine;
450 }
451
452 //
453 // Drop the terminating NUL, convert to UTF-16.
454 //
455 KernelLoadedImage->LoadOptionsSize = (UINT32)((CommandLineSize - 1) * 2);
456 }
457
458 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
459 InitrdSize = (UINTN)QemuFwCfgRead32 ();
460
461 if (InitrdSize > 0) {
462 //
463 // Append ' initrd=initrd' in UTF-16.
464 //
465 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
466 }
467
468 if (KernelLoadedImage->LoadOptionsSize == 0) {
469 KernelLoadedImage->LoadOptions = NULL;
470 } else {
471 //
472 // NUL-terminate in UTF-16.
473 //
474 KernelLoadedImage->LoadOptionsSize += 2;
475
476 KernelLoadedImage->LoadOptions = AllocatePool (
477 KernelLoadedImage->LoadOptionsSize
478 );
479 if (KernelLoadedImage->LoadOptions == NULL) {
480 KernelLoadedImage->LoadOptionsSize = 0;
481 Status = EFI_OUT_OF_RESOURCES;
482 goto FreeCommandLine;
483 }
484
485 UnicodeSPrintAsciiFormat (
486 KernelLoadedImage->LoadOptions,
487 KernelLoadedImage->LoadOptionsSize,
488 "%a%a",
489 (CommandLineSize == 0) ? "" : CommandLine,
490 (InitrdSize == 0) ? "" : " initrd=initrd"
491 );
492 DEBUG ((
493 DEBUG_INFO,
494 "%a: command line: \"%s\"\n",
495 __FUNCTION__,
496 (CHAR16 *)KernelLoadedImage->LoadOptions
497 ));
498 }
499
500 *ImageHandle = KernelImageHandle;
501 Status = EFI_SUCCESS;
502
503 FreeCommandLine:
504 if (CommandLineSize > 0) {
505 FreePool (CommandLine);
506 }
507
508 UnloadImage:
509 if (EFI_ERROR (Status)) {
510 gBS->UnloadImage (KernelImageHandle);
511 }
512
513 return Status;
514 }
515
516 /**
517 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
518
519 @param[in,out] ImageHandle Handle of image to be started. May assume a
520 different value on return if the image was
521 reloaded.
522
523 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
524 or the image has already been initialized with
525 StartImage
526 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
527 image should not be started.
528
529 @return Error codes returned by the started image
530 **/
531 EFI_STATUS
532 EFIAPI
533 QemuStartKernelImage (
534 IN OUT EFI_HANDLE *ImageHandle
535 )
536 {
537 EFI_STATUS Status;
538 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
539
540 Status = gBS->OpenProtocol (
541 *ImageHandle,
542 &gOvmfLoadedX86LinuxKernelProtocolGuid,
543 (VOID **)&LoadedImage,
544 gImageHandle, // AgentHandle
545 NULL, // ControllerHandle
546 EFI_OPEN_PROTOCOL_GET_PROTOCOL
547 );
548 if (!EFI_ERROR (Status)) {
549 return QemuStartLegacyImage (*ImageHandle);
550 }
551
552 Status = gBS->StartImage (
553 *ImageHandle,
554 NULL, // ExitDataSize
555 NULL // ExitData
556 );
557 #ifdef MDE_CPU_IA32
558 if (Status == EFI_UNSUPPORTED) {
559 EFI_HANDLE KernelImageHandle;
560
561 //
562 // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
563 // we are expecting a IA32 one, and the StartImage () boot service is unable
564 // to handle it, either because the image does not have the special .compat
565 // PE/COFF section that Linux specifies for mixed mode capable images, or
566 // because we are running without the support code for that. So load the
567 // image again, using the legacy loader, and unload the normally loaded
568 // image before starting the legacy one.
569 //
570 Status = QemuLoadLegacyImage (&KernelImageHandle);
571 if (EFI_ERROR (Status)) {
572 //
573 // Note: no change to (*ImageHandle), the caller will release it.
574 //
575 return Status;
576 }
577
578 //
579 // Swap in the legacy-loaded image.
580 //
581 QemuUnloadKernelImage (*ImageHandle);
582 *ImageHandle = KernelImageHandle;
583 return QemuStartLegacyImage (KernelImageHandle);
584 }
585
586 #endif
587 return Status;
588 }
589
590 /**
591 Unloads an image loaded with QemuLoadKernelImage ().
592
593 @param ImageHandle Handle that identifies the image to be
594 unloaded.
595
596 @retval EFI_SUCCESS The image has been unloaded.
597 @retval EFI_UNSUPPORTED The image has been started, and does not
598 support unload.
599 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
600
601 @return Exit code from the image's unload function.
602 **/
603 EFI_STATUS
604 EFIAPI
605 QemuUnloadKernelImage (
606 IN EFI_HANDLE ImageHandle
607 )
608 {
609 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
610 EFI_STATUS Status;
611
612 Status = gBS->OpenProtocol (
613 ImageHandle,
614 &gEfiLoadedImageProtocolGuid,
615 (VOID **)&KernelLoadedImage,
616 gImageHandle, // AgentHandle
617 NULL, // ControllerHandle
618 EFI_OPEN_PROTOCOL_GET_PROTOCOL
619 );
620 if (Status == EFI_UNSUPPORTED) {
621 //
622 // The handle exists but does not have an instance of the standard loaded
623 // image protocol installed on it. Attempt to unload it as a legacy image
624 // instead.
625 //
626 return QemuUnloadLegacyImage (ImageHandle);
627 }
628
629 if (EFI_ERROR (Status)) {
630 return EFI_INVALID_PARAMETER;
631 }
632
633 //
634 // We are unloading a normal, non-legacy loaded image, either on behalf of
635 // an external caller, or called from QemuStartKernelImage() on IA32, while
636 // switching from the normal to the legacy method to load and start a X64
637 // image.
638 //
639 if (KernelLoadedImage->LoadOptions != NULL) {
640 FreePool (KernelLoadedImage->LoadOptions);
641 KernelLoadedImage->LoadOptions = NULL;
642 }
643
644 KernelLoadedImage->LoadOptionsSize = 0;
645
646 return gBS->UnloadImage (ImageHandle);
647 }