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
)) {
89 Status
= FileHandleGetSize (FileHandle
, &FileSize
);
90 if (EFI_ERROR (Status
)) {
93 if (FileSize
> MAX_UINTN
) {
94 Status
= EFI_UNSUPPORTED
;
97 *Size
= (UINTN
)FileSize
;
100 FileHandle
->Close (FileHandle
);
106 ReadWholeQemuKernelLoaderBlob (
107 IN EFI_FILE_HANDLE Root
,
114 EFI_FILE_HANDLE FileHandle
;
117 Status
= Root
->Open (Root
, &FileHandle
, FileName
, EFI_FILE_MODE_READ
, 0);
118 if (EFI_ERROR (Status
)) {
122 Status
= FileHandle
->Read (FileHandle
, &ReadSize
, Buffer
);
123 if (EFI_ERROR (Status
)) {
126 if (ReadSize
!= Size
) {
127 Status
= EFI_PROTOCOL_ERROR
;
130 Status
= EFI_SUCCESS
;
132 FileHandle
->Close (FileHandle
);
137 Download the kernel, the initial ramdisk, and the kernel command line from
138 QEMU's fw_cfg. The kernel will be instructed via its command line to load
139 the initrd from the same Simple FileSystem where the kernel was loaded from.
141 @param[out] ImageHandle The image handle that was allocated for
144 @retval EFI_SUCCESS The image was loaded successfully.
145 @retval EFI_NOT_FOUND Kernel image was not found.
146 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
147 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
148 @retval EFI_ACCESS_DENIED The underlying LoadImage boot service call
149 returned EFI_SECURITY_VIOLATION, and the image
152 @return Error codes from any of the underlying
157 QemuLoadKernelImage (
158 OUT EFI_HANDLE
*ImageHandle
162 EFI_HANDLE KernelImageHandle
;
163 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
164 EFI_DEVICE_PATH_PROTOCOL
*DevicePathNode
;
165 EFI_HANDLE FsVolumeHandle
;
166 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL
*FsProtocol
;
167 EFI_FILE_HANDLE Root
;
168 UINTN CommandLineSize
;
173 // Load the image. This should call back into the QEMU EFI loader file system.
175 Status
= gBS
->LoadImage (
176 FALSE
, // BootPolicy: exact match required
177 gImageHandle
, // ParentImageHandle
178 (EFI_DEVICE_PATH_PROTOCOL
*)&mKernelDevicePath
,
179 NULL
, // SourceBuffer
187 case EFI_SECURITY_VIOLATION
:
189 // In this case, the image was loaded but failed to authenticate.
191 Status
= EFI_ACCESS_DENIED
;
195 DEBUG ((Status
== EFI_NOT_FOUND
? DEBUG_INFO
: DEBUG_ERROR
,
196 "%a: LoadImage(): %r\n", __FUNCTION__
, Status
));
201 // Construct the kernel command line.
203 Status
= gBS
->OpenProtocol (
205 &gEfiLoadedImageProtocolGuid
,
206 (VOID
**)&KernelLoadedImage
,
207 gImageHandle
, // AgentHandle
208 NULL
, // ControllerHandle
209 EFI_OPEN_PROTOCOL_GET_PROTOCOL
211 ASSERT_EFI_ERROR (Status
);
214 // Open the Qemu Kernel Loader abstract filesystem (volume) which will be
215 // used to query the "initrd" and to read the "cmdline" synthetic files.
217 DevicePathNode
= (EFI_DEVICE_PATH_PROTOCOL
*)&mQemuKernelLoaderFsDevicePath
;
218 Status
= gBS
->LocateDevicePath (
219 &gEfiSimpleFileSystemProtocolGuid
,
223 if (EFI_ERROR (Status
)) {
227 Status
= gBS
->HandleProtocol (
229 &gEfiSimpleFileSystemProtocolGuid
,
232 if (EFI_ERROR (Status
)) {
236 Status
= FsProtocol
->OpenVolume (FsVolumeHandle
, &Root
);
237 if (EFI_ERROR (Status
)) {
241 Status
= GetQemuKernelLoaderBlobSize (Root
, L
"cmdline", &CommandLineSize
);
242 if (EFI_ERROR (Status
)) {
246 if (CommandLineSize
== 0) {
247 KernelLoadedImage
->LoadOptionsSize
= 0;
249 CommandLine
= AllocatePool (CommandLineSize
);
250 if (CommandLine
== NULL
) {
251 Status
= EFI_OUT_OF_RESOURCES
;
255 Status
= ReadWholeQemuKernelLoaderBlob (Root
, L
"cmdline", CommandLineSize
,
257 if (EFI_ERROR (Status
)) {
258 goto FreeCommandLine
;
262 // Verify NUL-termination of the command line.
264 if (CommandLine
[CommandLineSize
- 1] != '\0') {
265 DEBUG ((DEBUG_ERROR
, "%a: kernel command line is not NUL-terminated\n",
267 Status
= EFI_PROTOCOL_ERROR
;
268 goto FreeCommandLine
;
272 // Drop the terminating NUL, convert to UTF-16.
274 KernelLoadedImage
->LoadOptionsSize
= (UINT32
)((CommandLineSize
- 1) * 2);
277 Status
= GetQemuKernelLoaderBlobSize (Root
, L
"initrd", &InitrdSize
);
278 if (EFI_ERROR (Status
)) {
279 goto FreeCommandLine
;
282 if (InitrdSize
> 0) {
284 // Append ' initrd=initrd' in UTF-16.
286 KernelLoadedImage
->LoadOptionsSize
+= sizeof (L
" initrd=initrd") - 2;
289 if (KernelLoadedImage
->LoadOptionsSize
== 0) {
290 KernelLoadedImage
->LoadOptions
= NULL
;
293 // NUL-terminate in UTF-16.
295 KernelLoadedImage
->LoadOptionsSize
+= 2;
297 KernelLoadedImage
->LoadOptions
= AllocatePool (
298 KernelLoadedImage
->LoadOptionsSize
);
299 if (KernelLoadedImage
->LoadOptions
== NULL
) {
300 KernelLoadedImage
->LoadOptionsSize
= 0;
301 Status
= EFI_OUT_OF_RESOURCES
;
302 goto FreeCommandLine
;
305 UnicodeSPrintAsciiFormat (
306 KernelLoadedImage
->LoadOptions
,
307 KernelLoadedImage
->LoadOptionsSize
,
309 (CommandLineSize
== 0) ? "" : CommandLine
,
310 (InitrdSize
== 0) ? "" : " initrd=initrd"
312 DEBUG ((DEBUG_INFO
, "%a: command line: \"%s\"\n", __FUNCTION__
,
313 (CHAR16
*)KernelLoadedImage
->LoadOptions
));
316 *ImageHandle
= KernelImageHandle
;
317 Status
= EFI_SUCCESS
;
320 if (CommandLineSize
> 0) {
321 FreePool (CommandLine
);
326 if (EFI_ERROR (Status
)) {
327 gBS
->UnloadImage (KernelImageHandle
);
334 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
336 @param[in,out] ImageHandle Handle of image to be started. May assume a
337 different value on return if the image was
340 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
341 or the image has already been initialized with
343 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
344 image should not be started.
346 @return Error codes returned by the started image
350 QemuStartKernelImage (
351 IN OUT EFI_HANDLE
*ImageHandle
354 return gBS
->StartImage (
356 NULL
, // ExitDataSize
362 Unloads an image loaded with QemuLoadKernelImage ().
364 @param ImageHandle Handle that identifies the image to be
367 @retval EFI_SUCCESS The image has been unloaded.
368 @retval EFI_UNSUPPORTED The image has been started, and does not
370 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
372 @return Exit code from the image's unload function.
376 QemuUnloadKernelImage (
377 IN EFI_HANDLE ImageHandle
380 EFI_LOADED_IMAGE_PROTOCOL
*KernelLoadedImage
;
383 Status
= gBS
->OpenProtocol (
385 &gEfiLoadedImageProtocolGuid
,
386 (VOID
**)&KernelLoadedImage
,
387 gImageHandle
, // AgentHandle
388 NULL
, // ControllerHandle
389 EFI_OPEN_PROTOCOL_GET_PROTOCOL
391 if (EFI_ERROR (Status
)) {
392 return EFI_INVALID_PARAMETER
;
395 if (KernelLoadedImage
->LoadOptions
!= NULL
) {
396 FreePool (KernelLoadedImage
->LoadOptions
);
397 KernelLoadedImage
->LoadOptions
= NULL
;
399 KernelLoadedImage
->LoadOptionsSize
= 0;
401 return gBS
->UnloadImage (ImageHandle
);