SrvPciRawR0.cpp revision 7790c8b32f534f1d0939c1d2231c38dc54ca4423
/* $Id$ */
/** @file
* PCI passthrough - The ring 0 service.
*/
/*
* Copyright (C) 2011 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>
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)
{
RTSPINLOCKTMP aTmp;
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
RTSpinlockAcquireNoInts(pThis->hSpinlock, &aTmp);
pThis->iPendingIrq = iHostIrq;
RTSpinlockReleaseNoInts(pThis->hSpinlock, &aTmp);
/**
* @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(PVM pVM)
{
PRAWPCIFACTORY pFactory = NULL;
int rc;
rc = SUPR0ComponentQueryFactory(pVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
if (RT_SUCCESS(rc))
{
if (pFactory)
{
PGVM pGVM = NULL;
rc = GVMMR0ByVM(pVM, &pGVM);
if (RT_SUCCESS(rc))
rc = pFactory->pfnInitVm(pFactory, pVM, &pGVM->rawpci.s);
pFactory->pfnRelease(pFactory);
}
}
return VINF_SUCCESS;
}
/**
* Per-VM R0 module termination routine.
*/
PCIRAWR0DECL(void) PciRawR0TermVM(PVM pVM)
{
PRAWPCIFACTORY pFactory = NULL;
int rc;
rc = SUPR0ComponentQueryFactory(pVM->pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
if (RT_SUCCESS(rc))
{
if (pFactory)
{
PGVM pGVM = NULL;
rc = GVMMR0ByVM(pVM, &pGVM);
if (RT_SUCCESS(rc))
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_OFFSETOF(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
static DECLCALLBACK(void) pcirawr0DevObjDestructor(void *pvObj, void *pvIns, void *pvUnused)
{
PPCIRAWDEV pThis = (PPCIRAWDEV)pvIns;
/* Forcefully deinit. */
pcirawr0DevTerm(pThis, 0);
/* And destroy. */
pThis->pPort->pfnDestroy(pThis->pPort);
RTMemFree(pThis);
}
static int pcirawr0OpenDevice(PSUPDRVSESSION pSession,
PVM pVM,
uint32_t HostDevice,
uint32_t fFlags,
PCIRAWDEVHANDLE *pHandle,
uint32_t *pfDevFlags)
{
/*
* Query the factory we want, then use it create and connect the host device.
*/
PRAWPCIFACTORY pFactory = NULL;
PRAWPCIDEVPORT pDevPort = NULL;
int rc;
PPCIRAWDEV pNew;
pNew = (PPCIRAWDEV)RTMemAllocZ(sizeof(*pNew));
if (!pNew)
return VERR_NO_MEMORY;
rc = SUPR0ComponentQueryFactory(pSession, "VBoxRawPci", RAWPCIFACTORY_UUID_STR, (void **)&pFactory);
/* No host driver registered, provide some fake implementation
for debugging purposes. */
#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)
{
PGVM pGVM = NULL;
rc = GVMMR0ByVM(pVM, &pGVM);
if (RT_SUCCESS(rc))
rc = pFactory->pfnCreateAndConnect(pFactory,
HostDevice,
fFlags,
&pGVM->rawpci.s,
&pDevPort,
pfDevFlags);
pFactory->pfnRelease(pFactory);
}
if (RT_SUCCESS(rc))
{
rc = RTSpinlockCreate(&pNew->hSpinlock);
AssertRC(rc);
rc = RTSemEventCreate(&pNew->hIrqEvent);
AssertRC(rc);
pNew->pSession = pSession;
pNew->pPort = pDevPort;
pNew->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_RAW_PCI_DEVICE,
pcirawr0DevObjDestructor, pNew, NULL);
uint32_t hHandle = 0;
rc = RTHandleTableAllocWithCtx(g_State.hHtDevs, pNew, pSession, &hHandle);
if (RT_SUCCESS(rc))
{
pNew->hHandle = (PCIRAWDEVHANDLE)hHandle;
*pHandle = pNew->hHandle;
}
else
{
SUPR0ObjRelease(pNew->pvObj, pSession);
RTSpinlockDestroy(pNew->hSpinlock);
RTSemEventDestroy(pNew->hIrqEvent);
}
}
}
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;
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)
{
/// @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)
{
/// @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)
{
/// @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)
{
/// @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;
RTSPINLOCKTMP aTmp;
bool fTerminate = false;
int32_t iPendingIrq = 0;
LogFlow(("pcirawr0GetIrq\n"));
GET_PORT(TargetDevice);
RTSpinlockAcquireNoInts(pDev->hSpinlock, &aTmp);
iPendingIrq = pDev->iPendingIrq;
pDev->iPendingIrq = 0;
fTerminate = pDev->fTerminate;
RTSpinlockReleaseNoInts(pDev->hSpinlock, &aTmp);
/* 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))
{
RTSpinlockAcquireNoInts(pDev->hSpinlock, &aTmp);
iPendingIrq = pDev->iPendingIrq;
pDev->iPendingIrq = 0;
RTSpinlockReleaseNoInts(pDev->hSpinlock, &aTmp);
}
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(PSUPDRVSESSION pSession, PVM pVM, 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(pSession, pVM,
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;
}