[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [xen staging] x86/mm: use cache in guest_walk_tables()
commit 8e85181cf88f83c4ca289f7273863179b1c20611 Author: Jan Beulich <jbeulich@xxxxxxxx> AuthorDate: Thu Apr 23 09:58:04 2020 +0200 Commit: Jan Beulich <jbeulich@xxxxxxxx> CommitDate: Thu Apr 23 09:58:04 2020 +0200 x86/mm: use cache in guest_walk_tables() Emulation requiring device model assistance uses a form of instruction re-execution, assuming that the second (and any further) pass takes exactly the same path. This is a valid assumption as far as use of CPU registers goes (as those can't change without any other instruction executing in between [1]), but is wrong for memory accesses. In particular it has been observed that Windows might page out buffers underneath an instruction currently under emulation (hitting between two passes). If the first pass translated a linear address successfully, any subsequent pass needs to do so too, yielding the exact same translation. To guarantee this, leverage the caching that now backs HVM insn emulation. [1] Other than on actual hardware, actions like XEN_DOMCTL_sethvmcontext, XEN_DOMCTL_setvcpucontext, VCPUOP_initialise, INIT, or SIPI issued against the vCPU can occur while the vCPU is blocked waiting for a device model to return data. In such cases emulation now gets canceled, though, and hence re- execution correctness is unaffected. Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> Reviewed-by: Paul Durrant <pdurrant@xxxxxxxx> --- xen/arch/x86/hvm/emulate.c | 4 +-- xen/arch/x86/mm/guest_walk.c | 73 +++++++++++++++++++++++++++++++++------ xen/arch/x86/mm/hap/guest_walk.c | 3 +- xen/arch/x86/mm/shadow/multi.c | 8 +++-- xen/include/asm-x86/guest_pt.h | 5 +-- xen/include/asm-x86/hvm/emulate.h | 6 ++++ 6 files changed, 81 insertions(+), 18 deletions(-) diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index 6b3cbc7e50..e87ceaf23e 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -2940,7 +2940,7 @@ bool hvmemul_read_cache(const struct vcpu *v, paddr_t gpa, unsigned int i; /* Cache unavailable? */ - if ( cache->num_ents > cache->max_ents ) + if ( !is_hvm_vcpu(v) || cache->num_ents > cache->max_ents ) return false; while ( size > sizeof(cache->ents->data) ) @@ -2972,7 +2972,7 @@ void hvmemul_write_cache(const struct vcpu *v, paddr_t gpa, unsigned int i; /* Cache unavailable? */ - if ( cache->num_ents > cache->max_ents ) + if ( !is_hvm_vcpu(v) || cache->num_ents > cache->max_ents ) return; while ( size > sizeof(cache->ents->data) ) diff --git a/xen/arch/x86/mm/guest_walk.c b/xen/arch/x86/mm/guest_walk.c index ab7021a1ce..1c601314f3 100644 --- a/xen/arch/x86/mm/guest_walk.c +++ b/xen/arch/x86/mm/guest_walk.c @@ -31,6 +31,7 @@ asm(".file \"" __OBJECT_FILE__ "\""); #include <xen/sched.h> #include <asm/page.h> #include <asm/guest_pt.h> +#include <asm/hvm/emulate.h> /* * Modify a guest pagetable entry to set the Accessed and Dirty bits. @@ -80,9 +81,9 @@ static bool set_ad_bits(guest_intpte_t *guest_p, guest_intpte_t *walk_p, * requested walk, to see whether the access is permitted. */ bool -guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, - unsigned long va, walk_t *gw, - uint32_t walk, mfn_t top_mfn, void *top_map) +guest_walk_tables(const struct vcpu *v, struct p2m_domain *p2m, + unsigned long va, walk_t *gw, uint32_t walk, + gfn_t top_gfn, mfn_t top_mfn, void *top_map) { struct domain *d = v->domain; guest_l1e_t *l1p = NULL; @@ -90,8 +91,13 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, #if GUEST_PAGING_LEVELS >= 4 /* 64-bit only... */ guest_l3e_t *l3p = NULL; guest_l4e_t *l4p; + paddr_t l4gpa; +#endif +#if GUEST_PAGING_LEVELS >= 3 /* PAE or 64... */ + paddr_t l3gpa; #endif uint32_t gflags, rc; + paddr_t l1gpa = 0, l2gpa = 0; unsigned int leaf_level; p2m_query_t qt = P2M_ALLOC | P2M_UNSHARE; @@ -132,7 +138,13 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, /* Get the l4e from the top level table and check its flags*/ gw->l4mfn = top_mfn; l4p = (guest_l4e_t *) top_map; - gw->l4e = l4p[guest_l4_table_offset(va)]; + l4gpa = gfn_to_gaddr(top_gfn) + + guest_l4_table_offset(va) * sizeof(gw->l4e); + if ( !hvmemul_read_cache(v, l4gpa, &gw->l4e, sizeof(gw->l4e)) ) + { + gw->l4e = l4p[guest_l4_table_offset(va)]; + hvmemul_write_cache(v, l4gpa, &gw->l4e, sizeof(gw->l4e)); + } gflags = guest_l4e_get_flags(gw->l4e); if ( !(gflags & _PAGE_PRESENT) ) goto out; @@ -161,7 +173,13 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, } /* Get the l3e and check its flags*/ - gw->l3e = l3p[guest_l3_table_offset(va)]; + l3gpa = gfn_to_gaddr(guest_l4e_get_gfn(gw->l4e)) + + guest_l3_table_offset(va) * sizeof(gw->l3e); + if ( !hvmemul_read_cache(v, l3gpa, &gw->l3e, sizeof(gw->l3e)) ) + { + gw->l3e = l3p[guest_l3_table_offset(va)]; + hvmemul_write_cache(v, l3gpa, &gw->l3e, sizeof(gw->l3e)); + } gflags = guest_l3e_get_flags(gw->l3e); if ( !(gflags & _PAGE_PRESENT) ) goto out; @@ -213,7 +231,14 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, #else /* PAE only... */ /* Get the l3e and check its flag */ - gw->l3e = ((guest_l3e_t *) top_map)[guest_l3_table_offset(va)]; + l3gpa = gfn_to_gaddr(top_gfn) + ((unsigned long)top_map & ~PAGE_MASK) + + guest_l3_table_offset(va) * sizeof(gw->l3e); + if ( !hvmemul_read_cache(v, l3gpa, &gw->l3e, sizeof(gw->l3e)) ) + { + gw->l3e = ((guest_l3e_t *)top_map)[guest_l3_table_offset(va)]; + hvmemul_write_cache(v, l3gpa, &gw->l3e, sizeof(gw->l3e)); + } + gflags = guest_l3e_get_flags(gw->l3e); if ( !(gflags & _PAGE_PRESENT) ) goto out; @@ -238,18 +263,24 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, goto out; } - /* Get the l2e */ - gw->l2e = l2p[guest_l2_table_offset(va)]; + l2gpa = gfn_to_gaddr(guest_l3e_get_gfn(gw->l3e)); #else /* 32-bit only... */ - /* Get l2e from the top level table */ gw->l2mfn = top_mfn; l2p = (guest_l2e_t *) top_map; - gw->l2e = l2p[guest_l2_table_offset(va)]; + l2gpa = gfn_to_gaddr(top_gfn); #endif /* All levels... */ + /* Get the l2e */ + l2gpa += guest_l2_table_offset(va) * sizeof(gw->l2e); + if ( !hvmemul_read_cache(v, l2gpa, &gw->l2e, sizeof(gw->l2e)) ) + { + gw->l2e = l2p[guest_l2_table_offset(va)]; + hvmemul_write_cache(v, l2gpa, &gw->l2e, sizeof(gw->l2e)); + } + /* Check the l2e flags. */ gflags = guest_l2e_get_flags(gw->l2e); if ( !(gflags & _PAGE_PRESENT) ) @@ -330,7 +361,15 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, gw->pfec |= rc & PFEC_synth_mask; goto out; } - gw->l1e = l1p[guest_l1_table_offset(va)]; + + l1gpa = gfn_to_gaddr(guest_l2e_get_gfn(gw->l2e)) + + guest_l1_table_offset(va) * sizeof(gw->l1e); + if ( !hvmemul_read_cache(v, l1gpa, &gw->l1e, sizeof(gw->l1e)) ) + { + gw->l1e = l1p[guest_l1_table_offset(va)]; + hvmemul_write_cache(v, l1gpa, &gw->l1e, sizeof(gw->l1e)); + } + gflags = guest_l1e_get_flags(gw->l1e); if ( !(gflags & _PAGE_PRESENT) ) goto out; @@ -441,22 +480,34 @@ guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, case 1: if ( set_ad_bits(&l1p[guest_l1_table_offset(va)].l1, &gw->l1e.l1, (walk & PFEC_write_access)) ) + { paging_mark_dirty(d, gw->l1mfn); + hvmemul_write_cache(v, l1gpa, &gw->l1e, sizeof(gw->l1e)); + } /* Fallthrough */ case 2: if ( set_ad_bits(&l2p[guest_l2_table_offset(va)].l2, &gw->l2e.l2, (walk & PFEC_write_access) && leaf_level == 2) ) + { paging_mark_dirty(d, gw->l2mfn); + hvmemul_write_cache(v, l2gpa, &gw->l2e, sizeof(gw->l2e)); + } /* Fallthrough */ #if GUEST_PAGING_LEVELS == 4 /* 64-bit only... */ case 3: if ( set_ad_bits(&l3p[guest_l3_table_offset(va)].l3, &gw->l3e.l3, (walk & PFEC_write_access) && leaf_level == 3) ) + { paging_mark_dirty(d, gw->l3mfn); + hvmemul_write_cache(v, l3gpa, &gw->l3e, sizeof(gw->l3e)); + } if ( set_ad_bits(&l4p[guest_l4_table_offset(va)].l4, &gw->l4e.l4, false) ) + { paging_mark_dirty(d, gw->l4mfn); + hvmemul_write_cache(v, l4gpa, &gw->l4e, sizeof(gw->l4e)); + } #endif } diff --git a/xen/arch/x86/mm/hap/guest_walk.c b/xen/arch/x86/mm/hap/guest_walk.c index 3b8ee2efce..6001cf3330 100644 --- a/xen/arch/x86/mm/hap/guest_walk.c +++ b/xen/arch/x86/mm/hap/guest_walk.c @@ -91,7 +91,8 @@ unsigned long hap_p2m_ga_to_gfn(GUEST_PAGING_LEVELS)( #if GUEST_PAGING_LEVELS == 3 top_map += (cr3 & ~(PAGE_MASK | 31)); #endif - walk_ok = guest_walk_tables(v, p2m, ga, &gw, *pfec, top_mfn, top_map); + walk_ok = guest_walk_tables(v, p2m, ga, &gw, *pfec, + top_gfn, top_mfn, top_map); unmap_domain_page(top_map); put_page(top_page); diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index bd88852b64..5368adf474 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -175,9 +175,13 @@ static inline bool sh_walk_guest_tables(struct vcpu *v, unsigned long va, walk_t *gw, uint32_t pfec) { + gfn_t root_gfn = _gfn(paging_mode_external(v->domain) + ? cr3_pa(v->arch.hvm.guest_cr[3]) >> PAGE_SHIFT + : pagetable_get_pfn(v->arch.guest_table)); + #if GUEST_PAGING_LEVELS == 3 /* PAE */ return guest_walk_tables(v, p2m_get_hostp2m(v->domain), va, gw, pfec, - INVALID_MFN, v->arch.paging.shadow.gl3e); + root_gfn, INVALID_MFN, v->arch.paging.shadow.gl3e); #else /* 32 or 64 */ const struct domain *d = v->domain; mfn_t root_mfn = (v->arch.flags & TF_kernel_mode @@ -185,7 +189,7 @@ sh_walk_guest_tables(struct vcpu *v, unsigned long va, walk_t *gw, : pagetable_get_mfn(v->arch.guest_table_user)); void *root_map = map_domain_page(root_mfn); bool ok = guest_walk_tables(v, p2m_get_hostp2m(d), va, gw, pfec, - root_mfn, root_map); + root_gfn, root_mfn, root_map); unmap_domain_page(root_map); diff --git a/xen/include/asm-x86/guest_pt.h b/xen/include/asm-x86/guest_pt.h index 6ab2041e48..6647ccfb85 100644 --- a/xen/include/asm-x86/guest_pt.h +++ b/xen/include/asm-x86/guest_pt.h @@ -428,8 +428,9 @@ static inline unsigned int guest_walk_to_page_order(const walk_t *gw) #define guest_walk_tables GPT_RENAME(guest_walk_tables, GUEST_PAGING_LEVELS) bool -guest_walk_tables(struct vcpu *v, struct p2m_domain *p2m, unsigned long va, - walk_t *gw, uint32_t pfec, mfn_t top_mfn, void *top_map); +guest_walk_tables(const struct vcpu *v, struct p2m_domain *p2m, + unsigned long va, walk_t *gw, uint32_t pfec, + gfn_t top_gfn, mfn_t top_mfn, void *top_map); /* Pretty-print the contents of a guest-walk */ static inline void print_gw(const walk_t *gw) diff --git a/xen/include/asm-x86/hvm/emulate.h b/xen/include/asm-x86/hvm/emulate.h index 148bab93ce..f40290945c 100644 --- a/xen/include/asm-x86/hvm/emulate.h +++ b/xen/include/asm-x86/hvm/emulate.h @@ -130,6 +130,12 @@ static inline bool hvmemul_cache_disabled(struct vcpu *v) { return hvmemul_cache_disable(v) == hvmemul_cache_disable(v); } +#else +static inline bool hvmemul_read_cache(const struct vcpu *v, paddr_t gpa, + void *buf, + unsigned int size) { return false; } +static inline void hvmemul_write_cache(const struct vcpu *v, paddr_t gpa, + const void *buf, unsigned int size) {} #endif void hvm_dump_emulation_state(const char *loglvl, const char *prefix, -- generated by git-patchbot for /home/xen/git/xen.git#staging
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |