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;
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;
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,
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);
#include <xen/pci.h>
#include <xen/list.h>
#include <xen/prefetch.h>
+#include <xen/iommu.h>
#include <xen/keyhandler.h>
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;
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++ )
{
static int domain_context_unmap(u8 bus, u8 devfn)
{
struct acpi_drhd_unit *drhd;
+ u16 sec_bus, sub_bus;
int ret = 0;
u32 type;
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:
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);
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);
}
}
}
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,
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.
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);
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);
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);