2 X86 specific implementation of QemuLoadImageLib library class interface
3 with support for loading mixed mode images and non-EFI stub images
5 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
8 SPDX-License-Identifier: BSD-2-Clause-Patent
13 #include <Guid/QemuKernelLoaderFsMedia.h>
14 #include <Library/DebugLib.h>
15 #include <Library/LoadLinuxLib.h>
16 #include <Library/MemoryAllocationLib.h>
17 #include <Library/PrintLib.h>
18 #include <Library/QemuFwCfgLib.h>
19 #include <Library/QemuLoadImageLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Protocol/DevicePath.h>
22 #include <Protocol/LoadedImage.h>
23 #include <Protocol/OvmfLoadedX86LinuxKernel.h>
27 EFI_DEVICE_PATH_PROTOCOL FilePathHeader
;
28 CHAR16 FilePath
[ARRAY_SIZE (L
"kernel")];
29 } KERNEL_FILE_DEVPATH
;
32 VENDOR_DEVICE_PATH VenMediaNode
;
33 KERNEL_FILE_DEVPATH FileNode
;
34 EFI_DEVICE_PATH_PROTOCOL EndNode
;
35 } KERNEL_VENMEDIA_FILE_DEVPATH
;
38 STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath
= {
41 MEDIA_DEVICE_PATH
, MEDIA_VENDOR_DP
,
42 { sizeof (VENDOR_DEVICE_PATH
) }
44 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
47 MEDIA_DEVICE_PATH
, MEDIA_FILEPATH_DP
,
48 { sizeof (KERNEL_FILE_DEVPATH
) }
52 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
53 { sizeof (EFI_DEVICE_PATH_PROTOCOL
) }
60 IN OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
63 if (LoadedImage
->SetupBuf
!= NULL
) {
64 FreePages (LoadedImage
->SetupBuf
,
65 EFI_SIZE_TO_PAGES (LoadedImage
->SetupSize
));
67 if (LoadedImage
->KernelBuf
!= NULL
) {
68 FreePages (LoadedImage
->KernelBuf
,
69 EFI_SIZE_TO_PAGES (LoadedImage
->KernelInitialSize
));
71 if (LoadedImage
->CommandLine
!= NULL
) {
72 FreePages (LoadedImage
->CommandLine
,
73 EFI_SIZE_TO_PAGES (LoadedImage
->CommandLineSize
));
75 if (LoadedImage
->InitrdData
!= NULL
) {
76 FreePages (LoadedImage
->InitrdData
,
77 EFI_SIZE_TO_PAGES (LoadedImage
->InitrdSize
));
84 OUT EFI_HANDLE
*ImageHandle
90 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
92 QemuFwCfgSelectItem (QemuFwCfgItemKernelSize
);
93 KernelSize
= (UINTN
)QemuFwCfgRead32 ();
95 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize
);
96 SetupSize
= (UINTN
)QemuFwCfgRead32 ();
98 if (KernelSize
== 0 || SetupSize
== 0) {
99 DEBUG ((DEBUG_INFO
, "qemu -kernel was not used.\n"));
100 return EFI_NOT_FOUND
;
103 LoadedImage
= AllocateZeroPool (sizeof (*LoadedImage
));
104 if (LoadedImage
== NULL
) {
105 return EFI_OUT_OF_RESOURCES
;
108 LoadedImage
->SetupSize
= SetupSize
;
109 LoadedImage
->SetupBuf
= LoadLinuxAllocateKernelSetupPages (
110 EFI_SIZE_TO_PAGES (LoadedImage
->SetupSize
));
111 if (LoadedImage
->SetupBuf
== NULL
) {
112 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for kernel setup!\n"));
113 Status
= EFI_OUT_OF_RESOURCES
;
117 DEBUG ((DEBUG_INFO
, "Setup size: 0x%x\n", (UINT32
)LoadedImage
->SetupSize
));
118 DEBUG ((DEBUG_INFO
, "Reading kernel setup image ..."));
119 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupData
);
120 QemuFwCfgReadBytes (LoadedImage
->SetupSize
, LoadedImage
->SetupBuf
);
121 DEBUG ((DEBUG_INFO
, " [done]\n"));
123 Status
= LoadLinuxCheckKernelSetup (LoadedImage
->SetupBuf
,
124 LoadedImage
->SetupSize
);
125 if (EFI_ERROR (Status
)) {
129 Status
= LoadLinuxInitializeKernelSetup (LoadedImage
->SetupBuf
);
130 if (EFI_ERROR (Status
)) {
134 LoadedImage
->KernelInitialSize
= LoadLinuxGetKernelSize (
135 LoadedImage
->SetupBuf
, KernelSize
);
136 if (LoadedImage
->KernelInitialSize
== 0) {
137 Status
= EFI_UNSUPPORTED
;
141 LoadedImage
->KernelBuf
= LoadLinuxAllocateKernelPages (
142 LoadedImage
->SetupBuf
,
143 EFI_SIZE_TO_PAGES (LoadedImage
->KernelInitialSize
)
145 if (LoadedImage
->KernelBuf
== NULL
) {
146 DEBUG ((DEBUG_ERROR
, "Unable to allocate memory for kernel!\n"));
147 Status
= EFI_OUT_OF_RESOURCES
;
151 DEBUG ((DEBUG_INFO
, "Kernel size: 0x%x\n", (UINT32
)KernelSize
));
152 DEBUG ((DEBUG_INFO
, "Reading kernel image ..."));
153 QemuFwCfgSelectItem (QemuFwCfgItemKernelData
);
154 QemuFwCfgReadBytes (KernelSize
, LoadedImage
->KernelBuf
);
155 DEBUG ((DEBUG_INFO
, " [done]\n"));
157 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize
);
158 LoadedImage
->CommandLineSize
= (UINTN
)QemuFwCfgRead32 ();
160 if (LoadedImage
->CommandLineSize
> 0) {
161 LoadedImage
->CommandLine
= LoadLinuxAllocateCommandLinePages (
163 LoadedImage
->CommandLineSize
));
164 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData
);
165 QemuFwCfgReadBytes (LoadedImage
->CommandLineSize
, LoadedImage
->CommandLine
);
168 Status
= LoadLinuxSetCommandLine (LoadedImage
->SetupBuf
,
169 LoadedImage
->CommandLine
);
170 if (EFI_ERROR (Status
)) {
174 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize
);
175 LoadedImage
->InitrdSize
= (UINTN
)QemuFwCfgRead32 ();
177 if (LoadedImage
->InitrdSize
> 0) {
178 LoadedImage
->InitrdData
= LoadLinuxAllocateInitrdPages (
179 LoadedImage
->SetupBuf
,
180 EFI_SIZE_TO_PAGES (LoadedImage
->InitrdSize
));
181 DEBUG ((DEBUG_INFO
, "Initrd size: 0x%x\n",
182 (UINT32
)LoadedImage
->InitrdSize
));
183 DEBUG ((DEBUG_INFO
, "Reading initrd image ..."));
184 QemuFwCfgSelectItem (QemuFwCfgItemInitrdData
);
185 QemuFwCfgReadBytes (LoadedImage
->InitrdSize
, LoadedImage
->InitrdData
);
186 DEBUG ((DEBUG_INFO
, " [done]\n"));
189 Status
= LoadLinuxSetInitrd (LoadedImage
->SetupBuf
, LoadedImage
->InitrdData
,
190 LoadedImage
->InitrdSize
);
191 if (EFI_ERROR (Status
)) {
196 Status
= gBS
->InstallProtocolInterface (ImageHandle
,
197 &gOvmfLoadedX86LinuxKernelProtocolGuid
, EFI_NATIVE_INTERFACE
,
199 if (EFI_ERROR (Status
)) {
205 FreeLegacyImage (LoadedImage
);
207 FreePool (LoadedImage
);
213 QemuStartLegacyImage (
214 IN EFI_HANDLE ImageHandle
218 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
220 Status
= gBS
->OpenProtocol (
222 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
223 (VOID
**)&LoadedImage
,
224 gImageHandle
, // AgentHandle
225 NULL
, // ControllerHandle
226 EFI_OPEN_PROTOCOL_GET_PROTOCOL
228 if (EFI_ERROR (Status
)) {
229 return EFI_INVALID_PARAMETER
;
232 return LoadLinux (LoadedImage
->KernelBuf
, LoadedImage
->SetupBuf
);
237 QemuUnloadLegacyImage (
238 IN EFI_HANDLE ImageHandle
242 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
244 Status
= gBS
->OpenProtocol (
246 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
247 (VOID
**)&LoadedImage
,
248 gImageHandle
, // AgentHandle
249 NULL
, // ControllerHandle
250 EFI_OPEN_PROTOCOL_GET_PROTOCOL
252 if (EFI_ERROR (Status
)) {
253 return EFI_INVALID_PARAMETER
;
256 Status
= gBS
->UninstallProtocolInterface (ImageHandle
,
257 &gOvmfLoadedX86LinuxKernelProtocolGuid
, LoadedImage
);
258 ASSERT_EFI_ERROR (Status
);
260 FreeLegacyImage (LoadedImage
);
261 FreePool (LoadedImage
);
266 Download the kernel, the initial ramdisk, and the kernel command line from
267 QEMU's fw_cfg. The kernel will be instructed via its command line to load
268 the initrd from the same Simple FileSystem where the kernel was loaded from.
270 @param[out] ImageHandle The image handle that was allocated for
273 @retval EFI_SUCCESS The image was loaded successfully.
274 @retval EFI_NOT_FOUND Kernel image was not found.
275 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
276 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
278 @return Error codes from any of the underlying
283 QemuLoadKernelImage (
284 OUT EFI_HANDLE
*ImageHandle
288 EFI_HANDLE KernelImageHandle
;
289 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
290 UINTN CommandLineSize
;
295 // Redundant assignment to work around GCC48/GCC49 limitations.
300 // Load the image. This should call back into the QEMU EFI loader file system.
302 Status
= gBS
->LoadImage (
303 FALSE
, // BootPolicy: exact match required
304 gImageHandle
, // ParentImageHandle
305 (EFI_DEVICE_PATH_PROTOCOL
*)&mKernelDevicePath
,
306 NULL
, // SourceBuffer
316 // The image does not exist - no -kernel image was supplied via the
317 // command line so no point in invoking the legacy fallback
319 return EFI_NOT_FOUND
;
321 case EFI_SECURITY_VIOLATION
:
323 // Since the image has been loaded, we need to unload it before proceeding
324 // to the EFI_ACCESS_DENIED case below.
326 gBS
->UnloadImage (KernelImageHandle
);
330 case EFI_ACCESS_DENIED
:
332 // We are running with UEFI secure boot enabled, and the image failed to
333 // authenticate. For compatibility reasons, we fall back to the legacy
334 // loader in this case.
338 case EFI_UNSUPPORTED
:
340 // The image is not natively supported or cross-type supported. Let's try
341 // loading it using the loader that parses the bzImage metadata directly.
343 Status
= QemuLoadLegacyImage (&KernelImageHandle
);
344 if (EFI_ERROR (Status
)) {
345 DEBUG ((DEBUG_ERROR
, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__
,
349 *ImageHandle
= KernelImageHandle
;
353 DEBUG ((DEBUG_ERROR
, "%a: LoadImage(): %r\n", __FUNCTION__
, Status
));
358 // Construct the kernel command line.
360 Status
= gBS
->OpenProtocol (
362 &gEfiLoadedImageProtocolGuid
,
363 (VOID
**)&KernelLoadedImage
,
364 gImageHandle
, // AgentHandle
365 NULL
, // ControllerHandle
366 EFI_OPEN_PROTOCOL_GET_PROTOCOL
368 ASSERT_EFI_ERROR (Status
);
370 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize
);
371 CommandLineSize
= (UINTN
)QemuFwCfgRead32 ();
373 if (CommandLineSize
== 0) {
374 KernelLoadedImage
->LoadOptionsSize
= 0;
376 CommandLine
= AllocatePool (CommandLineSize
);
377 if (CommandLine
== NULL
) {
378 Status
= EFI_OUT_OF_RESOURCES
;
382 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData
);
383 QemuFwCfgReadBytes (CommandLineSize
, CommandLine
);
386 // Verify NUL-termination of the command line.
388 if (CommandLine
[CommandLineSize
- 1] != '\0') {
389 DEBUG ((DEBUG_ERROR
, "%a: kernel command line is not NUL-terminated\n",
391 Status
= EFI_PROTOCOL_ERROR
;
392 goto FreeCommandLine
;
396 // Drop the terminating NUL, convert to UTF-16.
398 KernelLoadedImage
->LoadOptionsSize
= (UINT32
) ((CommandLineSize
- 1) * 2);
401 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize
);
402 InitrdSize
= (UINTN
)QemuFwCfgRead32 ();
404 if (InitrdSize
> 0) {
406 // Append ' initrd=initrd' in UTF-16.
408 KernelLoadedImage
->LoadOptionsSize
+= sizeof (L
" initrd=initrd") - 2;
411 if (KernelLoadedImage
->LoadOptionsSize
== 0) {
412 KernelLoadedImage
->LoadOptions
= NULL
;
415 // NUL-terminate in UTF-16.
417 KernelLoadedImage
->LoadOptionsSize
+= 2;
419 KernelLoadedImage
->LoadOptions
= AllocatePool (
420 KernelLoadedImage
->LoadOptionsSize
);
421 if (KernelLoadedImage
->LoadOptions
== NULL
) {
422 KernelLoadedImage
->LoadOptionsSize
= 0;
423 Status
= EFI_OUT_OF_RESOURCES
;
424 goto FreeCommandLine
;
427 UnicodeSPrintAsciiFormat (
428 KernelLoadedImage
->LoadOptions
,
429 KernelLoadedImage
->LoadOptionsSize
,
431 (CommandLineSize
== 0) ? "" : CommandLine
,
432 (InitrdSize
== 0) ? "" : " initrd=initrd"
434 DEBUG ((DEBUG_INFO
, "%a: command line: \"%s\"\n", __FUNCTION__
,
435 (CHAR16
*)KernelLoadedImage
->LoadOptions
));
438 *ImageHandle
= KernelImageHandle
;
442 if (CommandLineSize
> 0) {
443 FreePool (CommandLine
);
446 gBS
->UnloadImage (KernelImageHandle
);
452 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
454 @param[in,out] ImageHandle Handle of image to be started. May assume a
455 different value on return if the image was
458 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
459 or the image has already been initialized with
461 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
462 image should not be started.
464 @return Error codes returned by the started image
468 QemuStartKernelImage (
469 IN OUT EFI_HANDLE
*ImageHandle
473 OVMF_LOADED_X86_LINUX_KERNEL
*LoadedImage
;
475 Status
= gBS
->OpenProtocol (
477 &gOvmfLoadedX86LinuxKernelProtocolGuid
,
478 (VOID
**)&LoadedImage
,
479 gImageHandle
, // AgentHandle
480 NULL
, // ControllerHandle
481 EFI_OPEN_PROTOCOL_GET_PROTOCOL
483 if (!EFI_ERROR (Status
)) {
484 return QemuStartLegacyImage (*ImageHandle
);
487 Status
= gBS
->StartImage (
489 NULL
, // ExitDataSize
493 if (Status
== EFI_UNSUPPORTED
) {
494 EFI_HANDLE KernelImageHandle
;
497 // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
498 // we are expecting a IA32 one, and the StartImage () boot service is unable
499 // to handle it, either because the image does not have the special .compat
500 // PE/COFF section that Linux specifies for mixed mode capable images, or
501 // because we are running without the support code for that. So load the
502 // image again, using the legacy loader, and unload the normally loaded
503 // image before starting the legacy one.
505 Status
= QemuLoadLegacyImage (&KernelImageHandle
);
506 if (EFI_ERROR (Status
)) {
508 // Note: no change to (*ImageHandle), the caller will release it.
513 // Swap in the legacy-loaded image.
515 QemuUnloadKernelImage (*ImageHandle
);
516 *ImageHandle
= KernelImageHandle
;
517 return QemuStartLegacyImage (KernelImageHandle
);
524 Unloads an image loaded with QemuLoadKernelImage ().
526 @param ImageHandle Handle that identifies the image to be
529 @retval EFI_SUCCESS The image has been unloaded.
530 @retval EFI_UNSUPPORTED The image has been started, and does not
532 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
534 @return Exit code from the image's unload function.
538 QemuUnloadKernelImage (
539 IN EFI_HANDLE ImageHandle
542 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
545 Status
= gBS
->OpenProtocol (
547 &gEfiLoadedImageProtocolGuid
,
548 (VOID
**)&KernelLoadedImage
,
549 gImageHandle
, // AgentHandle
550 NULL
, // ControllerHandle
551 EFI_OPEN_PROTOCOL_GET_PROTOCOL
553 if (Status
== EFI_UNSUPPORTED
) {
555 // The handle exists but does not have an instance of the standard loaded
556 // image protocol installed on it. Attempt to unload it as a legacy image
559 return QemuUnloadLegacyImage (ImageHandle
);
562 if (EFI_ERROR (Status
)) {
563 return EFI_INVALID_PARAMETER
;
567 // We are unloading a normal, non-legacy loaded image, either on behalf of
568 // an external caller, or called from QemuStartKernelImage() on IA32, while
569 // switching from the normal to the legacy method to load and start a X64
572 if (KernelLoadedImage
->LoadOptions
!= NULL
) {
573 FreePool (KernelLoadedImage
->LoadOptions
);
574 KernelLoadedImage
->LoadOptions
= NULL
;
576 KernelLoadedImage
->LoadOptionsSize
= 0;
578 return gBS
->UnloadImage (ImageHandle
);