[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH v5 3/4] 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> --- v5: Move hvmemul_{read,write}_cache() stubs here. v4: Adjust for cache now (elsewhere) being transparent to callers. Provide inline stubs for the !HVM case. v2: Don't wrongly use top_gfn for non-root gpa calculation. Re-write cache entries after setting A/D bits (an alternative would be to suppress their setting upon cache hits). --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -2940,7 +2940,7 @@ bool hvmemul_read_cache(const struct vcp 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 vc 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) ) --- 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 * * 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 #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 /* 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 } /* 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 #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 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 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 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 } --- 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_PA #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); --- 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, uns : 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); --- 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 #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) --- a/xen/include/asm-x86/hvm/emulate.h +++ b/xen/include/asm-x86/hvm/emulate.h @@ -129,6 +129,12 @@ static inline bool hvmemul_cache_disable { 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, _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxxx https://lists.xenproject.org/mailman/listinfo/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |