/**
 * 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));
}