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