VBoxPci-linux.c revision a822858bf01e98b56ae4f8d0d80647eb849c4187
/* $Id$ */
/** @file
* VBoxPci - PCI Driver (Host), Linux Specific Code.
*/
/*
* 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;
* 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 *
*******************************************************************************/
#include "the-linux-kernel.h"
#include "version-generated.h"
#include "product-generated.h"
#define LOG_GROUP LOG_GROUP_DEV_PCI_RAW
#include <iprt/initterm.h>
#include "VBoxPciInternal.h"
#ifdef VBOX_WITH_IOMMU
# include <linux/intel-iommu.h>
# include <asm/amd_iommu.h>
# else
# include <linux/amd-iommu.h>
# endif
# define IOMMU_PRESENT() iommu_found()
# define IOMMU_DOMAIN_ALLOC() iommu_domain_alloc()
# else
# endif
#endif /* VBOX_WITH_IOMMU */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
static int VBoxPciLinuxInit(void);
static void VBoxPciLinuxUnload(void);
/*******************************************************************************
* Global Variables *
*******************************************************************************/
static VBOXRAWPCIGLOBALS g_VBoxPciGlobals;
MODULE_LICENSE("GPL");
#ifdef MODULE_VERSION
#endif
# define PCI_DEV_GET(v,d,p) pci_get_device(v,d,p)
# define PCI_DEV_PUT(x) pci_dev_put(x)
#else
# define PCI_DEV_GET(v,d,p) pci_find_device(v,d,p)
# define PCI_DEV_PUT(x) do { } while (0)
#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"
/**
* Initialize module.
*
* @returns appropriate status code.
*/
static int __init VBoxPciLinuxInit(void)
{
int rc;
/*
* Initialize IPRT.
*/
if (RT_FAILURE(rc))
goto error;
LogRel(("VBoxPciLinuxInit\n"));
if (RT_FAILURE(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)
{
/* find_module() is static before Linux 2.6.30 */
{
g_VBoxPciGlobals.fPciStubModuleAvail = true;
}
else
# endif
}
else
#else
#endif
#ifdef VBOX_WITH_IOMMU
if (IOMMU_PRESENT())
else
#else
#endif
return 0;
return -RTErrConvertToErrno(rc);
}
/**
* Unload the module.
*/
static void __exit VBoxPciLinuxUnload(void)
{
LogRel(("VBoxPciLinuxLinuxUnload\n"));
/*
* Undo the work done during start (in reverse order).
*/
RTR0Term();
{
}
Log(("VBoxPciLinuxUnload - done\n"));
}
{
#ifdef VBOX_WITH_IOMMU
int rc;
int status;
if (!pData)
{
return VERR_INVALID_PARAMETER;
}
if (!pData->pIommuDomain)
{
return VERR_NOT_FOUND;
}
if (status == 0)
{
pIns->fIommuUsed = true;
rc = VINF_SUCCESS;;
}
else
{
}
/* @todo: KVM checks IOMMU_CAP_CACHE_COHERENCY and sets
flag IOMMU_CACHE later used when mapping physical
addresses, which could improve performance. */
return rc;
#else
return VERR_NOT_SUPPORTED;
#endif
}
{
#ifdef VBOX_WITH_IOMMU
int rc = VINF_SUCCESS;
if (!pData)
{
return VERR_INVALID_PARAMETER;
}
if (!pData->pIommuDomain)
{
return VERR_NOT_FOUND;
}
if (pIns->fIommuUsed)
{
pIns->fIommuUsed = false;
}
return rc;
#else
return VERR_NOT_SUPPORTED;
#endif
}
{
int rc = VINF_SUCCESS;
{
{
}
#else
#endif
}
return rc;
}
{
int err = 0;
{
return NULL;
}
{
return NULL;
}
return filp;
}
{
}
static int vboxPciFileWrite(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
{
int ret;
if (ret < 0)
return ret;
}
#if 0
static int vboxPciFileRead(struct file* file, unsigned long long offset, unsigned char* data, unsigned int size)
{
int ret;
return ret;
}
#endif
{
const char* currentDriver;
int fDetach = 0;
{
return VERR_ACCESS_DENIED;
}
if (!pPciDev)
{
return VERR_NOT_FOUND;
}
/* Init previous driver data. */
if (fDetach && currentDriver)
{
/* Dangerous: if device name for some reasons contains slashes - arbitrary file could be written to. */
{
return VERR_ACCESS_DENIED;
}
/** @todo: RTStrCopy not exported. */
}
if (fDetach)
{
char* szCmdBuf;
char* szFileBuf;
int iCmdLen;
const int cMaxBuf = 128;
/*
* Now perform kernel analog of:
*
*
* We do this way, as this interface is presumingly more stable than
* in-kernel ones.
*/
goto done;
/* Somewhat ugly hack - override current credentials */
pNewCreds = prepare_creds();
if (!pNewCreds)
goto done;
# else
# endif
#endif
if (pFile)
{
"%04x %04x",
/* Don't write trailing \0 */
}
else
"0000:%02x:%02x.%d",
/* Unbind if bound to smth */
if (pIns->szPrevDriver[0])
{
pIns->szPrevDriver);
if (pFile)
{
/* Don't write trailing \0 */
}
else
}
if (pFile)
{
/* Don't write trailing \0 */
}
else
#endif
done:
}
return 0;
}
{
if (!pPciDev)
return VINF_SUCCESS;
if (pIns->szPrevDriver[0])
{
char* szCmdBuf;
char* szFileBuf;
int iCmdLen;
const int cMaxBuf = 128;
/*
* Now perform kernel analog of:
*
*/
goto done;
"0000:%02x:%02x.%d",
/* Somewhat ugly hack - override current credentials */
pNewCreds = prepare_creds();
if (!pNewCreds)
goto done;
# else
# endif
#endif
if (pFile)
{
/* Don't write trailing \0 */
}
else
pIns->szPrevDriver);
if (pFile)
{
/* Don't write trailing \0 */
}
else
#endif
done:
}
return VINF_SUCCESS;
}
{
int rc;
{
if (RT_FAILURE(rc))
{
return VERR_ACCESS_DENIED;
}
}
if (!pPciDev)
return 0;
if (pci_enable_msi(pPciDev) == 0)
{
}
#endif
// 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). */
return VINF_SUCCESS;
}
{
if (pPciDev)
{
int iRegion;
{
{
}
}
#endif
// pci_disable_msix(pPciDev);
}
return 0;
}
{
return 0;
}
bool *pfPresent,
{
int flags;
if (!pPciDev)
{
*pfPresent = false;
return 0;
}
||
((flags & IORESOURCE_DISABLED) != 0))
{
*pfPresent = false;
return 0;
}
*pfPresent = true;
fResFlags = 0;
if (flags & IORESOURCE_MEM)
if (flags & IORESOURCE_IO)
#ifdef IORESOURCE_MEM_64
if (flags & IORESOURCE_MEM_64)
#endif
if (flags & IORESOURCE_PREFETCH)
return 0;
}
{
int error;
printk(KERN_DEBUG "linux vboxPciOsDevMapRegion: reg=%d start=%llx size=%lld\n", iRegion, RegionStart, u64RegionSize);
if (!pPciDev)
return VERR_INVALID_PARAMETER;
{
return VERR_INVALID_PARAMETER;
}
return VERR_INVALID_PARAMETER;
return VERR_INVALID_PARAMETER;
return VERR_INVALID_PARAMETER;
/*
* XXX: Current code never calls unmap. To avoid leaking mappings
* only request and map resources once.
*/
{
return VINF_SUCCESS;
}
if (error)
return VERR_RESOURCE_BUSY;
/* For now no caching, try to optimize later. */
if (!result)
{
return VERR_MAP_FAILED;
}
return VINF_SUCCESS;
}
{
/* XXX: Current code never calls unmap. */
return VERR_NOT_IMPLEMENTED;
}
{
if (!pPciDev)
return VINF_SUCCESS;
{
case 1:
break;
case 2:
break;
case 4:
break;
}
return VINF_SUCCESS;
}
{
if (!pPciDev)
return VINF_SUCCESS;
{
case 1:
break;
case 2:
break;
case 4:
break;
}
return VINF_SUCCESS;
}
/**
* 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 pvRegs Register set. Removed in 2.6.19.
*/
#else
#endif
{
bool fTaken = true;
#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;
}
int vboxPciOsDevRegisterIrqHandler(PVBOXRAWPCIINS pIns, PFNRAWPCIISR pfnHandler, void* pIrqContext, int32_t *piHostIrq)
{
int rc;
if (iIrq == 0)
{
return VERR_INVALID_PARAMETER;
}
/* Allow interrupts sharing. */
# else
# endif
#else
/* We don't allow interrupts sharing */
IRQF_DISABLED, /* keep irqs disabled when calling the action handler */
# else
0,
# endif
#endif
pIns);
if (rc)
{
return VERR_RESOURCE_BUSY;
}
return VINF_SUCCESS;
}
{
return VINF_SUCCESS;
}
{
int rc;
switch (aState)
{
case PCIRAW_POWER_ON:
/* Reset device, just in case. */
/* register us with IOMMU */
break;
case PCIRAW_POWER_RESET:
break;
case PCIRAW_POWER_OFF:
/* unregister us from IOMMU */
break;
case PCIRAW_POWER_SUSPEND:
case PCIRAW_POWER_RESUME:
rc = VINF_SUCCESS;
/// @todo: what do we do here?
break;
default:
/* to make compiler happy */
break;
}
return rc;
}
#ifdef VBOX_WITH_IOMMU
/** Callback for FNRAWPCICONTIGPHYSMEMINFO. */
static int vboxPciOsContigMemInfo(PRAWPCIPERVM pVmCtx, RTHCPHYS HostStart, RTGCPHYS GuestStart, uint64_t cMemSize, PCIRAWMEMINFOACTION Action)
{
int rc = VINF_SUCCESS;
switch (Action)
{
case PCIRAW_MEMINFO_MAP:
{
int flags, r;
break;
/* @todo: flags |= IOMMU_CACHE; */
if (r)
{
"iommu failed to map pfn=%llx\n", HostStart);
break;
}
rc = VINF_SUCCESS;
break;
}
case PCIRAW_MEMINFO_UNMAP:
{
int order;
break;
}
default:
break;
}
return rc;
}
#endif
{
#ifdef DEBUG
#endif
#ifdef VBOX_WITH_IOMMU
if (IOMMU_PRESENT())
{
if (!pThis->pIommuDomain)
{
return VERR_NO_MEMORY;
}
}
#endif
return VINF_SUCCESS;
}
{
#ifdef DEBUG
#endif
#ifdef VBOX_WITH_IOMMU
if (pThis->pIommuDomain)
{
}
#endif
}