mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
[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>
This commit is contained in:
1 parent
a561aacab7
commit
6b73fb435b
5 files changed
+101
-25
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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__ */
|
||||