VirtualBox

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

Last change on this file since 95664 was 93953, checked in by vboxsync, 3 years ago

DevFwCommon: Tweaks for making it build with GCC 11.2.1 and some cleanups.

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