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