mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
VT-d: Fix free VT-d page table issue
This patch frees VT-d page tables from pgd, rather than free them according to a guest address range. This fixes [Bug 1244] Poweroff/Destroying HVM guest causes HV crash. http://bugzilla.xensource.com/bugzilla/show_bug.cgi?id=1244. Signed-off-by: Weidong Han <weidong.han@intel.com>
This commit is contained in:
1 parent
d4a7565965
commit
3401030f64
2 files changed
+53
-6
No files matched your search
@@ -678,17 +678,63 @@ void dma_pte_free_pagetable(struct domain *domain, u64 start, u64 end)
|
||||
}
|
||||
}
|
||||
|
||||
/* free all VT-d page tables when shut down or destroy domain. */
|
||||
static void iommu_free_next_pagetable(u64 pt_maddr, unsigned long index,
|
||||
int level)
|
||||
{
|
||||
struct acpi_drhd_unit *drhd;
|
||||
unsigned long next_index;
|
||||
struct dma_pte *pt_vaddr, *pde;
|
||||
int next_level;
|
||||
|
||||
if ( pt_maddr == 0 )
|
||||
return;
|
||||
|
||||
pt_vaddr = (struct dma_pte *)map_vtd_domain_page(pt_maddr);
|
||||
pde = &pt_vaddr[index];
|
||||
if ( dma_pte_addr(*pde) != 0 )
|
||||
{
|
||||
next_level = level - 1;
|
||||
if ( next_level > 1 )
|
||||
{
|
||||
next_index = 0;
|
||||
do
|
||||
{
|
||||
iommu_free_next_pagetable(pde->val,
|
||||
next_index, next_level);
|
||||
next_index++;
|
||||
} while ( next_index < PTE_NUM );
|
||||
}
|
||||
|
||||
dma_clear_pte(*pde);
|
||||
drhd = list_entry(acpi_drhd_units.next, typeof(*drhd), list);
|
||||
iommu_flush_cache_entry(drhd->iommu, pde);
|
||||
free_pgtable_maddr(pde->val);
|
||||
unmap_vtd_domain_page(pt_vaddr);
|
||||
}
|
||||
else
|
||||
unmap_vtd_domain_page(pt_vaddr);
|
||||
}
|
||||
|
||||
/* free all VT-d page tables when shut down or destroy domain. */
|
||||
static void iommu_free_pagetable(struct domain *domain)
|
||||
{
|
||||
unsigned long index;
|
||||
struct hvm_iommu *hd = domain_hvm_iommu(domain);
|
||||
int addr_width = agaw_to_width(hd->agaw);
|
||||
u64 start, end;
|
||||
int total_level = agaw_to_level(hd->agaw);
|
||||
|
||||
start = 0;
|
||||
end = (((u64)1) << addr_width) - 1;
|
||||
if ( hd->pgd_maddr != 0 )
|
||||
{
|
||||
index = 0;
|
||||
do
|
||||
{
|
||||
iommu_free_next_pagetable(hd->pgd_maddr,
|
||||
index, total_level + 1);
|
||||
index++;
|
||||
} while ( index < PTE_NUM );
|
||||
|
||||
dma_pte_free_pagetable(domain, start, end);
|
||||
free_pgtable_maddr(hd->pgd_maddr);
|
||||
hd->pgd_maddr = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int iommu_set_root_entry(struct iommu *iommu)
|
||||
|
||||
@@ -235,6 +235,7 @@ struct context_entry {
|
||||
/* page table handling */
|
||||
#define LEVEL_STRIDE (9)
|
||||
#define LEVEL_MASK ((1 << LEVEL_STRIDE) - 1)
|
||||
#define PTE_NUM (1 << LEVEL_STRIDE)
|
||||
#define agaw_to_level(val) ((val) + 2)
|
||||
#define agaw_to_width(val) (30 + val * LEVEL_STRIDE)
|
||||
#define width_to_agaw(w) ((w - 30)/LEVEL_STRIDE)
|
||||
|
||||