DevEFI.cpp revision 108008bf37ebb349b5b04dacfe73da99dbb0106c
/* $Id$ */
/** @file
* DevEFI - EFI <-> VirtualBox Integration Framework.
*/
/*
* Copyright (C) 2006-2009 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_EFI
#ifdef DEBUG
# define DEVEFI_WITH_VBOXDBG_SCRIPT
#endif
#include "../Builtins.h"
#include "../Builtins2.h"
#include "../PC/DevFwCommon.h"
/* EFI includes */
#include <ProcessorBind.h>
#include <Common/UefiBaseTypes.h>
#include <Common/PiFirmwareVolume.h>
#include <Common/PiFirmwareFile.h>
#include <IndustryStandard/PeImage.h>
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct DEVEFI
{
/** Pointer back to the device instance. */
/** EFI message buffer. */
char szMsg[VBOX_EFI_DEBUG_BUFFER];
/** EFI message buffer index. */
/** EFI panic message buffer. */
char szPanicMsg[2048];
/** EFI panic message buffer index. */
/** The system EFI ROM data. */
/** The size of the system EFI ROM. */
/** The name of the EFI ROM file. */
char *pszEfiRomFile;
/** Thunk page pointer. */
/** First entry point of the EFI firmware */
/* Second Entry Point (PeiCore)*/
/** EFI firmware physical load address */
/** Current info selector */
/** Current info position */
/** Number of virtual CPUs. (Config) */
/** RAM below 4GB (in bytes). (Config) */
/** RAM above 4GB (in bytes). (Config) */
/** The DMI tables. */
/* Boot parameters passed to the firmware */
char szBootArgs[256];
/* Host UUID (for DMI) */
/* Device properties buffer */
/* Device properties buffer size */
/* Virtual machine front side bus frequency */
/* Virtual machine time stamp counter frequency */
/* Virtual machine CPU frequency */
/* GOP mode */
/* Uga mode resolutions */
} DEVEFI;
/**
* Write to CMOS memory.
* This is used by the init complete code.
*/
{
}
{
switch (pThis->iInfoSelector)
{
case EFI_INFO_INDEX_GOP_MODE:
return 4;
case EFI_INFO_INDEX_BOOT_ARGS:
return pThis->u32DevicePropsLen;
return 8;
}
Assert(false);
return 0;
}
{
union
{
} value;
switch (pThis->iInfoSelector)
{
break;
break;
break;
break;
/* Keep in sync with value in EfiThunk.asm */
break;
break;
break;
break;
break;
case EFI_INFO_INDEX_BOOT_ARGS:
case EFI_INFO_INDEX_GOP_MODE:
break;
break;
break;
default:
Assert(false);
break;
}
}
/**
* Port I/O Handler for IN operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param pu32 Where to store the result.
* @param cb Number of bytes read.
*/
static DECLCALLBACK(int) efiIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
{
switch (Port)
{
case EFI_INFO_PORT:
{
pThis->iInfoPosition = 0;
}
else
{
/* So far */
if (cb != 1)
return VERR_IOM_IOPORT_UNUSED;
pThis->iInfoPosition++;
}
return VINF_SUCCESS;
case EFI_PANIC_PORT:
#ifdef IN_RING3
LogRel(("Panic port read!\n"));
/* Insert special code here on panic reads */
#else
/* Reschedule to R3 */
return VINF_IOM_HC_IOPORT_READ;
#endif
}
return VERR_IOM_IOPORT_UNUSED;
}
/**
* Port I/O Handler for OUT operations.
*
* @returns VBox status code.
*
* @param pDevIns The device instance.
* @param pvUser User argument - ignored.
* @param Port Port number used for the IN operation.
* @param u32 The value to output.
* @param cb The value size in bytes.
*/
static DECLCALLBACK(int) efiIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
{
switch (Port)
{
case EFI_INFO_PORT:
break;
case EFI_DEBUG_PORT:
{
/* The raw version. */
switch (u32)
{
}
/* The readable, buffered version. */
{
{
#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
if (pszVBoxDbg)
{
if (RT_SUCCESS(rc))
{
}
}
#endif
}
}
else
{
{
}
}
break;
}
case EFI_PANIC_PORT:
{
switch (u32)
{
case EFI_PANIC_CMD_BAD_ORG:
LogRel(("EFI Panic: You have to fix ORG offset in EfiThunk.asm! Must be 0x%x\n",
RTAssertMsg2Weak("Fix ORG offset in EfiThunk.asm: must be 0x%x\n",
break;
case EFI_PANIC_CMD_THUNK_TRAP:
LogRel(("EFI Panic: Unexpected trap!!\n"));
#ifdef VBOX_STRICT
return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "EFI Panic: Unexpected trap during early bootstrap!\n");
#else
AssertReleaseMsgFailed(("Unexpected trap during early EFI bootstrap!!\n"));
#endif
break;
case EFI_PANIC_CMD_START_MSG:
break;
case EFI_PANIC_CMD_END_MSG:
#ifdef VBOX_STRICT
#else
return VERR_INTERNAL_ERROR;
#endif
default:
if ( u32 >= EFI_PANIC_CMD_MSG_FIRST
&& u32 <= EFI_PANIC_CMD_MSG_LAST)
{
/* Add the message char to the buffer. */
{
if ( ch == '\n'
&& i > 0
i--;
}
}
else
break;
}
break;
}
default:
break;
}
return VINF_SUCCESS;
}
/**
* Init complete notification.
*
* @returns VBOX status code.
* @param pDevIns The device instance.
*/
{
/* PC Bios */
/*
* Memory sizes.
*/
u32 = (uint32_t)( (RT_MIN(RT_MIN(pThis->cbRam, offRamHole), UINT32_C(0xffe00000)) - 16U * _1M) / _64K );
else
u32 = 0;
/*
* Number of CPUs.
*/
return VINF_SUCCESS;
}
/**
* Reset notification.
*
* @returns VBox status.
* @param pDevIns The device instance data.
*/
{
int rc;
LogFlow(("efiReset\n"));
pThis->iInfoSelector = 0;
/*
* Plan some structures in RAM.
*/
/*
*/
while (cPages > 0)
{
/* Read the (original) ROM page and write it back to the RAM page. */
if (RT_FAILURE(rc))
/* Advance */
cPages--;
}
}
/**
* Destruct a device instance.
*
* Most VM resources are freed by the VM. This callback is provided so that any non-VM
* resources can be freed correctly.
*
* @param pDevIns The device instance data.
*/
{
/*
* Free MM heap pointers.
*/
{
}
if (pThis->pszEfiRomFile)
{
}
if (pThis->pu8EfiThunk)
{
}
if (pThis->pu8DeviceProps)
{
pThis->u32DevicePropsLen = 0;
}
return VINF_SUCCESS;
}
/**
* Helper that searches for a FFS file of a given type.
*
* @returns Pointer to the FFS file header if found, NULL if not.
*
* @param pFfsFile Pointer to the FFS file header to start searching at.
* @param pbEnd The end of the firmware volume.
* @param FileType The file type to look for.
* @param pcbFfsFile Where to store the FFS file size (includes header).
*/
DECLINLINE(EFI_FFS_FILE_HEADER const *)
efiFwVolFindFileByType(EFI_FFS_FILE_HEADER const *pFfsFile, uint8_t const *pbEnd, EFI_FV_FILETYPE FileType, uint32_t *pcbFile)
{
{
{
return pFfsFile;
}
}
return NULL;
}
static int efiFindEntryPoint(EFI_FFS_FILE_HEADER const *pFfsFile, uint32_t cbFfsFile, RTGCPHYS *pImageBase, uint8_t **ppbImage)
{
/*
* Sections headers are lays at the beginning of block it describes,
* the first section header is located immediately after FFS header.
*/
{
{
/*Here should be other code containing sections*/
pbImage = (uint8_t const *)&uSecHdrPtr.Pe32Section[1]; /* the PE/PE+/TE headers begins just after the Section Header */
break;
}
}
AssertLogRelMsgReturn(pbImage, ("Failed to find PE32 or TE section for the SECURITY_CORE FFS\n"), VERR_INVALID_PARAMETER);
/*
* Parse the image extracting the ImageBase and the EntryPoint.
*/
int rc = VINF_SUCCESS;
union EfiHdrUnion
{
/* Skip MZ if found. */
{
}
{
{
}
else
{
Log2(("EFI: PE+/AMD64\n"));
}
}
{
/* TE header */
Log2(("EFI: TE header\n"));
}
else
if (pImageBase != NULL)
*pImageBase = ImageBase;
}
/**
* Parse EFI ROM headers and find entry points.
*
* @returns VBox status.
* @param pThis The device instance data.
*/
{
/*
* Validate firmware volume header.
*/
/** @todo check checksum, see PE spec vol. 3 */
AssertLogRelMsgReturn(!(pThis->cbEfiRom & PAGE_OFFSET_MASK), ("%RX64\n", pThis->cbEfiRom), VERR_INVALID_PARAMETER);
/*
* Ffs files are stored one by one, so to find SECURITY_CORE we've to
* search thru every one on the way.
*/
EFI_FFS_FILE_HEADER const *pFfsFile = (EFI_FFS_FILE_HEADER const *)(pThis->pu8EfiRom + pFwVolHdr->HeaderLength);
AssertLogRelMsgReturn(pFfsFile, ("No SECURITY_CORE found in the firmware volume\n"), VERR_FILE_NOT_FOUND);
/*
* Calc the firmware load address from the image base and validate it.
*/
LogRel(("EFI: Firmware volume loading at %RGp, SEC CORE at %RGp with EP at %RGp\n",
LogRel(("EFI: Firmware volume loading at %RGp, PEI CORE at with EP at %RGp\n",
return VINF_SUCCESS;
}
/**
* Load EFI ROM file into the memory.
*
* @returns VBox status.
* @param pThis The device instance data.
* @param pCfg Configuration node handle for the device.
*/
{
/*
* Read the entire firmware volume into memory.
*/
void *pvFile;
0 /*off*/,
RTFOFF_MAX /*cbMax*/,
&pvFile,
&cbFile);
if (RT_FAILURE(rc))
N_("Loading the EFI firmware volume '%s' failed with rc=%Rrc"),
/*
* Validate firmware volume and figure out the load address as well as the SEC entry point.
*/
if (RT_FAILURE(rc))
N_("Parsing the EFI firmware volume '%s' failed with rc=%Rrc"),
/*
* Map the firmware volume into memory as shadowed ROM.
*/
/** @todo fix PGMR3PhysRomRegister so it doesn't mess up in SUPLib when mapping a big ROM image. */
"EFI Firmware Volume");
if (RT_FAILURE(rc))
return rc;
"EFI Firmware Volume (Part 2)");
if (RT_FAILURE(rc))
return rc;
"EFI Firmware Volume (Part 3)");
if (RT_FAILURE(rc))
return rc;
"EFI Firmware Volume (Part 4)");
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* Patches and loads the EfiThunk ROM image.
*
* The thunk image is where the CPU starts and will switch it into
* 32-bit protected or long mode and invoke the SEC CORE image in the
* firmware volume. It also contains some static VM configuration data
* at the very beginning of the page, see DEVEFIINFO.
*
* @returns VBox status code.
* @param pThis The device instance data.
* @param pCfg Configuration node handle for the device.
*/
{
uint8_t f64BitEntry = 0;
int rc;
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"64BitEntry\""));
/*
* Make a copy of the page and set the values of the DEVEFIINFO structure
* found at the beginning of it.
*/
if (f64BitEntry)
LogRel(("Using 64-bit EFI firmware\n"));
/* Duplicate the page so we can change it. */
return VERR_NO_MEMORY;
/* Fill in the info. */
//AssertRelease(pEfiInfo->pfnFirmwareEP == pThis->GCEntryPoint0);
pEfiInfo->HighEPAddress = 0;
/* zeroth bit controls use of 64-bit entry point in fw */
pEfiInfo->u32Reserved2 = 0;
/* Register the page as a ROM (data will be copied). */
PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk");
if (RT_FAILURE(rc))
return rc;
#if 1 /** @todo this is probably not necessary. */
/*
* Map thunk page also at low address, so that real->protected mode jump code can
* protected mode, while being in 16-bits mode.
*
* @todo: maybe need to unregister later or place somewhere else (although could
* be needed during reset)
*/
PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "EFI Thunk (2)");
if (RT_FAILURE(rc))
return rc;
#endif
return rc;
}
{
else
val = 0xff;
return val;
}
{
int rc = 0;
bool fUpper = true;
if (!pThis->pu8DeviceProps)
return VERR_NO_MEMORY;
{
if (u8Hb > 0xf)
continue;
if (fUpper)
else
}
return rc;
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
int rc;
/*
* Validate and read the configuration.
*/
if (!CFGMR3AreValuesValid(pCfg,
"EfiRom\0"
"RamSize\0"
"RamHoleSize\0"
"NumCPUs\0"
"UUID\0"
"IOAPIC\0"
"DmiBIOSVendor\0"
"DmiBIOSVersion\0"
"DmiBIOSReleaseDate\0"
"DmiBIOSReleaseMajor\0"
"DmiBIOSReleaseMinor\0"
"DmiBIOSFirmwareMajor\0"
"DmiBIOSFirmwareMinor\0"
"DmiSystemSKU\0"
"DmiSystemFamily\0"
"DmiSystemProduct\0"
"DmiSystemSerial\0"
"DmiSystemUuid\0"
"DmiSystemVendor\0"
"DmiSystemVersion\0"
"DmiChassisVendor\0"
"DmiChassisVersion\0"
"DmiChassisSerial\0"
"DmiChassisAssetTag\0"
#ifdef VBOX_WITH_DMI_OEMSTRINGS
"DmiOEMVBoxVer\0"
"DmiOEMVBoxRev\0"
#endif
"DmiUseHostInfo\0"
"DmiExposeMemoryTable\0"
"64BitEntry\0"
"BootArgs\0"
"DeviceProps\0"
"GopMode\0"
"UgaHorizontalResolution\0"
"UgaVerticalResolution\0"
))
N_("Configuration error: Invalid config value(s) for the EFI device"));
/* CPU count (optional). */
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"IOAPIC\""));
/*
*/
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"UUID\" failed"));
/*
* Convert the UUID to network byte order. Not entirely straightforward as
* parts are MSB already...
*/
/*
* RAM sizes
*/
/*
* Get the system EFI ROM file name.
*/
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
if (!pThis->pszEfiRomFile)
return VERR_NO_MEMORY;
}
else if (RT_FAILURE(rc))
N_("Configuration error: Querying \"EfiRom\" as a string failed"));
else if (!*pThis->pszEfiRomFile)
{
}
/*
* Get boot args.
*/
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
rc = VINF_SUCCESS;
}
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"BootArgs\" as a string failed"));
/*
* Get device props.
*/
char* pszDeviceProps;
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
{
rc = VINF_SUCCESS;
}
if (RT_FAILURE(rc))
N_("Configuration error: Querying \"DeviceProps\" as a string failed"));
if (pszDeviceProps)
{
if (RT_FAILURE(rc))
N_("Configuration error: Cannot parse device properties"));
}
else
{
pThis->u32DevicePropsLen = 0;
}
/*
* CPU frequencies
*/
// @todo: we need to have VMM API to access TSC increase speed, for now provide reasonable default
pThis->u64TscFrequency = RTMpGetMaxFrequency(0) * 1024 * 1024;// TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns));
if (pThis->u64TscFrequency == 0)
/* Multiplier is read from MSR_IA32_PERF_STATUS, and now is hardcoded as 4 */
/*
* GOP graphics
*/
{
}
/*
* Uga graphics
*/
if (pThis->u32UgaHorisontal == 0)
{
}
if (pThis->u32UgaVertical == 0)
{
}
#ifdef DEVEFI_WITH_VBOXDBG_SCRIPT
/*
* Zap the debugger script
*/
RTFileDelete("./DevEFI.VBoxDbg");
#endif
/*
* Load firmware volume and thunk ROM.
*/
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
/*
* Register our communication ports.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Plant DMI and MPS tables
*/
PGMPHYS_ROM_FLAGS_PERMANENT_BINARY, "DMI tables");
/*
* Call reset to set things up.
*/
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceEFI =
{
/* u32Version */
/* szName */
"efi",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"Extensible Firmware Interface Device",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(DEVEFI),
/* pfnConstruct */
/* pfnDestruct */
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete. */
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};