VirtualBox

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

Last change on this file since 106212 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

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