[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] [xen-unstable] Restructure VT-d device scope and PCI bridge handling



# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1215190302 -3600
# Node ID bd7f2a120f9446337d1b6a0417eae157e0abe291
# Parent  1e9df5cb885f8848f147c21758040978019aefd0
Restructure VT-d device scope and PCI bridge handling

Create a bitmap for each device scope indicating which buses are
covered by the scope.  Upon mapping PCI-PCI bridges we now detect
whether we have a bridge to a non-PCIe bus.  If so, all devices mapped
on that bus are squashed to the requester-id of the bridge.  Bridges
to PCIe busses are ignored.  The requester-id squashing also
determines the iommu device group id for the device.

Signed-off-by: Espen Skoglund <espen.skoglund@xxxxxxxxxxxxx>
---
 xen/drivers/passthrough/vtd/dmar.c     |  358 ++++++++++++---------------------
 xen/drivers/passthrough/vtd/dmar.h     |   40 +--
 xen/drivers/passthrough/vtd/intremap.c |    4 
 xen/drivers/passthrough/vtd/iommu.c    |  329 ++++++++++++++----------------
 xen/drivers/passthrough/vtd/utils.c    |   10 
 xen/include/xen/pci.h                  |   19 +
 6 files changed, 328 insertions(+), 432 deletions(-)

diff -r 1e9df5cb885f -r bd7f2a120f94 xen/drivers/passthrough/vtd/dmar.c
--- a/xen/drivers/passthrough/vtd/dmar.c        Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.c        Fri Jul 04 17:51:42 2008 +0100
@@ -45,6 +45,26 @@ LIST_HEAD(acpi_atsr_units);
 
 u8 dmar_host_address_width;
 
+void dmar_scope_add_buses(struct dmar_scope *scope, u16 sec_bus, u16 sub_bus)
+{
+    sub_bus &= 0xff;
+    if (sec_bus > sub_bus)
+        return;
+
+    while ( sec_bus <= sub_bus )
+        set_bit(sec_bus++, scope->buses);
+}
+
+void dmar_scope_remove_buses(struct dmar_scope *scope, u16 sec_bus, u16 
sub_bus)
+{
+    sub_bus &= 0xff;
+    if (sec_bus > sub_bus)
+        return;
+
+    while ( sec_bus <= sub_bus )
+        clear_bit(sec_bus++, scope->buses);
+}
+
 static int __init acpi_register_drhd_unit(struct acpi_drhd_unit *drhd)
 {
     /*
@@ -94,21 +114,6 @@ struct iommu * ioapic_to_iommu(unsigned 
     return NULL;
 }
 
-static int acpi_pci_device_match(struct pci_dev *devices, int cnt,
-                                 struct pci_dev *dev)
-{
-    int i;
-
-    for ( i = 0; i < cnt; i++ )
-    {
-        if ( (dev->bus == devices->bus) &&
-             (dev->devfn == devices->devfn) )
-            return 1;
-        devices++;
-    }
-    return 0;
-}
-
 static int __init acpi_register_atsr_unit(struct acpi_atsr_unit *atsr)
 {
     /*
@@ -122,39 +127,36 @@ static int __init acpi_register_atsr_uni
     return 0;
 }
 
-struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev)
+struct acpi_drhd_unit * acpi_find_matched_drhd_unit(u8 bus, u8 devfn)
 {
     struct acpi_drhd_unit *drhd;
-    struct acpi_drhd_unit *include_all_drhd;
-
-    include_all_drhd = NULL;
+    struct acpi_drhd_unit *found = NULL, *include_all = NULL;
+    int i;
+
     list_for_each_entry ( drhd, &acpi_drhd_units, list )
     {
+        for (i = 0; i < drhd->scope.devices_cnt; i++)
+            if ( drhd->scope.devices[i] == PCI_BDF2(bus, devfn) )
+                return drhd;
+
+        if ( test_bit(bus, drhd->scope.buses) )
+            found = drhd;
+
         if ( drhd->include_all )
-        {
-            include_all_drhd = drhd;
-            continue;
-        }
-
-        if ( acpi_pci_device_match(drhd->devices,
-                                   drhd->devices_cnt, dev) )
-            return drhd;
-    }
-
-    if ( include_all_drhd )
-        return include_all_drhd;
-
-    return NULL;
-}
-
+            include_all = drhd;
+    }
+
+    return found ? found : include_all;
+}
+
+/*
+ * Count number of devices in device scope.  Do not include PCI sub
+ * hierarchies.
+ */
 static int scope_device_count(void *start, void *end)
 {
     struct acpi_dev_scope *scope;
-    u16 bus, sub_bus, sec_bus;
-    struct acpi_pci_path *path;
-    int depth, count = 0;
-    u8 dev, func;
-    u32 l;
+    int count = 0;
 
     while ( start < end )
     {
@@ -162,73 +164,14 @@ static int scope_device_count(void *star
         if ( (scope->length < MIN_SCOPE_LEN) ||
              (scope->dev_type >= ACPI_DEV_ENTRY_COUNT) )
         {
-            dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope\n");
+            dprintk(XENLOG_WARNING VTDPREFIX, "Invalid device scope.\n");
             return -EINVAL;
         }
 
-        path = (struct acpi_pci_path *)(scope + 1);
-        bus = scope->start_bus;
-        depth = (scope->length - sizeof(struct acpi_dev_scope))
-                   / sizeof(struct acpi_pci_path);
-        while ( --depth > 0 )
-        {
-            bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SECONDARY_BUS);
-            path++;
-        }
-
-        if ( scope->dev_type == ACPI_DEV_ENDPOINT )
-        {
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found endpoint: bdf = %x:%x:%x\n",
-                    bus, path->dev, path->fn);
+        if ( scope->dev_type == ACPI_DEV_ENDPOINT ||
+             scope->dev_type == ACPI_DEV_IOAPIC ||
+             scope->dev_type == ACPI_DEV_MSI_HPET )
             count++;
-        }
-        else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE )
-        {
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found bridge: bdf = %x:%x:%x\n",
-                    bus, path->dev, path->fn);
-            sec_bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SECONDARY_BUS);
-            sub_bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
-
-            while ( sec_bus <= sub_bus )
-            {
-                for ( dev = 0; dev < 32; dev++ )
-                {
-                    for ( func = 0; func < 8; func++ )
-                    {
-                        l = pci_conf_read32(
-                            sec_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 )
-                            break;
-                        count++;
-                    }
-                }
-                sec_bus++;
-            }
-        }
-        else if ( scope->dev_type == ACPI_DEV_IOAPIC )
-        {
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found IOAPIC: bdf = %x:%x:%x\n",
-                    bus, path->dev, path->fn);
-            count++;
-        }
-        else
-        {
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found MSI HPET: bdf = %x:%x:%x\n",
-                    bus, path->dev, path->fn);
-            count++;
-        }
 
         start += scope->length;
     }
