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;
* you can redistribute it and/or modify it under the terms of the GNU
* 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 <VBox/vmm/pdmdev.h>
#include <VBox/log.h>
#include <VBox/err.h>
#include <VBox/param.h>
#include <iprt/asm.h>
#include <iprt/assert.h>
#include <iprt/buildconfig.h>
#include <iprt/file.h>
#include <iprt/mem.h>
#include <iprt/string.h>
#include <iprt/uuid.h>
#include <iprt/system.h>
#include <iprt/cdefs.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 = "";
static const int32_t g_iDefDmiBoardBoardType = 0x0A; /* Motherboard */
/* type 3 -- DMI chassis information */
static const char *g_pszDefDmiChassisVendor = "Oracle Corporation";
static const int32_t g_iDefDmiChassisType = 0x01; /* ''other'', no chassis lock present */
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
{
uint8_t au8Signature[4];
uint8_t u8Checksum;
uint8_t u8Eps;
uint8_t u8VersionMajor;
uint8_t u8VersionMinor;
uint16_t u16MaxStructureSize;
uint8_t u8EntryPointRevision;
uint8_t u8Pad[5];
} *SMBIOSHDRPTR;
AssertCompileSize(SMBIOSHDR, 16);
typedef struct DMIMAINHDR
{
uint8_t au8Signature[5];
uint8_t u8Checksum;
uint16_t u16TablesLength;
uint32_t u32TableBase;
uint16_t u16TableEntries;
uint8_t u8TableVersion;
} *DMIMAINHDRPTR;
AssertCompileSize(DMIMAINHDR, 15);
/** DMI header */
typedef struct DMIHDR
{
uint8_t u8Type;
uint8_t u8Length;
uint16_t u16Handle;
} *PDMIHDR;
AssertCompileSize(DMIHDR, 4);
/** DMI BIOS information (Type 0) */
typedef struct DMIBIOSINF
{
DMIHDR header;
uint8_t u8Vendor;
uint8_t u8Version;
uint16_t u16Start;
uint8_t u8Release;
uint8_t u8ROMSize;
uint64_t u64Characteristics;
uint8_t u8CharacteristicsByte1;
uint8_t u8CharacteristicsByte2;
uint8_t u8ReleaseMajor;
uint8_t u8ReleaseMinor;
uint8_t u8FirmwareMajor;
uint8_t u8FirmwareMinor;
} *PDMIBIOSINF;
AssertCompileSize(DMIBIOSINF, 0x18);
/** DMI system information (Type 1) */
typedef struct DMISYSTEMINF
{
DMIHDR header;
uint8_t u8Manufacturer;
uint8_t u8ProductName;
uint8_t u8Version;
uint8_t u8SerialNumber;
uint8_t au8Uuid[16];
uint8_t u8WakeupType;
uint8_t u8SKUNumber;
uint8_t u8Family;
} *PDMISYSTEMINF;
AssertCompileSize(DMISYSTEMINF, 0x1b);
/** DMI board (or module) information (Type 2) */
typedef struct DMIBOARDINF
{
DMIHDR header;
uint8_t u8Manufacturer;
uint8_t u8Product;
uint8_t u8Version;
uint8_t u8SerialNumber;
uint8_t u8AssetTag;
uint8_t u8FeatureFlags;
uint8_t u8LocationInChass;
uint16_t u16ChassisHandle;
uint8_t u8BoardType;
uint8_t u8cObjectHandles;
} *PDMIBOARDINF;
AssertCompileSize(DMIBOARDINF, 0x0f);
/** DMI system enclosure or chassis type (Type 3) */
typedef struct DMICHASSIS
{
DMIHDR header;
uint8_t u8Manufacturer;
uint8_t u8Type;
uint8_t u8Version;
uint8_t u8SerialNumber;
uint8_t u8AssetTag;
uint8_t u8BootupState;
uint8_t u8PowerSupplyState;
uint8_t u8ThermalState;
uint8_t u8SecurityStatus;
/* v2.3+, currently not supported */
uint32_t u32OEMdefined;
uint8_t u8Height;
uint8_t u8NumPowerChords;
uint8_t u8ContElems;
uint8_t u8ContElemRecLen;
} *PDMICHASSIS;
AssertCompileSize(DMICHASSIS, 0x15);
/** DMI processor information (Type 4) */
typedef struct DMIPROCESSORINF
{
DMIHDR header;
uint8_t u8SocketDesignation;
uint8_t u8ProcessorType;
uint8_t u8ProcessorFamily;
uint8_t u8ProcessorManufacturer;
uint64_t u64ProcessorID;
uint8_t u8ProcessorVersion;
uint8_t u8Voltage;
uint16_t u16ExternalClock;
uint16_t u16MaxSpeed;
uint16_t u16CurrentSpeed;
uint8_t u8Status;
uint8_t u8ProcessorUpgrade;
/* v2.1+ */
uint16_t u16L1CacheHandle;
uint16_t u16L2CacheHandle;
uint16_t u16L3CacheHandle;
/* v2.3+ */
uint8_t u8SerialNumber;
uint8_t u8AssetTag;
uint8_t u8PartNumber;
/* v2.5+ */
uint8_t u8CoreCount;
uint8_t u8CoreEnabled;
uint8_t u8ThreadCount;
uint16_t u16ProcessorCharacteristics;
/* v2.6+ */
uint16_t u16ProcessorFamily2;
} *PDMIPROCESSORINF;
AssertCompileSize(DMIPROCESSORINF, 0x2a);
/** DMI OEM strings (Type 11) */
typedef struct DMIOEMSTRINGS
{
DMIHDR header;
uint8_t u8Count;
uint8_t u8VBoxVersion;
uint8_t u8VBoxRevision;
} *PDMIOEMSTRINGS;
AssertCompileSize(DMIOEMSTRINGS, 0x7);
/** DMI OEM-specific table (Type 128) */
typedef struct DMIOEMSPECIFIC
{
DMIHDR header;
uint32_t u32CpuFreqKHz;
} *PDMIOEMSPECIFIC;
AssertCompileSize(DMIOEMSPECIFIC, 0x8);
/** Physical memory array (Type 16) */
typedef struct DMIRAMARRAY
{
DMIHDR header;
uint8_t u8Location;
uint8_t u8Use;
uint8_t u8MemErrorCorrection;
uint32_t u32MaxCapacity;
uint16_t u16MemErrorHandle;
uint16_t u16NumberOfMemDevices;
} *PDMIRAMARRAY;
AssertCompileSize(DMIRAMARRAY, 15);
/** DMI Memory Device (Type 17) */
typedef struct DMIMEMORYDEV
{
DMIHDR header;
uint16_t u16PhysMemArrayHandle;
uint16_t u16MemErrHandle;
uint16_t u16TotalWidth;
uint16_t u16DataWidth;
uint16_t u16Size;
uint8_t u8FormFactor;
uint8_t u8DeviceSet;
uint8_t u8DeviceLocator;
uint8_t u8BankLocator;
uint8_t u8MemoryType;
uint16_t u16TypeDetail;
uint16_t u16Speed;
uint8_t u8Manufacturer;
uint8_t u8SerialNumber;
uint8_t u8AssetTag;
uint8_t u8PartNumber;
/* v2.6+ */
uint8_t u8Attributes;
} *PDMIMEMORYDEV;
AssertCompileSize(DMIMEMORYDEV, 28);
/** MPS floating pointer structure */
typedef struct MPSFLOATPTR
{
uint8_t au8Signature[4];
uint32_t u32MPSAddr;
uint8_t u8Length;
uint8_t u8SpecRev;
uint8_t u8Checksum;
uint8_t au8Feature[5];
} *PMPSFLOATPTR;
AssertCompileSize(MPSFLOATPTR, 16);
/** MPS config table header */
typedef struct MPSCFGTBLHEADER
{
uint8_t au8Signature[4];
uint16_t u16Length;
uint8_t u8SpecRev;
uint8_t u8Checksum;
uint8_t au8OemId[8];
uint8_t au8ProductId[12];
uint32_t u32OemTablePtr;
uint16_t u16OemTableSize;
uint16_t u16EntryCount;
uint32_t u32AddrLocalApic;
uint16_t u16ExtTableLength;
uint8_t u8ExtTableChecksum;
uint8_t u8Reserved;
} *PMPSCFGTBLHEADER;
AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
/** MPS processor entry */
typedef struct MPSPROCENTRY
{
uint8_t u8EntryType;
uint8_t u8LocalApicId;
uint8_t u8LocalApicVersion;
uint8_t u8CPUFlags;
uint32_t u32CPUSignature;
uint32_t u32CPUFeatureFlags;
uint32_t u32Reserved[2];
} *PMPSPROCENTRY;
AssertCompileSize(MPSPROCENTRY, 20);
/** MPS bus entry */
typedef struct MPSBUSENTRY
{
uint8_t u8EntryType;
uint8_t u8BusId;
uint8_t au8BusTypeStr[6];
} *PMPSBUSENTRY;
AssertCompileSize(MPSBUSENTRY, 8);
/** MPS I/O-APIC entry */
typedef struct MPSIOAPICENTRY
{
uint8_t u8EntryType;
uint8_t u8Id;
uint8_t u8Version;
uint8_t u8Flags;
uint32_t u32Addr;
} *PMPSIOAPICENTRY;
AssertCompileSize(MPSIOAPICENTRY, 8);
/** MPS I/O-Interrupt entry */
typedef struct MPSIOINTERRUPTENTRY
{
uint8_t u8EntryType;
uint8_t u8Type;
uint16_t u16Flags;
uint8_t u8SrcBusId;
uint8_t u8SrcBusIrq;
uint8_t u8DstIOAPICId;
uint8_t u8DstIOAPICInt;
} *PMPSIOIRQENTRY;
AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
#pragma pack()
/**
* Calculate a simple checksum for the MPS table.
*
* @param data data
* @param len size of data
*/
static uint8_t fwCommonChecksum(const uint8_t * const au8Data, uint32_t u32Length)
{
uint8_t u8Sum = 0;
for (size_t i = 0; i < u32Length; ++i)
u8Sum += au8Data[i];
return -u8Sum;
}
#if 0 /* unused */
static bool fwCommonChecksumOk(const uint8_t * const au8Data, uint32_t u32Length)
{
uint8_t u8Sum = 0;
for (size_t i = 0; i < u32Length; i++)
u8Sum += au8Data[i];
return (u8Sum == 0);
}
#endif
/**
* Try fetch the DMI strings from the system.
*/
static void fwCommonUseHostDMIStrings(void)
{
int rc;
rc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME,
g_szHostDmiSystemProduct, sizeof(g_szHostDmiSystemProduct));
if (RT_SUCCESS(rc))
{
g_pszDefDmiSystemProduct = g_szHostDmiSystemProduct;
LogRel(("DMI: Using DmiSystemProduct from host: %s\n", g_szHostDmiSystemProduct));
}
rc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION,
g_szHostDmiSystemVersion, sizeof(g_szHostDmiSystemVersion));
if (RT_SUCCESS(rc))
{
g_pszDefDmiSystemVersion = g_szHostDmiSystemVersion;
LogRel(("DMI: Using DmiSystemVersion from host: %s\n", g_szHostDmiSystemVersion));
}
}
/**
* 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 (cbNeed > cbMax) \
{ \
if (fHideErrors) \
{ \
LogRel(("One of the DMI strings is too long -- using default DMI data!\n")); \
continue; \
} \
return PDMDevHlpVMSetError(pDevIns, VERR_TOO_MUCH_DATA, RT_SRC_POS, \
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); \
} \
}
#define DMI_READ_CFG_STR_DEF(variable, name, default_value) \
{ \
if (fForceDefault) \
pszTmp = default_value; \
else \
{ \
rc = CFGMR3QueryStringDef(pCfg, name, szBuf, sizeof(szBuf), default_value); \
if (RT_FAILURE(rc)) \
{ \
if (fHideErrors) \
{ \
LogRel(("Configuration error: Querying \"" name "\" as a string failed -- using default DMI data!\n")); \
continue; \
} \
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
N_("Configuration error: Querying \"" name "\" as a string failed")); \
} \
else if (!strcmp(szBuf, "<EMPTY>")) \
pszTmp = ""; \
else \
pszTmp = szBuf; \
} \
if (!pszTmp[0]) \
variable = 0; /* empty string */ \
else \
{ \
variable = iStrNr++; \
size_t cStr = strlen(pszTmp) + 1; \
DMI_CHECK_SIZE(cStr); \
memcpy(pszStr, pszTmp, cStr); \
pszStr += cStr ; \
} \
}
#define DMI_READ_CFG_STR(variable, name) \
DMI_READ_CFG_STR_DEF(variable, # name, g_pszDef ## name)
#define DMI_READ_CFG_S32(variable, name) \
{ \
if (fForceDefault) \
variable = g_iDef ## name; \
else \
{ \
rc = CFGMR3QueryS32Def(pCfg, # name, & variable, g_iDef ## name); \
if (RT_FAILURE(rc)) \
{ \
if (fHideErrors) \
{ \
LogRel(("Configuration error: Querying \"" # name "\" as an int failed -- using default DMI data!\n")); \
continue; \
} \
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
N_("Configuration error: Querying \"" # name "\" as an int failed")); \
} \
} \
}
#define DMI_START_STRUCT(tbl) \
pszStr = (char *)(tbl + 1); \
iStrNr = 1;
#define DMI_TERM_STRUCT \
{ \
*pszStr++ = '\0'; /* terminate set of text strings */ \
if (iStrNr == 1) \
*pszStr++ = '\0'; /* terminate a structure without strings */ \
}
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
uint8_t fDmiUseHostInfo;
int rc = CFGMR3QueryU8Def(pCfg, "DmiUseHostInfo", &fDmiUseHostInfo, 0);
if (RT_FAILURE (rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to read \"DmiUseHostInfo\""));
/* Sync up with host default DMI values */
if (fDmiUseHostInfo)
fwCommonUseHostDMIStrings();
uint8_t fDmiExposeMemoryTable;
rc = CFGMR3QueryU8Def(pCfg, "DmiExposeMemoryTable", &fDmiExposeMemoryTable, 0);
if (RT_FAILURE (rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to read \"DmiExposeMemoryTable\""));
uint8_t fDmiExposeProcessorInf;
rc = CFGMR3QueryU8Def(pCfg, "DmiExposeProcInf", &fDmiExposeProcessorInf, 0);
if (RT_FAILURE (rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to read \"DmiExposeProcInf\""));
for (;; fForceDefault = true, fHideErrors = false)
{
int iStrNr;
char szBuf[256];
char *pszStr = (char *)pTable;
char szDmiSystemUuid[64];
char *pszDmiSystemUuid;
const char *pszTmp;
if (fForceDefault)
pszDmiSystemUuid = NULL;
else
{
rc = CFGMR3QueryString(pCfg, "DmiSystemUuid", szDmiSystemUuid, sizeof(szDmiSystemUuid));
if (rc == VERR_CFGM_VALUE_NOT_FOUND)
pszDmiSystemUuid = NULL;
else if (RT_FAILURE(rc))
{
if (fHideErrors)
{
LogRel(("Configuration error: Querying \"DmiSystemUuid\" as a string failed, using default DMI data\n"));
continue;
}
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("Configuration error: Querying \"DmiSystemUuid\" as a string failed"));
}
else
pszDmiSystemUuid = szDmiSystemUuid;
}
/*********************************
* DMI BIOS information (Type 0) *
*********************************/
PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
DMI_CHECK_SIZE(sizeof(*pBIOSInf));
pszStr = (char *)&pBIOSInf->u8ReleaseMajor;
pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8ReleaseMajor);
/* don't set these fields by default for legacy compatibility */
int iDmiBIOSReleaseMajor, iDmiBIOSReleaseMinor;
DMI_READ_CFG_S32(iDmiBIOSReleaseMajor, DmiBIOSReleaseMajor);
DMI_READ_CFG_S32(iDmiBIOSReleaseMinor, DmiBIOSReleaseMinor);
if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0)
{
pszStr = (char *)&pBIOSInf->u8FirmwareMajor;
pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8FirmwareMajor);
pBIOSInf->u8ReleaseMajor = iDmiBIOSReleaseMajor;
pBIOSInf->u8ReleaseMinor = iDmiBIOSReleaseMinor;
int iDmiBIOSFirmwareMajor, iDmiBIOSFirmwareMinor;
DMI_READ_CFG_S32(iDmiBIOSFirmwareMajor, DmiBIOSFirmwareMajor);
DMI_READ_CFG_S32(iDmiBIOSFirmwareMinor, DmiBIOSFirmwareMinor);
if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0)
{
pszStr = (char *)(pBIOSInf + 1);
pBIOSInf->header.u8Length = sizeof(DMIBIOSINF);
pBIOSInf->u8FirmwareMajor = iDmiBIOSFirmwareMajor;
pBIOSInf->u8FirmwareMinor = iDmiBIOSFirmwareMinor;
}
}
iStrNr = 1;
pBIOSInf->header.u8Type = 0; /* BIOS Information */
pBIOSInf->header.u16Handle = 0x0000;
DMI_READ_CFG_STR(pBIOSInf->u8Vendor, DmiBIOSVendor);
DMI_READ_CFG_STR(pBIOSInf->u8Version, DmiBIOSVersion);
pBIOSInf->u16Start = 0xE000;
DMI_READ_CFG_STR(pBIOSInf->u8Release, DmiBIOSReleaseDate);
pBIOSInf->u8ROMSize = 1; /* 128K */
pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */
| RT_BIT(7) /* PCI is supported */
| RT_BIT(15) /* Boot from CD is supported */
| RT_BIT(16) /* Selectable Boot is supported */
| RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */
| RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */
/* any more?? */
;
pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */
/* any more?? */
;
pBIOSInf->u8CharacteristicsByte2 = 0
/* any more?? */
;
DMI_TERM_STRUCT;
/***********************************
* DMI system information (Type 1) *
***********************************/
PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
DMI_CHECK_SIZE(sizeof(*pSystemInf));
DMI_START_STRUCT(pSystemInf);
pSystemInf->header.u8Type = 1; /* System Information */
pSystemInf->header.u8Length = sizeof(*pSystemInf);
pSystemInf->header.u16Handle = 0x0001;
DMI_READ_CFG_STR(pSystemInf->u8Manufacturer, DmiSystemVendor);
DMI_READ_CFG_STR(pSystemInf->u8ProductName, DmiSystemProduct);
DMI_READ_CFG_STR(pSystemInf->u8Version, DmiSystemVersion);
DMI_READ_CFG_STR(pSystemInf->u8SerialNumber, DmiSystemSerial);
RTUUID uuid;
if (pszDmiSystemUuid)
{
rc = RTUuidFromStr(&uuid, pszDmiSystemUuid);
if (RT_FAILURE(rc))
{
if (fHideErrors)
{
LogRel(("Configuration error: Invalid UUID for DMI tables specified, using default DMI data\n"));
continue;
}
return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
N_("Configuration error: Invalid UUID for DMI tables specified"));
}
uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
pUuid = &uuid;
}
memcpy(pSystemInf->au8Uuid, pUuid, sizeof(RTUUID));
pSystemInf->u8WakeupType = 6; /* Power Switch */
DMI_READ_CFG_STR(pSystemInf->u8SKUNumber, DmiSystemSKU);
DMI_READ_CFG_STR(pSystemInf->u8Family, DmiSystemFamily);
DMI_TERM_STRUCT;
/**********************************
* DMI board information (Type 2) *
**********************************/
PDMIBOARDINF pBoardInf = (PDMIBOARDINF)pszStr;
DMI_CHECK_SIZE(sizeof(*pBoardInf));
DMI_START_STRUCT(pBoardInf);
int iDmiBoardBoardType;
pBoardInf->header.u8Type = 2; /* Board Information */
pBoardInf->header.u8Length = sizeof(*pBoardInf);
pBoardInf->header.u16Handle = 0x0008;
DMI_READ_CFG_STR(pBoardInf->u8Manufacturer, DmiBoardVendor);
DMI_READ_CFG_STR(pBoardInf->u8Product, DmiBoardProduct);
DMI_READ_CFG_STR(pBoardInf->u8Version, DmiBoardVersion);
DMI_READ_CFG_STR(pBoardInf->u8SerialNumber, DmiBoardSerial);
DMI_READ_CFG_STR(pBoardInf->u8AssetTag, DmiBoardAssetTag);
pBoardInf->u8FeatureFlags = RT_BIT(0) /* hosting board, e.g. motherboard */
;
DMI_READ_CFG_STR(pBoardInf->u8LocationInChass, DmiBoardLocInChass);
pBoardInf->u16ChassisHandle = 0x0003; /* see type 3 */
DMI_READ_CFG_S32(iDmiBoardBoardType, DmiBoardBoardType);
pBoardInf->u8BoardType = iDmiBoardBoardType;
pBoardInf->u8cObjectHandles = 0;
DMI_TERM_STRUCT;
/********************************************
* DMI System Enclosure or Chassis (Type 3) *
********************************************/
PDMICHASSIS pChassis = (PDMICHASSIS)pszStr;
DMI_CHECK_SIZE(sizeof(*pChassis));
pszStr = (char*)&pChassis->u32OEMdefined;
iStrNr = 1;
#ifdef VBOX_WITH_DMI_CHASSIS
pChassis->header.u8Type = 3; /* System Enclosure or Chassis */
#else
pChassis->header.u8Type = 0x7e; /* inactive */
#endif
pChassis->header.u8Length = RT_OFFSETOF(DMICHASSIS, u32OEMdefined);
pChassis->header.u16Handle = 0x0003;
DMI_READ_CFG_STR(pChassis->u8Manufacturer, DmiChassisVendor);
int iDmiChassisType;
DMI_READ_CFG_S32(iDmiChassisType, DmiChassisType);
pChassis->u8Type = iDmiChassisType;
DMI_READ_CFG_STR(pChassis->u8Version, DmiChassisVersion);
DMI_READ_CFG_STR(pChassis->u8SerialNumber, DmiChassisSerial);
DMI_READ_CFG_STR(pChassis->u8AssetTag, DmiChassisAssetTag);
pChassis->u8BootupState = 0x03; /* safe */
pChassis->u8PowerSupplyState = 0x03; /* safe */
pChassis->u8ThermalState = 0x03; /* safe */
pChassis->u8SecurityStatus = 0x03; /* none XXX */
# if 0
/* v2.3+, currently not supported */
pChassis->u32OEMdefined = 0;
pChassis->u8Height = 0; /* unspecified */
pChassis->u8NumPowerChords = 0; /* unspecified */
pChassis->u8ContElems = 0; /* no contained elements */
pChassis->u8ContElemRecLen = 0; /* no contained elements */
# endif
DMI_TERM_STRUCT;
/**************************************
* 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];
PDMIPROCESSORINF pProcessorInf = (PDMIPROCESSORINF)pszStr;
DMI_CHECK_SIZE(sizeof(*pProcessorInf));
DMI_START_STRUCT(pProcessorInf);
if (fDmiExposeProcessorInf)
pProcessorInf->header.u8Type = 4; /* Processor Information */
else
pProcessorInf->header.u8Type = 126; /* inactive structure */
pProcessorInf->header.u8Length = sizeof(*pProcessorInf);
pProcessorInf->header.u16Handle = 0x0007;
RTStrPrintf(szSocket, sizeof(szSocket), "Socket #%u", 0);
pProcessorInf->u8SocketDesignation = iStrNr++;
{
size_t cStr = strlen(szSocket) + 1;
DMI_CHECK_SIZE(cStr);
memcpy(pszStr, szSocket, cStr);
pszStr += cStr;
}
pProcessorInf->u8ProcessorType = 0x03; /* Central Processor */
pProcessorInf->u8ProcessorFamily = 0xB1; /* Pentium III with Intel SpeedStep(TM) */
DMI_READ_CFG_STR(pProcessorInf->u8ProcessorManufacturer, DmiProcManufacturer);
pProcessorInf->u64ProcessorID = UINT64_C(0x0FEBFBFF00010676);
/* 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 */
DMI_READ_CFG_STR(pProcessorInf->u8ProcessorVersion, DmiProcVersion);
pProcessorInf->u8Voltage = 0x02; /* 3.3V */
pProcessorInf->u16ExternalClock = 0x00; /* unknown */
pProcessorInf->u16MaxSpeed = 3000; /* 3GHz */
pProcessorInf->u16CurrentSpeed = 3000; /* 3GHz */
pProcessorInf->u8Status = RT_BIT(6) /* CPU socket populated */
| RT_BIT(0) /* CPU enabled */
;
pProcessorInf->u8ProcessorUpgrade = 0x04; /* ZIF Socket */
pProcessorInf->u16L1CacheHandle = 0xFFFF; /* not specified */
pProcessorInf->u16L2CacheHandle = 0xFFFF; /* not specified */
pProcessorInf->u16L3CacheHandle = 0xFFFF; /* not specified */
pProcessorInf->u8SerialNumber = 0; /* not specified */
pProcessorInf->u8AssetTag = 0; /* not specified */
pProcessorInf->u8PartNumber = 0; /* not specified */
pProcessorInf->u8CoreCount = cCpus; /* */
pProcessorInf->u8CoreEnabled = cCpus;
pProcessorInf->u8ThreadCount = 1;
pProcessorInf->u16ProcessorCharacteristics
= RT_BIT(2); /* 64-bit capable */
pProcessorInf->u16ProcessorFamily2 = 0;
DMI_TERM_STRUCT;
/***************************************
* DMI Physical Memory Array (Type 16) *
***************************************/
uint64_t u64RamSize;
rc = CFGMR3QueryU64(pCfg, "RamSize", &u64RamSize);
if (RT_FAILURE (rc))
return PDMDEV_SET_ERROR(pDevIns, rc,
N_("Configuration error: Failed to read \"RamSize\""));
PDMIRAMARRAY pMemArray = (PDMIRAMARRAY)pszStr;
DMI_CHECK_SIZE(sizeof(*pMemArray));
DMI_START_STRUCT(pMemArray);
if (fDmiExposeMemoryTable)
pMemArray->header.u8Type = 16; /* Physical Memory Array */
else
pMemArray->header.u8Type = 126; /* inactive structure */
pMemArray->header.u8Length = sizeof(*pMemArray);
pMemArray->header.u16Handle = 0x0005;
pMemArray->u8Location = 0x03; /* Motherboard */
pMemArray->u8Use = 0x03; /* System memory */
pMemArray->u8MemErrorCorrection = 0x01; /* Other */
pMemArray->u32MaxCapacity = (uint32_t)(u64RamSize / _1K); /* RAM size in K */
pMemArray->u16MemErrorHandle = 0xfffe; /* No error info structure */
pMemArray->u16NumberOfMemDevices = 1;
DMI_TERM_STRUCT;
/***************************************
* DMI Memory Device (Type 17) *
***************************************/
PDMIMEMORYDEV pMemDev = (PDMIMEMORYDEV)pszStr;
DMI_CHECK_SIZE(sizeof(*pMemDev));
DMI_START_STRUCT(pMemDev);
if (fDmiExposeMemoryTable)
pMemDev->header.u8Type = 17; /* Memory Device */
else
pMemDev->header.u8Type = 126; /* inactive structure */
pMemDev->header.u8Length = sizeof(*pMemDev);
pMemDev->header.u16Handle = 0x0006;
pMemDev->u16PhysMemArrayHandle = 0x0005; /* handle of array we belong to */
pMemDev->u16MemErrHandle = 0xfffe; /* system doesn't provide this information */
pMemDev->u16TotalWidth = 0xffff; /* Unknown */
pMemDev->u16DataWidth = 0xffff; /* Unknown */
int16_t u16RamSizeM = (uint16_t)(u64RamSize / _1M);
if (u16RamSizeM == 0)
u16RamSizeM = 0x400; /* 1G */
pMemDev->u16Size = u16RamSizeM; /* RAM size */
pMemDev->u8FormFactor = 0x09; /* DIMM */
pMemDev->u8DeviceSet = 0x00; /* Not part of a device set */
DMI_READ_CFG_STR_DEF(pMemDev->u8DeviceLocator, " ", "DIMM 0");
DMI_READ_CFG_STR_DEF(pMemDev->u8BankLocator, " ", "Bank 0");
pMemDev->u8MemoryType = 0x03; /* DRAM */
pMemDev->u16TypeDetail = 0; /* Nothing special */
pMemDev->u16Speed = 1600; /* Unknown, shall be speed in MHz */
DMI_READ_CFG_STR(pMemDev->u8Manufacturer, DmiSystemVendor);
DMI_READ_CFG_STR_DEF(pMemDev->u8SerialNumber, " ", "00000000");
DMI_READ_CFG_STR_DEF(pMemDev->u8AssetTag, " ", "00000000");
DMI_READ_CFG_STR_DEF(pMemDev->u8PartNumber, " ", "00000000");
pMemDev->u8Attributes = 0; /* Unknown */
DMI_TERM_STRUCT;
/*****************************
* DMI OEM strings (Type 11) *
*****************************/
PDMIOEMSTRINGS pOEMStrings = (PDMIOEMSTRINGS)pszStr;
DMI_CHECK_SIZE(sizeof(*pOEMStrings));
DMI_START_STRUCT(pOEMStrings);
#ifdef VBOX_WITH_DMI_OEMSTRINGS
pOEMStrings->header.u8Type = 0xb; /* OEM Strings */
#else
pOEMStrings->header.u8Type = 126; /* inactive structure */
#endif
pOEMStrings->header.u8Length = sizeof(*pOEMStrings);
pOEMStrings->header.u16Handle = 0x0002;
pOEMStrings->u8Count = 2;
char szTmp[64];
RTStrPrintf(szTmp, sizeof(szTmp), "vboxVer_%u.%u.%u",
RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild());
DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxVersion, "DmiOEMVBoxVer", szTmp);
RTStrPrintf(szTmp, sizeof(szTmp), "vboxRev_%u", RTBldCfgRevision());
DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxRevision, "DmiOEMVBoxRev", szTmp);
DMI_TERM_STRUCT;
/*************************************
* DMI OEM specific table (Type 128) *
************************************/
PDMIOEMSPECIFIC pOEMSpecific = (PDMIOEMSPECIFIC)pszStr;
DMI_CHECK_SIZE(sizeof(*pOEMSpecific));
DMI_START_STRUCT(pOEMSpecific);
pOEMSpecific->header.u8Type = 0x80; /* OEM specific */
pOEMSpecific->header.u8Length = sizeof(*pOEMSpecific);
pOEMSpecific->header.u16Handle = 0x0008; /* Just next free handle */
pOEMSpecific->u32CpuFreqKHz = RT_H2LE_U32((uint32_t)((uint64_t)TMCpuTicksPerSecond(PDMDevHlpGetVM(pDevIns)) / 1000));
DMI_TERM_STRUCT;
/* End-of-table marker - includes padding to account for fixed table size. */
PDMIHDR pEndOfTable = (PDMIHDR)pszStr;
pszStr = (char *)(pEndOfTable + 1);
pEndOfTable->u8Type = 0x7f;
pEndOfTable->u8Length = sizeof(*pEndOfTable);
pEndOfTable->u16Handle = 0xFEFF;
*pcbDmiTables = ((uintptr_t)pszStr - (uintptr_t)pTable) + 2;
/* 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;
}
#undef DMI_READ_CFG_STR
#undef DMI_READ_CFG_S32
#undef DMI_CHECK_SIZE
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 SMBIOSHDR smbios;
struct DMIMAINHDR dmi;
}
aBiosHeaders =
{
// 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.dmi.u16TablesLength = cbDmiTables;
aBiosHeaders.dmi.u16TableEntries = cNumDmiTables;
aBiosHeaders.smbios.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.smbios, sizeof(aBiosHeaders.smbios));
aBiosHeaders.dmi.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.dmi, sizeof(aBiosHeaders.dmi));
PDMDevHlpPhysWrite(pDevIns, 0xfe300, &aBiosHeaders, sizeof(aBiosHeaders));
}
/**
* 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.
*/
void FwCommonPlantMpsTable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, uint16_t cCpus)
{
/* configuration table */
PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
memcpy(pCfgTab->au8Signature, "PCMP", 4);
pCfgTab->u8SpecRev = 4; /* 1.4 */
memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
pCfgTab->u32OemTablePtr = 0;
pCfgTab->u16OemTableSize = 0;
pCfgTab->u16EntryCount = 0; /* Incremented as we go. */
pCfgTab->u32AddrLocalApic = 0xfee00000;
pCfgTab->u16ExtTableLength = 0;
pCfgTab->u8ExtTableChecksum = 0;
pCfgTab->u8Reserved = 0;
uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
if (u32Eax >= 1)
{
PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
u32CPUSignature = u32Eax & 0xfff;
/* 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. */
u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
}
/* Construct MPS table for each VCPU. */
PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
for (int i = 0; i < cCpus; i++)
{
pProcEntry->u8EntryType = 0; /* processor entry */
pProcEntry->u8LocalApicId = i;
pProcEntry->u8LocalApicVersion = 0x14;
pProcEntry->u8CPUFlags = (i == 0 ? 2 /* bootstrap processor */ : 0 /* application processor */) | 1 /* enabled */;
pProcEntry->u32CPUSignature = u32CPUSignature;
pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
pProcEntry->u32Reserved[0] =
pProcEntry->u32Reserved[1] = 0;
pProcEntry++;
pCfgTab->u16EntryCount++;
}
uint32_t iBusIdIsa = 0;
uint32_t iBusIdPci0 = 1;
/* ISA bus */
PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)pProcEntry;
pBusEntry->u8EntryType = 1; /* bus entry */
pBusEntry->u8BusId = iBusIdIsa; /* this ID is referenced by the interrupt entries */
memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
pBusEntry++;
pCfgTab->u16EntryCount++;
/* PCI bus */
pBusEntry->u8EntryType = 1; /* bus entry */
pBusEntry->u8BusId = iBusIdPci0; /* this ID can be referenced by the interrupt entries */
memcpy(pBusEntry->au8BusTypeStr, "PCI ", 6);
pCfgTab->u16EntryCount++;
/* I/O-APIC.
* MP spec: "The configuration table contains one or more entries for I/O APICs.
* ... At least one I/O APIC must be enabled." */
PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1);
uint16_t iApicId = 0;
pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
pIOAPICEntry->u8Id = iApicId; /* this ID is referenced by the interrupt entries */
pIOAPICEntry->u8Version = 0x11;
pIOAPICEntry->u8Flags = 1 /* enable */;
pIOAPICEntry->u32Addr = 0xfec00000;
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.
*/
PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
for (int iPin = 1; iPin < 16; iPin++, pIrqEntry++)
{
pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
/*
* 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.
*/
pIrqEntry->u8Type = (iPin == 0) ? 3 : 0;
pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
trigger mode = conforms to bus */
pIrqEntry->u8SrcBusId = iBusIdIsa; /* ISA bus */
/* IRQ0 mapped to pin 2, other are identity mapped */
/* If changing, also update PDMIsaSetIrq() and MADT */
pIrqEntry->u8SrcBusIrq = (iPin == 2) ? 0 : iPin; /* IRQ on the bus */
pIrqEntry->u8DstIOAPICId = iApicId; /* destination IO-APIC */
pIrqEntry->u8DstIOAPICInt = iPin; /* pin on destination IO-APIC */
pCfgTab->u16EntryCount++;
}
/* Local delivery */
pIrqEntry->u8EntryType = 4; /* Local interrupt entry */
pIrqEntry->u8Type = 3; /* ExtINT */
pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */
pIrqEntry->u8SrcBusId = iBusIdIsa;
pIrqEntry->u8SrcBusIrq = 0;
pIrqEntry->u8DstIOAPICId = 0xff;
pIrqEntry->u8DstIOAPICInt = 0;
pIrqEntry++;
pCfgTab->u16EntryCount++;
pIrqEntry->u8EntryType = 4; /* Local interrupt entry */
pIrqEntry->u8Type = 1; /* NMI */
pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */
pIrqEntry->u8SrcBusId = iBusIdIsa;
pIrqEntry->u8SrcBusIrq = 0;
pIrqEntry->u8DstIOAPICId = 0xff;
pIrqEntry->u8DstIOAPICInt = 1;
pIrqEntry++;
pCfgTab->u16EntryCount++;
pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
pCfgTab->u8Checksum = fwCommonChecksum(pTable, pCfgTab->u16Length);
AssertMsg(pCfgTab->u16Length < cbMax,
("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
pCfgTab->u16Length, cbMax));
}
/**
* Construct the MPS table pointer at VM construction and reset.
*
* Only applicable if IOAPIC is active!
*
* @param pDevIns The device instance data.
*/
void FwCommonPlantMpsFloatPtr(PPDMDEVINS pDevIns)
{
MPSFLOATPTR floatPtr;
floatPtr.au8Signature[0] = '_';
floatPtr.au8Signature[1] = 'M';
floatPtr.au8Signature[2] = 'P';
floatPtr.au8Signature[3] = '_';
floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE;
floatPtr.u8Length = 1; /* structure size in paragraphs */
floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
floatPtr.u8Checksum = 0;
floatPtr.au8Feature[0] = 0;
floatPtr.au8Feature[1] = 0;
floatPtr.au8Feature[2] = 0;
floatPtr.au8Feature[3] = 0;
floatPtr.au8Feature[4] = 0;
floatPtr.u8Checksum = fwCommonChecksum((uint8_t*)&floatPtr, 16);
PDMDevHlpPhysWrite(pDevIns, 0x9fff0, &floatPtr, 16);
}