]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Library/GenericQemuLoadImageLib/GenericQemuLoadImageLib.c
NetworkPkg: 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 Status = FileHandleGetSize (FileHandle, &FileSize);
90 if (EFI_ERROR (Status)) {
91 goto CloseFile;
92 }
93 if (FileSize > MAX_UINTN) {
94 Status = EFI_UNSUPPORTED;
95 goto CloseFile;
96 }
97 *Size = (UINTN)FileSize;
98 Status = EFI_SUCCESS;
99 CloseFile:
100 FileHandle->Close (FileHandle);
101 return Status;
102 }
103
104 STATIC
105 EFI_STATUS
106 ReadWholeQemuKernelLoaderBlob (
107 IN EFI_FILE_HANDLE Root,
108 IN CHAR16 *FileName,
109 IN UINTN Size,
110 OUT VOID *Buffer
111 )
112 {
113 EFI_STATUS Status;
114 EFI_FILE_HANDLE FileHandle;
115 UINTN ReadSize;
116
117 Status = Root->Open (Root, &FileHandle, FileName, EFI_FILE_MODE_READ, 0);
118 if (EFI_ERROR (Status)) {
119 return Status;
120 }
121 ReadSize = Size;
122 Status = FileHandle->Read (FileHandle, &ReadSize, Buffer);
123 if (EFI_ERROR (Status)) {
124 goto CloseFile;
125 }
126 if (ReadSize != Size) {
127 Status = EFI_PROTOCOL_ERROR;
128 goto CloseFile;
129 }
130 Status = EFI_SUCCESS;
131 CloseFile:
132 FileHandle->Close (FileHandle);
133 return Status;
134 }
135
136 /**
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.
140
141 @param[out] ImageHandle The image handle that was allocated for
142 loading the image
143
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
150 was unloaded again.
151
152 @return Error codes from any of the underlying
153 functions.
154 **/
155 EFI_STATUS
156 EFIAPI
157 QemuLoadKernelImage (
158 OUT EFI_HANDLE *ImageHandle
159 )
160 {
161 EFI_STATUS Status;
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;
169 CHAR8 *CommandLine;
170 UINTN InitrdSize;
171
172 //
173 // Load the image. This should call back into the QEMU EFI loader file system.
174 //
175 Status = gBS->LoadImage (
176 FALSE, // BootPolicy: exact match required
177 gImageHandle, // ParentImageHandle
178 (EFI_DEVICE_PATH_PROTOCOL *)&mKernelDevicePath,
179 NULL, // SourceBuffer
180 0, // SourceSize
181 &KernelImageHandle
182 );
183 switch (Status) {
184 case EFI_SUCCESS:
185 break;
186
187 case EFI_SECURITY_VIOLATION:
188 //
189 // In this case, the image was loaded but failed to authenticate.
190 //
191 Status = EFI_ACCESS_DENIED;
192 goto UnloadImage;
193
194 default:
195 DEBUG ((Status == EFI_NOT_FOUND ? DEBUG_INFO : DEBUG_ERROR,
196 "%a: LoadImage(): %r\n", __FUNCTION__, Status));
197 return Status;
198 }
199
200 //
201 // Construct the kernel command line.
202 //
203 Status = gBS->OpenProtocol (
204 KernelImageHandle,
205 &gEfiLoadedImageProtocolGuid,
206 (VOID **)&KernelLoadedImage,
207 gImageHandle, // AgentHandle
208 NULL, // ControllerHandle
209 EFI_OPEN_PROTOCOL_GET_PROTOCOL
210 );
211 ASSERT_EFI_ERROR (Status);
212
213 //
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.
216 //
217 DevicePathNode = (EFI_DEVICE_PATH_PROTOCOL *)&mQemuKernelLoaderFsDevicePath;
218 Status = gBS->LocateDevicePath (
219 &gEfiSimpleFileSystemProtocolGuid,
220 &DevicePathNode,
221 &FsVolumeHandle
222 );
223 if (EFI_ERROR (Status)) {
224 goto UnloadImage;
225 }
226
227 Status = gBS->HandleProtocol (
228 FsVolumeHandle,
229 &gEfiSimpleFileSystemProtocolGuid,
230 (VOID **)&FsProtocol
231 );
232 if (EFI_ERROR (Status)) {
233 goto UnloadImage;
234 }
235
236 Status = FsProtocol->OpenVolume (FsVolumeHandle, &Root);
237 if (EFI_ERROR (Status)) {
238 goto UnloadImage;
239 }
240
241 Status = GetQemuKernelLoaderBlobSize (Root, L"cmdline", &CommandLineSize);
242 if (EFI_ERROR (Status)) {
243 goto CloseRoot;
244 }
245
246 if (CommandLineSize == 0) {
247 KernelLoadedImage->LoadOptionsSize = 0;
248 } else {
249 CommandLine = AllocatePool (CommandLineSize);
250 if (CommandLine == NULL) {
251 Status = EFI_OUT_OF_RESOURCES;
252 goto CloseRoot;
253 }
254
255 Status = ReadWholeQemuKernelLoaderBlob (Root, L"cmdline", CommandLineSize,
256 CommandLine);
257 if (EFI_ERROR (Status)) {
258 goto FreeCommandLine;
259 }
260
261 //
262 // Verify NUL-termination of the command line.
263 //
264 if (CommandLine[CommandLineSize - 1] != '\0') {
265 DEBUG ((DEBUG_ERROR, "%a: kernel command line is not NUL-terminated\n",
266 __FUNCTION__));
267 Status = EFI_PROTOCOL_ERROR;
268 goto FreeCommandLine;
269 }
270
271 //
272 // Drop the terminating NUL, convert to UTF-16.
273 //
274 KernelLoadedImage->LoadOptionsSize = (UINT32)((CommandLineSize - 1) * 2);
275 }
276
277 Status = GetQemuKernelLoaderBlobSize (Root, L"initrd", &InitrdSize);
278 if (EFI_ERROR (Status)) {
279 goto FreeCommandLine;
280 }
281
282 if (InitrdSize > 0) {
283 //
284 // Append ' initrd=initrd' in UTF-16.
285 //
286 KernelLoadedImage->LoadOptionsSize += sizeof (L" initrd=initrd") - 2;
287 }
288
289 if (KernelLoadedImage->LoadOptionsSize == 0) {
290 KernelLoadedImage->LoadOptions = NULL;
291 } else {
292 //
293 // NUL-terminate in UTF-16.
294 //
295 KernelLoadedImage->LoadOptionsSize += 2;
296
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;
303 }
304
305 UnicodeSPrintAsciiFormat (
306 KernelLoadedImage->LoadOptions,
307 KernelLoadedImage->LoadOptionsSize,
308 "%a%a",
309 (CommandLineSize == 0) ? "" : CommandLine,
310 (InitrdSize == 0) ? "" : " initrd=initrd"
311 );
312 DEBUG ((DEBUG_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
313 (CHAR16 *)KernelLoadedImage->LoadOptions));
314 }
315
316 *ImageHandle = KernelImageHandle;
317 Status = EFI_SUCCESS;
318
319 FreeCommandLine:
320 if (CommandLineSize > 0) {
321 FreePool (CommandLine);
322 }
323 CloseRoot:
324 Root->Close (Root);
325 UnloadImage:
326 if (EFI_ERROR (Status)) {
327 gBS->UnloadImage (KernelImageHandle);
328 }
329
330 return Status;
331 }
332
333 /**
334 Transfer control to a kernel image loaded with QemuLoadKernelImage ()
335
336 @param[in,out] ImageHandle Handle of image to be started. May assume a
337 different value on return if the image was
338 reloaded.
339
340 @retval EFI_INVALID_PARAMETER ImageHandle is either an invalid image handle
341 or the image has already been initialized with
342 StartImage
343 @retval EFI_SECURITY_VIOLATION The current platform policy specifies that the
344 image should not be started.
345
346 @return Error codes returned by the started image
347 **/
348 EFI_STATUS
349 EFIAPI
350 QemuStartKernelImage (
351 IN OUT EFI_HANDLE *ImageHandle
352 )
353 {
354 return gBS->StartImage (
355 *ImageHandle,
356 NULL, // ExitDataSize
357 NULL // ExitData
358 );
359 }
360
361 /**
362 Unloads an image loaded with QemuLoadKernelImage ().
363
364 @param ImageHandle Handle that identifies the image to be
365 unloaded.
366
367 @retval EFI_SUCCESS The image has been unloaded.
368 @retval EFI_UNSUPPORTED The image has been started, and does not
369 support unload.
370 @retval EFI_INVALID_PARAMETER ImageHandle is not a valid image handle.
371
372 @return Exit code from the image's unload function.
373 **/
374 EFI_STATUS
375 EFIAPI
376 QemuUnloadKernelImage (
377 IN EFI_HANDLE ImageHandle
378 )
379 {
380 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
381 EFI_STATUS Status;
382
383 Status = gBS->OpenProtocol (
384 ImageHandle,
385 &gEfiLoadedImageProtocolGuid,
386 (VOID **)&KernelLoadedImage,
387 gImageHandle, // AgentHandle
388 NULL, // ControllerHandle
389 EFI_OPEN_PROTOCOL_GET_PROTOCOL
390 );
391 if (EFI_ERROR (Status)) {
392 return EFI_INVALID_PARAMETER;
393 }
394
395 if (KernelLoadedImage->LoadOptions != NULL) {
396 FreePool (KernelLoadedImage->LoadOptions);
397 KernelLoadedImage->LoadOptions = NULL;
398 }
399 KernelLoadedImage->LoadOptionsSize = 0;
400
401 return gBS->UnloadImage (ImageHandle);
402 }