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