2 Generic implementation of QemuLoadImageLib library class interface.
4 Copyright (c) 2020, ARM Ltd. All rights reserved.<BR>
6 SPDX-License-Identifier: BSD-2-Clause-Patent
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>
25 EFI_DEVICE_PATH_PROTOCOL FilePathHeader
;
26 CHAR16 FilePath
[ARRAY_SIZE (L
"kernel")];
27 } KERNEL_FILE_DEVPATH
;
30 VENDOR_DEVICE_PATH VenMediaNode
;
31 KERNEL_FILE_DEVPATH FileNode
;
32 EFI_DEVICE_PATH_PROTOCOL EndNode
;
33 } KERNEL_VENMEDIA_FILE_DEVPATH
;
36 VENDOR_DEVICE_PATH VenMediaNode
;
37 EFI_DEVICE_PATH_PROTOCOL EndNode
;
38 } SINGLE_VENMEDIA_NODE_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
) }
60 STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mQemuKernelLoaderFsDevicePath
= {
63 MEDIA_DEVICE_PATH
, MEDIA_VENDOR_DP
,
64 { sizeof (VENDOR_DEVICE_PATH
) }
66 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
68 END_DEVICE_PATH_TYPE
, END_ENTIRE_DEVICE_PATH_SUBTYPE
,
69 { sizeof (EFI_DEVICE_PATH_PROTOCOL
) }
75 GetQemuKernelLoaderBlobSize (
76 IN EFI_FILE_HANDLE Root
,
82 EFI_FILE_HANDLE FileHandle
;
85 Status
= Root
->Open (Root
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
86 if (EFI_ERROR (Status
)) {
90 Status
= FileHandleGetSize (FileHandle
, &FileSize
);
91 if (EFI_ERROR (Status
)) {
95 if (FileSize
> MAX_UINTN
) {
96 Status
= EFI_UNSUPPORTED
;
100 *Size
= (UINTN
)FileSize
;
101 Status
= EFI_SUCCESS
;
103 FileHandle
->Close (FileHandle
);
109 ReadWholeQemuKernelLoaderBlob (
110 IN EFI_FILE_HANDLE Root
,
117 EFI_FILE_HANDLE FileHandle
;
120 Status
= Root
->Open (Root
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
121 if (EFI_ERROR (Status
)) {
126 Status
= FileHandle
->Read (FileHandle
, &ReadSize
, Buffer
);
127 if (EFI_ERROR (Status
)) {
131 if (ReadSize
!= Size
) {
132 Status
= EFI_PROTOCOL_ERROR
;
136 Status
= EFI_SUCCESS
;
138 FileHandle
->Close (FileHandle
);
143 Download the kernel, the initial ramdisk, and the kernel command line from
144 QEMU's fw_cfg. The kernel will be instructed via its command line to load
145 the initrd from the same Simple FileSystem where the kernel was loaded from.
147 @param[out] ImageHandle The image handle that was allocated for
150 @retval EFI_SUCCESS The image was loaded successfully.
151 @retval EFI_NOT_FOUND Kernel image was not found.
152 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
153 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
154 @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call
155 returned EFI_SECURITY_VIOLATION, and the image
158 @return Error codes from any of the underlying
163 QemuLoadKernelImage (
164 OUT EFI_HANDLE
*ImageHandle
168 EFI_HANDLE KernelImageHandle
;
169 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
170 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
171 EFI_HANDLE FsVolumeHandle
;
172 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*FsProtocol
;
173 EFI_FILE_HANDLE Root
;
174 UINTN CommandLineSize
;
179 // Load the image. This should call back into the QEMU EFI loader file system.
181 Status
= gBS
->LoadImage (
182 FALSE
, // BootPolicy: exact match required
183 gImageHandle
, // ParentImageHandle
184 (EFI_DEVICE_PATH_PROTOCOL
*)&mKernelDevicePath
,
185 NULL
, // SourceBuffer
193 case EFI_SECURITY_VIOLATION
:
195 // In this case, the image was loaded but failed to authenticate.
197 Status
= EFI_ACCESS_DENIED
;
202 Status
== EFI_NOT_FOUND
? DEBUG_INFO
: DEBUG_ERROR
,
203 "%a: LoadImage(): %r\n",
211 // Construct the kernel command line.
213 Status
= gBS
->OpenProtocol (
215 &gEfiLoadedImageProtocolGuid
,
216 (VOID
**)&KernelLoadedImage
,
217 gImageHandle
, // AgentHandle
218 NULL
, // ControllerHandle
219 EFI_OPEN_PROTOCOL_GET_PROTOCOL
221 ASSERT_EFI_ERROR (Status
);
224 // Open the Qemu Kernel Loader abstract filesystem (volume) which will be
225 // used to query the "initrd" and to read the "cmdline" synthetic files.
227 DevicePathNode
= (EFI_DEVICE_PATH_PROTOCOL
*)&mQemuKernelLoaderFsDevicePath
;
228 Status
= gBS
->LocateDevicePath (
229 &gEfiSimpleFileSystemProtocolGuid
,
233 if (EFI_ERROR (Status
)) {
237 Status
= gBS
->HandleProtocol (
239 &gEfiSimpleFileSystemProtocolGuid
,
242 if (EFI_ERROR (Status
)) {
246 Status
= FsProtocol
->OpenVolume (FsVolumeHandle
, &Root
);
247 if (EFI_ERROR (Status
)) {
251 Status
= GetQemuKernelLoaderBlobSize (Root
, L
"cmdline", &CommandLineSize
);
252 if (EFI_ERROR (Status
)) {
256 if (CommandLineSize
== 0) {
257 KernelLoadedImage
->LoadOptionsSize
= 0;
259 CommandLine
= AllocatePool (CommandLineSize
);
260 if (CommandLine
== NULL
) {
261 Status
= EFI_OUT_OF_RESOURCES
;
265 Status
= ReadWholeQemuKernelLoaderBlob (
271 if (EFI_ERROR (Status
)) {
272 goto FreeCommandLine
;
276 // Verify NUL-termination of the command line.
278 if (CommandLine
[CommandLineSize
- 1] != '\0') {
281 "%a: kernel command line is not NUL-terminated\n",
284 Status
= EFI_PROTOCOL_ERROR
;
285 goto FreeCommandLine
;
289 // Drop the terminating NUL, convert to UTF-16.
291 KernelLoadedImage
->LoadOptionsSize
= (UINT32
)((CommandLineSize
- 1) * 2);
294 Status
= GetQemuKernelLoaderBlobSize (Root
, L
"initrd", &InitrdSize
);
295 if (EFI_ERROR (Status
)) {
296 goto FreeCommandLine
;
299 if (InitrdSize
> 0) {
301 // Append ' initrd=initrd' in UTF-16.
303 KernelLoadedImage
->LoadOptionsSize
+= sizeof (L
" initrd=initrd") - 2;
306 if (KernelLoadedImage
->LoadOptionsSize
== 0) {
307 KernelLoadedImage
->LoadOptions
= NULL
;
310 // NUL-terminate in UTF-16.
312 KernelLoadedImage
->LoadOptionsSize
+= 2;
314 KernelLoadedImage
->LoadOptions
= AllocatePool (
315 KernelLoadedImage
->LoadOptionsSize
317 if (KernelLoadedImage
->LoadOptions
== NULL
) {
318 KernelLoadedImage
->LoadOptionsSize
= 0;
319 Status
= EFI_OUT_OF_RESOURCES
;
320 goto FreeCommandLine
;
323 UnicodeSPrintAsciiFormat (
324 KernelLoadedImage
->LoadOptions
,
325 KernelLoadedImage
->LoadOptionsSize
,
327 (CommandLineSize
== 0) ? "" : CommandLine
,
328 (InitrdSize
== 0) ? "" : " initrd=initrd"
332 "%a: command line: \"%s\"\n",
334 (CHAR16
*)KernelLoadedImage
->LoadOptions
338 *ImageHandle
= KernelImageHandle
;
339 Status
= EFI_SUCCESS
;
342 if (CommandLineSize
> 0) {
343 FreePool (CommandLine
);
349 if (EFI_ERROR (Status
)) {
350 gBS
->UnloadImage (KernelImageHandle
);
357 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
359 @param[in,out] ImageHandle Handle of image to be started. May assume a
360 different value on return if the image was
363 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
364 or the image has already been initialized with
366 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
367 image should not be started.
369 @return Error codes returned by the started image
373 QemuStartKernelImage (
374 IN OUT EFI_HANDLE
*ImageHandle
377 return gBS
->StartImage (
379 NULL
, // ExitDataSize
385 Unloads an image loaded with QemuLoadKernelImage ().
387 @param ImageHandle Handle that identifies the image to be
390 @retval EFI_SUCCESS The image has been unloaded.
391 @retval EFI_UNSUPPORTED The image has been started, and does not
393 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
395 @return Exit code from the image's unload function.
399 QemuUnloadKernelImage (
400 IN EFI_HANDLE ImageHandle
403 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
406 Status
= gBS
->OpenProtocol (
408 &gEfiLoadedImageProtocolGuid
,
409 (VOID
**)&KernelLoadedImage
,
410 gImageHandle
, // AgentHandle
411 NULL
, // ControllerHandle
412 EFI_OPEN_PROTOCOL_GET_PROTOCOL
414 if (EFI_ERROR (Status
)) {
415 return EFI_INVALID_PARAMETER
;
418 if (KernelLoadedImage
->LoadOptions
!= NULL
) {
419 FreePool (KernelLoadedImage
->LoadOptions
);
420 KernelLoadedImage
->LoadOptions
= NULL
;
423 KernelLoadedImage
->LoadOptionsSize
= 0;
425 return gBS
->UnloadImage (ImageHandle
);