/** @interface_method_impl{PDMDEVREG,pfnConstruct} */
static DECLCALLBACK(int) ox958R3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
    RT_NOREF(iInstance);
    PDEVOX958   pThis = PDMINS_2_DATA(pDevIns, PDEVOX958);
    bool        fRCEnabled = true;
    bool        fR0Enabled = true;
    bool        fMsiXSupported = false;
    int         rc;

    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);

    /*
     * Validate and read configuration.
     */
    if (!CFGMR3AreValuesValid(pCfg, "RCEnabled\0"
                                    "R0Enabled\0"
                                    "MsiXSupported\0"
                                    "UartCount\0"))
        return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
                                N_("OXPCIe958 configuration error: Unknown option specified"));

    rc = CFGMR3QueryBoolDef(pCfg, "RCEnabled", &fRCEnabled, true);
    if (RT_FAILURE(rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("OXPCIe958 configuration error: Failed to read \"RCEnabled\" as boolean"));

    rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
    if (RT_FAILURE(rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("OXPCIe958 configuration error: failed to read \"R0Enabled\" as boolean"));

    rc = CFGMR3QueryBoolDef(pCfg, "MsiXSupported", &fMsiXSupported, true);
    if (RT_FAILURE(rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("OXPCIe958 configuration error: failed to read \"MsiXSupported\" as boolean"));

    rc = CFGMR3QueryU32Def(pCfg, "UartCount", &pThis->cUarts, OX958_UARTS_MAX);
    if (RT_FAILURE(rc))
        return PDMDEV_SET_ERROR(pDevIns, rc,
                                N_("OXPCIe958 configuration error: failed to read \"UartCount\" as unsigned 32bit integer"));

    if (!pThis->cUarts || pThis->cUarts > OX958_UARTS_MAX)
        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                   N_("OXPCIe958 configuration error: \"UartCount\" has invalid value %u (must be in range [1 .. %u]"),
                                   pThis->cUarts, OX958_UARTS_MAX);

    /*
     * Init instance data.
     */
    pThis->fR0Enabled            = fR0Enabled;
    pThis->fRCEnabled            = fRCEnabled;
    pThis->pDevInsR3             = pDevIns;
    pThis->pDevInsR0             = PDMDEVINS_2_R0PTR(pDevIns);
    pThis->pDevInsRC             = PDMDEVINS_2_RCPTR(pDevIns);

    /* Fill PCI config space. */
    PDMPciDevSetVendorId         (&pThis->PciDev, OX958_PCI_VENDOR_ID);
    PDMPciDevSetDeviceId         (&pThis->PciDev, OX958_PCI_DEVICE_ID);
    PDMPciDevSetCommand          (&pThis->PciDev, 0x0000);
#ifdef VBOX_WITH_MSI_DEVICES
    PDMPciDevSetStatus           (&pThis->PciDev, VBOX_PCI_STATUS_CAP_LIST);
    PDMPciDevSetCapabilityList   (&pThis->PciDev, OX958_PCI_MSI_CAP_OFS);
#else
    PDMPciDevSetCapabilityList   (&pThis->PciDev, 0x70);
#endif
    PDMPciDevSetRevisionId       (&pThis->PciDev, 0x00);
    PDMPciDevSetClassBase        (&pThis->PciDev, 0x07); /* Communication controller. */
    PDMPciDevSetClassSub         (&pThis->PciDev, 0x00); /* Serial controller. */
    PDMPciDevSetClassProg        (&pThis->PciDev, 0x02); /* 16550. */

    PDMPciDevSetRevisionId       (&pThis->PciDev, 0x00);
    PDMPciDevSetSubSystemVendorId(&pThis->PciDev, OX958_PCI_VENDOR_ID);
    PDMPciDevSetSubSystemId      (&pThis->PciDev, OX958_PCI_DEVICE_ID);

    PDMPciDevSetInterruptLine       (&pThis->PciDev, 0x00);
    PDMPciDevSetInterruptPin        (&pThis->PciDev, 0x01);
    /** @todo More Capabilities. */

    rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
    if (RT_FAILURE(rc))
        return rc;

    /*
     * Register PCI device and I/O region.
     */
    rc = PDMDevHlpPCIRegister(pDevIns, &pThis->PciDev);
    if (RT_FAILURE(rc))
        return rc;

#ifdef VBOX_WITH_MSI_DEVICES
    PDMMSIREG MsiReg;
    RT_ZERO(MsiReg);
    MsiReg.cMsiVectors     = 1;
    MsiReg.iMsiCapOffset   = OX958_PCI_MSI_CAP_OFS;
    MsiReg.iMsiNextOffset  = OX958_PCI_MSIX_CAP_OFS;
    MsiReg.fMsi64bit       = true;
    if (fMsiXSupported)
    {
        MsiReg.cMsixVectors    = VBOX_MSIX_MAX_ENTRIES;
        MsiReg.iMsixCapOffset  = OX958_PCI_MSIX_CAP_OFS;
        MsiReg.iMsixNextOffset = 0x00;
        MsiReg.iMsixBar        = OX958_PCI_MSIX_BAR;
    }
    rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
    if (RT_FAILURE(rc))
    {
        PCIDevSetCapabilityList(&pThis->PciDev, 0x0);
        /* That's OK, we can work without MSI */
    }
#endif

    rc = PDMDevHlpPCIIORegionRegister(pDevIns, 0, _16K, PCI_ADDRESS_SPACE_MEM, ox958R3Map);
    if (RT_FAILURE(rc))
        return rc;

    PVM pVM = PDMDevHlpGetVM(pDevIns);
    RTR0PTR pfnSerialIrqReqR0 = NIL_RTR0PTR;
    RTRCPTR pfnSerialIrqReqRC = NIL_RTRCPTR;

    if (   fRCEnabled
        && VM_IS_RAW_MODE_ENABLED(pVM))
    {
        rc = PDMR3LdrGetSymbolRC(pVM, pDevIns->pReg->szRCMod, "ox958IrqReq", &pfnSerialIrqReqRC);
        if (RT_FAILURE(rc))
            return rc;
    }

    if (fR0Enabled)
    {
        rc = PDMR3LdrGetSymbolR0(pVM, pDevIns->pReg->szR0Mod, "ox958IrqReq", &pfnSerialIrqReqR0);
        if (RT_FAILURE(rc))
            return rc;
    }

    for (uint32_t i = 0; i < pThis->cUarts; i++)
    {
        POX958UART pUart = &pThis->aUarts[i];
        rc = uartR3Init(&pUart->UartCore, pDevIns, UARTTYPE_16550A, i, 0, ox958IrqReq, pfnSerialIrqReqR0, pfnSerialIrqReqRC);
        if (RT_FAILURE(rc))
            return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                       N_("OXPCIe958 configuration error: failed to initialize UART %u"), i);
    }

    ox958R3Reset(pDevIns);
    return VINF_SUCCESS;
}
int MsixInit(PCPDMPCIHLP pPciHlp, PPCIDEVICE pDev, PPDMMSIREG pMsiReg)
{
    if (pMsiReg->cMsixVectors == 0)
         return VINF_SUCCESS;

     /* We cannot init MSI-X on raw devices yet. */
    Assert(!pciDevIsPassthrough(pDev));

    uint16_t   cVectors    = pMsiReg->cMsixVectors;
    uint8_t    iCapOffset  = pMsiReg->iMsixCapOffset;
    uint8_t    iNextOffset = pMsiReg->iMsixNextOffset;
    uint8_t    iBar        = pMsiReg->iMsixBar;

    if (cVectors > VBOX_MSIX_MAX_ENTRIES)
    {
        AssertMsgFailed(("Too many MSI-X vectors: %d\n", cVectors));
        return VERR_TOO_MUCH_DATA;
    }

    if (iBar > 5)
    {
        AssertMsgFailed(("Using wrong BAR for MSI-X: %d\n", iBar));
        return VERR_INVALID_PARAMETER;
    }

    Assert(iCapOffset != 0 && iCapOffset < 0xff && iNextOffset < 0xff);

    int rc = VINF_SUCCESS;

    /* If device is passthrough, BAR is registered using common mechanism. */
    if (!pciDevIsPassthrough(pDev))
    {
        rc = PDMDevHlpPCIIORegionRegister (pDev->pDevIns, iBar, 0x1000, PCI_ADDRESS_SPACE_MEM, msixMap);
        if (RT_FAILURE (rc))
            return rc;
    }

    pDev->Int.s.u8MsixCapOffset = iCapOffset;
    pDev->Int.s.u8MsixCapSize   = VBOX_MSIX_CAP_SIZE;
    PVM pVM = PDMDevHlpGetVM(pDev->pDevIns);

    pDev->Int.s.pMsixPageR3     = NULL;

    rc = MMHyperAlloc(pVM, 0x1000, 1, MM_TAG_PDM_DEVICE_USER, (void **)&pDev->Int.s.pMsixPageR3);
    if (RT_FAILURE(rc) || (pDev->Int.s.pMsixPageR3 == NULL))
        return VERR_NO_VM_MEMORY;
    RT_BZERO(pDev->Int.s.pMsixPageR3, 0x1000);
    pDev->Int.s.pMsixPageR0     = MMHyperR3ToR0(pVM, pDev->Int.s.pMsixPageR3);
    pDev->Int.s.pMsixPageRC     = MMHyperR3ToRC(pVM, pDev->Int.s.pMsixPageR3);

    /* R3 PCI helper */
    pDev->Int.s.pPciBusPtrR3    = pPciHlp;

    PCIDevSetByte(pDev,  iCapOffset + 0, VBOX_PCI_CAP_ID_MSIX);
    PCIDevSetByte(pDev,  iCapOffset + 1, iNextOffset); /* next */
    PCIDevSetWord(pDev,  iCapOffset + VBOX_MSIX_CAP_MESSAGE_CONTROL, cVectors - 1);

    uint32_t offTable = 0, offPBA = 0x800;

    PCIDevSetDWord(pDev,  iCapOffset + VBOX_MSIX_TABLE_BIROFFSET, offTable | iBar);
    PCIDevSetDWord(pDev,  iCapOffset + VBOX_MSIX_PBA_BIROFFSET,   offPBA   | iBar);

    pciDevSetMsixCapable(pDev);

    return VINF_SUCCESS;
}