summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Bus/SrvPciRawR0.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Devices/Bus/SrvPciRawR0.cpp')
-rw-r--r--src/VBox/Devices/Bus/SrvPciRawR0.cpp1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/src/VBox/Devices/Bus/SrvPciRawR0.cpp b/src/VBox/Devices/Bus/SrvPciRawR0.cpp
new file mode 100644
index 00000000..f973b804
--- /dev/null
+++ b/src/VBox/Devices/Bus/SrvPciRawR0.cpp
@@ -0,0 +1,1041 @@
+/* $Id: SrvPciRawR0.cpp $ */
+/** @file
+ * PCI passthrough - The ring 0 service.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
+#include <VBox/log.h>
+#include <VBox/sup.h>
+#include <VBox/rawpci.h>
+#include <VBox/vmm/pdmpci.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/vmm/gvm.h>
+#include <VBox/vmm/gvmm.h>
+#include <VBox/vmm/vmcc.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/handletable.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/asm-amd64-x86.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct PCIRAWSRVSTATE
+{
+ /** Structure lock. */
+ RTSPINLOCK hSpinlock;
+
+ /** Handle table for devices. */
+ RTHANDLETABLE hHtDevs;
+
+} PCIRAWSRVSTATE;
+typedef PCIRAWSRVSTATE *PPCIRAWSRVSTATE;
+
+typedef struct PCIRAWDEV
+{
+ /* Port pointer. */
+ PRAWPCIDEVPORT pPort;
+
+ /* Handle used by everybody else. */
+ PCIRAWDEVHANDLE hHandle;
+
+ /** The session this device is associated with. */
+ PSUPDRVSESSION pSession;
+
+ /** Structure lock. */
+ RTSPINLOCK hSpinlock;
+
+ /** Event for IRQ updates. */
+ RTSEMEVENT hIrqEvent;
+
+ /** Current pending IRQ for the device. */
+ int32_t iPendingIrq;
+
+ /** ISR handle. */
+ PCIRAWISRHANDLE hIsr;
+
+ /* If object is being destroyed. */
+ bool fTerminate;
+
+ /** The SUPR0 object. */
+ void *pvObj;
+} PCIRAWDEV;
+typedef PCIRAWDEV *PPCIRAWDEV;
+
+static PCIRAWSRVSTATE g_State;
+
+
+/** Interrupt handler. Could be called in the interrupt context,
+ * depending on host OS implmenetation. */
+static DECLCALLBACK(bool) pcirawr0Isr(void* pContext, int32_t iHostIrq)
+{
+ PPCIRAWDEV pThis = (PPCIRAWDEV)pContext;
+
+#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS
+ uint16_t uStatus;
+ PCIRAWMEMLOC Loc;
+ int rc;
+
+ Loc.cb = 2;
+ rc = pThis->pPort->pfnPciCfgRead(pThis->pPort, VBOX_PCI_STATUS, &Loc);
+ /* Cannot read, assume non-shared. */
+ if (RT_FAILURE(rc))
+ return false;
+
+ /* Check interrupt status bit. */
+ if ((Loc.u.u16 & (1 << 3)) == 0)
+ return false;
+#endif
+
+ RTSpinlockAcquire(pThis->hSpinlock);
+ pThis->iPendingIrq = iHostIrq;
+ RTSpinlockRelease(pThis->hSpinlock);
+
+ /**
+ * @todo RTSemEventSignal() docs claims that it's platform-dependent
+ * if RTSemEventSignal() could be called from the ISR, but it seems IPRT
+ * doesn't provide primitives that guaranteed to work this way.
+ */
+ RTSemEventSignal(pThis->hIrqEvent);
+
+ return true;
+}
+
+static DECLCALLBACK(int) pcirawr0DevRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
+{
+ NOREF(pvUser);
+ NOREF(hHandleTable);
+ PPCIRAWDEV pDev = (PPCIRAWDEV)pvObj;
+ if (pDev->hHandle != 0)
+ return SUPR0ObjAddRefEx(pDev->pvObj, (PSUPDRVSESSION)pvCtx, true /* fNoBlocking */);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initializes the raw PCI ring-0 service.
+ *
+ * @returns VBox status code.
+ */
+PCIRAWR0DECL(int) PciRawR0Init(void)
+{
+ LogFlow(("PciRawR0Init:\n"));
+ int rc = VINF_SUCCESS;
+
+ rc = RTHandleTableCreateEx(&g_State.hHtDevs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
+ UINT32_C(0xfefe0000), 4096, pcirawr0DevRetainHandle, NULL);
+
+ LogFlow(("PciRawR0Init: returns %Rrc\n", rc));
+ return rc;
+}
+
+/**
+ * Destroys raw PCI ring-0 service.
+ */
+PCIRAWR0DECL(void) PciRawR0Term(void)
+{
+ LogFlow(("PciRawR0Term:\n"));
+ RTHandleTableDestroy(g_State.hHtDevs, NULL, NULL);
+ g_State.hHtDevs = NIL_RTHANDLETABLE;
+}
+
+
+/**
+ * Per-VM R0 module init.
+ */
+PCIRAWR0DECL(int) PciRawR0InitVM(PGVM pGVM)
+{
+ PRAWPCIFACTORY pFactory = NULL;
+ int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ rc = pFactory->pfnInitVm(pFactory, pGVM, &pGVM->rawpci.s);
+ pFactory->pfnRelease(pFactory);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Per-VM R0 module termination routine.
+ */
+PCIRAWR0DECL(void) PciRawR0TermVM(PGVM pGVM)
+{
+ PRAWPCIFACTORY pFactory = NULL;
+ int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ pFactory->pfnDeinitVm(pFactory, pGVM, &pGVM->rawpci.s);
+ pFactory->pfnRelease(pFactory);
+ }
+ }
+}
+
+static int pcirawr0DevTerm(PPCIRAWDEV pThis, int32_t fFlags)
+{
+ ASMAtomicWriteBool(&pThis->fTerminate, true);
+
+ if (pThis->hIrqEvent)
+ RTSemEventSignal(pThis->hIrqEvent);
+
+ /* Enable that, once figure our how to make sure
+ IRQ getter thread notified and woke up. */
+#if 0
+ if (pThis->hIrqEvent)
+ {
+ RTSemEventDestroy(pThis->hIrqEvent);
+ pThis->hIrqEvent = NIL_RTSEMEVENT;
+ }
+#endif
+
+ if (pThis->hSpinlock)
+ {
+ RTSpinlockDestroy(pThis->hSpinlock);
+ pThis->hSpinlock = NIL_RTSPINLOCK;
+ }
+
+ /* Forcefully deinit. */
+ return pThis->pPort->pfnDeinit(pThis->pPort, fFlags);
+}
+
+#define GET_PORT(hDev) \
+ PPCIRAWDEV pDev = (PPCIRAWDEV)RTHandleTableLookupWithCtx(g_State.hHtDevs, hDev, pSession); \
+ if (!pDev) \
+ return VERR_INVALID_HANDLE; \
+ PRAWPCIDEVPORT pDevPort = pDev->pPort; \
+ AssertReturn(pDevPort != NULL, VERR_INVALID_PARAMETER); \
+ AssertReturn(pDevPort->u32Version == RAWPCIDEVPORT_VERSION, VERR_INVALID_PARAMETER); \
+ AssertReturn(pDevPort->u32VersionEnd == RAWPCIDEVPORT_VERSION, VERR_INVALID_PARAMETER);
+
+#define PUT_PORT() if (pDev->pvObj) SUPR0ObjRelease(pDev->pvObj, pSession)
+
+#ifdef DEBUG_nike
+
+/* Code to perform debugging without host driver. */
+typedef struct DUMMYRAWPCIINS
+{
+ /* Host PCI address of this device. */
+ uint32_t HostPciAddress;
+ /* Padding */
+ uint32_t pad0;
+
+ uint8_t aPciCfg[256];
+
+ /** Port, given to the outside world. */
+ RAWPCIDEVPORT DevPort;
+} DUMMYRAWPCIINS;
+typedef struct DUMMYRAWPCIINS *PDUMMYRAWPCIINS;
+
+#define DEVPORT_2_DUMMYRAWPCIINS(pPort) \
+ ( (PDUMMYRAWPCIINS)((uint8_t *)pPort - RT_UOFFSETOF(DUMMYRAWPCIINS, DevPort)) )
+
+static uint8_t dummyPciGetByte(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ return pThis->aPciCfg[iRegister];
+}
+
+static void dummyPciSetByte(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint8_t u8)
+{
+ pThis->aPciCfg[iRegister] = u8;
+}
+
+static uint16_t dummyPciGetWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ uint16_t u16Value = *(uint16_t*)&pThis->aPciCfg[iRegister];
+ return RT_H2LE_U16(u16Value);
+}
+
+static void dummyPciSetWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint16_t u16)
+{
+ *(uint16_t*)&pThis->aPciCfg[iRegister] = RT_H2LE_U16(u16);
+}
+
+static uint32_t dummyPciGetDWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister)
+{
+ uint32_t u32Value = *(uint32_t*)&pThis->aPciCfg[iRegister];
+ return RT_H2LE_U32(u32Value);
+}
+
+static void dummyPciSetDWord(PDUMMYRAWPCIINS pThis, uint32_t iRegister, uint32_t u32)
+{
+ *(uint32_t*)&pThis->aPciCfg[iRegister] = RT_H2LE_U32(u32);
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnInit
+ */
+static DECLCALLBACK(int) dummyPciDevInit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ dummyPciSetWord(pThis, VBOX_PCI_VENDOR_ID, 0xccdd);
+ dummyPciSetWord(pThis, VBOX_PCI_DEVICE_ID, 0xeeff);
+ dummyPciSetWord(pThis, VBOX_PCI_COMMAND, PCI_COMMAND_IOACCESS | PCI_COMMAND_MEMACCESS | PCI_COMMAND_BUSMASTER);
+ dummyPciSetByte(pThis, VBOX_PCI_INTERRUPT_PIN, 1);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnDeinit
+ */
+static DECLCALLBACK(int) dummyPciDevDeinit(PRAWPCIDEVPORT pPort, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnDestroy
+ */
+static DECLCALLBACK(int) dummyPciDevDestroy(PRAWPCIDEVPORT pPort)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnGetRegionInfo
+ */
+static DECLCALLBACK(int) dummyPciDevGetRegionInfo(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ if (iRegion == 0)
+ {
+ *pfPresent = true;
+ *pRegionStart = 0xfef0;
+ *pu64RegionSize = 0x10;
+ *pfFlags = PCIRAW_ADDRESS_SPACE_IO;
+ }
+ else if (iRegion == 2)
+ {
+ *pfPresent = true;
+ *pRegionStart = 0xffff0000;
+ *pu64RegionSize = 0x1000;
+ *pfFlags = PCIRAW_ADDRESS_SPACE_BAR64 | PCIRAW_ADDRESS_SPACE_MEM;
+ }
+ else
+ *pfPresent = false;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnMapRegion
+ */
+static DECLCALLBACK(int) dummyPciDevMapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ int32_t fFlags,
+ RTR0PTR *pRegionBase)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnUnapRegion
+ */
+static DECLCALLBACK(int) dummyPciDevUnmapRegion(PRAWPCIDEVPORT pPort,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ RTR0PTR RegionBase)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnPciCfgRead
+ */
+static DECLCALLBACK(int) dummyPciDevPciCfgRead(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ switch (pValue->cb)
+ {
+ case 1:
+ pValue->u.u8 = dummyPciGetByte(pThis, Register);
+ break;
+ case 2:
+ pValue->u.u16 = dummyPciGetWord(pThis, Register);
+ break;
+ case 4:
+ pValue->u.u32 = dummyPciGetDWord(pThis, Register);
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @copydoc RAWPCIDEVPORT:: pfnPciCfgWrite
+ */
+static DECLCALLBACK(int) dummyPciDevPciCfgWrite(PRAWPCIDEVPORT pPort,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+
+ switch (pValue->cb)
+ {
+ case 1:
+ dummyPciSetByte(pThis, Register, pValue->u.u8);
+ break;
+ case 2:
+ dummyPciSetWord(pThis, Register, pValue->u.u16);
+ break;
+ case 4:
+ dummyPciSetDWord(pThis, Register, pValue->u.u32);
+ break;
+ }
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevRegisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PFNRAWPCIISR pfnHandler,
+ void* pIrqContext,
+ PCIRAWISRHANDLE *phIsr)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevUnregisterIrqHandler(PRAWPCIDEVPORT pPort,
+ PCIRAWISRHANDLE hIsr)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) dummyPciDevPowerStateChange(PRAWPCIDEVPORT pPort,
+ PCIRAWPOWERSTATE aState,
+ uint64_t *pu64Param)
+{
+ PDUMMYRAWPCIINS pThis = DEVPORT_2_DUMMYRAWPCIINS(pPort);
+ return VINF_SUCCESS;
+}
+
+static PRAWPCIDEVPORT pcirawr0CreateDummyDevice(uint32_t HostDevice, uint32_t fFlags)
+{
+ PDUMMYRAWPCIINS pNew = (PDUMMYRAWPCIINS)RTMemAllocZ(sizeof(*pNew));
+ if (!pNew)
+ return NULL;
+
+ pNew->HostPciAddress = HostDevice;
+
+ pNew->DevPort.u32Version = RAWPCIDEVPORT_VERSION;
+ pNew->DevPort.pfnInit = dummyPciDevInit;
+ pNew->DevPort.pfnDeinit = dummyPciDevDeinit;
+ pNew->DevPort.pfnDestroy = dummyPciDevDestroy;
+ pNew->DevPort.pfnGetRegionInfo = dummyPciDevGetRegionInfo;
+ pNew->DevPort.pfnMapRegion = dummyPciDevMapRegion;
+ pNew->DevPort.pfnUnmapRegion = dummyPciDevUnmapRegion;
+ pNew->DevPort.pfnPciCfgRead = dummyPciDevPciCfgRead;
+ pNew->DevPort.pfnPciCfgWrite = dummyPciDevPciCfgWrite;
+ pNew->DevPort.pfnRegisterIrqHandler = dummyPciDevRegisterIrqHandler;
+ pNew->DevPort.pfnUnregisterIrqHandler = dummyPciDevUnregisterIrqHandler;
+ pNew->DevPort.pfnPowerStateChange = dummyPciDevPowerStateChange;
+
+ pNew->DevPort.u32VersionEnd = RAWPCIDEVPORT_VERSION;
+
+ return &pNew->DevPort;
+}
+
+#endif /* DEBUG_nike */
+
+static DECLCALLBACK(void) pcirawr0DevObjDestructor(void *pvObj, void *pvIns, void *pvUnused)
+{
+ PPCIRAWDEV pThis = (PPCIRAWDEV)pvIns;
+ NOREF(pvObj); NOREF(pvUnused);
+
+ /* Forcefully deinit. */
+ pcirawr0DevTerm(pThis, 0);
+
+ /* And destroy. */
+ pThis->pPort->pfnDestroy(pThis->pPort);
+
+ RTMemFree(pThis);
+}
+
+
+static int pcirawr0OpenDevice(PGVM pGVM, PSUPDRVSESSION pSession,
+ uint32_t HostDevice,
+ uint32_t fFlags,
+ PCIRAWDEVHANDLE *pHandle,
+ uint32_t *pfDevFlags)
+{
+
+ int rc = GVMMR0ValidateGVMandEMT(pGVM, 0 /*idCpu*/);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Query the factory we want, then use it create and connect the host device.
+ */
+ PPCIRAWDEV pNew = (PPCIRAWDEV)RTMemAllocZ(sizeof(*pNew));
+ if (!pNew)
+ return VERR_NO_MEMORY;
+
+ PRAWPCIFACTORY pFactory = NULL;
+ rc = SUPR0ComponentQueryFactory(pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
+ /* No host driver registered, provide some fake implementation
+ for debugging purposes. */
+ PRAWPCIDEVPORT pDevPort = NULL;
+#ifdef DEBUG_nike
+ if (rc == VERR_SUPDRV_COMPONENT_NOT_FOUND)
+ {
+ pDevPort = pcirawr0CreateDummyDevice(HostDevice, fFlags);
+ if (pDevPort)
+ {
+ pDevPort->pfnInit(pDevPort, fFlags);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pFactory)
+ {
+ rc = pFactory->pfnCreateAndConnect(pFactory,
+ HostDevice,
+ fFlags,
+ &pGVM->rawpci.s,
+ &pDevPort,
+ pfDevFlags);
+ pFactory->pfnRelease(pFactory);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "PciRaw");
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTSemEventCreate(&pNew->hIrqEvent);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ pNew->pSession = pSession;
+ pNew->pPort = pDevPort;
+ pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_RAW_PCI_DEVICE,
+ pcirawr0DevObjDestructor, pNew, NULL);
+ if (pNew->pvObj)
+ {
+
+ uint32_t hHandle = 0;
+ rc = RTHandleTableAllocWithCtx(g_State.hHtDevs, pNew, pSession, &hHandle);
+ if (RT_SUCCESS(rc))
+ {
+ pNew->hHandle = (PCIRAWDEVHANDLE)hHandle;
+ *pHandle = pNew->hHandle;
+ return rc;
+ }
+ SUPR0ObjRelease(pNew->pvObj, pSession);
+ }
+ RTSemEventDestroy(pNew->hIrqEvent);
+ }
+ RTSpinlockDestroy(pNew->hSpinlock);
+ }
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ RTMemFree(pNew);
+
+ return rc;
+}
+
+static int pcirawr0CloseDevice(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t fFlags)
+{
+ GET_PORT(TargetDevice);
+ int rc;
+
+ pDevPort->pfnUnregisterIrqHandler(pDevPort, pDev->hIsr);
+ pDev->hIsr = 0;
+
+ rc = pcirawr0DevTerm(pDev, fFlags);
+
+ RTHandleTableFreeWithCtx(g_State.hHtDevs, TargetDevice, pSession);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+/* We may want to call many functions here directly, so no static */
+static int pcirawr0GetRegionInfo(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS *pRegionStart,
+ uint64_t *pu64RegionSize,
+ bool *pfPresent,
+ uint32_t *pfFlags)
+{
+ LogFlow(("pcirawr0GetRegionInfo: %d\n", iRegion));
+ GET_PORT(TargetDevice);
+
+ int rc = pDevPort->pfnGetRegionInfo(pDevPort, iRegion, pRegionStart, pu64RegionSize, pfPresent, pfFlags);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0MapRegion(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ uint32_t fFlags,
+ RTR3PTR *ppvAddressR3,
+ RTR0PTR *ppvAddressR0)
+{
+ LogFlow(("pcirawr0MapRegion\n"));
+ GET_PORT(TargetDevice);
+ int rc;
+
+ rc = pDevPort->pfnMapRegion(pDevPort, iRegion, HCRegionStart, u64RegionSize, fFlags, ppvAddressR0);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(*ppvAddressR0 != NULL);
+
+ /* Do we need to do something to help with R3 mapping, if ((fFlags & PCIRAWRFLAG_ALLOW_R3MAP) != 0) */
+ }
+
+ *ppvAddressR3 = 0;
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0UnmapRegion(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int32_t iRegion,
+ RTHCPHYS HCRegionStart,
+ uint64_t u64RegionSize,
+ RTR3PTR pvAddressR3,
+ RTR0PTR pvAddressR0)
+{
+ LogFlow(("pcirawr0UnmapRegion\n"));
+ int rc;
+ NOREF(pSession); NOREF(pvAddressR3);
+
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnUnmapRegion(pDevPort, iRegion, HCRegionStart, u64RegionSize, pvAddressR0);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0PioWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint16_t Port,
+ uint32_t u32,
+ unsigned cb)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that port fits into device range
+ switch (cb)
+ {
+ case 1:
+ ASMOutU8 (Port, u32);
+ break;
+ case 2:
+ ASMOutU16(Port, u32);
+ break;
+ case 4:
+ ASMOutU32(Port, u32);
+ break;
+ default:
+ AssertMsgFailed(("Unhandled port write: %d\n", cb));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int pcirawr0PioRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint16_t Port,
+ uint32_t *pu32,
+ unsigned cb)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that port fits into device range
+ switch (cb)
+ {
+ case 1:
+ *pu32 = ASMInU8 (Port);
+ break;
+ case 2:
+ *pu32 = ASMInU16(Port);
+ break;
+ case 4:
+ *pu32 = ASMInU32(Port);
+ break;
+ default:
+ AssertMsgFailed(("Unhandled port read: %d\n", cb));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int pcirawr0MmioRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ RTR0PTR Address,
+ PCIRAWMEMLOC *pValue)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that address fits into device range
+#if 1
+ switch (pValue->cb)
+ {
+ case 1:
+ pValue->u.u8 = *(uint8_t*)Address;
+ break;
+ case 2:
+ pValue->u.u16 = *(uint16_t*)Address;
+ break;
+ case 4:
+ pValue->u.u32 = *(uint32_t*)Address;
+ break;
+ case 8:
+ pValue->u.u64 = *(uint64_t*)Address;
+ break;
+ }
+#else
+ memset(&pValue->u.u64, 0, 8);
+#endif
+ return VINF_SUCCESS;
+}
+
+static int pcirawr0MmioWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ RTR0PTR Address,
+ PCIRAWMEMLOC *pValue)
+{
+ NOREF(pSession); NOREF(TargetDevice);
+ /// @todo add check that address fits into device range
+#if 1
+ switch (pValue->cb)
+ {
+ case 1:
+ *(uint8_t*)Address = pValue->u.u8;
+ break;
+ case 2:
+ *(uint16_t*)Address = pValue->u.u16;
+ break;
+ case 4:
+ *(uint32_t*)Address = pValue->u.u32;
+ break;
+ case 8:
+ *(uint64_t*)Address = pValue->u.u64;
+ break;
+ }
+#endif
+ return VINF_SUCCESS;
+}
+
+static int pcirawr0PciCfgRead(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ GET_PORT(TargetDevice);
+
+ return pDevPort->pfnPciCfgRead(pDevPort, Register, pValue);
+}
+
+static int pcirawr0PciCfgWrite(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ uint32_t Register,
+ PCIRAWMEMLOC *pValue)
+{
+ int rc;
+
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnPciCfgWrite(pDevPort, Register, pValue);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0EnableIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice)
+{
+ int rc = VINF_SUCCESS;
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnRegisterIrqHandler(pDevPort, pcirawr0Isr, pDev,
+ &pDev->hIsr);
+
+ PUT_PORT();
+ return rc;
+}
+
+static int pcirawr0DisableIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice)
+{
+ int rc = VINF_SUCCESS;
+ GET_PORT(TargetDevice);
+
+ rc = pDevPort->pfnUnregisterIrqHandler(pDevPort, pDev->hIsr);
+ pDev->hIsr = 0;
+
+ PUT_PORT();
+ return rc;
+}
+
+static int pcirawr0GetIrq(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ int64_t iTimeout,
+ int32_t *piIrq)
+{
+ int rc = VINF_SUCCESS;
+ bool fTerminate = false;
+ int32_t iPendingIrq = 0;
+
+ LogFlow(("pcirawr0GetIrq\n"));
+
+ GET_PORT(TargetDevice);
+
+ RTSpinlockAcquire(pDev->hSpinlock);
+ iPendingIrq = pDev->iPendingIrq;
+ pDev->iPendingIrq = 0;
+ fTerminate = pDev->fTerminate;
+ RTSpinlockRelease(pDev->hSpinlock);
+
+ /* Block until new IRQs arrives */
+ if (!fTerminate)
+ {
+ if (iPendingIrq == 0)
+ {
+ rc = RTSemEventWaitNoResume(pDev->hIrqEvent, iTimeout);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo racy */
+ if (!ASMAtomicReadBool(&pDev->fTerminate))
+ {
+ RTSpinlockAcquire(pDev->hSpinlock);
+ iPendingIrq = pDev->iPendingIrq;
+ pDev->iPendingIrq = 0;
+ RTSpinlockRelease(pDev->hSpinlock);
+ }
+ else
+ rc = VERR_INTERRUPTED;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ *piIrq = iPendingIrq;
+ }
+ else
+ rc = VERR_INTERRUPTED;
+
+ PUT_PORT();
+
+ return rc;
+}
+
+static int pcirawr0PowerStateChange(PSUPDRVSESSION pSession,
+ PCIRAWDEVHANDLE TargetDevice,
+ PCIRAWPOWERSTATE aState,
+ uint64_t *pu64Param)
+{
+ LogFlow(("pcirawr0PowerStateChange\n"));
+ GET_PORT(TargetDevice);
+
+ int rc = pDevPort->pfnPowerStateChange(pDevPort, aState, pu64Param);
+
+ PUT_PORT();
+
+ return rc;
+}
+
+/**
+ * Process PCI raw request
+ *
+ * @returns VBox status code.
+ */
+PCIRAWR0DECL(int) PciRawR0ProcessReq(PGVM pGVM, PSUPDRVSESSION pSession, PPCIRAWSENDREQ pReq)
+{
+ LogFlow(("PciRawR0ProcessReq: %d for %x\n", pReq->iRequest, pReq->TargetDevice));
+ int rc = VINF_SUCCESS;
+
+ /* Route request to the host driver */
+ switch (pReq->iRequest)
+ {
+ case PCIRAWR0_DO_OPEN_DEVICE:
+ rc = pcirawr0OpenDevice(pGVM, pSession,
+ pReq->u.aOpenDevice.PciAddress,
+ pReq->u.aOpenDevice.fFlags,
+ &pReq->u.aOpenDevice.Device,
+ &pReq->u.aOpenDevice.fDevFlags);
+ break;
+ case PCIRAWR0_DO_CLOSE_DEVICE:
+ rc = pcirawr0CloseDevice(pSession,
+ pReq->TargetDevice,
+ pReq->u.aCloseDevice.fFlags);
+ break;
+ case PCIRAWR0_DO_GET_REGION_INFO:
+ rc = pcirawr0GetRegionInfo(pSession,
+ pReq->TargetDevice,
+ pReq->u.aGetRegionInfo.iRegion,
+ &pReq->u.aGetRegionInfo.RegionStart,
+ &pReq->u.aGetRegionInfo.u64RegionSize,
+ &pReq->u.aGetRegionInfo.fPresent,
+ &pReq->u.aGetRegionInfo.fFlags);
+ break;
+ case PCIRAWR0_DO_MAP_REGION:
+ rc = pcirawr0MapRegion(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMapRegion.iRegion,
+ pReq->u.aMapRegion.StartAddress,
+ pReq->u.aMapRegion.iRegionSize,
+ pReq->u.aMapRegion.fFlags,
+ &pReq->u.aMapRegion.pvAddressR3,
+ &pReq->u.aMapRegion.pvAddressR0);
+ break;
+ case PCIRAWR0_DO_UNMAP_REGION:
+ rc = pcirawr0UnmapRegion(pSession,
+ pReq->TargetDevice,
+ pReq->u.aUnmapRegion.iRegion,
+ pReq->u.aUnmapRegion.StartAddress,
+ pReq->u.aUnmapRegion.iRegionSize,
+ pReq->u.aUnmapRegion.pvAddressR3,
+ pReq->u.aUnmapRegion.pvAddressR0);
+ break;
+ case PCIRAWR0_DO_PIO_WRITE:
+ rc = pcirawr0PioWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPioWrite.iPort,
+ pReq->u.aPioWrite.iValue,
+ pReq->u.aPioWrite.cb);
+ break;
+ case PCIRAWR0_DO_PIO_READ:
+ rc = pcirawr0PioRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPioRead.iPort,
+ &pReq->u.aPioWrite.iValue,
+ pReq->u.aPioRead.cb);
+ break;
+ case PCIRAWR0_DO_MMIO_WRITE:
+ rc = pcirawr0MmioWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMmioWrite.Address,
+ &pReq->u.aMmioWrite.Value);
+ break;
+ case PCIRAWR0_DO_MMIO_READ:
+ rc = pcirawr0MmioRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aMmioRead.Address,
+ &pReq->u.aMmioRead.Value);
+ break;
+ case PCIRAWR0_DO_PCICFG_WRITE:
+ rc = pcirawr0PciCfgWrite(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPciCfgWrite.iOffset,
+ &pReq->u.aPciCfgWrite.Value);
+ break;
+ case PCIRAWR0_DO_PCICFG_READ:
+ rc = pcirawr0PciCfgRead(pSession,
+ pReq->TargetDevice,
+ pReq->u.aPciCfgRead.iOffset,
+ &pReq->u.aPciCfgRead.Value);
+ break;
+ case PCIRAWR0_DO_ENABLE_IRQ:
+ rc = pcirawr0EnableIrq(pSession,
+ pReq->TargetDevice);
+ break;
+ case PCIRAWR0_DO_DISABLE_IRQ:
+ rc = pcirawr0DisableIrq(pSession,
+ pReq->TargetDevice);
+ break;
+ case PCIRAWR0_DO_GET_IRQ:
+ rc = pcirawr0GetIrq(pSession,
+ pReq->TargetDevice,
+ pReq->u.aGetIrq.iTimeout,
+ &pReq->u.aGetIrq.iIrq);
+ break;
+ case PCIRAWR0_DO_POWER_STATE_CHANGE:
+ rc = pcirawr0PowerStateChange(pSession,
+ pReq->TargetDevice,
+ (PCIRAWPOWERSTATE)pReq->u.aPowerStateChange.iState,
+ &pReq->u.aPowerStateChange.u64Param);
+ break;
+ default:
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ LogFlow(("PciRawR0ProcessReq: returns %Rrc\n", rc));
+ return rc;
+}
+