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>
This commit is contained in:
parent
8140a95d22
commit
4399c8bf2b
1 changed files with 8 additions and 9 deletions
|
@ -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 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
Loading…
Add table
Reference in a new issue