VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

  • Property svn:eol-style set to native
File size: 13.2 KB
Line 
1/** @file
2 Generic implementation of QemuLoadImageLib library class interface.
3
4 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7**/
8
9#include <Uefi.h>
10
11#include <Base.h>
12#include <Guid/QemuKernelLoaderFsMedia.h>
13#include <Library/DebugLib.h>
14#include <Library/FileHandleLib.h>
15#include <Library/MemoryAllocationLib.h>
16#include <Library/PrintLib.h>
17#include <Library/QemuLoadImageLib.h>
18#include <Library/UefiBootServicesTableLib.h>
19#include <Protocol/DevicePath.h>
20#include <Protocol/LoadedImage.h>
21#include <Protocol/SimpleFileSystem.h>
22
23#pragma pack (1)
24typedef struct {
25 EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
26 CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
27} KERNEL_FILE_DEVPATH;
28
29typedef struct {
30 VENDOR_DEVICE_PATH VenMediaNode;
31 KERNEL_FILE_DEVPATH FileNode;
32 EFI_DEVICE_PATH_PROTOCOL EndNode;
33} KERNEL_VENMEDIA_FILE_DEVPATH;
34
35typedef struct {
36 VENDOR_DEVICE_PATH VenMediaNode;
37 EFI_DEVICE_PATH_PROTOCOL EndNode;
38} SINGLE_VENMEDIA_NODE_DEVPATH;
39#pragma pack ()
40
41STATIC 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
60STATIC CONST KERNEL_VENMEDIA_FILE_DEVPATH mShimDevicePath = {
61 {
62 {
63 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
64 { sizeof (VENDOR_DEVICE_PATH) }
65 },
66 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
67 }, {
68 {
69 MEDIA_DEVICE_PATH, MEDIA_FILEPATH_DP,
70 { sizeof (KERNEL_FILE_DEVPATH) }
71 },
72 L"shim",
73 }, {
74 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
75 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
76 }
77};
78
79STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mQemuKernelLoaderFsDevicePath = {
80 {
81 {
82 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
83 { sizeof (VENDOR_DEVICE_PATH) }
84 },
85 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
86 }, {
87 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
88 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
89 }
90};
91
92STATIC
93EFI_STATUS
94GetQemuKernelLoaderBlobSize (
95 IN EFI_FILE_HANDLE Root,
96 IN CHAR16 *FileName,
97 OUT UINTN *Size
98 )
99{
100 EFI_STATUS Status;
101 EFI_FILE_HANDLE FileHandle;
102 UINT64 FileSize;
103
104 Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
105 if (EFI_ERROR (Status)) {
106 return Status;
107 }
108
109 Status = FileHandleGetSize (FileHandle, &FileSize);
110 if (EFI_ERROR (Status)) {
111 goto CloseFile;
112 }
113
114 if (FileSize > MAX_UINTN) {
115 Status = EFI_UNSUPPORTED;
116 goto CloseFile;
117 }
118
119 *Size = (UINTN)FileSize;
120 Status = EFI_SUCCESS;
121CloseFile:
122 FileHandle->Close (FileHandle);
123 return Status;
124}
125
126STATIC
127EFI_STATUS
128ReadWholeQemuKernelLoaderBlob (
129 IN EFI_FILE_HANDLE Root,
130 IN CHAR16 *FileName,
131 IN UINTN Size,
132 OUT VOID *Buffer
133 )
134{
135 EFI_STATUS Status;
136 EFI_FILE_HANDLE FileHandle;
137 UINTN ReadSize;
138
139 Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
140 if (EFI_ERROR (Status)) {
141 return Status;
142 }
143
144 ReadSize = Size;
145 Status = FileHandle->Read (FileHandle, &ReadSize, Buffer);
146 if (EFI_ERROR (Status)) {
147 goto CloseFile;
148 }
149
150 if (ReadSize != Size) {
151 Status = EFI_PROTOCOL_ERROR;
152 goto CloseFile;
153 }
154
155 Status = EFI_SUCCESS;
156CloseFile:
157 FileHandle->Close (FileHandle);
158 return Status;
159}
160
161/**
162 Download the kernel, the initial ramdisk, and the kernel command line from
163 QEMU's fw_cfg. The kernel will be instructed via its command line to load
164 the initrd from the same Simple FileSystem where the kernel was loaded from.
165
166 @param[out] ImageHandle The image handle that was allocated for
167 loading the image
168
169 @retval EFI_SUCCESS The image was loaded successfully.
170 @retval EFI_NOT_FOUND Kernel image was not found.
171 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
172 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
173 @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call
174 returned EFI_SECURITY_VIOLATION, and the image
175 was unloaded again.
176
177 @return Error codes from any of the underlying
178 functions.
179**/
180EFI_STATUS
181EFIAPI
182QemuLoadKernelImage (
183 OUT EFI_HANDLE *ImageHandle
184 )
185{
186 EFI_STATUS Status;
187 EFI_HANDLE KernelImageHandle;
188 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
189 EFI_DEVICE_PATH_PROTOCOL *DevicePathNode;
190 EFI_HANDLE FsVolumeHandle;
191 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *FsProtocol;
192 EFI_FILE_HANDLE Root;
193 UINTN CommandLineSize;
194 CHAR8 *CommandLine;
195 UINTN InitrdSize;
196 BOOLEAN Shim;
197
198 //
199 // Load the image. This should call back into the QEMU EFI loader file system.
200 //
201 Status = gBS->LoadImage (
202 FALSE, // BootPolicy: exact match required
203 gImageHandle, // ParentImageHandle
204 (EFI_DEVICE_PATH_PROTOCOL *)&mShimDevicePath,
205 NULL, // SourceBuffer
206 0, // SourceSize
207 &KernelImageHandle
208 );
209 if (Status == EFI_SUCCESS) {
210 Shim = TRUE;
211 DEBUG ((DEBUG_INFO, "%a: booting via shim\n", __func__));
212 } else {
213 Shim = FALSE;
214 if (Status == EFI_SECURITY_VIOLATION) {
215 gBS->UnloadImage (KernelImageHandle);
216 }
217
218 if (Status != EFI_NOT_FOUND) {
219 DEBUG ((DEBUG_INFO, "%a: LoadImage(shim): %r\n", __func__, Status));
220 return Status;
221 }
222
223 Status = gBS->LoadImage (
224 FALSE, // BootPolicy: exact match required
225 gImageHandle, // ParentImageHandle
226 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
227 NULL, // SourceBuffer
228 0, // SourceSize
229 &KernelImageHandle
230 );
231 }
232
233 switch (Status) {
234 case EFI_SUCCESS:
235 break;
236
237 case EFI_SECURITY_VIOLATION:
238 //
239 // In this case, the image was loaded but failed to authenticate.
240 //
241 Status = EFI_ACCESS_DENIED;
242 goto UnloadImage;
243
244 default:
245 DEBUG ((
246 Status == EFI_NOT_FOUND ? DEBUG_INFO : DEBUG_ERROR,
247 "%a: LoadImage(): %r\n",
248 __func__,
249 Status
250 ));
251 return Status;
252 }
253
254 //
255 // Construct the kernel command line.
256 //
257 Status = gBS->OpenProtocol (
258 KernelImageHandle,
259 &gEfiLoadedImageProtocolGuid,
260 (VOID **)&KernelLoadedImage,
261 gImageHandle, // AgentHandle
262 NULL, // ControllerHandle
263 EFI_OPEN_PROTOCOL_GET_PROTOCOL
264 );
265 ASSERT_EFI_ERROR (Status);
266
267 //
268 // Open the Qemu Kernel Loader abstract filesystem (volume) which will be
269 // used to query the "initrd" and to read the "cmdline" synthetic files.
270 //
271 DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL *)&mQemuKernelLoaderFsDevicePath;
272 Status = gBS->LocateDevicePath (
273 &gEfiSimpleFileSystemProtocolGuid,
274 &DevicePathNode,
275 &FsVolumeHandle
276 );
277 if (EFI_ERROR (Status)) {
278 goto UnloadImage;
279 }
280
281 Status = gBS->HandleProtocol (
282 FsVolumeHandle,
283 &gEfiSimpleFileSystemProtocolGuid,
284 (VOID **)&FsProtocol
285 );
286 if (EFI_ERROR (Status)) {
287 goto UnloadImage;
288 }
289
290 Status = FsProtocol->OpenVolume (FsVolumeHandle, &Root);
291 if (EFI_ERROR (Status)) {
292 goto UnloadImage;
293 }
294
295 Status = GetQemuKernelLoaderBlobSize (Root, L"cmdline", &CommandLineSize);
296 if (EFI_ERROR (Status)) {
297 if (Status == EFI_NOT_FOUND) {
298 CommandLineSize = 0;
299 } else {
300 goto CloseRoot;
301 }
302 }
303
304 CommandLine = NULL;
305 if (CommandLineSize == 0) {
306 KernelLoadedImage->LoadOptionsSize = 0;
307 } else {
308 CommandLine = AllocatePool (CommandLineSize);
309 if (CommandLine == NULL) {
310 Status = EFI_OUT_OF_RESOURCES;
311 goto CloseRoot;
312 }
313
314 Status = ReadWholeQemuKernelLoaderBlob (
315 Root,
316 L"cmdline",
317 CommandLineSize,
318 CommandLine
319 );
320 if (EFI_ERROR (Status)) {
321 goto FreeCommandLine;
322 }
323
324 //
325 // Verify NUL-termination of the command line.
326 //
327 if (CommandLine[CommandLineSize - 1] != '\0') {
328 DEBUG ((
329 DEBUG_ERROR,
330 "%a: kernel command line is not NUL-terminated\n",
331 __func__
332 ));
333 Status = EFI_PROTOCOL_ERROR;
334 goto FreeCommandLine;
335 }
336
337 //
338 // Drop the terminating NUL, convert to UTF-16.
339 //
340 KernelLoadedImage->LoadOptionsSize = (UINT32)((CommandLineSize - 1) * 2);
341 }
342
343 Status = GetQemuKernelLoaderBlobSize (Root, L"initrd", &InitrdSize);
344 if (EFI_ERROR (Status)) {
345 if (Status == EFI_NOT_FOUND) {
346 InitrdSize = 0;
347 } else {
348 goto FreeCommandLine;
349 }
350 }
351
352 if (InitrdSize > 0) {
353 //
354 // Append ' initrd=initrd' in UTF-16.
355 //
356 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
357 }
358
359 if (Shim) {
360 //
361 // Prefix 'kernel ' in UTF-16.
362 //
363 KernelLoadedImage->LoadOptionsSize += sizeof (L"kernel ") - 2;
364 }
365
366 if (KernelLoadedImage->LoadOptionsSize == 0) {
367 KernelLoadedImage->LoadOptions = NULL;
368 } else {
369 //
370 // NUL-terminate in UTF-16.
371 //
372 KernelLoadedImage->LoadOptionsSize += 2;
373
374 KernelLoadedImage->LoadOptions = AllocatePool (
375 KernelLoadedImage->LoadOptionsSize
376 );
377 if (KernelLoadedImage->LoadOptions == NULL) {
378 KernelLoadedImage->LoadOptionsSize = 0;
379 Status = EFI_OUT_OF_RESOURCES;
380 goto FreeCommandLine;
381 }
382
383 UnicodeSPrintAsciiFormat (
384 KernelLoadedImage->LoadOptions,
385 KernelLoadedImage->LoadOptionsSize,
386 "%a%a%a",
387 (Shim == FALSE) ? "" : "kernel ",
388 (CommandLineSize == 0) ? "" : CommandLine,
389 (InitrdSize == 0) ? "" : " initrd=initrd"
390 );
391 DEBUG ((
392 DEBUG_INFO,
393 "%a: command line: \"%s\"\n",
394 __func__,
395 (CHAR16 *)KernelLoadedImage->LoadOptions
396 ));
397 }
398
399 *ImageHandle = KernelImageHandle;
400 Status = EFI_SUCCESS;
401
402FreeCommandLine:
403 if (CommandLineSize > 0) {
404 FreePool (CommandLine);
405 }
406
407CloseRoot:
408 Root->Close (Root);
409UnloadImage:
410 if (EFI_ERROR (Status)) {
411 gBS->UnloadImage (KernelImageHandle);
412 }
413
414 return Status;
415}
416
417/**
418 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
419
420 @param[in,out] ImageHandle Handle of image to be started. May assume a
421 different value on return if the image was
422 reloaded.
423
424 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
425 or the image has already been initialized with
426 StartImage
427 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
428 image should not be started.
429
430 @return Error codes returned by the started image
431**/
432EFI_STATUS
433EFIAPI
434QemuStartKernelImage (
435 IN OUT EFI_HANDLE *ImageHandle
436 )
437{
438 return gBS->StartImage (
439 *ImageHandle,
440 NULL, // ExitDataSize
441 NULL // ExitData
442 );
443}
444
445/**
446 Unloads an image loaded with QemuLoadKernelImage ().
447
448 @param ImageHandle Handle that identifies the image to be
449 unloaded.
450
451 @retval EFI_SUCCESS The image has been unloaded.
452 @retval EFI_UNSUPPORTED The image has been started, and does not
453 support unload.
454 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
455
456 @return Exit code from the image's unload function.
457**/
458EFI_STATUS
459EFIAPI
460QemuUnloadKernelImage (
461 IN EFI_HANDLE ImageHandle
462 )
463{
464 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
465 EFI_STATUS Status;
466
467 Status = gBS->OpenProtocol (
468 ImageHandle,
469 &gEfiLoadedImageProtocolGuid,
470 (VOID **)&KernelLoadedImage,
471 gImageHandle, // AgentHandle
472 NULL, // ControllerHandle
473 EFI_OPEN_PROTOCOL_GET_PROTOCOL
474 );
475 if (EFI_ERROR (Status)) {
476 return EFI_INVALID_PARAMETER;
477 }
478
479 if (KernelLoadedImage->LoadOptions != NULL) {
480 FreePool (KernelLoadedImage->LoadOptions);
481 KernelLoadedImage->LoadOptions = NULL;
482 }
483
484 KernelLoadedImage->LoadOptionsSize = 0;
485
486 return gBS->UnloadImage (ImageHandle);
487}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette