# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1245397520 -3600
# Node ID 2f1fa2215e60a325aabc14e22187761c13987f20
# Parent e2625f2359408ed47bc6e65278c79b3cddafebd3
VT-d: pci code cleanup
This patch moves the pci code from iommu.c to pci.c. Instead of setup
pci hierarchy in array bus2bridge in iommu_context_mapping, use
scan_pci_devices once to add all existed PCI devices in system to
alldevs_list and setup pci hierarchy in array bus2bridge. In addition,
implement find_upstream_bridge to find the upstream PCIe-to-PCI/PCIX
bridge or PCI legacy bridge for a PCI device, therefore it's cleanly
to handle context map/unmap for PCI device, even for source-id
setting.
Signed-off-by: Weidong Han <weidong.han@xxxxxxxxx>
---
xen/drivers/passthrough/pci.c | 153 +++++++++++++++++++++++++++
xen/drivers/passthrough/vtd/iommu.c | 203 +++++++++---------------------------
xen/include/xen/pci.h | 11 +
3 files changed, 215 insertions(+), 152 deletions(-)
diff -r e2625f235940 -r 2f1fa2215e60 xen/drivers/passthrough/pci.c
--- a/xen/drivers/passthrough/pci.c Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/drivers/passthrough/pci.c Fri Jun 19 08:45:20 2009 +0100
@@ -30,6 +30,16 @@ LIST_HEAD(alldevs_list);
LIST_HEAD(alldevs_list);
spinlock_t pcidevs_lock = SPIN_LOCK_UNLOCKED;
+#define MAX_BUSES 256
+static struct {
+ u8 map;
+ u8 bus;
+ u8 devfn;
+} bus2bridge[MAX_BUSES];
+
+/* bus2bridge_lock protects bus2bridge array */
+static DEFINE_SPINLOCK(bus2bridge_lock);
+
struct pci_dev *alloc_pdev(u8 bus, u8 devfn)
{
struct pci_dev *pdev;
@@ -240,6 +250,149 @@ void pci_release_devices(struct domain *
spin_unlock(&pcidevs_lock);
}
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+
+int pdev_type(u8 bus, u8 devfn)
+{
+ u16 class_device;
+ u16 status, creg;
+ int pos;
+ u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
+
+ class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
+ if ( class_device == PCI_CLASS_BRIDGE_PCI )
+ {
+ pos = pci_find_next_cap(bus, devfn,
+ PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
+ if ( !pos )
+ return DEV_TYPE_LEGACY_PCI_BRIDGE;
+ creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
+ return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
+ DEV_TYPE_PCIe2PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
+ }
+
+ status = pci_conf_read16(bus, d, f, PCI_STATUS);
+ if ( !(status & PCI_STATUS_CAP_LIST) )
+ return DEV_TYPE_PCI;
+
+ if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
+ return DEV_TYPE_PCIe_ENDPOINT;
+
+ return DEV_TYPE_PCI;
+}
+
+/*
+ * find the upstream PCIe-to-PCI/PCIX bridge or PCI legacy bridge
+ * return 0: the device is integrated PCI device or PCIe
+ * return 1: find PCIe-to-PCI/PCIX bridge or PCI legacy bridge
+ * return -1: fail
+ */
+int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus)
+{
+ int ret = 0;
+ int cnt = 0;
+
+ if ( *bus == 0 )
+ return 0;
+
+ if ( !bus2bridge[*bus].map )
+ return 0;
+
+ ret = 1;
+ spin_lock(&bus2bridge_lock);
+ while ( bus2bridge[*bus].map )
+ {
+ *secbus = *bus;
+ *devfn = bus2bridge[*bus].devfn;
+ *bus = bus2bridge[*bus].bus;
+ if ( cnt++ >= MAX_BUSES )
+ {
+ ret = -1;
+ goto out;
+ }
+ }
+
+out:
+ spin_unlock(&bus2bridge_lock);
+ return ret;
+}
+
+/*
+ * scan pci devices to add all existed PCI devices to alldevs_list,
+ * and setup pci hierarchy in array bus2bridge. This function is only
+ * called in VT-d hardware setup
+ */
+int __init scan_pci_devices(void)
+{
+ struct pci_dev *pdev;
+ int bus, dev, func;
+ u8 sec_bus, sub_bus;
+ int type;
+ u32 l;
+
+ spin_lock(&pcidevs_lock);
+ for ( bus = 0; bus < 256; bus++ )
+ {
+ for ( dev = 0; dev < 32; dev++ )
+ {
+ for ( func = 0; func < 8; func++ )
+ {
+ l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
+ /* some broken boards return 0 or ~0 if a slot is empty: */
+ if ( (l == 0xffffffff) || (l == 0x00000000) ||
+ (l == 0x0000ffff) || (l == 0xffff0000) )
+ continue;
+
+ pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
+ if ( !pdev )
+ {
+ printk("%s: alloc_pdev failed.\n", __func__);
+ spin_unlock(&pcidevs_lock);
+ return -ENOMEM;
+ }
+
+ /* build bus2bridge */
+ type = pdev_type(bus, PCI_DEVFN(dev, func));
+ switch ( type )
+ {
+ case DEV_TYPE_PCIe_BRIDGE:
+ break;
+
+ case DEV_TYPE_PCIe2PCI_BRIDGE:
+ case DEV_TYPE_LEGACY_PCI_BRIDGE:
+ sec_bus = pci_conf_read8(bus, dev, func,
+ PCI_SECONDARY_BUS);
+ sub_bus = pci_conf_read8(bus, dev, func,
+ PCI_SUBORDINATE_BUS);
+
+ spin_lock(&bus2bridge_lock);
+ for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
+ {
+ bus2bridge[sec_bus].map = 1;
+ bus2bridge[sec_bus].bus = bus;
+ bus2bridge[sec_bus].devfn = PCI_DEVFN(dev, func);
+ }
+ spin_unlock(&bus2bridge_lock);
+ break;
+
+ case DEV_TYPE_PCIe_ENDPOINT:
+ case DEV_TYPE_PCI:
+ break;
+
+ default:
+ printk("%s: unknown type: bdf = %x:%x.%x\n",
+ __func__, bus, dev, func);
+ spin_unlock(&pcidevs_lock);
+ return -EINVAL;
+ }
+ }
+ }
+ }
+
+ spin_unlock(&pcidevs_lock);
+ return 0;
+}
+
#ifdef SUPPORT_MSI_REMAPPING
static void dump_pci_devices(unsigned char ch)
{
diff -r e2625f235940 -r 2f1fa2215e60 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c Fri Jun 19 08:45:20 2009 +0100
@@ -1066,92 +1066,12 @@ static int domain_context_mapping_one(
return 0;
}
-#define PCI_BASE_CLASS_BRIDGE 0x06
-#define PCI_CLASS_BRIDGE_PCI 0x0604
-
-enum {
- DEV_TYPE_PCIe_ENDPOINT,
- DEV_TYPE_PCIe_BRIDGE, // PCIe root port, switch
- DEV_TYPE_PCI_BRIDGE, // PCIe-to-PCI/PCIx bridge, PCI-to-PCI bridge
- DEV_TYPE_PCI,
-};
-
-int pdev_type(u8 bus, u8 devfn)
-{
- u16 class_device;
- u16 status, creg;
- int pos;
- u8 d = PCI_SLOT(devfn), f = PCI_FUNC(devfn);
-
- class_device = pci_conf_read16(bus, d, f, PCI_CLASS_DEVICE);
- if ( class_device == PCI_CLASS_BRIDGE_PCI )
- {
- pos = pci_find_next_cap(bus, devfn,
- PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP);
- if ( !pos )
- return DEV_TYPE_PCI_BRIDGE;
- creg = pci_conf_read16(bus, d, f, pos + PCI_EXP_FLAGS);
- return ((creg & PCI_EXP_FLAGS_TYPE) >> 4) == PCI_EXP_TYPE_PCI_BRIDGE ?
- DEV_TYPE_PCI_BRIDGE : DEV_TYPE_PCIe_BRIDGE;
- }
-
- status = pci_conf_read16(bus, d, f, PCI_STATUS);
- if ( !(status & PCI_STATUS_CAP_LIST) )
- return DEV_TYPE_PCI;
-
- if ( pci_find_next_cap(bus, devfn, PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
- return DEV_TYPE_PCIe_ENDPOINT;
-
- return DEV_TYPE_PCI;
-}
-
-#define MAX_BUSES 256
-static DEFINE_SPINLOCK(bus2bridge_lock);
-static struct { u8 map, bus, devfn; } bus2bridge[MAX_BUSES];
-
-static int _find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
-{
- int cnt = 0;
- *secbus = *bus;
-
- ASSERT(spin_is_locked(&bus2bridge_lock));
- if ( !bus2bridge[*bus].map )
- return 0;
-
- while ( bus2bridge[*bus].map )
- {
- *secbus = *bus;
- *devfn = bus2bridge[*bus].devfn;
- *bus = bus2bridge[*bus].bus;
- if ( cnt++ >= MAX_BUSES )
- return 0;
- }
-
- return 1;
-}
-
-static int find_pcie_endpoint(u8 *bus, u8 *devfn, u8 *secbus)
-{
- int ret = 0;
-
- if ( *bus == 0 )
- /* assume integrated PCI devices in RC have valid requester-id */
- return 1;
-
- spin_lock(&bus2bridge_lock);
- ret = _find_pcie_endpoint(bus, devfn, secbus);
- spin_unlock(&bus2bridge_lock);
-
- return ret;
-}
-
static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
{
struct acpi_drhd_unit *drhd;
int ret = 0;
- u16 sec_bus, sub_bus;
u32 type;
- u8 secbus, secdevfn;
+ u8 secbus;
struct pci_dev *pdev = pci_get_pdev(bus, devfn);
if ( pdev == NULL )
@@ -1179,22 +1099,8 @@ static int domain_context_mapping(struct
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);
-
- spin_lock(&bus2bridge_lock);
- for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
- {
- bus2bridge[sec_bus].map = 1;
- bus2bridge[sec_bus].bus = bus;
- bus2bridge[sec_bus].devfn = devfn;
- }
- spin_unlock(&bus2bridge_lock);
+ case DEV_TYPE_PCIe2PCI_BRIDGE:
+ case DEV_TYPE_LEGACY_PCI_BRIDGE:
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -1211,31 +1117,29 @@ static int domain_context_mapping(struct
ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
if ( ret )
- break;
-
- secbus = bus;
- secdevfn = devfn;
- /* dependent devices mapping */
- while ( bus2bridge[bus].map )
- {
- secbus = bus;
- secdevfn = devfn;
- devfn = bus2bridge[bus].devfn;
- bus = bus2bridge[bus].bus;
+ break;
+
+ if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
+ break;
+
+ /* PCIe to PCI/PCIx bridge */
+ if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
+ {
ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
if ( ret )
return ret;
- }
-
- if ( (secbus != bus) && (secdevfn != 0) )
+
/*
- * The source-id for transactions on non-PCIe buses seem
- * to originate from devfn=0 on the secondary bus behind
- * the bridge. Map that id as well. The id to use in
- * these scanarios is not particularly well documented
- * anywhere.
+ * Devices behind PCIe-to-PCI/PCIx bridge may generate
+ * different requester-id. It may originate from devfn=0
+ * on the secondary bus behind the bridge. Map that id
+ * as well.
*/
ret = domain_context_mapping_one(domain, drhd->iommu, secbus, 0);
+ }
+ else /* Legacy PCI bridge */
+ ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
+
break;
default:
@@ -1296,7 +1200,7 @@ static int domain_context_unmap(struct d
struct acpi_drhd_unit *drhd;
int ret = 0;
u32 type;
- u8 secbus, secdevfn;
+ u8 secbus;
struct pci_dev *pdev = pci_get_pdev(bus, devfn);
BUG_ON(!pdev);
@@ -1309,7 +1213,8 @@ static int domain_context_unmap(struct d
switch ( type )
{
case DEV_TYPE_PCIe_BRIDGE:
- case DEV_TYPE_PCI_BRIDGE:
+ case DEV_TYPE_PCIe2PCI_BRIDGE:
+ case DEV_TYPE_LEGACY_PCI_BRIDGE:
break;
case DEV_TYPE_PCIe_ENDPOINT:
@@ -1327,22 +1232,21 @@ static int domain_context_unmap(struct d
if ( ret )
break;
- secbus = bus;
- secdevfn = devfn;
- /* dependent devices unmapping */
- while ( bus2bridge[bus].map )
- {
- secbus = bus;
- secdevfn = devfn;
- devfn = bus2bridge[bus].devfn;
- bus = bus2bridge[bus].bus;
+ if ( find_upstream_bridge(&bus, &devfn, &secbus) < 1 )
+ break;
+
+ /* PCIe to PCI/PCIx bridge */
+ if ( pdev_type(bus, devfn) == DEV_TYPE_PCIe2PCI_BRIDGE )
+ {
ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
if ( ret )
return ret;
- }
-
- if ( (secbus != bus) && (secdevfn != 0) )
+
ret = domain_context_unmap_one(domain, drhd->iommu, secbus, 0);
+ }
+ else /* Legacy PCI bridge */
+ ret = domain_context_unmap_one(domain, drhd->iommu, bus, devfn);
+
break;
default:
@@ -1584,31 +1488,24 @@ static void setup_dom0_devices(struct do
{
struct hvm_iommu *hd;
struct pci_dev *pdev;
- int bus, dev, func;
- u32 l;
+ int bus, devfn;
hd = domain_hvm_iommu(d);
spin_lock(&pcidevs_lock);
for ( bus = 0; bus < 256; bus++ )
{
- for ( dev = 0; dev < 32; dev++ )
- {
- for ( func = 0; func < 8; func++ )
- {
- l = pci_conf_read32(bus, dev, func, PCI_VENDOR_ID);
- /* some broken boards return 0 or ~0 if a slot is empty: */
- if ( (l == 0xffffffff) || (l == 0x00000000) ||
- (l == 0x0000ffff) || (l == 0xffff0000) )
- continue;
-
- pdev = alloc_pdev(bus, PCI_DEVFN(dev, func));
- pdev->domain = d;
- list_add(&pdev->domain_list, &d->arch.pdev_list);
- domain_context_mapping(d, pdev->bus, pdev->devfn);
- if ( ats_device(0, pdev->bus, pdev->devfn) )
- enable_ats_device(0, pdev->bus, pdev->devfn);
- }
+ for ( devfn = 0; devfn < 256; devfn++ )
+ {
+ pdev = pci_get_pdev(bus, devfn);
+ if ( !pdev )
+ continue;
+
+ pdev->domain = d;
+ list_add(&pdev->domain_list, &d->arch.pdev_list);
+ domain_context_mapping(d, pdev->bus, pdev->devfn);
+ if ( ats_device(0, pdev->bus, pdev->devfn) )
+ enable_ats_device(0, pdev->bus, pdev->devfn);
}
}
spin_unlock(&pcidevs_lock);
@@ -1809,6 +1706,8 @@ int intel_vtd_setup(void)
memset(domid_bitmap, 0, domid_bitmap_size / 8);
set_bit(0, domid_bitmap);
+ scan_pci_devices();
+
if ( init_vtd_hw() )
goto error;
@@ -1902,10 +1801,10 @@ static int intel_iommu_group_id(u8 bus,
static int intel_iommu_group_id(u8 bus, u8 devfn)
{
u8 secbus;
- if ( !bus2bridge[bus].map || find_pcie_endpoint(&bus, &devfn, &secbus) )
+ if ( find_upstream_bridge(&bus, &devfn, &secbus) < 0 )
+ return -1;
+ else
return PCI_BDF2(bus, devfn);
- else
- return -1;
}
static u32 iommu_state[MAX_IOMMUS][MAX_IOMMU_REGS];
diff -r e2625f235940 -r 2f1fa2215e60 xen/include/xen/pci.h
--- a/xen/include/xen/pci.h Fri Jun 19 08:44:33 2009 +0100
+++ b/xen/include/xen/pci.h Fri Jun 19 08:45:20 2009 +0100
@@ -66,6 +66,17 @@ struct pci_dev {
extern spinlock_t pcidevs_lock;
+enum {
+ DEV_TYPE_PCIe_ENDPOINT,
+ DEV_TYPE_PCIe_BRIDGE, // PCIe root port, switch
+ DEV_TYPE_PCIe2PCI_BRIDGE, // PCIe-to-PCI/PCIx bridge
+ DEV_TYPE_LEGACY_PCI_BRIDGE, // Legacy PCI bridge
+ DEV_TYPE_PCI,
+};
+
+int scan_pci_devices(void);
+int pdev_type(u8 bus, u8 devfn);
+int find_upstream_bridge(u8 *bus, u8 *devfn, u8 *secbus);
struct pci_dev *alloc_pdev(u8 bus, u8 devfn);
void free_pdev(struct pci_dev *pdev);
struct pci_dev *pci_lock_pdev(int bus, int devfn);
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|