]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/PlatformBootManagerLib/QemuKernel.c
ArmVirtPkg/PlatformBootManagerLib: add missing report status code call
[mirror_edk2.git] / ArmVirtPkg / Library / PlatformBootManagerLib / QemuKernel.c
CommitLineData
8d620322
LE
1/** @file\r
2 Try to load an EFI-stubbed ARM Linux kernel from QEMU's fw_cfg.\r
3\r
4 This implementation differs from OvmfPkg/Library/LoadLinuxLib. An EFI\r
5 stub in the subject kernel is a hard requirement here.\r
6\r
7 Copyright (C) 2014-2016, Red Hat, Inc.\r
8\r
9 This program and the accompanying materials are licensed and made available\r
10 under the terms and conditions of the BSD License which accompanies this\r
11 distribution. The full text of the license may be found at\r
12 http://opensource.org/licenses/bsd-license.php\r
13\r
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
15 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
16**/\r
17\r
18#include <Guid/FileInfo.h>\r
19#include <Guid/FileSystemInfo.h>\r
20#include <Guid/FileSystemVolumeLabelInfo.h>\r
21#include <Library/PrintLib.h>\r
22#include <Library/QemuFwCfgLib.h>\r
97399f2d 23#include <Library/ReportStatusCodeLib.h>\r
8d620322
LE
24#include <Protocol/DevicePath.h>\r
25#include <Protocol/LoadedImage.h>\r
26#include <Protocol/SimpleFileSystem.h>\r
27\r
28#include "PlatformBm.h"\r
29\r
30//\r
31// Static data that hosts the fw_cfg blobs and serves file requests.\r
32//\r
33typedef enum {\r
34 KernelBlobTypeKernel,\r
35 KernelBlobTypeInitrd,\r
36 KernelBlobTypeCommandLine,\r
37 KernelBlobTypeMax\r
38} KERNEL_BLOB_TYPE;\r
39\r
40typedef struct {\r
41 FIRMWARE_CONFIG_ITEM CONST SizeKey;\r
42 FIRMWARE_CONFIG_ITEM CONST DataKey;\r
43 CONST CHAR16 * CONST Name;\r
44 UINT32 Size;\r
45 UINT8 *Data;\r
46} KERNEL_BLOB;\r
47\r
48STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {\r
49 { QemuFwCfgItemKernelSize, QemuFwCfgItemKernelData, L"kernel" },\r
50 { QemuFwCfgItemInitrdSize, QemuFwCfgItemInitrdData, L"initrd" },\r
51 { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }\r
52};\r
53\r
54STATIC UINT64 mTotalBlobBytes;\r
55\r
56//\r
57// Device path for the handle that incorporates our "EFI stub filesystem". The\r
58// GUID is arbitrary and need not be standardized or advertized.\r
59//\r
60#pragma pack(1)\r
61typedef struct {\r
62 VENDOR_DEVICE_PATH VenHwNode;\r
63 EFI_DEVICE_PATH_PROTOCOL EndNode;\r
64} SINGLE_VENHW_NODE_DEVPATH;\r
65#pragma pack()\r
66\r
67STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = {\r
68 {\r
69 { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } },\r
70 {\r
71 0xb0fae7e7, 0x6b07, 0x49d0,\r
72 { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d }\r
73 }\r
74 },\r
75\r
76 {\r
77 END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,\r
78 { sizeof (EFI_DEVICE_PATH_PROTOCOL) }\r
79 }\r
80};\r
81\r
82//\r
83// The "file in the EFI stub filesystem" abstraction.\r
84//\r
85STATIC EFI_TIME mInitTime;\r
86\r
87#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')\r
88\r
89typedef struct {\r
90 UINT64 Signature; // Carries STUB_FILE_SIG.\r
91\r
92 KERNEL_BLOB_TYPE BlobType; // Index into mKernelBlob. KernelBlobTypeMax\r
93 // denotes the root directory of the filesystem.\r
94\r
95 UINT64 Position; // Byte position for regular files;\r
96 // next directory entry to return for the root\r
97 // directory.\r
98\r
99 EFI_FILE_PROTOCOL File; // Standard protocol interface.\r
100} STUB_FILE;\r
101\r
102#define STUB_FILE_FROM_FILE(FilePointer) \\r
103 CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG)\r
104\r
105//\r
106// Tentative definition of the file protocol template. The initializer\r
107// (external definition) will be provided later.\r
108//\r
109STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate;\r
110\r
111\r
112//\r
113// Protocol member functions for File.\r
114//\r
115\r
116/**\r
117 Opens a new file relative to the source file's location.\r
118\r
119 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is\r
120 the file handle to the source location. This would\r
121 typically be an open handle to a directory.\r
122\r
123 @param[out] NewHandle A pointer to the location to return the opened handle\r
124 for the new file.\r
125\r
126 @param[in] FileName The Null-terminated string of the name of the file to\r
127 be opened. The file name may contain the following\r
128 path modifiers: "\", ".", and "..".\r
129\r
130 @param[in] OpenMode The mode to open the file. The only valid\r
131 combinations that the file may be opened with are:\r
132 Read, Read/Write, or Create/Read/Write.\r
133\r
134 @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case\r
135 these are the attribute bits for the newly created\r
136 file.\r
137\r
138 @retval EFI_SUCCESS The file was opened.\r
139 @retval EFI_NOT_FOUND The specified file could not be found on the\r
140 device.\r
141 @retval EFI_NO_MEDIA The device has no medium.\r
142 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the\r
143 medium is no longer supported.\r
144 @retval EFI_DEVICE_ERROR The device reported an error.\r
145 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
146 @retval EFI_WRITE_PROTECTED An attempt was made to create a file, or open a\r
147 file for write when the media is\r
148 write-protected.\r
149 @retval EFI_ACCESS_DENIED The service denied access to the file.\r
150 @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the\r
151 file.\r
152 @retval EFI_VOLUME_FULL The volume is full.\r
153**/\r
154STATIC\r
155EFI_STATUS\r
156EFIAPI\r
157StubFileOpen (\r
158 IN EFI_FILE_PROTOCOL *This,\r
159 OUT EFI_FILE_PROTOCOL **NewHandle,\r
160 IN CHAR16 *FileName,\r
161 IN UINT64 OpenMode,\r
162 IN UINT64 Attributes\r
163 )\r
164{\r
165 CONST STUB_FILE *StubFile;\r
166 UINTN BlobType;\r
167 STUB_FILE *NewStubFile;\r
168\r
169 //\r
170 // We're read-only.\r
171 //\r
172 switch (OpenMode) {\r
173 case EFI_FILE_MODE_READ:\r
174 break;\r
175\r
176 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:\r
177 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:\r
178 return EFI_WRITE_PROTECTED;\r
179\r
180 default:\r
181 return EFI_INVALID_PARAMETER;\r
182 }\r
183\r
184 //\r
185 // Only the root directory supports opening files in it.\r
186 //\r
187 StubFile = STUB_FILE_FROM_FILE (This);\r
188 if (StubFile->BlobType != KernelBlobTypeMax) {\r
189 return EFI_UNSUPPORTED;\r
190 }\r
191\r
192 //\r
193 // Locate the file.\r
194 //\r
195 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {\r
196 if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {\r
197 break;\r
198 }\r
199 }\r
200 if (BlobType == KernelBlobTypeMax) {\r
201 return EFI_NOT_FOUND;\r
202 }\r
203\r
204 //\r
205 // Found it.\r
206 //\r
207 NewStubFile = AllocatePool (sizeof *NewStubFile);\r
208 if (NewStubFile == NULL) {\r
209 return EFI_OUT_OF_RESOURCES;\r
210 }\r
211\r
212 NewStubFile->Signature = STUB_FILE_SIG;\r
213 NewStubFile->BlobType = (KERNEL_BLOB_TYPE)BlobType;\r
214 NewStubFile->Position = 0;\r
215 CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate,\r
216 sizeof mEfiFileProtocolTemplate);\r
217 *NewHandle = &NewStubFile->File;\r
218\r
219 return EFI_SUCCESS;\r
220}\r
221\r
222\r
223/**\r
224 Closes a specified file handle.\r
225\r
226 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file\r
227 handle to close.\r
228\r
229 @retval EFI_SUCCESS The file was closed.\r
230**/\r
231STATIC\r
232EFI_STATUS\r
233EFIAPI\r
234StubFileClose (\r
235 IN EFI_FILE_PROTOCOL *This\r
236 )\r
237{\r
238 FreePool (STUB_FILE_FROM_FILE (This));\r
239 return EFI_SUCCESS;\r
240}\r
241\r
242\r
243/**\r
244 Close and delete the file handle.\r
245\r
246 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
247 handle to the file to delete.\r
248\r
249 @retval EFI_SUCCESS The file was closed and deleted, and the\r
250 handle was closed.\r
251 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not\r
252 deleted.\r
253\r
254**/\r
255STATIC\r
256EFI_STATUS\r
257EFIAPI\r
258StubFileDelete (\r
259 IN EFI_FILE_PROTOCOL *This\r
260 )\r
261{\r
262 FreePool (STUB_FILE_FROM_FILE (This));\r
263 return EFI_WARN_DELETE_FAILURE;\r
264}\r
265\r
266\r
267/**\r
268 Helper function that formats an EFI_FILE_INFO structure into the\r
269 user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including\r
270 KernelBlobTypeMax, which stands for the root directory).\r
271\r
272 The interface follows the EFI_FILE_GET_INFO -- and for directories, the\r
273 EFI_FILE_READ -- interfaces.\r
274\r
275 @param[in] BlobType The KERNEL_BLOB_TYPE value identifying the fw_cfg\r
276 blob backing the STUB_FILE that information is\r
277 being requested about. If BlobType equals\r
278 KernelBlobTypeMax, then information will be\r
279 provided about the root directory of the\r
280 filesystem.\r
281\r
282 @param[in,out] BufferSize On input, the size of Buffer. On output, the\r
283 amount of data returned in Buffer. In both cases,\r
284 the size is measured in bytes.\r
285\r
286 @param[out] Buffer A pointer to the data buffer to return. The\r
287 buffer's type is EFI_FILE_INFO.\r
288\r
289 @retval EFI_SUCCESS The information was returned.\r
290 @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the\r
291 EFI_FILE_INFO structure. BufferSize has been\r
292 updated with the size needed to complete the\r
293 request.\r
294**/\r
295STATIC\r
296EFI_STATUS\r
297ConvertKernelBlobTypeToFileInfo (\r
298 IN KERNEL_BLOB_TYPE BlobType,\r
299 IN OUT UINTN *BufferSize,\r
300 OUT VOID *Buffer\r
301 )\r
302{\r
303 CONST CHAR16 *Name;\r
304 UINT64 FileSize;\r
305 UINT64 Attribute;\r
306\r
307 UINTN NameSize;\r
308 UINTN FileInfoSize;\r
309 EFI_FILE_INFO *FileInfo;\r
310 UINTN OriginalBufferSize;\r
311\r
312 if (BlobType == KernelBlobTypeMax) {\r
313 //\r
314 // getting file info about the root directory\r
315 //\r
316 Name = L"\\";\r
317 FileSize = KernelBlobTypeMax;\r
318 Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;\r
319 } else {\r
320 CONST KERNEL_BLOB *Blob;\r
321\r
322 Blob = &mKernelBlob[BlobType];\r
323 Name = Blob->Name;\r
324 FileSize = Blob->Size;\r
325 Attribute = EFI_FILE_READ_ONLY;\r
326 }\r
327\r
328 NameSize = (StrLen(Name) + 1) * 2;\r
329 FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;\r
330 ASSERT (FileInfoSize >= sizeof *FileInfo);\r
331\r
332 OriginalBufferSize = *BufferSize;\r
333 *BufferSize = FileInfoSize;\r
334 if (OriginalBufferSize < *BufferSize) {\r
335 return EFI_BUFFER_TOO_SMALL;\r
336 }\r
337\r
338 FileInfo = (EFI_FILE_INFO *)Buffer;\r
339 FileInfo->Size = FileInfoSize;\r
340 FileInfo->FileSize = FileSize;\r
341 FileInfo->PhysicalSize = FileSize;\r
342 FileInfo->Attribute = Attribute;\r
343\r
344 CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime);\r
345 CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime);\r
346 CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);\r
347 CopyMem (FileInfo->FileName, Name, NameSize);\r
348\r
349 return EFI_SUCCESS;\r
350}\r
351\r
352\r
353/**\r
354 Reads data from a file, or continues scanning a directory.\r
355\r
356 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that\r
357 is the file handle to read data from.\r
358\r
359 @param[in,out] BufferSize On input, the size of the Buffer. On output, the\r
360 amount of data returned in Buffer. In both cases,\r
361 the size is measured in bytes. If the read goes\r
362 beyond the end of the file, the read length is\r
363 truncated to the end of the file.\r
364\r
365 If This is a directory, the function reads the\r
366 directory entry at the current position and\r
367 returns the entry (as EFI_FILE_INFO) in Buffer. If\r
368 there are no more directory entries, the\r
369 BufferSize is set to zero on output.\r
370\r
371 @param[out] Buffer The buffer into which the data is read.\r
372\r
373 @retval EFI_SUCCESS Data was read.\r
374 @retval EFI_NO_MEDIA The device has no medium.\r
375 @retval EFI_DEVICE_ERROR The device reported an error.\r
376 @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted\r
377 file.\r
378 @retval EFI_DEVICE_ERROR On entry, the current file position is beyond\r
379 the end of the file.\r
380 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
381 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the\r
382 current directory entry as a EFI_FILE_INFO\r
383 structure. BufferSize has been updated with the\r
384 size needed to complete the request, and the\r
385 directory position has not been advanced.\r
386**/\r
387STATIC\r
388EFI_STATUS\r
389EFIAPI\r
390StubFileRead (\r
391 IN EFI_FILE_PROTOCOL *This,\r
392 IN OUT UINTN *BufferSize,\r
393 OUT VOID *Buffer\r
394 )\r
395{\r
396 STUB_FILE *StubFile;\r
397 CONST KERNEL_BLOB *Blob;\r
398 UINT64 Left;\r
399\r
400 StubFile = STUB_FILE_FROM_FILE (This);\r
401\r
402 //\r
403 // Scanning the root directory?\r
404 //\r
405 if (StubFile->BlobType == KernelBlobTypeMax) {\r
406 EFI_STATUS Status;\r
407\r
408 if (StubFile->Position == KernelBlobTypeMax) {\r
409 //\r
410 // Scanning complete.\r
411 //\r
412 *BufferSize = 0;\r
413 return EFI_SUCCESS;\r
414 }\r
415\r
416 Status = ConvertKernelBlobTypeToFileInfo (\r
417 (KERNEL_BLOB_TYPE)StubFile->Position,\r
418 BufferSize,\r
419 Buffer);\r
420 if (EFI_ERROR (Status)) {\r
421 return Status;\r
422 }\r
423\r
424 ++StubFile->Position;\r
425 return EFI_SUCCESS;\r
426 }\r
427\r
428 //\r
429 // Reading a file.\r
430 //\r
431 Blob = &mKernelBlob[StubFile->BlobType];\r
432 if (StubFile->Position > Blob->Size) {\r
433 return EFI_DEVICE_ERROR;\r
434 }\r
435\r
436 Left = Blob->Size - StubFile->Position;\r
437 if (*BufferSize > Left) {\r
438 *BufferSize = (UINTN)Left;\r
439 }\r
440 if (Blob->Data != NULL) {\r
441 CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);\r
442 }\r
443 StubFile->Position += *BufferSize;\r
444 return EFI_SUCCESS;\r
445}\r
446\r
447\r
448/**\r
449 Writes data to a file.\r
450\r
451 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that\r
452 is the file handle to write data to.\r
453\r
454 @param[in,out] BufferSize On input, the size of the Buffer. On output, the\r
455 amount of data actually written. In both cases,\r
456 the size is measured in bytes.\r
457\r
458 @param[in] Buffer The buffer of data to write.\r
459\r
460 @retval EFI_SUCCESS Data was written.\r
461 @retval EFI_UNSUPPORTED Writes to open directory files are not\r
462 supported.\r
463 @retval EFI_NO_MEDIA The device has no medium.\r
464 @retval EFI_DEVICE_ERROR The device reported an error.\r
465 @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file.\r
466 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
467 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.\r
468 @retval EFI_ACCESS_DENIED The file was opened read only.\r
469 @retval EFI_VOLUME_FULL The volume is full.\r
470**/\r
471STATIC\r
472EFI_STATUS\r
473EFIAPI\r
474StubFileWrite (\r
475 IN EFI_FILE_PROTOCOL *This,\r
476 IN OUT UINTN *BufferSize,\r
477 IN VOID *Buffer\r
478 )\r
479{\r
480 STUB_FILE *StubFile;\r
481\r
482 StubFile = STUB_FILE_FROM_FILE (This);\r
483 return (StubFile->BlobType == KernelBlobTypeMax) ?\r
484 EFI_UNSUPPORTED :\r
485 EFI_WRITE_PROTECTED;\r
486}\r
487\r
488\r
489/**\r
490 Returns a file's current position.\r
491\r
492 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
493 file handle to get the current position on.\r
494\r
495 @param[out] Position The address to return the file's current position\r
496 value.\r
497\r
498 @retval EFI_SUCCESS The position was returned.\r
499 @retval EFI_UNSUPPORTED The request is not valid on open directories.\r
500 @retval EFI_DEVICE_ERROR An attempt was made to get the position from a\r
501 deleted file.\r
502**/\r
503STATIC\r
504EFI_STATUS\r
505EFIAPI\r
506StubFileGetPosition (\r
507 IN EFI_FILE_PROTOCOL *This,\r
508 OUT UINT64 *Position\r
509 )\r
510{\r
511 STUB_FILE *StubFile;\r
512\r
513 StubFile = STUB_FILE_FROM_FILE (This);\r
514 if (StubFile->BlobType == KernelBlobTypeMax) {\r
515 return EFI_UNSUPPORTED;\r
516 }\r
517\r
518 *Position = StubFile->Position;\r
519 return EFI_SUCCESS;\r
520}\r
521\r
522\r
523/**\r
524 Sets a file's current position.\r
525\r
526 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
527 file handle to set the requested position on.\r
528\r
529 @param[in] Position The byte position from the start of the file to set. For\r
530 regular files, MAX_UINT64 means "seek to end". For\r
531 directories, zero means "rewind directory scan".\r
532\r
533 @retval EFI_SUCCESS The position was set.\r
534 @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open\r
535 directories.\r
536 @retval EFI_DEVICE_ERROR An attempt was made to set the position of a\r
537 deleted file.\r
538**/\r
539STATIC\r
540EFI_STATUS\r
541EFIAPI\r
542StubFileSetPosition (\r
543 IN EFI_FILE_PROTOCOL *This,\r
544 IN UINT64 Position\r
545 )\r
546{\r
547 STUB_FILE *StubFile;\r
548 KERNEL_BLOB *Blob;\r
549\r
550 StubFile = STUB_FILE_FROM_FILE (This);\r
551\r
552 if (StubFile->BlobType == KernelBlobTypeMax) {\r
553 if (Position == 0) {\r
554 //\r
555 // rewinding a directory scan is allowed\r
556 //\r
557 StubFile->Position = 0;\r
558 return EFI_SUCCESS;\r
559 }\r
560 return EFI_UNSUPPORTED;\r
561 }\r
562\r
563 //\r
564 // regular file seek\r
565 //\r
566 Blob = &mKernelBlob[StubFile->BlobType];\r
567 if (Position == MAX_UINT64) {\r
568 //\r
569 // seek to end\r
570 //\r
571 StubFile->Position = Blob->Size;\r
572 } else {\r
573 //\r
574 // absolute seek from beginning -- seeking past the end is allowed\r
575 //\r
576 StubFile->Position = Position;\r
577 }\r
578 return EFI_SUCCESS;\r
579}\r
580\r
581\r
582/**\r
583 Returns information about a file.\r
584\r
585 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance\r
586 that is the file handle the requested\r
587 information is for.\r
588\r
589 @param[in] InformationType The type identifier GUID for the information\r
590 being requested. The following information\r
591 types are supported, storing the\r
592 corresponding structures in Buffer:\r
593\r
594 - gEfiFileInfoGuid: EFI_FILE_INFO\r
595\r
596 - gEfiFileSystemInfoGuid:\r
597 EFI_FILE_SYSTEM_INFO\r
598\r
599 - gEfiFileSystemVolumeLabelInfoIdGuid:\r
600 EFI_FILE_SYSTEM_VOLUME_LABEL\r
601\r
602 @param[in,out] BufferSize On input, the size of Buffer. On output, the\r
603 amount of data returned in Buffer. In both\r
604 cases, the size is measured in bytes.\r
605\r
606 @param[out] Buffer A pointer to the data buffer to return. The\r
607 buffer's type is indicated by\r
608 InformationType.\r
609\r
610 @retval EFI_SUCCESS The information was returned.\r
611 @retval EFI_UNSUPPORTED The InformationType is not known.\r
612 @retval EFI_NO_MEDIA The device has no medium.\r
613 @retval EFI_DEVICE_ERROR The device reported an error.\r
614 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
615 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the\r
616 information structure requested by\r
617 InformationType. BufferSize has been updated\r
618 with the size needed to complete the request.\r
619**/\r
620STATIC\r
621EFI_STATUS\r
622EFIAPI\r
623StubFileGetInfo (\r
624 IN EFI_FILE_PROTOCOL *This,\r
625 IN EFI_GUID *InformationType,\r
626 IN OUT UINTN *BufferSize,\r
627 OUT VOID *Buffer\r
628 )\r
629{\r
630 CONST STUB_FILE *StubFile;\r
631 UINTN OriginalBufferSize;\r
632\r
633 StubFile = STUB_FILE_FROM_FILE (This);\r
634\r
635 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {\r
636 return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,\r
637 Buffer);\r
638 }\r
639\r
640 OriginalBufferSize = *BufferSize;\r
641\r
642 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {\r
643 EFI_FILE_SYSTEM_INFO *FileSystemInfo;\r
644\r
645 *BufferSize = sizeof *FileSystemInfo;\r
646 if (OriginalBufferSize < *BufferSize) {\r
647 return EFI_BUFFER_TOO_SMALL;\r
648 }\r
649\r
650 FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;\r
651 FileSystemInfo->Size = sizeof *FileSystemInfo;\r
652 FileSystemInfo->ReadOnly = TRUE;\r
653 FileSystemInfo->VolumeSize = mTotalBlobBytes;\r
654 FileSystemInfo->FreeSpace = 0;\r
655 FileSystemInfo->BlockSize = 1;\r
656 FileSystemInfo->VolumeLabel[0] = L'\0';\r
657\r
658 return EFI_SUCCESS;\r
659 }\r
660\r
661 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {\r
662 EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;\r
663\r
664 *BufferSize = sizeof *FileSystemVolumeLabel;\r
665 if (OriginalBufferSize < *BufferSize) {\r
666 return EFI_BUFFER_TOO_SMALL;\r
667 }\r
668\r
669 FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;\r
670 FileSystemVolumeLabel->VolumeLabel[0] = L'\0';\r
671\r
672 return EFI_SUCCESS;\r
673 }\r
674\r
675 return EFI_UNSUPPORTED;\r
676}\r
677\r
678\r
679/**\r
680 Sets information about a file.\r
681\r
682 @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that\r
683 is the file handle the information is for.\r
684\r
685 @param[in] InformationType The type identifier for the information being\r
686 set.\r
687\r
688 @param[in] BufferSize The size, in bytes, of Buffer.\r
689\r
690 @param[in] Buffer A pointer to the data buffer to write. The\r
691 buffer's type is indicated by InformationType.\r
692\r
693 @retval EFI_SUCCESS The information was set.\r
694 @retval EFI_UNSUPPORTED The InformationType is not known.\r
695 @retval EFI_NO_MEDIA The device has no medium.\r
696 @retval EFI_DEVICE_ERROR The device reported an error.\r
697 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
698 @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the\r
699 media is read-only.\r
700 @retval EFI_WRITE_PROTECTED InformationType is\r
701 EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media\r
702 is read only.\r
703 @retval EFI_WRITE_PROTECTED InformationType is\r
704 EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media\r
705 is read-only.\r
706 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file\r
707 to a file that is already present.\r
708 @retval EFI_ACCESS_DENIED An attempt is being made to change the\r
709 EFI_FILE_DIRECTORY Attribute.\r
710 @retval EFI_ACCESS_DENIED An attempt is being made to change the size of\r
711 a directory.\r
712 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the\r
713 file was opened read-only and an attempt is\r
714 being made to modify a field other than\r
715 Attribute.\r
716 @retval EFI_VOLUME_FULL The volume is full.\r
717 @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type\r
718 indicated by InformationType.\r
719**/\r
720STATIC\r
721EFI_STATUS\r
722EFIAPI\r
723StubFileSetInfo (\r
724 IN EFI_FILE_PROTOCOL *This,\r
725 IN EFI_GUID *InformationType,\r
726 IN UINTN BufferSize,\r
727 IN VOID *Buffer\r
728 )\r
729{\r
730 return EFI_WRITE_PROTECTED;\r
731}\r
732\r
733\r
734/**\r
735 Flushes all modified data associated with a file to a device.\r
736\r
737 @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
738 file handle to flush.\r
739\r
740 @retval EFI_SUCCESS The data was flushed.\r
741 @retval EFI_NO_MEDIA The device has no medium.\r
742 @retval EFI_DEVICE_ERROR The device reported an error.\r
743 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
744 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.\r
745 @retval EFI_ACCESS_DENIED The file was opened read-only.\r
746 @retval EFI_VOLUME_FULL The volume is full.\r
747**/\r
748STATIC\r
749EFI_STATUS\r
750EFIAPI\r
751StubFileFlush (\r
752 IN EFI_FILE_PROTOCOL *This\r
753 )\r
754{\r
755 return EFI_WRITE_PROTECTED;\r
756}\r
757\r
758//\r
759// External definition of the file protocol template.\r
760//\r
761STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {\r
762 EFI_FILE_PROTOCOL_REVISION, // revision 1\r
763 StubFileOpen,\r
764 StubFileClose,\r
765 StubFileDelete,\r
766 StubFileRead,\r
767 StubFileWrite,\r
768 StubFileGetPosition,\r
769 StubFileSetPosition,\r
770 StubFileGetInfo,\r
771 StubFileSetInfo,\r
772 StubFileFlush,\r
773 NULL, // OpenEx, revision 2\r
774 NULL, // ReadEx, revision 2\r
775 NULL, // WriteEx, revision 2\r
776 NULL // FlushEx, revision 2\r
777};\r
778\r
779\r
780//\r
781// Protocol member functions for SimpleFileSystem.\r
782//\r
783\r
784/**\r
785 Open the root directory on a volume.\r
786\r
787 @param[in] This A pointer to the volume to open the root directory on.\r
788\r
789 @param[out] Root A pointer to the location to return the opened file handle\r
790 for the root directory in.\r
791\r
792 @retval EFI_SUCCESS The device was opened.\r
793 @retval EFI_UNSUPPORTED This volume does not support the requested file\r
794 system type.\r
795 @retval EFI_NO_MEDIA The device has no medium.\r
796 @retval EFI_DEVICE_ERROR The device reported an error.\r
797 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
798 @retval EFI_ACCESS_DENIED The service denied access to the file.\r
799 @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of\r
800 resources.\r
801 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the\r
802 medium is no longer supported. Any existing\r
803 file handles for this volume are no longer\r
804 valid. To access the files on the new medium,\r
805 the volume must be reopened with OpenVolume().\r
806**/\r
807STATIC\r
808EFI_STATUS\r
809EFIAPI\r
810StubFileSystemOpenVolume (\r
811 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,\r
812 OUT EFI_FILE_PROTOCOL **Root\r
813 )\r
814{\r
815 STUB_FILE *StubFile;\r
816\r
817 StubFile = AllocatePool (sizeof *StubFile);\r
818 if (StubFile == NULL) {\r
819 return EFI_OUT_OF_RESOURCES;\r
820 }\r
821\r
822 StubFile->Signature = STUB_FILE_SIG;\r
823 StubFile->BlobType = KernelBlobTypeMax;\r
824 StubFile->Position = 0;\r
825 CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,\r
826 sizeof mEfiFileProtocolTemplate);\r
827 *Root = &StubFile->File;\r
828\r
829 return EFI_SUCCESS;\r
830}\r
831\r
832STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {\r
833 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,\r
834 StubFileSystemOpenVolume\r
835};\r
836\r
837\r
838//\r
839// Utility functions.\r
840//\r
841\r
842/**\r
843 Populate a blob in mKernelBlob.\r
844\r
845 param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is\r
846 to be filled from fw_cfg.\r
847\r
848 @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a\r
849 size of zero for the blob, then Blob->Data has\r
850 been left unchanged.\r
851\r
852 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data.\r
853**/\r
854STATIC\r
855EFI_STATUS\r
856FetchBlob (\r
857 IN OUT KERNEL_BLOB *Blob\r
858 )\r
859{\r
860 UINT32 Left;\r
861\r
862 //\r
863 // Read blob size.\r
864 //\r
865 QemuFwCfgSelectItem (Blob->SizeKey);\r
866 Blob->Size = QemuFwCfgRead32 ();\r
867 if (Blob->Size == 0) {\r
868 return EFI_SUCCESS;\r
869 }\r
870\r
871 //\r
872 // Read blob.\r
873 //\r
874 Blob->Data = AllocatePool (Blob->Size);\r
875 if (Blob->Data == NULL) {\r
876 DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",\r
877 __FUNCTION__, (INT64)Blob->Size, Blob->Name));\r
878 return EFI_OUT_OF_RESOURCES;\r
879 }\r
880\r
881 DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,\r
882 (INT64)Blob->Size, Blob->Name));\r
883 QemuFwCfgSelectItem (Blob->DataKey);\r
884\r
885 Left = Blob->Size;\r
886 do {\r
887 UINT32 Chunk;\r
888\r
889 Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;\r
890 QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));\r
891 Left -= Chunk;\r
892 DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",\r
893 __FUNCTION__, (INT64)Left, Blob->Name));\r
894 } while (Left > 0);\r
895 return EFI_SUCCESS;\r
896}\r
897\r
898\r
899//\r
900// The entry point of the feature.\r
901//\r
902\r
903/**\r
904 Download the kernel, the initial ramdisk, and the kernel command line from\r
905 QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two\r
906 image files, and load and start the kernel from it.\r
907\r
908 The kernel will be instructed via its command line to load the initrd from\r
909 the same Simple FileSystem.\r
910\r
911 @retval EFI_NOT_FOUND Kernel image was not found.\r
912 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
913 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.\r
914\r
915 @return Error codes from any of the underlying\r
916 functions. On success, the function doesn't\r
917 return.\r
918**/\r
919EFI_STATUS\r
920EFIAPI\r
921TryRunningQemuKernel (\r
922 VOID\r
923 )\r
924{\r
925 UINTN BlobType;\r
926 KERNEL_BLOB *CurrentBlob;\r
927 KERNEL_BLOB *KernelBlob, *InitrdBlob, *CommandLineBlob;\r
928 EFI_STATUS Status;\r
929 EFI_HANDLE FileSystemHandle;\r
930 EFI_DEVICE_PATH_PROTOCOL *KernelDevicePath;\r
931 EFI_HANDLE KernelImageHandle;\r
932 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;\r
933\r
934 Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);\r
935 if (EFI_ERROR (Status)) {\r
936 DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));\r
937 return Status;\r
938 }\r
939\r
940 //\r
941 // Fetch all blobs.\r
942 //\r
943 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {\r
944 CurrentBlob = &mKernelBlob[BlobType];\r
945 Status = FetchBlob (CurrentBlob);\r
946 if (EFI_ERROR (Status)) {\r
947 goto FreeBlobs;\r
948 }\r
949 mTotalBlobBytes += CurrentBlob->Size;\r
950 }\r
951 KernelBlob = &mKernelBlob[KernelBlobTypeKernel];\r
952 InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];\r
953 CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];\r
954\r
955 if (KernelBlob->Data == NULL) {\r
956 Status = EFI_NOT_FOUND;\r
957 goto FreeBlobs;\r
958 }\r
959\r
960 //\r
961 // Create a new handle with a single VenHw() node device path protocol on it,\r
962 // plus a custom SimpleFileSystem protocol on it.\r
963 //\r
964 FileSystemHandle = NULL;\r
965 Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,\r
966 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,\r
967 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,\r
968 NULL);\r
969 if (EFI_ERROR (Status)) {\r
970 DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",\r
971 __FUNCTION__, Status));\r
972 goto FreeBlobs;\r
973 }\r
974\r
975 //\r
976 // Create a device path for the kernel image to be loaded from that will call\r
977 // back into our file system.\r
978 //\r
979 KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);\r
980 if (KernelDevicePath == NULL) {\r
981 DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",\r
982 __FUNCTION__));\r
983 Status = EFI_OUT_OF_RESOURCES;\r
984 goto UninstallProtocols;\r
985 }\r
986\r
987 //\r
988 // Load the image. This should call back into our file system.\r
989 //\r
990 Status = gBS->LoadImage (\r
991 FALSE, // BootPolicy: exact match required\r
992 gImageHandle, // ParentImageHandle\r
993 KernelDevicePath,\r
994 NULL, // SourceBuffer\r
995 0, // SourceSize\r
996 &KernelImageHandle\r
997 );\r
998 if (EFI_ERROR (Status)) {\r
999 DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));\r
1000 goto FreeKernelDevicePath;\r
1001 }\r
1002\r
1003 //\r
1004 // Construct the kernel command line.\r
1005 //\r
1006 Status = gBS->OpenProtocol (\r
1007 KernelImageHandle,\r
1008 &gEfiLoadedImageProtocolGuid,\r
1009 (VOID **)&KernelLoadedImage,\r
1010 gImageHandle, // AgentHandle\r
1011 NULL, // ControllerHandle\r
1012 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1013 );\r
1014 ASSERT_EFI_ERROR (Status);\r
1015\r
1016 if (CommandLineBlob->Data == NULL) {\r
1017 KernelLoadedImage->LoadOptionsSize = 0;\r
1018 } else {\r
1019 //\r
1020 // Verify NUL-termination of the command line.\r
1021 //\r
1022 if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {\r
1023 DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",\r
1024 __FUNCTION__));\r
1025 Status = EFI_PROTOCOL_ERROR;\r
1026 goto UnloadKernelImage;\r
1027 }\r
1028\r
1029 //\r
1030 // Drop the terminating NUL, convert to UTF-16.\r
1031 //\r
1032 KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;\r
1033 }\r
1034\r
1035 if (InitrdBlob->Data != NULL) {\r
1036 //\r
1037 // Append ' initrd=<name>' in UTF-16.\r
1038 //\r
1039 KernelLoadedImage->LoadOptionsSize +=\r
1040 (8 + StrLen(InitrdBlob->Name)) * 2;\r
1041 }\r
1042\r
1043 if (KernelLoadedImage->LoadOptionsSize == 0) {\r
1044 KernelLoadedImage->LoadOptions = NULL;\r
1045 } else {\r
1046 //\r
1047 // NUL-terminate in UTF-16.\r
1048 //\r
1049 KernelLoadedImage->LoadOptionsSize += 2;\r
1050\r
1051 KernelLoadedImage->LoadOptions = AllocatePool (\r
1052 KernelLoadedImage->LoadOptionsSize);\r
1053 if (KernelLoadedImage->LoadOptions == NULL) {\r
1054 KernelLoadedImage->LoadOptionsSize = 0;\r
1055 Status = EFI_OUT_OF_RESOURCES;\r
1056 goto UnloadKernelImage;\r
1057 }\r
1058\r
1059 UnicodeSPrintAsciiFormat (\r
1060 KernelLoadedImage->LoadOptions,\r
1061 KernelLoadedImage->LoadOptionsSize,\r
1062 "%a%a%s",\r
1063 (CommandLineBlob->Data == NULL) ? "" : (CHAR8 *)CommandLineBlob->Data,\r
1064 (InitrdBlob->Data == NULL) ? "" : " initrd=",\r
1065 (InitrdBlob->Data == NULL) ? L"" : InitrdBlob->Name\r
1066 );\r
1067 DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,\r
1068 (CHAR16 *)KernelLoadedImage->LoadOptions));\r
1069 }\r
1070\r
1071 //\r
1072 // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.\r
1073 //\r
1074 EfiSignalEventReadyToBoot();\r
1075\r
97399f2d
AB
1076 REPORT_STATUS_CODE (EFI_PROGRESS_CODE,\r
1077 (EFI_SOFTWARE_DXE_BS_DRIVER | EFI_SW_DXE_BS_PC_READY_TO_BOOT_EVENT));\r
1078\r
8d620322
LE
1079 //\r
1080 // Start the image.\r
1081 //\r
1082 Status = gBS->StartImage (\r
1083 KernelImageHandle,\r
1084 NULL, // ExitDataSize\r
1085 NULL // ExitData\r
1086 );\r
1087 if (EFI_ERROR (Status)) {\r
1088 DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));\r
1089 }\r
1090\r
1091 if (KernelLoadedImage->LoadOptions != NULL) {\r
1092 FreePool (KernelLoadedImage->LoadOptions);\r
1093 }\r
1094 KernelLoadedImage->LoadOptionsSize = 0;\r
1095\r
1096UnloadKernelImage:\r
1097 gBS->UnloadImage (KernelImageHandle);\r
1098\r
1099FreeKernelDevicePath:\r
1100 FreePool (KernelDevicePath);\r
1101\r
1102UninstallProtocols:\r
1103 gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,\r
1104 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,\r
1105 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,\r
1106 NULL);\r
1107\r
1108FreeBlobs:\r
1109 while (BlobType > 0) {\r
1110 CurrentBlob = &mKernelBlob[--BlobType];\r
1111 if (CurrentBlob->Data != NULL) {\r
1112 FreePool (CurrentBlob->Data);\r
1113 CurrentBlob->Size = 0;\r
1114 CurrentBlob->Data = NULL;\r
1115 }\r
1116 }\r
1117\r
1118 return Status;\r
1119}\r