[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH v2 3/8] x86emul: split off opcode 0fc7 handling
There's a fair amount of sub-cases (with some yet to be implemented), so a separate function seems warranted. Code moved gets slightly adjusted in a few places, e.g. replacing EXC_* by X86_EXC_* (such that EXC_* don't need to move as well; we want these to be phased out anyway). Signed-off-by: Jan Beulich <jbeulich@xxxxxxxx> --- a/tools/fuzz/x86_instruction_emulator/Makefile +++ b/tools/fuzz/x86_instruction_emulator/Makefile @@ -35,7 +35,7 @@ x86.h := $(addprefix $(XEN_ROOT)/tools/i x86_emulate.h := x86-emulate.h x86_emulate/x86_emulate.h $(x86.h) OBJS := fuzz-emul.o x86-emulate.o -OBJS += x86_emulate/0f01.o x86_emulate/0fae.o +OBJS += x86_emulate/0f01.o x86_emulate/0fae.o x86_emulate/0fc7.o # x86-emulate.c will be implicit for both x86-emulate.o x86-emulate-cov.o: x86_emulate/x86_emulate.c $(x86_emulate.h) x86_emulate/private.h --- a/tools/tests/x86_emulator/Makefile +++ b/tools/tests/x86_emulator/Makefile @@ -251,7 +251,7 @@ xop.h avx512f.h: simd-fma.c endif # 32-bit override OBJS := x86-emulate.o cpuid.o test_x86_emulator.o evex-disp8.o predicates.o wrappers.o -OBJS += x86_emulate/0f01.o x86_emulate/0fae.o +OBJS += x86_emulate/0f01.o x86_emulate/0fae.o x86_emulate/0fc7.o $(TARGET): $(OBJS) $(HOSTCC) $(HOSTCFLAGS) -o $@ $^ --- /dev/null +++ b/xen/arch/x86/x86_emulate/0fc7.c @@ -0,0 +1,210 @@ +/****************************************************************************** + * 0fc7.c - helper for x86_emulate.c + * + * Generic x86 (32-bit and 64-bit) instruction decoder and emulator. + * + * Copyright (c) 2005-2007 Keir Fraser + * Copyright (c) 2005-2007 XenSource Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; If not, see <http://www.gnu.org/licenses/>. + */ + +#include "private.h" + +/* Avoid namespace pollution. */ +#undef cmpxchg + +int x86emul_0fc7(struct x86_emulate_state *s, + struct cpu_user_regs *regs, + struct operand *dst, + struct x86_emulate_ctxt *ctxt, + const struct x86_emulate_ops *ops, + mmval_t *mmvalp) +{ + int rc; + + if ( s->ea.type == OP_REG ) + { + bool __maybe_unused carry; + + switch ( s->modrm_reg & 7 ) + { + default: + return X86EMUL_UNRECOGNIZED; + + case 6: /* rdrand */ +#ifdef HAVE_AS_RDRAND + generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD); + host_and_vcpu_must_have(rdrand); + *dst = s->ea; + switch ( s->op_bytes ) + { + case 2: + asm ( "rdrand %w0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + default: +# ifdef __x86_64__ + asm ( "rdrand %k0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + case 8: +# endif + asm ( "rdrand %0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + } + regs->eflags &= ~EFLAGS_MASK; + if ( carry ) + regs->eflags |= X86_EFLAGS_CF; + break; +#else + return X86EMUL_UNIMPLEMENTED; +#endif + + case 7: /* rdseed / rdpid */ + if ( s->vex.pfx == vex_f3 ) /* rdpid */ + { + uint64_t msr_val; + + generate_exception_if(s->ea.type != OP_REG, X86_EXC_UD); + vcpu_must_have(rdpid); + fail_if(!ops->read_msr); + if ( (rc = ops->read_msr(MSR_TSC_AUX, &msr_val, + ctxt)) != X86EMUL_OKAY ) + goto done; + *dst = s->ea; + dst->val = msr_val; + dst->bytes = 4; + break; + } +#ifdef HAVE_AS_RDSEED + generate_exception_if(s->vex.pfx >= vex_f3, X86_EXC_UD); + host_and_vcpu_must_have(rdseed); + *dst = s->ea; + switch ( s->op_bytes ) + { + case 2: + asm ( "rdseed %w0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + default: +# ifdef __x86_64__ + asm ( "rdseed %k0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + case 8: +# endif + asm ( "rdseed %0" ASM_FLAG_OUT(, "; setc %1") + : "=r" (dst->val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); + break; + } + regs->eflags &= ~EFLAGS_MASK; + if ( carry ) + regs->eflags |= X86_EFLAGS_CF; + break; +#endif + } + } + else + { + union { + uint32_t u32[2]; + uint64_t u64[2]; + } *old, *aux; + + /* cmpxchg8b/cmpxchg16b */ + generate_exception_if((s->modrm_reg & 7) != 1, X86_EXC_UD); + fail_if(!ops->cmpxchg); + if ( s->rex_prefix & REX_W ) + { + host_and_vcpu_must_have(cx16); + generate_exception_if(!is_aligned(s->ea.mem.seg, s->ea.mem.off, 16, + ctxt, ops), + X86_EXC_GP, 0); + s->op_bytes = 16; + } + else + { + vcpu_must_have(cx8); + s->op_bytes = 8; + } + + old = container_of(&mmvalp->ymm[0], typeof(*old), u64[0]); + aux = container_of(&mmvalp->ymm[2], typeof(*aux), u64[0]); + + /* Get actual old value. */ + if ( (rc = ops->read(s->ea.mem.seg, s->ea.mem.off, old, s->op_bytes, + ctxt)) != X86EMUL_OKAY ) + goto done; + + /* Get expected value. */ + if ( s->op_bytes == 8 ) + { + aux->u32[0] = regs->eax; + aux->u32[1] = regs->edx; + } + else + { + aux->u64[0] = regs->r(ax); + aux->u64[1] = regs->r(dx); + } + + if ( memcmp(old, aux, s->op_bytes) ) + { + cmpxchgNb_failed: + /* Expected != actual: store actual to rDX:rAX and clear ZF. */ + regs->r(ax) = s->op_bytes == 8 ? old->u32[0] : old->u64[0]; + regs->r(dx) = s->op_bytes == 8 ? old->u32[1] : old->u64[1]; + regs->eflags &= ~X86_EFLAGS_ZF; + } + else + { + /* + * Expected == actual: Get proposed value, attempt atomic cmpxchg + * and set ZF if successful. + */ + if ( s->op_bytes == 8 ) + { + aux->u32[0] = regs->ebx; + aux->u32[1] = regs->ecx; + } + else + { + aux->u64[0] = regs->r(bx); + aux->u64[1] = regs->r(cx); + } + + switch ( rc = ops->cmpxchg(s->ea.mem.seg, s->ea.mem.off, old, aux, + s->op_bytes, s->lock_prefix, ctxt) ) + { + case X86EMUL_OKAY: + regs->eflags |= X86_EFLAGS_ZF; + break; + + case X86EMUL_CMPXCHG_FAILED: + rc = X86EMUL_OKAY; + goto cmpxchgNb_failed; + + default: + goto done; + } + } + } + + rc = X86EMUL_OKAY; + + done: + return rc; +} --- a/xen/arch/x86/x86_emulate/Makefile +++ b/xen/arch/x86/x86_emulate/Makefile @@ -1,2 +1,3 @@ obj-y += 0f01.o obj-y += 0fae.o +obj-y += 0fc7.o --- a/xen/arch/x86/x86_emulate/private.h +++ b/xen/arch/x86/x86_emulate/private.h @@ -308,6 +308,14 @@ struct x86_emulate_state { #endif }; +typedef union { + uint64_t mmx; + uint64_t __attribute__ ((aligned(16))) xmm[2]; + uint64_t __attribute__ ((aligned(32))) ymm[4]; + uint64_t __attribute__ ((aligned(64))) zmm[8]; + uint32_t data32[16]; +} mmval_t; + struct x86_fxsr { uint16_t fcw; uint16_t fsw; @@ -558,6 +566,12 @@ int x86emul_0fae(struct x86_emulate_stat struct x86_emulate_ctxt *ctxt, const struct x86_emulate_ops *ops, enum x86_emulate_fpu_type *fpu_type); +int x86emul_0fc7(struct x86_emulate_state *s, + struct cpu_user_regs *regs, + struct operand *dst, + struct x86_emulate_ctxt *ctxt, + const struct x86_emulate_ops *ops, + mmval_t *mmvalp); static inline bool is_aligned(enum x86_segment seg, unsigned long offs, unsigned int size, struct x86_emulate_ctxt *ctxt, --- a/xen/arch/x86/x86_emulate/x86_emulate.c +++ b/xen/arch/x86/x86_emulate/x86_emulate.c @@ -687,17 +687,9 @@ struct x87_env32 { }; #endif -typedef union { - uint64_t mmx; - uint64_t __attribute__ ((aligned(16))) xmm[2]; - uint64_t __attribute__ ((aligned(32))) ymm[4]; - uint64_t __attribute__ ((aligned(64))) zmm[8]; - uint32_t data32[16]; -} mmval_t; - /* - * While proper alignment gets specified above, this doesn't get honored by - * the compiler for automatic variables. Use this helper to instantiate a + * While proper alignment gets specified in mmval_t, this doesn't get honored + * by the compiler for automatic variables. Use this helper to instantiate a * suitably aligned variable, producing a pointer to access it. */ #define DECLARE_ALIGNED(type, var) \ @@ -7680,174 +7672,8 @@ x86_emulate( #endif /* X86EMUL_NO_SIMD */ case X86EMUL_OPC(0x0f, 0xc7): /* Grp9 */ - { - union { - uint32_t u32[2]; - uint64_t u64[2]; - } *old, *aux; - - if ( ea.type == OP_REG ) - { - bool __maybe_unused carry; - - switch ( modrm_reg & 7 ) - { - default: - goto unrecognized_insn; - - case 6: /* rdrand */ -#ifdef HAVE_AS_RDRAND - generate_exception_if(rep_prefix(), EXC_UD); - host_and_vcpu_must_have(rdrand); - dst = ea; - switch ( op_bytes ) - { - case 2: - asm ( "rdrand %w0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - default: -# ifdef __x86_64__ - asm ( "rdrand %k0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - case 8: -# endif - asm ( "rdrand %0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - } - _regs.eflags &= ~EFLAGS_MASK; - if ( carry ) - _regs.eflags |= X86_EFLAGS_CF; - break; -#else - goto unimplemented_insn; -#endif - - case 7: /* rdseed / rdpid */ - if ( repe_prefix() ) /* rdpid */ - { - generate_exception_if(ea.type != OP_REG, EXC_UD); - vcpu_must_have(rdpid); - fail_if(!ops->read_msr); - if ( (rc = ops->read_msr(MSR_TSC_AUX, &msr_val, - ctxt)) != X86EMUL_OKAY ) - goto done; - dst = ea; - dst.val = msr_val; - dst.bytes = 4; - break; - } -#ifdef HAVE_AS_RDSEED - generate_exception_if(rep_prefix(), EXC_UD); - host_and_vcpu_must_have(rdseed); - dst = ea; - switch ( op_bytes ) - { - case 2: - asm ( "rdseed %w0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - default: -# ifdef __x86_64__ - asm ( "rdseed %k0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - case 8: -# endif - asm ( "rdseed %0" ASM_FLAG_OUT(, "; setc %1") - : "=r" (dst.val), ASM_FLAG_OUT("=@ccc", "=qm") (carry) ); - break; - } - _regs.eflags &= ~EFLAGS_MASK; - if ( carry ) - _regs.eflags |= X86_EFLAGS_CF; - break; -#endif - } - break; - } - - /* cmpxchg8b/cmpxchg16b */ - generate_exception_if((modrm_reg & 7) != 1, EXC_UD); - fail_if(!ops->cmpxchg); - if ( rex_prefix & REX_W ) - { - host_and_vcpu_must_have(cx16); - generate_exception_if(!is_aligned(ea.mem.seg, ea.mem.off, 16, - ctxt, ops), - EXC_GP, 0); - op_bytes = 16; - } - else - { - vcpu_must_have(cx8); - op_bytes = 8; - } - - old = container_of(&mmvalp->ymm[0], typeof(*old), u64[0]); - aux = container_of(&mmvalp->ymm[2], typeof(*aux), u64[0]); - - /* Get actual old value. */ - if ( (rc = ops->read(ea.mem.seg, ea.mem.off, old, op_bytes, - ctxt)) != X86EMUL_OKAY ) - goto done; - - /* Get expected value. */ - if ( !(rex_prefix & REX_W) ) - { - aux->u32[0] = _regs.eax; - aux->u32[1] = _regs.edx; - } - else - { - aux->u64[0] = _regs.r(ax); - aux->u64[1] = _regs.r(dx); - } - - if ( memcmp(old, aux, op_bytes) ) - { - cmpxchgNb_failed: - /* Expected != actual: store actual to rDX:rAX and clear ZF. */ - _regs.r(ax) = !(rex_prefix & REX_W) ? old->u32[0] : old->u64[0]; - _regs.r(dx) = !(rex_prefix & REX_W) ? old->u32[1] : old->u64[1]; - _regs.eflags &= ~X86_EFLAGS_ZF; - } - else - { - /* - * Expected == actual: Get proposed value, attempt atomic cmpxchg - * and set ZF if successful. - */ - if ( !(rex_prefix & REX_W) ) - { - aux->u32[0] = _regs.ebx; - aux->u32[1] = _regs.ecx; - } - else - { - aux->u64[0] = _regs.r(bx); - aux->u64[1] = _regs.r(cx); - } - - switch ( rc = ops->cmpxchg(ea.mem.seg, ea.mem.off, old, aux, - op_bytes, lock_prefix, ctxt) ) - { - case X86EMUL_OKAY: - _regs.eflags |= X86_EFLAGS_ZF; - break; - - case X86EMUL_CMPXCHG_FAILED: - rc = X86EMUL_OKAY; - goto cmpxchgNb_failed; - - default: - goto done; - } - } - break; - } + rc = x86emul_0fc7(state, &_regs, &dst, ctxt, ops, mmvalp); + goto dispatch_from_helper; case X86EMUL_OPC(0x0f, 0xc8) ... X86EMUL_OPC(0x0f, 0xcf): /* bswap */ dst.type = OP_REG;
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |