VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevFwCommon.cpp@ 96407

Last change on this file since 96407 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.4 KB
Line 
1/* $Id: DevFwCommon.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * FwCommon - Shared firmware code (used by DevPcBios & DevEFI).
4 */
5
6/*
7 * Copyright (C) 2009-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DEV
33#include <VBox/vmm/pdmdev.h>
34
35#include <VBox/log.h>
36#include <VBox/err.h>
37#include <VBox/param.h>
38
39#include <iprt/asm.h>
40#include <iprt/assert.h>
41#include <iprt/buildconfig.h>
42#include <iprt/file.h>
43#include <iprt/mem.h>
44#include <iprt/string.h>
45#include <iprt/uuid.h>
46#include <iprt/system.h>
47#include <iprt/cdefs.h>
48
49#include "VBoxDD.h"
50#include "VBoxDD2.h"
51#include "DevFwCommon.h"
52
53
54/*********************************************************************************************************************************
55* Defined Constants And Macros *
56*********************************************************************************************************************************/
57
58/*
59 * Default DMI data (legacy).
60 * Don't change this information otherwise Windows guests might demand re-activation!
61 */
62
63/* type 0 -- DMI BIOS information */
64static const int32_t g_iDefDmiBIOSReleaseMajor = 0;
65static const int32_t g_iDefDmiBIOSReleaseMinor = 0;
66static const int32_t g_iDefDmiBIOSFirmwareMajor = 0;
67static const int32_t g_iDefDmiBIOSFirmwareMinor = 0;
68static const char *g_pszDefDmiBIOSVendor = "innotek GmbH";
69static const char *g_pszDefDmiBIOSVersion = "VirtualBox";
70static const char *g_pszDefDmiBIOSReleaseDate = "12/01/2006";
71/* type 1 -- DMI system information */
72static const char *g_pszDefDmiSystemVendor = "innotek GmbH";
73static const char *g_pszDefDmiSystemProduct = "VirtualBox";
74static const char *g_pszDefDmiSystemVersion = "1.2";
75static const char *g_pszDefDmiSystemSerial = "0";
76static const char *g_pszDefDmiSystemSKU = "";
77static const char *g_pszDefDmiSystemFamily = "Virtual Machine";
78/* type 2 -- DMI board information */
79static const char *g_pszDefDmiBoardVendor = "Oracle Corporation";
80static const char *g_pszDefDmiBoardProduct = "VirtualBox";
81static const char *g_pszDefDmiBoardVersion = "1.2";
82static const char *g_pszDefDmiBoardSerial = "0";
83static const char *g_pszDefDmiBoardAssetTag = "";
84static const char *g_pszDefDmiBoardLocInChass = "";
85static const int32_t g_iDefDmiBoardBoardType = 0x0A; /* Motherboard */
86/* type 3 -- DMI chassis information */
87static const char *g_pszDefDmiChassisVendor = "Oracle Corporation";
88static const int32_t g_iDefDmiChassisType = 0x01; /* ''other'', no chassis lock present */
89static const char *g_pszDefDmiChassisVersion = "";
90static const char *g_pszDefDmiChassisSerial = "";
91static const char *g_pszDefDmiChassisAssetTag = "";
92/* type 4 -- DMI processor information */
93static const char *g_pszDefDmiProcManufacturer= "GenuineIntel";
94static const char *g_pszDefDmiProcVersion = "Pentium(R) III";
95
96/** The host DMI system product value, for DmiUseHostInfo=1. */
97static char g_szHostDmiSystemProduct[64];
98/** The host DMI system version value, for DmiUseHostInfo=1. */
99static char g_szHostDmiSystemVersion[64];
100
101
102/*********************************************************************************************************************************
103* Structures and Typedefs *
104*********************************************************************************************************************************/
105#pragma pack(1)
106
107typedef struct SMBIOSHDR
108{
109 uint8_t au8Signature[4];
110 uint8_t u8Checksum;
111 uint8_t u8Eps;
112 uint8_t u8VersionMajor;
113 uint8_t u8VersionMinor;
114 uint16_t u16MaxStructureSize;
115 uint8_t u8EntryPointRevision;
116 uint8_t u8Pad[5];
117} *SMBIOSHDRPTR;
118AssertCompileSize(SMBIOSHDR, 16);
119
120typedef struct DMIMAINHDR
121{
122 uint8_t au8Signature[5];
123 uint8_t u8Checksum;
124 uint16_t u16TablesLength;
125 uint32_t u32TableBase;
126 uint16_t u16TableEntries;
127 uint8_t u8TableVersion;
128} *DMIMAINHDRPTR;
129AssertCompileSize(DMIMAINHDR, 15);
130
131AssertCompile(sizeof(SMBIOSHDR) + sizeof(DMIMAINHDR) <= VBOX_DMI_HDR_SIZE);
132
133/** DMI header */
134typedef struct DMIHDR
135{
136 uint8_t u8Type;
137 uint8_t u8Length;
138 uint16_t u16Handle;
139} *PDMIHDR;
140AssertCompileSize(DMIHDR, 4);
141
142/** DMI BIOS information (Type 0) */
143typedef struct DMIBIOSINF
144{
145 DMIHDR header;
146 uint8_t u8Vendor;
147 uint8_t u8Version;
148 uint16_t u16Start;
149 uint8_t u8Release;
150 uint8_t u8ROMSize;
151 uint64_t u64Characteristics;
152 uint8_t u8CharacteristicsByte1;
153 uint8_t u8CharacteristicsByte2;
154 uint8_t u8ReleaseMajor;
155 uint8_t u8ReleaseMinor;
156 uint8_t u8FirmwareMajor;
157 uint8_t u8FirmwareMinor;
158} *PDMIBIOSINF;
159AssertCompileSize(DMIBIOSINF, 0x18);
160
161/** DMI system information (Type 1) */
162typedef struct DMISYSTEMINF
163{
164 DMIHDR header;
165 uint8_t u8Manufacturer;
166 uint8_t u8ProductName;
167 uint8_t u8Version;
168 uint8_t u8SerialNumber;
169 uint8_t au8Uuid[16];
170 uint8_t u8WakeupType;
171 uint8_t u8SKUNumber;
172 uint8_t u8Family;
173} *PDMISYSTEMINF;
174AssertCompileSize(DMISYSTEMINF, 0x1b);
175
176/** DMI board (or module) information (Type 2) */
177typedef struct DMIBOARDINF
178{
179 DMIHDR header;
180 uint8_t u8Manufacturer;
181 uint8_t u8Product;
182 uint8_t u8Version;
183 uint8_t u8SerialNumber;
184 uint8_t u8AssetTag;
185 uint8_t u8FeatureFlags;
186 uint8_t u8LocationInChass;
187 uint16_t u16ChassisHandle;
188 uint8_t u8BoardType;
189 uint8_t u8cObjectHandles;
190} *PDMIBOARDINF;
191AssertCompileSize(DMIBOARDINF, 0x0f);
192
193/** DMI system enclosure or chassis type (Type 3) */
194typedef struct DMICHASSIS
195{
196 DMIHDR header;
197 uint8_t u8Manufacturer;
198 uint8_t u8Type;
199 uint8_t u8Version;
200 uint8_t u8SerialNumber;
201 uint8_t u8AssetTag;
202 uint8_t u8BootupState;
203 uint8_t u8PowerSupplyState;
204 uint8_t u8ThermalState;
205 uint8_t u8SecurityStatus;
206 /* v2.3+, currently not supported */
207 uint32_t u32OEMdefined;
208 uint8_t u8Height;
209 uint8_t u8NumPowerChords;
210 uint8_t u8ContElems;
211 uint8_t u8ContElemRecLen;
212} *PDMICHASSIS;
213AssertCompileSize(DMICHASSIS, 0x15);
214
215/** DMI processor information (Type 4) */
216typedef struct DMIPROCESSORINF
217{
218 DMIHDR header;
219 uint8_t u8SocketDesignation;
220 uint8_t u8ProcessorType;
221 uint8_t u8ProcessorFamily;
222 uint8_t u8ProcessorManufacturer;
223 uint64_t u64ProcessorID;
224 uint8_t u8ProcessorVersion;
225 uint8_t u8Voltage;
226 uint16_t u16ExternalClock;
227 uint16_t u16MaxSpeed;
228 uint16_t u16CurrentSpeed;
229 uint8_t u8Status;
230 uint8_t u8ProcessorUpgrade;
231 /* v2.1+ */
232 uint16_t u16L1CacheHandle;
233 uint16_t u16L2CacheHandle;
234 uint16_t u16L3CacheHandle;
235 /* v2.3+ */
236 uint8_t u8SerialNumber;
237 uint8_t u8AssetTag;
238 uint8_t u8PartNumber;
239 /* v2.5+ */
240 uint8_t u8CoreCount;
241 uint8_t u8CoreEnabled;
242 uint8_t u8ThreadCount;
243 uint16_t u16ProcessorCharacteristics;
244 /* v2.6+ */
245 uint16_t u16ProcessorFamily2;
246} *PDMIPROCESSORINF;
247AssertCompileSize(DMIPROCESSORINF, 0x2a);
248
249/** DMI OEM strings (Type 11) */
250typedef struct DMIOEMSTRINGS
251{
252 DMIHDR header;
253 uint8_t u8Count;
254 uint8_t u8VBoxVersion;
255 uint8_t u8VBoxRevision;
256} *PDMIOEMSTRINGS;
257AssertCompileSize(DMIOEMSTRINGS, 0x7);
258
259/** DMI OEM-specific table (Type 128) */
260typedef struct DMIOEMSPECIFIC
261{
262 DMIHDR header;
263 uint32_t u32CpuFreqKHz;
264} *PDMIOEMSPECIFIC;
265AssertCompileSize(DMIOEMSPECIFIC, 0x8);
266
267/** Physical memory array (Type 16) */
268typedef struct DMIRAMARRAY
269{
270 DMIHDR header;
271 uint8_t u8Location;
272 uint8_t u8Use;
273 uint8_t u8MemErrorCorrection;
274 uint32_t u32MaxCapacity;
275 uint16_t u16MemErrorHandle;
276 uint16_t u16NumberOfMemDevices;
277} *PDMIRAMARRAY;
278AssertCompileSize(DMIRAMARRAY, 15);
279
280/** DMI Memory Device (Type 17) */
281typedef struct DMIMEMORYDEV
282{
283 DMIHDR header;
284 uint16_t u16PhysMemArrayHandle;
285 uint16_t u16MemErrHandle;
286 uint16_t u16TotalWidth;
287 uint16_t u16DataWidth;
288 uint16_t u16Size;
289 uint8_t u8FormFactor;
290 uint8_t u8DeviceSet;
291 uint8_t u8DeviceLocator;
292 uint8_t u8BankLocator;
293 uint8_t u8MemoryType;
294 uint16_t u16TypeDetail;
295 uint16_t u16Speed;
296 uint8_t u8Manufacturer;
297 uint8_t u8SerialNumber;
298 uint8_t u8AssetTag;
299 uint8_t u8PartNumber;
300 /* v2.6+ */
301 uint8_t u8Attributes;
302 /* v2.7+ */
303 uint32_t u32ExtendedSize;
304 uint16_t u16CfgSpeed; /* Configured speed in MT/sec. */
305} *PDMIMEMORYDEV;
306AssertCompileSize(DMIMEMORYDEV, 34);
307
308/** MPS floating pointer structure */
309typedef struct MPSFLOATPTR
310{
311 uint8_t au8Signature[4];
312 uint32_t u32MPSAddr;
313 uint8_t u8Length;
314 uint8_t u8SpecRev;
315 uint8_t u8Checksum;
316 uint8_t au8Feature[5];
317} *PMPSFLOATPTR;
318AssertCompileSize(MPSFLOATPTR, 16);
319
320/** MPS config table header */
321typedef struct MPSCFGTBLHEADER
322{
323 uint8_t au8Signature[4];
324 uint16_t u16Length;
325 uint8_t u8SpecRev;
326 uint8_t u8Checksum;
327 uint8_t au8OemId[8];
328 uint8_t au8ProductId[12];
329 uint32_t u32OemTablePtr;
330 uint16_t u16OemTableSize;
331 uint16_t u16EntryCount;
332 uint32_t u32AddrLocalApic;
333 uint16_t u16ExtTableLength;
334 uint8_t u8ExtTableChecksum;
335 uint8_t u8Reserved;
336} *PMPSCFGTBLHEADER;
337AssertCompileSize(MPSCFGTBLHEADER, 0x2c);
338
339/** MPS processor entry */
340typedef struct MPSPROCENTRY
341{
342 uint8_t u8EntryType;
343 uint8_t u8LocalApicId;
344 uint8_t u8LocalApicVersion;
345 uint8_t u8CPUFlags;
346 uint32_t u32CPUSignature;
347 uint32_t u32CPUFeatureFlags;
348 uint32_t u32Reserved[2];
349} *PMPSPROCENTRY;
350AssertCompileSize(MPSPROCENTRY, 20);
351
352/** MPS bus entry */
353typedef struct MPSBUSENTRY
354{
355 uint8_t u8EntryType;
356 uint8_t u8BusId;
357 uint8_t au8BusTypeStr[6];
358} *PMPSBUSENTRY;
359AssertCompileSize(MPSBUSENTRY, 8);
360
361/** MPS I/O-APIC entry */
362typedef struct MPSIOAPICENTRY
363{
364 uint8_t u8EntryType;
365 uint8_t u8Id;
366 uint8_t u8Version;
367 uint8_t u8Flags;
368 uint32_t u32Addr;
369} *PMPSIOAPICENTRY;
370AssertCompileSize(MPSIOAPICENTRY, 8);
371
372/** MPS I/O-Interrupt entry */
373typedef struct MPSIOINTERRUPTENTRY
374{
375 uint8_t u8EntryType;
376 uint8_t u8Type;
377 uint16_t u16Flags;
378 uint8_t u8SrcBusId;
379 uint8_t u8SrcBusIrq;
380 uint8_t u8DstIOAPICId;
381 uint8_t u8DstIOAPICInt;
382} *PMPSIOIRQENTRY;
383AssertCompileSize(MPSIOINTERRUPTENTRY, 8);
384
385#pragma pack()
386
387
388/**
389 * Calculate a simple checksum for the MPS table.
390 *
391 * @param au8Data data
392 * @param u32Length size of data
393 */
394static uint8_t fwCommonChecksum(const uint8_t * const au8Data, uint32_t u32Length)
395{
396 uint8_t u8Sum = 0;
397 for (size_t i = 0; i < u32Length; ++i)
398 u8Sum += au8Data[i];
399 return -u8Sum;
400}
401
402#if 0 /* unused */
403static bool fwCommonChecksumOk(const uint8_t * const au8Data, uint32_t u32Length)
404{
405 uint8_t u8Sum = 0;
406 for (size_t i = 0; i < u32Length; i++)
407 u8Sum += au8Data[i];
408 return (u8Sum == 0);
409}
410#endif
411
412/**
413 * Try fetch the DMI strings from the system.
414 */
415static void fwCommonUseHostDMIStrings(void)
416{
417 int rc;
418
419 rc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME,
420 g_szHostDmiSystemProduct, sizeof(g_szHostDmiSystemProduct));
421 if (RT_SUCCESS(rc))
422 {
423 g_pszDefDmiSystemProduct = g_szHostDmiSystemProduct;
424 LogRel(("DMI: Using DmiSystemProduct from host: %s\n", g_szHostDmiSystemProduct));
425 }
426
427 rc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION,
428 g_szHostDmiSystemVersion, sizeof(g_szHostDmiSystemVersion));
429 if (RT_SUCCESS(rc))
430 {
431 g_pszDefDmiSystemVersion = g_szHostDmiSystemVersion;
432 LogRel(("DMI: Using DmiSystemVersion from host: %s\n", g_szHostDmiSystemVersion));
433 }
434}
435
436/**
437 * Construct the DMI table.
438 *
439 * @returns VBox status code.
440 * @param pDevIns The device instance.
441 * @param pTable Where to create the DMI table.
442 * @param cbMax The maximum size of the DMI table.
443 * @param pUuid Pointer to the UUID to use if the DmiUuid
444 * configuration string isn't present.
445 * @param pCfg The handle to our config node.
446 * @param cCpus Number of VCPUs.
447 * @param pcbDmiTables Size of DMI data in bytes.
448 * @param pcDmiTables Number of DMI tables.
449 * @param fUefi Flag whether the UEFI specification is supported.
450 */
451int FwCommonPlantDMITable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, PCRTUUID pUuid, PCFGMNODE pCfg, uint16_t cCpus,
452 uint16_t *pcbDmiTables, uint16_t *pcDmiTables, bool fUefi)
453{
454 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
455
456 /*
457 * CFGM Hint!
458 *
459 * The macros below makes it a bit hard to figure out the config options
460 * available here. To get a quick hint, take a look a the CFGM
461 * validation in the calling code (DevEFI.cpp and DevPcBios.cpp).
462 *
463 * 32-bit signed integer CFGM options are read by DMI_READ_CFG_S32, the 2nd
464 * parameter is the CFGM value name.
465 *
466 * Strings are read by DMI_READ_CFG_STR and DMI_READ_CFG_STR_DEF, the 2nd parameter is
467 * the CFGM value name.
468 */
469#define DMI_CHECK_SIZE(cbWant) \
470 { \
471 size_t cbNeed = (size_t)(pszStr + cbWant - (char *)pTable) + 5; /* +1 for strtab terminator +4 for end-of-table entry */ \
472 if (cbNeed > cbMax) \
473 { \
474 if (fHideErrors) \
475 { \
476 LogRel(("One of the DMI strings is too long -- using default DMI data!\n")); \
477 continue; \
478 } \
479 return PDMDevHlpVMSetError(pDevIns, VERR_TOO_MUCH_DATA, RT_SRC_POS, \
480 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); \
481 } \
482 }
483
484#define DMI_READ_CFG_STR_DEF(variable, name, default_value) \
485 { \
486 if (fForceDefault) \
487 pszTmp = default_value; \
488 else \
489 { \
490 rc = pHlp->pfnCFGMQueryStringDef(pCfg, name, szBuf, sizeof(szBuf), default_value); \
491 if (RT_FAILURE(rc)) \
492 { \
493 if (fHideErrors) \
494 { \
495 LogRel(("Configuration error: Querying \"" name "\" as a string failed -- using default DMI data!\n")); \
496 continue; \
497 } \
498 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
499 N_("Configuration error: Querying \"" name "\" as a string failed")); \
500 } \
501 if (!strcmp(szBuf, "<EMPTY>")) \
502 pszTmp = ""; \
503 else \
504 pszTmp = szBuf; \
505 } \
506 if (!pszTmp[0]) \
507 variable = 0; /* empty string */ \
508 else \
509 { \
510 variable = iStrNr++; \
511 size_t const cbStr = strlen(pszTmp) + 1; \
512 DMI_CHECK_SIZE(cbStr); \
513 pszStr = (char *)mempcpy(pszStr, pszTmp, cbStr); \
514 } \
515 }
516
517#define DMI_READ_CFG_STR(variable, name) \
518 DMI_READ_CFG_STR_DEF(variable, # name, g_pszDef ## name)
519
520#define DMI_READ_CFG_S32(variable, name) \
521 { \
522 if (fForceDefault) \
523 variable = g_iDef ## name; \
524 else \
525 { \
526 rc = pHlp->pfnCFGMQueryS32Def(pCfg, # name, & variable, g_iDef ## name); \
527 if (RT_FAILURE(rc)) \
528 { \
529 if (fHideErrors) \
530 { \
531 LogRel(("Configuration error: Querying \"" # name "\" as an int failed -- using default DMI data!\n")); \
532 continue; \
533 } \
534 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS, \
535 N_("Configuration error: Querying \"" # name "\" as an int failed")); \
536 } \
537 } \
538 }
539
540#define DMI_START_STRUCT(a_pTbl) do { \
541 pszStr = (char *)((a_pTbl) + 1); \
542 iStrNr = 1; \
543 } while (0)
544
545#if 0 /* GCC 11.2.1 barfs on this: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=] */
546# define DMI_TERM_STRUCT do { \
547 *pszStr++ = '\0'; /* terminate set of text strings */ \
548 if (iStrNr == 1) \
549 *pszStr++ = '\0'; /* terminate a structure without strings */ \
550 } while (0)
551#else
552# define DMI_TERM_STRUCT do { \
553 size_t const cbToZero = iStrNr == 1 ? 2 : 1; \
554 pszStr = (char *)memset(pszStr, 0, cbToZero) + cbToZero; \
555 } while (0)
556#endif
557
558 bool fForceDefault = false;
559#ifdef VBOX_BIOS_DMI_FALLBACK
560 /*
561 * There will be two passes. If an error occurs during the first pass, a
562 * message will be written to the release log and we fall back to default
563 * DMI data and start a second pass.
564 */
565 bool fHideErrors = true;
566#else
567 /*
568 * There will be one pass, every error is fatal and will prevent the VM
569 * from starting.
570 */
571 bool fHideErrors = false;
572#endif
573
574 uint8_t fDmiUseHostInfo;
575 int rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DmiUseHostInfo", &fDmiUseHostInfo, 0);
576 if (RT_FAILURE (rc))
577 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiUseHostInfo\""));
578
579 /* Sync up with host default DMI values */
580 if (fDmiUseHostInfo)
581 fwCommonUseHostDMIStrings();
582
583 uint8_t fDmiExposeMemoryTable;
584 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DmiExposeMemoryTable", &fDmiExposeMemoryTable, 0);
585 if (RT_FAILURE (rc))
586 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiExposeMemoryTable\""));
587 uint8_t fDmiExposeProcessorInf;
588 rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DmiExposeProcInf", &fDmiExposeProcessorInf, 0);
589 if (RT_FAILURE (rc))
590 return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to read \"DmiExposeProcInf\""));
591
592 for (;; fForceDefault = true, fHideErrors = false)
593 {
594 int iStrNr;
595 char szBuf[256];
596 char *pszStr = (char *)pTable;
597 char szDmiSystemUuid[64];
598 char *pszDmiSystemUuid;
599 const char *pszTmp;
600
601 if (fForceDefault)
602 pszDmiSystemUuid = NULL;
603 else
604 {
605 rc = pHlp->pfnCFGMQueryString(pCfg, "DmiSystemUuid", szDmiSystemUuid, sizeof(szDmiSystemUuid));
606 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
607 pszDmiSystemUuid = NULL;
608 else if (RT_FAILURE(rc))
609 {
610 if (fHideErrors)
611 {
612 LogRel(("Configuration error: Querying \"DmiSystemUuid\" as a string failed, using default DMI data\n"));
613 continue;
614 }
615 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
616 N_("Configuration error: Querying \"DmiSystemUuid\" as a string failed"));
617 }
618 else
619 pszDmiSystemUuid = szDmiSystemUuid;
620 }
621
622 /*********************************
623 * DMI BIOS information (Type 0) *
624 *********************************/
625 PDMIBIOSINF pBIOSInf = (PDMIBIOSINF)pszStr;
626 DMI_CHECK_SIZE(sizeof(*pBIOSInf));
627
628 pszStr = (char *)&pBIOSInf->u8ReleaseMajor;
629 pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8ReleaseMajor);
630
631 /* don't set these fields by default for legacy compatibility */
632 int iDmiBIOSReleaseMajor, iDmiBIOSReleaseMinor;
633 DMI_READ_CFG_S32(iDmiBIOSReleaseMajor, DmiBIOSReleaseMajor);
634 DMI_READ_CFG_S32(iDmiBIOSReleaseMinor, DmiBIOSReleaseMinor);
635 if (iDmiBIOSReleaseMajor != 0 || iDmiBIOSReleaseMinor != 0)
636 {
637 pszStr = (char *)&pBIOSInf->u8FirmwareMajor;
638 pBIOSInf->header.u8Length = RT_OFFSETOF(DMIBIOSINF, u8FirmwareMajor);
639 pBIOSInf->u8ReleaseMajor = iDmiBIOSReleaseMajor;
640 pBIOSInf->u8ReleaseMinor = iDmiBIOSReleaseMinor;
641
642 int iDmiBIOSFirmwareMajor, iDmiBIOSFirmwareMinor;
643 DMI_READ_CFG_S32(iDmiBIOSFirmwareMajor, DmiBIOSFirmwareMajor);
644 DMI_READ_CFG_S32(iDmiBIOSFirmwareMinor, DmiBIOSFirmwareMinor);
645 if (iDmiBIOSFirmwareMajor != 0 || iDmiBIOSFirmwareMinor != 0)
646 {
647 pszStr = (char *)(pBIOSInf + 1);
648 pBIOSInf->header.u8Length = sizeof(DMIBIOSINF);
649 pBIOSInf->u8FirmwareMajor = iDmiBIOSFirmwareMajor;
650 pBIOSInf->u8FirmwareMinor = iDmiBIOSFirmwareMinor;
651 }
652 }
653
654 iStrNr = 1;
655 pBIOSInf->header.u8Type = 0; /* BIOS Information */
656 pBIOSInf->header.u16Handle = 0x0000;
657 DMI_READ_CFG_STR(pBIOSInf->u8Vendor, DmiBIOSVendor);
658 DMI_READ_CFG_STR(pBIOSInf->u8Version, DmiBIOSVersion);
659 pBIOSInf->u16Start = 0xE000;
660 DMI_READ_CFG_STR(pBIOSInf->u8Release, DmiBIOSReleaseDate);
661 pBIOSInf->u8ROMSize = 1; /* 128K */
662 pBIOSInf->u64Characteristics = RT_BIT(4) /* ISA is supported */
663 | RT_BIT(7) /* PCI is supported */
664 | RT_BIT(15) /* Boot from CD is supported */
665 | RT_BIT(16) /* Selectable Boot is supported */
666 | RT_BIT(27) /* Int 9h, 8042 Keyboard services supported */
667 | RT_BIT(30) /* Int 10h, CGA/Mono Video Services supported */
668 /* any more?? */
669 ;
670 pBIOSInf->u8CharacteristicsByte1 = RT_BIT(0) /* ACPI is supported */
671 /* any more?? */
672 ;
673 pBIOSInf->u8CharacteristicsByte2 = fUefi ? RT_BIT(3) : 0
674 /* any more?? */
675 ;
676 DMI_TERM_STRUCT;
677
678 /***********************************
679 * DMI system information (Type 1) *
680 ***********************************/
681 PDMISYSTEMINF pSystemInf = (PDMISYSTEMINF)pszStr;
682 DMI_CHECK_SIZE(sizeof(*pSystemInf));
683 DMI_START_STRUCT(pSystemInf);
684 pSystemInf->header.u8Type = 1; /* System Information */
685 pSystemInf->header.u8Length = sizeof(*pSystemInf);
686 pSystemInf->header.u16Handle = 0x0001;
687 DMI_READ_CFG_STR(pSystemInf->u8Manufacturer, DmiSystemVendor);
688 DMI_READ_CFG_STR(pSystemInf->u8ProductName, DmiSystemProduct);
689 DMI_READ_CFG_STR(pSystemInf->u8Version, DmiSystemVersion);
690 DMI_READ_CFG_STR(pSystemInf->u8SerialNumber, DmiSystemSerial);
691
692 RTUUID uuid;
693 if (pszDmiSystemUuid)
694 {
695 rc = RTUuidFromStr(&uuid, pszDmiSystemUuid);
696 if (RT_FAILURE(rc))
697 {
698 if (fHideErrors)
699 {
700 LogRel(("Configuration error: Invalid UUID for DMI tables specified, using default DMI data\n"));
701 continue;
702 }
703 return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
704 N_("Configuration error: Invalid UUID for DMI tables specified"));
705 }
706 uuid.Gen.u32TimeLow = RT_H2BE_U32(uuid.Gen.u32TimeLow);
707 uuid.Gen.u16TimeMid = RT_H2BE_U16(uuid.Gen.u16TimeMid);
708 uuid.Gen.u16TimeHiAndVersion = RT_H2BE_U16(uuid.Gen.u16TimeHiAndVersion);
709 pUuid = &uuid;
710 }
711 memcpy(pSystemInf->au8Uuid, pUuid, sizeof(RTUUID));
712
713 pSystemInf->u8WakeupType = 6; /* Power Switch */
714 DMI_READ_CFG_STR(pSystemInf->u8SKUNumber, DmiSystemSKU);
715 DMI_READ_CFG_STR(pSystemInf->u8Family, DmiSystemFamily);
716 DMI_TERM_STRUCT;
717
718 /**********************************
719 * DMI board information (Type 2) *
720 **********************************/
721 PDMIBOARDINF pBoardInf = (PDMIBOARDINF)pszStr;
722 DMI_CHECK_SIZE(sizeof(*pBoardInf));
723 DMI_START_STRUCT(pBoardInf);
724 int iDmiBoardBoardType;
725 pBoardInf->header.u8Type = 2; /* Board Information */
726 pBoardInf->header.u8Length = sizeof(*pBoardInf);
727 pBoardInf->header.u16Handle = 0x0008;
728 DMI_READ_CFG_STR(pBoardInf->u8Manufacturer, DmiBoardVendor);
729 DMI_READ_CFG_STR(pBoardInf->u8Product, DmiBoardProduct);
730 DMI_READ_CFG_STR(pBoardInf->u8Version, DmiBoardVersion);
731 DMI_READ_CFG_STR(pBoardInf->u8SerialNumber, DmiBoardSerial);
732 DMI_READ_CFG_STR(pBoardInf->u8AssetTag, DmiBoardAssetTag);
733 pBoardInf->u8FeatureFlags = RT_BIT(0) /* hosting board, e.g. motherboard */
734 ;
735 DMI_READ_CFG_STR(pBoardInf->u8LocationInChass, DmiBoardLocInChass);
736 pBoardInf->u16ChassisHandle = 0x0003; /* see type 3 */
737 DMI_READ_CFG_S32(iDmiBoardBoardType, DmiBoardBoardType);
738 pBoardInf->u8BoardType = iDmiBoardBoardType;
739 pBoardInf->u8cObjectHandles = 0;
740
741 DMI_TERM_STRUCT;
742
743 /********************************************
744 * DMI System Enclosure or Chassis (Type 3) *
745 ********************************************/
746 PDMICHASSIS pChassis = (PDMICHASSIS)pszStr;
747 DMI_CHECK_SIZE(sizeof(*pChassis));
748 pszStr = (char *)&pChassis->u32OEMdefined;
749 iStrNr = 1;
750#ifdef VBOX_WITH_DMI_CHASSIS
751 pChassis->header.u8Type = 3; /* System Enclosure or Chassis */
752#else
753 pChassis->header.u8Type = 0x7e; /* inactive */
754#endif
755 pChassis->header.u8Length = RT_OFFSETOF(DMICHASSIS, u32OEMdefined);
756 pChassis->header.u16Handle = 0x0003;
757 DMI_READ_CFG_STR(pChassis->u8Manufacturer, DmiChassisVendor);
758 int iDmiChassisType;
759 DMI_READ_CFG_S32(iDmiChassisType, DmiChassisType);
760 pChassis->u8Type = iDmiChassisType;
761 DMI_READ_CFG_STR(pChassis->u8Version, DmiChassisVersion);
762 DMI_READ_CFG_STR(pChassis->u8SerialNumber, DmiChassisSerial);
763 DMI_READ_CFG_STR(pChassis->u8AssetTag, DmiChassisAssetTag);
764 pChassis->u8BootupState = 0x03; /* safe */
765 pChassis->u8PowerSupplyState = 0x03; /* safe */
766 pChassis->u8ThermalState = 0x03; /* safe */
767 pChassis->u8SecurityStatus = 0x03; /* none XXX */
768# if 0
769 /* v2.3+, currently not supported */
770 pChassis->u32OEMdefined = 0;
771 pChassis->u8Height = 0; /* unspecified */
772 pChassis->u8NumPowerChords = 0; /* unspecified */
773 pChassis->u8ContElems = 0; /* no contained elements */
774 pChassis->u8ContElemRecLen = 0; /* no contained elements */
775# endif
776 DMI_TERM_STRUCT;
777
778 /**************************************
779 * DMI Processor Information (Type 4) *
780 **************************************/
781
782 /*
783 * This is just a dummy processor. Should we expose the real guest CPU features
784 * here? Accessing this information at this point is difficult.
785 */
786 char szSocket[32];
787 PDMIPROCESSORINF pProcessorInf = (PDMIPROCESSORINF)pszStr;
788 DMI_CHECK_SIZE(sizeof(*pProcessorInf));
789 DMI_START_STRUCT(pProcessorInf);
790 if (fDmiExposeProcessorInf)
791 pProcessorInf->header.u8Type = 4; /* Processor Information */
792 else
793 pProcessorInf->header.u8Type = 126; /* inactive structure */
794 pProcessorInf->header.u8Length = sizeof(*pProcessorInf);
795 pProcessorInf->header.u16Handle = 0x0007;
796 RTStrPrintf(szSocket, sizeof(szSocket), "Socket #%u", 0);
797 pProcessorInf->u8SocketDesignation = iStrNr++;
798 {
799 size_t const cbStr = strlen(szSocket) + 1;
800 DMI_CHECK_SIZE(cbStr);
801 pszStr = (char *)mempcpy(pszStr, szSocket, cbStr);
802 }
803 pProcessorInf->u8ProcessorType = 0x03; /* Central Processor */
804 pProcessorInf->u8ProcessorFamily = 0xB1; /* Pentium III with Intel SpeedStep(TM) */
805 DMI_READ_CFG_STR(pProcessorInf->u8ProcessorManufacturer, DmiProcManufacturer);
806
807 pProcessorInf->u64ProcessorID = UINT64_C(0x0FEBFBFF00010676);
808 /* Ext Family ID = 0
809 * Ext Model ID = 2
810 * Processor Type = 0
811 * Family ID = 6
812 * Model = 7
813 * Stepping = 6
814 * Features: FPU, VME, DE, PSE, TSC, MSR, PAE, MCE, CX8,
815 * APIC, SEP, MTRR, PGE, MCA, CMOV, PAT, PSE-36,
816 * CFLSH, DS, ACPI, MMX, FXSR, SSE, SSE2, SS */
817 DMI_READ_CFG_STR(pProcessorInf->u8ProcessorVersion, DmiProcVersion);
818 pProcessorInf->u8Voltage = 0x02; /* 3.3V */
819 pProcessorInf->u16ExternalClock = 0x00; /* unknown */
820 pProcessorInf->u16MaxSpeed = 3000; /* 3GHz */
821 pProcessorInf->u16CurrentSpeed = 3000; /* 3GHz */
822 pProcessorInf->u8Status = RT_BIT(6) /* CPU socket populated */
823 | RT_BIT(0) /* CPU enabled */
824 ;
825 pProcessorInf->u8ProcessorUpgrade = 0x04; /* ZIF Socket */
826 pProcessorInf->u16L1CacheHandle = 0xFFFF; /* not specified */
827 pProcessorInf->u16L2CacheHandle = 0xFFFF; /* not specified */
828 pProcessorInf->u16L3CacheHandle = 0xFFFF; /* not specified */
829 pProcessorInf->u8SerialNumber = 0; /* not specified */
830 pProcessorInf->u8AssetTag = 0; /* not specified */
831 pProcessorInf->u8PartNumber = 0; /* not specified */
832 pProcessorInf->u8CoreCount = cCpus; /* */
833 pProcessorInf->u8CoreEnabled = cCpus;
834 pProcessorInf->u8ThreadCount = 1;
835 pProcessorInf->u16ProcessorCharacteristics
836 = RT_BIT(2); /* 64-bit capable */
837 pProcessorInf->u16ProcessorFamily2 = 0;
838 DMI_TERM_STRUCT;
839
840 /***************************************
841 * DMI Physical Memory Array (Type 16) *
842 ***************************************/
843 uint64_t const cbRamSize = PDMDevHlpMMPhysGetRamSize(pDevIns);
844
845 PDMIRAMARRAY pMemArray = (PDMIRAMARRAY)pszStr;
846 DMI_CHECK_SIZE(sizeof(*pMemArray));
847 DMI_START_STRUCT(pMemArray);
848 if (fDmiExposeMemoryTable)
849 pMemArray->header.u8Type = 16; /* Physical Memory Array */
850 else
851 pMemArray->header.u8Type = 126; /* inactive structure */
852 pMemArray->header.u8Length = sizeof(*pMemArray);
853 pMemArray->header.u16Handle = 0x0005;
854 pMemArray->u8Location = 0x03; /* Motherboard */
855 pMemArray->u8Use = 0x03; /* System memory */
856 pMemArray->u8MemErrorCorrection = 0x01; /* Other */
857 if (cbRamSize / _1K > INT32_MAX)
858 {
859 /** @todo 2TB-1K limit. In such cases we probably need to provide multiple type-16 descriptors.
860 * Or use 0x8000'0000 = 'capacity unknown'? */
861 AssertLogRelMsgFailed(("DMI: RAM size %#RX64 does not fit into type-16 descriptor, clipping to %#RX64\n",
862 cbRamSize, (uint64_t)INT32_MAX * _1K));
863 pMemArray->u32MaxCapacity = INT32_MAX;
864 }
865 else
866 pMemArray->u32MaxCapacity = (int32_t)(cbRamSize / _1K); /* RAM size in K */
867 pMemArray->u16MemErrorHandle = 0xfffe; /* No error info structure */
868 pMemArray->u16NumberOfMemDevices = 1;
869 DMI_TERM_STRUCT;
870
871 /***************************************
872 * DMI Memory Device (Type 17) *
873 ***************************************/
874 PDMIMEMORYDEV pMemDev = (PDMIMEMORYDEV)pszStr;
875 DMI_CHECK_SIZE(sizeof(*pMemDev));
876 DMI_START_STRUCT(pMemDev);
877 if (fDmiExposeMemoryTable)
878 pMemDev->header.u8Type = 17; /* Memory Device */
879 else
880 pMemDev->header.u8Type = 126; /* inactive structure */
881 pMemDev->header.u8Length = sizeof(*pMemDev);
882 pMemDev->header.u16Handle = 0x0006;
883 pMemDev->u16PhysMemArrayHandle = 0x0005; /* handle of array we belong to */
884 pMemDev->u16MemErrHandle = 0xfffe; /* system doesn't provide this information */
885 pMemDev->u16TotalWidth = 0xffff; /* Unknown */
886 pMemDev->u16DataWidth = 0xffff; /* Unknown */
887 int16_t u16RamSizeM;
888 int32_t u32ExtRamSizeM = 0;
889 if (cbRamSize / _1M > INT16_MAX)
890 {
891 /* The highest bit of u16Size must be 0 to specify 'MB' units / 1 would be 'KB'.
892 * SMBIOS 2.7 introduced a 32-bit extended size. If module size is 32GB or greater,
893 * the old u16Size is set to 7FFFh; old parsers will see 32GB-1MB, new parsers will
894 * look at new u32ExtendedSize which can represent at least 128TB. OS X 10.14+ looks
895 * at the extended size.
896 */
897 LogRel(("DMI: RAM size %#RX64 too big for one type-17 descriptor, clipping to %#RX64\n",
898 cbRamSize, (uint64_t)INT16_MAX * _1M));
899 u16RamSizeM = INT16_MAX;
900 if (cbRamSize / _1M >= 0x8000000) {
901 AssertLogRelMsgFailed(("DMI: RAM size %#RX64 too big for one type-17 descriptor, clipping to %#RX64\n",
902 cbRamSize, (uint64_t)INT32_MAX * _1M));
903 u32ExtRamSizeM = 0x8000000; /* 128TB */
904 }
905 else
906 u32ExtRamSizeM = cbRamSize / _1M;
907 }
908 else
909 u16RamSizeM = (uint16_t)(cbRamSize / _1M);
910 if (u16RamSizeM == 0)
911 u16RamSizeM = 0x400; /* 1G */
912 pMemDev->u16Size = u16RamSizeM; /* RAM size */
913 pMemDev->u32ExtendedSize = u32ExtRamSizeM;
914 pMemDev->u8FormFactor = 0x09; /* DIMM */
915 pMemDev->u8DeviceSet = 0x00; /* Not part of a device set */
916 DMI_READ_CFG_STR_DEF(pMemDev->u8DeviceLocator, " ", "DIMM 0");
917 DMI_READ_CFG_STR_DEF(pMemDev->u8BankLocator, " ", "Bank 0");
918 pMemDev->u8MemoryType = 0x03; /* DRAM */
919 pMemDev->u16TypeDetail = 0; /* Nothing special */
920 pMemDev->u16Speed = 1600; /* Unknown, shall be speed in MHz */
921 DMI_READ_CFG_STR(pMemDev->u8Manufacturer, DmiSystemVendor);
922 DMI_READ_CFG_STR_DEF(pMemDev->u8SerialNumber, " ", "00000000");
923 DMI_READ_CFG_STR_DEF(pMemDev->u8AssetTag, " ", "00000000");
924 DMI_READ_CFG_STR_DEF(pMemDev->u8PartNumber, " ", "00000000");
925 pMemDev->u8Attributes = 0; /* Unknown */
926 DMI_TERM_STRUCT;
927
928 /*****************************
929 * DMI OEM strings (Type 11) *
930 *****************************/
931 PDMIOEMSTRINGS pOEMStrings = (PDMIOEMSTRINGS)pszStr;
932 DMI_CHECK_SIZE(sizeof(*pOEMStrings));
933 DMI_START_STRUCT(pOEMStrings);
934#ifdef VBOX_WITH_DMI_OEMSTRINGS
935 pOEMStrings->header.u8Type = 0xb; /* OEM Strings */
936#else
937 pOEMStrings->header.u8Type = 126; /* inactive structure */
938#endif
939 pOEMStrings->header.u8Length = sizeof(*pOEMStrings);
940 pOEMStrings->header.u16Handle = 0x0002;
941 pOEMStrings->u8Count = 2;
942
943 char szTmp[64];
944 RTStrPrintf(szTmp, sizeof(szTmp), "vboxVer_%u.%u.%u",
945 RTBldCfgVersionMajor(), RTBldCfgVersionMinor(), RTBldCfgVersionBuild());
946 DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxVersion, "DmiOEMVBoxVer", szTmp);
947 RTStrPrintf(szTmp, sizeof(szTmp), "vboxRev_%u", RTBldCfgRevision());
948 DMI_READ_CFG_STR_DEF(pOEMStrings->u8VBoxRevision, "DmiOEMVBoxRev", szTmp);
949 DMI_TERM_STRUCT;
950
951 /*************************************
952 * DMI OEM specific table (Type 128) *
953 ************************************/
954 PDMIOEMSPECIFIC pOEMSpecific = (PDMIOEMSPECIFIC)pszStr;
955 DMI_CHECK_SIZE(sizeof(*pOEMSpecific));
956 DMI_START_STRUCT(pOEMSpecific);
957 pOEMSpecific->header.u8Type = 0x80; /* OEM specific */
958 pOEMSpecific->header.u8Length = sizeof(*pOEMSpecific);
959 pOEMSpecific->header.u16Handle = 0x0008; /* Just next free handle */
960 pOEMSpecific->u32CpuFreqKHz = RT_H2LE_U32((uint32_t)((uint64_t)PDMDevHlpTMCpuTicksPerSecond(pDevIns) / 1000));
961 DMI_TERM_STRUCT;
962
963 /* End-of-table marker - includes padding to account for fixed table size. */
964 PDMIHDR pEndOfTable = (PDMIHDR)pszStr;
965 pszStr = (char *)(pEndOfTable + 1);
966 pEndOfTable->u8Type = 0x7f;
967
968 pEndOfTable->u8Length = sizeof(*pEndOfTable);
969 pEndOfTable->u16Handle = 0xFEFF;
970 *pcbDmiTables = ((uintptr_t)pszStr - (uintptr_t)pTable) + 2;
971
972 /* We currently plant 10 DMI tables. Update this if tables number changed. */
973 *pcDmiTables = 10;
974
975 /* If more fields are added here, fix the size check in DMI_READ_CFG_STR */
976
977 /* Success! */
978 break;
979 }
980
981#undef DMI_READ_CFG_STR
982#undef DMI_READ_CFG_S32
983#undef DMI_CHECK_SIZE
984 return VINF_SUCCESS;
985}
986
987/**
988 * Construct the SMBIOS and DMI headers table pointer at VM construction and
989 * reset.
990 *
991 * @param pDevIns The device instance data.
992 * @param pHdr Pointer to the header destination.
993 * @param cbDmiTables Size of all DMI tables planted in bytes.
994 * @param cNumDmiTables Number of DMI tables planted.
995 */
996void FwCommonPlantSmbiosAndDmiHdrs(PPDMDEVINS pDevIns, uint8_t *pHdr, uint16_t cbDmiTables, uint16_t cNumDmiTables)
997{
998 RT_NOREF(pDevIns);
999
1000 struct
1001 {
1002 struct SMBIOSHDR smbios;
1003 struct DMIMAINHDR dmi;
1004 }
1005 aBiosHeaders =
1006 {
1007 // The SMBIOS header
1008 {
1009 { 0x5f, 0x53, 0x4d, 0x5f}, // "_SM_" signature
1010 0x00, // checksum
1011 0x1f, // EPS length, defined by standard
1012 VBOX_SMBIOS_MAJOR_VER, // SMBIOS major version
1013 VBOX_SMBIOS_MINOR_VER, // SMBIOS minor version
1014 VBOX_SMBIOS_MAXSS, // Maximum structure size
1015 0x00, // Entry point revision
1016 { 0x00, 0x00, 0x00, 0x00, 0x00 } // padding
1017 },
1018 // The DMI header
1019 {
1020 { 0x5f, 0x44, 0x4d, 0x49, 0x5f }, // "_DMI_" signature
1021 0x00, // checksum
1022 0, // DMI tables length
1023 VBOX_DMI_TABLE_BASE, // DMI tables base
1024 0, // DMI tables entries
1025 VBOX_DMI_TABLE_VER, // DMI version
1026 }
1027 };
1028
1029 aBiosHeaders.dmi.u16TablesLength = cbDmiTables;
1030 aBiosHeaders.dmi.u16TableEntries = cNumDmiTables;
1031 /* NB: The _SM_ table checksum technically covers both the _SM_ part (16 bytes) and the _DMI_ part
1032 * (further 15 bytes). However, because the _DMI_ checksum must be zero, the _SM_ checksum can
1033 * be calculated independently.
1034 */
1035 aBiosHeaders.smbios.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.smbios, sizeof(aBiosHeaders.smbios));
1036 aBiosHeaders.dmi.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.dmi, sizeof(aBiosHeaders.dmi));
1037
1038 memcpy(pHdr, &aBiosHeaders, sizeof(aBiosHeaders));
1039}
1040
1041/**
1042 * Construct the MPS table for implanting as a ROM page.
1043 *
1044 * Only applicable if IOAPIC is active!
1045 *
1046 * See ``MultiProcessor Specification Version 1.4 (May 1997)'':
1047 * ``1.3 Scope
1048 * ...
1049 * The hardware required to implement the MP specification is kept to a
1050 * minimum, as follows:
1051 * * One or more processors that are Intel architecture instruction set
1052 * compatible, such as the CPUs in the Intel486 or Pentium processor
1053 * family.
1054 * * One or more APICs, such as the Intel 82489DX Advanced Programmable
1055 * Interrupt Controller or the integrated APIC, such as that on the
1056 * Intel Pentium 735\\90 and 815\\100 processors, together with a discrete
1057 * I/O APIC unit.''
1058 * and later:
1059 * ``4.3.3 I/O APIC Entries
1060 * The configuration table contains one or more entries for I/O APICs.
1061 * ...
1062 * I/O APIC FLAGS: EN 3:0 1 If zero, this I/O APIC is unusable, and the
1063 * operating system should not attempt to access
1064 * this I/O APIC.
1065 * At least one I/O APIC must be enabled.''
1066 *
1067 * @param pDevIns The device instance data.
1068 * @param pTable Where to write the table.
1069 * @param cbMax The maximum size of the MPS table.
1070 * @param cCpus The number of guest CPUs.
1071 */
1072void FwCommonPlantMpsTable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, uint16_t cCpus)
1073{
1074 RT_NOREF1(cbMax);
1075
1076 /* configuration table */
1077 PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable;
1078 memcpy(pCfgTab->au8Signature, "PCMP", 4);
1079 pCfgTab->u8SpecRev = 4; /* 1.4 */
1080 memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8);
1081 memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12);
1082 pCfgTab->u32OemTablePtr = 0;
1083 pCfgTab->u16OemTableSize = 0;
1084 pCfgTab->u16EntryCount = 0; /* Incremented as we go. */
1085 pCfgTab->u32AddrLocalApic = 0xfee00000;
1086 pCfgTab->u16ExtTableLength = 0;
1087 pCfgTab->u8ExtTableChecksum = 0;
1088 pCfgTab->u8Reserved = 0;
1089
1090 uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx;
1091 uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */
1092 uint32_t u32FeatureFlags = 0x0001; /* default: FPU */
1093 PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
1094 if (u32Eax >= 1)
1095 {
1096 PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx);
1097 u32CPUSignature = u32Eax & 0xfff;
1098 /* Local APIC will be enabled later so override it here. Since we provide
1099 * an MP table we have an IOAPIC and therefore a Local APIC. */
1100 u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC;
1101 }
1102 /* Construct MPS table for each VCPU. */
1103 PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1);
1104 for (int i = 0; i < cCpus; i++)
1105 {
1106 pProcEntry->u8EntryType = 0; /* processor entry */
1107 pProcEntry->u8LocalApicId = i;
1108 pProcEntry->u8LocalApicVersion = 0x14;
1109 pProcEntry->u8CPUFlags = (i == 0 ? 2 /* bootstrap processor */ : 0 /* application processor */) | 1 /* enabled */;
1110 pProcEntry->u32CPUSignature = u32CPUSignature;
1111 pProcEntry->u32CPUFeatureFlags = u32FeatureFlags;
1112 pProcEntry->u32Reserved[0] =
1113 pProcEntry->u32Reserved[1] = 0;
1114 pProcEntry++;
1115 pCfgTab->u16EntryCount++;
1116 }
1117
1118 uint32_t iBusIdIsa = 0;
1119 uint32_t iBusIdPci0 = 1;
1120
1121 /* ISA bus */
1122 PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)pProcEntry;
1123 pBusEntry->u8EntryType = 1; /* bus entry */
1124 pBusEntry->u8BusId = iBusIdIsa; /* this ID is referenced by the interrupt entries */
1125 memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6);
1126 pBusEntry++;
1127 pCfgTab->u16EntryCount++;
1128
1129 /* PCI bus */
1130 pBusEntry->u8EntryType = 1; /* bus entry */
1131 pBusEntry->u8BusId = iBusIdPci0; /* this ID can be referenced by the interrupt entries */
1132 memcpy(pBusEntry->au8BusTypeStr, "PCI ", 6);
1133 pBusEntry++;
1134 pCfgTab->u16EntryCount++;
1135
1136
1137 /* I/O-APIC.
1138 * MP spec: "The configuration table contains one or more entries for I/O APICs.
1139 * ... At least one I/O APIC must be enabled." */
1140 PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry);
1141 uint16_t iApicId = 0;
1142 pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */
1143 pIOAPICEntry->u8Id = iApicId; /* this ID is referenced by the interrupt entries */
1144 pIOAPICEntry->u8Version = 0x11;
1145 pIOAPICEntry->u8Flags = 1 /* enable */;
1146 pIOAPICEntry->u32Addr = 0xfec00000;
1147 pCfgTab->u16EntryCount++;
1148
1149 /* Interrupt tables */
1150 /* Bus vectors */
1151 /* Note: The PIC is currently not routed to the I/O APIC. Therefore we skip
1152 * pin 0 on the I/O APIC.
1153 */
1154 PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1);
1155 for (int iPin = 1; iPin < 16; iPin++, pIrqEntry++)
1156 {
1157 pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */
1158 /*
1159 * 0 - INT, vectored interrupt,
1160 * 3 - ExtINT, vectored interrupt provided by PIC
1161 * As we emulate system with both APIC and PIC, it's needed for their coexistence.
1162 */
1163 pIrqEntry->u8Type = (iPin == 0) ? 3 : 0;
1164 pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus,
1165 trigger mode = conforms to bus */
1166 pIrqEntry->u8SrcBusId = iBusIdIsa; /* ISA bus */
1167 /* IRQ0 mapped to pin 2, other are identity mapped */
1168 /* If changing, also update PDMIsaSetIrq() and MADT */
1169 pIrqEntry->u8SrcBusIrq = (iPin == 2) ? 0 : iPin; /* IRQ on the bus */
1170 pIrqEntry->u8DstIOAPICId = iApicId; /* destination IO-APIC */
1171 pIrqEntry->u8DstIOAPICInt = iPin; /* pin on destination IO-APIC */
1172 pCfgTab->u16EntryCount++;
1173 }
1174 /* Local delivery */
1175 pIrqEntry->u8EntryType = 4; /* Local interrupt entry */
1176 pIrqEntry->u8Type = 3; /* ExtINT */
1177 pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */
1178 pIrqEntry->u8SrcBusId = iBusIdIsa;
1179 pIrqEntry->u8SrcBusIrq = 0;
1180 pIrqEntry->u8DstIOAPICId = 0xff;
1181 pIrqEntry->u8DstIOAPICInt = 0;
1182 pIrqEntry++;
1183 pCfgTab->u16EntryCount++;
1184 pIrqEntry->u8EntryType = 4; /* Local interrupt entry */
1185 pIrqEntry->u8Type = 1; /* NMI */
1186 pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */
1187 pIrqEntry->u8SrcBusId = iBusIdIsa;
1188 pIrqEntry->u8SrcBusIrq = 0;
1189 pIrqEntry->u8DstIOAPICId = 0xff;
1190 pIrqEntry->u8DstIOAPICInt = 1;
1191 pIrqEntry++;
1192 pCfgTab->u16EntryCount++;
1193
1194 pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable;
1195 pCfgTab->u8Checksum = fwCommonChecksum(pTable, pCfgTab->u16Length);
1196
1197 AssertMsg(pCfgTab->u16Length < cbMax,
1198 ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d",
1199 pCfgTab->u16Length, cbMax));
1200}
1201
1202/**
1203 * Construct the MPS table pointer at VM construction and reset.
1204 *
1205 * Only applicable if IOAPIC is active!
1206 *
1207 * @param pDevIns The device instance data.
1208 * @param u32MpTableAddr The MP table physical address.
1209 */
1210void FwCommonPlantMpsFloatPtr(PPDMDEVINS pDevIns, uint32_t u32MpTableAddr)
1211{
1212 MPSFLOATPTR floatPtr;
1213 floatPtr.au8Signature[0] = '_';
1214 floatPtr.au8Signature[1] = 'M';
1215 floatPtr.au8Signature[2] = 'P';
1216 floatPtr.au8Signature[3] = '_';
1217 floatPtr.u32MPSAddr = u32MpTableAddr;
1218 floatPtr.u8Length = 1; /* structure size in paragraphs */
1219 floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */
1220 floatPtr.u8Checksum = 0;
1221 floatPtr.au8Feature[0] = 0;
1222 floatPtr.au8Feature[1] = 0;
1223 floatPtr.au8Feature[2] = 0;
1224 floatPtr.au8Feature[3] = 0;
1225 floatPtr.au8Feature[4] = 0;
1226 floatPtr.u8Checksum = fwCommonChecksum((uint8_t*)&floatPtr, 16);
1227 PDMDevHlpPhysWrite(pDevIns, 0x9fff0, &floatPtr, 16);
1228}
1229
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette