/** * Construct the SMBIOS and DMI headers table pointer at VM construction and * reset. * * @param pDevIns The device instance data. */ void FwCommonPlantSmbiosAndDmiHdrs(PPDMDEVINS pDevIns) { struct { struct SMBIOSHDR smbios; struct DMIMAINHDR dmi; } aBiosHeaders = { // The SMBIOS header { { 0x5f, 0x53, 0x4d, 0x5f}, // "_SM_" signature 0x00, // checksum 0x1f, // EPS length, defined by standard VBOX_SMBIOS_MAJOR_VER, // SMBIOS major version VBOX_SMBIOS_MINOR_VER, // SMBIOS minor version VBOX_SMBIOS_MAXSS, // Maximum structure size 0x00, // Entry point revision { 0x00, 0x00, 0x00, 0x00, 0x00 } // padding }, // The DMI header { { 0x5f, 0x44, 0x4d, 0x49, 0x5f }, // "_DMI_" signature 0x00, // checksum VBOX_DMI_TABLE_SIZE, // DMI tables length VBOX_DMI_TABLE_BASE, // DMI tables base VBOX_DMI_TABLE_ENTR, // DMI tables entries VBOX_DMI_TABLE_VER, // DMI version } }; aBiosHeaders.smbios.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.smbios, sizeof(aBiosHeaders.smbios)); aBiosHeaders.dmi.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.dmi, sizeof(aBiosHeaders.dmi)); PDMDevHlpPhysWrite(pDevIns, 0xfe300, &aBiosHeaders, sizeof(aBiosHeaders)); }
/** * Construct the SMBIOS and DMI headers table pointer at VM construction and * reset. * * @param pDevIns The device instance data. * @param pHdr Pointer to the header destination. * @param cbDmiTables Size of all DMI tables planted in bytes. * @param cNumDmiTables Number of DMI tables planted. */ void FwCommonPlantSmbiosAndDmiHdrs(PPDMDEVINS pDevIns, uint8_t *pHdr, uint16_t cbDmiTables, uint16_t cNumDmiTables) { RT_NOREF(pDevIns); struct { struct SMBIOSHDR smbios; struct DMIMAINHDR dmi; } aBiosHeaders = { // The SMBIOS header { { 0x5f, 0x53, 0x4d, 0x5f}, // "_SM_" signature 0x00, // checksum 0x1f, // EPS length, defined by standard VBOX_SMBIOS_MAJOR_VER, // SMBIOS major version VBOX_SMBIOS_MINOR_VER, // SMBIOS minor version VBOX_SMBIOS_MAXSS, // Maximum structure size 0x00, // Entry point revision { 0x00, 0x00, 0x00, 0x00, 0x00 } // padding }, // The DMI header { { 0x5f, 0x44, 0x4d, 0x49, 0x5f }, // "_DMI_" signature 0x00, // checksum 0, // DMI tables length VBOX_DMI_TABLE_BASE, // DMI tables base 0, // DMI tables entries VBOX_DMI_TABLE_VER, // DMI version } }; aBiosHeaders.dmi.u16TablesLength = cbDmiTables; aBiosHeaders.dmi.u16TableEntries = cNumDmiTables; /* NB: The _SM_ table checksum technically covers both the _SM_ part (16 bytes) and the _DMI_ part * (further 15 bytes). However, because the _DMI_ checksum must be zero, the _SM_ checksum can * be calculated independently. */ aBiosHeaders.smbios.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.smbios, sizeof(aBiosHeaders.smbios)); aBiosHeaders.dmi.u8Checksum = fwCommonChecksum((uint8_t*)&aBiosHeaders.dmi, sizeof(aBiosHeaders.dmi)); memcpy(pHdr, &aBiosHeaders, sizeof(aBiosHeaders)); }
/** * Construct the MPS table pointer at VM construction and reset. * * Only applicable if IOAPIC is active! * * @param pDevIns The device instance data. */ void FwCommonPlantMpsFloatPtr(PPDMDEVINS pDevIns) { MPSFLOATPTR floatPtr; floatPtr.au8Signature[0] = '_'; floatPtr.au8Signature[1] = 'M'; floatPtr.au8Signature[2] = 'P'; floatPtr.au8Signature[3] = '_'; floatPtr.u32MPSAddr = VBOX_MPS_TABLE_BASE; floatPtr.u8Length = 1; /* structure size in paragraphs */ floatPtr.u8SpecRev = 4; /* MPS revision 1.4 */ floatPtr.u8Checksum = 0; floatPtr.au8Feature[0] = 0; floatPtr.au8Feature[1] = 0; floatPtr.au8Feature[2] = 0; floatPtr.au8Feature[3] = 0; floatPtr.au8Feature[4] = 0; floatPtr.u8Checksum = fwCommonChecksum((uint8_t*)&floatPtr, 16); PDMDevHlpPhysWrite(pDevIns, 0x9fff0, &floatPtr, 16); }
/** * Construct the MPS table for implanting as a ROM page. * * Only applicable if IOAPIC is active! * * See ``MultiProcessor Specification Version 1.4 (May 1997)'': * ``1.3 Scope * ... * The hardware required to implement the MP specification is kept to a * minimum, as follows: * * One or more processors that are Intel architecture instruction set * compatible, such as the CPUs in the Intel486 or Pentium processor * family. * * One or more APICs, such as the Intel 82489DX Advanced Programmable * Interrupt Controller or the integrated APIC, such as that on the * Intel Pentium 735\\90 and 815\\100 processors, together with a discrete * I/O APIC unit.'' * and later: * ``4.3.3 I/O APIC Entries * The configuration table contains one or more entries for I/O APICs. * ... * I/O APIC FLAGS: EN 3:0 1 If zero, this I/O APIC is unusable, and the * operating system should not attempt to access * this I/O APIC. * At least one I/O APIC must be enabled.'' * * @param pDevIns The device instance data. * @param pTable Where to write the table. * @param cbMax The maximum size of the MPS table. * @param cCpus The number of guest CPUs. */ void FwCommonPlantMpsTable(PPDMDEVINS pDevIns, uint8_t *pTable, unsigned cbMax, uint16_t cCpus) { /* configuration table */ PMPSCFGTBLHEADER pCfgTab = (MPSCFGTBLHEADER*)pTable; memcpy(pCfgTab->au8Signature, "PCMP", 4); pCfgTab->u8SpecRev = 4; /* 1.4 */ memcpy(pCfgTab->au8OemId, "VBOXCPU ", 8); memcpy(pCfgTab->au8ProductId, "VirtualBox ", 12); pCfgTab->u32OemTablePtr = 0; pCfgTab->u16OemTableSize = 0; pCfgTab->u16EntryCount = 0; /* Incremented as we go. */ pCfgTab->u32AddrLocalApic = 0xfee00000; pCfgTab->u16ExtTableLength = 0; pCfgTab->u8ExtTableChecksum = 0; pCfgTab->u8Reserved = 0; uint32_t u32Eax, u32Ebx, u32Ecx, u32Edx; uint32_t u32CPUSignature = 0x0520; /* default: Pentium 100 */ uint32_t u32FeatureFlags = 0x0001; /* default: FPU */ PDMDevHlpGetCpuId(pDevIns, 0, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx); if (u32Eax >= 1) { PDMDevHlpGetCpuId(pDevIns, 1, &u32Eax, &u32Ebx, &u32Ecx, &u32Edx); u32CPUSignature = u32Eax & 0xfff; /* Local APIC will be enabled later so override it here. Since we provide * an MP table we have an IOAPIC and therefore a Local APIC. */ u32FeatureFlags = u32Edx | X86_CPUID_FEATURE_EDX_APIC; } /* Construct MPS table for each VCPU. */ PMPSPROCENTRY pProcEntry = (PMPSPROCENTRY)(pCfgTab+1); for (int i = 0; i < cCpus; i++) { pProcEntry->u8EntryType = 0; /* processor entry */ pProcEntry->u8LocalApicId = i; pProcEntry->u8LocalApicVersion = 0x14; pProcEntry->u8CPUFlags = (i == 0 ? 2 /* bootstrap processor */ : 0 /* application processor */) | 1 /* enabled */; pProcEntry->u32CPUSignature = u32CPUSignature; pProcEntry->u32CPUFeatureFlags = u32FeatureFlags; pProcEntry->u32Reserved[0] = pProcEntry->u32Reserved[1] = 0; pProcEntry++; pCfgTab->u16EntryCount++; } uint32_t iBusIdIsa = 0; uint32_t iBusIdPci0 = 1; /* ISA bus */ PMPSBUSENTRY pBusEntry = (PMPSBUSENTRY)pProcEntry; pBusEntry->u8EntryType = 1; /* bus entry */ pBusEntry->u8BusId = iBusIdIsa; /* this ID is referenced by the interrupt entries */ memcpy(pBusEntry->au8BusTypeStr, "ISA ", 6); pBusEntry++; pCfgTab->u16EntryCount++; /* PCI bus */ pBusEntry->u8EntryType = 1; /* bus entry */ pBusEntry->u8BusId = iBusIdPci0; /* this ID can be referenced by the interrupt entries */ memcpy(pBusEntry->au8BusTypeStr, "PCI ", 6); pCfgTab->u16EntryCount++; /* I/O-APIC. * MP spec: "The configuration table contains one or more entries for I/O APICs. * ... At least one I/O APIC must be enabled." */ PMPSIOAPICENTRY pIOAPICEntry = (PMPSIOAPICENTRY)(pBusEntry+1); uint16_t iApicId = 0; pIOAPICEntry->u8EntryType = 2; /* I/O-APIC entry */ pIOAPICEntry->u8Id = iApicId; /* this ID is referenced by the interrupt entries */ pIOAPICEntry->u8Version = 0x11; pIOAPICEntry->u8Flags = 1 /* enable */; pIOAPICEntry->u32Addr = 0xfec00000; pCfgTab->u16EntryCount++; /* Interrupt tables */ /* Bus vectors */ /* Note: The PIC is currently not routed to the I/O APIC. Therefore we skip * pin 0 on the I/O APIC. */ PMPSIOIRQENTRY pIrqEntry = (PMPSIOIRQENTRY)(pIOAPICEntry+1); for (int iPin = 1; iPin < 16; iPin++, pIrqEntry++) { pIrqEntry->u8EntryType = 3; /* I/O interrupt entry */ /* * 0 - INT, vectored interrupt, * 3 - ExtINT, vectored interrupt provided by PIC * As we emulate system with both APIC and PIC, it's needed for their coexistence. */ pIrqEntry->u8Type = (iPin == 0) ? 3 : 0; pIrqEntry->u16Flags = 0; /* polarity of APIC I/O input signal = conforms to bus, trigger mode = conforms to bus */ pIrqEntry->u8SrcBusId = iBusIdIsa; /* ISA bus */ /* IRQ0 mapped to pin 2, other are identity mapped */ /* If changing, also update PDMIsaSetIrq() and MADT */ pIrqEntry->u8SrcBusIrq = (iPin == 2) ? 0 : iPin; /* IRQ on the bus */ pIrqEntry->u8DstIOAPICId = iApicId; /* destination IO-APIC */ pIrqEntry->u8DstIOAPICInt = iPin; /* pin on destination IO-APIC */ pCfgTab->u16EntryCount++; } /* Local delivery */ pIrqEntry->u8EntryType = 4; /* Local interrupt entry */ pIrqEntry->u8Type = 3; /* ExtINT */ pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */ pIrqEntry->u8SrcBusId = iBusIdIsa; pIrqEntry->u8SrcBusIrq = 0; pIrqEntry->u8DstIOAPICId = 0xff; pIrqEntry->u8DstIOAPICInt = 0; pIrqEntry++; pCfgTab->u16EntryCount++; pIrqEntry->u8EntryType = 4; /* Local interrupt entry */ pIrqEntry->u8Type = 1; /* NMI */ pIrqEntry->u16Flags = (1 << 2) | 1; /* active-high, edge-triggered */ pIrqEntry->u8SrcBusId = iBusIdIsa; pIrqEntry->u8SrcBusIrq = 0; pIrqEntry->u8DstIOAPICId = 0xff; pIrqEntry->u8DstIOAPICInt = 1; pIrqEntry++; pCfgTab->u16EntryCount++; pCfgTab->u16Length = (uint8_t*)pIrqEntry - pTable; pCfgTab->u8Checksum = fwCommonChecksum(pTable, pCfgTab->u16Length); AssertMsg(pCfgTab->u16Length < cbMax, ("VBOX_MPS_TABLE_SIZE=%d, maximum allowed size is %d", pCfgTab->u16Length, cbMax)); }