diff options
author | Tim Deegan <Tim.Deegan@xensource.com> | 2007-06-01 14:32:11 +0100 |
---|---|---|
committer | Tim Deegan <Tim.Deegan@xensource.com> | 2007-06-01 14:32:11 +0100 |
commit | 6b73fb435b2da80d8febd3e626b0027e5509989a (patch) | |
tree | 2e3fd55dad9cea9473dc49e316a4105f39f293fa | |
parent | a561aacab7e9ef5aeab2173d23ad06ff45e36173 (diff) | |
download | xen-6b73fb435b2da80d8febd3e626b0027e5509989a.tar.gz xen-6b73fb435b2da80d8febd3e626b0027e5509989a.tar.bz2 xen-6b73fb435b2da80d8febd3e626b0027e5509989a.zip |
[XEN] Shadow: emulate a few extra instructions on PAE pagetable writes
in the hope of catching the "other half" write without another enter/exit.
Signed-off-by: Tim Deegan <Tim.Deegan@xensource.com>
-rw-r--r-- | xen/arch/x86/mm/shadow/common.c | 32 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/multi.c | 82 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/private.h | 7 | ||||
-rw-r--r-- | xen/include/asm-x86/domain.h | 2 | ||||
-rw-r--r-- | xen/include/asm-x86/perfc_defn.h | 3 |
5 files changed, 101 insertions, 25 deletions
diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index a4598e4bc2..2690acbeb2 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -248,7 +248,7 @@ hvm_emulate_insn_fetch(enum x86_segment seg, { struct sh_emulate_ctxt *sh_ctxt = container_of(ctxt, struct sh_emulate_ctxt, ctxt); - unsigned int insn_off = offset - ctxt->regs->eip; + unsigned int insn_off = offset - sh_ctxt->insn_buf_eip; /* Fall back if requested bytes are not in the prefetch cache. */ if ( unlikely((insn_off + bytes) > sh_ctxt->insn_buf_bytes) ) @@ -450,6 +450,7 @@ struct x86_emulate_ops *shadow_init_emulation( } /* Attempt to prefetch whole instruction. */ + sh_ctxt->insn_buf_eip = regs->eip; sh_ctxt->insn_buf_bytes = (!hvm_translate_linear_addr( x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf), @@ -461,6 +462,35 @@ struct x86_emulate_ops *shadow_init_emulation( return &hvm_shadow_emulator_ops; } +/* Update an initialized emulation context to prepare for the next + * instruction */ +void shadow_continue_emulation(struct sh_emulate_ctxt *sh_ctxt, + struct cpu_user_regs *regs) +{ + struct vcpu *v = current; + unsigned long addr, diff; + + /* We don't refetch the segment bases, because we don't emulate + * writes to segment registers */ + + if ( is_hvm_vcpu(v) ) + { + diff = regs->eip - sh_ctxt->insn_buf_eip; + if ( diff > sh_ctxt->insn_buf_bytes ) + { + /* Prefetch more bytes. */ + sh_ctxt->insn_buf_bytes = + (!hvm_translate_linear_addr( + x86_seg_cs, regs->eip, sizeof(sh_ctxt->insn_buf), + hvm_access_insn_fetch, sh_ctxt, &addr) && + !hvm_copy_from_guest_virt( + sh_ctxt->insn_buf, addr, sizeof(sh_ctxt->insn_buf))) + ? sizeof(sh_ctxt->insn_buf) : 0; + sh_ctxt->insn_buf_eip = regs->eip; + } + } +} + /**************************************************************************/ /* Code for "promoting" a guest page to the point where the shadow code is * willing to let it be treated as a guest page table. This generally diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index cfd4c70c21..c5fd34a3ca 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -2871,6 +2871,20 @@ static int sh_page_fault(struct vcpu *v, if ( !shadow_mode_refcounts(d) || !guest_mode(regs) ) goto not_a_shadow_fault; + /* + * We do not emulate user writes. Instead we use them as a hint that the + * page is no longer a page table. This behaviour differs from native, but + * it seems very unlikely that any OS grants user access to page tables. + */ + if ( (regs->error_code & PFEC_user_mode) ) + { + SHADOW_PRINTK("user-mode fault to PT, unshadowing mfn %#lx\n", + mfn_x(gmfn)); + perfc_incr(shadow_fault_emulate_failed); + sh_remove_shadows(v, gmfn, 0 /* thorough */, 1 /* must succeed */); + goto done; + } + if ( is_hvm_domain(d) ) { /* @@ -2897,14 +2911,7 @@ static int sh_page_fault(struct vcpu *v, emul_ops = shadow_init_emulation(&emul_ctxt, regs); - /* - * We do not emulate user writes. Instead we use them as a hint that the - * page is no longer a page table. This behaviour differs from native, but - * it seems very unlikely that any OS grants user access to page tables. - */ - r = X86EMUL_UNHANDLEABLE; - if ( !(regs->error_code & PFEC_user_mode) ) - r = x86_emulate(&emul_ctxt.ctxt, emul_ops); + r = x86_emulate(&emul_ctxt.ctxt, emul_ops); /* * NB. We do not unshadow on X86EMUL_EXCEPTION. It's not clear that it @@ -2922,6 +2929,35 @@ static int sh_page_fault(struct vcpu *v, sh_remove_shadows(v, gmfn, 0 /* thorough */, 1 /* must succeed */); } +#if GUEST_PAGING_LEVELS == 3 /* PAE guest */ + if ( r == X86EMUL_OKAY ) { + int i; + /* Emulate up to four extra instructions in the hope of catching + * the "second half" of a 64-bit pagetable write. */ + for ( i = 0 ; i < 4 ; i++ ) + { + shadow_continue_emulation(&emul_ctxt, regs); + v->arch.paging.last_write_was_pt = 0; + r = x86_emulate(&emul_ctxt.ctxt, emul_ops); + if ( r == X86EMUL_OKAY ) + { + if ( v->arch.paging.last_write_was_pt ) + { + perfc_incr(shadow_em_ex_pt); + break; /* Don't emulate past the other half of the write */ + } + else + perfc_incr(shadow_em_ex_non_pt); + } + else + { + perfc_incr(shadow_em_ex_fail); + break; /* Don't emulate again if we failed! */ + } + } + } +#endif /* PAE guest */ + /* Emulator has changed the user registers: write back */ if ( is_hvm_domain(d) ) hvm_load_cpu_guest_regs(v, regs); @@ -3878,6 +3914,11 @@ static inline void * emulate_map_dest(struct vcpu *v, gfn_t gfn; mfn_t mfn; + /* We don't emulate user-mode writes to page tables */ + if ( ring_3(sh_ctxt->ctxt.regs) ) + return NULL; + + /* Walk the guest pagetables */ guest_walk_tables(v, vaddr, &gw, 1); flags = accumulate_guest_flags(v, &gw); gfn = guest_l1e_get_gfn(gw.eff_l1e); @@ -3885,27 +3926,24 @@ static inline void * emulate_map_dest(struct vcpu *v, sh_audit_gw(v, &gw); unmap_walk(v, &gw); - if ( !(flags & _PAGE_PRESENT) ) - { - errcode = 0; + errcode = PFEC_write_access; + if ( !(flags & _PAGE_PRESENT) ) goto page_fault; - } - if ( !(flags & _PAGE_RW) || - (!(flags & _PAGE_USER) && ring_3(sh_ctxt->ctxt.regs)) ) - { - errcode = PFEC_page_present; + errcode |= PFEC_page_present; + if ( !(flags & _PAGE_RW) ) goto page_fault; - } - if ( !mfn_valid(mfn) ) + if ( mfn_valid(mfn) ) + { + *mfnp = mfn; + v->arch.paging.last_write_was_pt = !!sh_mfn_is_a_page_table(mfn); + return sh_map_domain_page(mfn) + (vaddr & ~PAGE_MASK); + } + else return NULL; - *mfnp = mfn; - return sh_map_domain_page(mfn) + (vaddr & ~PAGE_MASK); - page_fault: - errcode |= PFEC_write_access; if ( is_hvm_vcpu(v) ) hvm_inject_exception(TRAP_page_fault, errcode, vaddr); else diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h index 70c7c34010..52ea6d5d16 100644 --- a/xen/arch/x86/mm/shadow/private.h +++ b/xen/arch/x86/mm/shadow/private.h @@ -634,9 +634,10 @@ static inline void sh_unpin(struct vcpu *v, mfn_t smfn) struct sh_emulate_ctxt { struct x86_emulate_ctxt ctxt; - /* [HVM] Cache of up to 15 bytes of instruction. */ - uint8_t insn_buf[15]; + /* [HVM] Cache of up to 31 bytes of instruction. */ + uint8_t insn_buf[31]; uint8_t insn_buf_bytes; + unsigned long insn_buf_eip; /* [HVM] Cache of segment registers already gathered for this emulation. */ unsigned int valid_seg_regs; @@ -645,6 +646,8 @@ struct sh_emulate_ctxt { struct x86_emulate_ops *shadow_init_emulation( struct sh_emulate_ctxt *sh_ctxt, struct cpu_user_regs *regs); +void shadow_continue_emulation( + struct sh_emulate_ctxt *sh_ctxt, struct cpu_user_regs *regs); #endif /* _XEN_SHADOW_PRIVATE_H */ diff --git a/xen/include/asm-x86/domain.h b/xen/include/asm-x86/domain.h index 6bb8497319..30c7601d1a 100644 --- a/xen/include/asm-x86/domain.h +++ b/xen/include/asm-x86/domain.h @@ -171,6 +171,8 @@ struct paging_vcpu { struct paging_mode *mode; /* HVM guest: paging enabled (CR0.PG)? */ unsigned int translate_enabled:1; + /* HVM guest: last emulate was to a pagetable */ + unsigned int last_write_was_pt:1; /* paging support extension */ struct shadow_vcpu shadow; diff --git a/xen/include/asm-x86/perfc_defn.h b/xen/include/asm-x86/perfc_defn.h index 053e897774..56bb30331a 100644 --- a/xen/include/asm-x86/perfc_defn.h +++ b/xen/include/asm-x86/perfc_defn.h @@ -90,5 +90,8 @@ PERFCOUNTER(shadow_guest_walk, "shadow walks guest tables") PERFCOUNTER(shadow_invlpg, "shadow emulates invlpg") PERFCOUNTER(shadow_invlpg_fault, "shadow invlpg faults") +PERFCOUNTER(shadow_em_ex_pt, "shadow extra pt write") +PERFCOUNTER(shadow_em_ex_non_pt, "shadow extra non-pt-write op") +PERFCOUNTER(shadow_em_ex_fail, "shadow extra emulation failed") /*#endif*/ /* __XEN_PERFC_DEFN_H__ */ |