Commit 4399c8bf2b9093696fa8160d79712e7346989c46
Committed by
David Woodhouse
1 parent
8140a95d22
Exists in
master
and in
4 other branches
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; |