]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
931553c0c1fb2dc315be98eea5bbc91222fb5eb6
[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 Copyright (c) 2006 - 2015, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
7
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9 **/
10
11 #include <Uefi.h>
12
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>
24
25 #pragma pack (1)
26 typedef struct {
27 EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
28 CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
29 } KERNEL_FILE_DEVPATH;
30
31 typedef struct {
32 VENDOR_DEVICE_PATH VenMediaNode;
33 KERNEL_FILE_DEVPATH FileNode;
34 EFI_DEVICE_PATH_PROTOCOL EndNode;
35 } KERNEL_VENMEDIA_FILE_DEVPATH;
36 #pragma pack ()
37
38 STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mKernelDevicePath = {
39 {
40 {
41 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
42 { sizeof (VENDOR_DEVICE_PATH) }
43 },
44 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
45 }, {
46 {
47 MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
48 { sizeof (KERNEL_FILE_DEVPATH) }
49 },
50 L"kernel",
51 }, {
52 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
53 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
54 }
55 };
56
57 STATIC
58 VOID
59 FreeLegacyImage (
60 IN OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage
61 )
62 {
63 if (LoadedImage->SetupBuf != NULL) {
64 FreePages (LoadedImage->SetupBuf,
65 EFI_SIZE_TO_PAGES (LoadedImage->SetupSize));
66 }
67 if (LoadedImage->KernelBuf != NULL) {
68 FreePages (LoadedImage->KernelBuf,
69 EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize));
70 }
71 if (LoadedImage->CommandLine != NULL) {
72 FreePages (LoadedImage->CommandLine,
73 EFI_SIZE_TO_PAGES (LoadedImage->CommandLineSize));
74 }
75 if (LoadedImage->InitrdData != NULL) {
76 FreePages (LoadedImage->InitrdData,
77 EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
78 }
79 }
80
81 STATIC
82 EFI_STATUS
83 QemuLoadLegacyImage (
84 OUT EFI_HANDLE *ImageHandle
85 )
86 {
87 EFI_STATUS Status;
88 UINTN KernelSize;
89 UINTN SetupSize;
90 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
91
92 QemuFwCfgSelectItem (QemuFwCfgItemKernelSize);
93 KernelSize = (UINTN)QemuFwCfgRead32 ();
94
95 QemuFwCfgSelectItem (QemuFwCfgItemKernelSetupSize);
96 SetupSize = (UINTN)QemuFwCfgRead32 ();
97
98 if (KernelSize == 0 || SetupSize == 0) {
99 DEBUG ((DEBUG_INFO, "qemu -kernel was not used.\n"));
100 return EFI_NOT_FOUND;
101 }
102
103 LoadedImage = AllocateZeroPool (sizeof (*LoadedImage));
104 if (LoadedImage == NULL) {
105 return EFI_OUT_OF_RESOURCES;
106 }
107
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;
114 goto FreeImageDesc;
115 }
116
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"));
122
123 Status = LoadLinuxCheckKernelSetup (LoadedImage->SetupBuf,
124 LoadedImage->SetupSize);
125 if (EFI_ERROR (Status)) {
126 goto FreeImage;
127 }
128
129 Status = LoadLinuxInitializeKernelSetup (LoadedImage->SetupBuf);
130 if (EFI_ERROR (Status)) {
131 goto FreeImage;
132 }
133
134 LoadedImage->KernelInitialSize = LoadLinuxGetKernelSize (
135 LoadedImage->SetupBuf, KernelSize);
136 if (LoadedImage->KernelInitialSize == 0) {
137 Status = EFI_UNSUPPORTED;
138 goto FreeImage;
139 }
140
141 LoadedImage->KernelBuf = LoadLinuxAllocateKernelPages (
142 LoadedImage->SetupBuf,
143 EFI_SIZE_TO_PAGES (LoadedImage->KernelInitialSize)
144 );
145 if (LoadedImage->KernelBuf == NULL) {
146 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel!\n"));
147 Status = EFI_OUT_OF_RESOURCES;
148 goto FreeImage;
149 }
150
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"));
156
157 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
158 LoadedImage->CommandLineSize = (UINTN)QemuFwCfgRead32 ();
159
160 if (LoadedImage->CommandLineSize > 0) {
161 LoadedImage->CommandLine = LoadLinuxAllocateCommandLinePages (
162 EFI_SIZE_TO_PAGES (
163 LoadedImage->CommandLineSize));
164 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
165 QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
166 }
167
168 Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
169 LoadedImage->CommandLine);
170 if (EFI_ERROR (Status)) {
171 goto FreeImage;
172 }
173
174 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
175 LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 ();
176
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"));
187 }
188
189 Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
190 LoadedImage->InitrdSize);
191 if (EFI_ERROR (Status)) {
192 goto FreeImage;
193 }
194
195 *ImageHandle = NULL;
196 Status = gBS->InstallProtocolInterface (ImageHandle,
197 &gOvmfLoadedX86LinuxKernelProtocolGuid, EFI_NATIVE_INTERFACE,
198 LoadedImage);
199 if (EFI_ERROR (Status)) {
200 goto FreeImage;
201 }
202 return EFI_SUCCESS;
203
204 FreeImage:
205 FreeLegacyImage (LoadedImage);
206 FreeImageDesc:
207 FreePool (LoadedImage);
208 return Status;
209 }
210
211 STATIC
212 EFI_STATUS
213 QemuStartLegacyImage (
214 IN EFI_HANDLE ImageHandle
215 )
216 {
217 EFI_STATUS Status;
218 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
219
220 Status = gBS->OpenProtocol (
221 ImageHandle,
222 &gOvmfLoadedX86LinuxKernelProtocolGuid,
223 (VOID **)&LoadedImage,
224 gImageHandle, // AgentHandle
225 NULL, // ControllerHandle
226 EFI_OPEN_PROTOCOL_GET_PROTOCOL
227 );
228 if (EFI_ERROR (Status)) {
229 return EFI_INVALID_PARAMETER;
230 }
231
232 return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf);
233 }
234
235 STATIC
236 EFI_STATUS
237 QemuUnloadLegacyImage (
238 IN EFI_HANDLE ImageHandle
239 )
240 {
241 EFI_STATUS Status;
242 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
243
244 Status = gBS->OpenProtocol (
245 ImageHandle,
246 &gOvmfLoadedX86LinuxKernelProtocolGuid,
247 (VOID **)&LoadedImage,
248 gImageHandle, // AgentHandle
249 NULL, // ControllerHandle
250 EFI_OPEN_PROTOCOL_GET_PROTOCOL
251 );
252 if (EFI_ERROR (Status)) {
253 return EFI_INVALID_PARAMETER;
254 }
255
256 Status = gBS->UninstallProtocolInterface (ImageHandle,
257 &gOvmfLoadedX86LinuxKernelProtocolGuid, LoadedImage);
258 ASSERT_EFI_ERROR (Status);
259
260 FreeLegacyImage (LoadedImage);
261 FreePool (LoadedImage);
262 return EFI_SUCCESS;
263 }
264
265 /**
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.
269
270 @param[out] ImageHandle The image handle that was allocated for
271 loading the image
272
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.
277
278 @return Error codes from any of the underlying
279 functions.
280 **/
281 EFI_STATUS
282 EFIAPI
283 QemuLoadKernelImage (
284 OUT EFI_HANDLE *ImageHandle
285 )
286 {
287 EFI_STATUS Status;
288 EFI_HANDLE KernelImageHandle;
289 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
290 UINTN CommandLineSize;
291 CHAR8 *CommandLine;
292 UINTN InitrdSize;
293
294 //
295 // Redundant assignment to work around GCC48/GCC49 limitations.
296 //
297 CommandLine = NULL;
298
299 //
300 // Load the image. This should call back into the QEMU EFI loader file system.
301 //
302 Status = gBS->LoadImage (
303 FALSE, // BootPolicy: exact match required
304 gImageHandle, // ParentImageHandle
305 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
306 NULL, // SourceBuffer
307 0, // SourceSize
308 &KernelImageHandle
309 );
310 switch (Status) {
311 case EFI_SUCCESS:
312 break;
313
314 case EFI_NOT_FOUND:
315 //
316 // The image does not exist - no -kernel image was supplied via the
317 // command line so no point in invoking the legacy fallback
318 //
319 return EFI_NOT_FOUND;
320
321 case EFI_SECURITY_VIOLATION:
322 //
323 // Since the image has been loaded, we need to unload it before proceeding
324 // to the EFI_ACCESS_DENIED case below.
325 //
326 gBS->UnloadImage (KernelImageHandle);
327 //
328 // Fall through
329 //
330 case EFI_ACCESS_DENIED:
331 //
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.
335 //
336 // Fall through
337 //
338 case EFI_UNSUPPORTED:
339 //
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.
342 //
343 Status = QemuLoadLegacyImage (&KernelImageHandle);
344 if (EFI_ERROR (Status)) {
345 DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
346 Status));
347 return Status;
348 }
349 *ImageHandle = KernelImageHandle;
350 return EFI_SUCCESS;
351
352 default:
353 DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
354 return Status;
355 }
356
357 //
358 // Construct the kernel command line.
359 //
360 Status = gBS->OpenProtocol (
361 KernelImageHandle,
362 &gEfiLoadedImageProtocolGuid,
363 (VOID **)&KernelLoadedImage,
364 gImageHandle, // AgentHandle
365 NULL, // ControllerHandle
366 EFI_OPEN_PROTOCOL_GET_PROTOCOL
367 );
368 ASSERT_EFI_ERROR (Status);
369
370 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
371 CommandLineSize = (UINTN)QemuFwCfgRead32 ();
372
373 if (CommandLineSize == 0) {
374 KernelLoadedImage->LoadOptionsSize = 0;
375 } else {
376 CommandLine = AllocatePool (CommandLineSize);
377 if (CommandLine == NULL) {
378 Status = EFI_OUT_OF_RESOURCES;
379 goto UnloadImage;
380 }
381
382 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
383 QemuFwCfgReadBytes (CommandLineSize, CommandLine);
384
385 //
386 // Verify NUL-termination of the command line.
387 //
388 if (CommandLine[CommandLineSize - 1] != '\0') {
389 DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
390 __FUNCTION__));
391 Status = EFI_PROTOCOL_ERROR;
392 goto FreeCommandLine;
393 }
394
395 //
396 // Drop the terminating NUL, convert to UTF-16.
397 //
398 KernelLoadedImage->LoadOptionsSize = (UINT32) ((CommandLineSize - 1) * 2);
399 }
400
401 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
402 InitrdSize = (UINTN)QemuFwCfgRead32 ();
403
404 if (InitrdSize > 0) {
405 //
406 // Append ' initrd=initrd' in UTF-16.
407 //
408 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
409 }
410
411 if (KernelLoadedImage->LoadOptionsSize == 0) {
412 KernelLoadedImage->LoadOptions = NULL;
413 } else {
414 //
415 // NUL-terminate in UTF-16.
416 //
417 KernelLoadedImage->LoadOptionsSize += 2;
418
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;
425 }
426
427 UnicodeSPrintAsciiFormat (
428 KernelLoadedImage->LoadOptions,
429 KernelLoadedImage->LoadOptionsSize,
430 "%a%a",
431 (CommandLineSize == 0) ? "" : CommandLine,
432 (InitrdSize == 0) ? "" : " initrd=initrd"
433 );
434 DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
435 (CHAR16 *)KernelLoadedImage->LoadOptions));
436 }
437
438 *ImageHandle = KernelImageHandle;
439 return EFI_SUCCESS;
440
441 FreeCommandLine:
442 if (CommandLineSize > 0) {
443 FreePool (CommandLine);
444 }
445 UnloadImage:
446 gBS->UnloadImage (KernelImageHandle);
447
448 return Status;
449 }
450
451 /**
452 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
453
454 @param[in,out] ImageHandle Handle of image to be started. May assume a
455 different value on return if the image was
456 reloaded.
457
458 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
459 or the image has already been initialized with
460 StartImage
461 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
462 image should not be started.
463
464 @return Error codes returned by the started image
465 **/
466 EFI_STATUS
467 EFIAPI
468 QemuStartKernelImage (
469 IN OUT EFI_HANDLE *ImageHandle
470 )
471 {
472 EFI_STATUS Status;
473 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
474
475 Status = gBS->OpenProtocol (
476 *ImageHandle,
477 &gOvmfLoadedX86LinuxKernelProtocolGuid,
478 (VOID **)&LoadedImage,
479 gImageHandle, // AgentHandle
480 NULL, // ControllerHandle
481 EFI_OPEN_PROTOCOL_GET_PROTOCOL
482 );
483 if (!EFI_ERROR (Status)) {
484 return QemuStartLegacyImage (*ImageHandle);
485 }
486
487 Status = gBS->StartImage (
488 *ImageHandle,
489 NULL, // ExitDataSize
490 NULL // ExitData
491 );
492 #ifdef MDE_CPU_IA32
493 if (Status == EFI_UNSUPPORTED) {
494 EFI_HANDLE KernelImageHandle;
495
496 //
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.
504 //
505 Status = QemuLoadLegacyImage (&KernelImageHandle);
506 if (EFI_ERROR (Status)) {
507 //
508 // Note: no change to (*ImageHandle), the caller will release it.
509 //
510 return Status;
511 }
512 //
513 // Swap in the legacy-loaded image.
514 //
515 QemuUnloadKernelImage (*ImageHandle);
516 *ImageHandle = KernelImageHandle;
517 return QemuStartLegacyImage (KernelImageHandle);
518 }
519 #endif
520 return Status;
521 }
522
523 /**
524 Unloads an image loaded with QemuLoadKernelImage ().
525
526 @param ImageHandle Handle that identifies the image to be
527 unloaded.
528
529 @retval EFI_SUCCESS The image has been unloaded.
530 @retval EFI_UNSUPPORTED The image has been started, and does not
531 support unload.
532 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
533
534 @return Exit code from the image's unload function.
535 **/
536 EFI_STATUS
537 EFIAPI
538 QemuUnloadKernelImage (
539 IN EFI_HANDLE ImageHandle
540 )
541 {
542 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
543 EFI_STATUS Status;
544
545 Status = gBS->OpenProtocol (
546 ImageHandle,
547 &gEfiLoadedImageProtocolGuid,
548 (VOID **)&KernelLoadedImage,
549 gImageHandle, // AgentHandle
550 NULL, // ControllerHandle
551 EFI_OPEN_PROTOCOL_GET_PROTOCOL
552 );
553 if (Status == EFI_UNSUPPORTED) {
554 //
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
557 // instead.
558 //
559 return QemuUnloadLegacyImage (ImageHandle);
560 }
561
562 if (EFI_ERROR (Status)) {
563 return EFI_INVALID_PARAMETER;
564 }
565
566 //
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
570 // image.
571 //
572 if (KernelLoadedImage->LoadOptions != NULL) {
573 FreePool (KernelLoadedImage->LoadOptions);
574 KernelLoadedImage->LoadOptions = NULL;
575 }
576 KernelLoadedImage->LoadOptionsSize = 0;
577
578 return gBS->UnloadImage (ImageHandle);
579 }