diff options
-rw-r--r-- | tools/tests/test_x86_emulator.c | 6 | ||||
-rw-r--r-- | xen/arch/x86/mm.c | 9 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/common.c | 143 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/multi.c | 12 | ||||
-rw-r--r-- | xen/arch/x86/mm/shadow/private.h | 6 | ||||
-rw-r--r-- | xen/arch/x86/x86_emulate.c | 76 | ||||
-rw-r--r-- | xen/include/asm-x86/x86_emulate.h | 14 |
7 files changed, 187 insertions, 79 deletions
diff --git a/tools/tests/test_x86_emulator.c b/tools/tests/test_x86_emulator.c index e22e342ce2..64797d3f81 100644 --- a/tools/tests/test_x86_emulator.c +++ b/tools/tests/test_x86_emulator.c @@ -88,7 +88,11 @@ static int cmpxchg8b( } static struct x86_emulate_ops emulops = { - read, write, cmpxchg, cmpxchg8b + .read = read, + .insn_fetch = read, + .write = write, + .cmpxchg = cmpxchg, + .cmpxchg8b = cmpxchg8b }; int main(int argc, char **argv) diff --git a/xen/arch/x86/mm.c b/xen/arch/x86/mm.c index 47fb500fc6..48dea7cffb 100644 --- a/xen/arch/x86/mm.c +++ b/xen/arch/x86/mm.c @@ -3224,10 +3224,11 @@ static int ptwr_emulated_cmpxchg8b( } static struct x86_emulate_ops ptwr_emulate_ops = { - .read = ptwr_emulated_read, - .write = ptwr_emulated_write, - .cmpxchg = ptwr_emulated_cmpxchg, - .cmpxchg8b = ptwr_emulated_cmpxchg8b + .read = ptwr_emulated_read, + .insn_fetch = ptwr_emulated_read, + .write = ptwr_emulated_write, + .cmpxchg = ptwr_emulated_cmpxchg, + .cmpxchg8b = ptwr_emulated_cmpxchg8b }; /* Write page fault handler: check if guest is trying to modify a PTE. */ diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index 5b6965a7f1..a109716c9f 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -78,43 +78,54 @@ struct segment_register *hvm_get_seg_reg( return seg_reg; } +enum hvm_access_type { + hvm_access_insn_fetch, hvm_access_read, hvm_access_write +}; + static int hvm_translate_linear_addr( enum x86_segment seg, unsigned long offset, unsigned int bytes, - unsigned int is_write, + enum hvm_access_type access_type, struct sh_emulate_ctxt *sh_ctxt, unsigned long *paddr) { - struct segment_register *creg, *dreg; + struct segment_register *reg = hvm_get_seg_reg(seg, sh_ctxt); unsigned long limit, addr = offset; uint32_t last_byte; - creg = hvm_get_seg_reg(x86_seg_cs, sh_ctxt); - dreg = hvm_get_seg_reg(seg, sh_ctxt); - - if ( !creg->attr.fields.l || !hvm_long_mode_enabled(current) ) + if ( sh_ctxt->ctxt.mode != X86EMUL_MODE_PROT64 ) { /* * COMPATIBILITY MODE: Apply segment checks and add base. */ - /* If this is a store, is the segment a writable data segment? */ - if ( is_write && ((dreg->attr.fields.type & 0xa) != 0x2) ) - goto gpf; + switch ( access_type ) + { + case hvm_access_read: + if ( (reg->attr.fields.type & 0xa) == 0x8 ) + goto gpf; /* execute-only code segment */ + break; + case hvm_access_write: + if ( (reg->attr.fields.type & 0xa) != 0x2 ) + goto gpf; /* not a writable data segment */ + break; + default: + break; + } /* Calculate the segment limit, including granularity flag. */ - limit = dreg->limit; - if ( dreg->attr.fields.g ) + limit = reg->limit; + if ( reg->attr.fields.g ) limit = (limit << 12) | 0xfff; last_byte = offset + bytes - 1; /* Is this a grows-down data segment? Special limit check if so. */ - if ( (dreg->attr.fields.type & 0xc) == 0x4 ) + if ( (reg->attr.fields.type & 0xc) == 0x4 ) { /* Is upper limit 0xFFFF or 0xFFFFFFFF? */ - if ( !dreg->attr.fields.db ) + if ( !reg->attr.fields.db ) last_byte = (uint16_t)last_byte; /* Check first byte and last byte against respective bounds. */ @@ -128,7 +139,7 @@ static int hvm_translate_linear_addr( * Hardware truncates to 32 bits in compatibility mode. * It does not truncate to 16 bits in 16-bit address-size mode. */ - addr = (uint32_t)(addr + dreg->base); + addr = (uint32_t)(addr + reg->base); } else { @@ -137,7 +148,7 @@ static int hvm_translate_linear_addr( */ if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) ) - addr += dreg->base; + addr += reg->base; if ( !is_canonical_address(addr) ) goto gpf; @@ -153,18 +164,18 @@ static int hvm_translate_linear_addr( } static int -sh_x86_emulate_read(enum x86_segment seg, - unsigned long offset, - unsigned long *val, - unsigned int bytes, - struct x86_emulate_ctxt *ctxt) +hvm_read(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + enum hvm_access_type access_type, + struct sh_emulate_ctxt *sh_ctxt) { - struct sh_emulate_ctxt *sh_ctxt = - container_of(ctxt, struct sh_emulate_ctxt, ctxt); unsigned long addr; int rc, errcode; - rc = hvm_translate_linear_addr(seg, offset, bytes, 0, sh_ctxt, &addr); + rc = hvm_translate_linear_addr( + seg, offset, bytes, access_type, sh_ctxt, &addr); if ( rc ) return rc; @@ -189,10 +200,78 @@ sh_x86_emulate_read(enum x86_segment seg, * of a write fault at the end of the instruction we're emulating. */ SHADOW_PRINTK("read failed to va %#lx\n", addr); errcode = ring_3(sh_ctxt->ctxt.regs) ? PFEC_user_mode : 0; + if ( access_type == hvm_access_insn_fetch ) + errcode |= PFEC_insn_fetch; hvm_inject_exception(TRAP_page_fault, errcode, addr + bytes - rc); return X86EMUL_PROPAGATE_FAULT; } +void shadow_init_emulation(struct sh_emulate_ctxt *sh_ctxt, + struct cpu_user_regs *regs) +{ + struct segment_register *creg; + struct vcpu *v = current; + unsigned long addr; + + sh_ctxt->ctxt.regs = regs; + + /* Segment cache initialisation. Primed with CS. */ + sh_ctxt->valid_seg_regs = 0; + creg = hvm_get_seg_reg(x86_seg_cs, sh_ctxt); + + /* Work out the emulation mode. */ + if ( hvm_long_mode_enabled(v) ) + sh_ctxt->ctxt.mode = creg->attr.fields.l ? + X86EMUL_MODE_PROT64 : X86EMUL_MODE_PROT32; + else if ( regs->eflags & X86_EFLAGS_VM ) + sh_ctxt->ctxt.mode = X86EMUL_MODE_REAL; + else + sh_ctxt->ctxt.mode = creg->attr.fields.db ? + X86EMUL_MODE_PROT32 : X86EMUL_MODE_PROT16; + + /* Attempt to prefetch whole instruction. */ + 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; +} + +static int +sh_x86_emulate_read(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + return hvm_read(seg, offset, val, bytes, hvm_access_read, + container_of(ctxt, struct sh_emulate_ctxt, ctxt)); +} + +static int +sh_x86_emulate_insn_fetch(enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + struct sh_emulate_ctxt *sh_ctxt = + container_of(ctxt, struct sh_emulate_ctxt, ctxt); + unsigned int insn_off = offset - ctxt->regs->eip; + + /* Fall back if requested bytes are not in the prefetch cache. */ + if ( unlikely((insn_off + bytes) > sh_ctxt->insn_buf_bytes) ) + return hvm_read(seg, offset, val, bytes, + hvm_access_insn_fetch, sh_ctxt); + + /* Hit the cache. Simple memcpy. */ + *val = 0; + memcpy(val, &sh_ctxt->insn_buf[insn_off], bytes); + return X86EMUL_CONTINUE; +} + static int sh_x86_emulate_write(enum x86_segment seg, unsigned long offset, @@ -206,7 +285,8 @@ sh_x86_emulate_write(enum x86_segment seg, unsigned long addr; int rc; - rc = hvm_translate_linear_addr(seg, offset, bytes, 1, sh_ctxt, &addr); + rc = hvm_translate_linear_addr( + seg, offset, bytes, hvm_access_write, sh_ctxt, &addr); if ( rc ) return rc; @@ -232,7 +312,8 @@ sh_x86_emulate_cmpxchg(enum x86_segment seg, unsigned long addr; int rc; - rc = hvm_translate_linear_addr(seg, offset, bytes, 1, sh_ctxt, &addr); + rc = hvm_translate_linear_addr( + seg, offset, bytes, hvm_access_write, sh_ctxt, &addr); if ( rc ) return rc; @@ -259,7 +340,8 @@ sh_x86_emulate_cmpxchg8b(enum x86_segment seg, unsigned long addr; int rc; - rc = hvm_translate_linear_addr(seg, offset, 8, 1, sh_ctxt, &addr); + rc = hvm_translate_linear_addr( + seg, offset, 8, hvm_access_write, sh_ctxt, &addr); if ( rc ) return rc; @@ -274,10 +356,11 @@ sh_x86_emulate_cmpxchg8b(enum x86_segment seg, struct x86_emulate_ops shadow_emulator_ops = { - .read = sh_x86_emulate_read, - .write = sh_x86_emulate_write, - .cmpxchg = sh_x86_emulate_cmpxchg, - .cmpxchg8b = sh_x86_emulate_cmpxchg8b, + .read = sh_x86_emulate_read, + .insn_fetch = sh_x86_emulate_insn_fetch, + .write = sh_x86_emulate_write, + .cmpxchg = sh_x86_emulate_cmpxchg, + .cmpxchg8b = sh_x86_emulate_cmpxchg8b, }; /**************************************************************************/ diff --git a/xen/arch/x86/mm/shadow/multi.c b/xen/arch/x86/mm/shadow/multi.c index b3a0da43b0..98ff07ace4 100644 --- a/xen/arch/x86/mm/shadow/multi.c +++ b/xen/arch/x86/mm/shadow/multi.c @@ -2808,24 +2808,20 @@ static int sh_page_fault(struct vcpu *v, return EXCRET_fault_fixed; emulate: - if ( !is_hvm_domain(d) ) + if ( !is_hvm_domain(d) || !guest_mode(regs) ) goto not_a_shadow_fault; hvm_store_cpu_guest_regs(v, regs, NULL); - emul_ctxt.ctxt.regs = regs; - emul_ctxt.ctxt.mode = (is_hvm_domain(d) ? - hvm_guest_x86_mode(v) : X86EMUL_MODE_HOST); - emul_ctxt.valid_seg_regs = 0; - SHADOW_PRINTK("emulate: eip=%#lx\n", regs->eip); + 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. - * We also disallow guest PTE updates from within Xen. */ - if ( (regs->error_code & PFEC_user_mode) || !guest_mode(regs) || + if ( (regs->error_code & PFEC_user_mode) || x86_emulate_memop(&emul_ctxt.ctxt, &shadow_emulator_ops) ) { SHADOW_PRINTK("emulator failure, unshadowing mfn %#lx\n", diff --git a/xen/arch/x86/mm/shadow/private.h b/xen/arch/x86/mm/shadow/private.h index c2c52d8f6a..e5a5796548 100644 --- a/xen/arch/x86/mm/shadow/private.h +++ b/xen/arch/x86/mm/shadow/private.h @@ -513,11 +513,17 @@ static inline void sh_unpin(struct vcpu *v, mfn_t smfn) struct sh_emulate_ctxt { struct x86_emulate_ctxt ctxt; + /* Cache of up to 15 bytes of instruction. */ + uint8_t insn_buf[15]; + uint8_t insn_buf_bytes; + /* Cache of segment registers already gathered for this emulation. */ unsigned int valid_seg_regs; struct segment_register seg_reg[6]; }; +void shadow_init_emulation(struct sh_emulate_ctxt *sh_ctxt, + struct cpu_user_regs *regs); #endif /* _XEN_SHADOW_PRIVATE_H */ diff --git a/xen/arch/x86/x86_emulate.c b/xen/arch/x86/x86_emulate.c index 1582bdf299..4f882556c4 100644 --- a/xen/arch/x86/x86_emulate.c +++ b/xen/arch/x86/x86_emulate.c @@ -19,11 +19,6 @@ #endif #include <asm-x86/x86_emulate.h> -#ifndef PFEC_write_access -#define PFEC_write_access (1U<<1) -#define PFEC_insn_fetch (1U<<4) -#endif - /* * Opcode effective-address decode tables. * Note that we only emulate instructions that have at least one memory @@ -374,15 +369,15 @@ do{ __asm__ __volatile__ ( \ #endif /* __i386__ */ /* Fetch next part of the instruction being emulated. */ -#define _insn_fetch(_size) \ -({ unsigned long _x; \ - rc = ops->read(x86_seg_cs, _regs.eip, &_x, (_size), ctxt); \ - if ( rc != 0 ) \ - goto done; \ - _regs.eip += (_size); \ - _x; \ +#define insn_fetch_bytes(_size) \ +({ unsigned long _x; \ + rc = ops->insn_fetch(x86_seg_cs, _regs.eip, &_x, (_size), ctxt); \ + if ( rc != 0 ) \ + goto done; \ + _regs.eip += (_size); \ + _x; \ }) -#define insn_fetch(_type) ((_type)_insn_fetch(sizeof(_type))) +#define insn_fetch_type(_type) ((_type)insn_fetch_bytes(sizeof(_type))) #define truncate_ea(ea) \ ({ unsigned long __ea = (ea); \ @@ -481,7 +476,7 @@ x86_emulate_memop( /* Legacy prefixes. */ for ( i = 0; i < 8; i++ ) { - switch ( b = insn_fetch(uint8_t) ) + switch ( b = insn_fetch_type(uint8_t) ) { case 0x66: /* operand-size override */ op_bytes ^= 6; /* switch between 2/4 bytes */ @@ -530,7 +525,7 @@ x86_emulate_memop( rex_prefix = b; if ( b & 8 ) /* REX.W */ op_bytes = 8; - b = insn_fetch(uint8_t); + b = insn_fetch_type(uint8_t); } /* Opcode byte(s). */ @@ -541,7 +536,7 @@ x86_emulate_memop( if ( b == 0x0f ) { twobyte = 1; - b = insn_fetch(uint8_t); + b = insn_fetch_type(uint8_t); d = twobyte_table[b]; } @@ -553,7 +548,7 @@ x86_emulate_memop( /* ModRM and SIB bytes. */ if ( d & ModRM ) { - modrm = insn_fetch(uint8_t); + modrm = insn_fetch_type(uint8_t); modrm_mod = (modrm & 0xc0) >> 6; modrm_reg = ((rex_prefix & 4) << 1) | ((modrm & 0x38) >> 3); modrm_rm = modrm & 0x07; @@ -577,9 +572,16 @@ x86_emulate_memop( } switch ( modrm_mod ) { - case 0: if ( modrm_rm == 6 ) ea_off = insn_fetch(int16_t); break; - case 1: ea_off += insn_fetch(int8_t); break; - case 2: ea_off += insn_fetch(int16_t); break; + case 0: + if ( modrm_rm == 6 ) + ea_off = insn_fetch_type(int16_t); + break; + case 1: + ea_off += insn_fetch_type(int8_t); + break; + case 2: + ea_off += insn_fetch_type(int16_t); + break; } } else @@ -587,14 +589,14 @@ x86_emulate_memop( /* 32/64-bit ModR/M decode. */ if ( modrm_rm == 4 ) { - sib = insn_fetch(uint8_t); + sib = insn_fetch_type(uint8_t); sib_index = ((sib >> 3) & 7) | ((rex_prefix << 2) & 8); sib_base = (sib & 7) | ((rex_prefix << 3) & 8); if ( sib_index != 4 ) ea_off = *(long *)decode_register(sib_index, &_regs, 0); ea_off <<= (sib >> 6) & 3; if ( (modrm_mod == 0) && ((sib_base & 7) == 5) ) - ea_off += insn_fetch(int32_t); + ea_off += insn_fetch_type(int32_t); else ea_off += *(long *)decode_register(sib_base, &_regs, 0); } @@ -608,7 +610,7 @@ x86_emulate_memop( case 0: if ( (modrm_rm & 7) != 5 ) break; - ea_off = insn_fetch(int32_t); + ea_off = insn_fetch_type(int32_t); if ( mode != X86EMUL_MODE_PROT64 ) break; /* Relative to RIP of next instruction. Argh! */ @@ -624,8 +626,12 @@ x86_emulate_memop( ea_off += (d & ByteOp) ? 1 : ((op_bytes == 8) ? 4 : op_bytes); break; - case 1: ea_off += insn_fetch(int8_t); break; - case 2: ea_off += insn_fetch(int32_t); break; + case 1: + ea_off += insn_fetch_type(int8_t); + break; + case 2: + ea_off += insn_fetch_type(int32_t); + break; } } @@ -684,15 +690,15 @@ x86_emulate_memop( /* NB. Immediates are sign-extended as necessary. */ switch ( src.bytes ) { - case 1: src.val = insn_fetch(int8_t); break; - case 2: src.val = insn_fetch(int16_t); break; - case 4: src.val = insn_fetch(int32_t); break; + case 1: src.val = insn_fetch_type(int8_t); break; + case 2: src.val = insn_fetch_type(int16_t); break; + case 4: src.val = insn_fetch_type(int32_t); break; } break; case SrcImmByte: src.type = OP_IMM; src.bytes = 1; - src.val = insn_fetch(int8_t); + src.val = insn_fetch_type(int8_t); break; } @@ -885,9 +891,9 @@ x86_emulate_memop( if ( src.bytes == 8 ) src.bytes = 4; switch ( src.bytes ) { - case 1: src.val = insn_fetch(int8_t); break; - case 2: src.val = insn_fetch(int16_t); break; - case 4: src.val = insn_fetch(int32_t); break; + case 1: src.val = insn_fetch_type(int8_t); break; + case 2: src.val = insn_fetch_type(int16_t); break; + case 4: src.val = insn_fetch_type(int32_t); break; } goto test; case 2: /* not */ @@ -986,7 +992,7 @@ x86_emulate_memop( dst.type = OP_REG; dst.reg = (unsigned long *)&_regs.eax; dst.bytes = (d & ByteOp) ? 1 : op_bytes; - if ( (rc = ops->read(ea_seg, _insn_fetch(ad_bytes), + if ( (rc = ops->read(ea_seg, insn_fetch_bytes(ad_bytes), &dst.val, dst.bytes, ctxt)) != 0 ) goto done; break; @@ -994,7 +1000,7 @@ x86_emulate_memop( /* Destination EA is not encoded via ModRM. */ dst.type = OP_MEM; dst.mem_seg = ea_seg; - dst.mem_off = _insn_fetch(ad_bytes); + dst.mem_off = insn_fetch_bytes(ad_bytes); dst.bytes = (d & ByteOp) ? 1 : op_bytes; dst.val = (unsigned long)_regs.eax; break; @@ -1198,7 +1204,7 @@ x86_emulate_memop( for ( ea_off = ctxt->regs->eip; ea_off < _regs.eip; ea_off++ ) { unsigned long x; - ops->read(x86_seg_cs, ea_off, &x, 1, ctxt); + ops->insn_fetch(x86_seg_cs, ea_off, &x, 1, ctxt); printk(" %02x", (uint8_t)x); } printk("\n"); diff --git a/xen/include/asm-x86/x86_emulate.h b/xen/include/asm-x86/x86_emulate.h index ef23291cdc..89a42da7b0 100644 --- a/xen/include/asm-x86/x86_emulate.h +++ b/xen/include/asm-x86/x86_emulate.h @@ -56,7 +56,8 @@ struct x86_emulate_ops /* * All functions: * @seg: [IN ] Segment being dereferenced (specified as x86_seg_??). - * @offset [IN ] Offset within segment. + * @offset:[IN ] Offset within segment. + * @ctxt: [IN ] Emulation context info as passed to the emulator. */ /* @@ -72,6 +73,17 @@ struct x86_emulate_ops struct x86_emulate_ctxt *ctxt); /* + * insn_fetch: Emulate fetch from instruction byte stream. + * Parameters are same as for 'read'. @seg is always x86_seg_cs. + */ + int (*insn_fetch)( + enum x86_segment seg, + unsigned long offset, + unsigned long *val, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt); + + /* * write: Emulate a memory write. * @val: [IN ] Value to write to memory (low-order bytes used as req'd). * @bytes: [IN ] Number of bytes to write to memory. |