mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
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@suse.com> Acked-by: Roger Pau Monné <roger.pau@citrix.com>
This commit is contained in:
1 parent
3e957de632
commit
0bae69c96b
6 files changed
+231
-180
No files matched your search
@@ -32,7 +32,7 @@ GCOV_FLAGS := --coverage
|
||||
$(CC) -c $(CFLAGS) $(GCOV_FLAGS) $< -o $@
|
||||
|
||||
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.h: x86_emulate/x86_emulate.h
|
||||
x86-emulate.o x86-emulate-cov.o: x86-emulate.h x86_emulate/x86_emulate.c x86_emulate/private.h
|
||||
|
||||
@@ -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 $@ $^
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
obj-y += 0f01.o
|
||||
obj-y += 0fae.o
|
||||
obj-y += 0fc7.o
|
||||
@@ -309,6 +309,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;
|
||||
@@ -560,6 +568,12 @@ int x86emul_0fae(struct x86_emulate_state *s,
|
||||
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,
|
||||
|
||||
@@ -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) \
|
||||
@@ -7682,174 +7674,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;
|
||||
|
||||