Commit 4399c8bf authored by Allen Kay's avatar Allen Kay Committed by David Woodhouse

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: default avatarAllen Kay <allen.m.kay@intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 8140a95d
...@@ -306,6 +306,11 @@ static inline bool dma_pte_present(struct dma_pte *pte) ...@@ -306,6 +306,11 @@ static inline bool dma_pte_present(struct dma_pte *pte)
return (pte->val & 3) != 0; return (pte->val & 3) != 0;
} }
static inline bool dma_pte_superpage(struct dma_pte *pte)
{
return (pte->val & (1 << 7));
}
static inline int first_pte_in_page(struct dma_pte *pte) static inline int first_pte_in_page(struct dma_pte *pte)
{ {
return !((unsigned long)pte & ~VTD_PAGE_MASK); return !((unsigned long)pte & ~VTD_PAGE_MASK);
...@@ -734,29 +739,23 @@ static void free_context_table(struct intel_iommu *iommu) ...@@ -734,29 +739,23 @@ static void free_context_table(struct intel_iommu *iommu)
} }
static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain, static struct dma_pte *pfn_to_dma_pte(struct dmar_domain *domain,
unsigned long pfn, int large_level) unsigned long pfn, int target_level)
{ {
int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT; int addr_width = agaw_to_width(domain->agaw) - VTD_PAGE_SHIFT;
struct dma_pte *parent, *pte = NULL; struct dma_pte *parent, *pte = NULL;
int level = agaw_to_level(domain->agaw); int level = agaw_to_level(domain->agaw);
int offset, target_level; int offset;
BUG_ON(!domain->pgd); BUG_ON(!domain->pgd);
BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width); BUG_ON(addr_width < BITS_PER_LONG && pfn >> addr_width);
parent = domain->pgd; parent = domain->pgd;
/* Search pte */
if (!large_level)
target_level = 1;
else
target_level = large_level;
while (level > 0) { while (level > 0) {
void *tmp_page; void *tmp_page;
offset = pfn_level_offset(pfn, level); offset = pfn_level_offset(pfn, level);
pte = &parent[offset]; pte = &parent[offset];
if (!large_level && (pte->val & DMA_PTE_LARGE_PAGE)) if (!target_level && (dma_pte_superpage(pte) || !dma_pte_present(pte)))
break; break;
if (level == target_level) if (level == target_level)
break; break;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment