diff options
Diffstat (limited to 'drivers/pci/intel-iommu.c')
-rw-r--r-- | drivers/pci/intel-iommu.c | 40 |
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 */ |
3434 | static unsigned long vm_domid; | 3434 | static unsigned long vm_domid; |
3435 | 3435 | ||
3436 | static 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 | |||
3449 | static struct dmar_domain *iommu_alloc_vm_domain(void) | 3436 | static 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 | } |