# HG changeset patch
# User Keir Fraser <keir.fraser@xxxxxxxxxx>
# Date 1210148380 -3600
# Node ID fb58da5167497ce8752f99673149911909f0f269
# Parent 9d2a45d4b6c6ecf6d99f01dd035326d2215c548c
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@xxxxxxxxx>
---
xen/drivers/passthrough/vtd/iommu.c | 62 +++++++++++++++++++++++++++++++-----
xen/drivers/passthrough/vtd/iommu.h | 1
2 files changed, 55 insertions(+), 8 deletions(-)
diff -r 9d2a45d4b6c6 -r fb58da516749 xen/drivers/passthrough/vtd/iommu.c
--- a/xen/drivers/passthrough/vtd/iommu.c Wed May 07 09:17:52 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.c Wed May 07 09:19:40 2008 +0100
@@ -678,17 +678,63 @@ void dma_pte_free_pagetable(struct domai
}
}
- /* 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;
-
- start = 0;
- end = (((u64)1) << addr_width) - 1;
-
- dma_pte_free_pagetable(domain, start, end);
+ int total_level = agaw_to_level(hd->agaw);
+
+ if ( hd->pgd_maddr != 0 )
+ {
+ index = 0;
+ do
+ {
+ iommu_free_next_pagetable(hd->pgd_maddr,
+ index, total_level + 1);
+ index++;
+ } while ( index < PTE_NUM );
+
+ free_pgtable_maddr(hd->pgd_maddr);
+ hd->pgd_maddr = 0;
+ }
}
static int iommu_set_root_entry(struct iommu *iommu)
diff -r 9d2a45d4b6c6 -r fb58da516749 xen/drivers/passthrough/vtd/iommu.h
--- a/xen/drivers/passthrough/vtd/iommu.h Wed May 07 09:17:52 2008 +0100
+++ b/xen/drivers/passthrough/vtd/iommu.h Wed May 07 09:19:40 2008 +0100
@@ -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)
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog
|