160 lines
6.3 KiB
Diff
160 lines
6.3 KiB
Diff
|
# Commit 660fd65d5578a95ec5eac522128bba23325179eb
|
||
|
# Date 2015-10-02 13:40:36 +0200
|
||
|
# Author Jan Beulich <jbeulich@suse.com>
|
||
|
# Committer Jan Beulich <jbeulich@suse.com>
|
||
|
x86/p2m-pt: tighten conditions of IOMMU mapping updates
|
||
|
|
||
|
Whether the MFN changes does not depend on the new entry being valid
|
||
|
(but solely on the old one), and the need to update or TLB-flush also
|
||
|
depends on permission changes.
|
||
|
|
||
|
Signed-off-by: Jan Beulich <jbeulich@suse.com>
|
||
|
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
|
||
|
Reviewed-by: George Dunlap <george.dunlap@citrix.com>
|
||
|
|
||
|
--- a/xen/arch/x86/mm/p2m-pt.c
|
||
|
+++ b/xen/arch/x86/mm/p2m-pt.c
|
||
|
@@ -493,7 +493,18 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
l3_pgentry_t l3e_content;
|
||
|
int rc;
|
||
|
unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt);
|
||
|
- unsigned long old_mfn = 0;
|
||
|
+ /*
|
||
|
+ * old_mfn and iommu_old_flags control possible flush/update needs on the
|
||
|
+ * IOMMU: We need to flush when MFN or flags (i.e. permissions) change.
|
||
|
+ * iommu_old_flags being initialized to zero covers the case of the entry
|
||
|
+ * getting replaced being a non-present (leaf or intermediate) one. For
|
||
|
+ * present leaf entries the real value will get calculated below, while
|
||
|
+ * for present intermediate entries ~0 (guaranteed != iommu_pte_flags)
|
||
|
+ * will be used (to cover all cases of what the leaf entries underneath
|
||
|
+ * the intermediate one might be).
|
||
|
+ */
|
||
|
+ unsigned int flags, iommu_old_flags = 0;
|
||
|
+ unsigned long old_mfn = INVALID_MFN;
|
||
|
|
||
|
if ( tb_init_done )
|
||
|
{
|
||
|
@@ -540,12 +551,20 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
L3_PAGETABLE_SHIFT - PAGE_SHIFT,
|
||
|
L3_PAGETABLE_ENTRIES);
|
||
|
ASSERT(p2m_entry);
|
||
|
- if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) &&
|
||
|
- !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) )
|
||
|
+ flags = l1e_get_flags(*p2m_entry);
|
||
|
+ if ( flags & _PAGE_PRESENT )
|
||
|
{
|
||
|
- /* We're replacing a non-SP page with a superpage. Make sure to
|
||
|
- * handle freeing the table properly. */
|
||
|
- intermediate_entry = *p2m_entry;
|
||
|
+ if ( flags & _PAGE_PSE )
|
||
|
+ {
|
||
|
+ iommu_old_flags =
|
||
|
+ p2m_get_iommu_flags(p2m_flags_to_type(flags));
|
||
|
+ old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ iommu_old_flags = ~0;
|
||
|
+ intermediate_entry = *p2m_entry;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct);
|
||
|
@@ -556,10 +575,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
entry_content.l1 = l3e_content.l3;
|
||
|
|
||
|
if ( entry_content.l1 != 0 )
|
||
|
- {
|
||
|
p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags);
|
||
|
- old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
- }
|
||
|
|
||
|
p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content, 3);
|
||
|
/* NB: paging_write_p2m_entry() handles tlb flushes properly */
|
||
|
@@ -584,7 +600,10 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
p2m_entry = p2m_find_entry(table, &gfn_remainder, gfn,
|
||
|
0, L1_PAGETABLE_ENTRIES);
|
||
|
ASSERT(p2m_entry);
|
||
|
-
|
||
|
+ iommu_old_flags =
|
||
|
+ p2m_get_iommu_flags(p2m_flags_to_type(l1e_get_flags(*p2m_entry)));
|
||
|
+ old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
+
|
||
|
if ( mfn_valid(mfn) || (p2mt == p2m_mmio_direct)
|
||
|
|| p2m_is_paging(p2mt) )
|
||
|
entry_content = p2m_l1e_from_pfn(mfn_x(mfn),
|
||
|
@@ -593,10 +612,8 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
entry_content = l1e_empty();
|
||
|
|
||
|
if ( entry_content.l1 != 0 )
|
||
|
- {
|
||
|
p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags);
|
||
|
- old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
- }
|
||
|
+
|
||
|
/* level 1 entry */
|
||
|
p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content, 1);
|
||
|
/* NB: paging_write_p2m_entry() handles tlb flushes properly */
|
||
|
@@ -607,14 +624,20 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
L2_PAGETABLE_SHIFT - PAGE_SHIFT,
|
||
|
L2_PAGETABLE_ENTRIES);
|
||
|
ASSERT(p2m_entry);
|
||
|
-
|
||
|
- /* FIXME: Deal with 4k replaced by 2meg pages */
|
||
|
- if ( (l1e_get_flags(*p2m_entry) & _PAGE_PRESENT) &&
|
||
|
- !(l1e_get_flags(*p2m_entry) & _PAGE_PSE) )
|
||
|
- {
|
||
|
- /* We're replacing a non-SP page with a superpage. Make sure to
|
||
|
- * handle freeing the table properly. */
|
||
|
- intermediate_entry = *p2m_entry;
|
||
|
+ flags = l1e_get_flags(*p2m_entry);
|
||
|
+ if ( flags & _PAGE_PRESENT )
|
||
|
+ {
|
||
|
+ if ( flags & _PAGE_PSE )
|
||
|
+ {
|
||
|
+ iommu_old_flags =
|
||
|
+ p2m_get_iommu_flags(p2m_flags_to_type(flags));
|
||
|
+ old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
+ }
|
||
|
+ else
|
||
|
+ {
|
||
|
+ iommu_old_flags = ~0;
|
||
|
+ intermediate_entry = *p2m_entry;
|
||
|
+ }
|
||
|
}
|
||
|
|
||
|
ASSERT(!mfn_valid(mfn) || p2mt != p2m_mmio_direct);
|
||
|
@@ -628,10 +651,7 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
entry_content.l1 = l2e_content.l2;
|
||
|
|
||
|
if ( entry_content.l1 != 0 )
|
||
|
- {
|
||
|
p2m_add_iommu_flags(&entry_content, 0, iommu_pte_flags);
|
||
|
- old_mfn = l1e_get_pfn(*p2m_entry);
|
||
|
- }
|
||
|
|
||
|
p2m->write_p2m_entry(p2m, gfn, p2m_entry, entry_content, 2);
|
||
|
/* NB: paging_write_p2m_entry() handles tlb flushes properly */
|
||
|
@@ -642,17 +662,17 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
|
||
|
&& (gfn + (1UL << page_order) - 1 > p2m->max_mapped_pfn) )
|
||
|
p2m->max_mapped_pfn = gfn + (1UL << page_order) - 1;
|
||
|
|
||
|
- if ( iommu_enabled && need_iommu(p2m->domain) )
|
||
|
+ if ( iommu_enabled && need_iommu(p2m->domain) &&
|
||
|
+ (iommu_old_flags != iommu_pte_flags || old_mfn != mfn_x(mfn)) )
|
||
|
{
|
||
|
if ( iommu_use_hap_pt(p2m->domain) )
|
||
|
{
|
||
|
- if ( old_mfn && (old_mfn != mfn_x(mfn)) )
|
||
|
+ if ( iommu_old_flags )
|
||
|
amd_iommu_flush_pages(p2m->domain, gfn, page_order);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
- unsigned int flags = p2m_get_iommu_flags(p2mt);
|
||
|
-
|
||
|
+ flags = p2m_get_iommu_flags(p2mt);
|
||
|
if ( flags != 0 )
|
||
|
for ( i = 0; i < (1UL << page_order); i++ )
|
||
|
iommu_map_page(p2m->domain, gfn+i, mfn_x(mfn)+i, flags);
|