/** @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;
}
/**
 * @interface_method_impl{PDMDEVREG,pfnConstruct}
 */
static DECLCALLBACK(int) gimdevR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
    RT_NOREF2(iInstance, pCfg);
    Assert(iInstance == 0);
    PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);

    /*
     * Initialize relevant state bits.
     */
    pThis->pDevInsR3  = pDevIns;
    pThis->pDevInsR0  = PDMDEVINS_2_R0PTR(pDevIns);
    pThis->pDevInsRC  = PDMDEVINS_2_RCPTR(pDevIns);

    /*
     * Get debug setup requirements from GIM.
     */
    PVM pVM = PDMDevHlpGetVM(pDevIns);
    int rc = GIMR3GetDebugSetup(pVM, &pThis->DbgSetup);
    if (   RT_SUCCESS(rc)
        && pThis->DbgSetup.cbDbgRecvBuf > 0)
    {
        /*
         * Attach the stream driver for the debug connection.
         */
        PPDMISTREAM pDbgDrvStream = NULL;
        pThis->IDbgBase.pfnQueryInterface = gimdevR3QueryInterface;
        rc = PDMDevHlpDriverAttach(pDevIns, GIMDEV_DEBUG_LUN, &pThis->IDbgBase, &pThis->pDbgDrvBase, "GIM Debug Port");
        if (RT_SUCCESS(rc))
        {
            pDbgDrvStream = PDMIBASE_QUERY_INTERFACE(pThis->pDbgDrvBase, PDMISTREAM);
            if (pDbgDrvStream)
                LogRel(("GIMDev: LUN#%u: Debug port configured\n", GIMDEV_DEBUG_LUN));
            else
            {
                LogRel(("GIMDev: LUN#%u: No unit\n", GIMDEV_DEBUG_LUN));
                rc = VERR_INTERNAL_ERROR_2;
            }
        }
        else
        {
            pThis->pDbgDrvBase = NULL;
            LogRel(("GIMDev: LUN#%u: No debug port configured! rc=%Rrc\n", GIMDEV_DEBUG_LUN, rc));
        }

        if (!pDbgDrvStream)
        {
            Assert(rc != VINF_SUCCESS);
            return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                       N_("Debug port configuration expected when GIM configured with debugging support"));
        }

        void *pvDbgRecvBuf = RTMemAllocZ(pThis->DbgSetup.cbDbgRecvBuf);
        if (RT_UNLIKELY(!pvDbgRecvBuf))
        {
            LogRel(("GIMDev: Failed to alloc %u bytes for debug receive buffer\n", pThis->DbgSetup.cbDbgRecvBuf));
            return VERR_NO_MEMORY;
        }

        /*
         * Update the shared debug struct.
         */
        pThis->Dbg.pDbgDrvStream    = pDbgDrvStream;
        pThis->Dbg.pvDbgRecvBuf     = pvDbgRecvBuf;
        pThis->Dbg.cbDbgRecvBufRead = 0;
        pThis->Dbg.fDbgRecvBufRead  = false;

        /*
         * Create the sempahore and the debug receive thread itself.
         */
        rc = RTSemEventMultiCreate(&pThis->Dbg.hDbgRecvThreadSem);
        if (RT_SUCCESS(rc))
        {
            rc = RTThreadCreate(&pThis->hDbgRecvThread, gimDevR3DbgRecvThread, pDevIns, 0 /*cbStack*/, RTTHREADTYPE_IO,
                                RTTHREADFLAGS_WAITABLE, "GIMDebugRecv");
            if (RT_FAILURE(rc))
            {
                RTSemEventMultiDestroy(pThis->Dbg.hDbgRecvThreadSem);
                pThis->Dbg.hDbgRecvThreadSem = NIL_RTSEMEVENTMULTI;

                RTMemFree(pThis->Dbg.pvDbgRecvBuf);
                pThis->Dbg.pvDbgRecvBuf = NULL;
                return rc;
            }
        }
        else
            return rc;
    }

    /*
     * Register this device with the GIM component.
     */
    GIMR3GimDeviceRegister(pVM, pDevIns, pThis->DbgSetup.cbDbgRecvBuf ? &pThis->Dbg : NULL);

    /*
     * Get the MMIO2 regions from the GIM provider.
     */
    uint32_t cRegions = 0;
    PGIMMMIO2REGION pRegionsR3 = GIMR3GetMmio2Regions(pVM, &cRegions);
    if (   cRegions
        && pRegionsR3)
    {
        /*
         * Register the MMIO2 regions.
         */
        PGIMMMIO2REGION pCur = pRegionsR3;
        for (uint32_t i = 0; i < cRegions; i++, pCur++)
        {
            Assert(!pCur->fRegistered);
            rc = PDMDevHlpMMIO2Register(pDevIns, NULL, pCur->iRegion, pCur->cbRegion, 0 /* fFlags */, &pCur->pvPageR3,
                                        pCur->szDescription);
            if (RT_FAILURE(rc))
                return rc;

            pCur->fRegistered = true;

#if defined(VBOX_WITH_2X_4GB_ADDR_SPACE)
            RTR0PTR pR0Mapping = 0;
            rc = PDMDevHlpMMIO2MapKernel(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
                                         &pR0Mapping);
            AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMapMMIO2IntoR0(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
            pCur->pvPageR0 = pR0Mapping;
#else
            pCur->pvPageR0 = (RTR0PTR)pCur->pvPageR3;
#endif

            /*
             * Map into RC if required.
             */
            if (pCur->fRCMapping)
            {
                RTRCPTR pRCMapping = 0;
                rc = PDMDevHlpMMHyperMapMMIO2(pDevIns, NULL, pCur->iRegion, 0 /* off */, pCur->cbRegion, pCur->szDescription,
                                              &pRCMapping);
                AssertLogRelMsgRCReturn(rc, ("PDMDevHlpMMHyperMapMMIO2(%#x,) -> %Rrc\n", pCur->cbRegion, rc), rc);
                pCur->pvPageRC = pRCMapping;
            }
            else
                pCur->pvPageRC = NIL_RTRCPTR;

            LogRel(("GIMDev: Registered %s\n", pCur->szDescription));
        }
    }

    /** @todo Register SSM: PDMDevHlpSSMRegister(). */
    /** @todo Register statistics: STAM_REG(). */
    /** @todo Register DBGFInfo: PDMDevHlpDBGFInfoRegister(). */

    return VINF_SUCCESS;
}
static DECLCALLBACK(int) gimDevR3DbgRecvThread(RTTHREAD hThreadSelf, void *pvUser)
{
    RT_NOREF1(hThreadSelf);

    /*
     * Validate.
     */
    PPDMDEVINS pDevIns = (PPDMDEVINS)pvUser;
    AssertReturn(pDevIns, VERR_INVALID_PARAMETER);
    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);

    PGIMDEV pThis = PDMINS_2_DATA(pDevIns, PGIMDEV);
    AssertReturn(pThis, VERR_INVALID_POINTER);
    AssertReturn(pThis->DbgSetup.cbDbgRecvBuf, VERR_INTERNAL_ERROR);
    AssertReturn(pThis->Dbg.hDbgRecvThreadSem != NIL_RTSEMEVENTMULTI, VERR_INTERNAL_ERROR_2);
    AssertReturn(pThis->Dbg.pvDbgRecvBuf, VERR_INTERNAL_ERROR_3);

    PVM pVM = PDMDevHlpGetVM(pDevIns);
    AssertReturn(pVM, VERR_INVALID_POINTER);

    PPDMISTREAM pDbgDrvStream = pThis->Dbg.pDbgDrvStream;
    AssertReturn(pDbgDrvStream, VERR_INVALID_POINTER);

    for (;;)
    {
        /*
         * Read incoming debug data.
         */
        size_t cbRead = pThis->DbgSetup.cbDbgRecvBuf;
        int rc = pDbgDrvStream->pfnRead(pDbgDrvStream, pThis->Dbg.pvDbgRecvBuf, &cbRead);
        if (   RT_SUCCESS(rc)
            && cbRead > 0)
        {
            /*
             * Notify the consumer thread.
             */
            if (ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == false)
            {
                if (pThis->DbgSetup.pfnDbgRecvBufAvail)
                    pThis->DbgSetup.pfnDbgRecvBufAvail(pVM);
                pThis->Dbg.cbDbgRecvBufRead = cbRead;
                RTSemEventMultiReset(pThis->Dbg.hDbgRecvThreadSem);
                ASMAtomicWriteBool(&pThis->Dbg.fDbgRecvBufRead, true);
            }

            /*
             * Wait until the consumer thread has acknowledged reading of the
             * current buffer or we're asked to shut down.
             *
             * It is important that we do NOT re-invoke 'pfnRead' before the
             * current buffer is consumed, otherwise we risk data corruption.
             */
            while (   ASMAtomicReadBool(&pThis->Dbg.fDbgRecvBufRead) == true
                   && !pThis->fDbgRecvThreadShutdown)
            {
                RTSemEventMultiWait(pThis->Dbg.hDbgRecvThreadSem, RT_INDEFINITE_WAIT);
            }
        }
#ifdef RT_OS_LINUX
        else if (rc == VERR_NET_CONNECTION_REFUSED)
        {
            /*
             * With the current, simplistic PDMISTREAM interface, this is the best we can do.
             * Even using RTSocketSelectOne[Ex] on Linux returns immediately with 'ready-to-read'
             * on localhost UDP sockets that are not connected on the other end.
             */
            /** @todo Fix socket waiting semantics on localhost Linux unconnected UDP sockets. */
            RTThreadSleep(400);
        }
#endif
        else if (   rc != VINF_TRY_AGAIN
                 && rc != VERR_TRY_AGAIN
                 && rc != VERR_NET_CONNECTION_RESET_BY_PEER)
        {
            LogRel(("GIMDev: Debug thread terminating with rc=%Rrc\n", rc));
            break;
        }

        if (pThis->fDbgRecvThreadShutdown)
        {
            LogRel(("GIMDev: Debug thread shutting down\n"));
            break;
        }
    }

    return VINF_SUCCESS;
}
Example #4
0
/**
 * @interface_method_impl{PDMDEVREG,pfnConstruct}
 */