@@ -236,132 +179,96 @@ static int scope_device_count(void *star
     return count;
 }
 
-static int __init acpi_parse_dev_scope(
-    void *start, void *end, void *acpi_entry, int type)
-{
-    struct acpi_dev_scope *scope;
+
+static int __init acpi_parse_dev_scope(void *start, void *end,
+                                       void *acpi_entry, int type)
+{
+    struct dmar_scope *scope = acpi_entry;
+    struct acpi_ioapic_unit *acpi_ioapic_unit;
+    struct acpi_dev_scope *acpi_scope;
     u16 bus, sub_bus, sec_bus;
     struct acpi_pci_path *path;
-    struct acpi_ioapic_unit *acpi_ioapic_unit = NULL;
-    int depth;
-    struct pci_dev *pdev;
-    u8 dev, func;
-    u32 l;
-
-    int *cnt = NULL;
-    struct pci_dev **devices = NULL;
-    struct acpi_drhd_unit *dmaru = (struct acpi_drhd_unit *) acpi_entry;
-    struct acpi_rmrr_unit *rmrru = (struct acpi_rmrr_unit *) acpi_entry;
-    struct acpi_atsr_unit *atsru = (struct acpi_atsr_unit *) acpi_entry;
-
-    switch (type) {
-        case DMAR_TYPE:
-            cnt = &(dmaru->devices_cnt);
-            devices = &(dmaru->devices);
-            break;
-        case RMRR_TYPE:
-            cnt = &(rmrru->devices_cnt);
-            devices = &(rmrru->devices);
-            break;
-        case ATSR_TYPE:
-            cnt = &(atsru->devices_cnt);
-            devices = &(atsru->devices);
-            break;
-        default:
-            dprintk(XENLOG_ERR VTDPREFIX, "invalid vt-d acpi entry type\n");
-    }
-
-    *cnt = scope_device_count(start, end);
-    if ( *cnt == 0 )
-    {
-        dprintk(XENLOG_INFO VTDPREFIX, "acpi_parse_dev_scope: no device\n");
-        return 0;
-    }
-
-    *devices = xmalloc_array(struct pci_dev,  *cnt);
-    if ( !*devices )
-        return -ENOMEM;
-    memset(*devices, 0, sizeof(struct pci_dev) * (*cnt));
-
-    pdev = *devices;
+    int depth, cnt, didx = 0;
+
+    if ( (cnt = scope_device_count(start, end)) < 0 )
+        return cnt;
+
+    scope->devices_cnt = cnt;
+    if ( cnt > 0 )
+    {
+        scope->devices = xmalloc_array(u16, cnt);
+        if ( !scope->devices )
+            return -ENOMEM;
+        memset(scope->devices, 0, sizeof(u16) * cnt);
+    }
+
     while ( start < end )
     {
-        scope = start;
-        path = (struct acpi_pci_path *)(scope + 1);
-        depth = (scope->length - sizeof(struct acpi_dev_scope))
+        acpi_scope = start;
+        path = (struct acpi_pci_path *)(acpi_scope + 1);
+        depth = (acpi_scope->length - sizeof(struct acpi_dev_scope))
                    / sizeof(struct acpi_pci_path);
-        bus = scope->start_bus;
+        bus = acpi_scope->start_bus;
 
         while ( --depth > 0 )
         {
-            bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SECONDARY_BUS);
+            bus = pci_conf_read8(bus, path->dev, path->fn, PCI_SECONDARY_BUS);
             path++;
         }
-
-        if ( scope->dev_type == ACPI_DEV_ENDPOINT )
-        {
+        
+        switch ( acpi_scope->dev_type )
+        {
+        case ACPI_DEV_P2PBRIDGE:
+        {
+            sec_bus = pci_conf_read8(
+               bus, path->dev, path->fn, PCI_SECONDARY_BUS);
+            sub_bus = pci_conf_read8(
+               bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
             dprintk(XENLOG_INFO VTDPREFIX,
-                    "found endpoint: bdf = %x:%x:%x\n",
+                    "found bridge: bdf = %x:%x.%x  sec = %x  sub = %x\n",
+                    bus, path->dev, path->fn, sec_bus, sub_bus);
+
+            dmar_scope_add_buses(scope, sec_bus, sub_bus);
+            break;
+        }
+
+       case ACPI_DEV_MSI_HPET:
+            dprintk(XENLOG_INFO VTDPREFIX, "found MSI HPET: bdf = %x:%x.%x\n",
                     bus, path->dev, path->fn);
-            pdev->bus = bus;
-            pdev->devfn = PCI_DEVFN(path->dev, path->fn);
-            pdev++;
-        }
-        else if ( scope->dev_type == ACPI_DEV_P2PBRIDGE )
-        {
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found bridge: bus = %x dev = %x func = %x\n",
+            scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
+            break;
+
+        case ACPI_DEV_ENDPOINT:
+            dprintk(XENLOG_INFO VTDPREFIX, "found endpoint: bdf = %x:%x.%x\n",
                     bus, path->dev, path->fn);
-            sec_bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SECONDARY_BUS);
-            sub_bus = pci_conf_read8(
-                bus, path->dev, path->fn, PCI_SUBORDINATE_BUS);
-
-            while ( sec_bus <= sub_bus )
+            scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
+            break;
+
+        case ACPI_DEV_IOAPIC:
+        {
+            dprintk(XENLOG_INFO VTDPREFIX, "found IOAPIC: bdf = %x:%x.%x\n",
+                    bus, path->dev, path->fn);
+
+            if ( type == DMAR_TYPE )
             {
-                for ( dev = 0; dev < 32; dev++ )
-                {
-                    for ( func = 0; func < 8; func++ )
-                    {
-                        l = pci_conf_read32(
-                            sec_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 )
-                            break;
-
-                        pdev->bus = sec_bus;
-                        pdev->devfn = PCI_DEVFN(dev, func);
-                        pdev++;
-                    }
-                }
-                sec_bus++;
+                struct acpi_drhd_unit *drhd = acpi_entry;
+                acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
+                if ( !acpi_ioapic_unit )
+                    return -ENOMEM;
+                acpi_ioapic_unit->apic_id = acpi_scope->enum_id;
+                acpi_ioapic_unit->ioapic.bdf.bus = bus;
+                acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
+                acpi_ioapic_unit->ioapic.bdf.func = path->fn;
+                list_add(&acpi_ioapic_unit->list, &drhd->ioapic_list);
             }
-        }
-        else if ( scope->dev_type == ACPI_DEV_IOAPIC )
-        {
-            acpi_ioapic_unit = xmalloc(struct acpi_ioapic_unit);
-            if ( !acpi_ioapic_unit )
-                return -ENOMEM;
-            acpi_ioapic_unit->apic_id = scope->enum_id;
-            acpi_ioapic_unit->ioapic.bdf.bus = bus;
-            acpi_ioapic_unit->ioapic.bdf.dev = path->dev;
-            acpi_ioapic_unit->ioapic.bdf.func = path->fn;
-            list_add(&acpi_ioapic_unit->list, &dmaru->ioapic_list);
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found IOAPIC: bus = %x dev = %x func = %x\n",
-                    bus, path->dev, path->fn);
-        }
-        else
-            dprintk(XENLOG_INFO VTDPREFIX,
-                    "found MSI HPET: bus = %x dev = %x func = %x\n",
-                    bus, path->dev, path->fn);
-        start += scope->length;
-    }
+
+            scope->devices[didx++] = PCI_BDF(bus, path->dev, path->fn);
+            break;
+        }
+        }
+
+        start += acpi_scope->length;
+   }
 
     return 0;
 }
