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