]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Library / GenericQemuLoadImageLib / GenericQemuLoadImageLib.c
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)
24 typedef struct {
25 EFI_DEVICE_PATH_PROTOCOL FilePathHeader;
26 CHAR16 FilePath[ARRAY_SIZE (L"kernel")];
27 } KERNEL_FILE_DEVPATH;
28
29 typedef struct {
30 VENDOR_DEVICE_PATH VenMediaNode;
31 KERNEL_FILE_DEVPATH FileNode;
32 EFI_DEVICE_PATH_PROTOCOL EndNode;
33 } KERNEL_VENMEDIA_FILE_DEVPATH;
34
35 typedef struct {
36 VENDOR_DEVICE_PATH VenMediaNode;
37 EFI_DEVICE_PATH_PROTOCOL EndNode;
38 } SINGLE_VENMEDIA_NODE_DEVPATH;
39 #pragma pack ()
40
41 STATIC 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
60 STATIC CONST SINGLE_VENMEDIA_NODE_DEVPATH mQemuKernelLoaderFsDevicePath = {
61 {
62 {
63 MEDIA_DEVICE_PATH, MEDIA_VENDOR_DP,
64 { sizeof (VENDOR_DEVICE_PATH) }
65 },
66 QEMU_KERNEL_LOADER_FS_MEDIA_GUID
67 }, {
68 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
69 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
70 }
71 };
72
73 STATIC
74 EFI_STATUS
75 GetQemuKernelLoaderBlobSize (
76 IN EFI_FILE_HANDLE Root,
77 IN CHAR16 *FileName,
78 OUT UINTN *Size
79 )
80 {
81 EFI_STATUS Status;
82 EFI_FILE_HANDLE FileHandle;
83 UINT64 FileSize;
84
85 Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
86 if (EFI_ERROR (Status)) {
87 return Status;
88 }
89
90 Status = FileHandleGetSize (FileHandle, &FileSize);
91 if (EFI_ERROR (Status)) {
92 goto CloseFile;
93 }
94
95 if (FileSize > MAX_UINTN) {
96 Status = EFI_UNSUPPORTED;
97 goto CloseFile;
98 }
99
100 *Size = (UINTN)FileSize;
101 Status = EFI_SUCCESS;
102 CloseFile:
103 FileHandle->Close (FileHandle);
104 return Status;
105 }
106
107 STATIC
108 EFI_STATUS
109 ReadWholeQemuKernelLoaderBlob (
110 IN EFI_FILE_HANDLE Root,
111 IN CHAR16 *FileName,
112 IN UINTN Size,
113 OUT VOID *Buffer
114 )
115 {
116 EFI_STATUS Status;
117 EFI_FILE_HANDLE FileHandle;
118 UINTN ReadSize;
119
120 Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
121 if (EFI_ERROR (Status)) {
122 return Status;
123 }
124
125 ReadSize = Size;
126 Status = FileHandle->Read (FileHandle, &ReadSize, Buffer);
127 if (EFI_ERROR (Status)) {
128 goto CloseFile;
129 }
130
131 if (ReadSize != Size) {
132 Status = EFI_PROTOCOL_ERROR;
133 goto CloseFile;
134 }
135
136 Status = EFI_SUCCESS;
137 CloseFile:
138 FileHandle->Close (FileHandle);
139 return Status;
140 }
141
142 /**
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.
146
147 @param[out] ImageHandle The image handle that was allocated for
148 loading the image
149
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
156 was unloaded again.
157
158 @return Error codes from any of the underlying
159 functions.
160 **/
161 EFI_STATUS
162 EFIAPI
163 QemuLoadKernelImage (
164 OUT EFI_HANDLE *ImageHandle
165 )
166 {
167 EFI_STATUS Status;
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;
175 CHAR8 *CommandLine;
176 UINTN InitrdSize;
177
178 //
179 // Load the image. This should call back into the QEMU EFI loader file system.
180 //
181 Status = gBS->LoadImage (
182 FALSE, // BootPolicy: exact match required
183 gImageHandle, // ParentImageHandle
184 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
185 NULL, // SourceBuffer
186 0, // SourceSize
187 &KernelImageHandle
188 );
189 switch (Status) {
190 case EFI_SUCCESS:
191 break;
192
193 case EFI_SECURITY_VIOLATION:
194 //
195 // In this case, the image was loaded but failed to authenticate.
196 //
197 Status = EFI_ACCESS_DENIED;
198 goto UnloadImage;
199
200 default:
201 DEBUG ((
202 Status == EFI_NOT_FOUND ? DEBUG_INFO : DEBUG_ERROR,
203 "%a: LoadImage(): %r\n",
204 __FUNCTION__,
205 Status
206 ));
207 return Status;
208 }
209
210 //
211 // Construct the kernel command line.
212 //
213 Status = gBS->OpenProtocol (
214 KernelImageHandle,
215 &gEfiLoadedImageProtocolGuid,
216 (VOID **)&KernelLoadedImage,
217 gImageHandle, // AgentHandle
218 NULL, // ControllerHandle
219 EFI_OPEN_PROTOCOL_GET_PROTOCOL
220 );
221 ASSERT_EFI_ERROR (Status);
222
223 //
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.
226 //
227 DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL *)&mQemuKernelLoaderFsDevicePath;
228 Status = gBS->LocateDevicePath (
229 &gEfiSimpleFileSystemProtocolGuid,
230 &DevicePathNode,
231 &FsVolumeHandle
232 );
233 if (EFI_ERROR (Status)) {
234 goto UnloadImage;
235 }
236
237 Status = gBS->HandleProtocol (
238 FsVolumeHandle,
239 &gEfiSimpleFileSystemProtocolGuid,
240 (VOID **)&FsProtocol
241 );
242 if (EFI_ERROR (Status)) {
243 goto UnloadImage;
244 }
245
246 Status = FsProtocol->OpenVolume (FsVolumeHandle, &Root);
247 if (EFI_ERROR (Status)) {
248 goto UnloadImage;
249 }
250
251 Status = GetQemuKernelLoaderBlobSize (Root, L"cmdline", &CommandLineSize);
252 if (EFI_ERROR (Status)) {
253 goto CloseRoot;
254 }
255
256 if (CommandLineSize == 0) {
257 KernelLoadedImage->LoadOptionsSize = 0;
258 } else {
259 CommandLine = AllocatePool (CommandLineSize);
260 if (CommandLine == NULL) {
261 Status = EFI_OUT_OF_RESOURCES;
262 goto CloseRoot;
263 }
264
265 Status = ReadWholeQemuKernelLoaderBlob (
266 Root,
267 L"cmdline",
268 CommandLineSize,
269 CommandLine
270 );
271 if (EFI_ERROR (Status)) {
272 goto FreeCommandLine;
273 }
274
275 //
276 // Verify NUL-termination of the command line.
277 //
278 if (CommandLine[CommandLineSize - 1] != '\0') {
279 DEBUG ((
280 DEBUG_ERROR,
281 "%a: kernel command line is not NUL-terminated\n",
282 __FUNCTION__
283 ));
284 Status = EFI_PROTOCOL_ERROR;
285 goto FreeCommandLine;
286 }
287
288 //
289 // Drop the terminating NUL, convert to UTF-16.
290 //
291 KernelLoadedImage->LoadOptionsSize = (UINT32)((CommandLineSize - 1) * 2);
292 }
293
294 Status = GetQemuKernelLoaderBlobSize (Root, L"initrd", &InitrdSize);
295 if (EFI_ERROR (Status)) {
296 goto FreeCommandLine;
297 }
298
299 if (InitrdSize > 0) {
300 //
301 // Append ' initrd=initrd' in UTF-16.
302 //
303 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
304 }
305
306 if (KernelLoadedImage->LoadOptionsSize == 0) {
307 KernelLoadedImage->LoadOptions = NULL;
308 } else {
309 //
310 // NUL-terminate in UTF-16.
311 //
312 KernelLoadedImage->LoadOptionsSize += 2;
313
314 KernelLoadedImage->LoadOptions = AllocatePool (
315 KernelLoadedImage->LoadOptionsSize
316 );
317 if (KernelLoadedImage->LoadOptions == NULL) {
318 KernelLoadedImage->LoadOptionsSize = 0;
319 Status = EFI_OUT_OF_RESOURCES;
320 goto FreeCommandLine;
321 }
322
323 UnicodeSPrintAsciiFormat (
324 KernelLoadedImage->LoadOptions,
325 KernelLoadedImage->LoadOptionsSize,
326 "%a%a",
327 (CommandLineSize == 0) ? "" : CommandLine,
328 (InitrdSize == 0) ? "" : " initrd=initrd"
329 );
330 DEBUG ((
331 DEBUG_INFO,
332 "%a: command line: \"%s\"\n",
333 __FUNCTION__,
334 (CHAR16 *)KernelLoadedImage->LoadOptions
335 ));
336 }
337
338 *ImageHandle = KernelImageHandle;
339 Status = EFI_SUCCESS;
340
341 FreeCommandLine:
342 if (CommandLineSize > 0) {
343 FreePool (CommandLine);
344 }
345
346 CloseRoot:
347 Root->Close (Root);
348 UnloadImage:
349 if (EFI_ERROR (Status)) {
350 gBS->UnloadImage (KernelImageHandle);
351 }
352
353 return Status;
354 }
355
356 /**
357 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
358
359 @param[in,out] ImageHandle Handle of image to be started. May assume a
360 different value on return if the image was
361 reloaded.
362
363 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
364 or the image has already been initialized with
365 StartImage
366 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
367 image should not be started.
368
369 @return Error codes returned by the started image
370 **/
371 EFI_STATUS
372 EFIAPI
373 QemuStartKernelImage (
374 IN OUT EFI_HANDLE *ImageHandle
375 )
376 {
377 return gBS->StartImage (
378 *ImageHandle,
379 NULL, // ExitDataSize
380 NULL // ExitData
381 );
382 }
383
384 /**
385 Unloads an image loaded with QemuLoadKernelImage ().
386
387 @param ImageHandle Handle that identifies the image to be
388 unloaded.
389
390 @retval EFI_SUCCESS The image has been unloaded.
391 @retval EFI_UNSUPPORTED The image has been started, and does not
392 support unload.
393 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
394
395 @return Exit code from the image's unload function.
396 **/
397 EFI_STATUS
398 EFIAPI
399 QemuUnloadKernelImage (
400 IN EFI_HANDLE ImageHandle
401 )
402 {
403 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
404 EFI_STATUS Status;
405
406 Status = gBS->OpenProtocol (
407 ImageHandle,
408 &gEfiLoadedImageProtocolGuid,
409 (VOID **)&KernelLoadedImage,
410 gImageHandle, // AgentHandle
411 NULL, // ControllerHandle
412 EFI_OPEN_PROTOCOL_GET_PROTOCOL
413 );
414 if (EFI_ERROR (Status)) {
415 return EFI_INVALID_PARAMETER;
416 }
417
418 if (KernelLoadedImage->LoadOptions != NULL) {
419 FreePool (KernelLoadedImage->LoadOptions);
420 KernelLoadedImage->LoadOptions = NULL;
421 }
422
423 KernelLoadedImage->LoadOptionsSize = 0;
424
425 return gBS->UnloadImage (ImageHandle);
426 }