From patchwork Thu Sep 8 13:16:24 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jan Beulich X-Patchwork-Id: 9321377 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 82081607D3 for ; Thu, 8 Sep 2016 13:19:12 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 703B02987B for ; Thu, 8 Sep 2016 13:19:12 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 64B8429880; Thu, 8 Sep 2016 13:19:12 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 88E622987B for ; Thu, 8 Sep 2016 13:19:09 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bhzBu-0001Kh-6d; Thu, 08 Sep 2016 13:16:30 +0000 Received: from mail6.bemta5.messagelabs.com ([195.245.231.135]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1bhzBt-0001KK-0T for xen-devel@lists.xenproject.org; Thu, 08 Sep 2016 13:16:29 +0000 Received: from [85.158.139.211] by server-14.bemta-5.messagelabs.com id 1F/CF-10347-CA461D75; Thu, 08 Sep 2016 13:16:28 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFtrHIsWRWlGSWpSXmKPExsXS6fjDS3d1ysV wg6lnBS2+b5nM5MDocfjDFZYAxijWzLyk/IoE1oxHn1ezFfx+xlhxe+4X1gbGpomMXYycHEIC eRJdJ36xg9i8AnYSJ/7fBYtLCBhKPH1/nQ3EZhFQlZg46yNYDZuAukTbs+2sXYwcHCICBhLnj iaBmMwC+hLb1rGAVAgLxEhMOX2XFWK6ncSzUwfB4pwC9hJfWucwgpTzCghK/N0hDBJmBiq59f kG+wRGnlkImVlIMhC2lsTDX7dYIGxtiWULXzPPAtsrLbH8HwdE2EOibeUKJlQlIHawxMM5Hew LGDlWMWoUpxaVpRbpGlnoJRVlpmeU5CZm5ugaGpjq5aYWFyemp+YkJhXrJefnbmIEhms9AwPj Dsa+VX6HGCU5mJREeX2KL4QL8SXlp1RmJBZnxBeV5qQWH2KU4eBQkuC1Sb4YLiRYlJqeWpGWm QOMHJi0BAePkghvLkiat7ggMbc4Mx0idYpRUUqclxckIQCSyCjNg2uDReslRlkpYV5GBgYGIZ 6C1KLczBJU+VeM4hyMSsK88SBTeDLzSuCmvwJazAS0WOjUeZDFJYkIKakGRrbZF+6lvfhXnmw gnhWiNTErecUhT8F3n+YrWtp5/XacF8Zfkpm7Il53wrRszq8nnt9OKzvsH/Vv3p9J54qee1Qz /zuxcrOnXsuZTXntW6pTI84JdPWcaWO6Mat13upJ/EkzCtNm/3wgN+dw91Ujhpo89T/H15Zmn 4wNnjPj5Qojub1/N3qfdVFiKc5INNRiLipOBACeTENk0QIAAA== X-Env-Sender: JBeulich@suse.com X-Msg-Ref: server-15.tower-206.messagelabs.com!1473340584!44738470!1 X-Originating-IP: [137.65.248.74] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 8.84; banners=-,-,- X-VirusChecked: Checked Received: (qmail 49129 invoked from network); 8 Sep 2016 13:16:26 -0000 Received: from prv-mh.provo.novell.com (HELO prv-mh.provo.novell.com) (137.65.248.74) by server-15.tower-206.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 8 Sep 2016 13:16:26 -0000 Received: from INET-PRV-MTA by prv-mh.provo.novell.com with Novell_GroupWise; Thu, 08 Sep 2016 07:16:24 -0600 Message-Id: <57D180C8020000780010D193@prv-mh.provo.novell.com> X-Mailer: Novell GroupWise Internet Agent 14.2.1 Date: Thu, 08 Sep 2016 07:16:24 -0600 From: "Jan Beulich" To: "xen-devel" References: <57D17C78020000780010D127@prv-mh.provo.novell.com> In-Reply-To: <57D17C78020000780010D127@prv-mh.provo.novell.com> Mime-Version: 1.0 Cc: Andrew Cooper Subject: [Xen-devel] [PATCH 10/17] x86/32on64: use generic instruction decoding for call gate emulation X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP ... instead of custom handling. Note that we can't use generic emulation, as the emulator's far branch support is rather rudimentary at this point in time. Signed-off-by: Jan Beulich x86/32on64: use generic instruction decoding for call gate emulation ... instead of custom handling. Note that we can't use generic emulation, as the emulator's far branch support is rather rudimentary at this point in time. Signed-off-by: Jan Beulich --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -3138,13 +3139,92 @@ static inline int check_stack_limit(unsi (!(ar & _SEGMENT_EC) ? (esp - 1) <= limit : (esp - decr) > limit)); } +struct gate_op_ctxt { + struct x86_emulate_ctxt ctxt; + struct { + unsigned long base, limit; + } cs; + bool insn_fetch; +}; + +static int gate_op_read( + enum x86_segment seg, + unsigned long offset, + void *p_data, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + const struct gate_op_ctxt *goc = + container_of(ctxt, struct gate_op_ctxt, ctxt); + unsigned int rc = bytes, sel = 0; + unsigned long addr = offset, limit = 0; + + switch ( seg ) + { + case x86_seg_cs: + addr += goc->cs.base; + limit = goc->cs.limit; + break; + case x86_seg_ds: + sel = read_sreg(ds); + break; + case x86_seg_es: + sel = read_sreg(es); + break; + case x86_seg_fs: + sel = read_sreg(fs); + break; + case x86_seg_gs: + sel = read_sreg(gs); + break; + case x86_seg_ss: + sel = ctxt->regs->ss; + break; + default: + return X86EMUL_UNHANDLEABLE; + } + if ( sel ) + { + unsigned int ar; + + ASSERT(!goc->insn_fetch); + if ( !read_descriptor(sel, current, &addr, &limit, &ar, 0) || + !(ar & _SEGMENT_S) || + !(ar & _SEGMENT_P) || + ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) ) + return X86EMUL_UNHANDLEABLE; + addr += offset; + } + else if ( seg != x86_seg_cs ) + return X86EMUL_UNHANDLEABLE; + + if ( limit < bytes - 1 || offset > limit - bytes + 1 ) + return X86EMUL_UNHANDLEABLE; + + if ( is_pv_32bit_vcpu(current) ) + addr = (uint32_t)addr; + + if ( !__addr_ok(addr) || + (rc = __copy_from_user(p_data, (void *)addr, bytes)) ) + { + propagate_page_fault(addr + bytes - rc, + goc->insn_fetch && cpu_has_nx + ? PFEC_insn_fetch : 0 ); + return X86EMUL_EXCEPTION; + } + + return X86EMUL_OKAY; +} + static void emulate_gate_op(struct cpu_user_regs *regs) { struct vcpu *v = current; - unsigned int sel, ar, dpl, nparm, opnd_sel; - unsigned int op_default, op_bytes, ad_default, ad_bytes; - unsigned long off, eip, opnd_off, base, limit; - int jump; + unsigned int sel, ar, dpl, nparm, insn_len; + struct gate_op_ctxt ctxt = { .ctxt.regs = regs, .insn_fetch = true }; + struct x86_emulate_state *state; + unsigned long off, base, limit; + uint16_t opnd_sel = 0; + int jump = -1, rc = X86EMUL_OKAY; /* Check whether this fault is due to the use of a call gate. */ if ( !read_gate_descriptor(regs->error_code, v, &sel, &off, &ar) || @@ -3166,7 +3246,8 @@ static void emulate_gate_op(struct cpu_u * Decode instruction (and perhaps operand) to determine RPL, * whether this is a jump or a call, and the call return offset. */ - if ( !read_descriptor(regs->cs, v, &base, &limit, &ar, 0) || + if ( !read_descriptor(regs->cs, v, &ctxt.cs.base, &ctxt.cs.limit, + &ar, 0) || !(ar & _SEGMENT_S) || !(ar & _SEGMENT_P) || !(ar & _SEGMENT_CODE) ) @@ -3175,179 +3256,59 @@ static void emulate_gate_op(struct cpu_u return; } - op_bytes = op_default = ar & _SEGMENT_DB ? 4 : 2; - ad_default = ad_bytes = op_default; - opnd_sel = opnd_off = 0; - jump = -1; - for ( eip = regs->eip; eip - regs->_eip < 10; ) + ctxt.ctxt.addr_size = ar & _SEGMENT_DB ? 32 : 16; + /* Leave zero in ctxt.ctxt.sp_size, as it's not needed for decoding. */ + state = x86_decode_insn(&ctxt.ctxt, gate_op_read); + ctxt.insn_fetch = false; + if ( IS_ERR_OR_NULL(state) ) + { + if ( PTR_ERR(state) != -X86EMUL_EXCEPTION ) + do_guest_trap(TRAP_gp_fault, regs); + return; + } + + switch ( ctxt.ctxt.opcode ) { - switch ( insn_fetch(u8, base, eip, limit) ) + unsigned int modrm_345; + + case 0xea: + ++jump; + /* fall through */ + case 0x9a: + ++jump; + opnd_sel = x86_insn_immediate(state, 1); + break; + case 0xff: + if ( x86_insn_modrm(state, NULL, &modrm_345) >= 3 ) + break; + switch ( modrm_345 & 7 ) { - case 0x66: /* operand-size override */ - op_bytes = op_default ^ 6; /* switch between 2/4 bytes */ - continue; - case 0x67: /* address-size override */ - ad_bytes = ad_default != 4 ? 4 : 2; /* switch to 2/4 bytes */ - continue; - case 0x2e: /* CS override */ - opnd_sel = regs->cs; - ASSERT(opnd_sel); - continue; - case 0x3e: /* DS override */ - opnd_sel = read_sreg(ds); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x26: /* ES override */ - opnd_sel = read_sreg(es); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x64: /* FS override */ - opnd_sel = read_sreg(fs); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x65: /* GS override */ - opnd_sel = read_sreg(gs); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x36: /* SS override */ - opnd_sel = regs->ss; - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0xea: + enum x86_segment seg; + + case 5: ++jump; - /* FALLTHROUGH */ - case 0x9a: + /* fall through */ + case 3: ++jump; - opnd_sel = regs->cs; - opnd_off = eip; - ad_bytes = ad_default; - eip += op_bytes + 2; - break; - case 0xff: - { - unsigned int modrm; - - switch ( (modrm = insn_fetch(u8, base, eip, limit)) & 0xf8 ) - { - case 0x28: case 0x68: case 0xa8: - ++jump; - /* FALLTHROUGH */ - case 0x18: case 0x58: case 0x98: - ++jump; - if ( ad_bytes != 2 ) - { - if ( (modrm & 7) == 4 ) - { - unsigned int sib; - sib = insn_fetch(u8, base, eip, limit); - - modrm = (modrm & ~7) | (sib & 7); - if ( ((sib >>= 3) & 7) != 4 ) - opnd_off = *(unsigned long *) - decode_register(sib & 7, regs, 0); - opnd_off <<= sib >> 3; - } - if ( (modrm & 7) != 5 || (modrm & 0xc0) ) - opnd_off += *(unsigned long *) - decode_register(modrm & 7, regs, 0); - else - modrm |= 0x87; - if ( !opnd_sel ) - { - switch ( modrm & 7 ) - { - default: - opnd_sel = read_sreg(ds); - break; - case 4: case 5: - opnd_sel = regs->ss; - break; - } - } - } - else - { - switch ( modrm & 7 ) - { - case 0: case 1: case 7: - opnd_off = regs->ebx; - break; - case 6: - if ( !(modrm & 0xc0) ) - modrm |= 0x80; - else - case 2: case 3: - { - opnd_off = regs->ebp; - if ( !opnd_sel ) - opnd_sel = regs->ss; - } - break; - } - if ( !opnd_sel ) - opnd_sel = read_sreg(ds); - switch ( modrm & 7 ) - { - case 0: case 2: case 4: - opnd_off += regs->esi; - break; - case 1: case 3: case 5: - opnd_off += regs->edi; - break; - } - } - switch ( modrm & 0xc0 ) - { - case 0x40: - opnd_off += insn_fetch(s8, base, eip, limit); - break; - case 0x80: - if ( ad_bytes > 2 ) - opnd_off += insn_fetch(s32, base, eip, limit); - else - opnd_off += insn_fetch(s16, base, eip, limit); - break; - } - if ( ad_bytes == 4 ) - opnd_off = (unsigned int)opnd_off; - else if ( ad_bytes == 2 ) - opnd_off = (unsigned short)opnd_off; - break; - } - } + base = x86_insn_operand_ea(state, &seg); + rc = gate_op_read(seg, + base + (x86_insn_opsize(state) >> 3), + &opnd_sel, sizeof(opnd_sel), &ctxt.ctxt); break; } break; } - if ( jump < 0 ) - { - fail: - do_guest_trap(TRAP_gp_fault, regs); - skip: - return; - } + insn_len = x86_insn_length(state, &ctxt.ctxt); + x86_emulate_free_state(state); - if ( (opnd_sel != regs->cs && - !read_descriptor(opnd_sel, v, &base, &limit, &ar, 0)) || - !(ar & _SEGMENT_S) || - !(ar & _SEGMENT_P) || - ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) ) - { - do_guest_trap(TRAP_gp_fault, regs); - return; - } + if ( rc == X86EMUL_EXCEPTION ) + return; - opnd_off += op_bytes; -#define ad_default ad_bytes - opnd_sel = insn_fetch(u16, base, opnd_off, limit); -#undef ad_default - if ( (opnd_sel & ~3) != regs->error_code || dpl < (opnd_sel & 3) ) + if ( rc != X86EMUL_OKAY || + jump < 0 || + (opnd_sel & ~3) != regs->error_code || + dpl < (opnd_sel & 3) ) { do_guest_trap(TRAP_gp_fault, regs); return; @@ -3488,7 +3449,7 @@ static void emulate_gate_op(struct cpu_u } } push(regs->cs); - push(eip); + push(regs->eip + insn_len); #undef push regs->esp = esp; regs->ss = ss; --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -5269,6 +5269,14 @@ void x86_emulate_free_state(struct x86_e } #endif +unsigned int +x86_insn_opsize(const struct x86_emulate_state *state) +{ + check_state(state); + + return state->op_bytes << 3; +} + int x86_insn_modrm(const struct x86_emulate_state *state, unsigned int *rm, unsigned int *reg) @@ -5286,6 +5294,33 @@ x86_insn_modrm(const struct x86_emulate_ return state->modrm_mod; } +unsigned long +x86_insn_operand_ea(const struct x86_emulate_state *state, + enum x86_segment *seg) +{ + *seg = state->ea.type == OP_MEM ? state->ea.mem.seg : x86_seg_none; + + check_state(state); + + return state->ea.mem.off; +} + +unsigned long +x86_insn_immediate(const struct x86_emulate_state *state, unsigned int nr) +{ + check_state(state); + + switch ( nr ) + { + case 0: + return state->imm1; + case 1: + return state->imm2; + } + + return 0; +} + unsigned int x86_insn_length(const struct x86_emulate_state *state, const struct x86_emulate_ctxt *ctxt) --- a/xen/arch/x86/x86_emulate/x86_emulate.h +++ b/xen/arch/x86/x86_emulate/x86_emulate.h @@ -533,9 +533,17 @@ x86_decode_insn( void *p_data, unsigned int bytes, struct x86_emulate_ctxt *ctxt)); +unsigned int +x86_insn_opsize(const struct x86_emulate_state *state); int x86_insn_modrm(const struct x86_emulate_state *state, unsigned int *rm, unsigned int *reg); +unsigned long +x86_insn_operand_ea(const struct x86_emulate_state *state, + enum x86_segment *seg); +unsigned long +x86_insn_immediate(const struct x86_emulate_state *state, + unsigned int nr); unsigned int x86_insn_length(const struct x86_emulate_state *state, const struct x86_emulate_ctxt *ctxt); --- a/xen/arch/x86/traps.c +++ b/xen/arch/x86/traps.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -3138,13 +3139,92 @@ static inline int check_stack_limit(unsi (!(ar & _SEGMENT_EC) ? (esp - 1) <= limit : (esp - decr) > limit)); } +struct gate_op_ctxt { + struct x86_emulate_ctxt ctxt; + struct { + unsigned long base, limit; + } cs; + bool insn_fetch; +}; + +static int gate_op_read( + enum x86_segment seg, + unsigned long offset, + void *p_data, + unsigned int bytes, + struct x86_emulate_ctxt *ctxt) +{ + const struct gate_op_ctxt *goc = + container_of(ctxt, struct gate_op_ctxt, ctxt); + unsigned int rc = bytes, sel = 0; + unsigned long addr = offset, limit = 0; + + switch ( seg ) + { + case x86_seg_cs: + addr += goc->cs.base; + limit = goc->cs.limit; + break; + case x86_seg_ds: + sel = read_sreg(ds); + break; + case x86_seg_es: + sel = read_sreg(es); + break; + case x86_seg_fs: + sel = read_sreg(fs); + break; + case x86_seg_gs: + sel = read_sreg(gs); + break; + case x86_seg_ss: + sel = ctxt->regs->ss; + break; + default: + return X86EMUL_UNHANDLEABLE; + } + if ( sel ) + { + unsigned int ar; + + ASSERT(!goc->insn_fetch); + if ( !read_descriptor(sel, current, &addr, &limit, &ar, 0) || + !(ar & _SEGMENT_S) || + !(ar & _SEGMENT_P) || + ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) ) + return X86EMUL_UNHANDLEABLE; + addr += offset; + } + else if ( seg != x86_seg_cs ) + return X86EMUL_UNHANDLEABLE; + + if ( limit < bytes - 1 || offset > limit - bytes + 1 ) + return X86EMUL_UNHANDLEABLE; + + if ( is_pv_32bit_vcpu(current) ) + addr = (uint32_t)addr; + + if ( !__addr_ok(addr) || + (rc = __copy_from_user(p_data, (void *)addr, bytes)) ) + { + propagate_page_fault(addr + bytes - rc, + goc->insn_fetch && cpu_has_nx + ? PFEC_insn_fetch : 0 ); + return X86EMUL_EXCEPTION; + } + + return X86EMUL_OKAY; +} + static void emulate_gate_op(struct cpu_user_regs *regs) { struct vcpu *v = current; - unsigned int sel, ar, dpl, nparm, opnd_sel; - unsigned int op_default, op_bytes, ad_default, ad_bytes; - unsigned long off, eip, opnd_off, base, limit; - int jump; + unsigned int sel, ar, dpl, nparm, insn_len; + struct gate_op_ctxt ctxt = { .ctxt.regs = regs, .insn_fetch = true }; + struct x86_emulate_state *state; + unsigned long off, base, limit; + uint16_t opnd_sel = 0; + int jump = -1, rc = X86EMUL_OKAY; /* Check whether this fault is due to the use of a call gate. */ if ( !read_gate_descriptor(regs->error_code, v, &sel, &off, &ar) || @@ -3166,7 +3246,8 @@ static void emulate_gate_op(struct cpu_u * Decode instruction (and perhaps operand) to determine RPL, * whether this is a jump or a call, and the call return offset. */ - if ( !read_descriptor(regs->cs, v, &base, &limit, &ar, 0) || + if ( !read_descriptor(regs->cs, v, &ctxt.cs.base, &ctxt.cs.limit, + &ar, 0) || !(ar & _SEGMENT_S) || !(ar & _SEGMENT_P) || !(ar & _SEGMENT_CODE) ) @@ -3175,179 +3256,59 @@ static void emulate_gate_op(struct cpu_u return; } - op_bytes = op_default = ar & _SEGMENT_DB ? 4 : 2; - ad_default = ad_bytes = op_default; - opnd_sel = opnd_off = 0; - jump = -1; - for ( eip = regs->eip; eip - regs->_eip < 10; ) + ctxt.ctxt.addr_size = ar & _SEGMENT_DB ? 32 : 16; + /* Leave zero in ctxt.ctxt.sp_size, as it's not needed for decoding. */ + state = x86_decode_insn(&ctxt.ctxt, gate_op_read); + ctxt.insn_fetch = false; + if ( IS_ERR_OR_NULL(state) ) + { + if ( PTR_ERR(state) != -X86EMUL_EXCEPTION ) + do_guest_trap(TRAP_gp_fault, regs); + return; + } + + switch ( ctxt.ctxt.opcode ) { - switch ( insn_fetch(u8, base, eip, limit) ) + unsigned int modrm_345; + + case 0xea: + ++jump; + /* fall through */ + case 0x9a: + ++jump; + opnd_sel = x86_insn_immediate(state, 1); + break; + case 0xff: + if ( x86_insn_modrm(state, NULL, &modrm_345) >= 3 ) + break; + switch ( modrm_345 & 7 ) { - case 0x66: /* operand-size override */ - op_bytes = op_default ^ 6; /* switch between 2/4 bytes */ - continue; - case 0x67: /* address-size override */ - ad_bytes = ad_default != 4 ? 4 : 2; /* switch to 2/4 bytes */ - continue; - case 0x2e: /* CS override */ - opnd_sel = regs->cs; - ASSERT(opnd_sel); - continue; - case 0x3e: /* DS override */ - opnd_sel = read_sreg(ds); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x26: /* ES override */ - opnd_sel = read_sreg(es); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x64: /* FS override */ - opnd_sel = read_sreg(fs); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x65: /* GS override */ - opnd_sel = read_sreg(gs); - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0x36: /* SS override */ - opnd_sel = regs->ss; - if ( !opnd_sel ) - opnd_sel = dpl; - continue; - case 0xea: + enum x86_segment seg; + + case 5: ++jump; - /* FALLTHROUGH */ - case 0x9a: + /* fall through */ + case 3: ++jump; - opnd_sel = regs->cs; - opnd_off = eip; - ad_bytes = ad_default; - eip += op_bytes + 2; - break; - case 0xff: - { - unsigned int modrm; - - switch ( (modrm = insn_fetch(u8, base, eip, limit)) & 0xf8 ) - { - case 0x28: case 0x68: case 0xa8: - ++jump; - /* FALLTHROUGH */ - case 0x18: case 0x58: case 0x98: - ++jump; - if ( ad_bytes != 2 ) - { - if ( (modrm & 7) == 4 ) - { - unsigned int sib; - sib = insn_fetch(u8, base, eip, limit); - - modrm = (modrm & ~7) | (sib & 7); - if ( ((sib >>= 3) & 7) != 4 ) - opnd_off = *(unsigned long *) - decode_register(sib & 7, regs, 0); - opnd_off <<= sib >> 3; - } - if ( (modrm & 7) != 5 || (modrm & 0xc0) ) - opnd_off += *(unsigned long *) - decode_register(modrm & 7, regs, 0); - else - modrm |= 0x87; - if ( !opnd_sel ) - { - switch ( modrm & 7 ) - { - default: - opnd_sel = read_sreg(ds); - break; - case 4: case 5: - opnd_sel = regs->ss; - break; - } - } - } - else - { - switch ( modrm & 7 ) - { - case 0: case 1: case 7: - opnd_off = regs->ebx; - break; - case 6: - if ( !(modrm & 0xc0) ) - modrm |= 0x80; - else - case 2: case 3: - { - opnd_off = regs->ebp; - if ( !opnd_sel ) - opnd_sel = regs->ss; - } - break; - } - if ( !opnd_sel ) - opnd_sel = read_sreg(ds); - switch ( modrm & 7 ) - { - case 0: case 2: case 4: - opnd_off += regs->esi; - break; - case 1: case 3: case 5: - opnd_off += regs->edi; - break; - } - } - switch ( modrm & 0xc0 ) - { - case 0x40: - opnd_off += insn_fetch(s8, base, eip, limit); - break; - case 0x80: - if ( ad_bytes > 2 ) - opnd_off += insn_fetch(s32, base, eip, limit); - else - opnd_off += insn_fetch(s16, base, eip, limit); - break; - } - if ( ad_bytes == 4 ) - opnd_off = (unsigned int)opnd_off; - else if ( ad_bytes == 2 ) - opnd_off = (unsigned short)opnd_off; - break; - } - } + base = x86_insn_operand_ea(state, &seg); + rc = gate_op_read(seg, + base + (x86_insn_opsize(state) >> 3), + &opnd_sel, sizeof(opnd_sel), &ctxt.ctxt); break; } break; } - if ( jump < 0 ) - { - fail: - do_guest_trap(TRAP_gp_fault, regs); - skip: - return; - } + insn_len = x86_insn_length(state, &ctxt.ctxt); + x86_emulate_free_state(state); - if ( (opnd_sel != regs->cs && - !read_descriptor(opnd_sel, v, &base, &limit, &ar, 0)) || - !(ar & _SEGMENT_S) || - !(ar & _SEGMENT_P) || - ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) ) - { - do_guest_trap(TRAP_gp_fault, regs); - return; - } + if ( rc == X86EMUL_EXCEPTION ) + return; - opnd_off += op_bytes; -#define ad_default ad_bytes - opnd_sel = insn_fetch(u16, base, opnd_off, limit); -#undef ad_default - if ( (opnd_sel & ~3) != regs->error_code || dpl < (opnd_sel & 3) ) + if ( rc != X86EMUL_OKAY || + jump < 0 || + (opnd_sel & ~3) != regs->error_code || + dpl < (opnd_sel & 3) ) { do_guest_trap(TRAP_gp_fault, regs); return; @@ -3488,7 +3449,7 @@ static void emulate_gate_op(struct cpu_u } } push(regs->cs); - push(eip); + push(regs->eip + insn_len); #undef push regs->esp = esp; regs->ss = ss; --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -5269,6 +5269,14 @@ void x86_emulate_free_state(struct x86_e } #endif +unsigned int +x86_insn_opsize(const struct x86_emulate_state *state) +{ + check_state(state); + + return state->op_bytes << 3; +} + int x86_insn_modrm(const struct x86_emulate_state *state, unsigned int *rm, unsigned int *reg) @@ -5286,6 +5294,33 @@ x86_insn_modrm(const struct x86_emulate_ return state->modrm_mod; } +unsigned long +x86_insn_operand_ea(const struct x86_emulate_state *state, + enum x86_segment *seg) +{ + *seg = state->ea.type == OP_MEM ? state->ea.mem.seg : x86_seg_none; + + check_state(state); + + return state->ea.mem.off; +} + +unsigned long +x86_insn_immediate(const struct x86_emulate_state *state, unsigned int nr) +{ + check_state(state); + + switch ( nr ) + { + case 0: + return state->imm1; + case 1: + return state->imm2; + } + + return 0; +} + unsigned int x86_insn_length(const struct x86_emulate_state *state, const struct x86_emulate_ctxt *ctxt) --- a/xen/arch/x86/x86_emulate/x86_emulate.h +++ b/xen/arch/x86/x86_emulate/x86_emulate.h @@ -533,9 +533,17 @@ x86_decode_insn( void *p_data, unsigned int bytes, struct x86_emulate_ctxt *ctxt)); +unsigned int +x86_insn_opsize(const struct x86_emulate_state *state); int x86_insn_modrm(const struct x86_emulate_state *state, unsigned int *rm, unsigned int *reg); +unsigned long +x86_insn_operand_ea(const struct x86_emulate_state *state, + enum x86_segment *seg); +unsigned long +x86_insn_immediate(const struct x86_emulate_state *state, + unsigned int nr); unsigned int x86_insn_length(const struct x86_emulate_state *state, const struct x86_emulate_ctxt *ctxt);