static DECLCALLBACK(int)  smcConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
{
    PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
    PDEVSMC pThis = PDMINS_2_DATA(pDevIns, PDEVSMC);
    Assert(iInstance == 0);

    /*
     * Init the data.
     */
    pThis->bDollaryNumber  = 1;
    pThis->bShutdownReason = 3; /* STOP_CAUSE_POWERKEY_GOOD_CODE */

    /*
     * Validate configuration.
     */
    PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "DeviceKey|GetKeyFromRealSMC", "");

    /*
     * Read configuration.
     */

    /* The DeviceKey sets OSK0 and OSK1. */
    int rc = CFGMR3QueryStringDef(pCfg, "DeviceKey", pThis->szOsk0And1, sizeof(pThis->szOsk0And1), "");
    if (RT_FAILURE(rc))
        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                   N_("Configuration error: Querying \"DeviceKey\" as a string failed"));

    /* Query the key from the real hardware if asked to do so. */
    bool fGetKeyFromRealSMC;
    rc = CFGMR3QueryBoolDef(pCfg, "GetKeyFromRealSMC", &fGetKeyFromRealSMC, false);
    if (RT_FAILURE(rc))
        return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                   N_("Configuration error: Querying \"GetKeyFromRealSMC\" as a boolean failed"));
    if (fGetKeyFromRealSMC)
    {
        rc = PDMDevHlpCallR0(pDevIns, SMC_CALLR0_READ_OSK, 0 /*u64Arg*/);
        if (RT_FAILURE(rc))
            return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
                                       N_("Failed to query SMC value from the host"));
    }

    /*
     * Register I/O Ports
     */
    rc = PDMDevHlpIOPortRegister(pDevIns, SMC_PORT_FIRST, SMC_REG_COUNT, NULL,
                                 smcIoPortWrite, smcIoPortRead,
                                 NULL, NULL, "SMC data port");
    AssertRCReturn(rc, rc);

    /** @todo Newer versions (2.03) have an MMIO mapping as well (ACPI). */


    /*
     * Saved state.
     */
    rc = PDMDevHlpSSMRegister(pDevIns, SMC_SAVED_STATE_VERSION, sizeof(*pThis), smcSaveExec, smcLoadExec);
    if (RT_FAILURE(rc))
        return rc;

    return VINF_SUCCESS;
}