]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/X86QemuLoadImageLib/X86QemuLoadImageLib.c
1177582ab05134080b68a7d09cee475f5e754c90
[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 if (LoadedImage->CommandLine == NULL) {
165 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for kernel command line!\n"));
166 Status = EFI_OUT_OF_RESOURCES;
167 goto FreeImage;
168 }
169 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
170 QemuFwCfgReadBytes (LoadedImage->CommandLineSize, LoadedImage->CommandLine);
171 }
172
173 Status = LoadLinuxSetCommandLine (LoadedImage->SetupBuf,
174 LoadedImage->CommandLine);
175 if (EFI_ERROR (Status)) {
176 goto FreeImage;
177 }
178
179 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
180 LoadedImage->InitrdSize = (UINTN)QemuFwCfgRead32 ();
181
182 if (LoadedImage->InitrdSize > 0) {
183 LoadedImage->InitrdData = LoadLinuxAllocateInitrdPages (
184 LoadedImage->SetupBuf,
185 EFI_SIZE_TO_PAGES (LoadedImage->InitrdSize));
186 if (LoadedImage->InitrdData == NULL) {
187 DEBUG ((DEBUG_ERROR, "Unable to allocate memory for initrd!\n"));
188 Status = EFI_OUT_OF_RESOURCES;
189 goto FreeImage;
190 }
191 DEBUG ((DEBUG_INFO, "Initrd size: 0x%x\n",
192 (UINT32)LoadedImage->InitrdSize));
193 DEBUG ((DEBUG_INFO, "Reading initrd image ..."));
194 QemuFwCfgSelectItem (QemuFwCfgItemInitrdData);
195 QemuFwCfgReadBytes (LoadedImage->InitrdSize, LoadedImage->InitrdData);
196 DEBUG ((DEBUG_INFO, " [done]\n"));
197 }
198
199 Status = LoadLinuxSetInitrd (LoadedImage->SetupBuf, LoadedImage->InitrdData,
200 LoadedImage->InitrdSize);
201 if (EFI_ERROR (Status)) {
202 goto FreeImage;
203 }
204
205 *ImageHandle = NULL;
206 Status = gBS->InstallProtocolInterface (ImageHandle,
207 &gOvmfLoadedX86LinuxKernelProtocolGuid, EFI_NATIVE_INTERFACE,
208 LoadedImage);
209 if (EFI_ERROR (Status)) {
210 goto FreeImage;
211 }
212 return EFI_SUCCESS;
213
214 FreeImage:
215 FreeLegacyImage (LoadedImage);
216 FreeImageDesc:
217 FreePool (LoadedImage);
218 return Status;
219 }
220
221 STATIC
222 EFI_STATUS
223 QemuStartLegacyImage (
224 IN EFI_HANDLE ImageHandle
225 )
226 {
227 EFI_STATUS Status;
228 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
229
230 Status = gBS->OpenProtocol (
231 ImageHandle,
232 &gOvmfLoadedX86LinuxKernelProtocolGuid,
233 (VOID **)&LoadedImage,
234 gImageHandle, // AgentHandle
235 NULL, // ControllerHandle
236 EFI_OPEN_PROTOCOL_GET_PROTOCOL
237 );
238 if (EFI_ERROR (Status)) {
239 return EFI_INVALID_PARAMETER;
240 }
241
242 return LoadLinux (LoadedImage->KernelBuf, LoadedImage->SetupBuf);
243 }
244
245 STATIC
246 EFI_STATUS
247 QemuUnloadLegacyImage (
248 IN EFI_HANDLE ImageHandle
249 )
250 {
251 EFI_STATUS Status;
252 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
253
254 Status = gBS->OpenProtocol (
255 ImageHandle,
256 &gOvmfLoadedX86LinuxKernelProtocolGuid,
257 (VOID **)&LoadedImage,
258 gImageHandle, // AgentHandle
259 NULL, // ControllerHandle
260 EFI_OPEN_PROTOCOL_GET_PROTOCOL
261 );
262 if (EFI_ERROR (Status)) {
263 return EFI_INVALID_PARAMETER;
264 }
265
266 Status = gBS->UninstallProtocolInterface (ImageHandle,
267 &gOvmfLoadedX86LinuxKernelProtocolGuid, LoadedImage);
268 ASSERT_EFI_ERROR (Status);
269
270 FreeLegacyImage (LoadedImage);
271 FreePool (LoadedImage);
272 return EFI_SUCCESS;
273 }
274
275 /**
276 Download the kernel, the initial ramdisk, and the kernel command line from
277 QEMU's fw_cfg. The kernel will be instructed via its command line to load
278 the initrd from the same Simple FileSystem where the kernel was loaded from.
279
280 @param[out] ImageHandle The image handle that was allocated for
281 loading the image
282
283 @retval EFI_SUCCESS The image was loaded successfully.
284 @retval EFI_NOT_FOUND Kernel image was not found.
285 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
286 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
287
288 @return Error codes from any of the underlying
289 functions.
290 **/
291 EFI_STATUS
292 EFIAPI
293 QemuLoadKernelImage (
294 OUT EFI_HANDLE *ImageHandle
295 )
296 {
297 EFI_STATUS Status;
298 EFI_HANDLE KernelImageHandle;
299 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
300 UINTN CommandLineSize;
301 CHAR8 *CommandLine;
302 UINTN InitrdSize;
303
304 //
305 // Redundant assignment to work around GCC48/GCC49 limitations.
306 //
307 CommandLine = NULL;
308
309 //
310 // Load the image. This should call back into the QEMU EFI loader file system.
311 //
312 Status = gBS->LoadImage (
313 FALSE, // BootPolicy: exact match required
314 gImageHandle, // ParentImageHandle
315 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
316 NULL, // SourceBuffer
317 0, // SourceSize
318 &KernelImageHandle
319 );
320 switch (Status) {
321 case EFI_SUCCESS:
322 break;
323
324 case EFI_NOT_FOUND:
325 //
326 // The image does not exist - no -kernel image was supplied via the
327 // command line so no point in invoking the legacy fallback
328 //
329 return EFI_NOT_FOUND;
330
331 case EFI_SECURITY_VIOLATION:
332 //
333 // Since the image has been loaded, we need to unload it before proceeding
334 // to the EFI_ACCESS_DENIED case below.
335 //
336 gBS->UnloadImage (KernelImageHandle);
337 //
338 // Fall through
339 //
340 case EFI_ACCESS_DENIED:
341 //
342 // We are running with UEFI secure boot enabled, and the image failed to
343 // authenticate. For compatibility reasons, we fall back to the legacy
344 // loader in this case.
345 //
346 // Fall through
347 //
348 case EFI_UNSUPPORTED:
349 //
350 // The image is not natively supported or cross-type supported. Let's try
351 // loading it using the loader that parses the bzImage metadata directly.
352 //
353 Status = QemuLoadLegacyImage (&KernelImageHandle);
354 if (EFI_ERROR (Status)) {
355 DEBUG ((DEBUG_ERROR, "%a: QemuLoadLegacyImage(): %r\n", __FUNCTION__,
356 Status));
357 return Status;
358 }
359 *ImageHandle = KernelImageHandle;
360 return EFI_SUCCESS;
361
362 default:
363 DEBUG ((DEBUG_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
364 return Status;
365 }
366
367 //
368 // Construct the kernel command line.
369 //
370 Status = gBS->OpenProtocol (
371 KernelImageHandle,
372 &gEfiLoadedImageProtocolGuid,
373 (VOID **)&KernelLoadedImage,
374 gImageHandle, // AgentHandle
375 NULL, // ControllerHandle
376 EFI_OPEN_PROTOCOL_GET_PROTOCOL
377 );
378 ASSERT_EFI_ERROR (Status);
379
380 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineSize);
381 CommandLineSize = (UINTN)QemuFwCfgRead32 ();
382
383 if (CommandLineSize == 0) {
384 KernelLoadedImage->LoadOptionsSize = 0;
385 } else {
386 CommandLine = AllocatePool (CommandLineSize);
387 if (CommandLine == NULL) {
388 Status = EFI_OUT_OF_RESOURCES;
389 goto UnloadImage;
390 }
391
392 QemuFwCfgSelectItem (QemuFwCfgItemCommandLineData);
393 QemuFwCfgReadBytes (CommandLineSize, CommandLine);
394
395 //
396 // Verify NUL-termination of the command line.
397 //
398 if (CommandLine[CommandLineSize - 1] != '\0') {
399 DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
400 __FUNCTION__));
401 Status = EFI_PROTOCOL_ERROR;
402 goto FreeCommandLine;
403 }
404
405 //
406 // Drop the terminating NUL, convert to UTF-16.
407 //
408 KernelLoadedImage->LoadOptionsSize = (UINT32) ((CommandLineSize - 1) * 2);
409 }
410
411 QemuFwCfgSelectItem (QemuFwCfgItemInitrdSize);
412 InitrdSize = (UINTN)QemuFwCfgRead32 ();
413
414 if (InitrdSize > 0) {
415 //
416 // Append ' initrd=initrd' in UTF-16.
417 //
418 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
419 }
420
421 if (KernelLoadedImage->LoadOptionsSize == 0) {
422 KernelLoadedImage->LoadOptions = NULL;
423 } else {
424 //
425 // NUL-terminate in UTF-16.
426 //
427 KernelLoadedImage->LoadOptionsSize += 2;
428
429 KernelLoadedImage->LoadOptions = AllocatePool (
430 KernelLoadedImage->LoadOptionsSize);
431 if (KernelLoadedImage->LoadOptions == NULL) {
432 KernelLoadedImage->LoadOptionsSize = 0;
433 Status = EFI_OUT_OF_RESOURCES;
434 goto FreeCommandLine;
435 }
436
437 UnicodeSPrintAsciiFormat (
438 KernelLoadedImage->LoadOptions,
439 KernelLoadedImage->LoadOptionsSize,
440 "%a%a",
441 (CommandLineSize == 0) ? "" : CommandLine,
442 (InitrdSize == 0) ? "" : " initrd=initrd"
443 );
444 DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
445 (CHAR16 *)KernelLoadedImage->LoadOptions));
446 }
447
448 *ImageHandle = KernelImageHandle;
449 return EFI_SUCCESS;
450
451 FreeCommandLine:
452 if (CommandLineSize > 0) {
453 FreePool (CommandLine);
454 }
455 UnloadImage:
456 gBS->UnloadImage (KernelImageHandle);
457
458 return Status;
459 }
460
461 /**
462 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
463
464 @param[in,out] ImageHandle Handle of image to be started. May assume a
465 different value on return if the image was
466 reloaded.
467
468 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
469 or the image has already been initialized with
470 StartImage
471 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
472 image should not be started.
473
474 @return Error codes returned by the started image
475 **/
476 EFI_STATUS
477 EFIAPI
478 QemuStartKernelImage (
479 IN OUT EFI_HANDLE *ImageHandle
480 )
481 {
482 EFI_STATUS Status;
483 OVMF_LOADED_X86_LINUX_KERNEL *LoadedImage;
484
485 Status = gBS->OpenProtocol (
486 *ImageHandle,
487 &gOvmfLoadedX86LinuxKernelProtocolGuid,
488 (VOID **)&LoadedImage,
489 gImageHandle, // AgentHandle
490 NULL, // ControllerHandle
491 EFI_OPEN_PROTOCOL_GET_PROTOCOL
492 );
493 if (!EFI_ERROR (Status)) {
494 return QemuStartLegacyImage (*ImageHandle);
495 }
496
497 Status = gBS->StartImage (
498 *ImageHandle,
499 NULL, // ExitDataSize
500 NULL // ExitData
501 );
502 #ifdef MDE_CPU_IA32
503 if (Status == EFI_UNSUPPORTED) {
504 EFI_HANDLE KernelImageHandle;
505
506 //
507 // On IA32, EFI_UNSUPPORTED means that the image's machine type is X64 while
508 // we are expecting a IA32 one, and the StartImage () boot service is unable
509 // to handle it, either because the image does not have the special .compat
510 // PE/COFF section that Linux specifies for mixed mode capable images, or
511 // because we are running without the support code for that. So load the
512 // image again, using the legacy loader, and unload the normally loaded
513 // image before starting the legacy one.
514 //
515 Status = QemuLoadLegacyImage (&KernelImageHandle);
516 if (EFI_ERROR (Status)) {
517 //
518 // Note: no change to (*ImageHandle), the caller will release it.
519 //
520 return Status;
521 }
522 //
523 // Swap in the legacy-loaded image.
524 //
525 QemuUnloadKernelImage (*ImageHandle);
526 *ImageHandle = KernelImageHandle;
527 return QemuStartLegacyImage (KernelImageHandle);
528 }
529 #endif
530 return Status;
531 }
532
533 /**
534 Unloads an image loaded with QemuLoadKernelImage ().
535
536 @param ImageHandle Handle that identifies the image to be
537 unloaded.
538
539 @retval EFI_SUCCESS The image has been unloaded.
540 @retval EFI_UNSUPPORTED The image has been started, and does not
541 support unload.
542 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
543
544 @return Exit code from the image's unload function.
545 **/
546 EFI_STATUS
547 EFIAPI
548 QemuUnloadKernelImage (
549 IN EFI_HANDLE ImageHandle
550 )
551 {
552 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
553 EFI_STATUS Status;
554
555 Status = gBS->OpenProtocol (
556 ImageHandle,
557 &gEfiLoadedImageProtocolGuid,
558 (VOID **)&KernelLoadedImage,
559 gImageHandle, // AgentHandle
560 NULL, // ControllerHandle
561 EFI_OPEN_PROTOCOL_GET_PROTOCOL
562 );
563 if (Status == EFI_UNSUPPORTED) {
564 //
565 // The handle exists but does not have an instance of the standard loaded
566 // image protocol installed on it. Attempt to unload it as a legacy image
567 // instead.
568 //
569 return QemuUnloadLegacyImage (ImageHandle);
570 }
571
572 if (EFI_ERROR (Status)) {
573 return EFI_INVALID_PARAMETER;
574 }
575
576 //
577 // We are unloading a normal, non-legacy loaded image, either on behalf of
578 // an external caller, or called from QemuStartKernelImage() on IA32, while
579 // switching from the normal to the legacy method to load and start a X64
580 // image.
581 //
582 if (KernelLoadedImage->LoadOptions != NULL) {
583 FreePool (KernelLoadedImage->LoadOptions);
584 KernelLoadedImage->LoadOptions = NULL;
585 }
586 KernelLoadedImage->LoadOptionsSize = 0;
587
588 return gBS->UnloadImage (ImageHandle);
589 }