DevOVMF.cpp revision 63a05c8e524e623f920f2372b393686e5c432f10
/* $Id$ */
/** @file
* DevEFI - EFI <-> VirtualBox Integration Framework.
*/
/*
* Copyright (C) 2012 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
#define VBOX_WITH_OVMF
#include "VBoxDD.h"
#include "VBoxDD2.h"
#include "../PC/DevFwCommon.h"
/* EFI includes */
#include <ProcessorBind.h>
#include <Common/UefiBaseTypes.h>
#include <Common/PiFirmwareVolume.h>
#include <Common/PiFirmwareFile.h>
#define EFI_SSM_VERSION 1
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct {
int idxVariable;
typedef struct {
int cNvramVaraibles;
int iNvramLastIndex;
int idxCurrentVar;
} NVRAMDESC;
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 */
struct {
} Lun0;
} DEVEFI;
static SSMFIELD const g_aEfiNvramDescField[] =
{
};
static SSMFIELD const g_aEfiVariableDescFields[] =
{
};
/**
* Write to CMOS memory.
* This is used by the init complete code.
*/
{
}
{
{
}
}
/**
* This function looks up variable in NVRAM list
*/
static int nvramLookupVariableByUuidAndName(PDEVEFI pThis, char *pszVariableName, PCRTUUID pUuid, PPEFIVAR ppEfiVar)
{
int rc = VERR_NOT_FOUND;
{
if ( pEfiVar
{
rc = VINF_SUCCESS;
break;
}
}
return rc;
}
{
int rc = VINF_SUCCESS;
int idxValue = 0;
while(idxValue < 100)
{
if (!pEfiVar)
{
LogRel(("EFI: Can't allocate space for stored EFI variable\n"));
return VERR_NO_MEMORY;
}
idxValue++;
if (RT_FAILURE(rc))
{
break;
}
}
if ( RT_FAILURE(rc)
&& rc == VERR_NOT_FOUND)
rc = VINF_SUCCESS;
return rc;
}
{
int rc = VINF_SUCCESS;
int idxVar = 0;
{
idxVar++;
}
return VINF_SUCCESS;
}
{
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;
}
}
{
int rc = VINF_SUCCESS;
SSMR3PutStructEx(pSSM, &pThis->NVRAM.OperationVarOp, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
{
}
return rc;
}
static DECLCALLBACK(int) efiLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
{
int rc = VINF_SUCCESS;
if (uPass != SSM_PASS_FINAL)
return rc;
/* we should clean up the loaded values */
if (uVersion == 1)
{
SSMR3GetStructEx(pSSM, &pThis->NVRAM.OperationVarOp, sizeof(EFIVAR), 0, g_aEfiVariableDescFields, NULL);
int idxVariable = 0;
{
}
}
return rc;
}
#if 0
{
int rc = VINF_SUCCESS;
return rc;
}
#endif
/**
* 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_R3_IOPORT_READ;
#endif
case EFI_VARIABLE_OP:
{
case EFI_VM_VARIABLE_OP_START:
/* @todo: nop ? */
break;
case EFI_VM_VARIABLE_OP_END:
break;
case EFI_VM_VARIABLE_OP_INDEX:
break;
case EFI_VM_VARIABLE_OP_GUID:
break;
break;
case EFI_VM_VARIABLE_OP_NAME:
break;
break;
case EFI_VM_VARIABLE_OP_VALUE:
break;
break;
default:
break;
}
return VINF_SUCCESS;
case EFI_VARIABLE_PARAM:
{
break;
}
return VINF_SUCCESS;
}
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:
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;
}
case EFI_VARIABLE_OP:
{
/* clear buffer index */
if (u32 >= EFI_VM_VARIABLE_OP_MAX)
{
break;
}
}
break;
case EFI_VARIABLE_PARAM:
{
{
case EFI_VM_VARIABLE_OP_START:
{
switch (u32)
{
case EFI_VARIABLE_OP_QUERY:
{
LogRel(("EFI: variable lookup %RTuuid, %s\n",
&pEfiVar);
if (RT_SUCCESS(nvramRc))
{
LogFlowFunc(("OperationVar: au8Value:%.*Rhxs\n",
}
else
}
break;
case EFI_VARIABLE_OP_ADD:
{
LogRel(("EFI: variable add %RTuuid, %s\n", &pThis->NVRAM.OperationVarOp.uuid, pThis->NVRAM.OperationVarOp.szVariableName));
LogFlowFunc(("OperationVar: au8Value:%.*Rhxs\n",
&pEfiVar);
if (RT_SUCCESS(nvramRc))
{
/* delete or update ? */
/* @todo: check whether pEfiVar is WP */
LogFlowFunc(("pEfiVar: au8Value:%.*Rhxs\n",
{
/* delete */
}
else
{
/* update */
}
}
else
{
{
if (!pEfiVar)
{
break;
}
}
else
{
break;
}
}
}
break;
{
if (pEfiVar)
{
}
else
}
break;
default:
/* @todo: return error */
break;
}
}
case EFI_VM_VARIABLE_OP_END:
break;
case EFI_VM_VARIABLE_OP_INDEX:
break;
case EFI_VM_VARIABLE_OP_GUID:
break;
break;
case EFI_VM_VARIABLE_OP_NAME:
break;
break;
case EFI_VM_VARIABLE_OP_VALUE:
break;
break;
default:
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;
}
/**
* 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);
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");
rc = PDMDevHlpROMProtectShadow(pThis->pDevIns, pThis->GCLoadAddress, (uint32_t)cbQuart, PGMROMPROT_READ_RAM_WRITE_IGNORE);
"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;
}
{
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;
}
/**
* @copydoc(PDMIBASE::pfnQueryInterface)
*/
{
return NULL;
}
/**
* @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"
"DmiExposeProcInf\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)
{
}
/* NVRAM processing */
#if 0
#else
#endif
if (RT_FAILURE(rc))
pThis->Lun0.pNvramDown = (PPDMINVRAM)pThis->Lun0.pDrvBase->pfnQueryInterface(pThis->Lun0.pDrvBase, PDMINVRAM_IID);
/*
* 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) * 1000 * 1000;// 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;
/*
* Register our communication ports.
*/
if (RT_FAILURE(rc))
return rc;
/*
* Plant DMI and MPS tables
* XXX I wonder if we really need these tables as there is no SMBIOS header...
*/
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 */
};