# 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
|