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//
32typedef enum {
33  KernelBlobTypeKernel,
34  KernelBlobTypeInitrd,
35  KernelBlobTypeCommandLine,
36  KernelBlobTypeMax
37} KERNEL_BLOB_TYPE;
38
39typedef 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
47STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
48  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
49  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
50  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
51};
52
53STATIC 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)
60typedef struct {
61  VENDOR_DEVICE_PATH       VenHwNode;
62  EFI_DEVICE_PATH_PROTOCOL EndNode;
63} SINGLE_VENHW_NODE_DEVPATH;
64#pragma pack()
65
66STATIC 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//
84STATIC EFI_TIME mInitTime;
85
86#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')
87
88typedef 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//
108STATIC 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**/
153STATIC
154EFI_STATUS
155EFIAPI
156StubFileOpen (
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**/
230STATIC
231EFI_STATUS
232EFIAPI
233StubFileClose (
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**/
254STATIC
255EFI_STATUS
256EFIAPI
257StubFileDelete (
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**/
294STATIC
295EFI_STATUS
296ConvertKernelBlobTypeToFileInfo (
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**/
386STATIC
387EFI_STATUS
388EFIAPI
389StubFileRead (
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**/
470STATIC
471EFI_STATUS
472EFIAPI
473StubFileWrite (
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**/
502STATIC
503EFI_STATUS
504EFIAPI
505StubFileGetPosition (
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**/
538STATIC
539EFI_STATUS
540EFIAPI
541StubFileSetPosition (
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**/
619STATIC
620EFI_STATUS
621EFIAPI
622StubFileGetInfo (
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**/
719STATIC
720EFI_STATUS
721EFIAPI
722StubFileSetInfo (
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**/
747STATIC
748EFI_STATUS
749EFIAPI
750StubFileFlush (
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//
760STATIC 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**/
806STATIC
807EFI_STATUS
808EFIAPI
809StubFileSystemOpenVolume (
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
831STATIC 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**/
853STATIC
854EFI_STATUS
855FetchBlob (
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**/
918EFI_STATUS
919EFIAPI
920TryRunningQemuKernel (
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
1092UnloadKernelImage:
1093  gBS->UnloadImage (KernelImageHandle);
1094
1095FreeKernelDevicePath:
1096  FreePool (KernelDevicePath);
1097
1098UninstallProtocols:
1099  gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
1100         &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
1101         &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
1102         NULL);
1103
1104FreeBlobs:
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