Create a copy of PlatformIntelBdsLib under the name
PlatformBootManagerLib, with the following initial changes:
- replace PlatformBdsLib references with PlatformBootManagerLib in
  comments,
- replace "IntelBdsPlatform" with "PlatformBm" in file names and their
  references,
- generate a new FILE_GUID.
PlatformBootManagerLib will be linked into the BDS driver from
MdeModulePkg.
This patch parallels OvmfPkg commit 3054188189.
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Reviewed-by: Ruiyu Ni <ruiyu.ni@Intel.com>
		
	
		
			
				
	
	
		
			1116 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1116 lines
		
	
	
		
			35 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/** @file
 | 
						|
  Try to load an EFI-stubbed ARM Linux kernel from QEMU's fw_cfg.
 | 
						|
 | 
						|
  This implementation differs from OvmfPkg/Library/LoadLinuxLib. An EFI
 | 
						|
  stub in the subject kernel is a hard requirement here.
 | 
						|
 | 
						|
  Copyright (C) 2014-2016, Red Hat, Inc.
 | 
						|
 | 
						|
  This program and the accompanying materials are licensed and made available
 | 
						|
  under the terms and conditions of the BSD License which accompanies this
 | 
						|
  distribution.  The full text of the license may be found at
 | 
						|
  http://opensource.org/licenses/bsd-license.php
 | 
						|
 | 
						|
  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
 | 
						|
  WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | 
						|
**/
 | 
						|
 | 
						|
#include <Guid/FileInfo.h>
 | 
						|
#include <Guid/FileSystemInfo.h>
 | 
						|
#include <Guid/FileSystemVolumeLabelInfo.h>
 | 
						|
#include <Library/PrintLib.h>
 | 
						|
#include <Library/QemuFwCfgLib.h>
 | 
						|
#include <Protocol/DevicePath.h>
 | 
						|
#include <Protocol/LoadedImage.h>
 | 
						|
#include <Protocol/SimpleFileSystem.h>
 | 
						|
 | 
						|
#include "PlatformBm.h"
 | 
						|
 | 
						|
//
 | 
						|
// Static data that hosts the fw_cfg blobs and serves file requests.
 | 
						|
//
 | 
						|
typedef enum {
 | 
						|
  KernelBlobTypeKernel,
 | 
						|
  KernelBlobTypeInitrd,
 | 
						|
  KernelBlobTypeCommandLine,
 | 
						|
  KernelBlobTypeMax
 | 
						|
} KERNEL_BLOB_TYPE;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  FIRMWARE_CONFIG_ITEM CONST SizeKey;
 | 
						|
  FIRMWARE_CONFIG_ITEM CONST DataKey;
 | 
						|
  CONST CHAR16 *       CONST Name;
 | 
						|
  UINT32                     Size;
 | 
						|
  UINT8                      *Data;
 | 
						|
} KERNEL_BLOB;
 | 
						|
 | 
						|
STATIC KERNEL_BLOB mKernelBlob[KernelBlobTypeMax] = {
 | 
						|
  { QemuFwCfgItemKernelSize,      QemuFwCfgItemKernelData,      L"kernel"  },
 | 
						|
  { QemuFwCfgItemInitrdSize,      QemuFwCfgItemInitrdData,      L"initrd"  },
 | 
						|
  { QemuFwCfgItemCommandLineSize, QemuFwCfgItemCommandLineData, L"cmdline" }
 | 
						|
};
 | 
						|
 | 
						|
STATIC UINT64 mTotalBlobBytes;
 | 
						|
 | 
						|
//
 | 
						|
// Device path for the handle that incorporates our "EFI stub filesystem". The
 | 
						|
// GUID is arbitrary and need not be standardized or advertized.
 | 
						|
//
 | 
						|
#pragma pack(1)
 | 
						|
typedef struct {
 | 
						|
  VENDOR_DEVICE_PATH       VenHwNode;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL EndNode;
 | 
						|
} SINGLE_VENHW_NODE_DEVPATH;
 | 
						|
#pragma pack()
 | 
						|
 | 
						|
