[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |