xen-pci-addremove.patch
Add hypercall for adding and removing PCI devices
The add hypercall will add a new PCI device and register it. The
remove hypercall will remove the pci_dev strucure for the device. The
IOMMU hardware (if present) will be notifed as well.
Signed-off-by: Espen Skoglund <espen.skoglund@xxxxxxxxxxxxx>
Signed-off-by: Joshua LeVasseur <joshua.levasseur@xxxxxxxxxxxxx>
--
arch/x86/physdev.c | 26 ++++++++++++++++
drivers/passthrough/amd/pci_amd_iommu.c | 12 +++++++
drivers/passthrough/iommu.c | 26 ++++++++++++++++
drivers/passthrough/pci.c | 52 ++++++++++++++++++++++++++++++++
drivers/passthrough/vtd/iommu.c | 39 +++++++++++++++++-------
include/public/physdev.h | 11 ++++++
include/xen/iommu.h | 6 ++-
include/xen/pci.h | 2 +
8 files changed, 161 insertions(+), 13 deletions(-)
--
diff -r 795e85588ded xen/arch/x86/physdev.c
--- a/xen/arch/x86/physdev.c Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/arch/x86/physdev.c Fri Jul 04 16:31:03 2008 +0100
@@ -500,6 +500,32 @@
break;
}
+ case PHYSDEVOP_manage_pci_add: {
+ struct physdev_manage_pci manage_pci;
+ ret = -EPERM;
+ if ( !IS_PRIV(v->domain) )
+ break;
+ ret = -EFAULT;
+ if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
+ break;
+
+ ret = pci_add_device(manage_pci.bus, manage_pci.devfn);
+ break;
+ }
+
+ case PHYSDEVOP_manage_pci_remove: {
+ struct physdev_manage_pci manage_pci;
+ ret = -EPERM;
+ if ( !IS_PRIV(v->domain) )
+ break;
+ ret = -EFAULT;
+ if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
+ break;
+
+ ret = pci_remove_device(manage_pci.bus, manage_pci.devfn);
+ break;
+ }
+
default:
ret = -ENOSYS;
break;
diff -r 795e85588ded xen/drivers/passthrough/amd/pci_amd_iommu.c
--- a/xen/drivers/passthrough/amd/pci_amd_iommu.c Fri Jul 04 16:27:50
2008 +0100
+++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c Fri Jul 04 16:31:03
2008 +0100
@@ -628,6 +628,16 @@
return reassign_device(s, t, bus, devfn);
}
+static int amd_iommu_add_device(struct pci_dev *pdev)
+{
+ return 0;
+}
+
+static int amd_iommu_remove_device(struct pci_dev *pdev)
+{
+ return 0;
+}
+
static int amd_iommu_group_id(u8 bus, u8 devfn)
{
int rt;
@@ -640,6 +650,8 @@
struct iommu_ops amd_iommu_ops = {
.init = amd_iommu_domain_init,
+ .add_device = amd_iommu_add_device,
+ .remove_device = amd_iommu_remove_device,
.assign_device = amd_iommu_assign_device,
.teardown = amd_iommu_domain_destroy,
.map_page = amd_iommu_map_page,
diff -r 795e85588ded xen/drivers/passthrough/iommu.c
--- a/xen/drivers/passthrough/iommu.c Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/drivers/passthrough/iommu.c Fri Jul 04 16:31:03 2008 +0100
@@ -53,6 +53,32 @@
}
return hd->platform_ops->init(domain);
+}
+
+int iommu_add_device(struct pci_dev *pdev)
+{
+ struct hvm_iommu *hd;
+ if ( !pdev->domain )
+ return -EINVAL;
+
+ hd = domain_hvm_iommu(pdev->domain);
+ if ( !iommu_enabled || !hd->platform_ops )
+ return 0;
+
+ return hd->platform_ops->add_device(pdev);
+}
+
+int iommu_remove_device(struct pci_dev *pdev)
+{
+ struct hvm_iommu *hd;
+ if ( !pdev->domain )
+ return -EINVAL;
+
+ hd = domain_hvm_iommu(pdev->domain);
+ if ( !iommu_enabled || !hd->platform_ops )
+ return 0;
+
+ return hd->platform_ops->remove_device(pdev);
}
int assign_device(struct domain *d, u8 bus, u8 devfn)
diff -r 795e85588ded xen/drivers/passthrough/pci.c
--- a/xen/drivers/passthrough/pci.c Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/drivers/passthrough/pci.c Fri Jul 04 16:31:03 2008 +0100
@@ -19,6 +19,7 @@
#include <xen/pci.h>
#include <xen/list.h>
#include <xen/prefetch.h>
+#include <xen/iommu.h>
#include <xen/keyhandler.h>
@@ -93,6 +94,57 @@
return NULL;
}
+int pci_add_device(u8 bus, u8 devfn)
+{
+ struct pci_dev *pdev;
+ int ret = -ENOMEM;
+
+ write_lock(&pcidevs_lock);
+ pdev = alloc_pdev(bus, devfn);
+ if ( !pdev )
+ goto out;
+
+ ret = 0;
+ spin_lock(&pdev->lock);
+ if ( !pdev->domain )
+ {
+ pdev->domain = dom0;
+ list_add(&pdev->domain_list, &dom0->arch.pdev_list);
+ ret = iommu_add_device(pdev);
+ }
+ spin_unlock(&pdev->lock);
+ printk(XENLOG_DEBUG "PCI add device %02x:%02x.%x\n", bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+out:
+ write_unlock(&pcidevs_lock);
+ return ret;
+}
+
+int pci_remove_device(u8 bus, u8 devfn)
+{
+ struct pci_dev *pdev;
+ int ret = -ENODEV;;
+
+ write_lock(&pcidevs_lock);
+ list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
+ if ( pdev->bus == bus && pdev->devfn == devfn )
+ {
+ spin_lock(&pdev->lock);
+ ret = iommu_remove_device(pdev);
+ if ( pdev->domain )
+ list_del(&pdev->domain_list);
+ pci_cleanup_msi(pdev);
+ free_pdev(pdev);
+ printk(XENLOG_DEBUG "PCI remove device %02x:%02x.%x\n", bus,
+ PCI_SLOT(devfn), PCI_FUNC(devfn));
+ break;
+ }
+
+ write_unlock(&pcidevs_lock);
+ return ret;
+}
+
static void dump_pci_devices(unsigned char ch)
{
struct pci_dev *pdev;
diff -r 795e85588ded xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c Fri Jul 04 16:31:03 2008 +0100
@@ -1223,13 +1223,15 @@
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
- break;
-
case DEV_TYPE_PCI_BRIDGE:
sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
PCI_SECONDARY_BUS);
sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
PCI_SUBORDINATE_BUS);
+ /*dmar_scope_add_buses(&drhd->scope, sec_bus, sub_bus);*/
+
+ if ( type == DEV_TYPE_PCIe_BRIDGE )
+ break;
for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
{
@@ -1308,6 +1310,7 @@
static int domain_context_unmap(u8 bus, u8 devfn)
{
struct acpi_drhd_unit *drhd;
+ u16 sec_bus, sub_bus;
int ret = 0;
u32 type;
@@ -1319,10 +1322,14 @@
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
- break;
-
case DEV_TYPE_PCI_BRIDGE:
- ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
+ sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ PCI_SECONDARY_BUS);
+ sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+ PCI_SUBORDINATE_BUS);
+ /*dmar_scope_remove_buses(&drhd->scope, sec_bus, sub_bus);*/
+ if ( DEV_TYPE_PCI_BRIDGE )
+ ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -1574,11 +1581,23 @@
return ret;
}
+static int intel_iommu_add_device(struct pci_dev *pdev)
+{
+ if ( !pdev->domain )
+ return -EINVAL;
+ return domain_context_mapping(pdev->domain, pdev->bus, pdev->devfn);
+}
+
+static int intel_iommu_remove_device(struct pci_dev *pdev)
+{
+ return domain_context_unmap(pdev->bus, pdev->devfn);
+}
+
static void setup_dom0_devices(struct domain *d)
{
struct hvm_iommu *hd;
struct pci_dev *pdev;
- int bus, dev, func, ret;
+ int bus, dev, func;
u32 l;
hd = domain_hvm_iommu(d);
@@ -1599,11 +1618,7 @@
pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
pdev->domain = d;
list_add(&pdev->domain_list, &d->arch.pdev_list);
-
- ret = domain_context_mapping(d, pdev->bus, pdev->devfn);
- if ( ret != 0 )
- gdprintk(XENLOG_ERR VTDPREFIX,
- "domain_context_mapping failed\n");
+ domain_context_mapping(d, pdev->bus, pdev->devfn);
}
}
}
@@ -1866,6 +1881,8 @@
struct iommu_ops intel_iommu_ops = {
.init = intel_iommu_domain_init,
+ .add_device = intel_iommu_add_device,
+ .remove_device = intel_iommu_remove_device,
.assign_device = intel_iommu_assign_device,
.teardown = iommu_domain_teardown,
.map_page = intel_iommu_map_page,
diff -r 795e85588ded xen/include/public/physdev.h
--- a/xen/include/public/physdev.h Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/include/public/physdev.h Fri Jul 04 16:31:03 2008 +0100
@@ -154,6 +154,17 @@
typedef struct physdev_unmap_pirq physdev_unmap_pirq_t;
DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t);
+#define PHYSDEVOP_manage_pci_add 15
+#define PHYSDEVOP_manage_pci_remove 16
+struct physdev_manage_pci {
+ /* IN */
+ uint8_t bus;
+ uint8_t devfn;
+};
+
+typedef struct physdev_manage_pci physdev_manage_pci_t;
+DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t);
+
/*
* Argument to physdev_op_compat() hypercall. Superceded by new physdev_op()
* hypercall since 0x00030202.
diff -r 795e85588ded xen/include/xen/iommu.h
--- a/xen/include/xen/iommu.h Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/include/xen/iommu.h Fri Jul 04 16:31:03 2008 +0100
@@ -56,8 +56,8 @@
struct intel_iommu *intel;
};
-int iommu_add_device(u8 bus, u8 devfn);
-void iommu_remove_device(u8 bus, u8 devfn);
+int iommu_add_device(struct pci_dev *pdev);
+int iommu_remove_device(struct pci_dev *pdev);
int iommu_domain_init(struct domain *d);
void iommu_domain_destroy(struct domain *d);
int device_assigned(u8 bus, u8 devfn);
@@ -94,6 +94,8 @@
struct iommu_ops {
int (*init)(struct domain *d);
+ int (*add_device)(struct pci_dev *pdev);
+ int (*remove_device)(struct pci_dev *pdev);
int (*assign_device)(struct domain *d, u8 bus, u8 devfn);
void (*teardown)(struct domain *d);
int (*map_page)(struct domain *d, unsigned long gfn, unsigned long mfn);
diff -r 795e85588ded xen/include/xen/pci.h
--- a/xen/include/xen/pci.h Fri Jul 04 16:27:50 2008 +0100
+++ b/xen/include/xen/pci.h Fri Jul 04 16:31:03 2008 +0100
@@ -56,6 +56,8 @@
struct pci_dev *pci_lock_pdev(int bus, int devfn);
struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn);
+int pci_add_device(u8 bus, u8 devfn);
+int pci_remove_device(u8 bus, u8 devfn);
uint8_t pci_conf_read8(
unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg);
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|