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