DevFwCommon.cpp revision 1e9e76e4273dcc2e3d560a0f3605c46f0013eb7b
/* $Id$ */
/** @file
* FwCommon - Shared firmware code (used by DevPcBios & DevEFI).
*/
/*
* Copyright (C) 2009-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
#include <iprt/buildconfig.h>
#include "VBoxDD.h"
#include "VBoxDD2.h"
#include "DevFwCommon.h"
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/*
* Default DMI data (legacy).
* Don't change this information otherwise Windows guests might demand re-activation!
*/
/* type 0 -- DMI BIOS information */
static const int32_t g_iDefDmiBIOSReleaseMajor = 0;
static const int32_t g_iDefDmiBIOSReleaseMinor = 0;
static const int32_t g_iDefDmiBIOSFirmwareMajor = 0;
static const int32_t g_iDefDmiBIOSFirmwareMinor = 0;
static const char *g_pszDefDmiBIOSVendor = "innotek GmbH";
static const char *g_pszDefDmiBIOSVersion = "VirtualBox";
static const char *g_pszDefDmiBIOSReleaseDate = "12/01/2006";
/* type 1 -- DMI system information */
static const char *g_pszDefDmiSystemVendor = "innotek GmbH";
static const char *g_pszDefDmiSystemProduct = "VirtualBox";
static const char *g_pszDefDmiSystemVersion = "1.2";
static const char *g_pszDefDmiSystemSerial = "0";
static const char *g_pszDefDmiSystemSKU = "";
static const char *g_pszDefDmiSystemFamily = "Virtual Machine";
/* type 2 -- DMI board information */
static const char *g_pszDefDmiBoardVendor = "Oracle Corporation";
static const char *g_pszDefDmiBoardProduct = "VirtualBox";
static const char *g_pszDefDmiBoardVersion = "1.2";
static const char *g_pszDefDmiBoardSerial = "0";
static const char *g_pszDefDmiBoardAssetTag = "";
static const char *g_pszDefDmiBoardLocInChass = "";
/* type 3 -- DMI chassis information */
static const char *g_pszDefDmiChassisVendor = "Oracle Corporation";
static const char *g_pszDefDmiChassisVersion = "";
static const char *g_pszDefDmiChassisSerial = "";
static const char *g_pszDefDmiChassisAssetTag = "";
/* type 4 -- DMI processor information */
static const char *g_pszDefDmiProcManufacturer= "GenuineIntel";
static const char *g_pszDefDmiProcVersion = "Pentium(R) III";
/** The host DMI system product value, for DmiUseHostInfo=1. */
static char g_szHostDmiSystemProduct[64];
/** The host DMI system version value, for DmiUseHostInfo=1. */
static char g_szHostDmiSystemVersion[64];
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
#pragma pack(1)
typedef struct SMBIOSHDR
{
} *SMBIOSHDRPTR;
typedef struct DMIMAINHDR
{
} *DMIMAINHDRPTR;
/** DMI header */
typedef struct DMIHDR
{
} *PDMIHDR;
/** DMI BIOS information (Type 0) */
typedef struct DMIBIOSINF
{
} *PDMIBIOSINF;
/** DMI system information (Type 1) */
typedef struct DMISYSTEMINF
{
} *PDMISYSTEMINF;
/** DMI board (or module) information (Type 2) */
typedef struct DMIBOARDINF
{
} *PDMIBOARDINF;
/** DMI system enclosure or chassis type (Type 3) */
typedef struct DMICHASSIS
{
/* v2.3+, currently not supported */
} *PDMICHASSIS;
/** DMI processor information (Type 4) */
typedef struct DMIPROCESSORINF
{
/* v2.1+ */
/* v2.3+ */
/* v2.5+ */
/* v2.6+ */
} *PDMIPROCESSORINF;
/** DMI OEM strings (Type 11) */
typedef struct DMIOEMSTRINGS
{
} *PDMIOEMSTRINGS;
/** DMI OEM-specific table (Type 128) */
typedef struct DMIOEMSPECIFIC
{
} *PDMIOEMSPECIFIC;
/** Physical memory array (Type 16) */
typedef struct DMIRAMARRAY
{
} *PDMIRAMARRAY;
/** DMI Memory Device (Type 17) */
typedef struct DMIMEMORYDEV
{
/* v2.6+ */
} *PDMIMEMORYDEV;
/** MPS floating pointer structure */
typedef struct MPSFLOATPTR
{
} *PMPSFLOATPTR;
/** MPS config table header */
typedef struct MPSCFGTBLHEADER
{
} *PMPSCFGTBLHEADER;
/** MPS processor entry */
typedef struct MPSPROCENTRY
{
} *PMPSPROCENTRY;
/** MPS bus entry */
typedef struct MPSBUSENTRY
{
} *PMPSBUSENTRY;
typedef struct MPSIOAPICENTRY
{
} *PMPSIOAPICENTRY;
/** MPS I/O-Interrupt entry */
typedef struct MPSIOINTERRUPTENTRY
{
} *PMPSIOIRQENTRY;
#pragma pack()
/**
* Calculate a simple checksum for the MPS table.
*
* @param data data
* @param len size of data
*/
{
return -u8Sum;
}
#if 0 /* unused */
{
return (u8Sum == 0);
}
#endif
/**
* Try fetch the DMI strings from the system.
*/
static void fwCommonUseHostDMIStrings(void)
{
int rc;
g_szHostDmiSystemProduct, sizeof(g_szHostDmiSystemProduct));
if (RT_SUCCESS(rc))
{
}
g_szHostDmiSystemVersion, sizeof(g_szHostDmiSystemVersion));
if (RT_SUCCESS(rc))
{
}
}
/**
* Construct the DMI table.
*
* @returns VBox status code.
* @param pDevIns The device instance.
* @param pTable Where to create the DMI table.
* @param cbMax The maximum size of the DMI table.
* @param pUuid Pointer to the UUID to use if the DmiUuid
* configuration string isn't present.
* @param pCfg The handle to our config node.
* @param cCpus Number of VCPUs.
* @param pcbDmiTables Size of DMI data in bytes.
* @param pcNumDmiTables Number of DMI tables.
*/
int FwCommonPlantDMITable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, PCRTUUID pUuid, PCFGMNODE pCfg, uint16_t cCpus, uint16_t *pcbDmiTables, uint16_t *pcNumDmiTables)
{
/*
* CFGM Hint!
*
* The macros below makes it a bit hard to figure out the config options
* available here. To get a quick hint, take a look a the CFGM
* validation in the calling code (DevEFI.cpp and DevPcBios.cpp).
*
* 32-bit signed integer CFGM options are read by DMI_READ_CFG_S32, the 2nd
* parameter is the CFGM value name.
*
* Strings are read by DMI_READ_CFG_STR and DMI_READ_CFG_STR_DEF, the 2nd parameter is
* the CFGM value name.
*/
#define DMI_CHECK_SIZE(cbWant) \
{ \
size_t cbNeed = (size_t)(pszStr + cbWant - (char *)pTable) + 5; /* +1 for strtab terminator +4 for end-of-table entry */ \
{ \
if (fHideErrors) \
{ \
LogRel(("One of the DMI strings is too long -- using default DMI data!\n")); \
continue; \
} \
N_("One of the DMI strings is too long. Check all bios/Dmi* configuration entries. At least %zu bytes are needed but there is no space for more than %d bytes"), cbNeed, cbMax); \
} \
}
{ \
if (fForceDefault) \
pszTmp = default_value; \
else \
{ \
if (RT_FAILURE(rc)) \
{ \
if (fHideErrors) \
{ \
LogRel(("Configuration error: Querying \"" name "\" as a string failed -- using default DMI data!\n")); \
continue; \
} \
} \
pszTmp = ""; \
else \
} \
if (!pszTmp[0]) \
variable = 0; /* empty string */ \
else \
{ \
DMI_CHECK_SIZE(cStr); \
} \
}
{ \
if (fForceDefault) \
else \
{ \
if (RT_FAILURE(rc)) \
{ \
if (fHideErrors) \
{ \
LogRel(("Configuration error: Querying \"" # name "\" as an int failed -- using default DMI data!\n")); \
continue; \
} \
} \
} \
}
#define DMI_START_STRUCT(tbl) \
iStrNr = 1;
#define DMI_TERM_STRUCT \
{ \
if (iStrNr == 1) \
}
bool fForceDefault = false;
#ifdef VBOX_BIOS_DMI_FALLBACK
/*
* There will be two passes. If an error occurs during the first pass, a
* message will be written to the release log and we fall back to default
* DMI data and start a second pass.
*/
bool fHideErrors = true;
#else
/*
* There will be one pass, every error is fatal and will prevent the VM
* from starting.
*/
bool fHideErrors = false;
#endif
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"DmiUseHostInfo\""));
/* Sync up with host default DMI values */
if (fDmiUseHostInfo)
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"DmiExposeMemoryTable\""));
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"DmiExposeProcInf\""));
for (;; fForceDefault = true, fHideErrors = false)
{
int iStrNr;
char szBuf[256];
char szDmiSystemUuid[64];
char *pszDmiSystemUuid;
const char *pszTmp;
if (fForceDefault)
else
{
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
else if (RT_FAILURE(rc))
{
if (fHideErrors)
{
LogRel(("Configuration error: Querying \"DmiSystemUuid\" as a string failed, using default DMI data\n"));
continue;
}
N_("Configuration error: Querying \"DmiSystemUuid\" as a string failed"));
}
else
}
/*********************************
* DMI BIOS information (Type 0) *
*********************************/
DMI_CHECK_SIZE(sizeof(*pBIOSInf));
/* don't set these fields by default for legacy compatibility */
if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0)
{
if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0)
{
}
}
iStrNr = 1;
/* any more?? */
;
/* any more?? */
;
/* any more?? */
;
/***********************************
* DMI system information (Type 1) *
***********************************/
DMI_CHECK_SIZE(sizeof(*pSystemInf));
if (pszDmiSystemUuid)
{
if (RT_FAILURE(rc))
{
if (fHideErrors)
{
LogRel(("Configuration error: Invalid UUID for DMI tables specified, using default DMI data\n"));
continue;
}
N_("Configuration error: Invalid UUID for DMI tables specified"));
}
}
/**********************************
* DMI board information (Type 2) *
**********************************/
DMI_CHECK_SIZE(sizeof(*pBoardInf));
int iDmiBoardBoardType;
;
pBoardInf->u8cObjectHandles = 0;
/********************************************
* DMI System Enclosure or Chassis (Type 3) *
********************************************/
DMI_CHECK_SIZE(sizeof(*pChassis));
iStrNr = 1;
#ifdef VBOX_WITH_DMI_CHASSIS
#else
#endif
int iDmiChassisType;
# if 0
/* v2.3+, currently not supported */
pChassis->u32OEMdefined = 0;
# endif
/**************************************
* DMI Processor Information (Type 4) *
**************************************/
/*
* This is just a dummy processor. Should we expose the real guest CPU features
* here? Accessing this information at this point is difficult.
*/
char szSocket[32];
DMI_CHECK_SIZE(sizeof(*pProcessorInf));
else
{
}
/* Ext Family ID = 0
* Ext Model ID = 2
* Processor Type = 0
* Family ID = 6
* Model = 7
* Stepping = 6
* Features: FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8,
* APIC, SEP, MTRR, PGE, MCA, CMOV, PAT, PSE-36,
* CFLSH, DS, ACPI, MMX, FXSR, SSE, SSE2, SS */
| RT_BIT(0) /* CPU enabled */
;
/***************************************
* DMI Physical Memory Array (Type 16) *
***************************************/
if (RT_FAILURE (rc))
N_("Configuration error: Failed to read \"RamSize\""));
DMI_CHECK_SIZE(sizeof(*pMemArray));
else
/***************************************
* DMI Memory Device (Type 17) *
***************************************/
DMI_CHECK_SIZE(sizeof(*pMemDev));
else
if (u16RamSizeM == 0)
/*****************************
* DMI OEM strings (Type 11) *
*****************************/
DMI_CHECK_SIZE(sizeof(*pOEMStrings));
#ifdef VBOX_WITH_DMI_OEMSTRINGS
#else
#endif
char szTmp[64];
/*************************************
* DMI OEM specific table (Type 128) *
************************************/
DMI_CHECK_SIZE(sizeof(*pOEMSpecific));
pOEMSpecific->u32CpuFreqKHz = RT_H2LE_U32((uint32_t)((uint64_t)TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns)) / 1000));
/* End-of-table marker - includes padding to account for fixed table size. */
/* We currently plant 10 DMI tables. Update this if tables number changed. */
*pcNumDmiTables = 10;
/* If more fields are added here, fix the size check in DMI_READ_CFG_STR */
/* Success! */
break;
}
return VINF_SUCCESS;
}
/**
* Construct the SMBIOS and DMI headers table pointer at VM construction and
* reset.
*
* @param pDevIns The device instance data.
*/
void FwCommonPlantSmbiosAndDmiHdrs(PPDMDEVINS pDevIns, uint16_t cbDmiTables, uint16_t cNumDmiTables)
{
struct
{
struct DMIMAINHDR dmi;
}
{
// The SMBIOS header
{
{ 0x5f, 0x53, 0x4d, 0x5f}, // "_SM_" signature
0x00, // checksum
0x1f, // EPS length, defined by standard
VBOX_SMBIOS_MAJOR_VER, // SMBIOS major version
VBOX_SMBIOS_MINOR_VER, // SMBIOS minor version
VBOX_SMBIOS_MAXSS, // Maximum structure size
0x00, // Entry point revision
{ 0x00, 0x00, 0x00, 0x00, 0x00 } // padding
},
// The DMI header
{
{ 0x5f, 0x44, 0x4d, 0x49, 0x5f }, // "_DMI_" signature
0x00, // checksum
0, // DMI tables length
VBOX_DMI_TABLE_BASE, // DMI tables base
0, // DMI tables entries
VBOX_DMI_TABLE_VER, // DMI version
}
};
aBiosHeaders.smbios.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.smbios, sizeof(aBiosHeaders.smbios));
aBiosHeaders.dmi.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.dmi, sizeof(aBiosHeaders.dmi));
}
/**
* Construct the MPS table for implanting as a ROM page.
*
* Only applicable if IOAPIC is active!
*
* See ``MultiProcessor Specification Version 1.4 (May 1997)'':
* ``1.3 Scope
* ...
* The hardware required to implement the MP specification is kept to a
* minimum, as follows:
* * One or more processors that are Intel architecture instruction set
* compatible, such as the CPUs in the Intel486 or Pentium processor
* family.
* * One or more APICs, such as the Intel 82489DX Advanced Programmable
* Interrupt Controller or the integrated APIC, such as that on the
* Intel Pentium 735\\90 and 815\\100 processors, together with a discrete
* I/O APIC unit.''
* and later:
* ``4.3.3 I/O APIC Entries
* The configuration table contains one or more entries for I/O APICs.
* ...
* I/O APIC FLAGS: EN 3:0 1 If zero, this I/O APIC is unusable, and the
* operating system should not attempt to access
* this I/O APIC.
* At least one I/O APIC must be enabled.''
*
* @param pDevIns The device instance data.
* @param pTable Where to write the table.
* @param cbMax The maximum size of the MPS table.
* @param cCpus The number of guest CPUs.
*/
{
/* configuration table */
pCfgTab->u32OemTablePtr = 0;
pCfgTab->u16OemTableSize = 0;
pCfgTab->u16ExtTableLength = 0;
pCfgTab->u8ExtTableChecksum = 0;
pCfgTab->u8Reserved = 0;
if (u32Eax >= 1)
{
/* Local APIC will be enabled later so override it here. Since we provide
* an MP table we have an IOAPIC and therefore a Local APIC. */
}
/* Construct MPS table for each VCPU. */
for (int i = 0; i < cCpus; i++)
{
pProcEntry->u8LocalApicId = i;
pProcEntry->u8CPUFlags = (i == 0 ? 2 /* bootstrap processor */ : 0 /* application processor */) | 1 /* enabled */;
pProcEntry->u32Reserved[0] =
pProcEntry++;
pCfgTab->u16EntryCount++;
}
/* ISA bus */
pBusEntry++;
pCfgTab->u16EntryCount++;
/* PCI bus */
pCfgTab->u16EntryCount++;
* MP spec: "The configuration table contains one or more entries for I/O APICs.
* ... At least one I/O APIC must be enabled." */
pCfgTab->u16EntryCount++;
/* Interrupt tables */
/* Bus vectors */
/* Note: The PIC is currently not routed to the I/O APIC. Therefore we skip
* pin 0 on the I/O APIC.
*/
{
/*
* 0 - INT, vectored interrupt,
* 3 - ExtINT, vectored interrupt provided by PIC
* As we emulate system with both APIC and PIC, it's needed for their coexistence.
*/
trigger mode = conforms to bus */
/* IRQ0 mapped to pin 2, other are identity mapped */
/* If changing, also update PDMIsaSetIrq() and MADT */
pCfgTab->u16EntryCount++;
}
/* Local delivery */
pIrqEntry->u8SrcBusIrq = 0;
pIrqEntry->u8DstIOAPICInt = 0;
pIrqEntry++;
pCfgTab->u16EntryCount++;
pIrqEntry->u8SrcBusIrq = 0;
pIrqEntry++;
pCfgTab->u16EntryCount++;
("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
}
/**
* Construct the MPS table pointer at VM construction and reset.
*
* Only applicable if IOAPIC is active!
*
* @param pDevIns The device instance data.
*/
{
floatPtr.u8Checksum = 0;
floatPtr.au8Feature[0] = 0;
}