VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/OvmfPkg/QemuKernelLoaderFsDxe/QemuKernelLoaderFsDxe.c@ 108801

Last change on this file since 108801 was 108794, checked in by vboxsync, 3 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette