diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 17:25:01 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 17:25:01 -0700 |
commit | 0961d6581c870850342ad6ea25263763433d666f (patch) | |
tree | 371c61fd7f621397907983031003e784a040402e /drivers | |
parent | 1756ac3d3c41341297ea25b818b7fce505bb2a9a (diff) | |
parent | fd0c8894893cba722bdea12de25b49f980795d06 (diff) | |
download | linux-0961d6581c870850342ad6ea25263763433d666f.tar.bz2 |
Merge git://git.infradead.org/iommu-2.6
* git://git.infradead.org/iommu-2.6:
intel-iommu: Set a more specific taint flag for invalid BIOS DMAR tables
intel-iommu: Combine the BIOS DMAR table warning messages
panic: Add taint flag TAINT_FIRMWARE_WORKAROUND ('I')
panic: Allow warnings to set different taint flags
intel-iommu: intel_iommu_map_range failed at very end of address space
intel-iommu: errors with smaller iommu widths
intel-iommu: Fix boot inside 64bit virtualbox with io-apic disabled
intel-iommu: use physfn to search drhd for VF
intel-iommu: Print out iommu seq_id
intel-iommu: Don't complain that ACPI_DMAR_SCOPE_TYPE_IOAPIC is not supported
intel-iommu: Avoid global flushes with caching mode.
intel-iommu: Use correct domain ID when caching mode is enabled
intel-iommu mistakenly uses offset_pfn when caching mode is enabled
intel-iommu: use for_each_set_bit()
intel-iommu: Fix section mismatch dmar_ir_support() uses dmar_tbl.
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/pci/dmar.c | 82 | ||||
-rw-r--r-- | drivers/pci/intel-iommu.c | 129 | ||||
-rw-r--r-- | drivers/pci/intr_remapping.c | 6 |
3 files changed, 99 insertions, 118 deletions
diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 33ead97f0c4b..0a19708074c2 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -131,9 +131,10 @@ static int __init dmar_parse_dev_scope(void *start, void *end, int *cnt, if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_ENDPOINT || scope->entry_type == ACPI_DMAR_SCOPE_TYPE_BRIDGE) (*cnt)++; - else + else if (scope->entry_type != ACPI_DMAR_SCOPE_TYPE_IOAPIC) { printk(KERN_WARNING PREFIX - "Unsupported device scope\n"); + "Unsupported device scope\n"); + } start += scope->length; } if (*cnt == 0) @@ -309,6 +310,8 @@ int dmar_find_matched_atsr_unit(struct pci_dev *dev) struct acpi_dmar_atsr *atsr; struct dmar_atsr_unit *atsru; + dev = pci_physfn(dev); + list_for_each_entry(atsru, &dmar_atsr_units, list) { atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header); if (atsr->segment == pci_domain_nr(dev->bus)) @@ -358,12 +361,14 @@ dmar_parse_one_rhsa(struct acpi_dmar_header *header) return 0; } } - WARN(1, "Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - drhd->reg_base_addr, - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); + WARN_TAINT( + 1, TAINT_FIRMWARE_WORKAROUND, + "Your BIOS is broken; RHSA refers to non-existent DMAR unit at %llx\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + drhd->reg_base_addr, + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); return 0; } @@ -507,7 +512,7 @@ parse_dmar_table(void) return ret; } -int dmar_pci_device_match(struct pci_dev *devices[], int cnt, +static int dmar_pci_device_match(struct pci_dev *devices[], int cnt, struct pci_dev *dev) { int index; @@ -530,6 +535,8 @@ dmar_find_matched_drhd_unit(struct pci_dev *dev) struct dmar_drhd_unit *dmaru = NULL; struct acpi_dmar_hardware_unit *drhd; + dev = pci_physfn(dev); + list_for_each_entry(dmaru, &dmar_drhd_units, list) { drhd = container_of(dmaru->hdr, struct acpi_dmar_hardware_unit, @@ -614,7 +621,17 @@ int __init dmar_table_init(void) return 0; } -static int bios_warned; +static void warn_invalid_dmar(u64 addr, const char *message) +{ + WARN_TAINT_ONCE( + 1, TAINT_FIRMWARE_WORKAROUND, + "Your BIOS is broken; DMAR reported at address %llx%s!\n" + "BIOS vendor: %s; Ver: %s; Product Version: %s\n", + addr, message, + dmi_get_system_info(DMI_BIOS_VENDOR), + dmi_get_system_info(DMI_BIOS_VERSION), + dmi_get_system_info(DMI_PRODUCT_VERSION)); +} int __init check_zero_address(void) { @@ -640,13 +657,7 @@ int __init check_zero_address(void) drhd = (void *)entry_header; if (!drhd->address) { - /* Promote an attitude of violence to a BIOS engineer today */ - WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - bios_warned = 1; + warn_invalid_dmar(0, ""); goto failed; } @@ -659,14 +670,8 @@ int __init check_zero_address(void) ecap = dmar_readq(addr + DMAR_ECAP_REG); early_iounmap(addr, VTD_PAGE_SIZE); if (cap == (uint64_t)-1 && ecap == (uint64_t)-1) { - /* Promote an attitude of violence to a BIOS engineer today */ - WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - drhd->address, - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - bios_warned = 1; + warn_invalid_dmar(drhd->address, + " returns all ones"); goto failed; } } @@ -731,14 +736,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) int msagaw = 0; if (!drhd->reg_base_addr) { - if (!bios_warned) { - WARN(1, "Your BIOS is broken; DMAR reported at address zero!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - bios_warned = 1; - } + warn_invalid_dmar(0, ""); return -EINVAL; } @@ -758,16 +756,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) iommu->ecap = dmar_readq(iommu->reg + DMAR_ECAP_REG); if (iommu->cap == (uint64_t)-1 && iommu->ecap == (uint64_t)-1) { - if (!bios_warned) { - /* Promote an attitude of violence to a BIOS engineer today */ - WARN(1, "Your BIOS is broken; DMAR reported at address %llx returns all ones!\n" - "BIOS vendor: %s; Ver: %s; Product Version: %s\n", - drhd->reg_base_addr, - dmi_get_system_info(DMI_BIOS_VENDOR), - dmi_get_system_info(DMI_BIOS_VERSION), - dmi_get_system_info(DMI_PRODUCT_VERSION)); - bios_warned = 1; - } + warn_invalid_dmar(drhd->reg_base_addr, " returns all ones"); goto err_unmap; } @@ -806,7 +795,8 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) } ver = readl(iommu->reg + DMAR_VER_REG); - pr_info("IOMMU %llx: ver %d:%d cap %llx ecap %llx\n", + pr_info("IOMMU %d: reg_base_addr %llx ver %d:%d cap %llx ecap %llx\n", + iommu->seq_id, (unsigned long long)drhd->reg_base_addr, DMAR_VER_MAJOR(ver), DMAR_VER_MINOR(ver), (unsigned long long)iommu->cap, @@ -1457,9 +1447,11 @@ int dmar_reenable_qi(struct intel_iommu *iommu) /* * Check interrupt remapping support in DMAR table description. */ -int dmar_ir_support(void) +int __init dmar_ir_support(void) { struct acpi_table_dmar *dmar; dmar = (struct acpi_table_dmar *)dmar_tbl; + if (!dmar) + return 0; return dmar->flags & 0x1; } diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 371dc564e2e4..796828fce34c 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -491,13 +491,11 @@ static void domain_update_iommu_coherency(struct dmar_domain *domain) domain->iommu_coherency = 1; - i = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); - for (; i < g_num_of_iommus; ) { + for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) { if (!ecap_coherent(g_iommus[i]->ecap)) { domain->iommu_coherency = 0; break; } - i = find_next_bit(&domain->iommu_bmp, g_num_of_iommus, i+1); } } @@ -507,13 +505,11 @@ static void domain_update_iommu_snooping(struct dmar_domain *domain) domain->iommu_snooping = 1; - i = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); - for (; i < g_num_of_iommus; ) { + for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) { if (!ecap_sc_support(g_iommus[i]->ecap)) { domain->iommu_snooping = 0; break; } - i = find_next_bit(&domain->iommu_bmp, g_num_of_iommus, i+1); } } @@ -1068,7 +1064,7 @@ static void iommu_flush_dev_iotlb(struct dmar_domain *domain, } static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did, - unsigned long pfn, unsigned int pages) + unsigned long pfn, unsigned int pages, int map) { unsigned int mask = ilog2(__roundup_pow_of_two(pages)); uint64_t addr = (uint64_t)pfn << VTD_PAGE_SHIFT; @@ -1089,10 +1085,10 @@ static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did, DMA_TLB_PSI_FLUSH); /* - * In caching mode, domain ID 0 is reserved for non-present to present - * mapping flush. Device IOTLB doesn't need to be flushed in this case. + * In caching mode, changes of pages from non-present to present require + * flush. However, device IOTLB doesn't need to be flushed in this case. */ - if (!cap_caching_mode(iommu->cap) || did) + if (!cap_caching_mode(iommu->cap) || !map) iommu_flush_dev_iotlb(iommu->domains[did], addr, mask); } @@ -1154,7 +1150,8 @@ static int iommu_init_domains(struct intel_iommu *iommu) unsigned long nlongs; ndomains = cap_ndoms(iommu->cap); - pr_debug("Number of Domains supportd <%ld>\n", ndomains); + pr_debug("IOMMU %d: Number of Domains supportd <%ld>\n", iommu->seq_id, + ndomains); nlongs = BITS_TO_LONGS(ndomains); spin_lock_init(&iommu->lock); @@ -1194,8 +1191,7 @@ void free_dmar_iommu(struct intel_iommu *iommu) unsigned long flags; if ((iommu->domains) && (iommu->domain_ids)) { - i = find_first_bit(iommu->domain_ids, cap_ndoms(iommu->cap)); - for (; i < cap_ndoms(iommu->cap); ) { + for_each_set_bit(i, iommu->domain_ids, cap_ndoms(iommu->cap)) { domain = iommu->domains[i]; clear_bit(i, iommu->domain_ids); @@ -1207,9 +1203,6 @@ void free_dmar_iommu(struct intel_iommu *iommu) domain_exit(domain); } spin_unlock_irqrestore(&domain->iommu_lock, flags); - - i = find_next_bit(iommu->domain_ids, - cap_ndoms(iommu->cap), i+1); } } @@ -1292,14 +1285,11 @@ static void iommu_detach_domain(struct dmar_domain *domain, spin_lock_irqsave(&iommu->lock, flags); ndomains = cap_ndoms(iommu->cap); - num = find_first_bit(iommu->domain_ids, ndomains); - for (; num < ndomains; ) { + for_each_set_bit(num, iommu->domain_ids, ndomains) { if (iommu->domains[num] == domain) { found = 1; break; } - num = find_next_bit(iommu->domain_ids, - cap_ndoms(iommu->cap), num+1); } if (found) { @@ -1485,15 +1475,12 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment, /* find an available domain id for this device in iommu */ ndomains = cap_ndoms(iommu->cap); - num = find_first_bit(iommu->domain_ids, ndomains); - for (; num < ndomains; ) { + for_each_set_bit(num, iommu->domain_ids, ndomains) { if (iommu->domains[num] == domain) { id = num; found = 1; break; } - num = find_next_bit(iommu->domain_ids, - cap_ndoms(iommu->cap), num+1); } if (found == 0) { @@ -1558,7 +1545,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, int segment, (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, DMA_CCMD_DEVICE_INVL); - iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_DSI_FLUSH); + iommu->flush.flush_iotlb(iommu, domain->id, 0, 0, DMA_TLB_DSI_FLUSH); } else { iommu_flush_write_buffer(iommu); } @@ -2333,14 +2320,16 @@ int __init init_dmars(void) */ iommu->flush.flush_context = __iommu_flush_context; iommu->flush.flush_iotlb = __iommu_flush_iotlb; - printk(KERN_INFO "IOMMU 0x%Lx: using Register based " + printk(KERN_INFO "IOMMU %d 0x%Lx: using Register based " "invalidation\n", + iommu->seq_id, (unsigned long long)drhd->reg_base_addr); } else { iommu->flush.flush_context = qi_flush_context; iommu->flush.flush_iotlb = qi_flush_iotlb; - printk(KERN_INFO "IOMMU 0x%Lx: using Queued " + printk(KERN_INFO "IOMMU %d 0x%Lx: using Queued " "invalidation\n", + iommu->seq_id, (unsigned long long)drhd->reg_base_addr); } } @@ -2621,7 +2610,7 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, /* it's a non-present to present mapping. Only flush if caching mode */ if (cap_caching_mode(iommu->cap)) - iommu_flush_iotlb_psi(iommu, 0, mm_to_dma_pfn(iova->pfn_lo), size); + iommu_flush_iotlb_psi(iommu, domain->id, mm_to_dma_pfn(iova->pfn_lo), size, 1); else iommu_flush_write_buffer(iommu); @@ -2661,15 +2650,24 @@ static void flush_unmaps(void) if (!deferred_flush[i].next) continue; - iommu->flush.flush_iotlb(iommu, 0, 0, 0, + /* In caching mode, global flushes turn emulation expensive */ + if (!cap_caching_mode(iommu->cap)) + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); for (j = 0; j < deferred_flush[i].next; j++) { unsigned long mask; struct iova *iova = deferred_flush[i].iova[j]; - - mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1)); - iommu_flush_dev_iotlb(deferred_flush[i].domain[j], - (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); + struct dmar_domain *domain = deferred_flush[i].domain[j]; + + /* On real hardware multiple invalidations are expensive */ + if (cap_caching_mode(iommu->cap)) + iommu_flush_iotlb_psi(iommu, domain->id, + iova->pfn_lo, iova->pfn_hi - iova->pfn_lo + 1, 0); + else { + mask = ilog2(mm_to_dma_pfn(iova->pfn_hi - iova->pfn_lo + 1)); + iommu_flush_dev_iotlb(deferred_flush[i].domain[j], + (uint64_t)iova->pfn_lo << PAGE_SHIFT, mask); + } __free_iova(&deferred_flush[i].domain[j]->iovad, iova); } deferred_flush[i].next = 0; @@ -2750,7 +2748,7 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, if (intel_iommu_strict) { iommu_flush_iotlb_psi(iommu, domain->id, start_pfn, - last_pfn - start_pfn + 1); + last_pfn - start_pfn + 1, 0); /* free iova */ __free_iova(&domain->iovad, iova); } else { @@ -2840,7 +2838,7 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, if (intel_iommu_strict) { iommu_flush_iotlb_psi(iommu, domain->id, start_pfn, - last_pfn - start_pfn + 1); + last_pfn - start_pfn + 1, 0); /* free iova */ __free_iova(&domain->iovad, iova); } else { @@ -2874,7 +2872,6 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne struct dmar_domain *domain; size_t size = 0; int prot = 0; - size_t offset_pfn = 0; struct iova *iova = NULL; int ret; struct scatterlist *sg; @@ -2928,7 +2925,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne /* it's a non-present to present mapping. Only flush if caching mode */ if (cap_caching_mode(iommu->cap)) - iommu_flush_iotlb_psi(iommu, 0, start_vpfn, offset_pfn); + iommu_flush_iotlb_psi(iommu, domain->id, start_vpfn, size, 1); else iommu_flush_write_buffer(iommu); @@ -3436,22 +3433,6 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) /* domain id for virtual machine, it won't be set in context */ static unsigned long vm_domid; -static int vm_domain_min_agaw(struct dmar_domain *domain) -{ - int i; - int min_agaw = domain->agaw; - - i = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); - for (; i < g_num_of_iommus; ) { - if (min_agaw > g_iommus[i]->agaw) - min_agaw = g_iommus[i]->agaw; - - i = find_next_bit(&domain->iommu_bmp, g_num_of_iommus, i+1); - } - - return min_agaw; -} - static struct dmar_domain *iommu_alloc_vm_domain(void) { struct dmar_domain *domain; @@ -3512,8 +3493,7 @@ static void iommu_free_vm_domain(struct dmar_domain *domain) iommu = drhd->iommu; ndomains = cap_ndoms(iommu->cap); - i = find_first_bit(iommu->domain_ids, ndomains); - for (; i < ndomains; ) { + for_each_set_bit(i, iommu->domain_ids, ndomains) { if (iommu->domains[i] == domain) { spin_lock_irqsave(&iommu->lock, flags); clear_bit(i, iommu->domain_ids); @@ -3521,7 +3501,6 @@ static void iommu_free_vm_domain(struct dmar_domain *domain) spin_unlock_irqrestore(&iommu->lock, flags); break; } - i = find_next_bit(iommu->domain_ids, ndomains, i+1); } } } @@ -3582,7 +3561,6 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, struct pci_dev *pdev = to_pci_dev(dev); struct intel_iommu *iommu; int addr_width; - u64 end; /* normally pdev is not mapped */ if (unlikely(domain_context_mapped(pdev))) { @@ -3605,14 +3583,30 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, /* check if this iommu agaw is sufficient for max mapped address */ addr_width = agaw_to_width(iommu->agaw); - end = DOMAIN_MAX_ADDR(addr_width); - end = end & VTD_PAGE_MASK; - if (end < dmar_domain->max_addr) { - printk(KERN_ERR "%s: iommu agaw (%d) is not " + if (addr_width > cap_mgaw(iommu->cap)) + addr_width = cap_mgaw(iommu->cap); + + if (dmar_domain->max_addr > (1LL << addr_width)) { + printk(KERN_ERR "%s: iommu width (%d) is not " "sufficient for the mapped address (%llx)\n", - __func__, iommu->agaw, dmar_domain->max_addr); + __func__, addr_width, dmar_domain->max_addr); return -EFAULT; } + dmar_domain->gaw = addr_width; + + /* + * Knock out extra levels of page tables if necessary + */ + while (iommu->agaw < dmar_domain->agaw) { + struct dma_pte *pte; + + pte = dmar_domain->pgd; + if (dma_pte_present(pte)) { + free_pgtable_page(dmar_domain->pgd); + dmar_domain->pgd = (struct dma_pte *)dma_pte_addr(pte); + } + dmar_domain->agaw--; + } return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL); } @@ -3632,7 +3626,6 @@ static int intel_iommu_map(struct iommu_domain *domain, { struct dmar_domain *dmar_domain = domain->priv; u64 max_addr; - int addr_width; int prot = 0; size_t size; int ret; @@ -3647,18 +3640,14 @@ static int intel_iommu_map(struct iommu_domain *domain, size = PAGE_SIZE << gfp_order; max_addr = iova + size; if (dmar_domain->max_addr < max_addr) { - int min_agaw; u64 end; /* check if minimum agaw is sufficient for mapped address */ - min_agaw = vm_domain_min_agaw(dmar_domain); - addr_width = agaw_to_width(min_agaw); - end = DOMAIN_MAX_ADDR(addr_width); - end = end & VTD_PAGE_MASK; + end = __DOMAIN_MAX_ADDR(dmar_domain->gaw) + 1; if (end < max_addr) { - printk(KERN_ERR "%s: iommu agaw (%d) is not " + printk(KERN_ERR "%s: iommu width (%d) is not " "sufficient for the mapped address (%llx)\n", - __func__, min_agaw, max_addr); + __func__, dmar_domain->gaw, max_addr); return -EFAULT; } dmar_domain->max_addr = max_addr; diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 6ee98a56946f..1315ac688aa2 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -832,9 +832,9 @@ static int ir_parse_ioapic_hpet_scope(struct acpi_dmar_header *header, return -1; } - printk(KERN_INFO "IOAPIC id %d under DRHD base" - " 0x%Lx\n", scope->enumeration_id, - drhd->address); + printk(KERN_INFO "IOAPIC id %d under DRHD base " + " 0x%Lx IOMMU %d\n", scope->enumeration_id, + drhd->address, iommu->seq_id); ir_parse_one_ioapic_scope(scope, iommu); } else if (scope->entry_type == ACPI_DMAR_SCOPE_TYPE_HPET) { |