Commit 4399c8bf2b9093696fa8160d79712e7346989c46

Authored by Allen Kay
Committed by David Woodhouse
1 parent 8140a95d22

intel-iommu: fix superpage support in pfn_to_dma_pte()

If target_level == 0, current code breaks out of the while-loop if
SUPERPAGE bit is set. We should also break out if PTE is not present.
If we don't do this, KVM calls to iommu_iova_to_phys() will cause
pfn_to_dma_pte() to create mapping for 4KiB pages.

Signed-off-by: Allen Kay <allen.m.kay@intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>

Showing 1 changed file with 8 additions and 9 deletions Side-by-side Diff

drivers/iommu/intel-iommu.c
... ... @@ -306,6 +306,11 @@
306 306 return (pte->val & 3) != 0;
307 307 }
308 308  
  309 +static inline bool dma_pte_superpage(struct dma_pte *pte)
  310 +{
  311 + return (pte->val & (1 << 7));
  312 +}
  313 +
309 314 static inline int first_pte_in_page(struct dma_pte *pte)
310 315 {
311 316 return !((unsigned long)pte & ~VTD_PAGE_MASK);
312 317  
313 318  
314 319  
... ... @@ -734,29 +739,23 @@
734 739 }
735 740  
736 741 static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
737   - unsigned long pfn, int large_level)
  742 + unsigned long pfn, int target_level)
738 743 {
739 744 int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
740 745 struct dma_pte *parent, *pte = NULL;
741 746 int level = agaw_to_level(domain->agaw);
742   - int offset, target_level;
  747 + int offset;
743 748  
744 749 BUG_ON(!domain->pgd);
745 750 BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width);
746 751 parent = domain->pgd;
747 752  
748   - /* Search pte */
749   - if (!large_level)
750   - target_level = 1;
751   - else
752   - target_level = large_level;
753   -
754 753 while (level > 0) {
755 754 void *tmp_page;
756 755  
757 756 offset = pfn_level_offset(pfn, level);
758 757 pte = &parent[offset];
759   - if (!large_level && (pte->val & DMA_PTE_LARGE_PAGE))
  758 + if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
760 759 break;
761 760 if (level == target_level)
762 761 break;