@@ -370,10 +277,17 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
 acpi_parse_one_drhd(struct acpi_dmar_entry_header *header)
 {
     struct acpi_table_drhd * drhd = (struct acpi_table_drhd *)header;
+    void *dev_scope_start, *dev_scope_end;
     struct acpi_drhd_unit *dmaru;
     int ret = 0;
-    static int include_all;
-    void *dev_scope_start, *dev_scope_end;
+    static int include_all = 0;
+
+    if ( include_all )
+    {
+        dprintk(XENLOG_WARNING VTDPREFIX,
+                "DMAR unit with INCLUDE_ALL is not not the last unit.\n");
+        return -EINVAL;
+    }
 
     dmaru = xmalloc(struct acpi_drhd_unit);
     if ( !dmaru )
@@ -387,20 +301,13 @@ acpi_parse_one_drhd(struct acpi_dmar_ent
             dmaru->address);
 
     dev_scope_start = (void *)(drhd + 1);
-    dev_scope_end   = ((void *)drhd) + header->length;
+    dev_scope_end = ((void *)drhd) + header->length;
     ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
                                dmaru, DMAR_TYPE);
 
     if ( dmaru->include_all )
     {
         dprintk(XENLOG_INFO VTDPREFIX, "found INCLUDE_ALL\n");
-        /* Only allow one INCLUDE_ALL */
-        if ( include_all )
-        {
-            dprintk(XENLOG_WARNING VTDPREFIX,
-                    "Only one INCLUDE_ALL device scope is allowed\n");
-            ret = -EINVAL;
-        }
         include_all = 1;
     }
 
