2 X86 specific implementation of QemuLoadImageLib library class interface
3 with support for loading mixed mode images and non-EFI stub images
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.
8 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
9 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
11 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
30 EFI_DEVICE_PATH_PROTOCOL FilePathHeader
;
31 CHAR16 FilePath
[ARRAY_SIZE (L
"kernel")];
32 } KERNEL_FILE_DEVPATH
;
35 VENDOR_DEVICE_PATH VenMediaNode
;
36 KERNEL_FILE_DEVPATH FileNode
;
37 EFI_DEVICE_PATH_PROTOCOL EndNode
;
38 } KERNEL_VENMEDIA_FILE_DEVPATH
;
41 STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath
= {
44 MEDIA_DEVICE_PATH
, MEDIA_VENDOR_DP
,
45 { sizeof (VENDOR_DEVICE_PATH
) }
47 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
50 MEDIA_DEVICE_PATH
, MEDIA_FILEPATH_DP
,
51 { sizeof (KERNEL_FILE_DEVPATH
) }
55 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
56 { sizeof (EFI_DEVICE_PATH_PROTOCOL
) }
63 IN OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
66 if (LoadedImage
->SetupBuf
!= NULL
) {
68 LoadedImage
->SetupBuf
,
69 EFI_SIZE_TO_PAGES (LoadedImage
->SetupSize
)
73 if (LoadedImage
->KernelBuf
!= NULL
) {
75 LoadedImage
->KernelBuf
,
76 EFI_SIZE_TO_PAGES (LoadedImage
->KernelInitialSize
)
80 if (LoadedImage
->CommandLine
!= NULL
) {
82 LoadedImage
->CommandLine
,
83 EFI_SIZE_TO_PAGES (LoadedImage
->CommandLineSize
)
87 if (LoadedImage
->InitrdData
!= NULL
) {
89 LoadedImage
->InitrdData
,
90 EFI_SIZE_TO_PAGES (LoadedImage
->InitrdSize
)
98 OUT EFI_HANDLE
*ImageHandle
104 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
106 QemuFwCfgSelectItem (QemuFwCfgItemKernelSize
);
107 KernelSize
= (UINTN
)QemuFwCfgRead32 ();
109 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize
);
110 SetupSize
= (UINTN
)QemuFwCfgRead32 ();
112 if ((KernelSize
== 0) || (SetupSize
== 0)) {
113 DEBUG ((DEBUG_INFO
, "qemu -kernel was not used.\n"));
114 return EFI_NOT_FOUND
;
117 LoadedImage
= AllocateZeroPool (sizeof (*LoadedImage
));
118 if (LoadedImage
== NULL
) {
119 return EFI_OUT_OF_RESOURCES
;
122 LoadedImage
->SetupSize
= SetupSize
;
123 LoadedImage
->SetupBuf
= LoadLinuxAllocateKernelSetupPages (
124 EFI_SIZE_TO_PAGES (LoadedImage
->SetupSize
)
126 if (LoadedImage
->SetupBuf
== NULL
) {
127 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for kernel setup!\n"));
128 Status
= EFI_OUT_OF_RESOURCES
;
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"));
138 Status
= LoadLinuxCheckKernelSetup (
139 LoadedImage
->SetupBuf
,
140 LoadedImage
->SetupSize
142 if (EFI_ERROR (Status
)) {
146 Status
= LoadLinuxInitializeKernelSetup (LoadedImage
->SetupBuf
);
147 if (EFI_ERROR (Status
)) {
151 LoadedImage
->KernelInitialSize
= LoadLinuxGetKernelSize (
152 LoadedImage
->SetupBuf
,
155 if (LoadedImage
->KernelInitialSize
== 0) {
156 Status
= EFI_UNSUPPORTED
;
160 LoadedImage
->KernelBuf
= LoadLinuxAllocateKernelPages (
161 LoadedImage
->SetupBuf
,
162 EFI_SIZE_TO_PAGES (LoadedImage
->KernelInitialSize
)
164 if (LoadedImage
->KernelBuf
== NULL
) {
165 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for kernel!\n"));
166 Status
= EFI_OUT_OF_RESOURCES
;
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"));
176 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize
);
177 LoadedImage
->CommandLineSize
= (UINTN
)QemuFwCfgRead32 ();
179 if (LoadedImage
->CommandLineSize
> 0) {
180 LoadedImage
->CommandLine
= LoadLinuxAllocateCommandLinePages (
182 LoadedImage
->CommandLineSize
185 if (LoadedImage
->CommandLine
== NULL
) {
186 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for kernel command line!\n"));
187 Status
= EFI_OUT_OF_RESOURCES
;
191 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData
);
192 QemuFwCfgReadBytes (LoadedImage
->CommandLineSize
, LoadedImage
->CommandLine
);
195 Status
= LoadLinuxSetCommandLine (
196 LoadedImage
->SetupBuf
,
197 LoadedImage
->CommandLine
199 if (EFI_ERROR (Status
)) {
203 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize
);
204 LoadedImage
->InitrdSize
= (UINTN
)QemuFwCfgRead32 ();
206 if (LoadedImage
->InitrdSize
> 0) {
207 LoadedImage
->InitrdData
= LoadLinuxAllocateInitrdPages (
208 LoadedImage
->SetupBuf
,
209 EFI_SIZE_TO_PAGES (LoadedImage
->InitrdSize
)
211 if (LoadedImage
->InitrdData
== NULL
) {
212 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for initrd!\n"));
213 Status
= EFI_OUT_OF_RESOURCES
;
219 "Initrd size: 0x%x\n",
220 (UINT32
)LoadedImage
->InitrdSize
222 DEBUG ((DEBUG_INFO
, "Reading initrd image ..."));
223 QemuFwCfgSelectItem (QemuFwCfgItemInitrdData
);
224 QemuFwCfgReadBytes (LoadedImage
->InitrdSize
, LoadedImage
->InitrdData
);
225 DEBUG ((DEBUG_INFO
, " [done]\n"));
228 Status
= LoadLinuxSetInitrd (
229 LoadedImage
->SetupBuf
,
230 LoadedImage
->InitrdData
,
231 LoadedImage
->InitrdSize
233 if (EFI_ERROR (Status
)) {
238 Status
= gBS
->InstallProtocolInterface (
240 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
241 EFI_NATIVE_INTERFACE
,
244 if (EFI_ERROR (Status
)) {
251 FreeLegacyImage (LoadedImage
);
253 FreePool (LoadedImage
);
259 QemuStartLegacyImage (
260 IN EFI_HANDLE ImageHandle
264 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
266 Status
= gBS
->OpenProtocol (
268 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
269 (VOID
**)&LoadedImage
,
270 gImageHandle
, // AgentHandle
271 NULL
, // ControllerHandle
272 EFI_OPEN_PROTOCOL_GET_PROTOCOL
274 if (EFI_ERROR (Status
)) {
275 return EFI_INVALID_PARAMETER
;
278 return LoadLinux (LoadedImage
->KernelBuf
, LoadedImage
->SetupBuf
);
283 QemuUnloadLegacyImage (
284 IN EFI_HANDLE ImageHandle
288 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
290 Status
= gBS
->OpenProtocol (
292 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
293 (VOID
**)&LoadedImage
,
294 gImageHandle
, // AgentHandle
295 NULL
, // ControllerHandle
296 EFI_OPEN_PROTOCOL_GET_PROTOCOL
298 if (EFI_ERROR (Status
)) {
299 return EFI_INVALID_PARAMETER
;
302 Status
= gBS
->UninstallProtocolInterface (
304 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
307 ASSERT_EFI_ERROR (Status
);
309 FreeLegacyImage (LoadedImage
);
310 FreePool (LoadedImage
);
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.
319 @param[out] ImageHandle The image handle that was allocated for
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.
327 @return Error codes from any of the underlying
332 QemuLoadKernelImage (
333 OUT EFI_HANDLE
*ImageHandle
337 EFI_HANDLE KernelImageHandle
;
338 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
339 UINTN CommandLineSize
;
344 // Redundant assignment to work around GCC48/GCC49 limitations.
349 // Load the image. This should call back into the QEMU EFI loader file system.
351 Status
= gBS
->LoadImage (
352 FALSE
, // BootPolicy: exact match required
353 gImageHandle
, // ParentImageHandle
354 (EFI_DEVICE_PATH_PROTOCOL
*)&mKernelDevicePath
,
355 NULL
, // SourceBuffer
365 // The image does not exist - no -kernel image was supplied via the
366 // command line so no point in invoking the legacy fallback
368 return EFI_NOT_FOUND
;
370 case EFI_SECURITY_VIOLATION
:
372 // Since the image has been loaded, we need to unload it before proceeding
373 // to the EFI_ACCESS_DENIED case below.
375 gBS
->UnloadImage (KernelImageHandle
);
379 case EFI_ACCESS_DENIED
:
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.
387 case EFI_UNSUPPORTED
:
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.
392 Status
= QemuLoadLegacyImage (&KernelImageHandle
);
393 if (EFI_ERROR (Status
)) {
396 "%a: QemuLoadLegacyImage(): %r\n",
403 *ImageHandle
= KernelImageHandle
;
407 DEBUG ((DEBUG_ERROR
, "%a: LoadImage(): %r\n", __FUNCTION__
, Status
));
412 // Construct the kernel command line.
414 Status
= gBS
->OpenProtocol (
416 &gEfiLoadedImageProtocolGuid
,
417 (VOID
**)&KernelLoadedImage
,
418 gImageHandle
, // AgentHandle
419 NULL
, // ControllerHandle
420 EFI_OPEN_PROTOCOL_GET_PROTOCOL
422 ASSERT_EFI_ERROR (Status
);
424 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize
);
425 CommandLineSize
= (UINTN
)QemuFwCfgRead32 ();
427 if (CommandLineSize
== 0) {
428 KernelLoadedImage
->LoadOptionsSize
= 0;
430 CommandLine
= AllocatePool (CommandLineSize
);
431 if (CommandLine
== NULL
) {
432 Status
= EFI_OUT_OF_RESOURCES
;
436 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData
);
437 QemuFwCfgReadBytes (CommandLineSize
, CommandLine
);
440 // Verify NUL-termination of the command line.
442 if (CommandLine
[CommandLineSize
- 1] != '\0') {
445 "%a: kernel command line is not NUL-terminated\n",
448 Status
= EFI_PROTOCOL_ERROR
;
449 goto FreeCommandLine
;
453 // Drop the terminating NUL, convert to UTF-16.
455 KernelLoadedImage
->LoadOptionsSize
= (UINT32
)((CommandLineSize
- 1) * 2);
458 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize
);
459 InitrdSize
= (UINTN
)QemuFwCfgRead32 ();
461 if (InitrdSize
> 0) {
463 // Append ' initrd=initrd' in UTF-16.
465 KernelLoadedImage
->LoadOptionsSize
+= sizeof (L
" initrd=initrd") - 2;
468 if (KernelLoadedImage
->LoadOptionsSize
== 0) {
469 KernelLoadedImage
->LoadOptions
= NULL
;
472 // NUL-terminate in UTF-16.
474 KernelLoadedImage
->LoadOptionsSize
+= 2;
476 KernelLoadedImage
->LoadOptions
= AllocatePool (
477 KernelLoadedImage
->LoadOptionsSize
479 if (KernelLoadedImage
->LoadOptions
== NULL
) {
480 KernelLoadedImage
->LoadOptionsSize
= 0;
481 Status
= EFI_OUT_OF_RESOURCES
;
482 goto FreeCommandLine
;
485 UnicodeSPrintAsciiFormat (
486 KernelLoadedImage
->LoadOptions
,
487 KernelLoadedImage
->LoadOptionsSize
,
489 (CommandLineSize
== 0) ? "" : CommandLine
,
490 (InitrdSize
== 0) ? "" : " initrd=initrd"
494 "%a: command line: \"%s\"\n",
496 (CHAR16
*)KernelLoadedImage
->LoadOptions
500 *ImageHandle
= KernelImageHandle
;
501 Status
= EFI_SUCCESS
;
504 if (CommandLineSize
> 0) {
505 FreePool (CommandLine
);
509 if (EFI_ERROR (Status
)) {
510 gBS
->UnloadImage (KernelImageHandle
);
517 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
519 @param[in,out] ImageHandle Handle of image to be started. May assume a
520 different value on return if the image was
523 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
524 or the image has already been initialized with
526 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
527 image should not be started.
529 @return Error codes returned by the started image
533 QemuStartKernelImage (
534 IN OUT EFI_HANDLE
*ImageHandle
538 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
540 Status
= gBS
->OpenProtocol (
542 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
543 (VOID
**)&LoadedImage
,
544 gImageHandle
, // AgentHandle
545 NULL
, // ControllerHandle
546 EFI_OPEN_PROTOCOL_GET_PROTOCOL
548 if (!EFI_ERROR (Status
)) {
549 return QemuStartLegacyImage (*ImageHandle
);
552 Status
= gBS
->StartImage (
554 NULL
, // ExitDataSize
558 if (Status
== EFI_UNSUPPORTED
) {
559 EFI_HANDLE KernelImageHandle
;
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.
570 Status
= QemuLoadLegacyImage (&KernelImageHandle
);
571 if (EFI_ERROR (Status
)) {
573 // Note: no change to (*ImageHandle), the caller will release it.
579 // Swap in the legacy-loaded image.
581 QemuUnloadKernelImage (*ImageHandle
);
582 *ImageHandle
= KernelImageHandle
;
583 return QemuStartLegacyImage (KernelImageHandle
);
591 Unloads an image loaded with QemuLoadKernelImage ().
593 @param ImageHandle Handle that identifies the image to be
596 @retval EFI_SUCCESS The image has been unloaded.
597 @retval EFI_UNSUPPORTED The image has been started, and does not
599 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
601 @return Exit code from the image's unload function.
605 QemuUnloadKernelImage (
606 IN EFI_HANDLE ImageHandle
609 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
612 Status
= gBS
->OpenProtocol (
614 &gEfiLoadedImageProtocolGuid
,
615 (VOID
**)&KernelLoadedImage
,
616 gImageHandle
, // AgentHandle
617 NULL
, // ControllerHandle
618 EFI_OPEN_PROTOCOL_GET_PROTOCOL
620 if (Status
== EFI_UNSUPPORTED
) {
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
626 return QemuUnloadLegacyImage (ImageHandle
);
629 if (EFI_ERROR (Status
)) {
630 return EFI_INVALID_PARAMETER
;
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
639 if (KernelLoadedImage
->LoadOptions
!= NULL
) {
640 FreePool (KernelLoadedImage
->LoadOptions
);
641 KernelLoadedImage
->LoadOptions
= NULL
;
644 KernelLoadedImage
->LoadOptionsSize
= 0;
646 return gBS
->UnloadImage (ImageHandle
);