aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci
diff options
context:
space:
mode:
authorTom Lyon <pugs@cisco.com>2010-05-17 03:20:45 -0400
committerDavid Woodhouse <David.Woodhouse@intel.com>2010-05-17 03:34:18 -0400
commita99c47a228c194aa75bffdcb82806c5f33c7c63b (patch)
tree90f8651c7374356bb29811ca6401858fb545610b /drivers/pci
parent4f506e07e0a3dff34427cece255a8f390a78d5a0 (diff)
intel-iommu: errors with smaller iommu widths
When using iommu_domain_alloc with the Intel iommu, the domain address width is always initialized to 48 bits (agaw 2). This domain->agaw value is then used by pfn_to_dma_pte to (always) build a 4 level page table. However, not all systems support iommu width of 48 or 4 level page tables. In particular, the Core i5-660 and i5-670 support an address width of 36 bits (not 39!), an agaw of only 1, and only 3 level page tables. This version of the patch simply lops off extra levels of the page tables if the agaw value of the iommu is less than what is currently allocated for the domain (in intel_iommu_attach_device). If there were already allocated addresses above what the new iommu can handle, EFAULT is returned. Signed-off-by: Tom Lyon <pugs@cisco.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/pci')
-rw-r--r--drivers/pci/intel-iommu.c40
1 files changed, 21 insertions, 19 deletions
diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
index da40f0789739..57be89e6f484 100644
--- a/drivers/pci/intel-iommu.c
+++ b/drivers/pci/intel-iommu.c
@@ -3433,19 +3433,6 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain)
3433/* domain id for virtual machine, it won't be set in context */ 3433/* domain id for virtual machine, it won't be set in context */
3434static unsigned long vm_domid; 3434static unsigned long vm_domid;
3435 3435
3436static int vm_domain_min_agaw(struct dmar_domain *domain)
3437{
3438 int i;
3439 int min_agaw = domain->agaw;
3440
3441 for_each_set_bit(i, &domain->iommu_bmp, g_num_of_iommus) {
3442 if (min_agaw > g_iommus[i]->agaw)
3443 min_agaw = g_iommus[i]->agaw;
3444 }
3445
3446 return min_agaw;
3447}
3448
3449static struct dmar_domain *iommu_alloc_vm_domain(void) 3436static struct dmar_domain *iommu_alloc_vm_domain(void)
3450{ 3437{
3451 struct dmar_domain *domain; 3438 struct dmar_domain *domain;
@@ -3574,7 +3561,6 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
3574 struct pci_dev *pdev = to_pci_dev(dev); 3561 struct pci_dev *pdev = to_pci_dev(dev);
3575 struct intel_iommu *iommu; 3562 struct intel_iommu *iommu;
3576 int addr_width; 3563 int addr_width;
3577 u64 end;
3578 3564
3579 /* normally pdev is not mapped */ 3565 /* normally pdev is not mapped */
3580 if (unlikely(domain_context_mapped(pdev))) { 3566 if (unlikely(domain_context_mapped(pdev))) {
@@ -3597,14 +3583,30 @@ static int intel_iommu_attach_device(struct iommu_domain *domain,
3597 3583
3598 /* check if this iommu agaw is sufficient for max mapped address */ 3584 /* check if this iommu agaw is sufficient for max mapped address */
3599 addr_width = agaw_to_width(iommu->agaw); 3585 addr_width = agaw_to_width(iommu->agaw);
3600 end = DOMAIN_MAX_ADDR(addr_width); 3586 if (addr_width > cap_mgaw(iommu->cap))
3601 end = end & VTD_PAGE_MASK; 3587 addr_width = cap_mgaw(iommu->cap);
3602 if (end < dmar_domain->max_addr) { 3588
3603 printk(KERN_ERR "%s: iommu agaw (%d) is not " 3589 if (dmar_domain->max_addr > (1LL << addr_width)) {
3590 printk(KERN_ERR "%s: iommu width (%d) is not "
3604 "sufficient for the mapped address (%llx)\n", 3591 "sufficient for the mapped address (%llx)\n",
3605 __func__, iommu->agaw, dmar_domain->max_addr); 3592 __func__, addr_width, dmar_domain->max_addr);
3606 return -EFAULT; 3593 return -EFAULT;
3607 } 3594 }
3595 dmar_domain->gaw = addr_width;
3596
3597 /*
3598 * Knock out extra levels of page tables if necessary
3599 */
3600 while (iommu->agaw < dmar_domain->agaw) {
3601 struct dma_pte *pte;
3602
3603 pte = dmar_domain->pgd;
3604 if (dma_pte_present(pte)) {
3605 free_pgtable_page(dmar_domain->pgd);
3606 dmar_domain->pgd = (struct dma_pte *)dma_pte_addr(pte);
3607 }
3608 dmar_domain->agaw--;
3609 }
3608 3610
3609 return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL); 3611 return domain_add_dev_info(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL);
3610} 3612}