STATIC CONST SINGLE_VENHW_NODE_DEVPATH mFileSystemDevicePath = {
 | 
						|
  {
 | 
						|
    { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH) } },
 | 
						|
    {
 | 
						|
      0xb0fae7e7, 0x6b07, 0x49d0,
 | 
						|
      { 0x9e, 0x5b, 0x3b, 0xde, 0xc8, 0x3b, 0x03, 0x9d }
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  {
 | 
						|
    END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE,
 | 
						|
    { sizeof (EFI_DEVICE_PATH_PROTOCOL) }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
//
 | 
						|
// The "file in the EFI stub filesystem" abstraction.
 | 
						|
//
 | 
						|
STATIC EFI_TIME mInitTime;
 | 
						|
 | 
						|
#define STUB_FILE_SIG SIGNATURE_64 ('S', 'T', 'U', 'B', 'F', 'I', 'L', 'E')
 | 
						|
 | 
						|
typedef struct {
 | 
						|
  UINT64            Signature; // Carries STUB_FILE_SIG.
 | 
						|
 | 
						|
  KERNEL_BLOB_TYPE  BlobType;  // Index into mKernelBlob. KernelBlobTypeMax
 | 
						|
                               // denotes the root directory of the filesystem.
 | 
						|
 | 
						|
  UINT64            Position;  // Byte position for regular files;
 | 
						|
                               // next directory entry to return for the root
 | 
						|
                               // directory.
 | 
						|
 | 
						|
  EFI_FILE_PROTOCOL File;      // Standard protocol interface.
 | 
						|
} STUB_FILE;
 | 
						|
 | 
						|
#define STUB_FILE_FROM_FILE(FilePointer) \
 | 
						|
        CR (FilePointer, STUB_FILE, File, STUB_FILE_SIG)
 | 
						|
 | 
						|
//
 | 
						|
// Tentative definition of the file protocol template. The initializer
 | 
						|
// (external definition) will be provided later.
 | 
						|
//
 | 
						|
STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate;
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// Protocol member functions for File.
 | 
						|
//
 | 
						|
 | 
						|
/**
 | 
						|
  Opens a new file relative to the source file's location.
 | 
						|
 | 
						|
  @param[in]  This        A pointer to the EFI_FILE_PROTOCOL instance that is
 | 
						|
                          the file handle to the source location. This would
 | 
						|
                          typically be an open handle to a directory.
 | 
						|
 | 
						|
  @param[out] NewHandle   A pointer to the location to return the opened handle
 | 
						|
                          for the new file.
 | 
						|
 | 
						|
  @param[in]  FileName    The Null-terminated string of the name of the file to
 | 
						|
                          be opened. The file name may contain the following
 | 
						|
                          path modifiers: "\", ".", and "..".
 | 
						|
 | 
						|
  @param[in]  OpenMode    The mode to open the file. The only valid
 | 
						|
                          combinations that the file may be opened with are:
 | 
						|
                          Read, Read/Write, or Create/Read/Write.
 | 
						|
 | 
						|
  @param[in]  Attributes  Only valid for EFI_FILE_MODE_CREATE, in which case
 | 
						|
                          these are the attribute bits for the newly created
 | 
						|
                          file.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The file was opened.
 | 
						|
  @retval EFI_NOT_FOUND         The specified file could not be found on the
 | 
						|
                                device.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
 | 
						|
                                medium is no longer supported.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_WRITE_PROTECTED   An attempt was made to create a file, or open a
 | 
						|
                                file for write when the media is
 | 
						|
                                write-protected.
 | 
						|
  @retval EFI_ACCESS_DENIED     The service denied access to the file.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Not enough resources were available to open the
 | 
						|
                                file.
 | 
						|
  @retval EFI_VOLUME_FULL       The volume is full.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileOpen (
 | 
						|
  IN EFI_FILE_PROTOCOL  *This,
 | 
						|
  OUT EFI_FILE_PROTOCOL **NewHandle,
 | 
						|
  IN CHAR16             *FileName,
 | 
						|
  IN UINT64             OpenMode,
 | 
						|
  IN UINT64             Attributes
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST STUB_FILE *StubFile;
 | 
						|
  UINTN           BlobType;
 | 
						|
  STUB_FILE       *NewStubFile;
 | 
						|
 | 
						|
  //
 | 
						|
  // We're read-only.
 | 
						|
  //
 | 
						|
  switch (OpenMode) {
 | 
						|
    case EFI_FILE_MODE_READ:
 | 
						|
      break;
 | 
						|
 | 
						|
    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE:
 | 
						|
    case EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE:
 | 
						|
      return EFI_WRITE_PROTECTED;
 | 
						|
 | 
						|
    default:
 | 
						|
      return EFI_INVALID_PARAMETER;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Only the root directory supports opening files in it.
 | 
						|
  //
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
  if (StubFile->BlobType != KernelBlobTypeMax) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Locate the file.
 | 
						|
  //
 | 
						|
  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
 | 
						|
    if (StrCmp (FileName, mKernelBlob[BlobType].Name) == 0) {
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
  if (BlobType == KernelBlobTypeMax) {
 | 
						|
    return EFI_NOT_FOUND;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Found it.
 | 
						|
  //
 | 
						|
  NewStubFile = AllocatePool (sizeof *NewStubFile);
 | 
						|
  if (NewStubFile == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  NewStubFile->Signature = STUB_FILE_SIG;
 | 
						|
  NewStubFile->BlobType  = (KERNEL_BLOB_TYPE)BlobType;
 | 
						|
  NewStubFile->Position  = 0;
 | 
						|
  CopyMem (&NewStubFile->File, &mEfiFileProtocolTemplate,
 | 
						|
    sizeof mEfiFileProtocolTemplate);
 | 
						|
  *NewHandle = &NewStubFile->File;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Closes a specified file handle.
 | 
						|
 | 
						|
  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the file
 | 
						|
                   handle to close.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS  The file was closed.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileClose (
 | 
						|
  IN EFI_FILE_PROTOCOL *This
 | 
						|
  )
 | 
						|
{
 | 
						|
  FreePool (STUB_FILE_FROM_FILE (This));
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Close and delete the file handle.
 | 
						|
 | 
						|
  @param[in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
 | 
						|
                   handle to the file to delete.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS              The file was closed and deleted, and the
 | 
						|
                                   handle was closed.
 | 
						|
  @retval EFI_WARN_DELETE_FAILURE  The handle was closed, but the file was not
 | 
						|
                                   deleted.
 | 
						|
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileDelete (
 | 
						|
  IN EFI_FILE_PROTOCOL *This
 | 
						|
  )
 | 
						|
{
 | 
						|
  FreePool (STUB_FILE_FROM_FILE (This));
 | 
						|
  return EFI_WARN_DELETE_FAILURE;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Helper function that formats an EFI_FILE_INFO structure into the
 | 
						|
  user-allocated buffer, for any valid KERNEL_BLOB_TYPE value (including
 | 
						|
  KernelBlobTypeMax, which stands for the root directory).
 | 
						|
 | 
						|
  The interface follows the EFI_FILE_GET_INFO -- and for directories, the
 | 
						|
  EFI_FILE_READ -- interfaces.
 | 
						|
 | 
						|
  @param[in]     BlobType     The KERNEL_BLOB_TYPE value identifying the fw_cfg
 | 
						|
                              blob backing the STUB_FILE that information is
 | 
						|
                              being requested about. If BlobType equals
 | 
						|
                              KernelBlobTypeMax, then information will be
 | 
						|
                              provided about the root directory of the
 | 
						|
                              filesystem.
 | 
						|
 | 
						|
  @param[in,out] BufferSize  On input, the size of Buffer. On output, the
 | 
						|
                             amount of data returned in Buffer. In both cases,
 | 
						|
                             the size is measured in bytes.
 | 
						|
 | 
						|
  @param[out]    Buffer      A pointer to the data buffer to return. The
 | 
						|
                             buffer's type is EFI_FILE_INFO.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The information was returned.
 | 
						|
  @retval EFI_BUFFER_TOO_SMALL  BufferSize is too small to store the
 | 
						|
                                EFI_FILE_INFO structure. BufferSize has been
 | 
						|
                                updated with the size needed to complete the
 | 
						|
                                request.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
ConvertKernelBlobTypeToFileInfo (
 | 
						|
  IN KERNEL_BLOB_TYPE BlobType,
 | 
						|
  IN OUT UINTN        *BufferSize,
 | 
						|
  OUT VOID            *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST CHAR16  *Name;
 | 
						|
  UINT64        FileSize;
 | 
						|
  UINT64        Attribute;
 | 
						|
 | 
						|
  UINTN         NameSize;
 | 
						|
  UINTN         FileInfoSize;
 | 
						|
  EFI_FILE_INFO *FileInfo;
 | 
						|
  UINTN         OriginalBufferSize;
 | 
						|
 | 
						|
  if (BlobType == KernelBlobTypeMax) {
 | 
						|
    //
 | 
						|
    // getting file info about the root directory
 | 
						|
    //
 | 
						|
    Name      = L"\\";
 | 
						|
    FileSize  = KernelBlobTypeMax;
 | 
						|
    Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY;
 | 
						|
  } else {
 | 
						|
    CONST KERNEL_BLOB *Blob;
 | 
						|
 | 
						|
    Blob      = &mKernelBlob[BlobType];
 | 
						|
    Name      = Blob->Name;
 | 
						|
    FileSize  = Blob->Size;
 | 
						|
    Attribute = EFI_FILE_READ_ONLY;
 | 
						|
  }
 | 
						|
 | 
						|
  NameSize     = (StrLen(Name) + 1) * 2;
 | 
						|
  FileInfoSize = OFFSET_OF (EFI_FILE_INFO, FileName) + NameSize;
 | 
						|
  ASSERT (FileInfoSize >= sizeof *FileInfo);
 | 
						|
 | 
						|
  OriginalBufferSize = *BufferSize;
 | 
						|
  *BufferSize        = FileInfoSize;
 | 
						|
  if (OriginalBufferSize < *BufferSize) {
 | 
						|
    return EFI_BUFFER_TOO_SMALL;
 | 
						|
  }
 | 
						|
 | 
						|
  FileInfo               = (EFI_FILE_INFO *)Buffer;
 | 
						|
  FileInfo->Size         = FileInfoSize;
 | 
						|
  FileInfo->FileSize     = FileSize;
 | 
						|
  FileInfo->PhysicalSize = FileSize;
 | 
						|
  FileInfo->Attribute    = Attribute;
 | 
						|
 | 
						|
  CopyMem (&FileInfo->CreateTime,       &mInitTime, sizeof mInitTime);
 | 
						|
  CopyMem (&FileInfo->LastAccessTime,   &mInitTime, sizeof mInitTime);
 | 
						|
  CopyMem (&FileInfo->ModificationTime, &mInitTime, sizeof mInitTime);
 | 
						|
  CopyMem (FileInfo->FileName,          Name,       NameSize);
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Reads data from a file, or continues scanning a directory.
 | 
						|
 | 
						|
  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
 | 
						|
                             is the file handle to read data from.
 | 
						|
 | 
						|
  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
 | 
						|
                             amount of data returned in Buffer. In both cases,
 | 
						|
                             the size is measured in bytes. If the read goes
 | 
						|
                             beyond the end of the file, the read length is
 | 
						|
                             truncated to the end of the file.
 | 
						|
 | 
						|
                             If This is a directory, the function reads the
 | 
						|
                             directory entry at the current position and
 | 
						|
                             returns the entry (as EFI_FILE_INFO) in Buffer. If
 | 
						|
                             there are no more directory entries, the
 | 
						|
                             BufferSize is set to zero on output.
 | 
						|
 | 
						|
  @param[out]    Buffer      The buffer into which the data is read.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Data was read.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_DEVICE_ERROR      An attempt was made to read from a deleted
 | 
						|
                                file.
 | 
						|
  @retval EFI_DEVICE_ERROR      On entry, the current file position is beyond
 | 
						|
                                the end of the file.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
 | 
						|
                                current directory entry as a EFI_FILE_INFO
 | 
						|
                                structure. BufferSize has been updated with the
 | 
						|
                                size needed to complete the request, and the
 | 
						|
                                directory position has not been advanced.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileRead (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  IN OUT UINTN         *BufferSize,
 | 
						|
  OUT VOID             *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  STUB_FILE         *StubFile;
 | 
						|
  CONST KERNEL_BLOB *Blob;
 | 
						|
  UINT64            Left;
 | 
						|
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
 | 
						|
  //
 | 
						|
  // Scanning the root directory?
 | 
						|
  //
 | 
						|
  if (StubFile->BlobType == KernelBlobTypeMax) {
 | 
						|
    EFI_STATUS Status;
 | 
						|
 | 
						|
    if (StubFile->Position == KernelBlobTypeMax) {
 | 
						|
      //
 | 
						|
      // Scanning complete.
 | 
						|
      //
 | 
						|
      *BufferSize = 0;
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
 | 
						|
    Status = ConvertKernelBlobTypeToFileInfo (
 | 
						|
               (KERNEL_BLOB_TYPE)StubFile->Position,
 | 
						|
               BufferSize,
 | 
						|
               Buffer);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      return Status;
 | 
						|
    }
 | 
						|
 | 
						|
    ++StubFile->Position;
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Reading a file.
 | 
						|
  //
 | 
						|
  Blob = &mKernelBlob[StubFile->BlobType];
 | 
						|
  if (StubFile->Position > Blob->Size) {
 | 
						|
    return EFI_DEVICE_ERROR;
 | 
						|
  }
 | 
						|
 | 
						|
  Left = Blob->Size - StubFile->Position;
 | 
						|
  if (*BufferSize > Left) {
 | 
						|
    *BufferSize = (UINTN)Left;
 | 
						|
  }
 | 
						|
  if (Blob->Data != NULL) {
 | 
						|
    CopyMem (Buffer, Blob->Data + StubFile->Position, *BufferSize);
 | 
						|
  }
 | 
						|
  StubFile->Position += *BufferSize;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Writes data to a file.
 | 
						|
 | 
						|
  @param[in]     This        A pointer to the EFI_FILE_PROTOCOL instance that
 | 
						|
                             is the file handle to write data to.
 | 
						|
 | 
						|
  @param[in,out] BufferSize  On input, the size of the Buffer. On output, the
 | 
						|
                             amount of data actually written. In both cases,
 | 
						|
                             the size is measured in bytes.
 | 
						|
 | 
						|
  @param[in]     Buffer      The buffer of data to write.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Data was written.
 | 
						|
  @retval EFI_UNSUPPORTED       Writes to open directory files are not
 | 
						|
                                supported.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_DEVICE_ERROR      An attempt was made to write to a deleted file.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
 | 
						|
  @retval EFI_ACCESS_DENIED     The file was opened read only.
 | 
						|
  @retval EFI_VOLUME_FULL       The volume is full.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileWrite (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  IN OUT UINTN         *BufferSize,
 | 
						|
  IN VOID              *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  STUB_FILE *StubFile;
 | 
						|
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
  return (StubFile->BlobType == KernelBlobTypeMax) ?
 | 
						|
         EFI_UNSUPPORTED :
 | 
						|
         EFI_WRITE_PROTECTED;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Returns a file's current position.
 | 
						|
 | 
						|
  @param[in]  This      A pointer to the EFI_FILE_PROTOCOL instance that is the
 | 
						|
                        file handle to get the current position on.
 | 
						|
 | 
						|
  @param[out] Position  The address to return the file's current position
 | 
						|
                        value.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS      The position was returned.
 | 
						|
  @retval EFI_UNSUPPORTED  The request is not valid on open directories.
 | 
						|
  @retval EFI_DEVICE_ERROR An attempt was made to get the position from a
 | 
						|
                           deleted file.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileGetPosition (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  OUT UINT64           *Position
 | 
						|
  )
 | 
						|
{
 | 
						|
  STUB_FILE *StubFile;
 | 
						|
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
  if (StubFile->BlobType == KernelBlobTypeMax) {
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  *Position = StubFile->Position;
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Sets a file's current position.
 | 
						|
 | 
						|
  @param[in] This      A pointer to the EFI_FILE_PROTOCOL instance that is the
 | 
						|
                       file handle to set the requested position on.
 | 
						|
 | 
						|
  @param[in] Position  The byte position from the start of the file to set. For
 | 
						|
                       regular files, MAX_UINT64 means "seek to end". For
 | 
						|
                       directories, zero means "rewind directory scan".
 | 
						|
 | 
						|
  @retval EFI_SUCCESS       The position was set.
 | 
						|
  @retval EFI_UNSUPPORTED   The seek request for nonzero is not valid on open
 | 
						|
                            directories.
 | 
						|
  @retval EFI_DEVICE_ERROR  An attempt was made to set the position of a
 | 
						|
                            deleted file.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileSetPosition (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  IN UINT64            Position
 | 
						|
  )
 | 
						|
{
 | 
						|
  STUB_FILE   *StubFile;
 | 
						|
  KERNEL_BLOB *Blob;
 | 
						|
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
 | 
						|
  if (StubFile->BlobType == KernelBlobTypeMax) {
 | 
						|
    if (Position == 0) {
 | 
						|
      //
 | 
						|
      // rewinding a directory scan is allowed
 | 
						|
      //
 | 
						|
      StubFile->Position = 0;
 | 
						|
      return EFI_SUCCESS;
 | 
						|
    }
 | 
						|
    return EFI_UNSUPPORTED;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // regular file seek
 | 
						|
  //
 | 
						|
  Blob = &mKernelBlob[StubFile->BlobType];
 | 
						|
  if (Position == MAX_UINT64) {
 | 
						|
    //
 | 
						|
    // seek to end
 | 
						|
    //
 | 
						|
    StubFile->Position = Blob->Size;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // absolute seek from beginning -- seeking past the end is allowed
 | 
						|
    //
 | 
						|
    StubFile->Position = Position;
 | 
						|
  }
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Returns information about a file.
 | 
						|
 | 
						|
  @param[in]     This             A pointer to the EFI_FILE_PROTOCOL instance
 | 
						|
                                  that is the file handle the requested
 | 
						|
                                  information is for.
 | 
						|
 | 
						|
  @param[in]     InformationType  The type identifier GUID for the information
 | 
						|
                                  being requested. The following information
 | 
						|
                                  types are supported, storing the
 | 
						|
                                  corresponding structures in Buffer:
 | 
						|
 | 
						|
                                  - gEfiFileInfoGuid: EFI_FILE_INFO
 | 
						|
 | 
						|
                                  - gEfiFileSystemInfoGuid:
 | 
						|
                                    EFI_FILE_SYSTEM_INFO
 | 
						|
 | 
						|
                                  - gEfiFileSystemVolumeLabelInfoIdGuid:
 | 
						|
                                    EFI_FILE_SYSTEM_VOLUME_LABEL
 | 
						|
 | 
						|
  @param[in,out] BufferSize       On input, the size of Buffer. On output, the
 | 
						|
                                  amount of data returned in Buffer. In both
 | 
						|
                                  cases, the size is measured in bytes.
 | 
						|
 | 
						|
  @param[out]    Buffer           A pointer to the data buffer to return. The
 | 
						|
                                  buffer's type is indicated by
 | 
						|
                                  InformationType.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The information was returned.
 | 
						|
  @retval EFI_UNSUPPORTED       The InformationType is not known.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small to store the
 | 
						|
                                information structure requested by
 | 
						|
                                InformationType. BufferSize has been updated
 | 
						|
                                with the size needed to complete the request.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileGetInfo (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  IN EFI_GUID          *InformationType,
 | 
						|
  IN OUT UINTN         *BufferSize,
 | 
						|
  OUT VOID             *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  CONST STUB_FILE *StubFile;
 | 
						|
  UINTN           OriginalBufferSize;
 | 
						|
 | 
						|
  StubFile = STUB_FILE_FROM_FILE (This);
 | 
						|
 | 
						|
  if (CompareGuid (InformationType, &gEfiFileInfoGuid)) {
 | 
						|
    return ConvertKernelBlobTypeToFileInfo (StubFile->BlobType, BufferSize,
 | 
						|
             Buffer);
 | 
						|
  }
 | 
						|
 | 
						|
  OriginalBufferSize = *BufferSize;
 | 
						|
 | 
						|
  if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) {
 | 
						|
    EFI_FILE_SYSTEM_INFO *FileSystemInfo;
 | 
						|
 | 
						|
    *BufferSize = sizeof *FileSystemInfo;
 | 
						|
    if (OriginalBufferSize < *BufferSize) {
 | 
						|
      return EFI_BUFFER_TOO_SMALL;
 | 
						|
    }
 | 
						|
 | 
						|
    FileSystemInfo                 = (EFI_FILE_SYSTEM_INFO *)Buffer;
 | 
						|
    FileSystemInfo->Size           = sizeof *FileSystemInfo;
 | 
						|
    FileSystemInfo->ReadOnly       = TRUE;
 | 
						|
    FileSystemInfo->VolumeSize     = mTotalBlobBytes;
 | 
						|
    FileSystemInfo->FreeSpace      = 0;
 | 
						|
    FileSystemInfo->BlockSize      = 1;
 | 
						|
    FileSystemInfo->VolumeLabel[0] = L'\0';
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) {
 | 
						|
    EFI_FILE_SYSTEM_VOLUME_LABEL *FileSystemVolumeLabel;
 | 
						|
 | 
						|
    *BufferSize = sizeof *FileSystemVolumeLabel;
 | 
						|
    if (OriginalBufferSize < *BufferSize) {
 | 
						|
      return EFI_BUFFER_TOO_SMALL;
 | 
						|
    }
 | 
						|
 | 
						|
    FileSystemVolumeLabel = (EFI_FILE_SYSTEM_VOLUME_LABEL *)Buffer;
 | 
						|
    FileSystemVolumeLabel->VolumeLabel[0] = L'\0';
 | 
						|
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  return EFI_UNSUPPORTED;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Sets information about a file.
 | 
						|
 | 
						|
  @param[in] File             A pointer to the EFI_FILE_PROTOCOL instance that
 | 
						|
                              is the file handle the information is for.
 | 
						|
 | 
						|
  @param[in] InformationType  The type identifier for the information being
 | 
						|
                              set.
 | 
						|
 | 
						|
  @param[in] BufferSize       The size, in bytes, of Buffer.
 | 
						|
 | 
						|
  @param[in] Buffer           A pointer to the data buffer to write. The
 | 
						|
                              buffer's type is indicated by InformationType.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The information was set.
 | 
						|
  @retval EFI_UNSUPPORTED       The InformationType is not known.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_WRITE_PROTECTED   InformationType is EFI_FILE_INFO_ID and the
 | 
						|
                                media is read-only.
 | 
						|
  @retval EFI_WRITE_PROTECTED   InformationType is
 | 
						|
                                EFI_FILE_PROTOCOL_SYSTEM_INFO_ID and the media
 | 
						|
                                is read only.
 | 
						|
  @retval EFI_WRITE_PROTECTED   InformationType is
 | 
						|
                                EFI_FILE_SYSTEM_VOLUME_LABEL_ID and the media
 | 
						|
                                is read-only.
 | 
						|
  @retval EFI_ACCESS_DENIED     An attempt is made to change the name of a file
 | 
						|
                                to a file that is already present.
 | 
						|
  @retval EFI_ACCESS_DENIED     An attempt is being made to change the
 | 
						|
                                EFI_FILE_DIRECTORY Attribute.
 | 
						|
  @retval EFI_ACCESS_DENIED     An attempt is being made to change the size of
 | 
						|
                                a directory.
 | 
						|
  @retval EFI_ACCESS_DENIED     InformationType is EFI_FILE_INFO_ID and the
 | 
						|
                                file was opened read-only and an attempt is
 | 
						|
                                being made to modify a field other than
 | 
						|
                                Attribute.
 | 
						|
  @retval EFI_VOLUME_FULL       The volume is full.
 | 
						|
  @retval EFI_BAD_BUFFER_SIZE   BufferSize is smaller than the size of the type
 | 
						|
                                indicated by InformationType.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileSetInfo (
 | 
						|
  IN EFI_FILE_PROTOCOL *This,
 | 
						|
  IN EFI_GUID          *InformationType,
 | 
						|
  IN UINTN             BufferSize,
 | 
						|
  IN VOID              *Buffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  return EFI_WRITE_PROTECTED;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Flushes all modified data associated with a file to a device.
 | 
						|
 | 
						|
  @param [in] This  A pointer to the EFI_FILE_PROTOCOL instance that is the
 | 
						|
                    file handle to flush.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The data was flushed.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_WRITE_PROTECTED   The file or medium is write-protected.
 | 
						|
  @retval EFI_ACCESS_DENIED     The file was opened read-only.
 | 
						|
  @retval EFI_VOLUME_FULL       The volume is full.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileFlush (
 | 
						|
  IN EFI_FILE_PROTOCOL *This
 | 
						|
  )
 | 
						|
{
 | 
						|
  return EFI_WRITE_PROTECTED;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// External definition of the file protocol template.
 | 
						|
//
 | 
						|
STATIC CONST EFI_FILE_PROTOCOL mEfiFileProtocolTemplate = {
 | 
						|
  EFI_FILE_PROTOCOL_REVISION, // revision 1
 | 
						|
  StubFileOpen,
 | 
						|
  StubFileClose,
 | 
						|
  StubFileDelete,
 | 
						|
  StubFileRead,
 | 
						|
  StubFileWrite,
 | 
						|
  StubFileGetPosition,
 | 
						|
  StubFileSetPosition,
 | 
						|
  StubFileGetInfo,
 | 
						|
  StubFileSetInfo,
 | 
						|
  StubFileFlush,
 | 
						|
  NULL,                       // OpenEx, revision 2
 | 
						|
  NULL,                       // ReadEx, revision 2
 | 
						|
  NULL,                       // WriteEx, revision 2
 | 
						|
  NULL                        // FlushEx, revision 2
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// Protocol member functions for SimpleFileSystem.
 | 
						|
//
 | 
						|
 | 
						|
/**
 | 
						|
  Open the root directory on a volume.
 | 
						|
 | 
						|
  @param[in]  This  A pointer to the volume to open the root directory on.
 | 
						|
 | 
						|
  @param[out] Root  A pointer to the location to return the opened file handle
 | 
						|
                    for the root directory in.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           The device was opened.
 | 
						|
  @retval EFI_UNSUPPORTED       This volume does not support the requested file
 | 
						|
                                system type.
 | 
						|
  @retval EFI_NO_MEDIA          The device has no medium.
 | 
						|
  @retval EFI_DEVICE_ERROR      The device reported an error.
 | 
						|
  @retval EFI_VOLUME_CORRUPTED  The file system structures are corrupted.
 | 
						|
  @retval EFI_ACCESS_DENIED     The service denied access to the file.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  The volume was not opened due to lack of
 | 
						|
                                resources.
 | 
						|
  @retval EFI_MEDIA_CHANGED     The device has a different medium in it or the
 | 
						|
                                medium is no longer supported. Any existing
 | 
						|
                                file handles for this volume are no longer
 | 
						|
                                valid. To access the files on the new medium,
 | 
						|
                                the volume must be reopened with OpenVolume().
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
StubFileSystemOpenVolume (
 | 
						|
  IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
 | 
						|
  OUT EFI_FILE_PROTOCOL              **Root
 | 
						|
  )
 | 
						|
{
 | 
						|
  STUB_FILE *StubFile;
 | 
						|
 | 
						|
  StubFile = AllocatePool (sizeof *StubFile);
 | 
						|
  if (StubFile == NULL) {
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  StubFile->Signature = STUB_FILE_SIG;
 | 
						|
  StubFile->BlobType  = KernelBlobTypeMax;
 | 
						|
  StubFile->Position  = 0;
 | 
						|
  CopyMem (&StubFile->File, &mEfiFileProtocolTemplate,
 | 
						|
    sizeof mEfiFileProtocolTemplate);
 | 
						|
  *Root = &StubFile->File;
 | 
						|
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
STATIC CONST EFI_SIMPLE_FILE_SYSTEM_PROTOCOL mFileSystem = {
 | 
						|
  EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
 | 
						|
  StubFileSystemOpenVolume
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// Utility functions.
 | 
						|
//
 | 
						|
 | 
						|
/**
 | 
						|
  Populate a blob in mKernelBlob.
 | 
						|
 | 
						|
  param[in,out] Blob  Pointer to the KERNEL_BLOB element in mKernelBlob that is
 | 
						|
                      to be filled from fw_cfg.
 | 
						|
 | 
						|
  @retval EFI_SUCCESS           Blob has been populated. If fw_cfg reported a
 | 
						|
                                size of zero for the blob, then Blob->Data has
 | 
						|
                                been left unchanged.
 | 
						|
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory for Blob->Data.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
EFI_STATUS
 | 
						|
FetchBlob (
 | 
						|
  IN OUT KERNEL_BLOB *Blob
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINT32 Left;
 | 
						|
 | 
						|
  //
 | 
						|
  // Read blob size.
 | 
						|
  //
 | 
						|
  QemuFwCfgSelectItem (Blob->SizeKey);
 | 
						|
  Blob->Size = QemuFwCfgRead32 ();
 | 
						|
  if (Blob->Size == 0) {
 | 
						|
    return EFI_SUCCESS;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Read blob.
 | 
						|
  //
 | 
						|
  Blob->Data = AllocatePool (Blob->Size);
 | 
						|
  if (Blob->Data == NULL) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: failed to allocate %Ld bytes for \"%s\"\n",
 | 
						|
      __FUNCTION__, (INT64)Blob->Size, Blob->Name));
 | 
						|
    return EFI_OUT_OF_RESOURCES;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((EFI_D_INFO, "%a: loading %Ld bytes for \"%s\"\n", __FUNCTION__,
 | 
						|
    (INT64)Blob->Size, Blob->Name));
 | 
						|
  QemuFwCfgSelectItem (Blob->DataKey);
 | 
						|
 | 
						|
  Left = Blob->Size;
 | 
						|
  do {
 | 
						|
    UINT32 Chunk;
 | 
						|
 | 
						|
    Chunk = (Left < SIZE_1MB) ? Left : SIZE_1MB;
 | 
						|
    QemuFwCfgReadBytes (Chunk, Blob->Data + (Blob->Size - Left));
 | 
						|
    Left -= Chunk;
 | 
						|
    DEBUG ((EFI_D_VERBOSE, "%a: %Ld bytes remaining for \"%s\"\n",
 | 
						|
      __FUNCTION__, (INT64)Left, Blob->Name));
 | 
						|
  } while (Left > 0);
 | 
						|
  return EFI_SUCCESS;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
// The entry point of the feature.
 | 
						|
//
 | 
						|
 | 
						|
/**
 | 
						|
  Download the kernel, the initial ramdisk, and the kernel command line from
 | 
						|
  QEMU's fw_cfg. Construct a minimal SimpleFileSystem that contains the two
 | 
						|
  image files, and load and start the kernel from it.
 | 
						|
 | 
						|
  The kernel will be instructed via its command line to load the initrd from
 | 
						|
  the same Simple FileSystem.
 | 
						|
 | 
						|
  @retval EFI_NOT_FOUND         Kernel image was not found.
 | 
						|
  @retval EFI_OUT_OF_RESOURCES  Memory allocation failed.
 | 
						|
  @retval EFI_PROTOCOL_ERROR    Unterminated kernel command line.
 | 
						|
 | 
						|
  @return                       Error codes from any of the underlying
 | 
						|
                                functions. On success, the function doesn't
 | 
						|
                                return.
 | 
						|
**/
 | 
						|
EFI_STATUS
 | 
						|
EFIAPI
 | 
						|
TryRunningQemuKernel (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  UINTN                     BlobType;
 | 
						|
  KERNEL_BLOB               *CurrentBlob;
 | 
						|
  KERNEL_BLOB               *KernelBlob, *InitrdBlob, *CommandLineBlob;
 | 
						|
  EFI_STATUS                Status;
 | 
						|
  EFI_HANDLE                FileSystemHandle;
 | 
						|
  EFI_DEVICE_PATH_PROTOCOL  *KernelDevicePath;
 | 
						|
  EFI_HANDLE                KernelImageHandle;
 | 
						|
  EFI_LOADED_IMAGE_PROTOCOL *KernelLoadedImage;
 | 
						|
 | 
						|
  Status = gRT->GetTime (&mInitTime, NULL /* Capabilities */);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: GetTime(): %r\n", __FUNCTION__, Status));
 | 
						|
    return Status;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Fetch all blobs.
 | 
						|
  //
 | 
						|
  for (BlobType = 0; BlobType < KernelBlobTypeMax; ++BlobType) {
 | 
						|
    CurrentBlob = &mKernelBlob[BlobType];
 | 
						|
    Status = FetchBlob (CurrentBlob);
 | 
						|
    if (EFI_ERROR (Status)) {
 | 
						|
      goto FreeBlobs;
 | 
						|
    }
 | 
						|
    mTotalBlobBytes += CurrentBlob->Size;
 | 
						|
  }
 | 
						|
  KernelBlob      = &mKernelBlob[KernelBlobTypeKernel];
 | 
						|
  InitrdBlob      = &mKernelBlob[KernelBlobTypeInitrd];
 | 
						|
  CommandLineBlob = &mKernelBlob[KernelBlobTypeCommandLine];
 | 
						|
 | 
						|
  if (KernelBlob->Data == NULL) {
 | 
						|
    Status = EFI_NOT_FOUND;
 | 
						|
    goto FreeBlobs;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a new handle with a single VenHw() node device path protocol on it,
 | 
						|
  // plus a custom SimpleFileSystem protocol on it.
 | 
						|
  //
 | 
						|
  FileSystemHandle = NULL;
 | 
						|
  Status = gBS->InstallMultipleProtocolInterfaces (&FileSystemHandle,
 | 
						|
                  &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
 | 
						|
                  &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
 | 
						|
                  NULL);
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces(): %r\n",
 | 
						|
      __FUNCTION__, Status));
 | 
						|
    goto FreeBlobs;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Create a device path for the kernel image to be loaded from that will call
 | 
						|
  // back into our file system.
 | 
						|
  //
 | 
						|
  KernelDevicePath = FileDevicePath (FileSystemHandle, KernelBlob->Name);
 | 
						|
  if (KernelDevicePath == NULL) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: failed to allocate kernel device path\n",
 | 
						|
      __FUNCTION__));
 | 
						|
    Status = EFI_OUT_OF_RESOURCES;
 | 
						|
    goto UninstallProtocols;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Load the image. This should call back into our file system.
 | 
						|
  //
 | 
						|
  Status = gBS->LoadImage (
 | 
						|
                  FALSE,             // BootPolicy: exact match required
 | 
						|
                  gImageHandle,      // ParentImageHandle
 | 
						|
                  KernelDevicePath,
 | 
						|
                  NULL,              // SourceBuffer
 | 
						|
                  0,                 // SourceSize
 | 
						|
                  &KernelImageHandle
 | 
						|
                  );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: LoadImage(): %r\n", __FUNCTION__, Status));
 | 
						|
    goto FreeKernelDevicePath;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Construct the kernel command line.
 | 
						|
  //
 | 
						|
  Status = gBS->OpenProtocol (
 | 
						|
                  KernelImageHandle,
 | 
						|
                  &gEfiLoadedImageProtocolGuid,
 | 
						|
                  (VOID **)&KernelLoadedImage,
 | 
						|
                  gImageHandle,                  // AgentHandle
 | 
						|
                  NULL,                          // ControllerHandle
 | 
						|
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
 | 
						|
                  );
 | 
						|
  ASSERT_EFI_ERROR (Status);
 | 
						|
 | 
						|
  if (CommandLineBlob->Data == NULL) {
 | 
						|
    KernelLoadedImage->LoadOptionsSize = 0;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Verify NUL-termination of the command line.
 | 
						|
    //
 | 
						|
    if (CommandLineBlob->Data[CommandLineBlob->Size - 1] != '\0') {
 | 
						|
      DEBUG ((EFI_D_ERROR, "%a: kernel command line is not NUL-terminated\n",
 | 
						|
        __FUNCTION__));
 | 
						|
      Status = EFI_PROTOCOL_ERROR;
 | 
						|
      goto UnloadKernelImage;
 | 
						|
    }
 | 
						|
 | 
						|
    //
 | 
						|
    // Drop the terminating NUL, convert to UTF-16.
 | 
						|
    //
 | 
						|
    KernelLoadedImage->LoadOptionsSize = (CommandLineBlob->Size - 1) * 2;
 | 
						|
  }
 | 
						|
 | 
						|
  if (InitrdBlob->Data != NULL) {
 | 
						|
    //
 | 
						|
    // Append ' initrd=<name>' in UTF-16.
 | 
						|
    //
 | 
						|
    KernelLoadedImage->LoadOptionsSize +=
 | 
						|
                                        (8 + StrLen(InitrdBlob->Name)) * 2;
 | 
						|
  }
 | 
						|
 | 
						|
  if (KernelLoadedImage->LoadOptionsSize == 0) {
 | 
						|
    KernelLoadedImage->LoadOptions = NULL;
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // NUL-terminate in UTF-16.
 | 
						|
    //
 | 
						|
    KernelLoadedImage->LoadOptionsSize += 2;
 | 
						|
 | 
						|
    KernelLoadedImage->LoadOptions = AllocatePool (
 | 
						|
                                       KernelLoadedImage->LoadOptionsSize);
 | 
						|
    if (KernelLoadedImage->LoadOptions == NULL) {
 | 
						|
      KernelLoadedImage->LoadOptionsSize = 0;
 | 
						|
      Status = EFI_OUT_OF_RESOURCES;
 | 
						|
      goto UnloadKernelImage;
 | 
						|
    }
 | 
						|
 | 
						|
    UnicodeSPrintAsciiFormat (
 | 
						|
      KernelLoadedImage->LoadOptions,
 | 
						|
      KernelLoadedImage->LoadOptionsSize,
 | 
						|
      "%a%a%s",
 | 
						|
      (CommandLineBlob->Data == NULL) ?  "" : (CHAR8 *)CommandLineBlob->Data,
 | 
						|
      (InitrdBlob->Data      == NULL) ?  "" : " initrd=",
 | 
						|
      (InitrdBlob->Data      == NULL) ? L"" : InitrdBlob->Name
 | 
						|
      );
 | 
						|
    DEBUG ((EFI_D_INFO, "%a: command line: \"%s\"\n", __FUNCTION__,
 | 
						|
      (CHAR16 *)KernelLoadedImage->LoadOptions));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Signal the EFI_EVENT_GROUP_READY_TO_BOOT event.
 | 
						|
  //
 | 
						|
  EfiSignalEventReadyToBoot();
 | 
						|
 | 
						|
  //
 | 
						|
  // Start the image.
 | 
						|
  //
 | 
						|
  Status = gBS->StartImage (
 | 
						|
                KernelImageHandle,
 | 
						|
                NULL,              // ExitDataSize
 | 
						|
                NULL               // ExitData
 | 
						|
                );
 | 
						|
  if (EFI_ERROR (Status)) {
 | 
						|
    DEBUG ((EFI_D_ERROR, "%a: StartImage(): %r\n", __FUNCTION__, Status));
 | 
						|
  }
 | 
						|
 | 
						|
  if (KernelLoadedImage->LoadOptions != NULL) {
 | 
						|
    FreePool (KernelLoadedImage->LoadOptions);
 | 
						|
  }
 | 
						|
  KernelLoadedImage->LoadOptionsSize = 0;
 | 
						|
 | 
						|
UnloadKernelImage:
 | 
						|
  gBS->UnloadImage (KernelImageHandle);
 | 
						|
 | 
						|
FreeKernelDevicePath:
 | 
						|
  FreePool (KernelDevicePath);
 | 
						|
 | 
						|
UninstallProtocols:
 | 
						|
  gBS->UninstallMultipleProtocolInterfaces (FileSystemHandle,
 | 
						|
         &gEfiSimpleFileSystemProtocolGuid, &mFileSystem,
 | 
						|
         &gEfiDevicePathProtocolGuid,       &mFileSystemDevicePath,
 | 
						|
         NULL);
 | 
						|
 | 
						|
FreeBlobs:
 | 
						|
  while (BlobType > 0) {
 | 
						|
    CurrentBlob = &mKernelBlob[--BlobType];
 | 
						|
    if (CurrentBlob->Data != NULL) {
 | 
						|
      FreePool (CurrentBlob->Data);
 | 
						|
      CurrentBlob->Size = 0;
 | 
						|
      CurrentBlob->Data = NULL;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return Status;
 | 
						|
}
 |