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