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:
Jan Beulich committed 2023-04-03 12:43:51 +02:00
1 parent 3e957de632
commit 0bae69c96b
6 files changed
+231 -180

No files matched your search

+1 -1
View File
@@ -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
+1 -1
View File
@@ -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 $@ $^
+210
View File
@@ -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
View File
@@ -1,2 +1,3 @@
obj-y += 0f01.o
obj-y += 0fae.o
obj-y += 0fc7.o
+14
View File
@@ -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,
+4 -178
View File
@@ -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;
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.