# HG changeset patch # User Yu Zhao # Date 1237441166 14400 # Node ID 1e79dfa07877236ce5abcc61b916c8b5a67cb2c3 # Parent 2039e82710512f2b4b45f83cf4cdb3b037bf0d7c Xen: use proper device ID to search VT-d unit for ARI and SR-IOV device PCIe Alternative Routing-ID Interpretation (ARI) ECN defines the Extended Function -- a function whose function number is greater than 7 within an ARI Device. Intel VT-d spec 1.2 section 8.3.2 specifies that the Extended Function is under the scope of the same remapping unit as the traditional function. The hypervisor needs to know if a function is Extended Function so it can find proper DMAR for it. And section 8.3.3 specifies that the SR-IOV Virtual Function is under the scope of the same remapping unit as the Physical Function. The hypervisor also needs to know if a function is the Virtual Function and which Physical Function it's associated with for same reason. Signed-off-by: Yu Zhao diff -r 2039e8271051 -r 1e79dfa07877 xen/arch/ia64/xen/hypercall.c --- a/xen/arch/ia64/xen/hypercall.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/arch/ia64/xen/hypercall.c Thu Mar 19 01:39:26 2009 -0400 @@ -674,6 +674,28 @@ break; } + case PHYSDEVOP_manage_pci_add_ext: { + struct physdev_manage_pci_ext manage_pci_ext; + struct pci_dev_info pdev_info; + + ret = -EPERM; + if ( !IS_PRIV(current->domain) ) + break; + + ret = -EFAULT; + if ( copy_from_guest(&manage_pci_ext, arg, 1) != 0 ) + break; + + pdev_info.is_extfn = manage_pci_ext.is_extfn; + pdev_info.is_virtfn = manage_pci_ext.is_virtfn; + pdev_info.physfn.bus = manage_pci_ext.physfn.bus; + pdev_info.physfn.devfn = manage_pci_ext.physfn.devfn; + ret = pci_add_device_ext(manage_pci_ext.bus, + manage_pci_ext.devfn, + &pdev_info); + break; + } + default: ret = -ENOSYS; printk("not implemented do_physdev_op: %d\n", cmd); diff -r 2039e8271051 -r 1e79dfa07877 xen/arch/x86/physdev.c --- a/xen/arch/x86/physdev.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/arch/x86/physdev.c Thu Mar 19 01:39:26 2009 -0400 @@ -421,6 +421,28 @@ break; } + case PHYSDEVOP_manage_pci_add_ext: { + struct physdev_manage_pci_ext manage_pci_ext; + struct pci_dev_info pdev_info; + + ret = -EPERM; + if ( !IS_PRIV(current->domain) ) + break; + + ret = -EFAULT; + if ( copy_from_guest(&manage_pci_ext, arg, 1) != 0 ) + break; + + pdev_info.is_extfn = manage_pci_ext.is_extfn; + pdev_info.is_virtfn = manage_pci_ext.is_virtfn; + pdev_info.physfn.bus = manage_pci_ext.physfn.bus; + pdev_info.physfn.devfn = manage_pci_ext.physfn.devfn; + ret = pci_add_device_ext(manage_pci_ext.bus, + manage_pci_ext.devfn, + &pdev_info); + break; + } + case PHYSDEVOP_restore_msi: { struct physdev_restore_msi restore_msi; struct pci_dev *pdev; diff -r 2039e8271051 -r 1e79dfa07877 xen/drivers/passthrough/pci.c --- a/xen/drivers/passthrough/pci.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/drivers/passthrough/pci.c Thu Mar 19 01:39:26 2009 -0400 @@ -143,6 +143,47 @@ return ret; } +int pci_add_device_ext(u8 bus, u8 devfn, struct pci_dev_info *info) +{ + int ret; + char *pdev_type; + struct pci_dev *pdev; + + if (info->is_extfn) + pdev_type = "Extended Function"; + else if (info->is_virtfn) + pdev_type = "Virtual Function"; + else + return -EINVAL;; + + + ret = -ENOMEM; + spin_lock(&pcidevs_lock); + pdev = alloc_pdev(bus, devfn); + if ( !pdev ) + goto out; + + pdev->info = *info; + + ret = 0; + if ( !pdev->domain ) + { + pdev->domain = dom0; + ret = iommu_add_device(pdev); + if ( ret ) + goto out; + + list_add(&pdev->domain_list, &dom0->arch.pdev_list); + } + +out: + spin_unlock(&pcidevs_lock); + printk(XENLOG_DEBUG "PCI add %s %02x:%02x.%x\n", pdev_type, + bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + + return ret; +} + static void pci_clean_dpci_irqs(struct domain *d) { struct hvm_irq_dpci *hvm_irq_dpci = NULL; diff -r 2039e8271051 -r 1e79dfa07877 xen/drivers/passthrough/vtd/dmar.c --- a/xen/drivers/passthrough/vtd/dmar.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/drivers/passthrough/vtd/dmar.c Thu Mar 19 01:39:26 2009 -0400 @@ -152,11 +152,23 @@ return 0; } -struct acpi_drhd_unit * acpi_find_matched_drhd_unit(u8 bus, u8 devfn) +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *pdev) { + u8 bus, devfn; struct acpi_drhd_unit *drhd; struct acpi_drhd_unit *found = NULL, *include_all = NULL; int i; + + if (pdev->info.is_extfn) { + bus = pdev->bus; + devfn = 0; + } else if (pdev->info.is_virtfn) { + bus = pdev->info.physfn.bus; + devfn = PCI_SLOT(pdev->info.physfn.devfn) ? 0 : pdev->info.physfn.devfn; + } else { + bus = pdev->bus; + devfn = pdev->devfn; + } list_for_each_entry ( drhd, &acpi_drhd_units, list ) { diff -r 2039e8271051 -r 1e79dfa07877 xen/drivers/passthrough/vtd/dmar.h --- a/xen/drivers/passthrough/vtd/dmar.h Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/drivers/passthrough/vtd/dmar.h Thu Mar 19 01:39:26 2009 -0400 @@ -79,7 +79,7 @@ for (idx = 0; (bdf = rmrr->scope.devices[idx]) && \ idx < rmrr->scope.devices_cnt; idx++) -struct acpi_drhd_unit * acpi_find_matched_drhd_unit(u8 bus, u8 devfn); +struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *pdev); struct acpi_atsr_unit * acpi_find_matched_atsr_unit(u8 bus, u8 devfn); void dmar_scope_add_buses(struct dmar_scope *scope, u16 sec, u16 sub); void dmar_scope_remove_buses(struct dmar_scope *scope, u16 sec, u16 sub); diff -r 2039e8271051 -r 1e79dfa07877 xen/drivers/passthrough/vtd/intremap.c --- a/xen/drivers/passthrough/vtd/intremap.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/drivers/passthrough/vtd/intremap.c Thu Mar 19 01:39:26 2009 -0400 @@ -450,7 +450,7 @@ struct iommu *iommu = NULL; struct ir_ctrl *ir_ctrl; - drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn); + drhd = acpi_find_matched_drhd_unit(pdev); iommu = drhd->iommu; ir_ctrl = iommu_ir_ctrl(iommu); @@ -468,7 +468,7 @@ struct iommu *iommu = NULL; struct ir_ctrl *ir_ctrl; - drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn); + drhd = acpi_find_matched_drhd_unit(pdev); iommu = drhd->iommu; ir_ctrl = iommu_ir_ctrl(iommu); diff -r 2039e8271051 -r 1e79dfa07877 xen/drivers/passthrough/vtd/iommu.c --- a/xen/drivers/passthrough/vtd/iommu.c Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/drivers/passthrough/vtd/iommu.c Thu Mar 19 01:39:26 2009 -0400 @@ -1193,8 +1193,11 @@ u16 sec_bus, sub_bus; u32 type; u8 secbus, secdevfn; + struct pci_dev *pdev = pci_get_pdev(bus, devfn); - drhd = acpi_find_matched_drhd_unit(bus, devfn); + BUG_ON(!pdev); + + drhd = acpi_find_matched_drhd_unit(pdev); if ( !drhd ) return -ENODEV; @@ -1319,8 +1322,11 @@ int ret = 0; u32 type; u8 secbus, secdevfn; + struct pci_dev *pdev = pci_get_pdev(bus, devfn); - drhd = acpi_find_matched_drhd_unit(bus, devfn); + BUG_ON(!pdev); + + drhd = acpi_find_matched_drhd_unit(pdev); if ( !drhd ) return -ENODEV; @@ -1392,7 +1398,7 @@ if (!pdev) return -ENODEV; - drhd = acpi_find_matched_drhd_unit(bus, devfn); + drhd = acpi_find_matched_drhd_unit(pdev); pdev_iommu = drhd->iommu; domain_context_unmap(source, bus, devfn); @@ -1405,7 +1411,7 @@ for_each_pdev ( source, pdev ) { - drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn); + drhd = acpi_find_matched_drhd_unit(pdev); if ( drhd->iommu == pdev_iommu ) { found = 1; diff -r 2039e8271051 -r 1e79dfa07877 xen/include/public/physdev.h --- a/xen/include/public/physdev.h Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/include/public/physdev.h Thu Mar 19 01:39:26 2009 -0400 @@ -183,6 +183,22 @@ typedef struct physdev_manage_pci physdev_manage_pci_t; DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_t); +#define PHYSDEVOP_manage_pci_add_ext 20 +struct physdev_manage_pci_ext { + /* IN */ + uint8_t bus; + uint8_t devfn; + unsigned is_extfn; + unsigned is_virtfn; + struct { + uint8_t bus; + uint8_t devfn; + } physfn; +}; + +typedef struct physdev_manage_pci_ext physdev_manage_pci_ext_t; +DEFINE_XEN_GUEST_HANDLE(physdev_manage_pci_ext_t); + #define PHYSDEVOP_restore_msi 19 struct physdev_restore_msi { /* IN */ diff -r 2039e8271051 -r 1e79dfa07877 xen/include/xen/pci.h --- a/xen/include/xen/pci.h Wed Mar 18 17:30:13 2009 +0000 +++ b/xen/include/xen/pci.h Thu Mar 19 01:39:26 2009 -0400 @@ -31,6 +31,15 @@ #define MAX_MSIX_TABLE_ENTRIES 2048 #define MAX_MSIX_TABLE_PAGES 8 +struct pci_dev_info { + unsigned is_extfn; + unsigned is_virtfn; + struct { + u8 bus; + u8 devfn; + } physfn; +}; + struct pci_dev { struct list_head alldevs_list; struct list_head domain_list; @@ -43,6 +52,7 @@ struct domain *domain; const u8 bus; const u8 devfn; + struct pci_dev_info info; }; #define for_each_pdev(domain, pdev) \ @@ -64,6 +74,7 @@ void pci_release_devices(struct domain *d); int pci_add_device(u8 bus, u8 devfn); int pci_remove_device(u8 bus, u8 devfn); +int pci_add_device_ext(u8 bus, u8 devfn, struct pci_dev_info *info); struct pci_dev *pci_get_pdev(int bus, int devfn); struct pci_dev *pci_get_pdev_by_domain(struct domain *d, int bus, int devfn);