mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 18:50:05 +01:00
x86emul: split off opcode 0fae 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
9ace97ab9b
commit
3e957de632
6 files changed
+315
-238
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
|
||||
OBJS += x86_emulate/0f01.o x86_emulate/0fae.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
|
||||
OBJS += x86_emulate/0f01.o x86_emulate/0fae.o
|
||||
|
||||
$(TARGET): $(OBJS)
|
||||
$(HOSTCC) $(HOSTCFLAGS) -o $@ $^
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/******************************************************************************
|
||||
* 0fae.c - helper for x86_emulate.c
|
||||
*
|
||||
* Generic x86 (32-bit and 64-bit) instruction decoder and emulator.
|
||||
*
|
||||
* 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"
|
||||
|
||||
#if defined(__XEN__) && \
|
||||
(!defined(X86EMUL_NO_FPU) || !defined(X86EMUL_NO_MMX) || \
|
||||
!defined(X86EMUL_NO_SIMD))
|
||||
# include <asm/xstate.h>
|
||||
#endif
|
||||
|
||||
int x86emul_0fae(struct x86_emulate_state *s,
|
||||
struct cpu_user_regs *regs,
|
||||
struct operand *dst,
|
||||
const struct operand *src,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops,
|
||||
enum x86_emulate_fpu_type *fpu_type)
|
||||
#define fpu_type (*fpu_type) /* for get_fpu() */
|
||||
{
|
||||
unsigned long cr4;
|
||||
int rc;
|
||||
|
||||
if ( !s->vex.opcx && (!s->vex.pfx || s->vex.pfx == vex_66) )
|
||||
{
|
||||
switch ( s->modrm_reg & 7 )
|
||||
{
|
||||
#if !defined(X86EMUL_NO_FPU) || !defined(X86EMUL_NO_MMX) || \
|
||||
!defined(X86EMUL_NO_SIMD)
|
||||
case 0: /* fxsave */
|
||||
case 1: /* fxrstor */
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(fxsr);
|
||||
generate_exception_if(s->ea.type != OP_MEM, X86_EXC_UD);
|
||||
generate_exception_if(!is_aligned(s->ea.mem.seg, s->ea.mem.off, 16,
|
||||
ctxt, ops),
|
||||
X86_EXC_GP, 0);
|
||||
fail_if(!ops->blk);
|
||||
s->op_bytes =
|
||||
#ifdef __x86_64__
|
||||
!mode_64bit() ? offsetof(struct x86_fxsr, xmm[8]) :
|
||||
#endif
|
||||
sizeof(struct x86_fxsr);
|
||||
if ( amd_like(ctxt) )
|
||||
{
|
||||
uint64_t msr_val;
|
||||
|
||||
/* Assume "normal" operation in case of missing hooks. */
|
||||
if ( !ops->read_cr ||
|
||||
ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
|
||||
cr4 = X86_CR4_OSFXSR;
|
||||
if ( !ops->read_msr ||
|
||||
ops->read_msr(MSR_EFER, &msr_val, ctxt) != X86EMUL_OKAY )
|
||||
msr_val = 0;
|
||||
if ( !(cr4 & X86_CR4_OSFXSR) ||
|
||||
(mode_64bit() && mode_ring0() && (msr_val & EFER_FFXSE)) )
|
||||
s->op_bytes = offsetof(struct x86_fxsr, xmm[0]);
|
||||
}
|
||||
/*
|
||||
* This could also be X86EMUL_FPU_mmx, but it shouldn't be
|
||||
* X86EMUL_FPU_xmm, as we don't want CR4.OSFXSR checked.
|
||||
*/
|
||||
get_fpu(X86EMUL_FPU_fpu);
|
||||
s->fpu_ctrl = true;
|
||||
s->blk = s->modrm_reg & 1 ? blk_fxrstor : blk_fxsave;
|
||||
if ( (rc = ops->blk(s->ea.mem.seg, s->ea.mem.off, NULL,
|
||||
sizeof(struct x86_fxsr), ®s->eflags,
|
||||
s, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
#endif /* X86EMUL_NO_{FPU,MMX,SIMD} */
|
||||
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
case 2: /* ldmxcsr */
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(sse);
|
||||
ldmxcsr:
|
||||
generate_exception_if(src->type != OP_MEM, X86_EXC_UD);
|
||||
get_fpu(s->vex.opcx ? X86EMUL_FPU_ymm : X86EMUL_FPU_xmm);
|
||||
generate_exception_if(src->val & ~mxcsr_mask, X86_EXC_GP, 0);
|
||||
asm volatile ( "ldmxcsr %0" :: "m" (src->val) );
|
||||
break;
|
||||
|
||||
case 3: /* stmxcsr */
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(sse);
|
||||
stmxcsr:
|
||||
generate_exception_if(dst->type != OP_MEM, X86_EXC_UD);
|
||||
get_fpu(s->vex.opcx ? X86EMUL_FPU_ymm : X86EMUL_FPU_xmm);
|
||||
asm volatile ( "stmxcsr %0" : "=m" (dst->val) );
|
||||
break;
|
||||
#endif /* X86EMUL_NO_SIMD */
|
||||
|
||||
case 5: /* lfence */
|
||||
fail_if(s->modrm_mod != 3);
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(sse2);
|
||||
asm volatile ( "lfence" ::: "memory" );
|
||||
break;
|
||||
case 6:
|
||||
if ( s->modrm_mod == 3 ) /* mfence */
|
||||
{
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(sse2);
|
||||
asm volatile ( "mfence" ::: "memory" );
|
||||
break;
|
||||
}
|
||||
/* else clwb */
|
||||
fail_if(!s->vex.pfx);
|
||||
vcpu_must_have(clwb);
|
||||
fail_if(!ops->cache_op);
|
||||
if ( (rc = ops->cache_op(x86emul_clwb, s->ea.mem.seg, s->ea.mem.off,
|
||||
ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
case 7:
|
||||
if ( s->modrm_mod == 3 ) /* sfence */
|
||||
{
|
||||
generate_exception_if(s->vex.pfx, X86_EXC_UD);
|
||||
vcpu_must_have(mmxext);
|
||||
asm volatile ( "sfence" ::: "memory" );
|
||||
break;
|
||||
}
|
||||
/* else clflush{,opt} */
|
||||
if ( !s->vex.pfx )
|
||||
vcpu_must_have(clflush);
|
||||
else
|
||||
vcpu_must_have(clflushopt);
|
||||
fail_if(!ops->cache_op);
|
||||
if ( (rc = ops->cache_op(s->vex.pfx ? x86emul_clflushopt
|
||||
: x86emul_clflush,
|
||||
s->ea.mem.seg, s->ea.mem.off,
|
||||
ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
return X86EMUL_UNIMPLEMENTED;
|
||||
}
|
||||
}
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
else if ( s->vex.opcx && !s->vex.pfx )
|
||||
{
|
||||
switch ( s->modrm_reg & 7 )
|
||||
{
|
||||
case 2: /* vldmxcsr */
|
||||
generate_exception_if(s->vex.l || s->vex.reg != 0xf, X86_EXC_UD);
|
||||
vcpu_must_have(avx);
|
||||
goto ldmxcsr;
|
||||
case 3: /* vstmxcsr */
|
||||
generate_exception_if(s->vex.l || s->vex.reg != 0xf, X86_EXC_UD);
|
||||
vcpu_must_have(avx);
|
||||
goto stmxcsr;
|
||||
}
|
||||
return X86EMUL_UNRECOGNIZED;
|
||||
}
|
||||
#endif /* !X86EMUL_NO_SIMD */
|
||||
else if ( !s->vex.opcx && s->vex.pfx == vex_f3 )
|
||||
{
|
||||
enum x86_segment seg;
|
||||
struct segment_register sreg;
|
||||
|
||||
fail_if(s->modrm_mod != 3);
|
||||
generate_exception_if((s->modrm_reg & 4) || !mode_64bit(), X86_EXC_UD);
|
||||
fail_if(!ops->read_cr);
|
||||
if ( (rc = ops->read_cr(4, &cr4, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
generate_exception_if(!(cr4 & X86_CR4_FSGSBASE), X86_EXC_UD);
|
||||
seg = s->modrm_reg & 1 ? x86_seg_gs : x86_seg_fs;
|
||||
fail_if(!ops->read_segment);
|
||||
if ( (rc = ops->read_segment(seg, &sreg, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
dst->reg = decode_gpr(regs, s->modrm_rm);
|
||||
if ( !(s->modrm_reg & 2) )
|
||||
{
|
||||
/* rd{f,g}sbase */
|
||||
dst->type = OP_REG;
|
||||
dst->bytes = (s->op_bytes == 8) ? 8 : 4;
|
||||
dst->val = sreg.base;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* wr{f,g}sbase */
|
||||
if ( s->op_bytes == 8 )
|
||||
{
|
||||
sreg.base = *dst->reg;
|
||||
generate_exception_if(!is_canonical_address(sreg.base),
|
||||
X86_EXC_GP, 0);
|
||||
}
|
||||
else
|
||||
sreg.base = (uint32_t)*dst->reg;
|
||||
fail_if(!ops->write_segment);
|
||||
if ( (rc = ops->write_segment(seg, &sreg, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ASSERT_UNREACHABLE();
|
||||
return X86EMUL_UNRECOGNIZED;
|
||||
}
|
||||
|
||||
rc = X86EMUL_OKAY;
|
||||
|
||||
done:
|
||||
return rc;
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
obj-y += 0f01.o
|
||||
obj-y += 0fae.o
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
# include <xen/kernel.h>
|
||||
# include <asm/msr-index.h>
|
||||
# include <asm/x86-vendors.h>
|
||||
# include <asm/x86_emulate.h>
|
||||
|
||||
# ifndef CONFIG_HVM
|
||||
@@ -308,6 +309,29 @@ struct x86_emulate_state {
|
||||
#endif
|
||||
};
|
||||
|
||||
struct x86_fxsr {
|
||||
uint16_t fcw;
|
||||
uint16_t fsw;
|
||||
uint8_t ftw, :8;
|
||||
uint16_t fop;
|
||||
union {
|
||||
struct {
|
||||
uint32_t offs;
|
||||
uint16_t sel, :16;
|
||||
};
|
||||
uint64_t addr;
|
||||
} fip, fdp;
|
||||
uint32_t mxcsr;
|
||||
uint32_t mxcsr_mask;
|
||||
struct {
|
||||
uint8_t data[10];
|
||||
uint16_t :16, :16, :16;
|
||||
} fpreg[8];
|
||||
uint64_t __attribute__ ((aligned(16))) xmm[16][2];
|
||||
uint64_t rsvd[6];
|
||||
uint64_t avl[6];
|
||||
};
|
||||
|
||||
/*
|
||||
* Externally visible return codes from x86_emulate() are non-negative.
|
||||
* Use negative values for internal state change indicators from helpers
|
||||
@@ -397,6 +421,18 @@ in_protmode(
|
||||
(_cpl == 0); \
|
||||
})
|
||||
|
||||
static inline bool
|
||||
_amd_like(const struct cpuid_policy *cp)
|
||||
{
|
||||
return cp->x86_vendor & (X86_VENDOR_AMD | X86_VENDOR_HYGON);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
amd_like(const struct x86_emulate_ctxt *ctxt)
|
||||
{
|
||||
return _amd_like(ctxt->cpuid);
|
||||
}
|
||||
|
||||
#define vcpu_has_fpu() (ctxt->cpuid->basic.fpu)
|
||||
#define vcpu_has_sep() (ctxt->cpuid->basic.sep)
|
||||
#define vcpu_has_cx8() (ctxt->cpuid->basic.cx8)
|
||||
@@ -502,11 +538,52 @@ in_protmode(
|
||||
int x86emul_get_cpl(struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops);
|
||||
|
||||
int x86emul_get_fpu(enum x86_emulate_fpu_type type,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops);
|
||||
|
||||
#define get_fpu(type) \
|
||||
do { \
|
||||
rc = x86emul_get_fpu(fpu_type = (type), ctxt, ops); \
|
||||
if ( rc ) goto done; \
|
||||
} while (0)
|
||||
|
||||
int x86emul_0f01(struct x86_emulate_state *s,
|
||||
struct cpu_user_regs *regs,
|
||||
struct operand *dst,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops);
|
||||
int x86emul_0fae(struct x86_emulate_state *s,
|
||||
struct cpu_user_regs *regs,
|
||||
struct operand *dst,
|
||||
const struct operand *src,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops,
|
||||
enum x86_emulate_fpu_type *fpu_type);
|
||||
|
||||
static inline bool is_aligned(enum x86_segment seg, unsigned long offs,
|
||||
unsigned int size, struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops)
|
||||
{
|
||||
struct segment_register reg;
|
||||
|
||||
/* Expecting powers of two only. */
|
||||
ASSERT(!(size & (size - 1)));
|
||||
|
||||
if ( mode_64bit() && seg < x86_seg_fs )
|
||||
memset(®, 0, sizeof(reg));
|
||||
else
|
||||
{
|
||||
/* No alignment checking when we have no way to read segment data. */
|
||||
if ( !ops->read_segment )
|
||||
return true;
|
||||
|
||||
if ( ops->read_segment(seg, ®, ctxt) != X86EMUL_OKAY )
|
||||
return false;
|
||||
}
|
||||
|
||||
return !((reg.base + offs) & (size - 1));
|
||||
}
|
||||
|
||||
static inline bool umip_active(struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops)
|
||||
|
||||
@@ -695,29 +695,6 @@ typedef union {
|
||||
uint32_t data32[16];
|
||||
} mmval_t;
|
||||
|
||||
struct x86_fxsr {
|
||||
uint16_t fcw;
|
||||
uint16_t fsw;
|
||||
uint8_t ftw, :8;
|
||||
uint16_t fop;
|
||||
union {
|
||||
struct {
|
||||
uint32_t offs;
|
||||
uint16_t sel, :16;
|
||||
};
|
||||
uint64_t addr;
|
||||
} fip, fdp;
|
||||
uint32_t mxcsr;
|
||||
uint32_t mxcsr_mask;
|
||||
struct {
|
||||
uint8_t data[10];
|
||||
uint16_t :16, :16, :16;
|
||||
} fpreg[8];
|
||||
uint64_t __attribute__ ((aligned(16))) xmm[16][2];
|
||||
uint64_t rsvd[6];
|
||||
uint64_t avl[6];
|
||||
};
|
||||
|
||||
/*
|
||||
* While proper alignment gets specified above, this doesn't get honored by
|
||||
* the compiler for automatic variables. Use this helper to instantiate a
|
||||
@@ -1063,7 +1040,7 @@ do { \
|
||||
ops->write_segment(x86_seg_cs, cs, ctxt); \
|
||||
})
|
||||
|
||||
static int _get_fpu(
|
||||
int x86emul_get_fpu(
|
||||
enum x86_emulate_fpu_type type,
|
||||
struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops)
|
||||
@@ -1102,7 +1079,7 @@ static int _get_fpu(
|
||||
break;
|
||||
}
|
||||
|
||||
rc = ops->get_fpu(type, ctxt);
|
||||
rc = (ops->get_fpu)(type, ctxt);
|
||||
|
||||
if ( rc == X86EMUL_OKAY )
|
||||
{
|
||||
@@ -1146,12 +1123,6 @@ static int _get_fpu(
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define get_fpu(type) \
|
||||
do { \
|
||||
rc = _get_fpu(fpu_type = (type), ctxt, ops); \
|
||||
if ( rc ) goto done; \
|
||||
} while (0)
|
||||
|
||||
static void put_fpu(
|
||||
enum x86_emulate_fpu_type type,
|
||||
bool failed_late,
|
||||
@@ -1556,18 +1527,6 @@ static int ioport_access_check(
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool
|
||||
_amd_like(const struct cpuid_policy *cp)
|
||||
{
|
||||
return cp->x86_vendor & (X86_VENDOR_AMD | X86_VENDOR_HYGON);
|
||||
}
|
||||
|
||||
static bool
|
||||
amd_like(const struct x86_emulate_ctxt *ctxt)
|
||||
{
|
||||
return _amd_like(ctxt->cpuid);
|
||||
}
|
||||
|
||||
/* Initialise output state in x86_emulate_ctxt */
|
||||
static void init_context(struct x86_emulate_ctxt *ctxt)
|
||||
{
|
||||
@@ -1980,30 +1939,6 @@ static unsigned int decode_disp8scale(enum disp8scale scale,
|
||||
} \
|
||||
} while ( false )
|
||||
|
||||
static bool is_aligned(enum x86_segment seg, unsigned long offs,
|
||||
unsigned int size, struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops)
|
||||
{
|
||||
struct segment_register reg;
|
||||
|
||||
/* Expecting powers of two only. */
|
||||
ASSERT(!(size & (size - 1)));
|
||||
|
||||
if ( mode_64bit() && seg < x86_seg_fs )
|
||||
memset(®, 0, sizeof(reg));
|
||||
else
|
||||
{
|
||||
/* No alignment checking when we have no way to read segment data. */
|
||||
if ( !ops->read_segment )
|
||||
return true;
|
||||
|
||||
if ( ops->read_segment(seg, ®, ctxt) != X86EMUL_OKAY )
|
||||
return false;
|
||||
}
|
||||
|
||||
return !((reg.base + offs) & (size - 1));
|
||||
}
|
||||
|
||||
static bool is_branch_step(struct x86_emulate_ctxt *ctxt,
|
||||
const struct x86_emulate_ops *ops)
|
||||
{
|
||||
@@ -3346,7 +3281,8 @@ x86_emulate(
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
/* With a memory operand, fetch the mask register in use (if any). */
|
||||
if ( ea.type == OP_MEM && evex.opmsk &&
|
||||
_get_fpu(fpu_type = X86EMUL_FPU_opmask, ctxt, ops) == X86EMUL_OKAY )
|
||||
x86emul_get_fpu(fpu_type = X86EMUL_FPU_opmask,
|
||||
ctxt, ops) == X86EMUL_OKAY )
|
||||
{
|
||||
uint8_t *stb = get_stub(stub);
|
||||
|
||||
@@ -3369,7 +3305,7 @@ x86_emulate(
|
||||
|
||||
if ( fpu_type == X86EMUL_FPU_opmask )
|
||||
{
|
||||
/* Squash (side) effects of the _get_fpu() above. */
|
||||
/* Squash (side) effects of the x86emul_get_fpu() above. */
|
||||
x86_emul_reset_event(ctxt);
|
||||
put_fpu(X86EMUL_FPU_opmask, false, state, ctxt, ops);
|
||||
fpu_type = X86EMUL_FPU_none;
|
||||
@@ -7435,173 +7371,14 @@ x86_emulate(
|
||||
emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags);
|
||||
break;
|
||||
|
||||
case X86EMUL_OPC(0x0f, 0xae): case X86EMUL_OPC_66(0x0f, 0xae): /* Grp15 */
|
||||
switch ( modrm_reg & 7 )
|
||||
{
|
||||
#if !defined(X86EMUL_NO_FPU) || !defined(X86EMUL_NO_MMX) || \
|
||||
!defined(X86EMUL_NO_SIMD)
|
||||
case 0: /* fxsave */
|
||||
case 1: /* fxrstor */
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(fxsr);
|
||||
generate_exception_if(ea.type != OP_MEM, EXC_UD);
|
||||
generate_exception_if(!is_aligned(ea.mem.seg, ea.mem.off, 16,
|
||||
ctxt, ops),
|
||||
EXC_GP, 0);
|
||||
fail_if(!ops->blk);
|
||||
op_bytes =
|
||||
#ifdef __x86_64__
|
||||
!mode_64bit() ? offsetof(struct x86_fxsr, xmm[8]) :
|
||||
case X86EMUL_OPC(0x0f, 0xae): /* Grp15 */
|
||||
case X86EMUL_OPC_66(0x0f, 0xae):
|
||||
case X86EMUL_OPC_F3(0x0f, 0xae):
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
case X86EMUL_OPC_VEX(0x0f, 0xae):
|
||||
#endif
|
||||
sizeof(struct x86_fxsr);
|
||||
if ( amd_like(ctxt) )
|
||||
{
|
||||
/* Assume "normal" operation in case of missing hooks. */
|
||||
if ( !ops->read_cr ||
|
||||
ops->read_cr(4, &cr4, ctxt) != X86EMUL_OKAY )
|
||||
cr4 = X86_CR4_OSFXSR;
|
||||
if ( !ops->read_msr ||
|
||||
ops->read_msr(MSR_EFER, &msr_val, ctxt) != X86EMUL_OKAY )
|
||||
msr_val = 0;
|
||||
if ( !(cr4 & X86_CR4_OSFXSR) ||
|
||||
(mode_64bit() && mode_ring0() && (msr_val & EFER_FFXSE)) )
|
||||
op_bytes = offsetof(struct x86_fxsr, xmm[0]);
|
||||
}
|
||||
/*
|
||||
* This could also be X86EMUL_FPU_mmx, but it shouldn't be
|
||||
* X86EMUL_FPU_xmm, as we don't want CR4.OSFXSR checked.
|
||||
*/
|
||||
get_fpu(X86EMUL_FPU_fpu);
|
||||
state->fpu_ctrl = true;
|
||||
state->blk = modrm_reg & 1 ? blk_fxrstor : blk_fxsave;
|
||||
if ( (rc = ops->blk(ea.mem.seg, ea.mem.off, NULL,
|
||||
sizeof(struct x86_fxsr), &_regs.eflags,
|
||||
state, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
#endif /* X86EMUL_NO_{FPU,MMX,SIMD} */
|
||||
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
case 2: /* ldmxcsr */
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(sse);
|
||||
ldmxcsr:
|
||||
generate_exception_if(src.type != OP_MEM, EXC_UD);
|
||||
get_fpu(vex.opcx ? X86EMUL_FPU_ymm : X86EMUL_FPU_xmm);
|
||||
generate_exception_if(src.val & ~mxcsr_mask, EXC_GP, 0);
|
||||
asm volatile ( "ldmxcsr %0" :: "m" (src.val) );
|
||||
break;
|
||||
|
||||
case 3: /* stmxcsr */
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(sse);
|
||||
stmxcsr:
|
||||
generate_exception_if(dst.type != OP_MEM, EXC_UD);
|
||||
get_fpu(vex.opcx ? X86EMUL_FPU_ymm : X86EMUL_FPU_xmm);
|
||||
asm volatile ( "stmxcsr %0" : "=m" (dst.val) );
|
||||
break;
|
||||
#endif /* X86EMUL_NO_SIMD */
|
||||
|
||||
case 5: /* lfence */
|
||||
fail_if(modrm_mod != 3);
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(sse2);
|
||||
asm volatile ( "lfence" ::: "memory" );
|
||||
break;
|
||||
case 6:
|
||||
if ( modrm_mod == 3 ) /* mfence */
|
||||
{
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(sse2);
|
||||
asm volatile ( "mfence" ::: "memory" );
|
||||
break;
|
||||
}
|
||||
/* else clwb */
|
||||
fail_if(!vex.pfx);
|
||||
vcpu_must_have(clwb);
|
||||
fail_if(!ops->cache_op);
|
||||
if ( (rc = ops->cache_op(x86emul_clwb, ea.mem.seg, ea.mem.off,
|
||||
ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
case 7:
|
||||
if ( modrm_mod == 3 ) /* sfence */
|
||||
{
|
||||
generate_exception_if(vex.pfx, EXC_UD);
|
||||
vcpu_must_have(mmxext);
|
||||
asm volatile ( "sfence" ::: "memory" );
|
||||
break;
|
||||
}
|
||||
/* else clflush{,opt} */
|
||||
if ( !vex.pfx )
|
||||
vcpu_must_have(clflush);
|
||||
else
|
||||
vcpu_must_have(clflushopt);
|
||||
fail_if(!ops->cache_op);
|
||||
if ( (rc = ops->cache_op(vex.pfx ? x86emul_clflushopt
|
||||
: x86emul_clflush,
|
||||
ea.mem.seg, ea.mem.off,
|
||||
ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
goto unimplemented_insn;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef X86EMUL_NO_SIMD
|
||||
|
||||
case X86EMUL_OPC_VEX(0x0f, 0xae): /* Grp15 */
|
||||
switch ( modrm_reg & 7 )
|
||||
{
|
||||
case 2: /* vldmxcsr */
|
||||
generate_exception_if(vex.l || vex.reg != 0xf, EXC_UD);
|
||||
vcpu_must_have(avx);
|
||||
goto ldmxcsr;
|
||||
case 3: /* vstmxcsr */
|
||||
generate_exception_if(vex.l || vex.reg != 0xf, EXC_UD);
|
||||
vcpu_must_have(avx);
|
||||
goto stmxcsr;
|
||||
}
|
||||
goto unrecognized_insn;
|
||||
|
||||
#endif /* !X86EMUL_NO_SIMD */
|
||||
|
||||
case X86EMUL_OPC_F3(0x0f, 0xae): /* Grp15 */
|
||||
fail_if(modrm_mod != 3);
|
||||
generate_exception_if((modrm_reg & 4) || !mode_64bit(), EXC_UD);
|
||||
fail_if(!ops->read_cr);
|
||||
if ( (rc = ops->read_cr(4, &cr4, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
generate_exception_if(!(cr4 & X86_CR4_FSGSBASE), EXC_UD);
|
||||
seg = modrm_reg & 1 ? x86_seg_gs : x86_seg_fs;
|
||||
fail_if(!ops->read_segment);
|
||||
if ( (rc = ops->read_segment(seg, &sreg, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
dst.reg = decode_gpr(&_regs, modrm_rm);
|
||||
if ( !(modrm_reg & 2) )
|
||||
{
|
||||
/* rd{f,g}sbase */
|
||||
dst.type = OP_REG;
|
||||
dst.bytes = (op_bytes == 8) ? 8 : 4;
|
||||
dst.val = sreg.base;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* wr{f,g}sbase */
|
||||
if ( op_bytes == 8 )
|
||||
{
|
||||
sreg.base = *dst.reg;
|
||||
generate_exception_if(!is_canonical_address(sreg.base),
|
||||
EXC_GP, 0);
|
||||
}
|
||||
else
|
||||
sreg.base = (uint32_t)*dst.reg;
|
||||
fail_if(!ops->write_segment);
|
||||
if ( (rc = ops->write_segment(seg, &sreg, ctxt)) != X86EMUL_OKAY )
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
rc = x86emul_0fae(state, &_regs, &dst, &src, ctxt, ops, &fpu_type);
|
||||
goto dispatch_from_helper;
|
||||
|
||||
case X86EMUL_OPC(0x0f, 0xaf): /* imul */
|
||||
emulate_2op_SrcV_srcmem("imul", src, dst, _regs.eflags);
|
||||
@@ -10543,7 +10320,7 @@ x86_emulate(
|
||||
goto unrecognized_insn;
|
||||
|
||||
default:
|
||||
unimplemented_insn:
|
||||
unimplemented_insn: __maybe_unused;
|
||||
rc = X86EMUL_UNIMPLEMENTED;
|
||||
goto done;
|
||||
unrecognized_insn:
|
||||
|
||||