Many are needed by the hypervisor only - have one file for this purpose.
Some are also needed by the harness (but not the fuzzer) - have another
file for these.
Code moved gets slightly adjusted in a few places, e.g. replacing
"state" by "s" (like was done for other that has been split off).
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
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 x86_emulate/0fc7.o
-OBJS += x86_emulate/blk.o x86_emulate/decode.o x86_emulate/fpu.o
+OBJS += x86_emulate/blk.o x86_emulate/decode.o x86_emulate/fpu.o x86_emulate/util.o
$(TARGET): $(OBJS)
$(HOSTCC) $(HOSTCFLAGS) -o $@ $^
#include <asm/processor.h> /* current_cpu_info */
#include <asm/xstate.h>
#include <asm/amd.h> /* cpu_has_amd_erratum() */
-#include <asm/debugreg.h>
/* Avoid namespace pollution. */
#undef cmpxchg
#include "x86_emulate/x86_emulate.c"
-int cf_check x86emul_read_xcr(
- unsigned int reg, uint64_t *val, struct x86_emulate_ctxt *ctxt)
-{
- switch ( reg )
- {
- case 0:
- *val = current->arch.xcr0;
- return X86EMUL_OKAY;
-
- case 1:
- if ( current->domain->arch.cpuid->xstate.xgetbv1 )
- break;
- /* fall through */
- default:
- x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
- return X86EMUL_EXCEPTION;
- }
-
- *val = xgetbv(reg);
-
- return X86EMUL_OKAY;
-}
-
-/* Note: May be called with ctxt=NULL. */
-int cf_check x86emul_write_xcr(
- unsigned int reg, uint64_t val, struct x86_emulate_ctxt *ctxt)
-{
- switch ( reg )
- {
- case 0:
- break;
-
- default:
- gp_fault:
- if ( ctxt )
- x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
- return X86EMUL_EXCEPTION;
- }
-
- if ( unlikely(handle_xsetbv(reg, val) != 0) )
- goto gp_fault;
-
- return X86EMUL_OKAY;
-}
-
-#ifdef CONFIG_PV
-/* Called with NULL ctxt in hypercall context. */
-int cf_check x86emul_read_dr(
- unsigned int reg, unsigned long *val, struct x86_emulate_ctxt *ctxt)
-{
- struct vcpu *curr = current;
-
- /* HVM support requires a bit more plumbing before it will work. */
- ASSERT(is_pv_vcpu(curr));
-
- switch ( reg )
- {
- case 0 ... 3:
- *val = array_access_nospec(curr->arch.dr, reg);
- break;
-
- case 4:
- if ( curr->arch.pv.ctrlreg[4] & X86_CR4_DE )
- goto ud_fault;
-
- /* Fallthrough */
- case 6:
- *val = curr->arch.dr6;
- break;
-
- case 5:
- if ( curr->arch.pv.ctrlreg[4] & X86_CR4_DE )
- goto ud_fault;
-
- /* Fallthrough */
- case 7:
- *val = curr->arch.dr7 | curr->arch.pv.dr7_emul;
- break;
-
- ud_fault:
- default:
- if ( ctxt )
- x86_emul_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC, ctxt);
-
- return X86EMUL_EXCEPTION;
- }
-
- return X86EMUL_OKAY;
-}
-
-int cf_check x86emul_write_dr(
- unsigned int reg, unsigned long val, struct x86_emulate_ctxt *ctxt)
-{
- struct vcpu *curr = current;
-
- /* HVM support requires a bit more plumbing before it will work. */
- ASSERT(is_pv_vcpu(curr));
-
- switch ( set_debugreg(curr, reg, val) )
- {
- case 0:
- return X86EMUL_OKAY;
-
- case -ENODEV:
- x86_emul_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC, ctxt);
- return X86EMUL_EXCEPTION;
-
- default:
- x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
- return X86EMUL_EXCEPTION;
- }
-}
-#endif /* CONFIG_PV */
-
-int cf_check x86emul_cpuid(
- uint32_t leaf, uint32_t subleaf, struct cpuid_leaf *res,
- struct x86_emulate_ctxt *ctxt)
-{
- guest_cpuid(current, leaf, subleaf, res);
-
- return X86EMUL_OKAY;
-}
-
/*
* Local variables:
* mode: C
obj-y += blk.o
obj-y += decode.o
obj-$(CONFIG_HVM) += fpu.o
+obj-y += util.o
+obj-y += util-xen.o
#endif
};
+static inline void check_state(const struct x86_emulate_state *s)
+{
+#if defined(__XEN__) && !defined(NDEBUG)
+ ASSERT(s->caller);
+#endif
+}
+
typedef union {
uint64_t mmx;
uint64_t __attribute__ ((aligned(16))) xmm[2];
--- /dev/null
+/******************************************************************************
+ * util-xen.c
+ *
+ * Generic x86 (32-bit and 64-bit) instruction decoder and emulator hypervisor-
+ * only utility functions.
+ *
+ * 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"
+
+#include <xen/nospec.h>
+#include <xen/sched.h>
+#include <asm/debugreg.h>
+#include <asm/xstate.h>
+
+#ifndef NDEBUG
+void x86_emulate_free_state(struct x86_emulate_state *s)
+{
+ check_state(s);
+ s->caller = NULL;
+}
+#endif
+
+unsigned int x86_insn_opsize(const struct x86_emulate_state *s)
+{
+ check_state(s);
+
+ return s->op_bytes << 3;
+}
+
+int x86_insn_modrm(const struct x86_emulate_state *s,
+ unsigned int *rm, unsigned int *reg)
+{
+ check_state(s);
+
+ if ( unlikely(s->modrm_mod > 3) )
+ {
+ if ( rm )
+ *rm = ~0U;
+ if ( reg )
+ *reg = ~0U;
+ return -EINVAL;
+ }
+
+ if ( rm )
+ *rm = s->modrm_rm;
+ if ( reg )
+ *reg = s->modrm_reg;
+
+ return s->modrm_mod;
+}
+
+unsigned long x86_insn_operand_ea(const struct x86_emulate_state *s,
+ enum x86_segment *seg)
+{
+ *seg = s->ea.type == OP_MEM ? s->ea.mem.seg : x86_seg_none;
+
+ check_state(s);
+
+ return s->ea.mem.off;
+}
+
+bool cf_check x86_insn_is_portio(const struct x86_emulate_state *s,
+ const struct x86_emulate_ctxt *ctxt)
+{
+ switch ( ctxt->opcode )
+ {
+ case 0x6c ... 0x6f: /* INS / OUTS */
+ case 0xe4 ... 0xe7: /* IN / OUT imm8 */
+ case 0xec ... 0xef: /* IN / OUT %dx */
+ return true;
+ }
+
+ return false;
+}
+
+bool cf_check x86_insn_is_cr_access(const struct x86_emulate_state *s,
+ const struct x86_emulate_ctxt *ctxt)
+{
+ switch ( ctxt->opcode )
+ {
+ unsigned int ext;
+
+ case X86EMUL_OPC(0x0f, 0x01):
+ if ( x86_insn_modrm(s, NULL, &ext) >= 0
+ && (ext & 5) == 4 ) /* SMSW / LMSW */
+ return true;
+ break;
+
+ case X86EMUL_OPC(0x0f, 0x06): /* CLTS */
+ case X86EMUL_OPC(0x0f, 0x20): /* MOV from CRn */
+ case X86EMUL_OPC(0x0f, 0x22): /* MOV to CRn */
+ return true;
+ }
+
+ return false;
+}
+
+unsigned long x86_insn_immediate(const struct x86_emulate_state *s,
+ unsigned int nr)
+{
+ check_state(s);
+
+ switch ( nr )
+ {
+ case 0:
+ return s->imm1;
+ case 1:
+ return s->imm2;
+ }
+
+ return 0;
+}
+
+int cf_check x86emul_read_xcr(unsigned int reg, uint64_t *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ switch ( reg )
+ {
+ case 0:
+ *val = current->arch.xcr0;
+ return X86EMUL_OKAY;
+
+ case 1:
+ if ( current->domain->arch.cpuid->xstate.xgetbv1 )
+ break;
+ /* fall through */
+ default:
+ x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+
+ *val = xgetbv(reg);
+
+ return X86EMUL_OKAY;
+}
+
+/* Note: May be called with ctxt=NULL. */
+int cf_check x86emul_write_xcr(unsigned int reg, uint64_t val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ switch ( reg )
+ {
+ case 0:
+ break;
+
+ default:
+ gp_fault:
+ if ( ctxt )
+ x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+
+ if ( unlikely(handle_xsetbv(reg, val) != 0) )
+ goto gp_fault;
+
+ return X86EMUL_OKAY;
+}
+
+#ifdef CONFIG_PV
+
+/* Called with NULL ctxt in hypercall context. */
+int cf_check x86emul_read_dr(unsigned int reg, unsigned long *val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct vcpu *curr = current;
+
+ /* HVM support requires a bit more plumbing before it will work. */
+ ASSERT(is_pv_vcpu(curr));
+
+ switch ( reg )
+ {
+ case 0 ... 3:
+ *val = array_access_nospec(curr->arch.dr, reg);
+ break;
+
+ case 4:
+ if ( curr->arch.pv.ctrlreg[4] & X86_CR4_DE )
+ goto ud_fault;
+
+ /* Fallthrough */
+ case 6:
+ *val = curr->arch.dr6;
+ break;
+
+ case 5:
+ if ( curr->arch.pv.ctrlreg[4] & X86_CR4_DE )
+ goto ud_fault;
+
+ /* Fallthrough */
+ case 7:
+ *val = curr->arch.dr7 | curr->arch.pv.dr7_emul;
+ break;
+
+ ud_fault:
+ default:
+ if ( ctxt )
+ x86_emul_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC, ctxt);
+
+ return X86EMUL_EXCEPTION;
+ }
+
+ return X86EMUL_OKAY;
+}
+
+int cf_check x86emul_write_dr(unsigned int reg, unsigned long val,
+ struct x86_emulate_ctxt *ctxt)
+{
+ struct vcpu *curr = current;
+
+ /* HVM support requires a bit more plumbing before it will work. */
+ ASSERT(is_pv_vcpu(curr));
+
+ switch ( set_debugreg(curr, reg, val) )
+ {
+ case 0:
+ return X86EMUL_OKAY;
+
+ case -ENODEV:
+ x86_emul_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC, ctxt);
+ return X86EMUL_EXCEPTION;
+
+ default:
+ x86_emul_hw_exception(TRAP_gp_fault, 0, ctxt);
+ return X86EMUL_EXCEPTION;
+ }
+}
+
+#endif /* CONFIG_PV */
+
+int cf_check x86emul_cpuid(uint32_t leaf, uint32_t subleaf,
+ struct cpuid_leaf *res,
+ struct x86_emulate_ctxt *ctxt)
+{
+ guest_cpuid(current, leaf, subleaf, res);
+
+ return X86EMUL_OKAY;
+}
--- /dev/null
+/******************************************************************************
+ * util.c
+ *
+ * Generic x86 (32-bit and 64-bit) instruction decoder and emulator utility
+ * functions.
+ *
+ * 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"
+
+unsigned int x86_insn_length(const struct x86_emulate_state *s,
+ const struct x86_emulate_ctxt *ctxt)
+{
+ check_state(s);
+
+ return s->ip - ctxt->regs->r(ip);
+}
+
+/*
+ * This function means to return 'true' for all supported insns with explicit
+ * accesses to memory. This means also insns which don't have an explicit
+ * memory operand (like POP), but it does not mean e.g. segment selector
+ * loads, where the descriptor table access is considered an implicit one.
+ */
+bool cf_check x86_insn_is_mem_access(const struct x86_emulate_state *s,
+ const struct x86_emulate_ctxt *ctxt)
+{
+ if ( mode_64bit() && s->not_64bit )
+ return false;
+
+ if ( s->ea.type == OP_MEM )
+ {
+ switch ( ctxt->opcode )
+ {
+ case 0x8d: /* LEA */
+ case X86EMUL_OPC(0x0f, 0x0d): /* PREFETCH */
+ case X86EMUL_OPC(0x0f, 0x18)
+ ... X86EMUL_OPC(0x0f, 0x1f): /* NOP space */
+ case X86EMUL_OPC_66(0x0f, 0x18)
+ ... X86EMUL_OPC_66(0x0f, 0x1f): /* NOP space */
+ case X86EMUL_OPC_F3(0x0f, 0x18)
+ ... X86EMUL_OPC_F3(0x0f, 0x1f): /* NOP space */
+ case X86EMUL_OPC_F2(0x0f, 0x18)
+ ... X86EMUL_OPC_F2(0x0f, 0x1f): /* NOP space */
+ case X86EMUL_OPC(0x0f, 0xb9): /* UD1 */
+ case X86EMUL_OPC(0x0f, 0xff): /* UD0 */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0xc6): /* V{GATH,SCATT}ERPF*D* */
+ case X86EMUL_OPC_EVEX_66(0x0f38, 0xc7): /* V{GATH,SCATT}ERPF*Q* */
+ return false;
+
+ case X86EMUL_OPC(0x0f, 0x01):
+ return (s->modrm_reg & 7) != 7; /* INVLPG */
+
+ case X86EMUL_OPC(0x0f, 0xae):
+ return (s->modrm_reg & 7) != 7; /* CLFLUSH */
+
+ case X86EMUL_OPC_66(0x0f, 0xae):
+ return (s->modrm_reg & 7) < 6; /* CLWB, CLFLUSHOPT */
+ }
+
+ return true;
+ }
+
+ switch ( ctxt->opcode )
+ {
+ case 0x06 ... 0x07: /* PUSH / POP %es */
+ case 0x0e: /* PUSH %cs */
+ case 0x16 ... 0x17: /* PUSH / POP %ss */
+ case 0x1e ... 0x1f: /* PUSH / POP %ds */
+ case 0x50 ... 0x5f: /* PUSH / POP reg */
+ case 0x60 ... 0x61: /* PUSHA / POPA */
+ case 0x68: case 0x6a: /* PUSH imm */
+ case 0x6c ... 0x6f: /* INS / OUTS */
+ case 0x8f: /* POP r/m */
+ case 0x9a: /* CALL (far, direct) */
+ case 0x9c ... 0x9d: /* PUSHF / POPF */
+ case 0xa4 ... 0xa7: /* MOVS / CMPS */
+ case 0xaa ... 0xaf: /* STOS / LODS / SCAS */
+ case 0xc2 ... 0xc3: /* RET (near) */
+ case 0xc8 ... 0xc9: /* ENTER / LEAVE */
+ case 0xca ... 0xcb: /* RET (far) */
+ case 0xd7: /* XLAT */
+ case 0xe8: /* CALL (near, direct) */
+ case X86EMUL_OPC(0x0f, 0xa0): /* PUSH %fs */
+ case X86EMUL_OPC(0x0f, 0xa1): /* POP %fs */
+ case X86EMUL_OPC(0x0f, 0xa8): /* PUSH %gs */
+ case X86EMUL_OPC(0x0f, 0xa9): /* POP %gs */
+ case X86EMUL_OPC(0x0f, 0xf7): /* MASKMOVQ */
+ case X86EMUL_OPC_66(0x0f, 0xf7): /* MASKMOVDQU */
+ case X86EMUL_OPC_VEX_66(0x0f, 0xf7): /* VMASKMOVDQU */
+ return true;
+
+ case 0xff:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 2: /* CALL (near, indirect) */
+ case 6: /* PUSH r/m */
+ return true;
+ }
+ break;
+
+ case X86EMUL_OPC(0x0f, 0x01):
+ /* Cover CLZERO. */
+ return (s->modrm_rm & 7) == 4 && (s->modrm_reg & 7) == 7;
+ }
+
+ return false;
+}
+
+/*
+ * This function means to return 'true' for all supported insns with explicit
+ * writes to memory. This means also insns which don't have an explicit
+ * memory operand (like PUSH), but it does not mean e.g. segment selector
+ * loads, where the (possible) descriptor table write is considered an
+ * implicit access.
+ */
+bool cf_check x86_insn_is_mem_write(const struct x86_emulate_state *s,
+ const struct x86_emulate_ctxt *ctxt)
+{
+ if ( mode_64bit() && s->not_64bit )
+ return false;
+
+ switch ( s->desc & DstMask )
+ {
+ case DstMem:
+ /* The SrcMem check is to cover {,V}MASKMOV{Q,DQU}. */
+ return s->modrm_mod != 3 || (s->desc & SrcMask) == SrcMem;
+
+ case DstBitBase:
+ case DstImplicit:
+ break;
+
+ default:
+ switch ( ctxt->opcode )
+ {
+ case 0x63: /* ARPL */
+ return !mode_64bit();
+
+ case X86EMUL_OPC_66(0x0f38, 0xf8): /* MOVDIR64B */
+ case X86EMUL_OPC_F2(0x0f38, 0xf8): /* ENQCMD */
+ case X86EMUL_OPC_F3(0x0f38, 0xf8): /* ENQCMDS */
+ return true;
+
+ case X86EMUL_OPC_EVEX_F3(0x0f38, 0x10) ...
+ X86EMUL_OPC_EVEX_F3(0x0f38, 0x15): /* VPMOVUS* */
+ case X86EMUL_OPC_EVEX_F3(0x0f38, 0x20) ...
+ X86EMUL_OPC_EVEX_F3(0x0f38, 0x25): /* VPMOVS* */
+ case X86EMUL_OPC_EVEX_F3(0x0f38, 0x30) ...
+ X86EMUL_OPC_EVEX_F3(0x0f38, 0x35): /* VPMOV{D,Q,W}* */
+ return s->modrm_mod != 3;
+ }
+
+ return false;
+ }
+
+ if ( s->modrm_mod == 3 )
+ {
+ switch ( ctxt->opcode )
+ {
+ case 0xff: /* Grp5 */
+ break;
+
+ case X86EMUL_OPC(0x0f, 0x01): /* CLZERO is the odd one. */
+ return (s->modrm_rm & 7) == 4 && (s->modrm_reg & 7) == 7;
+
+ default:
+ return false;
+ }
+ }
+
+ switch ( ctxt->opcode )
+ {
+ case 0x06: /* PUSH %es */
+ case 0x0e: /* PUSH %cs */
+ case 0x16: /* PUSH %ss */
+ case 0x1e: /* PUSH %ds */
+ case 0x50 ... 0x57: /* PUSH reg */
+ case 0x60: /* PUSHA */
+ case 0x68: case 0x6a: /* PUSH imm */
+ case 0x6c: case 0x6d: /* INS */
+ case 0x9a: /* CALL (far, direct) */
+ case 0x9c: /* PUSHF */
+ case 0xa4: case 0xa5: /* MOVS */
+ case 0xaa: case 0xab: /* STOS */
+ case 0xc8: /* ENTER */
+ case 0xe8: /* CALL (near, direct) */
+ case X86EMUL_OPC(0x0f, 0xa0): /* PUSH %fs */
+ case X86EMUL_OPC(0x0f, 0xa8): /* PUSH %gs */
+ case X86EMUL_OPC(0x0f, 0xab): /* BTS */
+ case X86EMUL_OPC(0x0f, 0xb3): /* BTR */
+ case X86EMUL_OPC(0x0f, 0xbb): /* BTC */
+ return true;
+
+ case 0xd9:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 2: /* FST m32fp */
+ case 3: /* FSTP m32fp */
+ case 6: /* FNSTENV */
+ case 7: /* FNSTCW */
+ return true;
+ }
+ break;
+
+ case 0xdb:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 1: /* FISTTP m32i */
+ case 2: /* FIST m32i */
+ case 3: /* FISTP m32i */
+ case 7: /* FSTP m80fp */
+ return true;
+ }
+ break;
+
+ case 0xdd:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 1: /* FISTTP m64i */
+ case 2: /* FST m64fp */
+ case 3: /* FSTP m64fp */
+ case 6: /* FNSAVE */
+ case 7: /* FNSTSW */
+ return true;
+ }
+ break;
+
+ case 0xdf:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 1: /* FISTTP m16i */
+ case 2: /* FIST m16i */
+ case 3: /* FISTP m16i */
+ case 6: /* FBSTP */
+ case 7: /* FISTP m64i */
+ return true;
+ }
+ break;
+
+ case 0xff:
+ switch ( s->modrm_reg & 7 )
+ {
+ case 2: /* CALL (near, indirect) */
+ case 3: /* CALL (far, indirect) */
+ case 6: /* PUSH r/m */
+ return true;
+ }
+ break;
+
+ case X86EMUL_OPC(0x0f, 0x01):
+ switch ( s->modrm_reg & 7 )
+ {
+ case 0: /* SGDT */
+ case 1: /* SIDT */
+ case 4: /* SMSW */
+ return true;
+ }
+ break;
+
+ case X86EMUL_OPC(0x0f, 0xae):
+ switch ( s->modrm_reg & 7 )
+ {
+ case 0: /* FXSAVE */
+ /* case 3: STMXCSR - handled above */
+ case 4: /* XSAVE */
+ case 6: /* XSAVEOPT */
+ return true;
+ }
+ break;
+
+ case X86EMUL_OPC(0x0f, 0xba):
+ return (s->modrm_reg & 7) > 4; /* BTS / BTR / BTC */
+
+ case X86EMUL_OPC(0x0f, 0xc7):
+ switch ( s->modrm_reg & 7 )
+ {
+ case 1: /* CMPXCHG{8,16}B */
+ case 4: /* XSAVEC */
+ case 5: /* XSAVES */
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
return rc;
}
#endif
-
-static inline void check_state(const struct x86_emulate_state *state)
-{
-#if defined(__XEN__) && !defined(NDEBUG)
- ASSERT(state->caller);
-#endif
-}
-
-#if defined(__XEN__) && !defined(NDEBUG)
-void x86_emulate_free_state(struct x86_emulate_state *state)
-{
- check_state(state);
- state->caller = NULL;
-}
-#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)
-{
- check_state(state);
-
- if ( unlikely(state->modrm_mod > 3) )
- {
- if ( rm )
- *rm = ~0U;
- if ( reg )
- *reg = ~0U;
- return -EINVAL;
- }
-
- if ( rm )
- *rm = state->modrm_rm;
- if ( reg )
- *reg = state->modrm_reg;
-
- 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;
-}
-
-/*
- * This function means to return 'true' for all supported insns with explicit
- * accesses to memory. This means also insns which don't have an explicit
- * memory operand (like POP), but it does not mean e.g. segment selector
- * loads, where the descriptor table access is considered an implicit one.
- */
-bool cf_check
-x86_insn_is_mem_access(const struct x86_emulate_state *state,
- const struct x86_emulate_ctxt *ctxt)
-{
- if ( mode_64bit() && state->not_64bit )
- return false;
-
- if ( state->ea.type == OP_MEM )
- {
- switch ( ctxt->opcode )
- {
- case 0x8d: /* LEA */
- case X86EMUL_OPC(0x0f, 0x0d): /* PREFETCH */
- case X86EMUL_OPC(0x0f, 0x18)
- ... X86EMUL_OPC(0x0f, 0x1f): /* NOP space */
- case X86EMUL_OPC_66(0x0f, 0x18)
- ... X86EMUL_OPC_66(0x0f, 0x1f): /* NOP space */
- case X86EMUL_OPC_F3(0x0f, 0x18)
- ... X86EMUL_OPC_F3(0x0f, 0x1f): /* NOP space */
- case X86EMUL_OPC_F2(0x0f, 0x18)
- ... X86EMUL_OPC_F2(0x0f, 0x1f): /* NOP space */
- case X86EMUL_OPC(0x0f, 0xb9): /* UD1 */
- case X86EMUL_OPC(0x0f, 0xff): /* UD0 */
- case X86EMUL_OPC_EVEX_66(0x0f38, 0xc6): /* V{GATH,SCATT}ERPF*D* */
- case X86EMUL_OPC_EVEX_66(0x0f38, 0xc7): /* V{GATH,SCATT}ERPF*Q* */
- return false;
-
- case X86EMUL_OPC(0x0f, 0x01):
- return (state->modrm_reg & 7) != 7; /* INVLPG */
-
- case X86EMUL_OPC(0x0f, 0xae):
- return (state->modrm_reg & 7) != 7; /* CLFLUSH */
-
- case X86EMUL_OPC_66(0x0f, 0xae):
- return (state->modrm_reg & 7) < 6; /* CLWB, CLFLUSHOPT */
- }
-
- return true;
- }
-
- switch ( ctxt->opcode )
- {
- case 0x06 ... 0x07: /* PUSH / POP %es */
- case 0x0e: /* PUSH %cs */
- case 0x16 ... 0x17: /* PUSH / POP %ss */
- case 0x1e ... 0x1f: /* PUSH / POP %ds */
- case 0x50 ... 0x5f: /* PUSH / POP reg */
- case 0x60 ... 0x61: /* PUSHA / POPA */
- case 0x68: case 0x6a: /* PUSH imm */
- case 0x6c ... 0x6f: /* INS / OUTS */
- case 0x8f: /* POP r/m */
- case 0x9a: /* CALL (far, direct) */
- case 0x9c ... 0x9d: /* PUSHF / POPF */
- case 0xa4 ... 0xa7: /* MOVS / CMPS */
- case 0xaa ... 0xaf: /* STOS / LODS / SCAS */
- case 0xc2 ... 0xc3: /* RET (near) */
- case 0xc8 ... 0xc9: /* ENTER / LEAVE */
- case 0xca ... 0xcb: /* RET (far) */
- case 0xd7: /* XLAT */
- case 0xe8: /* CALL (near, direct) */
- case X86EMUL_OPC(0x0f, 0xa0): /* PUSH %fs */
- case X86EMUL_OPC(0x0f, 0xa1): /* POP %fs */
- case X86EMUL_OPC(0x0f, 0xa8): /* PUSH %gs */
- case X86EMUL_OPC(0x0f, 0xa9): /* POP %gs */
- CASE_SIMD_PACKED_INT_VEX(0x0f, 0xf7): /* MASKMOV{Q,DQU} */
- /* VMASKMOVDQU */
- return true;
-
- case 0xff:
- switch ( state->modrm_reg & 7 )
- {
- case 2: /* CALL (near, indirect) */
- case 6: /* PUSH r/m */
- return true;
- }
- break;
-
- case X86EMUL_OPC(0x0f, 0x01):
- /* Cover CLZERO. */
- return (state->modrm_rm & 7) == 4 && (state->modrm_reg & 7) == 7;
- }
-
- return false;
-}
-
-/*
- * This function means to return 'true' for all supported insns with explicit
- * writes to memory. This means also insns which don't have an explicit
- * memory operand (like PUSH), but it does not mean e.g. segment selector
- * loads, where the (possible) descriptor table write is considered an
- * implicit access.
- */
-bool cf_check
-x86_insn_is_mem_write(const struct x86_emulate_state *state,
- const struct x86_emulate_ctxt *ctxt)
-{
- if ( mode_64bit() && state->not_64bit )
- return false;
-
- switch ( state->desc & DstMask )
- {
- case DstMem:
- /* The SrcMem check is to cover {,V}MASKMOV{Q,DQU}. */
- return state->modrm_mod != 3 || (state->desc & SrcMask) == SrcMem;
-
- case DstBitBase:
- case DstImplicit:
- break;
-
- default:
- switch ( ctxt->opcode )
- {
- case 0x63: /* ARPL */
- return !mode_64bit();
-
- case X86EMUL_OPC_66(0x0f38, 0xf8): /* MOVDIR64B */
- case X86EMUL_OPC_F2(0x0f38, 0xf8): /* ENQCMD */
- case X86EMUL_OPC_F3(0x0f38, 0xf8): /* ENQCMDS */
- return true;
-
- case X86EMUL_OPC_EVEX_F3(0x0f38, 0x10) ...
- X86EMUL_OPC_EVEX_F3(0x0f38, 0x15): /* VPMOVUS* */
- case X86EMUL_OPC_EVEX_F3(0x0f38, 0x20) ...
- X86EMUL_OPC_EVEX_F3(0x0f38, 0x25): /* VPMOVS* */
- case X86EMUL_OPC_EVEX_F3(0x0f38, 0x30) ...
- X86EMUL_OPC_EVEX_F3(0x0f38, 0x35): /* VPMOV{D,Q,W}* */
- return state->modrm_mod != 3;
- }
-
- return false;
- }
-
- if ( state->modrm_mod == 3 )
- {
- switch ( ctxt->opcode )
- {
- case 0xff: /* Grp5 */
- break;
-
- case X86EMUL_OPC(0x0f, 0x01): /* CLZERO is the odd one. */
- return (state->modrm_rm & 7) == 4 && (state->modrm_reg & 7) == 7;
-
- default:
- return false;
- }
- }
-
- switch ( ctxt->opcode )
- {
- case 0x06: /* PUSH %es */
- case 0x0e: /* PUSH %cs */
- case 0x16: /* PUSH %ss */
- case 0x1e: /* PUSH %ds */
- case 0x50 ... 0x57: /* PUSH reg */
- case 0x60: /* PUSHA */
- case 0x68: case 0x6a: /* PUSH imm */
- case 0x6c: case 0x6d: /* INS */
- case 0x9a: /* CALL (far, direct) */
- case 0x9c: /* PUSHF */
- case 0xa4: case 0xa5: /* MOVS */
- case 0xaa: case 0xab: /* STOS */
- case 0xc8: /* ENTER */
- case 0xe8: /* CALL (near, direct) */
- case X86EMUL_OPC(0x0f, 0xa0): /* PUSH %fs */
- case X86EMUL_OPC(0x0f, 0xa8): /* PUSH %gs */
- case X86EMUL_OPC(0x0f, 0xab): /* BTS */
- case X86EMUL_OPC(0x0f, 0xb3): /* BTR */
- case X86EMUL_OPC(0x0f, 0xbb): /* BTC */
- return true;
-
- case 0xd9:
- switch ( state->modrm_reg & 7 )
- {
- case 2: /* FST m32fp */
- case 3: /* FSTP m32fp */
- case 6: /* FNSTENV */
- case 7: /* FNSTCW */
- return true;
- }
- break;
-
- case 0xdb:
- switch ( state->modrm_reg & 7 )
- {
- case 1: /* FISTTP m32i */
- case 2: /* FIST m32i */
- case 3: /* FISTP m32i */
- case 7: /* FSTP m80fp */
- return true;
- }
- break;
-
- case 0xdd:
- switch ( state->modrm_reg & 7 )
- {
- case 1: /* FISTTP m64i */
- case 2: /* FST m64fp */
- case 3: /* FSTP m64fp */
- case 6: /* FNSAVE */
- case 7: /* FNSTSW */
- return true;
- }
- break;
-
- case 0xdf:
- switch ( state->modrm_reg & 7 )
- {
- case 1: /* FISTTP m16i */
- case 2: /* FIST m16i */
- case 3: /* FISTP m16i */
- case 6: /* FBSTP */
- case 7: /* FISTP m64i */
- return true;
- }
- break;
-
- case 0xff:
- switch ( state->modrm_reg & 7 )
- {
- case 2: /* CALL (near, indirect) */
- case 3: /* CALL (far, indirect) */
- case 6: /* PUSH r/m */
- return true;
- }
- break;
-
- case X86EMUL_OPC(0x0f, 0x01):
- switch ( state->modrm_reg & 7 )
- {
- case 0: /* SGDT */
- case 1: /* SIDT */
- case 4: /* SMSW */
- return true;
- }
- break;
-
- case X86EMUL_OPC(0x0f, 0xae):
- switch ( state->modrm_reg & 7 )
- {
- case 0: /* FXSAVE */
- /* case 3: STMXCSR - handled above */
- case 4: /* XSAVE */
- case 6: /* XSAVEOPT */
- return true;
- }
- break;
-
- case X86EMUL_OPC(0x0f, 0xba):
- return (state->modrm_reg & 7) > 4; /* BTS / BTR / BTC */
-
- case X86EMUL_OPC(0x0f, 0xc7):
- switch ( state->modrm_reg & 7 )
- {
- case 1: /* CMPXCHG{8,16}B */
- case 4: /* XSAVEC */
- case 5: /* XSAVES */
- return true;
- }
- break;
- }
-
- return false;
-}
-
-bool cf_check
-x86_insn_is_portio(const struct x86_emulate_state *state,
- const struct x86_emulate_ctxt *ctxt)
-{
- switch ( ctxt->opcode )
- {
- case 0x6c ... 0x6f: /* INS / OUTS */
- case 0xe4 ... 0xe7: /* IN / OUT imm8 */
- case 0xec ... 0xef: /* IN / OUT %dx */
- return true;
- }
-
- return false;
-}
-
-bool cf_check
-x86_insn_is_cr_access(const struct x86_emulate_state *state,
- const struct x86_emulate_ctxt *ctxt)
-{
- switch ( ctxt->opcode )
- {
- unsigned int ext;
-
- case X86EMUL_OPC(0x0f, 0x01):
- if ( x86_insn_modrm(state, NULL, &ext) >= 0
- && (ext & 5) == 4 ) /* SMSW / LMSW */
- return true;
- break;
-
- case X86EMUL_OPC(0x0f, 0x06): /* CLTS */
- case X86EMUL_OPC(0x0f, 0x20): /* MOV from CRn */
- case X86EMUL_OPC(0x0f, 0x22): /* MOV to CRn */
- return true;
- }
-
- return false;
-}
-
-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)
-{
- check_state(state);
-
- return state->ip - ctxt->regs->r(ip);
-}