• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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-2016, 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 "PlatformBm.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
StubFileOpen(IN EFI_FILE_PROTOCOL * This,OUT EFI_FILE_PROTOCOL ** NewHandle,IN CHAR16 * FileName,IN UINT64 OpenMode,IN UINT64 Attributes)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
StubFileClose(IN EFI_FILE_PROTOCOL * This)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
StubFileDelete(IN EFI_FILE_PROTOCOL * This)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
ConvertKernelBlobTypeToFileInfo(IN KERNEL_BLOB_TYPE BlobType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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
StubFileRead(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,OUT VOID * Buffer)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 (
416                (KERNEL_BLOB_TYPE)StubFile->Position,
417                BufferSize,
418                Buffer);
419     if (EFI_ERROR (Status)) {
420       return Status;
421     }
422 
423     ++StubFile->Position;
424     return EFI_SUCCESS;
425   }
426 
427   //
428   // Reading a file.
429   //
430   Blob = &mKernelBlob[StubFile->BlobType];
431   if (StubFile->Position > Blob->Size) {
432     return EFI_DEVICE_ERROR;
433   }
434 
435   Left = Blob->Size - StubFile->Position;
436   if (*BufferSize > Left) {
437     *BufferSize = (UINTN)Left;
438   }
439   if (Blob->Data != NULL) {
440     CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
441   }
442   StubFile->Position += *BufferSize;
443   return EFI_SUCCESS;
444 }
445 
446 
447 /**
448   Writes data to a file.
449 
450   @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
451                              is the file handle to write data to.
452 
453   @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
454                              amount of data actually written. In both cases,
455                              the size is measured in bytes.
456 
457   @param[in]     Buffer      The buffer of data to write.
458 
459   @retval EFI_SUCCESS           Data was written.
460   @retval EFI_UNSUPPORTED       Writes to open directory files are not
461                                 supported.
462   @retval EFI_NO_MEDIA          The device has no medium.
463   @retval EFI_DEVICE_ERROR      The device reported an error.
464   @retval EFI_DEVICE_ERROR      An attempt was made to write to a deleted file.
465   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
466   @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
467   @retval EFI_ACCESS_DENIED     The file was opened read only.
468   @retval EFI_VOLUME_FULL       The volume is full.
469 **/
470 STATIC
471 EFI_STATUS
472 EFIAPI
StubFileWrite(IN EFI_FILE_PROTOCOL * This,IN OUT UINTN * BufferSize,IN VOID * Buffer)473 StubFileWrite (
474   IN EFI_FILE_PROTOCOL *This,
475   IN OUT UINTN         *BufferSize,
476   IN VOID              *Buffer
477   )
478 {
479   STUB_FILE *StubFile;
480 
481   StubFile = STUB_FILE_FROM_FILE (This);
482   return (StubFile->BlobType == KernelBlobTypeMax) ?
483          EFI_UNSUPPORTED :
484          EFI_WRITE_PROTECTED;
485 }
486 
487 
488 /**
489   Returns a file's current position.
490 
491   @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is the
492                         file handle to get the current position on.
493 
494   @param[out] Position  The address to return the file's current position
495                         value.
496 
497   @retval EFI_SUCCESS      The position was returned.
498   @retval EFI_UNSUPPORTED  The request is not valid on open directories.
499   @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
500                            deleted file.
501 **/
502 STATIC
503 EFI_STATUS
504 EFIAPI
StubFileGetPosition(IN EFI_FILE_PROTOCOL * This,OUT UINT64 * Position)505 StubFileGetPosition (
506   IN EFI_FILE_PROTOCOL *This,
507   OUT UINT64           *Position
508   )
509 {
510   STUB_FILE *StubFile;
511 
512   StubFile = STUB_FILE_FROM_FILE (This);
513   if (StubFile->BlobType == KernelBlobTypeMax) {
514     return EFI_UNSUPPORTED;
515   }
516 
517   *Position = StubFile->Position;
518   return EFI_SUCCESS;
519 }
520 
521 
522 /**
523   Sets a file's current position.
524 
525   @param[in] This      A pointer to the EFI_FILE_PROTOCOL instance that is the
526                        file handle to set the requested position on.
527 
528   @param[in] Position  The byte position from the start of the file to set. For
529                        regular files, MAX_UINT64 means "seek to end". For
530                        directories, zero means "rewind directory scan".
531 
532   @retval EFI_SUCCESS       The position was set.
533   @retval EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
534                             directories.
535   @retval EFI_DEVICE_ERROR  An attempt was made to set the position of a
536                             deleted file.
537 **/
538 STATIC
539 EFI_STATUS
540 EFIAPI
StubFileSetPosition(IN EFI_FILE_PROTOCOL * This,IN UINT64 Position)541 StubFileSetPosition (
542   IN EFI_FILE_PROTOCOL *This,
543   IN UINT64            Position
544   )
545 {
546   STUB_FILE   *StubFile;
547   KERNEL_BLOB *Blob;
548 
549   StubFile = STUB_FILE_FROM_FILE (This);
550 
551   if (StubFile->BlobType == KernelBlobTypeMax) {
552     if (Position == 0) {
553       //
554       // rewinding a directory scan is allowed
555       //
556       StubFile->Position = 0;
557       return EFI_SUCCESS;
558     }
559     return EFI_UNSUPPORTED;
560   }
561 
562   //
563   // regular file seek
564   //
565   Blob = &mKernelBlob[StubFile->BlobType];
566   if (Position == MAX_UINT64) {
567     //
568     // seek to end
569     //
570     StubFile->Position = Blob->Size;
571   } else {
572     //
573     // absolute seek from beginning -- seeking past the end is allowed
574     //
575     StubFile->Position = Position;
576   }
577   return EFI_SUCCESS;
578 }
579 
580 
581 /**
582   Returns information about a file.
583 
584   @param[in]     This             A pointer to the EFI_FILE_PROTOCOL instance
585                                   that is the file handle the requested
586                                   information is for.
587 
588   @param[in]     InformationType  The type identifier GUID for the information
589                                   being requested. The following information
590                                   types are supported, storing the
591                                   corresponding structures in Buffer:
592 
593                                   - gEfiFileInfoGuid: EFI_FILE_INFO
594 
595                                   - gEfiFileSystemInfoGuid:
596                                     EFI_FILE_SYSTEM_INFO
597 
598                                   - gEfiFileSystemVolumeLabelInfoIdGuid:
599                                     EFI_FILE_SYSTEM_VOLUME_LABEL
600 
601   @param[in,out] BufferSize       On input, the size of Buffer. On output, the
602                                   amount of data returned in Buffer. In both
603                                   cases, the size is measured in bytes.
604 
605   @param[out]    Buffer           A pointer to the data buffer to return. The
606                                   buffer's type is indicated by
607                                   InformationType.
608 
609   @retval EFI_SUCCESS           The information was returned.
610   @retval EFI_UNSUPPORTED       The InformationType is not known.
611   @retval EFI_NO_MEDIA          The device has no medium.
612   @retval EFI_DEVICE_ERROR      The device reported an error.
613   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
614   @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
615                                 information structure requested by
616                                 InformationType. BufferSize has been updated
617                                 with the size needed to complete the request.
618 **/
619 STATIC
620 EFI_STATUS
621 EFIAPI
StubFileGetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN OUT UINTN * BufferSize,OUT VOID * Buffer)622 StubFileGetInfo (
623   IN EFI_FILE_PROTOCOL *This,
624   IN EFI_GUID          *InformationType,
625   IN OUT UINTN         *BufferSize,
626   OUT VOID             *Buffer
627   )
628 {
629   CONST STUB_FILE *StubFile;
630   UINTN           OriginalBufferSize;
631 
632   StubFile = STUB_FILE_FROM_FILE (This);
633 
634   if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
635     return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
636              Buffer);
637   }
638 
639   OriginalBufferSize = *BufferSize;
640 
641   if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
642     EFI_FILE_SYSTEM_INFO *FileSystemInfo;
643 
644     *BufferSize = sizeof *FileSystemInfo;
645     if (OriginalBufferSize < *BufferSize) {
646       return EFI_BUFFER_TOO_SMALL;
647     }
648 
649     FileSystemInfo                 = (EFI_FILE_SYSTEM_INFO *)Buffer;
650     FileSystemInfo->Size           = sizeof *FileSystemInfo;
651     FileSystemInfo->ReadOnly       = TRUE;
652     FileSystemInfo->VolumeSize     = mTotalBlobBytes;
653     FileSystemInfo->FreeSpace      = 0;
654     FileSystemInfo->BlockSize      = 1;
655     FileSystemInfo->VolumeLabel[0] = L'\0';
656 
657     return EFI_SUCCESS;
658   }
659 
660   if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
661     EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
662 
663     *BufferSize = sizeof *FileSystemVolumeLabel;
664     if (OriginalBufferSize < *BufferSize) {
665       return EFI_BUFFER_TOO_SMALL;
666     }
667 
668     FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
669     FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
670 
671     return EFI_SUCCESS;
672   }
673 
674   return EFI_UNSUPPORTED;
675 }
676 
677 
678 /**
679   Sets information about a file.
680 
681   @param[in] File             A pointer to the EFI_FILE_PROTOCOL instance that
682                               is the file handle the information is for.
683 
684   @param[in] InformationType  The type identifier for the information being
685                               set.
686 
687   @param[in] BufferSize       The size, in bytes, of Buffer.
688 
689   @param[in] Buffer           A pointer to the data buffer to write. The
690                               buffer's type is indicated by InformationType.
691 
692   @retval EFI_SUCCESS           The information was set.
693   @retval EFI_UNSUPPORTED       The InformationType is not known.
694   @retval EFI_NO_MEDIA          The device has no medium.
695   @retval EFI_DEVICE_ERROR      The device reported an error.
696   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
697   @retval EFI_WRITE_PROTECTED   InformationType is EFI_FILE_INFO_ID and the
698                                 media is read-only.
699   @retval EFI_WRITE_PROTECTED   InformationType is
700                                 EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media
701                                 is read only.
702   @retval EFI_WRITE_PROTECTED   InformationType is
703                                 EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media
704                                 is read-only.
705   @retval EFI_ACCESS_DENIED     An attempt is made to change the name of a file
706                                 to a file that is already present.
707   @retval EFI_ACCESS_DENIED     An attempt is being made to change the
708                                 EFI_FILE_DIRECTORY Attribute.
709   @retval EFI_ACCESS_DENIED     An attempt is being made to change the size of
710                                 a directory.
711   @retval EFI_ACCESS_DENIED     InformationType is EFI_FILE_INFO_ID and the
712                                 file was opened read-only and an attempt is
713                                 being made to modify a field other than
714                                 Attribute.
715   @retval EFI_VOLUME_FULL       The volume is full.
716   @retval EFI_BAD_BUFFER_SIZE   BufferSize is smaller than the size of the type
717                                 indicated by InformationType.
718 **/
719 STATIC
720 EFI_STATUS
721 EFIAPI
StubFileSetInfo(IN EFI_FILE_PROTOCOL * This,IN EFI_GUID * InformationType,IN UINTN BufferSize,IN VOID * Buffer)722 StubFileSetInfo (
723   IN EFI_FILE_PROTOCOL *This,
724   IN EFI_GUID          *InformationType,
725   IN UINTN             BufferSize,
726   IN VOID              *Buffer
727   )
728 {
729   return EFI_WRITE_PROTECTED;
730 }
731 
732 
733 /**
734   Flushes all modified data associated with a file to a device.
735 
736   @param [in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
737                     file handle to flush.
738 
739   @retval EFI_SUCCESS           The data was flushed.
740   @retval EFI_NO_MEDIA          The device has no medium.
741   @retval EFI_DEVICE_ERROR      The device reported an error.
742   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
743   @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
744   @retval EFI_ACCESS_DENIED     The file was opened read-only.
745   @retval EFI_VOLUME_FULL       The volume is full.
746 **/
747 STATIC
748 EFI_STATUS
749 EFIAPI
StubFileFlush(IN EFI_FILE_PROTOCOL * This)750 StubFileFlush (
751   IN EFI_FILE_PROTOCOL *This
752   )
753 {
754   return EFI_WRITE_PROTECTED;
755 }
756 
757 //
758 // External definition of the file protocol template.
759 //
760 STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
761   EFI_FILE_PROTOCOL_REVISION, // revision 1
762   StubFileOpen,
763   StubFileClose,
764   StubFileDelete,
765   StubFileRead,
766   StubFileWrite,
767   StubFileGetPosition,
768   StubFileSetPosition,
769   StubFileGetInfo,
770   StubFileSetInfo,
771   StubFileFlush,
772   NULL,                       // OpenEx, revision 2
773   NULL,                       // ReadEx, revision 2
774   NULL,                       // WriteEx, revision 2
775   NULL                        // FlushEx, revision 2
776 };
777 
778 
779 //
780 // Protocol member functions for SimpleFileSystem.
781 //
782 
783 /**
784   Open the root directory on a volume.
785 
786   @param[in]  This  A pointer to the volume to open the root directory on.
787 
788   @param[out] Root  A pointer to the location to return the opened file handle
789                     for the root directory in.
790 
791   @retval EFI_SUCCESS           The device was opened.
792   @retval EFI_UNSUPPORTED       This volume does not support the requested file
793                                 system type.
794   @retval EFI_NO_MEDIA          The device has no medium.
795   @retval EFI_DEVICE_ERROR      The device reported an error.
796   @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
797   @retval EFI_ACCESS_DENIED     The service denied access to the file.
798   @retval EFI_OUT_OF_RESOURCES  The volume was not opened due to lack of
799                                 resources.
800   @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
801                                 medium is no longer supported. Any existing
802                                 file handles for this volume are no longer
803                                 valid. To access the files on the new medium,
804                                 the volume must be reopened with OpenVolume().
805 **/
806 STATIC
807 EFI_STATUS
808 EFIAPI
StubFileSystemOpenVolume(IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL * This,OUT EFI_FILE_PROTOCOL ** Root)809 StubFileSystemOpenVolume (
810   IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
811   OUT EFI_FILE_PROTOCOL              **Root
812   )
813 {
814   STUB_FILE *StubFile;
815 
816   StubFile = AllocatePool (sizeof *StubFile);
817   if (StubFile == NULL) {
818     return EFI_OUT_OF_RESOURCES;
819   }
820 
821   StubFile->Signature = STUB_FILE_SIG;
822   StubFile->BlobType  = KernelBlobTypeMax;
823   StubFile->Position  = 0;
824   CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
825     sizeof mEfiFileProtocolTemplate);
826   *Root = &StubFile->File;
827 
828   return EFI_SUCCESS;
829 }
830 
831 STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
832   EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
833   StubFileSystemOpenVolume
834 };
835 
836 
837 //
838 // Utility functions.
839 //
840 
841 /**
842   Populate a blob in mKernelBlob.
843 
844   param[in,out] Blob  Pointer to the KERNEL_BLOB element in mKernelBlob that is
845                       to be filled from fw_cfg.
846 
847   @retval EFI_SUCCESS           Blob has been populated. If fw_cfg reported a
848                                 size of zero for the blob, then Blob->Data has
849                                 been left unchanged.
850 
851   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for Blob->Data.
852 **/
853 STATIC
854 EFI_STATUS
FetchBlob(IN OUT KERNEL_BLOB * Blob)855 FetchBlob (
856   IN OUT KERNEL_BLOB *Blob
857   )
858 {
859   UINT32 Left;
860 
861   //
862   // Read blob size.
863   //
864   QemuFwCfgSelectItem (Blob->SizeKey);
865   Blob->Size = QemuFwCfgRead32 ();
866   if (Blob->Size == 0) {
867     return EFI_SUCCESS;
868   }
869 
870   //
871   // Read blob.
872   //
873   Blob->Data = AllocatePool (Blob->Size);
874   if (Blob->Data == NULL) {
875     DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
876       __FUNCTION__, (INT64)Blob->Size, Blob->Name));
877     return EFI_OUT_OF_RESOURCES;
878   }
879 
880   DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
881     (INT64)Blob->Size, Blob->Name));
882   QemuFwCfgSelectItem (Blob->DataKey);
883 
884   Left = Blob->Size;
885   do {
886     UINT32 Chunk;
887 
888     Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
889     QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
890     Left -= Chunk;
891     DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
892       __FUNCTION__, (INT64)Left, Blob->Name));
893   } while (Left > 0);
894   return EFI_SUCCESS;
895 }
896 
897 
898 //
899 // The entry point of the feature.
900 //
901 
902 /**
903   Download the kernel, the initial ramdisk, and the kernel command line from
904   QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
905   image files, and load and start the kernel from it.
906 
907   The kernel will be instructed via its command line to load the initrd from
908   the same Simple FileSystem.
909 
910   @retval EFI_NOT_FOUND         Kernel image was not found.
911   @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
912   @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
913 
914   @return                       Error codes from any of the underlying
915                                 functions. On success, the function doesn't
916                                 return.
917 **/
918 EFI_STATUS
919 EFIAPI
TryRunningQemuKernel(VOID)920 TryRunningQemuKernel (
921   VOID
922   )
923 {
924   UINTN                     BlobType;
925   KERNEL_BLOB               *CurrentBlob;
926   KERNEL_BLOB               *KernelBlob, *InitrdBlob, *CommandLineBlob;
927   EFI_STATUS                Status;
928   EFI_HANDLE                FileSystemHandle;
929   EFI_DEVICE_PATH_PROTOCOL  *KernelDevicePath;
930   EFI_HANDLE                KernelImageHandle;
931   EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
932 
933   Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
934   if (EFI_ERROR (Status)) {
935     DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
936     return Status;
937   }
938 
939   //
940   // Fetch all blobs.
941   //
942   for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
943     CurrentBlob = &mKernelBlob[BlobType];
944     Status = FetchBlob (CurrentBlob);
945     if (EFI_ERROR (Status)) {
946       goto FreeBlobs;
947     }
948     mTotalBlobBytes += CurrentBlob->Size;
949   }
950   KernelBlob      = &mKernelBlob[KernelBlobTypeKernel];
951   InitrdBlob      = &mKernelBlob[KernelBlobTypeInitrd];
952   CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];
953 
954   if (KernelBlob->Data == NULL) {
955     Status = EFI_NOT_FOUND;
956     goto FreeBlobs;
957   }
958 
959   //
960   // Create a new handle with a single VenHw() node device path protocol on it,
961   // plus a custom SimpleFileSystem protocol on it.
962   //
963   FileSystemHandle = NULL;
964   Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
965                   &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
966                   &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
967                   NULL);
968   if (EFI_ERROR (Status)) {
969     DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
970       __FUNCTION__, Status));
971     goto FreeBlobs;
972   }
973 
974   //
975   // Create a device path for the kernel image to be loaded from that will call
976   // back into our file system.
977   //
978   KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);
979   if (KernelDevicePath == NULL) {
980     DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",
981       __FUNCTION__));
982     Status = EFI_OUT_OF_RESOURCES;
983     goto UninstallProtocols;
984   }
985 
986   //
987   // Load the image. This should call back into our file system.
988   //
989   Status = gBS->LoadImage (
990                   FALSE,             // BootPolicy: exact match required
991                   gImageHandle,      // ParentImageHandle
992                   KernelDevicePath,
993                   NULL,              // SourceBuffer
994                   0,                 // SourceSize
995                   &KernelImageHandle
996                   );
997   if (EFI_ERROR (Status)) {
998     DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
999     goto FreeKernelDevicePath;
1000   }
1001 
1002   //
1003   // Construct the kernel command line.
1004   //
1005   Status = gBS->OpenProtocol (
1006                   KernelImageHandle,
1007                   &gEfiLoadedImageProtocolGuid,
1008                   (VOID **)&KernelLoadedImage,
1009                   gImageHandle,                  // AgentHandle
1010                   NULL,                          // ControllerHandle
1011                   EFI_OPEN_PROTOCOL_GET_PROTOCOL
1012                   );
1013   ASSERT_EFI_ERROR (Status);
1014 
1015   if (CommandLineBlob->Data == NULL) {
1016     KernelLoadedImage->LoadOptionsSize = 0;
1017   } else {
1018     //
1019     // Verify NUL-termination of the command line.
1020     //
1021     if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {
1022       DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",
1023         __FUNCTION__));
1024       Status = EFI_PROTOCOL_ERROR;
1025       goto UnloadKernelImage;
1026     }
1027 
1028     //
1029     // Drop the terminating NUL, convert to UTF-16.
1030     //
1031     KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;
1032   }
1033 
1034   if (InitrdBlob->Data != NULL) {
1035     //
1036     // Append ' initrd=<name>' in UTF-16.
1037     //
1038     KernelLoadedImage->LoadOptionsSize +=
1039                                         (8 + StrLen(InitrdBlob->Name)) * 2;
1040   }
1041 
1042   if (KernelLoadedImage->LoadOptionsSize == 0) {
1043     KernelLoadedImage->LoadOptions = NULL;
1044   } else {
1045     //
1046     // NUL-terminate in UTF-16.
1047     //
1048     KernelLoadedImage->LoadOptionsSize += 2;
1049 
1050     KernelLoadedImage->LoadOptions = AllocatePool (
1051                                        KernelLoadedImage->LoadOptionsSize);
1052     if (KernelLoadedImage->LoadOptions == NULL) {
1053       KernelLoadedImage->LoadOptionsSize = 0;
1054       Status = EFI_OUT_OF_RESOURCES;
1055       goto UnloadKernelImage;
1056     }
1057 
1058     UnicodeSPrintAsciiFormat (
1059       KernelLoadedImage->LoadOptions,
1060       KernelLoadedImage->LoadOptionsSize,
1061       "%a%a%s",
1062       (CommandLineBlob->Data == NULL) ?  "" : (CHAR8 *)CommandLineBlob->Data,
1063       (InitrdBlob->Data      == NULL) ?  "" : " initrd=",
1064       (InitrdBlob->Data      == NULL) ? L"" : InitrdBlob->Name
1065       );
1066     DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
1067       (CHAR16 *)KernelLoadedImage->LoadOptions));
1068   }
1069 
1070   //
1071   // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
1072   //
1073   EfiSignalEventReadyToBoot();
1074 
1075   //
1076   // Start the image.
1077   //
1078   Status = gBS->StartImage (
1079                 KernelImageHandle,
1080                 NULL,              // ExitDataSize
1081                 NULL               // ExitData
1082                 );
1083   if (EFI_ERROR (Status)) {
1084     DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
1085   }
1086 
1087   if (KernelLoadedImage->LoadOptions != NULL) {
1088     FreePool (KernelLoadedImage->LoadOptions);
1089   }
1090   KernelLoadedImage->LoadOptionsSize = 0;
1091 
1092 UnloadKernelImage:
1093   gBS->UnloadImage (KernelImageHandle);
1094 
1095 FreeKernelDevicePath:
1096   FreePool (KernelDevicePath);
1097 
1098 UninstallProtocols:
1099   gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
1100          &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
1101          &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
1102          NULL);
1103 
1104 FreeBlobs:
1105   while (BlobType > 0) {
1106     CurrentBlob = &mKernelBlob[--BlobType];
1107     if (CurrentBlob->Data != NULL) {
1108       FreePool (CurrentBlob->Data);
1109       CurrentBlob->Size = 0;
1110       CurrentBlob->Data = NULL;
1111     }
1112   }
1113 
1114   return Status;
1115 }
1116