diff options
Diffstat (limited to 'src/VBox/HostDrivers/VBoxPci')
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/Makefile.kmk | 93 | ||||
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/VBoxPci.c | 800 | ||||
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h | 213 | ||||
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/linux/Makefile | 81 | ||||
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup | 0 | ||||
-rw-r--r-- | src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c | 1186 | ||||
-rwxr-xr-x | src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci | 111 |
7 files changed, 2484 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxPci/Makefile.kmk b/src/VBox/HostDrivers/VBoxPci/Makefile.kmk new file mode 100644 index 00000000..736824d3 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/Makefile.kmk @@ -0,0 +1,93 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for the PCI passthru driver (VBoxPci). +# + +# +# 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>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +SUB_DEPTH = ../../../.. +include $(KBUILD_PATH)/subheader.kmk +if1of ($(KBUILD_TARGET), linux) + + ifdef VBOX_WITH_VBOXDRV + # + # The driver. + # + SYSMODS += VBoxPci + VBoxPci_TEMPLATE = VBoxR0Drv + VBoxPci_INST = $(INST_VBOXPCI)$(if $(eq $(KBUILD_TARGET),darwin),Contents/MacOS/) + VBoxPci_NAME.linux = vboxpci + VBoxPci_DEFS = IN_RT_R0 VBOX_SVN_REV=$(VBOX_SVN_REV) IN_SUP_STATIC + VBoxPci_INCS = \ + . + VBoxPci_SOURCES = \ + VBoxPci.c + VBoxPci_LIBS += \ + $(PATH_STAGE_LIB)/SUPR0IdcClient$(VBOX_SUFF_LIB) + endif + + # + # Install the sources. + # + include $(PATH_SUB_CURRENT)/linux/files_vboxpci + INSTALLS += VBoxPci-src + VBoxPci-src_INST = bin/src/vboxpci/ + VBoxPci-src_SOURCES = \ + $(subst $(DQUOTE),,$(VBOX_VBOXPCI_SOURCES)) \ + $(VBoxPci-src_0_OUTDIR)/Makefile + VBoxPci-src_CLEAN = \ + $(VBoxPci-src_0_OUTDIR)/Makefile \ + $(PATH_TARGET)/VBoxPciSrc-src-1.dep + + # Generate the scripts needed for building the kernel module. + + includedep $(PATH_TARGET)/VBoxPci-src-1.dep + $$(VBoxPci-src_0_OUTDIR)/Makefile: \ + $(PATH_SUB_CURRENT)/linux/Makefile \ + $$(if $$(eq $$(VBoxPci/linux/Makefile_VBOX_HARDENED),$$(VBOX_WITH_HARDENING)),,FORCE) \ + | $$(dir $$@) + $(QUIET)$(RM) -f -- $@ + ifndef VBOX_WITH_HARDENING + $(QUIET)$(SED) -e "s;VBOX_WITH_HARDENING;;g" --output $@ $< + else + $(QUIET)$(CP) -f $< $@ + endif + %$(QUIET2)$(APPEND) -t '$(PATH_TARGET)/VBoxPci-src-1.dep' 'VBoxPci/linux/Makefile_VBOX_HARDENED=$(VBOX_WITH_HARDENING)' + + # + # Build test for the linux host kernel modules. + # + $(evalcall2 VBOX_LINUX_KMOD_TEST_BUILD_RULE_FN,VBoxPci-src,vboxdrv-src,) + +endif # Supported platform. +include $(FILE_KBUILD_SUB_FOOTER) + diff --git a/src/VBox/HostDrivers/VBoxPci/VBoxPci.c b/src/VBox/HostDrivers/VBoxPci/VBoxPci.c new file mode 100644 index 00000000..d4e756a4 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/VBoxPci.c @@ -0,0 +1,800 @@ +/* $Id: VBoxPci.c $ */ +/** @file + * VBoxPci - PCI card passthrough support (Host), Common Code. + */ + +/* + * 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +/** @page pg_rawpci VBoxPci - host PCI support + * + * This is a kernel module that works as host proxy between guest and + * PCI hardware. + * + */ + +#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW +#include <VBox/log.h> +#include <VBox/err.h> +#include <VBox/sup.h> +#include <VBox/version.h> + +#include <iprt/string.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/uuid.h> +#include <iprt/asm.h> +#include <iprt/mem.h> + +#include "VBoxPciInternal.h" + + +#define DEVPORT_2_VBOXRAWPCIINS(pPort) \ + ( (PVBOXRAWPCIINS)((uint8_t *)pPort - RT_OFFSETOF(VBOXRAWPCIINS, DevPort)) ) + + +/** + * Implements the SUPDRV component factor interface query method. + * + * @returns Pointer to an interface. NULL if not supported. + * + * @param pSupDrvFactory Pointer to the component factory registration structure. + * @param pSession The session - unused. + * @param pszInterfaceUuid The factory interface id. + */ +static DECLCALLBACK(void *) vboxPciQueryFactoryInterface(PCSUPDRVFACTORY pSupDrvFactory, PSUPDRVSESSION pSession, const char *pszInterfaceUuid) +{ + PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pSupDrvFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, SupDrvFactory)); + + /* + * Convert the UUID strings and compare them. + */ + RTUUID UuidReq; + int rc = RTUuidFromStr(&UuidReq, pszInterfaceUuid); + if (RT_SUCCESS(rc)) + { + if (!RTUuidCompareStr(&UuidReq, RAWPCIFACTORY_UUID_STR)) + { + ASMAtomicIncS32(&pGlobals->cFactoryRefs); + return &pGlobals->RawPciFactory; + } + } + else + Log(("VBoxRawPci: rc=%Rrc, uuid=%s\n", rc, pszInterfaceUuid)); + + return NULL; +} +DECLINLINE(int) vboxPciDevLock(PVBOXRAWPCIINS pThis) +{ +#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS + RTSpinlockAcquire(pThis->hSpinlock); + return VINF_SUCCESS; +#else + int rc = RTSemFastMutexRequest(pThis->hFastMtx); + + AssertRC(rc); + return rc; +#endif +} + +DECLINLINE(void) vboxPciDevUnlock(PVBOXRAWPCIINS pThis) +{ +#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS + RTSpinlockRelease(pThis->hSpinlock); +#else + RTSemFastMutexRelease(pThis->hFastMtx); +#endif +} + +DECLINLINE(int) vboxPciVmLock(PVBOXRAWPCIDRVVM pThis) +{ + int rc = RTSemFastMutexRequest(pThis->hFastMtx); + AssertRC(rc); + return rc; +} + +DECLINLINE(void) vboxPciVmUnlock(PVBOXRAWPCIDRVVM pThis) +{ + RTSemFastMutexRelease(pThis->hFastMtx); +} + +DECLINLINE(int) vboxPciGlobalsLock(PVBOXRAWPCIGLOBALS pGlobals) +{ + int rc = RTSemFastMutexRequest(pGlobals->hFastMtx); + AssertRC(rc); + return rc; +} + +DECLINLINE(void) vboxPciGlobalsUnlock(PVBOXRAWPCIGLOBALS pGlobals) +{ + RTSemFastMutexRelease(pGlobals->hFastMtx); +} + +static PVBOXRAWPCIINS vboxPciFindInstanceLocked(PVBOXRAWPCIGLOBALS pGlobals, uint32_t iHostAddress) +{ + PVBOXRAWPCIINS pCur; + for (pCur = pGlobals->pInstanceHead; pCur != NULL; pCur = pCur->pNext) + { + if (iHostAddress == pCur->HostPciAddress) + return pCur; + } + return NULL; +} + +static void vboxPciUnlinkInstanceLocked(PVBOXRAWPCIGLOBALS pGlobals, PVBOXRAWPCIINS pToUnlink) +{ + if (pGlobals->pInstanceHead == pToUnlink) + pGlobals->pInstanceHead = pToUnlink->pNext; + else + { + PVBOXRAWPCIINS pCur; + for (pCur = pGlobals->pInstanceHead; pCur != NULL; pCur = pCur->pNext) + { + if (pCur->pNext == pToUnlink) + { + pCur->pNext = pToUnlink->pNext; + break; + } + } + } + pToUnlink->pNext = NULL; +} + + +#if 0 /** @todo r=bird: Who the heck is supposed to call this?!? */ +DECLHIDDEN(void) vboxPciDevCleanup(PVBOXRAWPCIINS pThis) +{ + pThis->DevPort.pfnDeinit(&pThis->DevPort, 0); + + if (pThis->hFastMtx) + { + RTSemFastMutexDestroy(pThis->hFastMtx); + pThis->hFastMtx = NIL_RTSEMFASTMUTEX; + } + + if (pThis->hSpinlock) + { + RTSpinlockDestroy(pThis->hSpinlock); + pThis->hSpinlock = NIL_RTSPINLOCK; + } + + vboxPciGlobalsLock(pThis->pGlobals); + vboxPciUnlinkInstanceLocked(pThis->pGlobals, pThis); + vboxPciGlobalsUnlock(pThis->pGlobals); +} +#endif + + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnInit} + */ +static DECLCALLBACK(int) vboxPciDevInit(PRAWPCIDEVPORT pPort, uint32_t fFlags) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevInit(pThis, fFlags); + + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnDeinit} + */ +static DECLCALLBACK(int) vboxPciDevDeinit(PRAWPCIDEVPORT pPort, uint32_t fFlags) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + if (pThis->IrqHandler.pfnIrqHandler) + { + vboxPciOsDevUnregisterIrqHandler(pThis, pThis->IrqHandler.iHostIrq); + pThis->IrqHandler.iHostIrq = 0; + pThis->IrqHandler.pfnIrqHandler = NULL; + } + + rc = vboxPciOsDevDeinit(pThis, fFlags); + + vboxPciDevUnlock(pThis); + + return rc; +} + + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnDestroy} + */ +static DECLCALLBACK(int) vboxPciDevDestroy(PRAWPCIDEVPORT pPort) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + rc = vboxPciOsDevDestroy(pThis); + if (rc == VINF_SUCCESS) + { + if (pThis->hFastMtx) + { + RTSemFastMutexDestroy(pThis->hFastMtx); + pThis->hFastMtx = NIL_RTSEMFASTMUTEX; + } + + if (pThis->hSpinlock) + { + RTSpinlockDestroy(pThis->hSpinlock); + pThis->hSpinlock = NIL_RTSPINLOCK; + } + + vboxPciGlobalsLock(pThis->pGlobals); + vboxPciUnlinkInstanceLocked(pThis->pGlobals, pThis); + vboxPciGlobalsUnlock(pThis->pGlobals); + + RTMemFree(pThis); + } + + return rc; +} +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnGetRegionInfo} + */ +static DECLCALLBACK(int) vboxPciDevGetRegionInfo(PRAWPCIDEVPORT pPort, + int32_t iRegion, + RTHCPHYS *pRegionStart, + uint64_t *pu64RegionSize, + bool *pfPresent, + uint32_t *pfFlags) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevGetRegionInfo(pThis, iRegion, + pRegionStart, pu64RegionSize, + pfPresent, pfFlags); + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnMapRegion} + */ +static DECLCALLBACK(int) vboxPciDevMapRegion(PRAWPCIDEVPORT pPort, + int32_t iRegion, + RTHCPHYS RegionStart, + uint64_t u64RegionSize, + int32_t fFlags, + RTR0PTR *pRegionBaseR0) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevMapRegion(pThis, iRegion, RegionStart, u64RegionSize, fFlags, pRegionBaseR0); + + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnUnmapRegion} + */ +static DECLCALLBACK(int) vboxPciDevUnmapRegion(PRAWPCIDEVPORT pPort, + int32_t iRegion, + RTHCPHYS RegionStart, + uint64_t u64RegionSize, + RTR0PTR RegionBase) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevUnmapRegion(pThis, iRegion, RegionStart, u64RegionSize, RegionBase); + + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnPciCfgRead} + */ +static DECLCALLBACK(int) vboxPciDevPciCfgRead(PRAWPCIDEVPORT pPort, + uint32_t Register, + PCIRAWMEMLOC *pValue) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevPciCfgRead(pThis, Register, pValue); + + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIDEVPORT,pfnPciCfgWrite} + */ +static DECLCALLBACK(int) vboxPciDevPciCfgWrite(PRAWPCIDEVPORT pPort, + uint32_t Register, + PCIRAWMEMLOC *pValue) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevPciCfgWrite(pThis, Register, pValue); + + vboxPciDevUnlock(pThis); + + return rc; +} + +static DECLCALLBACK(int) vboxPciDevRegisterIrqHandler(PRAWPCIDEVPORT pPort, + PFNRAWPCIISR pfnHandler, + void* pIrqContext, + PCIRAWISRHANDLE *phIsr) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + int32_t iHostIrq = 0; + + if (pfnHandler == NULL) + return VERR_INVALID_PARAMETER; + + vboxPciDevLock(pThis); + + if (pThis->IrqHandler.pfnIrqHandler) + { + rc = VERR_ALREADY_EXISTS; + } + else + { + rc = vboxPciOsDevRegisterIrqHandler(pThis, pfnHandler, pIrqContext, &iHostIrq); + if (RT_SUCCESS(rc)) + { + *phIsr = 0xcafe0000; + pThis->IrqHandler.iHostIrq = iHostIrq; + pThis->IrqHandler.pfnIrqHandler = pfnHandler; + pThis->IrqHandler.pIrqContext = pIrqContext; + } + } + + vboxPciDevUnlock(pThis); + + return rc; +} + +static DECLCALLBACK(int) vboxPciDevUnregisterIrqHandler(PRAWPCIDEVPORT pPort, + PCIRAWISRHANDLE hIsr) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + if (hIsr != 0xcafe0000) + return VERR_INVALID_PARAMETER; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevUnregisterIrqHandler(pThis, pThis->IrqHandler.iHostIrq); + if (RT_SUCCESS(rc)) + { + pThis->IrqHandler.pfnIrqHandler = NULL; + pThis->IrqHandler.pIrqContext = NULL; + pThis->IrqHandler.iHostIrq = 0; + } + vboxPciDevUnlock(pThis); + + return rc; +} + +static DECLCALLBACK(int) vboxPciDevPowerStateChange(PRAWPCIDEVPORT pPort, + PCIRAWPOWERSTATE aState, + uint64_t *pu64Param) +{ + PVBOXRAWPCIINS pThis = DEVPORT_2_VBOXRAWPCIINS(pPort); + int rc; + + vboxPciDevLock(pThis); + + rc = vboxPciOsDevPowerStateChange(pThis, aState); + + switch (aState) + { + case PCIRAW_POWER_ON: + /* + * Let virtual device know about VM caps. + */ + *pu64Param = VBOX_DRV_VMDATA(pThis)->pPerVmData->fVmCaps; + break; + default: + pu64Param = 0; + break; + } + + + vboxPciDevUnlock(pThis); + + return rc; +} + +/** + * Creates a new instance. + * + * @returns VBox status code. + * @param pGlobals The globals. + * @param u32HostAddress Host address. + * @param fFlags Flags. + * @param pVmCtx VM context. + * @param ppDevPort Where to store the pointer to our port interface. + * @param pfDevFlags The device flags. + */ +static int vboxPciNewInstance(PVBOXRAWPCIGLOBALS pGlobals, + uint32_t u32HostAddress, + uint32_t fFlags, + PRAWPCIPERVM pVmCtx, + PRAWPCIDEVPORT *ppDevPort, + uint32_t *pfDevFlags) +{ + int rc; + PVBOXRAWPCIINS pNew = (PVBOXRAWPCIINS)RTMemAllocZ(sizeof(*pNew)); + if (!pNew) + return VERR_NO_MEMORY; + + pNew->pGlobals = pGlobals; + pNew->hSpinlock = NIL_RTSPINLOCK; + pNew->cRefs = 1; + pNew->pNext = NULL; + pNew->HostPciAddress = u32HostAddress; + pNew->pVmCtx = pVmCtx; + + pNew->DevPort.u32Version = RAWPCIDEVPORT_VERSION; + + pNew->DevPort.pfnInit = vboxPciDevInit; + pNew->DevPort.pfnDeinit = vboxPciDevDeinit; + pNew->DevPort.pfnDestroy = vboxPciDevDestroy; + pNew->DevPort.pfnGetRegionInfo = vboxPciDevGetRegionInfo; + pNew->DevPort.pfnMapRegion = vboxPciDevMapRegion; + pNew->DevPort.pfnUnmapRegion = vboxPciDevUnmapRegion; + pNew->DevPort.pfnPciCfgRead = vboxPciDevPciCfgRead; + pNew->DevPort.pfnPciCfgWrite = vboxPciDevPciCfgWrite; + pNew->DevPort.pfnPciCfgRead = vboxPciDevPciCfgRead; + pNew->DevPort.pfnPciCfgWrite = vboxPciDevPciCfgWrite; + pNew->DevPort.pfnRegisterIrqHandler = vboxPciDevRegisterIrqHandler; + pNew->DevPort.pfnUnregisterIrqHandler = vboxPciDevUnregisterIrqHandler; + pNew->DevPort.pfnPowerStateChange = vboxPciDevPowerStateChange; + pNew->DevPort.u32VersionEnd = RAWPCIDEVPORT_VERSION; + + rc = RTSpinlockCreate(&pNew->hSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxPCI"); + if (RT_SUCCESS(rc)) + { + rc = RTSemFastMutexCreate(&pNew->hFastMtx); + if (RT_SUCCESS(rc)) + { + rc = pNew->DevPort.pfnInit(&pNew->DevPort, fFlags); + if (RT_SUCCESS(rc)) + { + *ppDevPort = &pNew->DevPort; + + pNew->pNext = pGlobals->pInstanceHead; + pGlobals->pInstanceHead = pNew; + } + else + { + RTSemFastMutexDestroy(pNew->hFastMtx); + RTSpinlockDestroy(pNew->hSpinlock); + RTMemFree(pNew); + } + } + } + + return rc; +} + +/** + * @interface_method_impl{RAWPCIFACTORY,pfnCreateAndConnect} + */ +static DECLCALLBACK(int) vboxPciFactoryCreateAndConnect(PRAWPCIFACTORY pFactory, + uint32_t u32HostAddress, + uint32_t fFlags, + PRAWPCIPERVM pVmCtx, + PRAWPCIDEVPORT *ppDevPort, + uint32_t *pfDevFlags) +{ + PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, RawPciFactory)); + int rc; + + LogFlow(("vboxPciFactoryCreateAndConnect: PCI=%x fFlags=%#x\n", u32HostAddress, fFlags)); + Assert(pGlobals->cFactoryRefs > 0); + rc = vboxPciGlobalsLock(pGlobals); + AssertRCReturn(rc, rc); + + /* First search if there's no existing instance with same host device + * address - if so - we cannot continue. + */ + if (vboxPciFindInstanceLocked(pGlobals, u32HostAddress) != NULL) + { + rc = VERR_RESOURCE_BUSY; + goto unlock; + } + + rc = vboxPciNewInstance(pGlobals, u32HostAddress, fFlags, pVmCtx, ppDevPort, pfDevFlags); + +unlock: + vboxPciGlobalsUnlock(pGlobals); + + return rc; +} + +/** + * @interface_method_impl{RAWPCIFACTORY,pfnRelease} + */ +static DECLCALLBACK(void) vboxPciFactoryRelease(PRAWPCIFACTORY pFactory) +{ + PVBOXRAWPCIGLOBALS pGlobals = (PVBOXRAWPCIGLOBALS)((uint8_t *)pFactory - RT_OFFSETOF(VBOXRAWPCIGLOBALS, RawPciFactory)); + + int32_t cRefs = ASMAtomicDecS32(&pGlobals->cFactoryRefs); + Assert(cRefs >= 0); NOREF(cRefs); + LogFlow(("vboxPciFactoryRelease: cRefs=%d (new)\n", cRefs)); +} + +/** + * @interface_method_impl{RAWPCIFACTORY,pfnInitVm} + */ +static DECLCALLBACK(int) vboxPciFactoryInitVm(PRAWPCIFACTORY pFactory, + PVM pVM, + PRAWPCIPERVM pVmData) +{ + PVBOXRAWPCIDRVVM pThis = (PVBOXRAWPCIDRVVM)RTMemAllocZ(sizeof(VBOXRAWPCIDRVVM)); + int rc; + + if (!pThis) + return VERR_NO_MEMORY; + + rc = RTSemFastMutexCreate(&pThis->hFastMtx); + if (RT_SUCCESS(rc)) + { + rc = vboxPciOsInitVm(pThis, pVM, pVmData); + + if (RT_SUCCESS(rc)) + { +#ifdef VBOX_WITH_IOMMU + /* If IOMMU notification routine in pVmData->pfnContigMemInfo + is set - we have functional IOMMU hardware. */ + if (pVmData->pfnContigMemInfo) + pVmData->fVmCaps |= PCIRAW_VMFLAGS_HAS_IOMMU; +#endif + pThis->pPerVmData = pVmData; + pVmData->pDriverData = pThis; + return VINF_SUCCESS; + } + + RTSemFastMutexDestroy(pThis->hFastMtx); + pThis->hFastMtx = NIL_RTSEMFASTMUTEX; + RTMemFree(pThis); + } + + return rc; +} + +/** + * @interface_method_impl{RAWPCIFACTORY,pfnDeinitVm} + */ +static DECLCALLBACK(void) vboxPciFactoryDeinitVm(PRAWPCIFACTORY pFactory, + PVM pVM, + PRAWPCIPERVM pVmData) +{ + if (pVmData->pDriverData) + { + PVBOXRAWPCIDRVVM pThis = (PVBOXRAWPCIDRVVM)pVmData->pDriverData; + +#ifdef VBOX_WITH_IOMMU + /* If we have IOMMU, need to unmap all guest's physical pages from IOMMU on VM termination. */ +#endif + + vboxPciOsDeinitVm(pThis, pVM); + + if (pThis->hFastMtx) + { + RTSemFastMutexDestroy(pThis->hFastMtx); + pThis->hFastMtx = NIL_RTSEMFASTMUTEX; + } + + RTMemFree(pThis); + pVmData->pDriverData = NULL; + } +} + + +static bool vboxPciCanUnload(PVBOXRAWPCIGLOBALS pGlobals) +{ + int rc = vboxPciGlobalsLock(pGlobals); + bool fRc = !pGlobals->pInstanceHead + && pGlobals->cFactoryRefs <= 0; + vboxPciGlobalsUnlock(pGlobals); + AssertRC(rc); + return fRc; +} + + +static int vboxPciInitIdc(PVBOXRAWPCIGLOBALS pGlobals) +{ + int rc; + Assert(!pGlobals->fIDCOpen); + + /* + * Establish a connection to SUPDRV and register our component factory. + */ + rc = SUPR0IdcOpen(&pGlobals->SupDrvIDC, 0 /* iReqVersion = default */, 0 /* iMinVersion = default */, NULL, NULL, NULL); + if (RT_SUCCESS(rc)) + { + rc = SUPR0IdcComponentRegisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + if (RT_SUCCESS(rc)) + { + pGlobals->fIDCOpen = true; + Log(("VBoxRawPci: pSession=%p\n", SUPR0IdcGetSession(&pGlobals->SupDrvIDC))); + return rc; + } + + /* bail out. */ + LogRel(("VBoxRawPci: Failed to register component factory, rc=%Rrc\n", rc)); + SUPR0IdcClose(&pGlobals->SupDrvIDC); + } + + return rc; +} + + +/** + * Try to close the IDC connection to SUPDRV if established. + * + * @returns VBox status code. + * @retval VINF_SUCCESS on success. + * @retval VERR_WRONG_ORDER if we're busy. + * + * @param pGlobals Pointer to the globals. + */ +static int vboxPciDeleteIdc(PVBOXRAWPCIGLOBALS pGlobals) +{ + int rc; + + Assert(pGlobals->hFastMtx != NIL_RTSEMFASTMUTEX); + + /* + * Check before trying to deregister the factory. + */ + if (!vboxPciCanUnload(pGlobals)) + return VERR_WRONG_ORDER; + + if (!pGlobals->fIDCOpen) + rc = VINF_SUCCESS; + else + { + /* + * Disconnect from SUPDRV. + */ + rc = SUPR0IdcComponentDeregisterFactory(&pGlobals->SupDrvIDC, &pGlobals->SupDrvFactory); + AssertRC(rc); + SUPR0IdcClose(&pGlobals->SupDrvIDC); + pGlobals->fIDCOpen = false; + } + + return rc; +} + + +/** + * Initializes the globals. + * + * @returns VBox status code. + * @param pGlobals Pointer to the globals. + */ +static int vboxPciInitGlobals(PVBOXRAWPCIGLOBALS pGlobals) +{ + /* + * Initialize the common portions of the structure. + */ + int rc = RTSemFastMutexCreate(&pGlobals->hFastMtx); + if (RT_SUCCESS(rc)) + { + pGlobals->pInstanceHead = NULL; + pGlobals->RawPciFactory.pfnRelease = vboxPciFactoryRelease; + pGlobals->RawPciFactory.pfnCreateAndConnect = vboxPciFactoryCreateAndConnect; + pGlobals->RawPciFactory.pfnInitVm = vboxPciFactoryInitVm; + pGlobals->RawPciFactory.pfnDeinitVm = vboxPciFactoryDeinitVm; + memcpy(pGlobals->SupDrvFactory.szName, "VBoxRawPci", sizeof("VBoxRawPci")); + pGlobals->SupDrvFactory.pfnQueryFactoryInterface = vboxPciQueryFactoryInterface; + pGlobals->fIDCOpen = false; + } + return rc; +} + + +/** + * Deletes the globals. + * + * @param pGlobals Pointer to the globals. + */ +static void vboxPciDeleteGlobals(PVBOXRAWPCIGLOBALS pGlobals) +{ + Assert(!pGlobals->fIDCOpen); + + /* + * Release resources. + */ + if (pGlobals->hFastMtx) + { + RTSemFastMutexDestroy(pGlobals->hFastMtx); + pGlobals->hFastMtx = NIL_RTSEMFASTMUTEX; + } +} + + +int vboxPciInit(PVBOXRAWPCIGLOBALS pGlobals) +{ + + /* + * Initialize the common portions of the structure. + */ + int rc = vboxPciInitGlobals(pGlobals); + if (RT_SUCCESS(rc)) + { + rc = vboxPciInitIdc(pGlobals); + if (RT_SUCCESS(rc)) + return rc; + + /* bail out. */ + vboxPciDeleteGlobals(pGlobals); + } + + return rc; +} + +void vboxPciShutdown(PVBOXRAWPCIGLOBALS pGlobals) +{ + int rc = vboxPciDeleteIdc(pGlobals); + if (RT_SUCCESS(rc)) + vboxPciDeleteGlobals(pGlobals); +} + diff --git a/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h b/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h new file mode 100644 index 00000000..c82cd18d --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h @@ -0,0 +1,213 @@ +/* $Id: VBoxPciInternal.h $ */ +/** @file + * VBoxPci - PCI driver (Host), Internal Header. + */ + +/* + * 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#ifndef VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h +#define VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/sup.h> +#include <VBox/rawpci.h> +#include <iprt/semaphore.h> +#include <iprt/assert.h> + +#ifdef RT_OS_LINUX + +#if RTLNX_VER_MIN(2,6,35) && defined(CONFIG_IOMMU_API) +# define VBOX_WITH_IOMMU +#endif + +#ifdef VBOX_WITH_IOMMU +#include <linux/errno.h> +#include <linux/iommu.h> +#endif + +#endif + +RT_C_DECLS_BEGIN + +/* Forward declaration. */ +typedef struct VBOXRAWPCIGLOBALS *PVBOXRAWPCIGLOBALS; +typedef struct VBOXRAWPCIDRVVM *PVBOXRAWPCIDRVVM; +typedef struct VBOXRAWPCIINS *PVBOXRAWPCIINS; + +typedef struct VBOXRAWPCIISRDESC +{ + /** Handler function. */ + PFNRAWPCIISR pfnIrqHandler; + /** Handler context. */ + void *pIrqContext; + /** Host IRQ. */ + int32_t iHostIrq; +} VBOXRAWPCIISRDESC; +typedef struct VBOXRAWPCIISRDESC *PVBOXRAWPCIISRDESC; + +/** + * The per-instance data of the VBox raw PCI interface. + * + * This is data associated with a host PCI card attached to the VM. + * + */ +typedef struct VBOXRAWPCIINS +{ + /** Pointer to the globals. */ + PVBOXRAWPCIGLOBALS pGlobals; + + /** Mutex protecting device access. */ + RTSEMFASTMUTEX hFastMtx; + /** The spinlock protecting the state variables and device access. */ + RTSPINLOCK hSpinlock; + /** Pointer to the next device in the list. */ + PVBOXRAWPCIINS pNext; + /** Reference count. */ + uint32_t volatile cRefs; + + /* Host PCI address of this device. */ + uint32_t HostPciAddress; + +#ifdef RT_OS_LINUX + struct pci_dev * pPciDev; + char szPrevDriver[64]; +#endif + bool fMsiUsed; + bool fMsixUsed; + bool fIommuUsed; + bool fPad0; + + /** Port, given to the outside world. */ + RAWPCIDEVPORT DevPort; + + /** IRQ handler. */ + VBOXRAWPCIISRDESC IrqHandler; + + /** Pointer to per-VM context in hypervisor data. */ + PRAWPCIPERVM pVmCtx; + + RTR0PTR aRegionR0Mapping[/* XXX: magic */ 7]; +} VBOXRAWPCIINS; + +/** + * Per-VM data of the VBox PCI driver. Pointed to by pGVM->rawpci.s.pDriverData. + * + */ +typedef struct VBOXRAWPCIDRVVM +{ + /** Mutex protecting state changes. */ + RTSEMFASTMUTEX hFastMtx; + +#ifdef RT_OS_LINUX +# ifdef VBOX_WITH_IOMMU + /* IOMMU domain. */ + struct iommu_domain* pIommuDomain; +# endif +#endif + /* Back pointer to pGVM->rawpci.s. */ + PRAWPCIPERVM pPerVmData; +} VBOXRAWPCIDRVVM; + +/** + * The global data of the VBox PCI driver. + * + * This contains the bit required for communicating with support driver, VBoxDrv + * (start out as SupDrv). + */ +typedef struct VBOXRAWPCIGLOBALS +{ + /** Mutex protecting the list of instances and state changes. */ + RTSEMFASTMUTEX hFastMtx; + + /** Pointer to a list of instance data. */ + PVBOXRAWPCIINS pInstanceHead; + + /** The raw PCI interface factory. */ + RAWPCIFACTORY RawPciFactory; + /** The SUPDRV component factory registration. */ + SUPDRVFACTORY SupDrvFactory; + /** The number of current factory references. */ + int32_t volatile cFactoryRefs; + /** Whether the IDC connection is open or not. + * This is only for cleaning up correctly after the separate IDC init on Windows. */ + bool fIDCOpen; + /** The SUPDRV IDC handle (opaque struct). */ + SUPDRVIDCHANDLE SupDrvIDC; +#ifdef RT_OS_LINUX + bool fPciStubModuleAvail; + struct module * pciStubModule; +#endif +} VBOXRAWPCIGLOBALS; + +DECLHIDDEN(int) vboxPciInit(PVBOXRAWPCIGLOBALS pGlobals); +DECLHIDDEN(void) vboxPciShutdown(PVBOXRAWPCIGLOBALS pGlobals); + +DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData); +DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM); + +DECLHIDDEN(int) vboxPciOsDevInit (PVBOXRAWPCIINS pIns, uint32_t fFlags); +DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags); +DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns); + +DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS *pRegionStart, + uint64_t *pu64RegionSize, + bool *pfPresent, + uint32_t *pfFlags); +DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS pRegionStart, + uint64_t u64RegionSize, + uint32_t fFlags, + RTR0PTR *pRegionBase); +DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS RegionStart, + uint64_t u64RegionSize, + RTR0PTR RegionBase); + +DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue); +DECLHIDDEN(int) vboxPciOsDevPciCfgRead (PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue); + +DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler (PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq); +DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq); + +DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState); + +#define VBOX_DRV_VMDATA(pIns) ((PVBOXRAWPCIDRVVM)(pIns->pVmCtx ? pIns->pVmCtx->pDriverData : NULL)) + +RT_C_DECLS_END + +#endif /* !VBOX_INCLUDED_SRC_VBoxPci_VBoxPciInternal_h */ diff --git a/src/VBox/HostDrivers/VBoxPci/linux/Makefile b/src/VBox/HostDrivers/VBoxPci/linux/Makefile new file mode 100644 index 00000000..bbcefe76 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/linux/Makefile @@ -0,0 +1,81 @@ +# $Id: Makefile $ +## @file +# Makefile for the VirtualBox Linux Host PCI Driver. +# + +# +# 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>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + + +# Linux kbuild sets this to our source directory if we are called from there +obj ?= $(CURDIR) +include $(obj)/Makefile-header.gmk +VBOXPCI_DIR := $(VBOX_MODULE_SRC_DIR) + +# Allow building directly from the subdirectory without assuming the toplevel +# makefile has done the copying. Not the default use case, but can be handy. +ifndef KBUILD_EXTRA_SYMBOLS +KBUILD_EXTRA_SYMBOLS=$(abspath $(VBOXPCI_DIR)/../vboxdrv/Module.symvers) +endif + +# override is required by the Debian guys +VBOXMOD_NAME = vboxpci +VBOXMOD_OBJS = \ + linux/VBoxPci-linux.o \ + VBoxPci.o \ + SUPR0IdcClient.o \ + SUPR0IdcClientComponent.o \ + linux/SUPR0IdcClient-linux.o +ifeq ($(VBOX_KBUILD_TARGET_ARCH),x86) +VBOXMOD_OBJS += \ + math/gcc/divdi3.o \ + math/gcc/moddi3.o \ + math/gcc/qdivrem.o \ + math/gcc/udivdi3.o \ + math/gcc/udivmoddi4.o \ + math/gcc/divdi3.o \ + math/gcc/umoddi3.o +endif +VBOXMOD_INCL = \ + $(VBOXPCI_DIR) \ + $(VBOXPCI_DIR)include \ + $(VBOXPCI_DIR)r0drv/linux +VBOXMOD_DEFS = \ + RT_OS_LINUX \ + IN_RING0 \ + IN_RT_R0 \ + IN_SUP_R0 \ + VBOX \ + RT_WITH_VBOX \ + VBOX_WITH_HARDENING +VBOXMOD_CFLAGS = -include $(VBOXPCI_DIR)include/VBox/SUPDrvMangling.h -fno-pie + +include $(obj)/Makefile-footer.gmk diff --git a/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup b/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/linux/Makefile.kup diff --git a/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c b/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c new file mode 100644 index 00000000..36685dd9 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c @@ -0,0 +1,1186 @@ +/* $Id: VBoxPci-linux.c $ */ +/** @file + * VBoxPci - PCI Driver (Host), Linux Specific Code. + */ + +/* + * 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>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "the-linux-kernel.h" +#include "version-generated.h" +#include "revision-generated.h" +#include "product-generated.h" + +#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW +#include <VBox/log.h> +#include <VBox/err.h> +#include <iprt/process.h> +#include <iprt/initterm.h> +#include <iprt/string.h> +#include <iprt/mem.h> + +#include "../VBoxPciInternal.h" + +#ifdef VBOX_WITH_IOMMU +# include <linux/dmar.h> +# include <linux/intel-iommu.h> +# include <linux/pci.h> +# if RTLNX_VER_MAX(3,1,0) && \ + (RTLNX_VER_MAX(2,6,41) || RTLNX_VER_MIN(3,0,0)) +# include <asm/amd_iommu.h> +# else +# include <linux/amd-iommu.h> +# endif +# if RTLNX_VER_MAX(3,2,0) +# define IOMMU_PRESENT() iommu_found() +# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc() +# else +# define IOMMU_PRESENT() iommu_present(&pci_bus_type) +# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc(&pci_bus_type) +# endif +#endif /* VBOX_WITH_IOMMU */ + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static int __init VBoxPciLinuxInit(void); +static void __exit VBoxPciLinuxUnload(void); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static VBOXRAWPCIGLOBALS g_VBoxPciGlobals; + +module_init(VBoxPciLinuxInit); +module_exit(VBoxPciLinuxUnload); + +MODULE_AUTHOR(VBOX_VENDOR); +MODULE_DESCRIPTION(VBOX_PRODUCT " PCI access Driver"); +MODULE_LICENSE("GPL"); +#ifdef MODULE_VERSION +MODULE_VERSION(VBOX_VERSION_STRING " r" RT_XSTR(VBOX_SVN_REV)); +#endif + + +#if RTLNX_VER_MIN(2,6,20) +# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p) +# define PCI_DEV_PUT(x) pci_dev_put(x) +#if RTLNX_VER_MIN(4,17,0) +/* assume the domain number to be zero - exactly the same assumption of + * pci_get_bus_and_slot() + */ +# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_domain_bus_and_slot(0, bus, devfn) +#else +# define PCI_DEV_GET_SLOT(bus, devfn) pci_get_bus_and_slot(bus, devfn) +#endif +#else +# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p) +# define PCI_DEV_PUT(x) do { } while (0) +# define PCI_DEV_GET_SLOT(bus, devfn) pci_find_slot(bus, devfn) +#endif + +/** + * Name of module used to attach to the host PCI device, when + * PCI device passthrough is used. + */ +#define PCI_STUB_MODULE "pci-stub" +/* For some reasons my kernel names module for find_module() this way, + * while device name seems to be above one. + */ +#define PCI_STUB_MODULE_NAME "pci_stub" + +/** + * Our driver name. + */ +#define DRIVER_NAME "vboxpci" + +/* + * Currently we keep the device bound to pci stub driver, so + * dev_printk() &co would report that instead of our name. They also + * expect non-NULL dev pointer in older kernels. + */ +#define vbpci_printk(level, pdev, format, arg...) \ + printk(level DRIVER_NAME "%s%s: " format, \ + pdev ? " " : "", pdev ? pci_name(pdev) : "", \ + ## arg) + + +/** + * Initialize module. + * + * @returns appropriate status code. + */ +static int __init VBoxPciLinuxInit(void) +{ + int rc; + /* + * Initialize IPRT. + */ + rc = RTR0Init(0); + + if (RT_FAILURE(rc)) + goto error; + + + LogRel(("VBoxPciLinuxInit\n")); + + RT_ZERO(g_VBoxPciGlobals); + + rc = vboxPciInit(&g_VBoxPciGlobals); + if (RT_FAILURE(rc)) + { + LogRel(("cannot do VBoxPciInit: %Rc\n", rc)); + goto error; + } + +#if defined(CONFIG_PCI_STUB) + /* nothing to do, pci_stub module part of the kernel */ + g_VBoxPciGlobals.fPciStubModuleAvail = true; + +#elif defined(CONFIG_PCI_STUB_MODULE) + if (request_module(PCI_STUB_MODULE) == 0) + { +# if RTLNX_VER_MIN(2,6,30) + /* find_module() is static before Linux 2.6.30 */ + mutex_lock(&module_mutex); + g_VBoxPciGlobals.pciStubModule = find_module(PCI_STUB_MODULE_NAME); + mutex_unlock(&module_mutex); + if (g_VBoxPciGlobals.pciStubModule) + { + if (try_module_get(g_VBoxPciGlobals.pciStubModule)) + g_VBoxPciGlobals.fPciStubModuleAvail = true; + } + else + printk(KERN_INFO "vboxpci: find_module %s failed\n", PCI_STUB_MODULE); +# endif + } + else + printk(KERN_INFO "vboxpci: cannot load %s\n", PCI_STUB_MODULE); + +#else + printk(KERN_INFO "vboxpci: %s module not available, cannot detach PCI devices\n", + PCI_STUB_MODULE); +#endif + +#ifdef VBOX_WITH_IOMMU + if (IOMMU_PRESENT()) + printk(KERN_INFO "vboxpci: IOMMU found\n"); + else + printk(KERN_INFO "vboxpci: IOMMU not found (not registered)\n"); +#else + printk(KERN_INFO "vboxpci: IOMMU not found (not compiled)\n"); +#endif + + return 0; + + error: + return -RTErrConvertToErrno(rc); +} + +/** + * Unload the module. + */ +static void __exit VBoxPciLinuxUnload(void) +{ + LogRel(("VBoxPciLinuxLinuxUnload\n")); + + /* + * Undo the work done during start (in reverse order). + */ + vboxPciShutdown(&g_VBoxPciGlobals); + + RTR0Term(); + + if (g_VBoxPciGlobals.pciStubModule) + { + module_put(g_VBoxPciGlobals.pciStubModule); + g_VBoxPciGlobals.pciStubModule = NULL; + } + + Log(("VBoxPciLinuxUnload - done\n")); +} + +static int vboxPciLinuxDevRegisterWithIommu(PVBOXRAWPCIINS pIns) +{ +#ifdef VBOX_WITH_IOMMU + int rc = VINF_SUCCESS; + struct pci_dev *pPciDev = pIns->pPciDev; + PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns); + IPRT_LINUX_SAVE_EFL_AC(); + + if (RT_LIKELY(pData)) + { + if (RT_LIKELY(pData->pIommuDomain)) + { + /** @todo KVM checks IOMMU_CAP_CACHE_COHERENCY and sets + * flag IOMMU_CACHE later used when mapping physical + * addresses, which could improve performance. + */ + int rcLnx = iommu_attach_device(pData->pIommuDomain, &pPciDev->dev); + if (!rcLnx) + { + vbpci_printk(KERN_DEBUG, pPciDev, "attached to IOMMU\n"); + pIns->fIommuUsed = true; + rc = VINF_SUCCESS; + } + else + { + vbpci_printk(KERN_DEBUG, pPciDev, "failed to attach to IOMMU, error %d\n", rcLnx); + rc = VERR_INTERNAL_ERROR; + } + } + else + { + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "cannot attach to IOMMU, no domain\n"); + rc = VERR_NOT_FOUND; + } + } + else + { + vbpci_printk(KERN_DEBUG, pPciDev, "cannot attach to IOMMU, no VM data\n"); + rc = VERR_INVALID_PARAMETER; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +#else + return VERR_NOT_SUPPORTED; +#endif +} + +static int vboxPciLinuxDevUnregisterWithIommu(PVBOXRAWPCIINS pIns) +{ +#ifdef VBOX_WITH_IOMMU + int rc = VINF_SUCCESS; + struct pci_dev *pPciDev = pIns->pPciDev; + PVBOXRAWPCIDRVVM pData = VBOX_DRV_VMDATA(pIns); + IPRT_LINUX_SAVE_EFL_AC(); + + if (RT_LIKELY(pData)) + { + if (RT_LIKELY(pData->pIommuDomain)) + { + if (pIns->fIommuUsed) + { + iommu_detach_device(pData->pIommuDomain, &pIns->pPciDev->dev); + vbpci_printk(KERN_DEBUG, pPciDev, "detached from IOMMU\n"); + pIns->fIommuUsed = false; + } + } + else + { + vbpci_printk(KERN_DEBUG, pPciDev, + "cannot detach from IOMMU, no domain\n"); + rc = VERR_NOT_FOUND; + } + } + else + { + vbpci_printk(KERN_DEBUG, pPciDev, + "cannot detach from IOMMU, no VM data\n"); + rc = VERR_INVALID_PARAMETER; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +#else + return VERR_NOT_SUPPORTED; +#endif +} + +static int vboxPciLinuxDevReset(PVBOXRAWPCIINS pIns) +{ + int rc = VINF_SUCCESS; + IPRT_LINUX_SAVE_EFL_AC(); + + if (RT_LIKELY(pIns->pPciDev)) + { +#if RTLNX_VER_MIN(2,6,28) + if (pci_reset_function(pIns->pPciDev)) + { + vbpci_printk(KERN_DEBUG, pIns->pPciDev, + "pci_reset_function() failed\n"); + rc = VERR_INTERNAL_ERROR; + } +#else + rc = VERR_NOT_SUPPORTED; +#endif + } + else + rc = VERR_INVALID_PARAMETER; + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +static struct file* vboxPciFileOpen(const char* path, int flags) +{ + struct file* filp = NULL; + int err = 0; + + filp = filp_open(path, flags, 0); + + if (IS_ERR(filp)) + { + err = PTR_ERR(filp); + printk(KERN_DEBUG "vboxPciFileOpen: error %d\n", err); + return NULL; + } + + if (!filp->f_op || !filp->f_op->write) + { + printk(KERN_DEBUG "Not writable FS\n"); + filp_close(filp, NULL); + return NULL; + } + + return filp; +} + +static void vboxPciFileClose(struct file* file) +{ + filp_close(file, NULL); +} + +static int vboxPciFileWrite(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size) +{ + int ret; + mm_segment_t fs_save; + + fs_save = get_fs(); + set_fs(KERNEL_DS); +#if RTLNX_VER_MIN(4,14,0) + ret = kernel_write(file, data, size, &offset); +#else + ret = vfs_write(file, data, size, &offset); +#endif + set_fs(fs_save); + if (ret < 0) + printk(KERN_DEBUG "vboxPciFileWrite: error %d\n", ret); + + return ret; +} + +static int vboxPciLinuxDevDetachHostDriver(PVBOXRAWPCIINS pIns) +{ + struct pci_dev *pPciDev = NULL; + uint8_t uBus = (pIns->HostPciAddress) >> 8; + uint8_t uDevFn = (pIns->HostPciAddress) & 0xff; + const char* currentDriver; + uint16_t uVendor, uDevice; + bool fDetach = 0; + + if (!g_VBoxPciGlobals.fPciStubModuleAvail) + { + printk(KERN_INFO "vboxpci: stub module %s not detected: cannot detach\n", + PCI_STUB_MODULE); + return VERR_ACCESS_DENIED; + } + + pPciDev = PCI_DEV_GET_SLOT(uBus, uDevFn); + + if (!pPciDev) + { + printk(KERN_INFO "vboxpci: device at %02x:%02x.%d not found\n", + uBus, uDevFn>>3, uDevFn&7); + return VERR_NOT_FOUND; + } + + uVendor = pPciDev->vendor; + uDevice = pPciDev->device; + + currentDriver = pPciDev->driver ? pPciDev->driver->name : NULL; + + printk(KERN_DEBUG "vboxpci: detected device: %04x:%04x at %02x:%02x.%d, driver %s\n", + uVendor, uDevice, uBus, uDevFn>>3, uDevFn&7, + currentDriver ? currentDriver : "<none>"); + + fDetach = (currentDriver == NULL || (strcmp(currentDriver, PCI_STUB_MODULE) != 0)); + + /* Init previous driver data. */ + pIns->szPrevDriver[0] = '\0'; + + if (fDetach && currentDriver) + { + /* Dangerous: if device name for some reasons contains slashes - arbitrary file could be written to. */ + if (strchr(currentDriver, '/') != 0) + { + printk(KERN_DEBUG "vboxpci: ERROR: %s contains invalid symbols\n", currentDriver); + return VERR_ACCESS_DENIED; + } + /** @todo RTStrCopy not exported. */ + strncpy(pIns->szPrevDriver, currentDriver, sizeof(pIns->szPrevDriver) - 1); + pIns->szPrevDriver[sizeof(pIns->szPrevDriver) - 1] = '\0'; + } + + PCI_DEV_PUT(pPciDev); + pPciDev = NULL; + + if (fDetach) + { + char* szCmdBuf; + char* szFileBuf; + struct file* pFile; + int iCmdLen; + const int cMaxBuf = 128; +#if RTLNX_VER_MIN(2,6,29) + const struct cred *pOldCreds; + struct cred *pNewCreds; +#endif + + /* + * Now perform kernel analog of: + * + * echo -n "10de 040a" > /sys/bus/pci/drivers/pci-stub/new_id + * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/unbind + * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/bind + * + * We do this way, as this interface is presumingly more stable than + * in-kernel ones. + */ + szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL); + szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL); + if (!szCmdBuf || !szFileBuf) + goto done; + + /* Somewhat ugly hack - override current credentials */ +#if RTLNX_VER_MIN(2,6,29) + pNewCreds = prepare_creds(); + if (!pNewCreds) + goto done; + +# if RTLNX_VER_MIN(3,5,0) + pNewCreds->fsuid = GLOBAL_ROOT_UID; +# else + pNewCreds->fsuid = 0; +# endif + pOldCreds = override_creds(pNewCreds); +#endif + + RTStrPrintf(szFileBuf, cMaxBuf, + "/sys/bus/pci/drivers/%s/new_id", + PCI_STUB_MODULE); + pFile = vboxPciFileOpen(szFileBuf, O_WRONLY); + if (pFile) + { + iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf, + "%04x %04x", + uVendor, uDevice); + /* Don't write trailing \0 */ + vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen); + vboxPciFileClose(pFile); + } + else + printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf); + + iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf, + "0000:%02x:%02x.%d", + uBus, uDevFn>>3, uDevFn&7); + + /* Unbind if bound to smth */ + if (pIns->szPrevDriver[0]) + { + RTStrPrintf(szFileBuf, cMaxBuf, + "/sys/bus/pci/drivers/%s/unbind", + pIns->szPrevDriver); + pFile = vboxPciFileOpen(szFileBuf, O_WRONLY); + if (pFile) + { + + /* Don't write trailing \0 */ + vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen); + vboxPciFileClose(pFile); + } + else + printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf); + } + + RTStrPrintf(szFileBuf, cMaxBuf, + "/sys/bus/pci/drivers/%s/bind", + PCI_STUB_MODULE); + pFile = vboxPciFileOpen(szFileBuf, O_WRONLY); + if (pFile) + { + /* Don't write trailing \0 */ + vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen); + vboxPciFileClose(pFile); + } + else + printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf); + +#if RTLNX_VER_MIN(2,6,29) + revert_creds(pOldCreds); + put_cred(pNewCreds); +#endif + + done: + kfree(szCmdBuf); + kfree(szFileBuf); + } + + return 0; +} + +static int vboxPciLinuxDevReattachHostDriver(PVBOXRAWPCIINS pIns) +{ + struct pci_dev *pPciDev = pIns->pPciDev; + + if (!pPciDev) + return VINF_SUCCESS; + + if (pIns->szPrevDriver[0]) + { + char* szCmdBuf; + char* szFileBuf; + struct file* pFile; + int iCmdLen; + const int cMaxBuf = 128; +#if RTLNX_VER_MIN(2,6,29) + const struct cred *pOldCreds; + struct cred *pNewCreds; +#endif + uint8_t uBus = (pIns->HostPciAddress) >> 8; + uint8_t uDevFn = (pIns->HostPciAddress) & 0xff; + + vbpci_printk(KERN_DEBUG, pPciDev, + "reattaching old host driver %s\n", pIns->szPrevDriver); + /* + * Now perform kernel analog of: + * + * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/pci-stub/unbind + * echo -n 0000:03:00.0 > /sys/bus/pci/drivers/nvidia/bind + */ + szCmdBuf = kmalloc(cMaxBuf, GFP_KERNEL); + szFileBuf = kmalloc(cMaxBuf, GFP_KERNEL); + + if (!szCmdBuf || !szFileBuf) + goto done; + + iCmdLen = RTStrPrintf(szCmdBuf, cMaxBuf, + "0000:%02x:%02x.%d", + uBus, uDevFn>>3, uDevFn&7); + + /* Somewhat ugly hack - override current credentials */ +#if RTLNX_VER_MIN(2,6,29) + pNewCreds = prepare_creds(); + if (!pNewCreds) + goto done; + +# if RTLNX_VER_MIN(3,5,0) + pNewCreds->fsuid = GLOBAL_ROOT_UID; +# else + pNewCreds->fsuid = 0; +# endif + pOldCreds = override_creds(pNewCreds); +#endif + RTStrPrintf(szFileBuf, cMaxBuf, + "/sys/bus/pci/drivers/%s/unbind", + PCI_STUB_MODULE); + pFile = vboxPciFileOpen(szFileBuf, O_WRONLY); + if (pFile) + { + + /* Don't write trailing \0 */ + vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen); + vboxPciFileClose(pFile); + } + else + printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf); + + RTStrPrintf(szFileBuf, cMaxBuf, + "/sys/bus/pci/drivers/%s/bind", + pIns->szPrevDriver); + pFile = vboxPciFileOpen(szFileBuf, O_WRONLY); + if (pFile) + { + + /* Don't write trailing \0 */ + vboxPciFileWrite(pFile, 0, szCmdBuf, iCmdLen); + vboxPciFileClose(pFile); + pIns->szPrevDriver[0] = '\0'; + } + else + printk(KERN_DEBUG "vboxpci: cannot open %s\n", szFileBuf); + +#if RTLNX_VER_MIN(2,6,29) + revert_creds(pOldCreds); + put_cred(pNewCreds); +#endif + + done: + kfree(szCmdBuf); + kfree(szFileBuf); + } + + return VINF_SUCCESS; +} + +DECLHIDDEN(int) vboxPciOsDevInit(PVBOXRAWPCIINS pIns, uint32_t fFlags) +{ + struct pci_dev *pPciDev = NULL; + int rc = VINF_SUCCESS; + IPRT_LINUX_SAVE_EFL_AC(); + + if (fFlags & PCIRAWDRIVERRFLAG_DETACH_HOST_DRIVER) + { + rc = vboxPciLinuxDevDetachHostDriver(pIns); + if (RT_FAILURE(rc)) + { + printk(KERN_DEBUG "Cannot detach host driver for device %x: %d\n", + pIns->HostPciAddress, rc); + } + } + + if (RT_SUCCESS(rc)) + { + pPciDev = PCI_DEV_GET_SLOT((pIns->HostPciAddress) >> 8, + (pIns->HostPciAddress) & 0xff); + + if (RT_LIKELY(pPciDev)) + { + int rcLnx = pci_enable_device(pPciDev); + + if (!rcLnx) + { + pIns->pPciDev = pPciDev; + vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__); + +#if RTLNX_VER_MIN(2,6,1) + if (pci_enable_msi(pPciDev) == 0) + pIns->fMsiUsed = true; +#endif + + /** @todo + * pci_enable_msix(pPciDev, entries, nvec) + * + * In fact, if device uses interrupts, and cannot be forced to use MSI or MSI-X + * we have to refuse using it, as we cannot work with shared PCI interrupts (unless we're lucky + * to grab unshared PCI interrupt). + */ + } + else + rc = RTErrConvertFromErrno(RT_ABS(rcLnx)); + } + else + rc = VERR_NOT_FOUND; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +DECLHIDDEN(int) vboxPciOsDevDeinit(PVBOXRAWPCIINS pIns, uint32_t fFlags) +{ + int rc = VINF_SUCCESS; + struct pci_dev *pPciDev = pIns->pPciDev; + IPRT_LINUX_SAVE_EFL_AC(); + + vbpci_printk(KERN_DEBUG, pPciDev, "%s\n", __func__); + + if (RT_LIKELY(pPciDev)) + { + int iRegion; + for (iRegion = 0; iRegion < 7; ++iRegion) + { + if (pIns->aRegionR0Mapping[iRegion]) + { + iounmap(pIns->aRegionR0Mapping[iRegion]); + pIns->aRegionR0Mapping[iRegion] = 0; + pci_release_region(pPciDev, iRegion); + } + } + + vboxPciLinuxDevUnregisterWithIommu(pIns); + +#if RTLNX_VER_MIN(2,6,1) + if (pIns->fMsiUsed) + pci_disable_msi(pPciDev); +#endif + // pci_disable_msix(pPciDev); + pci_disable_device(pPciDev); + vboxPciLinuxDevReattachHostDriver(pIns); + + PCI_DEV_PUT(pPciDev); + pIns->pPciDev = NULL; + } + else + rc = VERR_INVALID_PARAMETER; + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +DECLHIDDEN(int) vboxPciOsDevDestroy(PVBOXRAWPCIINS pIns) +{ + return VINF_SUCCESS; +} + +DECLHIDDEN(int) vboxPciOsDevGetRegionInfo(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS *pRegionStart, + uint64_t *pu64RegionSize, + bool *pfPresent, + uint32_t *pfFlags) +{ + int rc = VINF_SUCCESS; + struct pci_dev *pPciDev = pIns->pPciDev; + IPRT_LINUX_SAVE_EFL_AC(); + + if (RT_LIKELY(pPciDev)) + { + int fFlags = pci_resource_flags(pPciDev, iRegion); + + if ( ((fFlags & (IORESOURCE_MEM | IORESOURCE_IO)) == 0) + || ((fFlags & IORESOURCE_DISABLED) != 0)) + { + *pfPresent = false; + rc = VERR_INVALID_PARAMETER; + } + else + { + uint32_t fResFlags = 0; + *pfPresent = true; + + if (fFlags & IORESOURCE_MEM) + fResFlags |= PCIRAW_ADDRESS_SPACE_MEM; + + if (fFlags & IORESOURCE_IO) + fResFlags |= PCIRAW_ADDRESS_SPACE_IO; + +#ifdef IORESOURCE_MEM_64 + if (fFlags & IORESOURCE_MEM_64) + fResFlags |= PCIRAW_ADDRESS_SPACE_BAR64; +#endif + + if (fFlags & IORESOURCE_PREFETCH) + fResFlags |= PCIRAW_ADDRESS_SPACE_MEM_PREFETCH; + + *pfFlags = fResFlags; + *pRegionStart = pci_resource_start(pPciDev, iRegion); + *pu64RegionSize = pci_resource_len (pPciDev, iRegion); + + vbpci_printk(KERN_DEBUG, pPciDev, + "region %d: %s %llx+%lld\n", + iRegion, (fFlags & IORESOURCE_MEM) ? "mmio" : "pio", + *pRegionStart, *pu64RegionSize); + } + } + else + { + *pfPresent = false; + rc = VERR_INVALID_PARAMETER; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +DECLHIDDEN(int) vboxPciOsDevMapRegion(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS RegionStart, + uint64_t u64RegionSize, + uint32_t fFlags, + RTR0PTR *pRegionBase) +{ + int rc = VINF_SUCCESS; + struct pci_dev *pPciDev = pIns->pPciDev; + IPRT_LINUX_SAVE_EFL_AC(); + + if (!pPciDev || iRegion < 0 || iRegion > 0) + { + if (pPciDev) + vbpci_printk(KERN_DEBUG, pPciDev, "invalid region %d\n", iRegion); + + IPRT_LINUX_RESTORE_EFL_AC(); + return VERR_INVALID_PARAMETER; + } + + vbpci_printk(KERN_DEBUG, pPciDev, "reg=%d start=%llx size=%lld\n", + iRegion, RegionStart, u64RegionSize); + + if ( (pci_resource_flags(pPciDev, iRegion) & IORESOURCE_IO) + || RegionStart != pci_resource_start(pPciDev, iRegion) + || u64RegionSize != pci_resource_len(pPciDev, iRegion)) + { + IPRT_LINUX_RESTORE_EFL_AC(); + return VERR_INVALID_PARAMETER; + } + + /* + * XXX: Current code never calls unmap. To avoid leaking mappings + * only request and map resources once. + */ + if (!pIns->aRegionR0Mapping[iRegion]) + { + int rcLnx; + *pRegionBase = pIns->aRegionR0Mapping[iRegion]; + + rcLnx = pci_request_region(pPciDev, iRegion, "vboxpci"); + if (!rcLnx) + { +#if RTLNX_VER_MIN(2,6,25) + /* + * ioremap() defaults to no caching since the 2.6 kernels. + * ioremap_nocache() has been removed finally in 5.6-rc1. + */ + RTR0PTR R0PtrMapping = ioremap(pci_resource_start(pPciDev, iRegion), + pci_resource_len(pPciDev, iRegion)); +#else /* KERNEL_VERSION < 2.6.25 */ + /* For now no caching, try to optimize later. */ + RTR0PTR R0PtrMapping = ioremap_nocache(pci_resource_start(pPciDev, iRegion), + pci_resource_len(pPciDev, iRegion)); +#endif /* KERNEL_VERSION < 2.6.25 */ + if (R0PtrMapping != NIL_RTR0PTR) + pIns->aRegionR0Mapping[iRegion] = R0PtrMapping; + else + { +#if RTLNX_VER_MIN(2,6,25) + vbpci_printk(KERN_DEBUG, pPciDev, "ioremap() failed\n"); +#else + vbpci_printk(KERN_DEBUG, pPciDev, "ioremap_nocache() failed\n"); +#endif + pci_release_region(pPciDev, iRegion); + rc = VERR_MAP_FAILED; + } + } + else + rc = VERR_RESOURCE_BUSY; + } + + if (RT_SUCCESS(rc)) + *pRegionBase = pIns->aRegionR0Mapping[iRegion]; + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +DECLHIDDEN(int) vboxPciOsDevUnmapRegion(PVBOXRAWPCIINS pIns, + int32_t iRegion, + RTHCPHYS RegionStart, + uint64_t u64RegionSize, + RTR0PTR RegionBase) +{ + /* XXX: Current code never calls unmap. */ + return VERR_NOT_IMPLEMENTED; +} + +DECLHIDDEN(int) vboxPciOsDevPciCfgWrite(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue) +{ + struct pci_dev *pPciDev = pIns->pPciDev; + int rc = VINF_SUCCESS; + IPRT_LINUX_SAVE_EFL_AC(); + + if (RT_LIKELY(pPciDev)) + { + switch (pValue->cb) + { + case 1: + pci_write_config_byte(pPciDev, Register, pValue->u.u8); + break; + case 2: + pci_write_config_word(pPciDev, Register, pValue->u.u16); + break; + case 4: + pci_write_config_dword(pPciDev, Register, pValue->u.u32); + break; + } + } + else + rc = VERR_INVALID_PARAMETER; + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} + +DECLHIDDEN(int) vboxPciOsDevPciCfgRead(PVBOXRAWPCIINS pIns, uint32_t Register, PCIRAWMEMLOC *pValue) +{ + struct pci_dev *pPciDev = pIns->pPciDev; + int rc = VINF_SUCCESS; + + if (RT_LIKELY(pPciDev)) + { + IPRT_LINUX_SAVE_EFL_AC(); + + switch (pValue->cb) + { + case 1: + pci_read_config_byte(pPciDev, Register, &pValue->u.u8); + break; + case 2: + pci_read_config_word(pPciDev, Register, &pValue->u.u16); + break; + case 4: + pci_read_config_dword(pPciDev, Register, &pValue->u.u32); + break; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + } + else + rc = VERR_INVALID_PARAMETER; + + return rc; +} + +/** + * Interrupt service routine. + * + * @returns In 2.6 we indicate whether we've handled the IRQ or not. + * + * @param iIrq The IRQ number. + * @param pvDevId The device ID, a pointer to PVBOXRAWPCIINS. + * @param pRegs Register set. Removed in 2.6.19. + */ +#if RTLNX_VER_MIN(2,6,19) && !defined(DOXYGEN_RUNNING) +static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId) +#else +static irqreturn_t vboxPciOsIrqHandler(int iIrq, void *pvDevId, struct pt_regs *pRegs) +#endif +{ + PVBOXRAWPCIINS pIns = (PVBOXRAWPCIINS)pvDevId; + bool fTaken = true; + + if (pIns && pIns->IrqHandler.pfnIrqHandler) + fTaken = pIns->IrqHandler.pfnIrqHandler(pIns->IrqHandler.pIrqContext, iIrq); +#ifndef VBOX_WITH_SHARED_PCI_INTERRUPTS + /* If we don't allow interrupts sharing, we consider all interrupts as non-shared, thus targetted to us. */ + fTaken = true; +#endif + + return fTaken; +} + +DECLHIDDEN(int) vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq) +{ + int rc; + int32_t iIrq = pIns->pPciDev->irq; + IPRT_LINUX_SAVE_EFL_AC(); + + if (iIrq == 0) + { + vbpci_printk(KERN_NOTICE, pIns->pPciDev, "no irq assigned\n"); + IPRT_LINUX_RESTORE_EFL_AC(); + return VERR_INVALID_PARAMETER; + } + + rc = request_irq(iIrq, + vboxPciOsIrqHandler, +#ifdef VBOX_WITH_SHARED_PCI_INTERRUPTS + /* Allow interrupts sharing. */ +# if RTLNX_VER_MIN(2,6,20) + IRQF_SHARED, +# else + SA_SHIRQ, +# endif + +#else + + /* We don't allow interrupts sharing */ + /* XXX overhaul */ +# if RTLNX_VER_MIN(2,6,20) && RTLNX_VER_MAX(4,1,0) + IRQF_DISABLED, /* keep irqs disabled when calling the action handler */ +# else + 0, +# endif +#endif + DRIVER_NAME, + pIns); + if (rc) + { + vbpci_printk(KERN_DEBUG, pIns->pPciDev, + "could not request irq %d, error %d\n", iIrq, rc); + IPRT_LINUX_RESTORE_EFL_AC(); + return VERR_RESOURCE_BUSY; + } + + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "got irq %d\n", iIrq); + *piHostIrq = iIrq; + + IPRT_LINUX_RESTORE_EFL_AC(); + return VINF_SUCCESS; +} + +DECLHIDDEN(int) vboxPciOsDevUnregisterIrqHandler(PVBOXRAWPCIINS pIns, int32_t iHostIrq) +{ + IPRT_LINUX_SAVE_EFL_AC(); + + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "freeing irq %d\n", iHostIrq); + free_irq(iHostIrq, pIns); + + IPRT_LINUX_RESTORE_EFL_AC(); + return VINF_SUCCESS; +} + +DECLHIDDEN(int) vboxPciOsDevPowerStateChange(PVBOXRAWPCIINS pIns, PCIRAWPOWERSTATE aState) +{ + int rc; + + switch (aState) + { + case PCIRAW_POWER_ON: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_ON\n"); + /* Reset device, just in case. */ + vboxPciLinuxDevReset(pIns); + /* register us with IOMMU */ + rc = vboxPciLinuxDevRegisterWithIommu(pIns); + break; + case PCIRAW_POWER_RESET: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESET\n"); + rc = vboxPciLinuxDevReset(pIns); + break; + case PCIRAW_POWER_OFF: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_OFF\n"); + /* unregister us from IOMMU */ + rc = vboxPciLinuxDevUnregisterWithIommu(pIns); + break; + case PCIRAW_POWER_SUSPEND: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_SUSPEND\n"); + rc = VINF_SUCCESS; + /// @todo what do we do here? + break; + case PCIRAW_POWER_RESUME: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "PCIRAW_POWER_RESUME\n"); + rc = VINF_SUCCESS; + /// @todo what do we do here? + break; + default: + vbpci_printk(KERN_DEBUG, pIns->pPciDev, "unknown power state %u\n", aState); + /* to make compiler happy */ + rc = VERR_NOT_SUPPORTED; + break; + } + + return rc; +} + + +#ifdef VBOX_WITH_IOMMU +/** Callback for FNRAWPCICONTIGPHYSMEMINFO. */ +static DECLCALLBACK(int) vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart, + uint64_t cMemSize, PCIRAWMEMINFOACTION Action) +{ + struct iommu_domain* domain = ((PVBOXRAWPCIDRVVM)(pVmCtx->pDriverData))->pIommuDomain; + int rc = VINF_SUCCESS; + IPRT_LINUX_SAVE_EFL_AC(); + + switch (Action) + { + case PCIRAW_MEMINFO_MAP: + { + int flags, r; + + if (iommu_iova_to_phys(domain, GuestStart)) + break; + + flags = IOMMU_READ | IOMMU_WRITE; + /** @todo flags |= IOMMU_CACHE; */ + + r = iommu_map(domain, GuestStart, HostStart, get_order(cMemSize), flags); + if (r) + { + printk(KERN_ERR "vboxPciOsContigMemInfo:" + "iommu failed to map pfn=%llx\n", HostStart); + rc = VERR_GENERAL_FAILURE; + break; + } + rc = VINF_SUCCESS; + break; + } + case PCIRAW_MEMINFO_UNMAP: + { + int order; + order = iommu_unmap(domain, GuestStart, get_order(cMemSize)); + NOREF(order); + break; + } + + default: + printk(KERN_DEBUG "Unsupported action: %d\n", (int)Action); + rc = VERR_NOT_SUPPORTED; + break; + } + + IPRT_LINUX_RESTORE_EFL_AC(); + return rc; +} +#endif + +DECLHIDDEN(int) vboxPciOsInitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM, PRAWPCIPERVM pVmData) +{ + int rc = VINF_SUCCESS; + +#ifdef VBOX_WITH_IOMMU + IPRT_LINUX_SAVE_EFL_AC(); + + if (IOMMU_PRESENT()) + { + pThis->pIommuDomain = IOMMU_DOMAIN_ALLOC(); + if (!pThis->pIommuDomain) + { + vbpci_printk(KERN_DEBUG, NULL, "cannot allocate IOMMU domain\n"); + rc = VERR_NO_MEMORY; + } + else + { + pVmData->pfnContigMemInfo = vboxPciOsContigMemInfo; + + vbpci_printk(KERN_DEBUG, NULL, "created IOMMU domain %p\n", + pThis->pIommuDomain); + } + } + + IPRT_LINUX_RESTORE_EFL_AC(); +#endif + return rc; +} + +DECLHIDDEN(void) vboxPciOsDeinitVm(PVBOXRAWPCIDRVVM pThis, PVM pVM) +{ +#ifdef VBOX_WITH_IOMMU + IPRT_LINUX_SAVE_EFL_AC(); + + if (pThis->pIommuDomain) + { + vbpci_printk(KERN_DEBUG, NULL, "freeing IOMMU domain %p\n", + pThis->pIommuDomain); + iommu_domain_free(pThis->pIommuDomain); + pThis->pIommuDomain = NULL; + } + + IPRT_LINUX_RESTORE_EFL_AC(); +#endif +} diff --git a/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci b/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci new file mode 100755 index 00000000..70e15ed2 --- /dev/null +++ b/src/VBox/HostDrivers/VBoxPci/linux/files_vboxpci @@ -0,0 +1,111 @@ +#!/bin/sh +# $Id: files_vboxpci $ +## @file +# Shared file between Makefile.kmk and export_modules.sh. +# + +# +# 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>. +# +# The contents of this file may alternatively be used under the terms +# of the Common Development and Distribution License Version 1.0 +# (CDDL), a copy of it is provided in the "COPYING.CDDL" file included +# in the VirtualBox distribution, in which case the provisions of the +# CDDL are applicable instead of those of the GPL. +# +# You may elect to license modified versions of this file under the +# terms and conditions of either the GPL or the CDDL or both. +# +# SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 +# + +VBOX_VBOXPCI_SOURCES=" \ + ${PATH_ROOT}/include/iprt/alloc.h=>include/iprt/alloc.h \ + ${PATH_ROOT}/include/iprt/alloca.h=>include/iprt/alloca.h \ + ${PATH_ROOT}/include/iprt/asm.h=>include/iprt/asm.h \ + ${PATH_ROOT}/include/iprt/asm-amd64-x86.h=>include/iprt/asm-amd64-x86.h \ + ${PATH_ROOT}/include/iprt/asm-math.h=>include/iprt/asm-math.h \ + ${PATH_ROOT}/include/iprt/assert.h=>include/iprt/assert.h \ + ${PATH_ROOT}/include/iprt/assertcompile.h=>include/iprt/assertcompile.h \ + ${PATH_ROOT}/include/iprt/avl.h=>include/iprt/avl.h \ + ${PATH_ROOT}/include/iprt/cdefs.h=>include/iprt/cdefs.h \ + ${PATH_ROOT}/include/iprt/cpuset.h=>include/iprt/cpuset.h \ + ${PATH_ROOT}/include/iprt/ctype.h=>include/iprt/ctype.h \ + ${PATH_ROOT}/include/iprt/err.h=>include/iprt/err.h \ + ${PATH_ROOT}/include/iprt/errcore.h=>include/iprt/errcore.h \ + ${PATH_ROOT}/include/iprt/heap.h=>include/iprt/heap.h \ + ${PATH_ROOT}/include/iprt/initterm.h=>include/iprt/initterm.h \ + ${PATH_ROOT}/include/iprt/latin1.h=>include/iprt/latin1.h \ + ${PATH_ROOT}/include/iprt/log.h=>include/iprt/log.h \ + ${PATH_ROOT}/include/iprt/mangling.h=>include/iprt/mangling.h \ + ${PATH_ROOT}/include/iprt/mem.h=>include/iprt/mem.h \ + ${PATH_ROOT}/include/iprt/memobj.h=>include/iprt/memobj.h \ + ${PATH_ROOT}/include/iprt/mp.h=>include/iprt/mp.h \ + ${PATH_ROOT}/include/iprt/param.h=>include/iprt/param.h \ + ${PATH_ROOT}/include/iprt/power.h=>include/iprt/power.h \ + ${PATH_ROOT}/include/iprt/process.h=>include/iprt/process.h \ + ${PATH_ROOT}/include/iprt/semaphore.h=>include/iprt/semaphore.h \ + ${PATH_ROOT}/include/iprt/spinlock.h=>include/iprt/spinlock.h \ + ${PATH_ROOT}/include/iprt/stdarg.h=>include/iprt/stdarg.h \ + ${PATH_ROOT}/include/iprt/stdint.h=>include/iprt/stdint.h \ + ${PATH_ROOT}/include/iprt/string.h=>include/iprt/string.h \ + ${PATH_ROOT}/include/iprt/thread.h=>include/iprt/thread.h \ + ${PATH_ROOT}/include/iprt/time.h=>include/iprt/time.h \ + ${PATH_ROOT}/include/iprt/timer.h=>include/iprt/timer.h \ + ${PATH_ROOT}/include/iprt/types.h=>include/iprt/types.h \ + ${PATH_ROOT}/include/iprt/uint64.h=>include/iprt/uint64.h \ + ${PATH_ROOT}/include/iprt/uni.h=>include/iprt/uni.h \ + ${PATH_ROOT}/include/iprt/utf16.h=>include/iprt/utf16.h \ + ${PATH_ROOT}/include/iprt/uuid.h=>include/iprt/uuid.h \ + ${PATH_ROOT}/include/iprt/x86-helpers.h=>include/iprt/x86-helpers.h \ + ${PATH_ROOT}/include/iprt/linux/version.h=>include/iprt/linux/version.h \ + ${PATH_ROOT}/include/iprt/nocrt/limits.h=>include/iprt/nocrt/limits.h \ + ${PATH_ROOT}/include/VBox/cdefs.h=>include/VBox/cdefs.h \ + ${PATH_ROOT}/include/VBox/err.h=>include/VBox/err.h \ + ${PATH_ROOT}/include/VBox/log.h=>include/VBox/log.h \ + ${PATH_ROOT}/include/VBox/rawpci.h=>include/VBox/rawpci.h \ + ${PATH_ROOT}/include/VBox/vmm/stam.h=>include/VBox/vmm/stam.h \ + ${PATH_ROOT}/include/VBox/param.h=>include/VBox/param.h \ + ${PATH_ROOT}/include/VBox/sup.h=>include/VBox/sup.h \ + ${PATH_ROOT}/include/VBox/types.h=>include/VBox/types.h \ + ${PATH_ROOT}/include/VBox/version.h=>include/VBox/version.h \ + ${PATH_ROOT}/include/VBox/SUPDrvMangling.h=>include/VBox/SUPDrvMangling.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/linux/VBoxPci-linux.c=>linux/VBoxPci-linux.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/VBoxPci.c=>VBoxPci.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/VBoxPci/VBoxPciInternal.h=>VBoxPciInternal.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPDrvIDC.h=>SUPDrvIDC.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClient.c=>SUPR0IdcClient.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientComponent.c=>SUPR0IdcClientComponent.c \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/SUPR0IdcClientInternal.h=>SUPR0IdcClientInternal.h \ + ${PATH_ROOT}/src/VBox/HostDrivers/Support/linux/SUPR0IdcClient-linux.c=>linux/SUPR0IdcClient-linux.c \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-footer.gmk=>Makefile-footer.gmk \ + ${PATH_ROOT}/src/VBox/Installer/linux/Makefile-header.gmk=>Makefile-header.gmk \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/divdi3.c=>math/gcc/divdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/moddi3.c=>math/gcc/moddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/qdivrem.c=>math/gcc/qdivrem.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/quad.h=>math/gcc/quad.h \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivdi3.c=>math/gcc/udivdi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/udivmoddi4.c=>math/gcc/udivmoddi4.c \ + ${PATH_ROOT}/src/VBox/Runtime/common/math/gcc/umoddi3.c=>math/gcc/umoddi3.c \ + ${PATH_ROOT}/src/VBox/Runtime/r0drv/linux/the-linux-kernel.h=>r0drv/linux/the-linux-kernel.h \ + ${PATH_OUT}/version-generated.h=>version-generated.h \ + ${PATH_OUT}/revision-generated.h=>revision-generated.h \ + ${PATH_OUT}/product-generated.h=>product-generated.h \ +" + |