]> git.proxmox.com Git - mirror_edk2.git/blame - ArmVirtPkg/Library/PlatformIntelBdsLib/QemuKernel.c
ArmVirtPkg: PlatformIntelBdsLib: signal ReadyToBoot on direct kernel boot
[mirror_edk2.git] / ArmVirtPkg / Library / PlatformIntelBdsLib / QemuKernel.c
CommitLineData
23d04b58
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, 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
9a470dae 23#include <Library/UefiLib.h>\r
23d04b58
LE
24#include <Protocol/DevicePath.h>\r
25#include <Protocol/LoadedImage.h>\r
26#include <Protocol/SimpleFileSystem.h>\r
27\r
28#include "IntelBdsPlatform.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 (StubFile->Position, BufferSize,\r
417 Buffer);\r
418 if (EFI_ERROR (Status)) {\r
419 return Status;\r
420 }\r
421\r
422 ++StubFile->Position;\r
423 return EFI_SUCCESS;\r
424 }\r
425\r
426 //\r
427 // Reading a file.\r
428 //\r
429 Blob = &mKernelBlob[StubFile->BlobType];\r
430 if (StubFile->Position > Blob->Size) {\r
431 return EFI_DEVICE_ERROR;\r
432 }\r
433\r
434 Left = Blob->Size - StubFile->Position;\r
435 if (*BufferSize > Left) {\r
436 *BufferSize = (UINTN)Left;\r
437 }\r
438 if (Blob->Data != NULL) {\r
439 CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);\r
440 }\r
441 StubFile->Position += *BufferSize;\r
442 return EFI_SUCCESS;\r
443}\r
444\r
445\r
446/**\r
447 Writes data to a file.\r
448\r
449 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that\r
450 is the file handle to write data to.\r
451\r
452 @param[in,out] BufferSize On input, the size of the Buffer. On output, the\r
453 amount of data actually written. In both cases,\r
454 the size is measured in bytes.\r
455\r
456 @param[in] Buffer The buffer of data to write.\r
457\r
458 @retval EFI_SUCCESS Data was written.\r
459 @retval EFI_UNSUPPORTED Writes to open directory files are not\r
460 supported.\r
461 @retval EFI_NO_MEDIA The device has no medium.\r
462 @retval EFI_DEVICE_ERROR The device reported an error.\r
463 @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file.\r
464 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
465 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.\r
466 @retval EFI_ACCESS_DENIED The file was opened read only.\r
467 @retval EFI_VOLUME_FULL The volume is full.\r
468**/\r
469STATIC\r
470EFI_STATUS\r
471EFIAPI\r
472StubFileWrite (\r
473 IN EFI_FILE_PROTOCOL *This,\r
474 IN OUT UINTN *BufferSize,\r
475 IN VOID *Buffer\r
476 )\r
477{\r
478 STUB_FILE *StubFile;\r
479\r
480 StubFile = STUB_FILE_FROM_FILE (This);\r
481 return (StubFile->BlobType == KernelBlobTypeMax) ?\r
482 EFI_UNSUPPORTED :\r
483 EFI_WRITE_PROTECTED;\r
484}\r
485\r
486\r
487/**\r
488 Returns a file's current position.\r
489\r
490 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
491 file handle to get the current position on.\r
492\r
493 @param[out] Position The address to return the file's current position\r
494 value.\r
495\r
496 @retval EFI_SUCCESS The position was returned.\r
497 @retval EFI_UNSUPPORTED The request is not valid on open directories.\r
498 @retval EFI_DEVICE_ERROR An attempt was made to get the position from a\r
499 deleted file.\r
500**/\r
501STATIC\r
502EFI_STATUS\r
503EFIAPI\r
504StubFileGetPosition (\r
505 IN EFI_FILE_PROTOCOL *This,\r
506 OUT UINT64 *Position\r
507 )\r
508{\r
509 STUB_FILE *StubFile;\r
510\r
511 StubFile = STUB_FILE_FROM_FILE (This);\r
512 if (StubFile->BlobType == KernelBlobTypeMax) {\r
513 return EFI_UNSUPPORTED;\r
514 }\r
515\r
516 *Position = StubFile->Position;\r
517 return EFI_SUCCESS;\r
518}\r
519\r
520\r
521/**\r
522 Sets a file's current position.\r
523\r
524 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
525 file handle to set the requested position on.\r
526\r
527 @param[in] Position The byte position from the start of the file to set. For\r
528 regular files, MAX_UINT64 means "seek to end". For\r
529 directories, zero means "rewind directory scan".\r
530\r
531 @retval EFI_SUCCESS The position was set.\r
532 @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open\r
533 directories.\r
534 @retval EFI_DEVICE_ERROR An attempt was made to set the position of a\r
535 deleted file.\r
536**/\r
537STATIC\r
538EFI_STATUS\r
539EFIAPI\r
540StubFileSetPosition (\r
541 IN EFI_FILE_PROTOCOL *This,\r
542 IN UINT64 Position\r
543 )\r
544{\r
545 STUB_FILE *StubFile;\r
546 KERNEL_BLOB *Blob;\r
547\r
548 StubFile = STUB_FILE_FROM_FILE (This);\r
549\r
550 if (StubFile->BlobType == KernelBlobTypeMax) {\r
551 if (Position == 0) {\r
552 //\r
553 // rewinding a directory scan is allowed\r
554 //\r
555 StubFile->Position = 0;\r
556 return EFI_SUCCESS;\r
557 }\r
558 return EFI_UNSUPPORTED;\r
559 }\r
560\r
561 //\r
562 // regular file seek\r
563 //\r
564 Blob = &mKernelBlob[StubFile->BlobType];\r
565 if (Position == MAX_UINT64) {\r
566 //\r
567 // seek to end\r
568 //\r
569 StubFile->Position = Blob->Size;\r
570 } else {\r
571 //\r
572 // absolute seek from beginning -- seeking past the end is allowed\r
573 //\r
574 StubFile->Position = Position;\r
575 }\r
576 return EFI_SUCCESS;\r
577}\r
578\r
579\r
580/**\r
581 Returns information about a file.\r
582\r
583 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance\r
584 that is the file handle the requested\r
585 information is for.\r
586\r
587 @param[in] InformationType The type identifier GUID for the information\r
588 being requested. The following information\r
589 types are supported, storing the\r
590 corresponding structures in Buffer:\r
591\r
592 - gEfiFileInfoGuid: EFI_FILE_INFO\r
593\r
594 - gEfiFileSystemInfoGuid:\r
595 EFI_FILE_SYSTEM_INFO\r
596\r
597 - gEfiFileSystemVolumeLabelInfoIdGuid:\r
598 EFI_FILE_SYSTEM_VOLUME_LABEL\r
599\r
600 @param[in,out] BufferSize On input, the size of Buffer. On output, the\r
601 amount of data returned in Buffer. In both\r
602 cases, the size is measured in bytes.\r
603\r
604 @param[out] Buffer A pointer to the data buffer to return. The\r
605 buffer's type is indicated by\r
606 InformationType.\r
607\r
608 @retval EFI_SUCCESS The information was returned.\r
609 @retval EFI_UNSUPPORTED The InformationType is not known.\r
610 @retval EFI_NO_MEDIA The device has no medium.\r
611 @retval EFI_DEVICE_ERROR The device reported an error.\r
612 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
613 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the\r
614 information structure requested by\r
615 InformationType. BufferSize has been updated\r
616 with the size needed to complete the request.\r
617**/\r
618STATIC\r
619EFI_STATUS\r
620EFIAPI\r
621StubFileGetInfo (\r
622 IN EFI_FILE_PROTOCOL *This,\r
623 IN EFI_GUID *InformationType,\r
624 IN OUT UINTN *BufferSize,\r
625 OUT VOID *Buffer\r
626 )\r
627{\r
628 CONST STUB_FILE *StubFile;\r
629 UINTN OriginalBufferSize;\r
630\r
631 StubFile = STUB_FILE_FROM_FILE (This);\r
632\r
633 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {\r
634 return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,\r
635 Buffer);\r
636 }\r
637\r
638 OriginalBufferSize = *BufferSize;\r
639\r
640 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {\r
641 EFI_FILE_SYSTEM_INFO *FileSystemInfo;\r
642\r
643 *BufferSize = sizeof *FileSystemInfo;\r
644 if (OriginalBufferSize < *BufferSize) {\r
645 return EFI_BUFFER_TOO_SMALL;\r
646 }\r
647\r
648 FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;\r
649 FileSystemInfo->Size = sizeof *FileSystemInfo;\r
650 FileSystemInfo->ReadOnly = TRUE;\r
651 FileSystemInfo->VolumeSize = mTotalBlobBytes;\r
652 FileSystemInfo->FreeSpace = 0;\r
653 FileSystemInfo->BlockSize = 1;\r
654 FileSystemInfo->VolumeLabel[0] = L'\0';\r
655\r
656 return EFI_SUCCESS;\r
657 }\r
658\r
659 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {\r
660 EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;\r
661\r
662 *BufferSize = sizeof *FileSystemVolumeLabel;\r
663 if (OriginalBufferSize < *BufferSize) {\r
664 return EFI_BUFFER_TOO_SMALL;\r
665 }\r
666\r
667 FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;\r
668 FileSystemVolumeLabel->VolumeLabel[0] = L'\0';\r
669\r
670 return EFI_SUCCESS;\r
671 }\r
672\r
673 return EFI_UNSUPPORTED;\r
674}\r
675\r
676\r
677/**\r
678 Sets information about a file.\r
679\r
680 @param[in] File A pointer to the EFI_FILE_PROTOCOL instance that\r
681 is the file handle the information is for.\r
682\r
683 @param[in] InformationType The type identifier for the information being\r
684 set.\r
685\r
686 @param[in] BufferSize The size, in bytes, of Buffer.\r
687\r
688 @param[in] Buffer A pointer to the data buffer to write. The\r
689 buffer's type is indicated by InformationType.\r
690\r
691 @retval EFI_SUCCESS The information was set.\r
692 @retval EFI_UNSUPPORTED The InformationType is not known.\r
693 @retval EFI_NO_MEDIA The device has no medium.\r
694 @retval EFI_DEVICE_ERROR The device reported an error.\r
695 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
696 @retval EFI_WRITE_PROTECTED InformationType is EFI_FILE_INFO_ID and the\r
697 media is read-only.\r
698 @retval EFI_WRITE_PROTECTED InformationType is\r
699 EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media\r
700 is read only.\r
701 @retval EFI_WRITE_PROTECTED InformationType is\r
702 EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media\r
703 is read-only.\r
704 @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file\r
705 to a file that is already present.\r
706 @retval EFI_ACCESS_DENIED An attempt is being made to change the\r
707 EFI_FILE_DIRECTORY Attribute.\r
708 @retval EFI_ACCESS_DENIED An attempt is being made to change the size of\r
709 a directory.\r
710 @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and the\r
711 file was opened read-only and an attempt is\r
712 being made to modify a field other than\r
713 Attribute.\r
714 @retval EFI_VOLUME_FULL The volume is full.\r
715 @retval EFI_BAD_BUFFER_SIZE BufferSize is smaller than the size of the type\r
716 indicated by InformationType.\r
717**/\r
718STATIC\r
719EFI_STATUS\r
720EFIAPI\r
721StubFileSetInfo (\r
722 IN EFI_FILE_PROTOCOL *This,\r
723 IN EFI_GUID *InformationType,\r
724 IN UINTN BufferSize,\r
725 IN VOID *Buffer\r
726 )\r
727{\r
728 return EFI_WRITE_PROTECTED;\r
729}\r
730\r
731\r
732/**\r
733 Flushes all modified data associated with a file to a device.\r
734\r
735 @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the\r
736 file handle to flush.\r
737\r
738 @retval EFI_SUCCESS The data was flushed.\r
739 @retval EFI_NO_MEDIA The device has no medium.\r
740 @retval EFI_DEVICE_ERROR The device reported an error.\r
741 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
742 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.\r
743 @retval EFI_ACCESS_DENIED The file was opened read-only.\r
744 @retval EFI_VOLUME_FULL The volume is full.\r
745**/\r
746STATIC\r
747EFI_STATUS\r
748EFIAPI\r
749StubFileFlush (\r
750 IN EFI_FILE_PROTOCOL *This\r
751 )\r
752{\r
753 return EFI_WRITE_PROTECTED;\r
754}\r
755\r
756//\r
757// External definition of the file protocol template.\r
758//\r
759STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {\r
760 EFI_FILE_PROTOCOL_REVISION, // revision 1\r
761 StubFileOpen,\r
762 StubFileClose,\r
763 StubFileDelete,\r
764 StubFileRead,\r
765 StubFileWrite,\r
766 StubFileGetPosition,\r
767 StubFileSetPosition,\r
768 StubFileGetInfo,\r
769 StubFileSetInfo,\r
770 StubFileFlush,\r
771 NULL, // OpenEx, revision 2\r
772 NULL, // ReadEx, revision 2\r
773 NULL, // WriteEx, revision 2\r
774 NULL // FlushEx, revision 2\r
775};\r
776\r
777\r
778//\r
779// Protocol member functions for SimpleFileSystem.\r
780//\r
781\r
782/**\r
783 Open the root directory on a volume.\r
784\r
785 @param[in] This A pointer to the volume to open the root directory on.\r
786\r
787 @param[out] Root A pointer to the location to return the opened file handle\r
788 for the root directory in.\r
789\r
790 @retval EFI_SUCCESS The device was opened.\r
791 @retval EFI_UNSUPPORTED This volume does not support the requested file\r
792 system type.\r
793 @retval EFI_NO_MEDIA The device has no medium.\r
794 @retval EFI_DEVICE_ERROR The device reported an error.\r
795 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.\r
796 @retval EFI_ACCESS_DENIED The service denied access to the file.\r
797 @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of\r
798 resources.\r
799 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the\r
800 medium is no longer supported. Any existing\r
801 file handles for this volume are no longer\r
802 valid. To access the files on the new medium,\r
803 the volume must be reopened with OpenVolume().\r
804**/\r
805STATIC\r
806EFI_STATUS\r
807EFIAPI\r
808StubFileSystemOpenVolume (\r
809 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,\r
810 OUT EFI_FILE_PROTOCOL **Root\r
811 )\r
812{\r
813 STUB_FILE *StubFile;\r
814\r
815 StubFile = AllocatePool (sizeof *StubFile);\r
816 if (StubFile == NULL) {\r
817 return EFI_OUT_OF_RESOURCES;\r
818 }\r
819\r
820 StubFile->Signature = STUB_FILE_SIG;\r
821 StubFile->BlobType = KernelBlobTypeMax;\r
822 StubFile->Position = 0;\r
823 CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,\r
824 sizeof mEfiFileProtocolTemplate);\r
825 *Root = &StubFile->File;\r
826\r
827 return EFI_SUCCESS;\r
828}\r
829\r
830STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {\r
831 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,\r
832 StubFileSystemOpenVolume\r
833};\r
834\r
835\r
836//\r
837// Utility functions.\r
838//\r
839\r
840/**\r
841 Populate a blob in mKernelBlob.\r
842\r
843 param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is\r
844 to be filled from fw_cfg.\r
845\r
846 @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a\r
847 size of zero for the blob, then Blob->Data has\r
848 been left unchanged.\r
849\r
850 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data.\r
851**/\r
852STATIC\r
853EFI_STATUS\r
854FetchBlob (\r
855 IN OUT KERNEL_BLOB *Blob\r
856 )\r
857{\r
858 UINT32 Left;\r
859\r
860 //\r
861 // Read blob size.\r
862 //\r
863 QemuFwCfgSelectItem (Blob->SizeKey);\r
864 Blob->Size = QemuFwCfgRead32 ();\r
865 if (Blob->Size == 0) {\r
866 return EFI_SUCCESS;\r
867 }\r
868\r
869 //\r
870 // Read blob.\r
871 //\r
872 Blob->Data = AllocatePool (Blob->Size);\r
873 if (Blob->Data == NULL) {\r
874 DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",\r
875 __FUNCTION__, (INT64)Blob->Size, Blob->Name));\r
876 return EFI_OUT_OF_RESOURCES;\r
877 }\r
878\r
879 DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,\r
880 (INT64)Blob->Size, Blob->Name));\r
881 QemuFwCfgSelectItem (Blob->DataKey);\r
882\r
883 Left = Blob->Size;\r
884 do {\r
885 UINT32 Chunk;\r
886\r
887 Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;\r
888 QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));\r
889 Left -= Chunk;\r
890 DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",\r
891 __FUNCTION__, (INT64)Left, Blob->Name));\r
892 } while (Left > 0);\r
893 return EFI_SUCCESS;\r
894}\r
895\r
896\r
897//\r
898// The entry point of the feature.\r
899//\r
900\r
901/**\r
902 Download the kernel, the initial ramdisk, and the kernel command line from\r
903 QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two\r
904 image files, and load and start the kernel from it.\r
905\r
906 The kernel will be instructed via its command line to load the initrd from\r
907 the same Simple FileSystem.\r
908\r
909 @retval EFI_NOT_FOUND Kernel image was not found.\r
910 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.\r
911 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.\r
912\r
913 @return Error codes from any of the underlying\r
914 functions. On success, the function doesn't\r
915 return.\r
916**/\r
917EFI_STATUS\r
918EFIAPI\r
919TryRunningQemuKernel (\r
920 VOID\r
921 )\r
922{\r
923 UINTN BlobType;\r
924 KERNEL_BLOB *CurrentBlob;\r
925 KERNEL_BLOB *KernelBlob, *InitrdBlob, *CommandLineBlob;\r
926 EFI_STATUS Status;\r
927 EFI_HANDLE FileSystemHandle;\r
928 EFI_DEVICE_PATH_PROTOCOL *KernelDevicePath;\r
929 EFI_HANDLE KernelImageHandle;\r
930 EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;\r
931\r
932 Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);\r
933 if (EFI_ERROR (Status)) {\r
934 DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));\r
935 return Status;\r
936 }\r
937\r
938 //\r
939 // Fetch all blobs.\r
940 //\r
941 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {\r
942 CurrentBlob = &mKernelBlob[BlobType];\r
943 Status = FetchBlob (CurrentBlob);\r
944 if (EFI_ERROR (Status)) {\r
945 goto FreeBlobs;\r
946 }\r
947 mTotalBlobBytes += CurrentBlob->Size;\r
948 }\r
949 KernelBlob = &mKernelBlob[KernelBlobTypeKernel];\r
950 InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];\r
951 CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];\r
952\r
7602900a
LE
953 if (KernelBlob->Data == NULL) {\r
954 Status = EFI_NOT_FOUND;\r
955 goto FreeBlobs;\r
956 }\r
957\r
23d04b58
LE
958 //\r
959 // Create a new handle with a single VenHw() node device path protocol on it,\r
960 // plus a custom SimpleFileSystem protocol on it.\r
961 //\r
962 FileSystemHandle = NULL;\r
963 Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,\r
964 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,\r
965 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,\r
966 NULL);\r
967 if (EFI_ERROR (Status)) {\r
968 DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",\r
969 __FUNCTION__, Status));\r
970 goto FreeBlobs;\r
971 }\r
972\r
973 //\r
974 // Create a device path for the kernel image to be loaded from that will call\r
975 // back into our file system.\r
976 //\r
977 KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);\r
978 if (KernelDevicePath == NULL) {\r
979 DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",\r
980 __FUNCTION__));\r
981 Status = EFI_OUT_OF_RESOURCES;\r
982 goto UninstallProtocols;\r
983 }\r
984\r
985 //\r
986 // Load the image. This should call back into our file system.\r
987 //\r
988 Status = gBS->LoadImage (\r
989 FALSE, // BootPolicy: exact match required\r
990 gImageHandle, // ParentImageHandle\r
991 KernelDevicePath,\r
992 NULL, // SourceBuffer\r
993 0, // SourceSize\r
994 &KernelImageHandle\r
995 );\r
996 if (EFI_ERROR (Status)) {\r
997 DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));\r
998 goto FreeKernelDevicePath;\r
999 }\r
1000\r
1001 //\r
1002 // Construct the kernel command line.\r
1003 //\r
1004 Status = gBS->OpenProtocol (\r
1005 KernelImageHandle,\r
1006 &gEfiLoadedImageProtocolGuid,\r
1007 (VOID **)&KernelLoadedImage,\r
1008 gImageHandle, // AgentHandle\r
1009 NULL, // ControllerHandle\r
1010 EFI_OPEN_PROTOCOL_GET_PROTOCOL\r
1011 );\r
1012 ASSERT_EFI_ERROR (Status);\r
1013\r
1014 if (CommandLineBlob->Data == NULL) {\r
1015 KernelLoadedImage->LoadOptionsSize = 0;\r
1016 } else {\r
1017 //\r
1018 // Verify NUL-termination of the command line.\r
1019 //\r
1020 if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {\r
1021 DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",\r
1022 __FUNCTION__));\r
1023 Status = EFI_PROTOCOL_ERROR;\r
1024 goto UnloadKernelImage;\r
1025 }\r
1026\r
1027 //\r
1028 // Drop the terminating NUL, convert to UTF-16.\r
1029 //\r
1030 KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;\r
1031 }\r
1032\r
1033 if (InitrdBlob->Data != NULL) {\r
1034 //\r
1035 // Append ' initrd=<name>' in UTF-16.\r
1036 //\r
1037 KernelLoadedImage->LoadOptionsSize +=\r
1038 (8 + StrLen(InitrdBlob->Name)) * 2;\r
1039 }\r
1040\r
1041 if (KernelLoadedImage->LoadOptionsSize == 0) {\r
1042 KernelLoadedImage->LoadOptions = NULL;\r
1043 } else {\r
1044 //\r
1045 // NUL-terminate in UTF-16.\r
1046 //\r
1047 KernelLoadedImage->LoadOptionsSize += 2;\r
1048\r
1049 KernelLoadedImage->LoadOptions = AllocatePool (\r
1050 KernelLoadedImage->LoadOptionsSize);\r
1051 if (KernelLoadedImage->LoadOptions == NULL) {\r
1052 KernelLoadedImage->LoadOptionsSize = 0;\r
1053 Status = EFI_OUT_OF_RESOURCES;\r
1054 goto UnloadKernelImage;\r
1055 }\r
1056\r
1057 UnicodeSPrintAsciiFormat (\r
1058 KernelLoadedImage->LoadOptions,\r
1059 KernelLoadedImage->LoadOptionsSize,\r
1060 "%a%a%s",\r
1061 (CommandLineBlob->Data == NULL) ? "" : (CHAR8 *)CommandLineBlob->Data,\r
1062 (InitrdBlob->Data == NULL) ? "" : " initrd=",\r
1063 (InitrdBlob->Data == NULL) ? L"" : InitrdBlob->Name\r
1064 );\r
1065 DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,\r
1066 (CHAR16 *)KernelLoadedImage->LoadOptions));\r
1067 }\r
1068\r
9a470dae
LE
1069 //\r
1070 // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.\r
1071 //\r
1072 EfiSignalEventReadyToBoot();\r
1073\r
23d04b58
LE
1074 //\r
1075 // Start the image.\r
1076 //\r
1077 Status = gBS->StartImage (\r
1078 KernelImageHandle,\r
1079 NULL, // ExitDataSize\r
1080 NULL // ExitData\r
1081 );\r
1082 if (EFI_ERROR (Status)) {\r
1083 DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));\r
1084 }\r
1085\r
1086 if (KernelLoadedImage->LoadOptions != NULL) {\r
1087 FreePool (KernelLoadedImage->LoadOptions);\r
1088 }\r
1089 KernelLoadedImage->LoadOptionsSize = 0;\r
1090\r
1091UnloadKernelImage:\r
1092 gBS->UnloadImage (KernelImageHandle);\r
1093\r
1094FreeKernelDevicePath:\r
1095 FreePool (KernelDevicePath);\r
1096\r
1097UninstallProtocols:\r
1098 gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,\r
1099 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,\r
1100 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,\r
1101 NULL);\r
1102\r
1103FreeBlobs:\r
1104 while (BlobType > 0) {\r
1105 CurrentBlob = &mKernelBlob[--BlobType];\r
1106 if (CurrentBlob->Data != NULL) {\r
1107 FreePool (CurrentBlob->Data);\r
1108 CurrentBlob->Size = 0;\r
1109 CurrentBlob->Data = NULL;\r
1110 }\r
1111 }\r
1112\r
1113 return Status;\r
1114}\r