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