@@ -430,7 +337,8 @@ acpi_parse_one_rmrr(struct acpi_dmar_ent
     dev_scope_end   = ((void *)rmrr) + header->length;
     ret = acpi_parse_dev_scope(dev_scope_start, dev_scope_end,
                                rmrru, RMRR_TYPE);
-    if ( ret || (rmrru->devices_cnt == 0) )
+
+    if ( ret || (rmrru->scope.devices_cnt == 0) )
         xfree(rmrru);
     else
         acpi_register_rmrr_unit(rmrru);
diff -r 1e9df5cb885f -r bd7f2a120f94 xen/drivers/passthrough/vtd/dmar.h
--- a/xen/drivers/passthrough/vtd/dmar.h        Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/drivers/passthrough/vtd/dmar.h        Fri Jul 04 17:51:42 2008 +0100
@@ -40,48 +40,48 @@ struct acpi_ioapic_unit {
     }ioapic;
 };
 
+struct dmar_scope {
+    DECLARE_BITMAP(buses, 256);         /* buses owned by this unit */
+    u16    *devices;                    /* devices owned by this unit */
+    int    devices_cnt;
+};
+
 struct acpi_drhd_unit {
+    struct dmar_scope scope;            /* must be first member of struct */
     struct list_head list;
-    u64    address; /* register base address of the unit */
-    struct pci_dev *devices; /* target devices */
-    int    devices_cnt;
+    u64    address;                     /* register base address of the unit */
     u8     include_all:1;
     struct iommu *iommu;
     struct list_head ioapic_list;
 };
 
 struct acpi_rmrr_unit {
+    struct dmar_scope scope;            /* must be first member of struct */
     struct list_head list;
     u64    base_address;
     u64    end_address;
-    struct pci_dev *devices; /* target devices */
-    int    devices_cnt;
     u8     allow_all:1;
 };
 
 struct acpi_atsr_unit {
+    struct dmar_scope scope;            /* must be first member of struct */
     struct list_head list;
-    struct pci_dev *devices; /* target devices */
-    int    devices_cnt;
     u8     all_ports:1;
 };
 
-#define for_each_iommu(domain, iommu) \
-    list_for_each_entry(iommu, \
-        &(domain->arch.hvm_domain.hvm_iommu.iommu_list), list)
 
 #define for_each_drhd_unit(drhd) \
     list_for_each_entry(drhd, &acpi_drhd_units, list)
-#define for_each_rmrr_device(rmrr, pdev) \
-    list_for_each_entry(rmrr, &acpi_rmrr_units, list) { \
-        int _i; \
-        for (_i = 0; _i < rmrr->devices_cnt; _i++) { \
-            pdev = &(rmrr->devices[_i]);
-#define end_for_each_rmrr_device(rmrr, pdev) \
-        } \
-    }
 
-struct acpi_drhd_unit * acpi_find_matched_drhd_unit(struct pci_dev *dev);
+#define for_each_rmrr_device(rmrr, bdf, idx)            \
+    list_for_each_entry(rmrr, &acpi_rmrr_units, list)   \
+        /* assume there never is a bdf == 0 */          \
+        for (idx = 0; (bdf = rmrr->scope.devices[i]) && \
+                 idx < rmrr->scope.devices_cnt; idx++)
+
+struct acpi_drhd_unit * acpi_find_matched_drhd_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);
 
 #define DMAR_TYPE 1
 #define RMRR_TYPE 2
@@ -91,6 +91,6 @@ struct acpi_drhd_unit * acpi_find_matche
 
 int vtd_hw_check(void);
 void disable_pmr(struct iommu *iommu);
-int is_usb_device(struct pci_dev *pdev);
+int is_usb_device(u8 bus, u8 devfn);
 
 #endif /* _DMAR_H_ */
diff -r 1e9df5cb885f -r bd7f2a120f94 xen/drivers/passthrough/vtd/intremap.c
--- a/xen/drivers/passthrough/vtd/intremap.c    Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/drivers/passthrough/vtd/intremap.c    Fri Jul 04 17:51:42 2008 +0100
@@ -396,7 +396,7 @@ void msi_msg_read_remap_rte(
     struct iommu *iommu = NULL;
     struct ir_ctrl *ir_ctrl;
 
-    drhd = acpi_find_matched_drhd_unit(pdev);
+    drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn);
     iommu = drhd->iommu;
 
     ir_ctrl = iommu_ir_ctrl(iommu);
@@ -414,7 +414,7 @@ void msi_msg_write_remap_rte(
     struct iommu *iommu = NULL;
     struct ir_ctrl *ir_ctrl;
 
-    drhd = acpi_find_matched_drhd_unit(msi_desc->dev);
+    drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn);
     iommu = drhd->iommu;
 
     ir_ctrl = iommu_ir_ctrl(iommu);
