diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 03:01:46 +0000 |
commit | f8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch) | |
tree | 26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Devices/Bus/SrvPciRawR0.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip |
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Bus/SrvPciRawR0.cpp')
-rw-r--r-- | src/VBox/Devices/Bus/SrvPciRawR0.cpp | 1031 |
1 files changed, 1031 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..3d248e86 --- /dev/null +++ b/src/VBox/Devices/Bus/SrvPciRawR0.cpp @@ -0,0 +1,1031 @@ +/* $Id: SrvPciRawR0.cpp $ */ +/** @file + * PCI passthrough - The ring 0 service. + */ + +/* + * Copyright (C) 2011-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* 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/vm.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, PVM pVM) +{ + PRAWPCIFACTORY pFactory = NULL; + int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory); + if (RT_SUCCESS(rc)) + { + if (pFactory) + { + rc = pFactory->pfnInitVm(pFactory, pVM, &pGVM->rawpci.s); + pFactory->pfnRelease(pFactory); + } + } + return VINF_SUCCESS; +} + +/** + * Per-VM R0 module termination routine. + */ +PCIRAWR0DECL(void) PciRawR0TermVM(PGVM pGVM, PVM pVM) +{ + PRAWPCIFACTORY pFactory = NULL; + int rc = SUPR0ComponentQueryFactory(pGVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory); + if (RT_SUCCESS(rc)) + { + if (pFactory) + { + pFactory->pfnDeinitVm(pFactory, pVM, &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, PVM pVM, PSUPDRVSESSION pSession, + uint32_t HostDevice, + uint32_t fFlags, + PCIRAWDEVHANDLE *pHandle, + uint32_t *pfDevFlags) +{ + + int rc = GVMMR0ValidateGVMandVMandEMT(pGVM, pVM, 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, PVM pVM, 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, pVM, 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; +} + |