Home | History | Annotate | Download | only in PlatformIntelBdsLib
      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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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