]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c
OvmfPkg/QemuKernelLoaderFsDxe: call VerifyBlob after fetch from fw_cfg
[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 /**
209 Close and delete the file handle.
210
211 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
212 handle to the file to delete.
213
214 @retval EFI_SUCCESS The file was closed and deleted, and the
215 handle was closed.
216 @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not
217 deleted.
218
219 **/
220 STATIC
221 EFI_STATUS
222 EFIAPI
223 StubFileDelete (
224 IN EFI_FILE_PROTOCOL *This
225 )
226 {
227 FreePool (STUB_FILE_FROM_FILE (This));
228 return EFI_WARN_DELETE_FAILURE;
229 }
230
231
232 /**
233 Helper function that formats an EFI_FILE_INFO structure into the
234 user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including
235 KernelBlobTypeMax, which stands for the root directory).
236
237 The interface follows the EFI_FILE_GET_INFO -- and for directories, the
238 EFI_FILE_READ -- interfaces.
239
240 @param[in] BlobType The KERNEL_BLOB_TYPE value identifying the fw_cfg
241 blob backing the STUB_FILE that information is
242 being requested about. If BlobType equals
243 KernelBlobTypeMax, then information will be
244 provided about the root directory of the
245 filesystem.
246
247 @param[in,out] BufferSize On input, the size of Buffer. On output, the
248 amount of data returned in Buffer. In both cases,
249 the size is measured in bytes.
250
251 @param[out] Buffer A pointer to the data buffer to return. The
252 buffer's type is EFI_FILE_INFO.
253
254 @retval EFI_SUCCESS The information was returned.
255 @retval EFI_BUFFER_TOO_SMALL BufferSize is too small to store the
256 EFI_FILE_INFO structure. BufferSize has been
257 updated with the size needed to complete the
258 request.
259 **/
260 STATIC
261 EFI_STATUS
262 ConvertKernelBlobTypeToFileInfo (
263 IN KERNEL_BLOB_TYPE BlobType,
264 IN OUT UINTN *BufferSize,
265 OUT VOID *Buffer
266 )
267 {
268 CONST CHAR16 *Name;
269 UINT64 FileSize;
270 UINT64 Attribute;
271
272 UINTN NameSize;
273 UINTN FileInfoSize;
274 EFI_FILE_INFO *FileInfo;
275 UINTN OriginalBufferSize;
276
277 if (BlobType == KernelBlobTypeMax) {
278 //
279 // getting file info about the root directory
280 //
281 Name = L"\\";
282 FileSize = KernelBlobTypeMax;
283 Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
284 } else {
285 CONST KERNEL_BLOB *Blob;
286
287 Blob = &mKernelBlob[BlobType];
288 Name = Blob->Name;
289 FileSize = Blob->Size;
290 Attribute = EFI_FILE_READ_ONLY;
291 }
292
293 NameSize = (StrLen(Name) + 1) * 2;
294 FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;
295 ASSERT (FileInfoSize >= sizeof *FileInfo);
296
297 OriginalBufferSize = *BufferSize;
298 *BufferSize = FileInfoSize;
299 if (OriginalBufferSize < *BufferSize) {
300 return EFI_BUFFER_TOO_SMALL;
301 }
302
303 FileInfo = (EFI_FILE_INFO *)Buffer;
304 FileInfo->Size = FileInfoSize;
305 FileInfo->FileSize = FileSize;
306 FileInfo->PhysicalSize = FileSize;
307 FileInfo->Attribute = Attribute;
308
309 CopyMem (&FileInfo->CreateTime, &mInitTime, sizeof mInitTime);
310 CopyMem (&FileInfo->LastAccessTime, &mInitTime, sizeof mInitTime);
311 CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);
312 CopyMem (FileInfo->FileName, Name, NameSize);
313
314 return EFI_SUCCESS;
315 }
316
317
318 /**
319 Reads data from a file, or continues scanning a directory.
320
321 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that
322 is the file handle to read data from.
323
324 @param[in,out] BufferSize On input, the size of the Buffer. On output, the
325 amount of data returned in Buffer. In both cases,
326 the size is measured in bytes. If the read goes
327 beyond the end of the file, the read length is
328 truncated to the end of the file.
329
330 If This is a directory, the function reads the
331 directory entry at the current position and
332 returns the entry (as EFI_FILE_INFO) in Buffer. If
333 there are no more directory entries, the
334 BufferSize is set to zero on output.
335
336 @param[out] Buffer The buffer into which the data is read.
337
338 @retval EFI_SUCCESS Data was read.
339 @retval EFI_NO_MEDIA The device has no medium.
340 @retval EFI_DEVICE_ERROR The device reported an error.
341 @retval EFI_DEVICE_ERROR An attempt was made to read from a deleted
342 file.
343 @retval EFI_DEVICE_ERROR On entry, the current file position is beyond
344 the end of the file.
345 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
346 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the
347 current directory entry as a EFI_FILE_INFO
348 structure. BufferSize has been updated with the
349 size needed to complete the request, and the
350 directory position has not been advanced.
351 **/
352 STATIC
353 EFI_STATUS
354 EFIAPI
355 StubFileRead (
356 IN EFI_FILE_PROTOCOL *This,
357 IN OUT UINTN *BufferSize,
358 OUT VOID *Buffer
359 )
360 {
361 STUB_FILE *StubFile;
362 CONST KERNEL_BLOB *Blob;
363 UINT64 Left;
364
365 StubFile = STUB_FILE_FROM_FILE (This);
366
367 //
368 // Scanning the root directory?
369 //
370 if (StubFile->BlobType == KernelBlobTypeMax) {
371 EFI_STATUS Status;
372
373 if (StubFile->Position == KernelBlobTypeMax) {
374 //
375 // Scanning complete.
376 //
377 *BufferSize = 0;
378 return EFI_SUCCESS;
379 }
380
381 Status = ConvertKernelBlobTypeToFileInfo (
382 (KERNEL_BLOB_TYPE)StubFile->Position,
383 BufferSize,
384 Buffer);
385 if (EFI_ERROR (Status)) {
386 return Status;
387 }
388
389 ++StubFile->Position;
390 return EFI_SUCCESS;
391 }
392
393 //
394 // Reading a file.
395 //
396 Blob = &mKernelBlob[StubFile->BlobType];
397 if (StubFile->Position > Blob->Size) {
398 return EFI_DEVICE_ERROR;
399 }
400
401 Left = Blob->Size - StubFile->Position;
402 if (*BufferSize > Left) {
403 *BufferSize = (UINTN)Left;
404 }
405 if (Blob->Data != NULL) {
406 CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
407 }
408 StubFile->Position += *BufferSize;
409 return EFI_SUCCESS;
410 }
411
412
413 /**
414 Writes data to a file.
415
416 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that
417 is the file handle to write data to.
418
419 @param[in,out] BufferSize On input, the size of the Buffer. On output, the
420 amount of data actually written. In both cases,
421 the size is measured in bytes.
422
423 @param[in] Buffer The buffer of data to write.
424
425 @retval EFI_SUCCESS Data was written.
426 @retval EFI_UNSUPPORTED Writes to open directory files are not
427 supported.
428 @retval EFI_NO_MEDIA The device has no medium.
429 @retval EFI_DEVICE_ERROR The device reported an error.
430 @retval EFI_DEVICE_ERROR An attempt was made to write to a deleted file.
431 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
432 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.
433 @retval EFI_ACCESS_DENIED The file was opened read only.
434 @retval EFI_VOLUME_FULL The volume is full.
435 **/
436 STATIC
437 EFI_STATUS
438 EFIAPI
439 StubFileWrite (
440 IN EFI_FILE_PROTOCOL *This,
441 IN OUT UINTN *BufferSize,
442 IN VOID *Buffer
443 )
444 {
445 STUB_FILE *StubFile;
446
447 StubFile = STUB_FILE_FROM_FILE (This);
448 return (StubFile->BlobType == KernelBlobTypeMax) ?
449 EFI_UNSUPPORTED :
450 EFI_WRITE_PROTECTED;
451 }
452
453
454 /**
455 Returns a file's current position.
456
457 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
458 file handle to get the current position on.
459
460 @param[out] Position The address to return the file's current position
461 value.
462
463 @retval EFI_SUCCESS The position was returned.
464 @retval EFI_UNSUPPORTED The request is not valid on open directories.
465 @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
466 deleted file.
467 **/
468 STATIC
469 EFI_STATUS
470 EFIAPI
471 StubFileGetPosition (
472 IN EFI_FILE_PROTOCOL *This,
473 OUT UINT64 *Position
474 )
475 {
476 STUB_FILE *StubFile;
477
478 StubFile = STUB_FILE_FROM_FILE (This);
479 if (StubFile->BlobType == KernelBlobTypeMax) {
480 return EFI_UNSUPPORTED;
481 }
482
483 *Position = StubFile->Position;
484 return EFI_SUCCESS;
485 }
486
487
488 /**
489 Sets a file's current position.
490
491 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
492 file handle to set the requested position on.
493
494 @param[in] Position The byte position from the start of the file to set. For
495 regular files, MAX_UINT64 means "seek to end". For
496 directories, zero means "rewind directory scan".
497
498 @retval EFI_SUCCESS The position was set.
499 @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open
500 directories.
501 @retval EFI_DEVICE_ERROR An attempt was made to set the position of a
502 deleted file.
503 **/
504 STATIC
505 EFI_STATUS
506 EFIAPI
507 StubFileSetPosition (
508 IN EFI_FILE_PROTOCOL *This,
509 IN UINT64 Position
510 )
511 {
512 STUB_FILE *StubFile;
513 KERNEL_BLOB *Blob;
514
515 StubFile = STUB_FILE_FROM_FILE (This);
516
517 if (StubFile->BlobType == KernelBlobTypeMax) {
518 if (Position == 0) {
519 //
520 // rewinding a directory scan is allowed
521 //
522 StubFile->Position = 0;
523 return EFI_SUCCESS;
524 }
525 return EFI_UNSUPPORTED;
526 }
527
528 //
529 // regular file seek
530 //
531 Blob = &mKernelBlob[StubFile->BlobType];
532 if (Position == MAX_UINT64) {
533 //
534 // seek to end
535 //
536 StubFile->Position = Blob->Size;
537 } else {
538 //
539 // absolute seek from beginning -- seeking past the end is allowed
540 //
541 StubFile->Position = Position;
542 }
543 return EFI_SUCCESS;
544 }
545
546
547 /**
548 Returns information about a file.
549
550 @param[in] This A pointer to the EFI_FILE_PROTOCOL instance
551 that is the file handle the requested
552 information is for.
553
554 @param[in] InformationType The type identifier GUID for the information
555 being requested. The following information
556 types are supported, storing the
557 corresponding structures in Buffer:
558
559 - gEfiFileInfoGuid: EFI_FILE_INFO
560
561 - gEfiFileSystemInfoGuid:
562 EFI_FILE_SYSTEM_INFO
563
564 - gEfiFileSystemVolumeLabelInfoIdGuid:
565 EFI_FILE_SYSTEM_VOLUME_LABEL
566
567 @param[in,out] BufferSize On input, the size of Buffer. On output, the
568 amount of data returned in Buffer. In both
569 cases, the size is measured in bytes.
570
571 @param[out] Buffer A pointer to the data buffer to return. The
572 buffer's type is indicated by
573 InformationType.
574
575 @retval EFI_SUCCESS The information was returned.
576 @retval EFI_UNSUPPORTED The InformationType is not known.
577 @retval EFI_NO_MEDIA The device has no medium.
578 @retval EFI_DEVICE_ERROR The device reported an error.
579 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
580 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to store the
581 information structure requested by
582 InformationType. BufferSize has been updated
583 with the size needed to complete the request.
584 **/
585 STATIC
586 EFI_STATUS
587 EFIAPI
588 StubFileGetInfo (
589 IN EFI_FILE_PROTOCOL *This,
590 IN EFI_GUID *InformationType,
591 IN OUT UINTN *BufferSize,
592 OUT VOID *Buffer
593 )
594 {
595 CONST STUB_FILE *StubFile;
596 UINTN OriginalBufferSize;
597
598 StubFile = STUB_FILE_FROM_FILE (This);
599
600 if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
601 return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
602 Buffer);
603 }
604
605 OriginalBufferSize = *BufferSize;
606
607 if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
608 EFI_FILE_SYSTEM_INFO *FileSystemInfo;
609
610 *BufferSize = sizeof *FileSystemInfo;
611 if (OriginalBufferSize < *BufferSize) {
612 return EFI_BUFFER_TOO_SMALL;
613 }
614
615 FileSystemInfo = (EFI_FILE_SYSTEM_INFO *)Buffer;
616 FileSystemInfo->Size = sizeof *FileSystemInfo;
617 FileSystemInfo->ReadOnly = TRUE;
618 FileSystemInfo->VolumeSize = mTotalBlobBytes;
619 FileSystemInfo->FreeSpace = 0;
620 FileSystemInfo->BlockSize = 1;
621 FileSystemInfo->VolumeLabel[0] = L'\0';
622
623 return EFI_SUCCESS;
624 }
625
626 if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
627 EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
628
629 *BufferSize = sizeof *FileSystemVolumeLabel;
630 if (OriginalBufferSize < *BufferSize) {
631 return EFI_BUFFER_TOO_SMALL;
632 }
633
634 FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
635 FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
636
637 return EFI_SUCCESS;
638 }
639
640 return EFI_UNSUPPORTED;
641 }
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 /**
700 Flushes all modified data associated with a file to a device.
701
702 @param [in] This A pointer to the EFI_FILE_PROTOCOL instance that is the
703 file handle to flush.
704
705 @retval EFI_SUCCESS The data was flushed.
706 @retval EFI_NO_MEDIA The device has no medium.
707 @retval EFI_DEVICE_ERROR The device reported an error.
708 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
709 @retval EFI_WRITE_PROTECTED The file or medium is write-protected.
710 @retval EFI_ACCESS_DENIED The file was opened read-only.
711 @retval EFI_VOLUME_FULL The volume is full.
712 **/
713 STATIC
714 EFI_STATUS
715 EFIAPI
716 StubFileFlush (
717 IN EFI_FILE_PROTOCOL *This
718 )
719 {
720 return EFI_WRITE_PROTECTED;
721 }
722
723 //
724 // External definition of the file protocol template.
725 //
726 STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
727 EFI_FILE_PROTOCOL_REVISION, // revision 1
728 StubFileOpen,
729 StubFileClose,
730 StubFileDelete,
731 StubFileRead,
732 StubFileWrite,
733 StubFileGetPosition,
734 StubFileSetPosition,
735 StubFileGetInfo,
736 StubFileSetInfo,
737 StubFileFlush,
738 NULL, // OpenEx, revision 2
739 NULL, // ReadEx, revision 2
740 NULL, // WriteEx, revision 2
741 NULL // FlushEx, revision 2
742 };
743
744 STATIC
745 EFI_STATUS
746 EFIAPI
747 StubFileOpen (
748 IN EFI_FILE_PROTOCOL *This,
749 OUT EFI_FILE_PROTOCOL **NewHandle,
750 IN CHAR16 *FileName,
751 IN UINT64 OpenMode,
752 IN UINT64 Attributes
753 )
754 {
755 CONST STUB_FILE *StubFile;
756 UINTN BlobType;
757 STUB_FILE *NewStubFile;
758
759 //
760 // We're read-only.
761 //
762 switch (OpenMode) {
763 case EFI_FILE_MODE_READ:
764 break;
765
766 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
767 case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
768 return EFI_WRITE_PROTECTED;
769
770 default:
771 return EFI_INVALID_PARAMETER;
772 }
773
774 //
775 // Only the root directory supports opening files in it.
776 //
777 StubFile = STUB_FILE_FROM_FILE (This);
778 if (StubFile->BlobType != KernelBlobTypeMax) {
779 return EFI_UNSUPPORTED;
780 }
781
782 //
783 // Locate the file.
784 //
785 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
786 if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {
787 break;
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 (&NewStubFile->File, &mEfiFileProtocolTemplate,
806 sizeof mEfiFileProtocolTemplate);
807 *NewHandle = &NewStubFile->File;
808
809 return EFI_SUCCESS;
810 }
811
812
813 //
814 // Protocol member functions for SimpleFileSystem.
815 //
816
817 /**
818 Open the root directory on a volume.
819
820 @param[in] This A pointer to the volume to open the root directory on.
821
822 @param[out] Root A pointer to the location to return the opened file handle
823 for the root directory in.
824
825 @retval EFI_SUCCESS The device was opened.
826 @retval EFI_UNSUPPORTED This volume does not support the requested file
827 system type.
828 @retval EFI_NO_MEDIA The device has no medium.
829 @retval EFI_DEVICE_ERROR The device reported an error.
830 @retval EFI_VOLUME_CORRUPTED The file system structures are corrupted.
831 @retval EFI_ACCESS_DENIED The service denied access to the file.
832 @retval EFI_OUT_OF_RESOURCES The volume was not opened due to lack of
833 resources.
834 @retval EFI_MEDIA_CHANGED The device has a different medium in it or the
835 medium is no longer supported. Any existing
836 file handles for this volume are no longer
837 valid. To access the files on the new medium,
838 the volume must be reopened with OpenVolume().
839 **/
840 STATIC
841 EFI_STATUS
842 EFIAPI
843 StubFileSystemOpenVolume (
844 IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
845 OUT EFI_FILE_PROTOCOL **Root
846 )
847 {
848 STUB_FILE *StubFile;
849
850 StubFile = AllocatePool (sizeof *StubFile);
851 if (StubFile == NULL) {
852 return EFI_OUT_OF_RESOURCES;
853 }
854
855 StubFile->Signature = STUB_FILE_SIG;
856 StubFile->BlobType = KernelBlobTypeMax;
857 StubFile->Position = 0;
858 CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
859 sizeof mEfiFileProtocolTemplate);
860 *Root = &StubFile->File;
861
862 return EFI_SUCCESS;
863 }
864
865 STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
866 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
867 StubFileSystemOpenVolume
868 };
869
870 STATIC
871 EFI_STATUS
872 EFIAPI
873 InitrdLoadFile2 (
874 IN EFI_LOAD_FILE2_PROTOCOL *This,
875 IN EFI_DEVICE_PATH_PROTOCOL *FilePath,
876 IN BOOLEAN BootPolicy,
877 IN OUT UINTN *BufferSize,
878 OUT VOID *Buffer OPTIONAL
879 )
880 {
881 CONST KERNEL_BLOB *InitrdBlob = &mKernelBlob[KernelBlobTypeInitrd];
882
883 ASSERT (InitrdBlob->Size > 0);
884
885 if (BootPolicy) {
886 return EFI_UNSUPPORTED;
887 }
888
889 if (BufferSize == NULL || !IsDevicePathValid (FilePath, 0)) {
890 return EFI_INVALID_PARAMETER;
891 }
892
893 if (FilePath->Type != END_DEVICE_PATH_TYPE ||
894 FilePath->SubType != END_ENTIRE_DEVICE_PATH_SUBTYPE) {
895 return EFI_NOT_FOUND;
896 }
897
898 if (Buffer == NULL || *BufferSize < InitrdBlob->Size) {
899 *BufferSize = InitrdBlob->Size;
900 return EFI_BUFFER_TOO_SMALL;
901 }
902
903 CopyMem (Buffer, InitrdBlob->Data, InitrdBlob->Size);
904
905 *BufferSize = InitrdBlob->Size;
906 return EFI_SUCCESS;
907 }
908
909 STATIC CONST EFI_LOAD_FILE2_PROTOCOL mInitrdLoadFile2 = {
910 InitrdLoadFile2,
911 };
912
913 //
914 // Utility functions.
915 //
916
917 /**
918 Populate a blob in mKernelBlob.
919
920 param[in,out] Blob Pointer to the KERNEL_BLOB element in mKernelBlob that is
921 to be filled from fw_cfg.
922
923 @retval EFI_SUCCESS Blob has been populated. If fw_cfg reported a
924 size of zero for the blob, then Blob->Data has
925 been left unchanged.
926
927 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory for Blob->Data.
928 **/
929 STATIC
930 EFI_STATUS
931 FetchBlob (
932 IN OUT KERNEL_BLOB *Blob
933 )
934 {
935 UINT32 Left;
936 UINTN Idx;
937 UINT8 *ChunkData;
938
939 //
940 // Read blob size.
941 //
942 Blob->Size = 0;
943 for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
944 if (Blob->FwCfgItem[Idx].SizeKey == 0) {
945 break;
946 }
947 QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].SizeKey);
948 Blob->FwCfgItem[Idx].Size = QemuFwCfgRead32 ();
949 Blob->Size += Blob->FwCfgItem[Idx].Size;
950 }
951 if (Blob->Size == 0) {
952 return EFI_SUCCESS;
953 }
954
955 //
956 // Read blob.
957 //
958 Blob->Data = AllocatePool (Blob->Size);
959 if (Blob->Data == NULL) {
960 DEBUG ((DEBUG_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
961 __FUNCTION__, (INT64)Blob->Size, Blob->Name));
962 return EFI_OUT_OF_RESOURCES;
963 }
964
965 DEBUG ((DEBUG_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
966 (INT64)Blob->Size, Blob->Name));
967
968 ChunkData = Blob->Data;
969 for (Idx = 0; Idx < ARRAY_SIZE (Blob->FwCfgItem); Idx++) {
970 if (Blob->FwCfgItem[Idx].DataKey == 0) {
971 break;
972 }
973 QemuFwCfgSelectItem (Blob->FwCfgItem[Idx].DataKey);
974
975 Left = Blob->FwCfgItem[Idx].Size;
976 while (Left > 0) {
977 UINT32 Chunk;
978
979 Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
980 QemuFwCfgReadBytes (Chunk, ChunkData + Blob->FwCfgItem[Idx].Size - Left);
981 Left -= Chunk;
982 DEBUG ((DEBUG_VERBOSE, "%a: %Ld bytes remaining for \"%s\" (%d)\n",
983 __FUNCTION__, (INT64)Left, Blob->Name, (INT32)Idx));
984 }
985
986 ChunkData += Blob->FwCfgItem[Idx].Size;
987 }
988
989 return EFI_SUCCESS;
990 }
991
992
993 //
994 // The entry point of the feature.
995 //
996
997 /**
998 Download the kernel, the initial ramdisk, and the kernel command line from
999 QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
1000 image files.
1001
1002 @retval EFI_NOT_FOUND Kernel image was not found.
1003 @retval EFI_OUT_OF_RESOURCES Memory allocation failed.
1004 @retval EFI_PROTOCOL_ERROR Unterminated kernel command line.
1005
1006 @return Error codes from any of the underlying
1007 functions. On success, the function doesn't
1008 return.
1009 **/
1010 EFI_STATUS
1011 EFIAPI
1012 QemuKernelLoaderFsDxeEntrypoint (
1013 IN EFI_HANDLE ImageHandle,
1014 IN EFI_SYSTEM_TABLE *SystemTable
1015 )
1016 {
1017 UINTN BlobType;
1018 KERNEL_BLOB *CurrentBlob;
1019 KERNEL_BLOB *KernelBlob;
1020 EFI_STATUS Status;
1021 EFI_HANDLE FileSystemHandle;
1022 EFI_HANDLE InitrdLoadFile2Handle;
1023
1024 if (!QemuFwCfgIsAvailable ()) {
1025 return EFI_NOT_FOUND;
1026 }
1027
1028 Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
1029 if (EFI_ERROR (Status)) {
1030 DEBUG ((DEBUG_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
1031 return Status;
1032 }
1033
1034 //
1035 // Fetch all blobs.
1036 //
1037 for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
1038 CurrentBlob = &mKernelBlob[BlobType];
1039 Status = FetchBlob (CurrentBlob);
1040 if (EFI_ERROR (Status)) {
1041 goto FreeBlobs;
1042 }
1043 Status = VerifyBlob (
1044 CurrentBlob->Name,
1045 CurrentBlob->Data,
1046 CurrentBlob->Size
1047 );
1048 if (EFI_ERROR (Status)) {
1049 goto FreeBlobs;
1050 }
1051 mTotalBlobBytes += CurrentBlob->Size;
1052 }
1053 KernelBlob = &mKernelBlob[KernelBlobTypeKernel];
1054
1055 if (KernelBlob->Data == NULL) {
1056 Status = EFI_NOT_FOUND;
1057 goto FreeBlobs;
1058 }
1059
1060 //
1061 // Create a new handle with a single VenMedia() node device path protocol on
1062 // it, plus a custom SimpleFileSystem protocol on it.
1063 //
1064 FileSystemHandle = NULL;
1065 Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
1066 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,
1067 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
1068 NULL);
1069 if (EFI_ERROR (Status)) {
1070 DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
1071 __FUNCTION__, Status));
1072 goto FreeBlobs;
1073 }
1074
1075 if (KernelBlob[KernelBlobTypeInitrd].Size > 0) {
1076 InitrdLoadFile2Handle = NULL;
1077 Status = gBS->InstallMultipleProtocolInterfaces (&InitrdLoadFile2Handle,
1078 &gEfiDevicePathProtocolGuid, &mInitrdDevicePath,
1079 &gEfiLoadFile2ProtocolGuid, &mInitrdLoadFile2,
1080 NULL);
1081 if (EFI_ERROR (Status)) {
1082 DEBUG ((DEBUG_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
1083 __FUNCTION__, Status));
1084 goto UninstallFileSystemHandle;
1085 }
1086 }
1087
1088 return EFI_SUCCESS;
1089
1090 UninstallFileSystemHandle:
1091 Status = gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
1092 &gEfiDevicePathProtocolGuid, &mFileSystemDevicePath,
1093 &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
1094 NULL);
1095 ASSERT_EFI_ERROR (Status);
1096
1097 FreeBlobs:
1098 while (BlobType > 0) {
1099 CurrentBlob = &mKernelBlob[--BlobType];
1100 if (CurrentBlob->Data != NULL) {
1101 FreePool (CurrentBlob->Data);
1102 CurrentBlob->Size = 0;
1103 CurrentBlob->Data = NULL;
1104 }
1105 }
1106
1107 return Status;
1108 }