Add hypercall for adding and removing PCI devices
authorKeir Fraser <keir.fraser@citrix.com>
Fri, 4 Jul 2008 16:52:50 +0000 (17:52 +0100)
committerKeir Fraser <keir.fraser@citrix.com>
Fri, 4 Jul 2008 16:52:50 +0000 (17:52 +0100)
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@netronome.com>
Signed-off-by: Joshua LeVasseur <joshua.levasseur@netronome.com>
xen/arch/x86/physdev.c
xen/drivers/passthrough/amd/pci_amd_iommu.c
xen/drivers/passthrough/iommu.c
xen/drivers/passthrough/pci.c
xen/drivers/passthrough/vtd/iommu.c
xen/include/public/physdev.h
xen/include/xen/iommu.h
xen/include/xen/pci.h

index 75d8fa4dcc6becf7d87bf6d24424ea9601d6190b..d1e4f39e757c88066d72a9dfe1e490d31d6c1606 100644 (file)
@@ -500,6 +500,32 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_HANDLE(void) arg)
         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;
index 90ae9c003c0b898fbb216225c0f33029dad4aa6e..0cc63ae6f63803788a325aa656906dad597fc1b1 100644 (file)
@@ -628,6 +628,16 @@ static int amd_iommu_return_device(
     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 @@ static int amd_iommu_group_id(u8 bus, u8 devfn)
 
 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,
index 2f09a386f03453a358e31760539af0aa25101b4f..26719e4b58f64d2b9c802613414d8e6e8b220836 100644 (file)
@@ -55,6 +55,32 @@ int iommu_domain_init(struct domain *domain)
     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)
 {
     struct hvm_iommu *hd = domain_hvm_iommu(d);
index 4c3b875e94e999b8bb220b4c3ad8b37c6ee73293..b692c8e34f8de22915642de7ca8f86da49ef1f57 100644 (file)
@@ -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 @@ struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn)
     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;
index 0def8dc02ec7317c4eaa8b4f23215024b8791b3f..aab25809b790b19187932608899f80318bb0ecd9 100644 (file)
@@ -1223,13 +1223,15 @@ static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
     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_one(struct iommu *iommu, u8 bus, u8 devfn)
 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 @@ static int domain_context_unmap(u8 bus, u8 devfn)
     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 @@ static int iommu_prepare_rmrr_dev(struct domain *d,
     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 @@ static void setup_dom0_devices(struct domain *d)
                 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 @@ int iommu_resume(void)
 
 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,
index 789d965f6c0f9f8c017b7e8cfc0ec3ed7e187bfa..77aedf546c2d03d3e6024b32328017cdf75de0d5 100644 (file)
@@ -154,6 +154,17 @@ struct physdev_unmap_pirq {
 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.
index 833f5c67d701ed2ae30ec43b54454a9f59715a31..390fb9e4d1c85c991e6003ccc4751da5eabf9316 100644 (file)
@@ -56,8 +56,8 @@ struct iommu {
     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 @@ int domain_set_irq_dpci(struct domain *domain, struct hvm_irq_dpci *dpci);
 
 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);
index bf7c066fe8121f51d118ba5908a498a9f0b3daf8..f60c5855d7a62d4e1317eb78e2fafd21d8c08d04 100644 (file)
@@ -56,6 +56,8 @@ void free_pdev(struct pci_dev *pdev);
 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);