/* $Id: SrvPciRawR0.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */ /** @file * PCI passthrough - The ring 0 service. */ /* * Copyright (C) 2011-2024 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 . * * SPDX-License-Identifier: GPL-3.0-only */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ #define LOG_GROUP LOG_GROUP_DEV_PCI_RAW #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /********************************************************************************************************************************* * 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; }