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