diff -r 1e9df5cb885f -r bd7f2a120f94 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c       Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c       Fri Jul 04 17:51:42 2008 +0100
@@ -1089,8 +1089,8 @@ static int domain_context_mapping_one(
     if ( ecap_pass_thru(iommu->ecap) && (domain->domain_id == 0) )
         context_set_translation_type(*context, CONTEXT_TT_PASS_THRU);
     else
-    {
 #endif
+    {
         /* Ensure we have pagetables allocated down to leaf PTE. */
         if ( hd->pgd_maddr == 0 )
         {
@@ -1119,9 +1119,7 @@ static int domain_context_mapping_one(
 
         context_set_address_root(*context, pgd_maddr);
         context_set_translation_type(*context, CONTEXT_TT_MULTI_LEVEL);
-#ifdef CONTEXT_PASSTHRU
-    }
-#endif
+    }
 
     /*
      * domain_id 0 is not valid on Intel's IOMMU, force domain_id to
@@ -1150,115 +1148,128 @@ static int domain_context_mapping_one(
 #define PCI_BASE_CLASS_BRIDGE    0x06
 #define PCI_CLASS_BRIDGE_PCI     0x0604
 
-#define DEV_TYPE_PCIe_ENDPOINT   1
-#define DEV_TYPE_PCI_BRIDGE      2
-#define DEV_TYPE_PCI             3
-
-int pdev_type(struct pci_dev *dev)
+enum {
+    DEV_TYPE_PCIe_ENDPOINT,
+    DEV_TYPE_PCIe_BRIDGE,
+    DEV_TYPE_PCI_BRIDGE,
+    DEV_TYPE_PCI,
+};
+
+int pdev_type(u8 bus, u8 devfn)
 {
     u16 class_device;
-    u16 status;
-
-    class_device = pci_conf_read16(dev->bus, PCI_SLOT(dev->devfn),
-                                   PCI_FUNC(dev->devfn), PCI_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 )
-        return DEV_TYPE_PCI_BRIDGE;
-
-    status = pci_conf_read16(dev->bus, PCI_SLOT(dev->devfn),
-                             PCI_FUNC(dev->devfn), PCI_STATUS);
-
+    {
+        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(dev->bus, dev->devfn,
-                            PCI_CAPABILITY_LIST, PCI_CAP_ID_EXP) )
+    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
-struct pci_dev bus2bridge[MAX_BUSES];
-
-static int domain_context_mapping(
-    struct domain *domain,
-    struct iommu *iommu,
-    struct pci_dev *pdev)
-{
+static struct { u8 map, bus, devfn; } bus2bridge[MAX_BUSES];
+
+static int find_pcie_endpoint(u8 *bus, u8 *devfn)
+{
+    int cnt = 0;
+
+    if ( *bus == 0 )
+        /* assume integrated PCI devices in RC have valid requester-id */
+        return 1;
+
+    if ( !bus2bridge[*bus].map )
+        return 0;
+
+    while ( bus2bridge[*bus].map )
+    {
+        *devfn = bus2bridge[*bus].devfn;
+        *bus = bus2bridge[*bus].bus;
+        if ( cnt++ >= MAX_BUSES )
+            return 0;
+    }
+
+    return 1;
+}
+
+static int domain_context_mapping(struct domain *domain, u8 bus, u8 devfn)
+{
+    struct acpi_drhd_unit *drhd;
     int ret = 0;
-    int dev, func, sec_bus, sub_bus;
+    u16 sec_bus, sub_bus, ob, odf;
     u32 type;
 
-    type = pdev_type(pdev);
+    drhd = acpi_find_matched_drhd_unit(bus, devfn);
+    if ( !drhd )
+        return -ENODEV;
+
+    type = pdev_type(bus, devfn);
     switch ( type )
     {
+    case DEV_TYPE_PCIe_BRIDGE:
+        break;
+
     case DEV_TYPE_PCI_BRIDGE:
-        sec_bus = pci_conf_read8(
-            pdev->bus, PCI_SLOT(pdev->devfn),
-            PCI_FUNC(pdev->devfn), PCI_SECONDARY_BUS);
-
-        if ( bus2bridge[sec_bus].bus == 0 )
+        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);
+
+        for ( sub_bus &= 0xff; sec_bus <= sub_bus; sec_bus++ )
         {
-            bus2bridge[sec_bus].bus   =  pdev->bus;
-            bus2bridge[sec_bus].devfn =  pdev->devfn;
+            bus2bridge[sec_bus].map = 1;
+            bus2bridge[sec_bus].bus =  bus;
+            bus2bridge[sec_bus].devfn =  devfn;
         }
-
-        sub_bus = pci_conf_read8(
-            pdev->bus, PCI_SLOT(pdev->devfn),
-            PCI_FUNC(pdev->devfn), PCI_SUBORDINATE_BUS);
-
-        if ( sec_bus != sub_bus )
-            gdprintk(XENLOG_WARNING VTDPREFIX,
-                     "context_context_mapping: nested PCI bridge not "
-                     "supported: bdf = %x:%x:%x sec_bus = %x sub_bus = %x\n",
-                     pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
-                     sec_bus, sub_bus);
         break;
+
     case DEV_TYPE_PCIe_ENDPOINT:
         gdprintk(XENLOG_INFO VTDPREFIX,
-                 "domain_context_mapping:PCIe : bdf = %x:%x:%x\n",
-                 pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-        ret = domain_context_mapping_one(domain, iommu,
-                                         (u8)(pdev->bus), (u8)(pdev->devfn));
+                 "domain_context_mapping:PCIe: bdf = %x:%x.%x\n",
+                 bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+        ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
         break;
+
     case DEV_TYPE_PCI:
         gdprintk(XENLOG_INFO VTDPREFIX,
-                 "domain_context_mapping:PCI: bdf = %x:%x:%x\n",
-                 pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
-
-        if ( pdev->bus == 0 )
-            ret = domain_context_mapping_one(
-                domain, iommu, (u8)(pdev->bus), (u8)(pdev->devfn));
-        else
+                 "domain_context_mapping:PCI:  bdf = %x:%x.%x\n",
+                 bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+        ob = bus; odf = devfn;
+        if ( !find_pcie_endpoint(&bus, &devfn) )
         {
-            if ( bus2bridge[pdev->bus].bus != 0 )
-                gdprintk(XENLOG_WARNING VTDPREFIX,
-                         "domain_context_mapping:bus2bridge"
-                         "[%d].bus != 0\n", pdev->bus);
-
-            ret = domain_context_mapping_one(
-                domain, iommu,
-                (u8)(bus2bridge[pdev->bus].bus),
-                (u8)(bus2bridge[pdev->bus].devfn));
-
-            /* now map everything behind the PCI bridge */
-            for ( dev = 0; dev < 32; dev++ )
-            {
-                for ( func = 0; func < 8; func++ )
-                {
-                    ret = domain_context_mapping_one(
-                        domain, iommu,
-                        pdev->bus, (u8)PCI_DEVFN(dev, func));
-                    if ( ret )
-                        return ret;
-                }
-            }
+            gdprintk(XENLOG_WARNING VTDPREFIX, 
"domain_context_mapping:invalid");
+            break;
         }
+
+        if ( ob != bus || odf != devfn )
+            gdprintk(XENLOG_INFO VTDPREFIX,
+                     "domain_context_mapping:map:  bdf = %x:%x.%x -> 
%x:%x.%x\n",
+                     ob, PCI_SLOT(odf), PCI_FUNC(odf),
+                     bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+        ret = domain_context_mapping_one(domain, drhd->iommu, bus, devfn);
         break;
+
     default:
         gdprintk(XENLOG_ERR VTDPREFIX,
-                 "domain_context_mapping:unknown type : bdf = %x:%x:%x\n",
-                 pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+                 "domain_context_mapping:unknown type : bdf = %x:%x.%x\n",
+                 bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
         ret = -EINVAL;
         break;
     }
@@ -1266,9 +1277,7 @@ static int domain_context_mapping(
     return ret;
 }
 
-static int domain_context_unmap_one(
-    struct iommu *iommu,
-    u8 bus, u8 devfn)
+static int domain_context_unmap_one(struct iommu *iommu, u8 bus, u8 devfn)
 {
     struct context_entry *context, *context_entries;
     unsigned long flags;
@@ -1296,61 +1305,39 @@ static int domain_context_unmap_one(
     return 0;
 }
 
-static int domain_context_unmap(
-    struct iommu *iommu,
-    struct pci_dev *pdev)
-{
+static int domain_context_unmap(u8 bus, u8 devfn)
+{
+    struct acpi_drhd_unit *drhd;
     int ret = 0;
-    int dev, func, sec_bus, sub_bus;
     u32 type;
 
-    type = pdev_type(pdev);
+    drhd = acpi_find_matched_drhd_unit(bus, devfn);
+    if ( !drhd )
+        return -ENODEV;
+
+    type = pdev_type(bus, devfn);
     switch ( type )
     {
+    case DEV_TYPE_PCIe_BRIDGE:
+        break;
+
     case DEV_TYPE_PCI_BRIDGE:
-        sec_bus = pci_conf_read8(
-            pdev->bus, PCI_SLOT(pdev->devfn),
-            PCI_FUNC(pdev->devfn), PCI_SECONDARY_BUS);
-        sub_bus = pci_conf_read8(
-            pdev->bus, PCI_SLOT(pdev->devfn),
-            PCI_FUNC(pdev->devfn), PCI_SUBORDINATE_BUS);
+        ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
         break;
+
     case DEV_TYPE_PCIe_ENDPOINT:
-        ret = domain_context_unmap_one(iommu,
-                                       (u8)(pdev->bus), (u8)(pdev->devfn));
+        ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
         break;
+
     case DEV_TYPE_PCI:
-        if ( pdev->bus == 0 )
-            ret = domain_context_unmap_one(
-                iommu, (u8)(pdev->bus), (u8)(pdev->devfn));
-        else
-        {
-            if ( bus2bridge[pdev->bus].bus != 0 )
-                gdprintk(XENLOG_WARNING VTDPREFIX,
-                         "domain_context_unmap:"
-                         "bus2bridge[%d].bus != 0\n", pdev->bus);
-
-            ret = domain_context_unmap_one(iommu,
-                                           (u8)(bus2bridge[pdev->bus].bus),
-                                           (u8)(bus2bridge[pdev->bus].devfn));
-
-            /* Unmap everything behind the PCI bridge */
-            for ( dev = 0; dev < 32; dev++ )
-            {
-                for ( func = 0; func < 8; func++ )
-                {
-                    ret = domain_context_unmap_one(
-                        iommu, pdev->bus, (u8)PCI_DEVFN(dev, func));
-                    if ( ret )
-                        return ret;
-                }
-            }
-        }
+        if ( find_pcie_endpoint(&bus, &devfn) )
+            ret = domain_context_unmap_one(drhd->iommu, bus, devfn);
         break;
+
     default:
         gdprintk(XENLOG_ERR VTDPREFIX,
                  "domain_context_unmap:unknown type: bdf = %x:%x:%x\n",
-                 pdev->bus, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+                 bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
         ret = -EINVAL;
         break;
     }
@@ -1364,7 +1351,7 @@ void reassign_device_ownership(
     u8 bus, u8 devfn)
 {
     struct hvm_iommu *source_hd = domain_hvm_iommu(source);
-    struct pci_dev *pdev, *pdev2;
+    struct pci_dev *pdev;
     struct acpi_drhd_unit *drhd;
     struct iommu *iommu;
     int status;
@@ -1378,27 +1365,28 @@ void reassign_device_ownership(
 
     return;
 
- found:
-    drhd = acpi_find_matched_drhd_unit(pdev);
+found:
+    drhd = acpi_find_matched_drhd_unit(bus, devfn);
     iommu = drhd->iommu;
-    domain_context_unmap(iommu, pdev);
+    domain_context_unmap(bus, devfn);
 
     /* Move pci device from the source domain to target domain. */
     list_move(&pdev->domain_list, &target->arch.pdev_list);
 
-    for_each_pdev ( source, pdev2 )
-    {
-        drhd = acpi_find_matched_drhd_unit(pdev2);
+    for_each_pdev ( source, pdev )
+    {
+        drhd = acpi_find_matched_drhd_unit(pdev->bus, pdev->devfn);
         if ( drhd->iommu == iommu )
         {
             found = 1;
             break;
         }
     }
+
     if ( !found )
         clear_bit(iommu->index, &source_hd->iommu_bitmap);
 
-    status = domain_context_mapping(target, iommu, pdev);
+    status = domain_context_mapping(target, bus, devfn);
     if ( status != 0 )
         gdprintk(XENLOG_ERR VTDPREFIX, "domain_context_mapping failed\n");
 }
@@ -1436,19 +1424,13 @@ void iommu_domain_teardown(struct domain
     iommu_domid_release(d);
 }
 
-static int domain_context_mapped(struct pci_dev *pdev)
+static int domain_context_mapped(u8 bus, u8 devfn)
 {
     struct acpi_drhd_unit *drhd;
-    struct iommu *iommu;
-    int ret;
 
     for_each_drhd_unit ( drhd )
-    {
-        iommu = drhd->iommu;
-        ret = device_context_mapped(iommu, pdev->bus, pdev->devfn);
-        if ( ret )
-            return ret;
-    }
+        if ( device_context_mapped(drhd->iommu, bus, devfn) )
+            return 1;
 
     return 0;
 }
@@ -1570,12 +1552,10 @@ int iommu_page_unmapping(struct domain *
     return 0;
 }
 
-static int iommu_prepare_rmrr_dev(
-    struct domain *d,
-    struct acpi_rmrr_unit *rmrr,
-    struct pci_dev *pdev)
-{
-    struct acpi_drhd_unit *drhd;
+static int iommu_prepare_rmrr_dev(struct domain *d,
+                                  struct acpi_rmrr_unit *rmrr,
+                                  u8 bus, u8 devfn)
+{
     u64 size;
     int ret;
 
@@ -1587,10 +1567,9 @@ static int iommu_prepare_rmrr_dev(
     if ( ret )
         return ret;
 
-    if ( domain_context_mapped(pdev) == 0 )
-    {
-        drhd = acpi_find_matched_drhd_unit(pdev);
-        ret = domain_context_mapping(d, drhd->iommu, pdev);
+    if ( domain_context_mapped(bus, devfn) == 0 )
+    {
+        ret = domain_context_mapping(d, bus, devfn);
         if ( !ret )
             return 0;
     }
@@ -1601,7 +1580,6 @@ static void setup_dom0_devices(struct do
 static void setup_dom0_devices(struct domain *d)
 {
     struct hvm_iommu *hd;
-    struct acpi_drhd_unit *drhd;
     struct pci_dev *pdev;
     int bus, dev, func, ret;
     u32 l;
@@ -1624,8 +1602,7 @@ static void setup_dom0_devices(struct do
                 pdev->devfn = PCI_DEVFN(dev, func);
                 list_add_tail(&pdev->domain_list, &d->arch.pdev_list);
 
-                drhd = acpi_find_matched_drhd_unit(pdev);
-                ret = domain_context_mapping(d, drhd->iommu, pdev);
+                ret = domain_context_mapping(d, pdev->bus, pdev->devfn);
                 if ( ret != 0 )
                     gdprintk(XENLOG_ERR VTDPREFIX,
                              "domain_context_mapping failed\n");
@@ -1701,15 +1678,16 @@ static void setup_dom0_rmrr(struct domai
 static void setup_dom0_rmrr(struct domain *d)
 {
     struct acpi_rmrr_unit *rmrr;
-    struct pci_dev *pdev;
-    int ret;
-
-    for_each_rmrr_device ( rmrr, pdev )
-        ret = iommu_prepare_rmrr_dev(d, rmrr, pdev);
+    u16 bdf;
+    int ret, i;
+
+    for_each_rmrr_device ( rmrr, bdf, i )
+    {
+        ret = iommu_prepare_rmrr_dev(d, rmrr, PCI_BUS(bdf), PCI_DEVFN2(bdf));
         if ( ret )
             gdprintk(XENLOG_ERR VTDPREFIX,
                      "IOMMU: mapping reserved region failed\n");
-    end_for_each_rmrr_device ( rmrr, pdev )
+    }
 }
 
 int intel_vtd_setup(void)
@@ -1769,25 +1747,26 @@ int intel_iommu_assign_device(struct dom
 int intel_iommu_assign_device(struct domain *d, u8 bus, u8 devfn)
 {
     struct acpi_rmrr_unit *rmrr;
-    struct pci_dev *pdev;
-    int ret = 0;
+    int ret = 0, i;
+    u16 bdf;
 
     if ( list_empty(&acpi_drhd_units) )
         return ret;
 
     reassign_device_ownership(dom0, d, bus, devfn);
 
-    /* Setup rmrr identify mapping */
-    for_each_rmrr_device( rmrr, pdev )
-        if ( pdev->bus == bus && pdev->devfn == devfn )
+    /* Setup rmrr identity mapping */
+    for_each_rmrr_device( rmrr, bdf, i )
+    {
+        if ( PCI_BUS(bdf) == bus && PCI_DEVFN2(bdf) == devfn )
         {
             /* FIXME: Because USB RMRR conflicts with guest bios region,
              * ignore USB RMRR temporarily.
              */
-            if ( is_usb_device(pdev) )
+            if ( is_usb_device(bus, devfn) )
                 return 0;
 
-            ret = iommu_prepare_rmrr_dev(d, rmrr, pdev);
+            ret = iommu_prepare_rmrr_dev(d, rmrr, bus, devfn);
             if ( ret )
             {
                 gdprintk(XENLOG_ERR VTDPREFIX,
@@ -1795,9 +1774,17 @@ int intel_iommu_assign_device(struct dom
                 return ret;
             }
         }
-    end_for_each_rmrr_device(rmrr, pdev)
+    }
 
     return ret;
+}
+
+static int intel_iommu_group_id(u8 bus, u8 devfn)
+{
+    if ( !bus2bridge[bus].map || find_pcie_endpoint(&bus, &devfn) )
+        return PCI_BDF2(bus, devfn);
+    else
+        return -1;
 }
 
 u8 iommu_state[MAX_IOMMU_REGS * MAX_IOMMUS];
@@ -1881,7 +1868,7 @@ struct iommu_ops intel_iommu_ops = {
     .map_page = intel_iommu_map_page,
     .unmap_page = intel_iommu_unmap_page,
     .reassign_device = reassign_device_ownership,
-    .get_device_group_id = NULL,
+    .get_device_group_id = intel_iommu_group_id,
 };
 
 /*
diff -r 1e9df5cb885f -r bd7f2a120f94 xen/drivers/passthrough/vtd/utils.c
--- a/xen/drivers/passthrough/vtd/utils.c       Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/drivers/passthrough/vtd/utils.c       Fri Jul 04 17:51:42 2008 +0100
@@ -32,12 +32,10 @@
 #define SEABURG 0x4000
 #define C_STEP  2
 
-int is_usb_device(struct pci_dev *pdev)
-{
-    u8 bus = pdev->bus;
-    u8 dev = PCI_SLOT(pdev->devfn);
-    u8 func = PCI_FUNC(pdev->devfn);
-    u16 class = pci_conf_read16(bus, dev, func, PCI_CLASS_DEVICE);
+int is_usb_device(u8 bus, u8 devfn)
+{
+    u16 class = pci_conf_read16(bus, PCI_SLOT(devfn), PCI_FUNC(devfn),
+                                PCI_CLASS_DEVICE);
     return (class == 0xc03);
 }
 
diff -r 1e9df5cb885f -r bd7f2a120f94 xen/include/xen/pci.h
--- a/xen/include/xen/pci.h     Fri Jul 04 17:51:16 2008 +0100
+++ b/xen/include/xen/pci.h     Fri Jul 04 17:51:42 2008 +0100
@@ -20,9 +20,13 @@
  *  7:3 = slot
  *  2:0 = function
  */
-#define PCI_DEVFN(slot,func)  (((slot & 0x1f) << 3) | (func & 0x07))
-#define PCI_SLOT(devfn)       (((devfn) >> 3) & 0x1f)
-#define PCI_FUNC(devfn)       ((devfn) & 0x07)
+#define PCI_BUS(bdf)    (((bdf) >> 8) & 0xff)
+#define PCI_SLOT(bdf)   (((bdf) >> 3) & 0x1f)
+#define PCI_FUNC(bdf)   ((bdf) & 0x07)
+#define PCI_DEVFN(d,f)  (((d & 0x1f) << 3) | (f & 0x07))
+#define PCI_DEVFN2(bdf) ((bdf) & 0xff)
+#define PCI_BDF(b,d,f)  (((b * 0xff) << 8) | PCI_DEVFN(d,f))
+#define PCI_BDF2(b,df)  (((b & 0xff) << 8) | (df & 0xff))
 
 struct pci_dev {
     struct list_head domain_list;
@@ -31,6 +35,10 @@ struct pci_dev {
     u8 devfn;
     struct list_head msi_list;
 };
+
+#define for_each_pdev(domain, pdev) \
+    list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
+
 
 uint8_t pci_conf_read8(
     unsigned int bus, unsigned int dev, unsigned int func, unsigned int reg);
@@ -50,9 +58,4 @@ int pci_find_cap_offset(u8 bus, u8 dev, 
 int pci_find_cap_offset(u8 bus, u8 dev, u8 func, u8 cap);
 int pci_find_next_cap(u8 bus, unsigned int devfn, u8 pos, int cap);
 
-
-#define for_each_pdev(domain, pdev) \
-    list_for_each_entry(pdev, &(domain->arch.pdev_list), domain_list)
-
-
 #endif /* __XEN_PCI_H__ */

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.