ia64/xen-unstable

changeset 17977:183ca809e1d7

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@netronome.com>
Signed-off-by: Joshua LeVasseur <joshua.levasseur@netronome.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jul 04 17:52:50 2008 +0100 (2008-07-04)
parents 7f7d0e7aa01b
children a3e4840b1584
files 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
line diff
     1.1 --- a/xen/arch/x86/physdev.c	Fri Jul 04 17:52:24 2008 +0100
     1.2 +++ b/xen/arch/x86/physdev.c	Fri Jul 04 17:52:50 2008 +0100
     1.3 @@ -500,6 +500,32 @@ ret_t do_physdev_op(int cmd, XEN_GUEST_H
     1.4          break;
     1.5      }
     1.6  
     1.7 +    case PHYSDEVOP_manage_pci_add: {
     1.8 +        struct physdev_manage_pci manage_pci;
     1.9 +        ret = -EPERM;
    1.10 +        if ( !IS_PRIV(v->domain) )
    1.11 +            break;
    1.12 +        ret = -EFAULT;
    1.13 +        if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
    1.14 +            break;
    1.15 +
    1.16 +        ret = pci_add_device(manage_pci.bus, manage_pci.devfn);
    1.17 +        break;
    1.18 +    }
    1.19 +
    1.20 +    case PHYSDEVOP_manage_pci_remove: {
    1.21 +        struct physdev_manage_pci manage_pci;
    1.22 +        ret = -EPERM;
    1.23 +        if ( !IS_PRIV(v->domain) )
    1.24 +            break;
    1.25 +        ret = -EFAULT;
    1.26 +        if ( copy_from_guest(&manage_pci, arg, 1) != 0 )
    1.27 +            break;
    1.28 +
    1.29 +        ret = pci_remove_device(manage_pci.bus, manage_pci.devfn);
    1.30 +        break;
    1.31 +    }
    1.32 +
    1.33      default:
    1.34          ret = -ENOSYS;
    1.35          break;
     2.1 --- a/xen/drivers/passthrough/amd/pci_amd_iommu.c	Fri Jul 04 17:52:24 2008 +0100
     2.2 +++ b/xen/drivers/passthrough/amd/pci_amd_iommu.c	Fri Jul 04 17:52:50 2008 +0100
     2.3 @@ -628,6 +628,16 @@ static int amd_iommu_return_device(
     2.4      return reassign_device(s, t, bus, devfn);
     2.5  }
     2.6  
     2.7 +static int amd_iommu_add_device(struct pci_dev *pdev)
     2.8 +{
     2.9 +    return 0;
    2.10 +}
    2.11 +
    2.12 +static int amd_iommu_remove_device(struct pci_dev *pdev)
    2.13 +{
    2.14 +    return 0;
    2.15 +}
    2.16 +
    2.17  static int amd_iommu_group_id(u8 bus, u8 devfn)
    2.18  {
    2.19      int rt;
    2.20 @@ -640,6 +650,8 @@ static int amd_iommu_group_id(u8 bus, u8
    2.21  
    2.22  struct iommu_ops amd_iommu_ops = {
    2.23      .init = amd_iommu_domain_init,
    2.24 +    .add_device = amd_iommu_add_device,
    2.25 +    .remove_device = amd_iommu_remove_device,
    2.26      .assign_device  = amd_iommu_assign_device,
    2.27      .teardown = amd_iommu_domain_destroy,
    2.28      .map_page = amd_iommu_map_page,
     3.1 --- a/xen/drivers/passthrough/iommu.c	Fri Jul 04 17:52:24 2008 +0100
     3.2 +++ b/xen/drivers/passthrough/iommu.c	Fri Jul 04 17:52:50 2008 +0100
     3.3 @@ -55,6 +55,32 @@ int iommu_domain_init(struct domain *dom
     3.4      return hd->platform_ops->init(domain);
     3.5  }
     3.6  
     3.7 +int iommu_add_device(struct pci_dev *pdev)
     3.8 +{
     3.9 +    struct hvm_iommu *hd;
    3.10 +    if ( !pdev->domain )
    3.11 +        return -EINVAL;
    3.12 +
    3.13 +    hd = domain_hvm_iommu(pdev->domain);
    3.14 +    if ( !iommu_enabled || !hd->platform_ops )
    3.15 +        return 0;
    3.16 +
    3.17 +    return hd->platform_ops->add_device(pdev);
    3.18 +}
    3.19 +
    3.20 +int iommu_remove_device(struct pci_dev *pdev)
    3.21 +{
    3.22 +    struct hvm_iommu *hd;
    3.23 +    if ( !pdev->domain )
    3.24 +        return -EINVAL;
    3.25 +
    3.26 +    hd = domain_hvm_iommu(pdev->domain);
    3.27 +    if ( !iommu_enabled || !hd->platform_ops )
    3.28 +        return 0;
    3.29 +
    3.30 +    return hd->platform_ops->remove_device(pdev);
    3.31 +}
    3.32 +
    3.33  int assign_device(struct domain *d, u8 bus, u8 devfn)
    3.34  {
    3.35      struct hvm_iommu *hd = domain_hvm_iommu(d);
     4.1 --- a/xen/drivers/passthrough/pci.c	Fri Jul 04 17:52:24 2008 +0100
     4.2 +++ b/xen/drivers/passthrough/pci.c	Fri Jul 04 17:52:50 2008 +0100
     4.3 @@ -19,6 +19,7 @@
     4.4  #include <xen/pci.h>
     4.5  #include <xen/list.h>
     4.6  #include <xen/prefetch.h>
     4.7 +#include <xen/iommu.h>
     4.8  #include <xen/keyhandler.h>
     4.9  
    4.10  
    4.11 @@ -93,6 +94,57 @@ struct pci_dev *pci_lock_domain_pdev(str
    4.12      return NULL;
    4.13  }
    4.14  
    4.15 +int pci_add_device(u8 bus, u8 devfn)
    4.16 +{
    4.17 +    struct pci_dev *pdev;
    4.18 +    int ret = -ENOMEM;
    4.19 +
    4.20 +    write_lock(&pcidevs_lock);
    4.21 +    pdev = alloc_pdev(bus, devfn);
    4.22 +    if ( !pdev )
    4.23 +	goto out;
    4.24 +
    4.25 +    ret = 0;
    4.26 +    spin_lock(&pdev->lock);
    4.27 +    if ( !pdev->domain )
    4.28 +    {
    4.29 +	pdev->domain = dom0;
    4.30 +	list_add(&pdev->domain_list, &dom0->arch.pdev_list);
    4.31 +	ret = iommu_add_device(pdev);
    4.32 +    }
    4.33 +    spin_unlock(&pdev->lock);
    4.34 +    printk(XENLOG_DEBUG "PCI add device %02x:%02x.%x\n", bus,
    4.35 +	   PCI_SLOT(devfn), PCI_FUNC(devfn));
    4.36 +
    4.37 +out:
    4.38 +    write_unlock(&pcidevs_lock);
    4.39 +    return ret;
    4.40 +}
    4.41 +
    4.42 +int pci_remove_device(u8 bus, u8 devfn)
    4.43 +{
    4.44 +    struct pci_dev *pdev;
    4.45 +    int ret = -ENODEV;;
    4.46 +
    4.47 +    write_lock(&pcidevs_lock);
    4.48 +    list_for_each_entry ( pdev, &alldevs_list, alldevs_list )
    4.49 +        if ( pdev->bus == bus && pdev->devfn == devfn )
    4.50 +	{
    4.51 +	    spin_lock(&pdev->lock);
    4.52 +	    ret = iommu_remove_device(pdev);
    4.53 +	    if ( pdev->domain )
    4.54 +		list_del(&pdev->domain_list);
    4.55 +	    pci_cleanup_msi(pdev);
    4.56 +	    free_pdev(pdev);
    4.57 +	    printk(XENLOG_DEBUG "PCI remove device %02x:%02x.%x\n", bus,
    4.58 +		   PCI_SLOT(devfn), PCI_FUNC(devfn));
    4.59 +	    break;
    4.60 +	}
    4.61 +
    4.62 +    write_unlock(&pcidevs_lock);
    4.63 +    return ret;
    4.64 +}
    4.65 +
    4.66  static void dump_pci_devices(unsigned char ch)
    4.67  {
    4.68      struct pci_dev *pdev;
     5.1 --- a/xen/drivers/passthrough/vtd/iommu.c	Fri Jul 04 17:52:24 2008 +0100
     5.2 +++ b/xen/drivers/passthrough/vtd/iommu.c	Fri Jul 04 17:52:50 2008 +0100
     5.3 @@ -1223,13 +1223,15 @@ static int domain_context_mapping(struct
     5.4      switch ( type )
     5.5      {
     5.6      case DEV_TYPE_PCIe_BRIDGE:
     5.7 -        break;
     5.8 -
     5.9      case DEV_TYPE_PCI_BRIDGE:
    5.10          sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    5.11                                   PCI_SECONDARY_BUS);
    5.12          sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    5.13                                   PCI_SUBORDINATE_BUS);
    5.14 +        /*dmar_scope_add_buses(&drhd->scope, sec_bus, sub_bus);*/
    5.15 +
    5.16 +        if ( type == DEV_TYPE_PCIe_BRIDGE )
    5.17 +            break;
    5.18  
    5.19          for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
    5.20          {
    5.21 @@ -1308,6 +1310,7 @@ static int domain_context_unmap_one(stru
    5.22  static int domain_context_unmap(u8 bus, u8 devfn)
    5.23  {
    5.24      struct acpi_drhd_unit *drhd;
    5.25 +    u16 sec_bus, sub_bus;
    5.26      int ret = 0;
    5.27      u32 type;
    5.28  
    5.29 @@ -1319,10 +1322,14 @@ static int domain_context_unmap(u8 bus, 
    5.30      switch ( type )
    5.31      {
    5.32      case DEV_TYPE_PCIe_BRIDGE:
    5.33 -        break;
    5.34 -
    5.35      case DEV_TYPE_PCI_BRIDGE:
    5.36 -        ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
    5.37 +        sec_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    5.38 +                                 PCI_SECONDARY_BUS);
    5.39 +        sub_bus = pci_conf_read8(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
    5.40 +                                 PCI_SUBORDINATE_BUS);
    5.41 +        /*dmar_scope_remove_buses(&drhd->scope, sec_bus, sub_bus);*/
    5.42 +        if ( DEV_TYPE_PCI_BRIDGE )
    5.43 +            ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
    5.44          break;
    5.45  
    5.46      case DEV_TYPE_PCIe_ENDPOINT:
    5.47 @@ -1574,11 +1581,23 @@ static int iommu_prepare_rmrr_dev(struct
    5.48      return ret;
    5.49  }
    5.50  
    5.51 +static int intel_iommu_add_device(struct pci_dev *pdev)
    5.52 +{
    5.53 +    if ( !pdev->domain )
    5.54 +        return -EINVAL;
    5.55 +    return domain_context_mapping(pdev->domain, pdev->bus, pdev->devfn);
    5.56 +}
    5.57 +
    5.58 +static int intel_iommu_remove_device(struct pci_dev *pdev)
    5.59 +{
    5.60 +    return domain_context_unmap(pdev->bus, pdev->devfn);
    5.61 +}
    5.62 +
    5.63  static void setup_dom0_devices(struct domain *d)
    5.64  {
    5.65      struct hvm_iommu *hd;
    5.66      struct pci_dev *pdev;
    5.67 -    int bus, dev, func, ret;
    5.68 +    int bus, dev, func;
    5.69      u32 l;
    5.70  
    5.71      hd = domain_hvm_iommu(d);
    5.72 @@ -1599,11 +1618,7 @@ static void setup_dom0_devices(struct do
    5.73                  pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
    5.74                  pdev->domain = d;
    5.75                  list_add(&pdev->domain_list, &d->arch.pdev_list);
    5.76 -
    5.77 -                ret = domain_context_mapping(d, pdev->bus, pdev->devfn);
    5.78 -                if ( ret != 0 )
    5.79 -                    gdprintk(XENLOG_ERR VTDPREFIX,
    5.80 -                             "domain_context_mapping failed\n");
    5.81 +                domain_context_mapping(d, pdev->bus, pdev->devfn);
    5.82              }
    5.83          }
    5.84      }
    5.85 @@ -1866,6 +1881,8 @@ int iommu_resume(void)
    5.86  
    5.87  struct iommu_ops intel_iommu_ops = {
    5.88      .init = intel_iommu_domain_init,
    5.89 +    .add_device = intel_iommu_add_device,
    5.90 +    .remove_device = intel_iommu_remove_device,
    5.91      .assign_device  = intel_iommu_assign_device,
    5.92      .teardown = iommu_domain_teardown,
    5.93      .map_page = intel_iommu_map_page,
     6.1 --- a/xen/include/public/physdev.h	Fri Jul 04 17:52:24 2008 +0100
     6.2 +++ b/xen/include/public/physdev.h	Fri Jul 04 17:52:50 2008 +0100
     6.3 @@ -154,6 +154,17 @@ struct physdev_unmap_pirq {
     6.4  typedef struct physdev_unmap_pirq physdev_unmap_pirq_t;
     6.5  DEFINE_XEN_GUEST_HANDLE(physdev_unmap_pirq_t);
     6.6  
     6.7 +#define PHYSDEVOP_manage_pci_add         15
     6.8 +#define PHYSDEVOP_manage_pci_remove      16
     6.9 +struct physdev_manage_pci {
    6.10 +    /* IN */
    6.11 +    uint8_t bus;
    6.12 +    uint8_t devfn;
    6.13 +}; 
    6.14 +
    6.15 +typedef struct physdev_manage_pci physdev_manage_pci_t;
    6.16 +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t);
    6.17 +
    6.18  /*
    6.19   * Argument to physdev_op_compat() hypercall. Superceded by new physdev_op()
    6.20   * hypercall since 0x00030202.
     7.1 --- a/xen/include/xen/iommu.h	Fri Jul 04 17:52:24 2008 +0100
     7.2 +++ b/xen/include/xen/iommu.h	Fri Jul 04 17:52:50 2008 +0100
     7.3 @@ -56,8 +56,8 @@ struct iommu {
     7.4      struct intel_iommu *intel;
     7.5  };
     7.6  
     7.7 -int iommu_add_device(u8 bus, u8 devfn);
     7.8 -void iommu_remove_device(u8 bus, u8 devfn);
     7.9 +int iommu_add_device(struct pci_dev *pdev);
    7.10 +int iommu_remove_device(struct pci_dev *pdev);
    7.11  int iommu_domain_init(struct domain *d);
    7.12  void iommu_domain_destroy(struct domain *d);
    7.13  int device_assigned(u8 bus, u8 devfn);
    7.14 @@ -94,6 +94,8 @@ int domain_set_irq_dpci(struct domain *d
    7.15  
    7.16  struct iommu_ops {
    7.17      int (*init)(struct domain *d);
    7.18 +    int (*add_device)(struct pci_dev *pdev);
    7.19 +    int (*remove_device)(struct pci_dev *pdev);
    7.20      int (*assign_device)(struct domain *d, u8 bus, u8 devfn);
    7.21      void (*teardown)(struct domain *d);
    7.22      int (*map_page)(struct domain *d, unsigned long gfn, unsigned long mfn);
     8.1 --- a/xen/include/xen/pci.h	Fri Jul 04 17:52:24 2008 +0100
     8.2 +++ b/xen/include/xen/pci.h	Fri Jul 04 17:52:50 2008 +0100
     8.3 @@ -56,6 +56,8 @@ void free_pdev(struct pci_dev *pdev);
     8.4  struct pci_dev *pci_lock_pdev(int bus, int devfn);
     8.5  struct pci_dev *pci_lock_domain_pdev(struct domain *d, int bus, int devfn);
     8.6  
     8.7 +int pci_add_device(u8 bus, u8 devfn);
     8.8 +int pci_remove_device(u8 bus, u8 devfn);
     8.9  
    8.10  uint8_t pci_conf_read8(
    8.11      unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg);