From 1b248f147ea692c1a3d0ff18245a1b02df8b1502 Mon Sep 17 00:00:00 2001 From: Claudio Fontana Date: Sat, 12 Dec 2020 16:55:12 +0100 Subject: i386: move TCG accel files into tcg/ Signed-off-by: Claudio Fontana Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson [claudio: moved cc_helper_template.h to tcg/ too] Signed-off-by: Claudio Fontana Message-Id: <20201212155530.23098-6-cfontana@suse.de> Signed-off-by: Eduardo Habkost --- target/i386/bpt_helper.c | 335 -- target/i386/cc_helper.c | 388 -- target/i386/cc_helper_template.h | 242 - target/i386/excp_helper.c | 702 --- target/i386/fpu_helper.c | 3042 ------------ target/i386/int_helper.c | 493 -- target/i386/mem_helper.c | 193 - target/i386/meson.build | 14 +- target/i386/misc_helper.c | 647 --- target/i386/mpx_helper.c | 138 - target/i386/seg_helper.c | 2674 ----------- target/i386/smm_helper.c | 332 -- target/i386/svm_helper.c | 800 ---- target/i386/tcg-stub.c | 25 - target/i386/tcg/bpt_helper.c | 335 ++ target/i386/tcg/cc_helper.c | 388 ++ target/i386/tcg/cc_helper_template.h | 242 + target/i386/tcg/excp_helper.c | 702 +++ target/i386/tcg/fpu_helper.c | 3042 ++++++++++++ target/i386/tcg/int_helper.c | 493 ++ target/i386/tcg/mem_helper.c | 193 + target/i386/tcg/meson.build | 13 + target/i386/tcg/misc_helper.c | 647 +++ target/i386/tcg/mpx_helper.c | 138 + target/i386/tcg/seg_helper.c | 2674 +++++++++++ target/i386/tcg/smm_helper.c | 332 ++ target/i386/tcg/svm_helper.c | 800 ++++ target/i386/tcg/tcg-stub.c | 25 + target/i386/tcg/translate.c | 8642 ++++++++++++++++++++++++++++++++++ target/i386/translate.c | 8642 ---------------------------------- 30 files changed, 18667 insertions(+), 18666 deletions(-) delete mode 100644 target/i386/bpt_helper.c delete mode 100644 target/i386/cc_helper.c delete mode 100644 target/i386/cc_helper_template.h delete mode 100644 target/i386/excp_helper.c delete mode 100644 target/i386/fpu_helper.c delete mode 100644 target/i386/int_helper.c delete mode 100644 target/i386/mem_helper.c delete mode 100644 target/i386/misc_helper.c delete mode 100644 target/i386/mpx_helper.c delete mode 100644 target/i386/seg_helper.c delete mode 100644 target/i386/smm_helper.c delete mode 100644 target/i386/svm_helper.c delete mode 100644 target/i386/tcg-stub.c create mode 100644 target/i386/tcg/bpt_helper.c create mode 100644 target/i386/tcg/cc_helper.c create mode 100644 target/i386/tcg/cc_helper_template.h create mode 100644 target/i386/tcg/excp_helper.c create mode 100644 target/i386/tcg/fpu_helper.c create mode 100644 target/i386/tcg/int_helper.c create mode 100644 target/i386/tcg/mem_helper.c create mode 100644 target/i386/tcg/meson.build create mode 100644 target/i386/tcg/misc_helper.c create mode 100644 target/i386/tcg/mpx_helper.c create mode 100644 target/i386/tcg/seg_helper.c create mode 100644 target/i386/tcg/smm_helper.c create mode 100644 target/i386/tcg/svm_helper.c create mode 100644 target/i386/tcg/tcg-stub.c create mode 100644 target/i386/tcg/translate.c delete mode 100644 target/i386/translate.c (limited to 'target') diff --git a/target/i386/bpt_helper.c b/target/i386/bpt_helper.c deleted file mode 100644 index e6cc2921e2..0000000000 --- a/target/i386/bpt_helper.c +++ /dev/null @@ -1,335 +0,0 @@ -/* - * i386 breakpoint helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "exec/helper-proto.h" - - -#ifndef CONFIG_USER_ONLY -static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 1; -} - -static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) -{ - return (dr7 >> (index * 2)) & 2; - -} -static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) -{ - return hw_global_breakpoint_enabled(dr7, index) || - hw_local_breakpoint_enabled(dr7, index); -} - -static inline int hw_breakpoint_type(unsigned long dr7, int index) -{ - return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; -} - -static inline int hw_breakpoint_len(unsigned long dr7, int index) -{ - int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); - return (len == 2) ? 8 : len + 1; -} - -static int hw_breakpoint_insert(CPUX86State *env, int index) -{ - CPUState *cs = env_cpu(env); - target_ulong dr7 = env->dr[7]; - target_ulong drN = env->dr[index]; - int err = 0; - - switch (hw_breakpoint_type(dr7, index)) { - case DR7_TYPE_BP_INST: - if (hw_breakpoint_enabled(dr7, index)) { - err = cpu_breakpoint_insert(cs, drN, BP_CPU, - &env->cpu_breakpoint[index]); - } - break; - - case DR7_TYPE_IO_RW: - /* Notice when we should enable calls to bpt_io. */ - return hw_breakpoint_enabled(env->dr[7], index) - ? HF_IOBPT_MASK : 0; - - case DR7_TYPE_DATA_WR: - if (hw_breakpoint_enabled(dr7, index)) { - err = cpu_watchpoint_insert(cs, drN, - hw_breakpoint_len(dr7, index), - BP_CPU | BP_MEM_WRITE, - &env->cpu_watchpoint[index]); - } - break; - - case DR7_TYPE_DATA_RW: - if (hw_breakpoint_enabled(dr7, index)) { - err = cpu_watchpoint_insert(cs, drN, - hw_breakpoint_len(dr7, index), - BP_CPU | BP_MEM_ACCESS, - &env->cpu_watchpoint[index]); - } - break; - } - if (err) { - env->cpu_breakpoint[index] = NULL; - } - return 0; -} - -static void hw_breakpoint_remove(CPUX86State *env, int index) -{ - CPUState *cs = env_cpu(env); - - switch (hw_breakpoint_type(env->dr[7], index)) { - case DR7_TYPE_BP_INST: - if (env->cpu_breakpoint[index]) { - cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); - env->cpu_breakpoint[index] = NULL; - } - break; - - case DR7_TYPE_DATA_WR: - case DR7_TYPE_DATA_RW: - if (env->cpu_breakpoint[index]) { - cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); - env->cpu_breakpoint[index] = NULL; - } - break; - - case DR7_TYPE_IO_RW: - /* HF_IOBPT_MASK cleared elsewhere. */ - break; - } -} - -void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) -{ - target_ulong old_dr7 = env->dr[7]; - int iobpt = 0; - int i; - - new_dr7 |= DR7_FIXED_1; - - /* If nothing is changing except the global/local enable bits, - then we can make the change more efficient. */ - if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { - /* Fold the global and local enable bits together into the - global fields, then xor to show which registers have - changed collective enable state. */ - int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; - - for (i = 0; i < DR7_MAX_BP; i++) { - if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { - hw_breakpoint_remove(env, i); - } - } - env->dr[7] = new_dr7; - for (i = 0; i < DR7_MAX_BP; i++) { - if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { - iobpt |= hw_breakpoint_insert(env, i); - } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW - && hw_breakpoint_enabled(new_dr7, i)) { - iobpt |= HF_IOBPT_MASK; - } - } - } else { - for (i = 0; i < DR7_MAX_BP; i++) { - hw_breakpoint_remove(env, i); - } - env->dr[7] = new_dr7; - for (i = 0; i < DR7_MAX_BP; i++) { - iobpt |= hw_breakpoint_insert(env, i); - } - } - - env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; -} - -static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) -{ - target_ulong dr6; - int reg; - bool hit_enabled = false; - - dr6 = env->dr[6] & ~0xf; - for (reg = 0; reg < DR7_MAX_BP; reg++) { - bool bp_match = false; - bool wp_match = false; - - switch (hw_breakpoint_type(env->dr[7], reg)) { - case DR7_TYPE_BP_INST: - if (env->dr[reg] == env->eip) { - bp_match = true; - } - break; - case DR7_TYPE_DATA_WR: - case DR7_TYPE_DATA_RW: - if (env->cpu_watchpoint[reg] && - env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { - wp_match = true; - } - break; - case DR7_TYPE_IO_RW: - break; - } - if (bp_match || wp_match) { - dr6 |= 1 << reg; - if (hw_breakpoint_enabled(env->dr[7], reg)) { - hit_enabled = true; - } - } - } - - if (hit_enabled || force_dr6_update) { - env->dr[6] = dr6; - } - - return hit_enabled; -} - -void breakpoint_handler(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - CPUBreakpoint *bp; - - if (cs->watchpoint_hit) { - if (cs->watchpoint_hit->flags & BP_CPU) { - cs->watchpoint_hit = NULL; - if (check_hw_breakpoints(env, false)) { - raise_exception(env, EXCP01_DB); - } else { - cpu_loop_exit_noexc(cs); - } - } - } else { - QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { - if (bp->pc == env->eip) { - if (bp->flags & BP_CPU) { - check_hw_breakpoints(env, true); - raise_exception(env, EXCP01_DB); - } - break; - } - } - } -} -#endif - -void helper_single_step(CPUX86State *env) -{ -#ifndef CONFIG_USER_ONLY - check_hw_breakpoints(env, true); - env->dr[6] |= DR6_BS; -#endif - raise_exception(env, EXCP01_DB); -} - -void helper_rechecking_single_step(CPUX86State *env) -{ - if ((env->eflags & TF_MASK) != 0) { - helper_single_step(env); - } -} - -void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) -{ -#ifndef CONFIG_USER_ONLY - switch (reg) { - case 0: case 1: case 2: case 3: - if (hw_breakpoint_enabled(env->dr[7], reg) - && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { - hw_breakpoint_remove(env, reg); - env->dr[reg] = t0; - hw_breakpoint_insert(env, reg); - } else { - env->dr[reg] = t0; - } - return; - case 4: - if (env->cr[4] & CR4_DE_MASK) { - break; - } - /* fallthru */ - case 6: - env->dr[6] = t0 | DR6_FIXED_1; - return; - case 5: - if (env->cr[4] & CR4_DE_MASK) { - break; - } - /* fallthru */ - case 7: - cpu_x86_update_dr7(env, t0); - return; - } - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); -#endif -} - -target_ulong helper_get_dr(CPUX86State *env, int reg) -{ - switch (reg) { - case 0: case 1: case 2: case 3: case 6: case 7: - return env->dr[reg]; - case 4: - if (env->cr[4] & CR4_DE_MASK) { - break; - } else { - return env->dr[6]; - } - case 5: - if (env->cr[4] & CR4_DE_MASK) { - break; - } else { - return env->dr[7]; - } - } - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); -} - -/* Check if Port I/O is trapped by a breakpoint. */ -void helper_bpt_io(CPUX86State *env, uint32_t port, - uint32_t size, target_ulong next_eip) -{ -#ifndef CONFIG_USER_ONLY - target_ulong dr7 = env->dr[7]; - int i, hit = 0; - - for (i = 0; i < DR7_MAX_BP; ++i) { - if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW - && hw_breakpoint_enabled(dr7, i)) { - int bpt_len = hw_breakpoint_len(dr7, i); - if (port + size - 1 >= env->dr[i] - && port <= env->dr[i] + bpt_len - 1) { - hit |= 1 << i; - } - } - } - - if (hit) { - env->dr[6] = (env->dr[6] & ~0xf) | hit; - env->eip = next_eip; - raise_exception(env, EXCP01_DB); - } -#endif -} diff --git a/target/i386/cc_helper.c b/target/i386/cc_helper.c deleted file mode 100644 index 924dd3cd57..0000000000 --- a/target/i386/cc_helper.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * x86 condition code helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" - -const uint8_t parity_table[256] = { - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, - 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, -}; - -#define SHIFT 0 -#include "cc_helper_template.h" -#undef SHIFT - -#define SHIFT 1 -#include "cc_helper_template.h" -#undef SHIFT - -#define SHIFT 2 -#include "cc_helper_template.h" -#undef SHIFT - -#ifdef TARGET_X86_64 - -#define SHIFT 3 -#include "cc_helper_template.h" -#undef SHIFT - -#endif - -static target_ulong compute_all_adcx(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - return (src1 & ~CC_C) | (dst * CC_C); -} - -static target_ulong compute_all_adox(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - return (src1 & ~CC_O) | (src2 * CC_O); -} - -static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, - target_ulong src2) -{ - return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); -} - -target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, - target_ulong src2, int op) -{ - switch (op) { - default: /* should never happen */ - return 0; - - case CC_OP_EFLAGS: - return src1; - case CC_OP_CLR: - return CC_Z | CC_P; - case CC_OP_POPCNT: - return src1 ? 0 : CC_Z; - - case CC_OP_MULB: - return compute_all_mulb(dst, src1); - case CC_OP_MULW: - return compute_all_mulw(dst, src1); - case CC_OP_MULL: - return compute_all_mull(dst, src1); - - case CC_OP_ADDB: - return compute_all_addb(dst, src1); - case CC_OP_ADDW: - return compute_all_addw(dst, src1); - case CC_OP_ADDL: - return compute_all_addl(dst, src1); - - case CC_OP_ADCB: - return compute_all_adcb(dst, src1, src2); - case CC_OP_ADCW: - return compute_all_adcw(dst, src1, src2); - case CC_OP_ADCL: - return compute_all_adcl(dst, src1, src2); - - case CC_OP_SUBB: - return compute_all_subb(dst, src1); - case CC_OP_SUBW: - return compute_all_subw(dst, src1); - case CC_OP_SUBL: - return compute_all_subl(dst, src1); - - case CC_OP_SBBB: - return compute_all_sbbb(dst, src1, src2); - case CC_OP_SBBW: - return compute_all_sbbw(dst, src1, src2); - case CC_OP_SBBL: - return compute_all_sbbl(dst, src1, src2); - - case CC_OP_LOGICB: - return compute_all_logicb(dst, src1); - case CC_OP_LOGICW: - return compute_all_logicw(dst, src1); - case CC_OP_LOGICL: - return compute_all_logicl(dst, src1); - - case CC_OP_INCB: - return compute_all_incb(dst, src1); - case CC_OP_INCW: - return compute_all_incw(dst, src1); - case CC_OP_INCL: - return compute_all_incl(dst, src1); - - case CC_OP_DECB: - return compute_all_decb(dst, src1); - case CC_OP_DECW: - return compute_all_decw(dst, src1); - case CC_OP_DECL: - return compute_all_decl(dst, src1); - - case CC_OP_SHLB: - return compute_all_shlb(dst, src1); - case CC_OP_SHLW: - return compute_all_shlw(dst, src1); - case CC_OP_SHLL: - return compute_all_shll(dst, src1); - - case CC_OP_SARB: - return compute_all_sarb(dst, src1); - case CC_OP_SARW: - return compute_all_sarw(dst, src1); - case CC_OP_SARL: - return compute_all_sarl(dst, src1); - - case CC_OP_BMILGB: - return compute_all_bmilgb(dst, src1); - case CC_OP_BMILGW: - return compute_all_bmilgw(dst, src1); - case CC_OP_BMILGL: - return compute_all_bmilgl(dst, src1); - - case CC_OP_ADCX: - return compute_all_adcx(dst, src1, src2); - case CC_OP_ADOX: - return compute_all_adox(dst, src1, src2); - case CC_OP_ADCOX: - return compute_all_adcox(dst, src1, src2); - -#ifdef TARGET_X86_64 - case CC_OP_MULQ: - return compute_all_mulq(dst, src1); - case CC_OP_ADDQ: - return compute_all_addq(dst, src1); - case CC_OP_ADCQ: - return compute_all_adcq(dst, src1, src2); - case CC_OP_SUBQ: - return compute_all_subq(dst, src1); - case CC_OP_SBBQ: - return compute_all_sbbq(dst, src1, src2); - case CC_OP_LOGICQ: - return compute_all_logicq(dst, src1); - case CC_OP_INCQ: - return compute_all_incq(dst, src1); - case CC_OP_DECQ: - return compute_all_decq(dst, src1); - case CC_OP_SHLQ: - return compute_all_shlq(dst, src1); - case CC_OP_SARQ: - return compute_all_sarq(dst, src1); - case CC_OP_BMILGQ: - return compute_all_bmilgq(dst, src1); -#endif - } -} - -uint32_t cpu_cc_compute_all(CPUX86State *env, int op) -{ - return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); -} - -target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, - target_ulong src2, int op) -{ - switch (op) { - default: /* should never happen */ - case CC_OP_LOGICB: - case CC_OP_LOGICW: - case CC_OP_LOGICL: - case CC_OP_LOGICQ: - case CC_OP_CLR: - case CC_OP_POPCNT: - return 0; - - case CC_OP_EFLAGS: - case CC_OP_SARB: - case CC_OP_SARW: - case CC_OP_SARL: - case CC_OP_SARQ: - case CC_OP_ADOX: - return src1 & 1; - - case CC_OP_INCB: - case CC_OP_INCW: - case CC_OP_INCL: - case CC_OP_INCQ: - case CC_OP_DECB: - case CC_OP_DECW: - case CC_OP_DECL: - case CC_OP_DECQ: - return src1; - - case CC_OP_MULB: - case CC_OP_MULW: - case CC_OP_MULL: - case CC_OP_MULQ: - return src1 != 0; - - case CC_OP_ADCX: - case CC_OP_ADCOX: - return dst; - - case CC_OP_ADDB: - return compute_c_addb(dst, src1); - case CC_OP_ADDW: - return compute_c_addw(dst, src1); - case CC_OP_ADDL: - return compute_c_addl(dst, src1); - - case CC_OP_ADCB: - return compute_c_adcb(dst, src1, src2); - case CC_OP_ADCW: - return compute_c_adcw(dst, src1, src2); - case CC_OP_ADCL: - return compute_c_adcl(dst, src1, src2); - - case CC_OP_SUBB: - return compute_c_subb(dst, src1); - case CC_OP_SUBW: - return compute_c_subw(dst, src1); - case CC_OP_SUBL: - return compute_c_subl(dst, src1); - - case CC_OP_SBBB: - return compute_c_sbbb(dst, src1, src2); - case CC_OP_SBBW: - return compute_c_sbbw(dst, src1, src2); - case CC_OP_SBBL: - return compute_c_sbbl(dst, src1, src2); - - case CC_OP_SHLB: - return compute_c_shlb(dst, src1); - case CC_OP_SHLW: - return compute_c_shlw(dst, src1); - case CC_OP_SHLL: - return compute_c_shll(dst, src1); - - case CC_OP_BMILGB: - return compute_c_bmilgb(dst, src1); - case CC_OP_BMILGW: - return compute_c_bmilgw(dst, src1); - case CC_OP_BMILGL: - return compute_c_bmilgl(dst, src1); - -#ifdef TARGET_X86_64 - case CC_OP_ADDQ: - return compute_c_addq(dst, src1); - case CC_OP_ADCQ: - return compute_c_adcq(dst, src1, src2); - case CC_OP_SUBQ: - return compute_c_subq(dst, src1); - case CC_OP_SBBQ: - return compute_c_sbbq(dst, src1, src2); - case CC_OP_SHLQ: - return compute_c_shlq(dst, src1); - case CC_OP_BMILGQ: - return compute_c_bmilgq(dst, src1); -#endif - } -} - -void helper_write_eflags(CPUX86State *env, target_ulong t0, - uint32_t update_mask) -{ - cpu_load_eflags(env, t0, update_mask); -} - -target_ulong helper_read_eflags(CPUX86State *env) -{ - uint32_t eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - eflags |= (env->df & DF_MASK); - eflags |= env->eflags & ~(VM_MASK | RF_MASK); - return eflags; -} - -void helper_clts(CPUX86State *env) -{ - env->cr[0] &= ~CR0_TS_MASK; - env->hflags &= ~HF_TS_MASK; -} - -void helper_reset_rf(CPUX86State *env) -{ - env->eflags &= ~RF_MASK; -} - -void helper_cli(CPUX86State *env) -{ - env->eflags &= ~IF_MASK; -} - -void helper_sti(CPUX86State *env) -{ - env->eflags |= IF_MASK; -} - -void helper_clac(CPUX86State *env) -{ - env->eflags &= ~AC_MASK; -} - -void helper_stac(CPUX86State *env) -{ - env->eflags |= AC_MASK; -} - -#if 0 -/* vm86plus instructions */ -void helper_cli_vm(CPUX86State *env) -{ - env->eflags &= ~VIF_MASK; -} - -void helper_sti_vm(CPUX86State *env) -{ - env->eflags |= VIF_MASK; - if (env->eflags & VIP_MASK) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } -} -#endif diff --git a/target/i386/cc_helper_template.h b/target/i386/cc_helper_template.h deleted file mode 100644 index bb611feb04..0000000000 --- a/target/i386/cc_helper_template.h +++ /dev/null @@ -1,242 +0,0 @@ -/* - * x86 condition code helpers - * - * Copyright (c) 2008 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#define DATA_BITS (1 << (3 + SHIFT)) - -#if DATA_BITS == 8 -#define SUFFIX b -#define DATA_TYPE uint8_t -#elif DATA_BITS == 16 -#define SUFFIX w -#define DATA_TYPE uint16_t -#elif DATA_BITS == 32 -#define SUFFIX l -#define DATA_TYPE uint32_t -#elif DATA_BITS == 64 -#define SUFFIX q -#define DATA_TYPE uint64_t -#else -#error unhandled operand size -#endif - -#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) - -/* dynamic flags computation */ - -static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1; - - cf = dst < src1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return dst < src1; -} - -static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, - DATA_TYPE src3) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1 - src3; - - cf = (src3 ? dst <= src1 : dst < src1); - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, - DATA_TYPE src3) -{ - return src3 ? dst <= src1 : dst < src1; -} - -static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src1 = dst + src2; - - cf = src1 < src2; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) -{ - DATA_TYPE src1 = dst + src2; - - return src1 < src2; -} - -static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, - DATA_TYPE src3) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src1 = dst + src2 + src3; - - cf = (src3 ? src1 <= src2 : src1 < src2); - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, - DATA_TYPE src3) -{ - DATA_TYPE src1 = dst + src2 + src3; - - return (src3 ? src1 <= src2 : src1 < src2); -} - -static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = 0; - pf = parity_table[(uint8_t)dst]; - af = 0; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = 0; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; - - cf = src1; - src1 = dst - 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = (dst == SIGN_MASK) * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - DATA_TYPE src2; - - cf = src1; - src1 = dst + 1; - src2 = 1; - pf = parity_table[(uint8_t)dst]; - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = (dst == SIGN_MASK - 1) * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 >> (DATA_BITS - 1)) & CC_C; - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - /* of is defined iff shift count == 1 */ - of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return (src1 >> (DATA_BITS - 1)) & CC_C; -} - -static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = src1 & 1; - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - /* of is defined iff shift count == 1 */ - of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; - return cf | pf | af | zf | sf | of; -} - -/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and - CF are modified and it is slower to do that. Note as well that we - don't truncate SRC1 for computing carry to DATA_TYPE. */ -static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 != 0); - pf = parity_table[(uint8_t)dst]; - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = cf * CC_O; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - int cf, pf, af, zf, sf, of; - - cf = (src1 == 0); - pf = 0; /* undefined */ - af = 0; /* undefined */ - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = 0; - return cf | pf | af | zf | sf | of; -} - -static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) -{ - return src1 == 0; -} - -#undef DATA_BITS -#undef SIGN_MASK -#undef DATA_TYPE -#undef DATA_MASK -#undef SUFFIX diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c deleted file mode 100644 index 191471749f..0000000000 --- a/target/i386/excp_helper.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * x86 exception helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "qemu/log.h" -#include "sysemu/runstate.h" -#include "exec/helper-proto.h" - -void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) -{ - raise_interrupt(env, intno, 1, 0, next_eip_addend); -} - -void helper_raise_exception(CPUX86State *env, int exception_index) -{ - raise_exception(env, exception_index); -} - -/* - * Check nested exceptions and change to double or triple fault if - * needed. It should only be called, if this is not an interrupt. - * Returns the new exception number. - */ -static int check_exception(CPUX86State *env, int intno, int *error_code, - uintptr_t retaddr) -{ - int first_contributory = env->old_exception == 0 || - (env->old_exception >= 10 && - env->old_exception <= 13); - int second_contributory = intno == 0 || - (intno >= 10 && intno <= 13); - - qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n", - env->old_exception, intno); - -#if !defined(CONFIG_USER_ONLY) - if (env->old_exception == EXCP08_DBLE) { - if (env->hflags & HF_GUEST_MASK) { - cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */ - } - - qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); - - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - return EXCP_HLT; - } -#endif - - if ((first_contributory && second_contributory) - || (env->old_exception == EXCP0E_PAGE && - (second_contributory || (intno == EXCP0E_PAGE)))) { - intno = EXCP08_DBLE; - *error_code = 0; - } - - if (second_contributory || (intno == EXCP0E_PAGE) || - (intno == EXCP08_DBLE)) { - env->old_exception = intno; - } - - return intno; -} - -/* - * Signal an interruption. It is executed in the main CPU loop. - * is_int is TRUE if coming from the int instruction. next_eip is the - * env->eip value AFTER the interrupt instruction. It is only relevant if - * is_int is TRUE. - */ -static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, - int is_int, int error_code, - int next_eip_addend, - uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - if (!is_int) { - cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno, - error_code, retaddr); - intno = check_exception(env, intno, &error_code, retaddr); - } else { - cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0, retaddr); - } - - cs->exception_index = intno; - env->error_code = error_code; - env->exception_is_int = is_int; - env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit_restore(cs, retaddr); -} - -/* shortcuts to generate exceptions */ - -void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, - int error_code, int next_eip_addend) -{ - raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); -} - -void raise_exception_err(CPUX86State *env, int exception_index, - int error_code) -{ - raise_interrupt2(env, exception_index, 0, error_code, 0, 0); -} - -void raise_exception_err_ra(CPUX86State *env, int exception_index, - int error_code, uintptr_t retaddr) -{ - raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr); -} - -void raise_exception(CPUX86State *env, int exception_index) -{ - raise_interrupt2(env, exception_index, 0, 0, 0, 0); -} - -void raise_exception_ra(CPUX86State *env, int exception_index, uintptr_t retaddr) -{ - raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); -} - -#if !defined(CONFIG_USER_ONLY) -static hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, - int *prot) -{ - CPUX86State *env = &X86_CPU(cs)->env; - uint64_t rsvd_mask = PG_HI_RSVD_MASK; - uint64_t ptep, pte; - uint64_t exit_info_1 = 0; - target_ulong pde_addr, pte_addr; - uint32_t page_offset; - int page_size; - - if (likely(!(env->hflags2 & HF2_NPT_MASK))) { - return gphys; - } - - if (!(env->nested_pg_mode & SVM_NPT_NXE)) { - rsvd_mask |= PG_NX_MASK; - } - - if (env->nested_pg_mode & SVM_NPT_PAE) { - uint64_t pde, pdpe; - target_ulong pdpe_addr; - -#ifdef TARGET_X86_64 - if (env->nested_pg_mode & SVM_NPT_LMA) { - uint64_t pml5e; - uint64_t pml4e_addr, pml4e; - - pml5e = env->nested_cr3; - ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; - - pml4e_addr = (pml5e & PG_ADDRESS_MASK) + - (((gphys >> 39) & 0x1ff) << 3); - pml4e = x86_ldq_phys(cs, pml4e_addr); - if (!(pml4e & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pml4e & (rsvd_mask | PG_PSE_MASK)) { - goto do_fault_rsvd; - } - if (!(pml4e & PG_ACCESSED_MASK)) { - pml4e |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); - } - ptep &= pml4e ^ PG_NX_MASK; - pdpe_addr = (pml4e & PG_ADDRESS_MASK) + - (((gphys >> 30) & 0x1ff) << 3); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pdpe & rsvd_mask) { - goto do_fault_rsvd; - } - ptep &= pdpe ^ PG_NX_MASK; - if (!(pdpe & PG_ACCESSED_MASK)) { - pdpe |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); - } - if (pdpe & PG_PSE_MASK) { - /* 1 GB page */ - page_size = 1024 * 1024 * 1024; - pte_addr = pdpe_addr; - pte = pdpe; - goto do_check_protect; - } - } else -#endif - { - pdpe_addr = (env->nested_cr3 & ~0x1f) + ((gphys >> 27) & 0x18); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { - goto do_fault; - } - rsvd_mask |= PG_HI_USER_MASK; - if (pdpe & (rsvd_mask | PG_NX_MASK)) { - goto do_fault_rsvd; - } - ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; - } - - pde_addr = (pdpe & PG_ADDRESS_MASK) + (((gphys >> 21) & 0x1ff) << 3); - pde = x86_ldq_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pde & rsvd_mask) { - goto do_fault_rsvd; - } - ptep &= pde ^ PG_NX_MASK; - if (pde & PG_PSE_MASK) { - /* 2 MB page */ - page_size = 2048 * 1024; - pte_addr = pde_addr; - pte = pde; - goto do_check_protect; - } - /* 4 KB page */ - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); - } - pte_addr = (pde & PG_ADDRESS_MASK) + (((gphys >> 12) & 0x1ff) << 3); - pte = x86_ldq_phys(cs, pte_addr); - if (!(pte & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pte & rsvd_mask) { - goto do_fault_rsvd; - } - /* combine pde and pte nx, user and rw protections */ - ptep &= pte ^ PG_NX_MASK; - page_size = 4096; - } else { - uint32_t pde; - - /* page directory entry */ - pde_addr = (env->nested_cr3 & ~0xfff) + ((gphys >> 20) & 0xffc); - pde = x86_ldl_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { - goto do_fault; - } - ptep = pde | PG_NX_MASK; - - /* if host cr4 PSE bit is set, then we use a 4MB page */ - if ((pde & PG_PSE_MASK) && (env->nested_pg_mode & SVM_NPT_PSE)) { - page_size = 4096 * 1024; - pte_addr = pde_addr; - - /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. - * Leave bits 20-13 in place for setting accessed/dirty bits below. - */ - pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); - rsvd_mask = 0x200000; - goto do_check_protect_pse36; - } - - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); - } - - /* page directory entry */ - pte_addr = (pde & ~0xfff) + ((gphys >> 10) & 0xffc); - pte = x86_ldl_phys(cs, pte_addr); - if (!(pte & PG_PRESENT_MASK)) { - goto do_fault; - } - /* combine pde and pte user and rw protections */ - ptep &= pte | PG_NX_MASK; - page_size = 4096; - rsvd_mask = 0; - } - - do_check_protect: - rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; - do_check_protect_pse36: - if (pte & rsvd_mask) { - goto do_fault_rsvd; - } - ptep ^= PG_NX_MASK; - - if (!(ptep & PG_USER_MASK)) { - goto do_fault_protect; - } - if (ptep & PG_NX_MASK) { - if (access_type == MMU_INST_FETCH) { - goto do_fault_protect; - } - *prot &= ~PAGE_EXEC; - } - if (!(ptep & PG_RW_MASK)) { - if (access_type == MMU_DATA_STORE) { - goto do_fault_protect; - } - *prot &= ~PAGE_WRITE; - } - - pte &= PG_ADDRESS_MASK & ~(page_size - 1); - page_offset = gphys & (page_size - 1); - return pte + page_offset; - - do_fault_rsvd: - exit_info_1 |= SVM_NPTEXIT_RSVD; - do_fault_protect: - exit_info_1 |= SVM_NPTEXIT_P; - do_fault: - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), - gphys); - exit_info_1 |= SVM_NPTEXIT_US; - if (access_type == MMU_DATA_STORE) { - exit_info_1 |= SVM_NPTEXIT_RW; - } else if (access_type == MMU_INST_FETCH) { - exit_info_1 |= SVM_NPTEXIT_ID; - } - if (prot) { - exit_info_1 |= SVM_NPTEXIT_GPA; - } else { /* page table access */ - exit_info_1 |= SVM_NPTEXIT_GPT; - } - cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, env->retaddr); -} - -/* return value: - * -1 = cannot handle fault - * 0 = nothing more to do - * 1 = generate PF fault - */ -static int handle_mmu_fault(CPUState *cs, vaddr addr, int size, - int is_write1, int mmu_idx) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - uint64_t ptep, pte; - int32_t a20_mask; - target_ulong pde_addr, pte_addr; - int error_code = 0; - int is_dirty, prot, page_size, is_write, is_user; - hwaddr paddr; - uint64_t rsvd_mask = PG_HI_RSVD_MASK; - uint32_t page_offset; - target_ulong vaddr; - - is_user = mmu_idx == MMU_USER_IDX; -#if defined(DEBUG_MMU) - printf("MMU fault: addr=%" VADDR_PRIx " w=%d u=%d eip=" TARGET_FMT_lx "\n", - addr, is_write1, is_user, env->eip); -#endif - is_write = is_write1 & 1; - - a20_mask = x86_get_a20_mask(env); - if (!(env->cr[0] & CR0_PG_MASK)) { - pte = addr; -#ifdef TARGET_X86_64 - if (!(env->hflags & HF_LMA_MASK)) { - /* Without long mode we can only address 32bits in real mode */ - pte = (uint32_t)pte; - } -#endif - prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - page_size = 4096; - goto do_mapping; - } - - if (!(env->efer & MSR_EFER_NXE)) { - rsvd_mask |= PG_NX_MASK; - } - - if (env->cr[4] & CR4_PAE_MASK) { - uint64_t pde, pdpe; - target_ulong pdpe_addr; - -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - bool la57 = env->cr[4] & CR4_LA57_MASK; - uint64_t pml5e_addr, pml5e; - uint64_t pml4e_addr, pml4e; - int32_t sext; - - /* test virtual address sign extension */ - sext = la57 ? (int64_t)addr >> 56 : (int64_t)addr >> 47; - if (sext != 0 && sext != -1) { - env->error_code = 0; - cs->exception_index = EXCP0D_GPF; - return 1; - } - - if (la57) { - pml5e_addr = ((env->cr[3] & ~0xfff) + - (((addr >> 48) & 0x1ff) << 3)) & a20_mask; - pml5e_addr = get_hphys(cs, pml5e_addr, MMU_DATA_STORE, NULL); - pml5e = x86_ldq_phys(cs, pml5e_addr); - if (!(pml5e & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pml5e & (rsvd_mask | PG_PSE_MASK)) { - goto do_fault_rsvd; - } - if (!(pml5e & PG_ACCESSED_MASK)) { - pml5e |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pml5e_addr, pml5e); - } - ptep = pml5e ^ PG_NX_MASK; - } else { - pml5e = env->cr[3]; - ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; - } - - pml4e_addr = ((pml5e & PG_ADDRESS_MASK) + - (((addr >> 39) & 0x1ff) << 3)) & a20_mask; - pml4e_addr = get_hphys(cs, pml4e_addr, MMU_DATA_STORE, false); - pml4e = x86_ldq_phys(cs, pml4e_addr); - if (!(pml4e & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pml4e & (rsvd_mask | PG_PSE_MASK)) { - goto do_fault_rsvd; - } - if (!(pml4e & PG_ACCESSED_MASK)) { - pml4e |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); - } - ptep &= pml4e ^ PG_NX_MASK; - pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & - a20_mask; - pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, NULL); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pdpe & rsvd_mask) { - goto do_fault_rsvd; - } - ptep &= pdpe ^ PG_NX_MASK; - if (!(pdpe & PG_ACCESSED_MASK)) { - pdpe |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); - } - if (pdpe & PG_PSE_MASK) { - /* 1 GB page */ - page_size = 1024 * 1024 * 1024; - pte_addr = pdpe_addr; - pte = pdpe; - goto do_check_protect; - } - } else -#endif - { - /* XXX: load them when cr3 is loaded ? */ - pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & - a20_mask; - pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, false); - pdpe = x86_ldq_phys(cs, pdpe_addr); - if (!(pdpe & PG_PRESENT_MASK)) { - goto do_fault; - } - rsvd_mask |= PG_HI_USER_MASK; - if (pdpe & (rsvd_mask | PG_NX_MASK)) { - goto do_fault_rsvd; - } - ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; - } - - pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & - a20_mask; - pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); - pde = x86_ldq_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pde & rsvd_mask) { - goto do_fault_rsvd; - } - ptep &= pde ^ PG_NX_MASK; - if (pde & PG_PSE_MASK) { - /* 2 MB page */ - page_size = 2048 * 1024; - pte_addr = pde_addr; - pte = pde; - goto do_check_protect; - } - /* 4 KB page */ - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); - } - pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & - a20_mask; - pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); - pte = x86_ldq_phys(cs, pte_addr); - if (!(pte & PG_PRESENT_MASK)) { - goto do_fault; - } - if (pte & rsvd_mask) { - goto do_fault_rsvd; - } - /* combine pde and pte nx, user and rw protections */ - ptep &= pte ^ PG_NX_MASK; - page_size = 4096; - } else { - uint32_t pde; - - /* page directory entry */ - pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & - a20_mask; - pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); - pde = x86_ldl_phys(cs, pde_addr); - if (!(pde & PG_PRESENT_MASK)) { - goto do_fault; - } - ptep = pde | PG_NX_MASK; - - /* if PSE bit is set, then we use a 4MB page */ - if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { - page_size = 4096 * 1024; - pte_addr = pde_addr; - - /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. - * Leave bits 20-13 in place for setting accessed/dirty bits below. - */ - pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); - rsvd_mask = 0x200000; - goto do_check_protect_pse36; - } - - if (!(pde & PG_ACCESSED_MASK)) { - pde |= PG_ACCESSED_MASK; - x86_stl_phys_notdirty(cs, pde_addr, pde); - } - - /* page directory entry */ - pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & - a20_mask; - pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); - pte = x86_ldl_phys(cs, pte_addr); - if (!(pte & PG_PRESENT_MASK)) { - goto do_fault; - } - /* combine pde and pte user and rw protections */ - ptep &= pte | PG_NX_MASK; - page_size = 4096; - rsvd_mask = 0; - } - -do_check_protect: - rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; -do_check_protect_pse36: - if (pte & rsvd_mask) { - goto do_fault_rsvd; - } - ptep ^= PG_NX_MASK; - - /* can the page can be put in the TLB? prot will tell us */ - if (is_user && !(ptep & PG_USER_MASK)) { - goto do_fault_protect; - } - - prot = 0; - if (mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) { - prot |= PAGE_READ; - if ((ptep & PG_RW_MASK) || (!is_user && !(env->cr[0] & CR0_WP_MASK))) { - prot |= PAGE_WRITE; - } - } - if (!(ptep & PG_NX_MASK) && - (mmu_idx == MMU_USER_IDX || - !((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) { - prot |= PAGE_EXEC; - } - if ((env->cr[4] & CR4_PKE_MASK) && (env->hflags & HF_LMA_MASK) && - (ptep & PG_USER_MASK) && env->pkru) { - uint32_t pk = (pte & PG_PKRU_MASK) >> PG_PKRU_BIT; - uint32_t pkru_ad = (env->pkru >> pk * 2) & 1; - uint32_t pkru_wd = (env->pkru >> pk * 2) & 2; - uint32_t pkru_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - - if (pkru_ad) { - pkru_prot &= ~(PAGE_READ | PAGE_WRITE); - } else if (pkru_wd && (is_user || env->cr[0] & CR0_WP_MASK)) { - pkru_prot &= ~PAGE_WRITE; - } - - prot &= pkru_prot; - if ((pkru_prot & (1 << is_write1)) == 0) { - assert(is_write1 != 2); - error_code |= PG_ERROR_PK_MASK; - goto do_fault_protect; - } - } - - if ((prot & (1 << is_write1)) == 0) { - goto do_fault_protect; - } - - /* yes, it can! */ - is_dirty = is_write && !(pte & PG_DIRTY_MASK); - if (!(pte & PG_ACCESSED_MASK) || is_dirty) { - pte |= PG_ACCESSED_MASK; - if (is_dirty) { - pte |= PG_DIRTY_MASK; - } - x86_stl_phys_notdirty(cs, pte_addr, pte); - } - - if (!(pte & PG_DIRTY_MASK)) { - /* only set write access if already dirty... otherwise wait - for dirty access */ - assert(!is_write); - prot &= ~PAGE_WRITE; - } - - do_mapping: - pte = pte & a20_mask; - - /* align to page_size */ - pte &= PG_ADDRESS_MASK & ~(page_size - 1); - page_offset = addr & (page_size - 1); - paddr = get_hphys(cs, pte + page_offset, is_write1, &prot); - - /* Even if 4MB pages, we map only one 4KB page in the cache to - avoid filling it too fast */ - vaddr = addr & TARGET_PAGE_MASK; - paddr &= TARGET_PAGE_MASK; - - assert(prot & (1 << is_write1)); - tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), - prot, mmu_idx, page_size); - return 0; - do_fault_rsvd: - error_code |= PG_ERROR_RSVD_MASK; - do_fault_protect: - error_code |= PG_ERROR_P_MASK; - do_fault: - error_code |= (is_write << PG_ERROR_W_BIT); - if (is_user) - error_code |= PG_ERROR_U_MASK; - if (is_write1 == 2 && - (((env->efer & MSR_EFER_NXE) && - (env->cr[4] & CR4_PAE_MASK)) || - (env->cr[4] & CR4_SMEP_MASK))) - error_code |= PG_ERROR_I_D_MASK; - if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { - /* cr2 is not modified in case of exceptions */ - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), - addr); - } else { - env->cr[2] = addr; - } - env->error_code = error_code; - cs->exception_index = EXCP0E_PAGE; - return 1; -} -#endif - -bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - -#ifdef CONFIG_USER_ONLY - /* user mode only emulation */ - env->cr[2] = addr; - env->error_code = (access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT; - env->error_code |= PG_ERROR_U_MASK; - cs->exception_index = EXCP0E_PAGE; - env->exception_is_int = 0; - env->exception_next_eip = -1; - cpu_loop_exit_restore(cs, retaddr); -#else - env->retaddr = retaddr; - if (handle_mmu_fault(cs, addr, size, access_type, mmu_idx)) { - /* FIXME: On error in get_hphys we have already jumped out. */ - g_assert(!probe); - raise_exception_err_ra(env, cs->exception_index, - env->error_code, retaddr); - } - return true; -#endif -} diff --git a/target/i386/fpu_helper.c b/target/i386/fpu_helper.c deleted file mode 100644 index 03b35443a6..0000000000 --- a/target/i386/fpu_helper.c +++ /dev/null @@ -1,3042 +0,0 @@ -/* - * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include -#include "cpu.h" -#include "exec/helper-proto.h" -#include "qemu/host-utils.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "fpu/softfloat.h" -#include "fpu/softfloat-macros.h" - -#ifdef CONFIG_SOFTMMU -#include "hw/irq.h" -#endif - -#define FPU_RC_MASK 0xc00 -#define FPU_RC_NEAR 0x000 -#define FPU_RC_DOWN 0x400 -#define FPU_RC_UP 0x800 -#define FPU_RC_CHOP 0xc00 - -#define MAXTAN 9223372036854775808.0 - -/* the following deal with x86 long double-precision numbers */ -#define MAXEXPD 0x7fff -#define EXPBIAS 16383 -#define EXPD(fp) (fp.l.upper & 0x7fff) -#define SIGND(fp) ((fp.l.upper) & 0x8000) -#define MANTD(fp) (fp.l.lower) -#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS - -#define FPUS_IE (1 << 0) -#define FPUS_DE (1 << 1) -#define FPUS_ZE (1 << 2) -#define FPUS_OE (1 << 3) -#define FPUS_UE (1 << 4) -#define FPUS_PE (1 << 5) -#define FPUS_SF (1 << 6) -#define FPUS_SE (1 << 7) -#define FPUS_B (1 << 15) - -#define FPUC_EM 0x3f - -#define floatx80_lg2 make_floatx80(0x3ffd, 0x9a209a84fbcff799LL) -#define floatx80_lg2_d make_floatx80(0x3ffd, 0x9a209a84fbcff798LL) -#define floatx80_l2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcLL) -#define floatx80_l2e_d make_floatx80(0x3fff, 0xb8aa3b295c17f0bbLL) -#define floatx80_l2t make_floatx80(0x4000, 0xd49a784bcd1b8afeLL) -#define floatx80_l2t_u make_floatx80(0x4000, 0xd49a784bcd1b8affLL) -#define floatx80_ln2_d make_floatx80(0x3ffe, 0xb17217f7d1cf79abLL) -#define floatx80_pi_d make_floatx80(0x4000, 0xc90fdaa22168c234LL) - -#if !defined(CONFIG_USER_ONLY) -static qemu_irq ferr_irq; - -void x86_register_ferr_irq(qemu_irq irq) -{ - ferr_irq = irq; -} - -static void cpu_clear_ignne(void) -{ - CPUX86State *env = &X86_CPU(first_cpu)->env; - env->hflags2 &= ~HF2_IGNNE_MASK; -} - -void cpu_set_ignne(void) -{ - CPUX86State *env = &X86_CPU(first_cpu)->env; - env->hflags2 |= HF2_IGNNE_MASK; - /* - * We get here in response to a write to port F0h. The chipset should - * deassert FP_IRQ and FERR# instead should stay signaled until FPSW_SE is - * cleared, because FERR# and FP_IRQ are two separate pins on real - * hardware. However, we don't model FERR# as a qemu_irq, so we just - * do directly what the chipset would do, i.e. deassert FP_IRQ. - */ - qemu_irq_lower(ferr_irq); -} -#endif - - -static inline void fpush(CPUX86State *env) -{ - env->fpstt = (env->fpstt - 1) & 7; - env->fptags[env->fpstt] = 0; /* validate stack entry */ -} - -static inline void fpop(CPUX86State *env) -{ - env->fptags[env->fpstt] = 1; /* invalidate stack entry */ - env->fpstt = (env->fpstt + 1) & 7; -} - -static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr, - uintptr_t retaddr) -{ - CPU_LDoubleU temp; - - temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); - temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); - return temp.d; -} - -static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, - uintptr_t retaddr) -{ - CPU_LDoubleU temp; - - temp.d = f; - cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); - cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); -} - -/* x87 FPU helpers */ - -static inline double floatx80_to_double(CPUX86State *env, floatx80 a) -{ - union { - float64 f64; - double d; - } u; - - u.f64 = floatx80_to_float64(a, &env->fp_status); - return u.d; -} - -static inline floatx80 double_to_floatx80(CPUX86State *env, double a) -{ - union { - float64 f64; - double d; - } u; - - u.d = a; - return float64_to_floatx80(u.f64, &env->fp_status); -} - -static void fpu_set_exception(CPUX86State *env, int mask) -{ - env->fpus |= mask; - if (env->fpus & (~env->fpuc & FPUC_EM)) { - env->fpus |= FPUS_SE | FPUS_B; - } -} - -static inline uint8_t save_exception_flags(CPUX86State *env) -{ - uint8_t old_flags = get_float_exception_flags(&env->fp_status); - set_float_exception_flags(0, &env->fp_status); - return old_flags; -} - -static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) -{ - uint8_t new_flags = get_float_exception_flags(&env->fp_status); - float_raise(old_flags, &env->fp_status); - fpu_set_exception(env, - ((new_flags & float_flag_invalid ? FPUS_IE : 0) | - (new_flags & float_flag_divbyzero ? FPUS_ZE : 0) | - (new_flags & float_flag_overflow ? FPUS_OE : 0) | - (new_flags & float_flag_underflow ? FPUS_UE : 0) | - (new_flags & float_flag_inexact ? FPUS_PE : 0) | - (new_flags & float_flag_input_denormal ? FPUS_DE : 0))); -} - -static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) -{ - uint8_t old_flags = save_exception_flags(env); - floatx80 ret = floatx80_div(a, b, &env->fp_status); - merge_exception_flags(env, old_flags); - return ret; -} - -static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) -{ - if (env->cr[0] & CR0_NE_MASK) { - raise_exception_ra(env, EXCP10_COPR, retaddr); - } -#if !defined(CONFIG_USER_ONLY) - else if (ferr_irq && !(env->hflags2 & HF2_IGNNE_MASK)) { - qemu_irq_raise(ferr_irq); - } -#endif -} - -void helper_flds_FT0(CPUX86State *env, uint32_t val) -{ - uint8_t old_flags = save_exception_flags(env); - union { - float32 f; - uint32_t i; - } u; - - u.i = val; - FT0 = float32_to_floatx80(u.f, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fldl_FT0(CPUX86State *env, uint64_t val) -{ - uint8_t old_flags = save_exception_flags(env); - union { - float64 f; - uint64_t i; - } u; - - u.i = val; - FT0 = float64_to_floatx80(u.f, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fildl_FT0(CPUX86State *env, int32_t val) -{ - FT0 = int32_to_floatx80(val, &env->fp_status); -} - -void helper_flds_ST0(CPUX86State *env, uint32_t val) -{ - uint8_t old_flags = save_exception_flags(env); - int new_fpstt; - union { - float32 f; - uint32_t i; - } u; - - new_fpstt = (env->fpstt - 1) & 7; - u.i = val; - env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status); - env->fpstt = new_fpstt; - env->fptags[new_fpstt] = 0; /* validate stack entry */ - merge_exception_flags(env, old_flags); -} - -void helper_fldl_ST0(CPUX86State *env, uint64_t val) -{ - uint8_t old_flags = save_exception_flags(env); - int new_fpstt; - union { - float64 f; - uint64_t i; - } u; - - new_fpstt = (env->fpstt - 1) & 7; - u.i = val; - env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status); - env->fpstt = new_fpstt; - env->fptags[new_fpstt] = 0; /* validate stack entry */ - merge_exception_flags(env, old_flags); -} - -void helper_fildl_ST0(CPUX86State *env, int32_t val) -{ - int new_fpstt; - - new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status); - env->fpstt = new_fpstt; - env->fptags[new_fpstt] = 0; /* validate stack entry */ -} - -void helper_fildll_ST0(CPUX86State *env, int64_t val) -{ - int new_fpstt; - - new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status); - env->fpstt = new_fpstt; - env->fptags[new_fpstt] = 0; /* validate stack entry */ -} - -uint32_t helper_fsts_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - union { - float32 f; - uint32_t i; - } u; - - u.f = floatx80_to_float32(ST0, &env->fp_status); - merge_exception_flags(env, old_flags); - return u.i; -} - -uint64_t helper_fstl_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - union { - float64 f; - uint64_t i; - } u; - - u.f = floatx80_to_float64(ST0, &env->fp_status); - merge_exception_flags(env, old_flags); - return u.i; -} - -int32_t helper_fist_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int32_t val; - - val = floatx80_to_int32(ST0, &env->fp_status); - if (val != (int16_t)val) { - set_float_exception_flags(float_flag_invalid, &env->fp_status); - val = -32768; - } - merge_exception_flags(env, old_flags); - return val; -} - -int32_t helper_fistl_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int32_t val; - - val = floatx80_to_int32(ST0, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { - val = 0x80000000; - } - merge_exception_flags(env, old_flags); - return val; -} - -int64_t helper_fistll_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int64_t val; - - val = floatx80_to_int64(ST0, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { - val = 0x8000000000000000ULL; - } - merge_exception_flags(env, old_flags); - return val; -} - -int32_t helper_fistt_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int32_t val; - - val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); - if (val != (int16_t)val) { - set_float_exception_flags(float_flag_invalid, &env->fp_status); - val = -32768; - } - merge_exception_flags(env, old_flags); - return val; -} - -int32_t helper_fisttl_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int32_t val; - - val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { - val = 0x80000000; - } - merge_exception_flags(env, old_flags); - return val; -} - -int64_t helper_fisttll_ST0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int64_t val; - - val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); - if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { - val = 0x8000000000000000ULL; - } - merge_exception_flags(env, old_flags); - return val; -} - -void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) -{ - int new_fpstt; - - new_fpstt = (env->fpstt - 1) & 7; - env->fpregs[new_fpstt].d = helper_fldt(env, ptr, GETPC()); - env->fpstt = new_fpstt; - env->fptags[new_fpstt] = 0; /* validate stack entry */ -} - -void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) -{ - helper_fstt(env, ST0, ptr, GETPC()); -} - -void helper_fpush(CPUX86State *env) -{ - fpush(env); -} - -void helper_fpop(CPUX86State *env) -{ - fpop(env); -} - -void helper_fdecstp(CPUX86State *env) -{ - env->fpstt = (env->fpstt - 1) & 7; - env->fpus &= ~0x4700; -} - -void helper_fincstp(CPUX86State *env) -{ - env->fpstt = (env->fpstt + 1) & 7; - env->fpus &= ~0x4700; -} - -/* FPU move */ - -void helper_ffree_STN(CPUX86State *env, int st_index) -{ - env->fptags[(env->fpstt + st_index) & 7] = 1; -} - -void helper_fmov_ST0_FT0(CPUX86State *env) -{ - ST0 = FT0; -} - -void helper_fmov_FT0_STN(CPUX86State *env, int st_index) -{ - FT0 = ST(st_index); -} - -void helper_fmov_ST0_STN(CPUX86State *env, int st_index) -{ - ST0 = ST(st_index); -} - -void helper_fmov_STN_ST0(CPUX86State *env, int st_index) -{ - ST(st_index) = ST0; -} - -void helper_fxchg_ST0_STN(CPUX86State *env, int st_index) -{ - floatx80 tmp; - - tmp = ST(st_index); - ST(st_index) = ST0; - ST0 = tmp; -} - -/* FPU operations */ - -static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; - -void helper_fcom_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - FloatRelation ret; - - ret = floatx80_compare(ST0, FT0, &env->fp_status); - env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; - merge_exception_flags(env, old_flags); -} - -void helper_fucom_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - FloatRelation ret; - - ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; - merge_exception_flags(env, old_flags); -} - -static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; - -void helper_fcomi_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int eflags; - FloatRelation ret; - - ret = floatx80_compare(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env, CC_OP); - eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; - CC_SRC = eflags; - merge_exception_flags(env, old_flags); -} - -void helper_fucomi_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - int eflags; - FloatRelation ret; - - ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); - eflags = cpu_cc_compute_all(env, CC_OP); - eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; - CC_SRC = eflags; - merge_exception_flags(env, old_flags); -} - -void helper_fadd_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - ST0 = floatx80_add(ST0, FT0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fmul_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - ST0 = floatx80_mul(ST0, FT0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fsub_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - ST0 = floatx80_sub(ST0, FT0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fsubr_ST0_FT0(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - ST0 = floatx80_sub(FT0, ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fdiv_ST0_FT0(CPUX86State *env) -{ - ST0 = helper_fdiv(env, ST0, FT0); -} - -void helper_fdivr_ST0_FT0(CPUX86State *env) -{ - ST0 = helper_fdiv(env, FT0, ST0); -} - -/* fp operations between STN and ST0 */ - -void helper_fadd_STN_ST0(CPUX86State *env, int st_index) -{ - uint8_t old_flags = save_exception_flags(env); - ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fmul_STN_ST0(CPUX86State *env, int st_index) -{ - uint8_t old_flags = save_exception_flags(env); - ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fsub_STN_ST0(CPUX86State *env, int st_index) -{ - uint8_t old_flags = save_exception_flags(env); - ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) -{ - uint8_t old_flags = save_exception_flags(env); - ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fdiv_STN_ST0(CPUX86State *env, int st_index) -{ - floatx80 *p; - - p = &ST(st_index); - *p = helper_fdiv(env, *p, ST0); -} - -void helper_fdivr_STN_ST0(CPUX86State *env, int st_index) -{ - floatx80 *p; - - p = &ST(st_index); - *p = helper_fdiv(env, ST0, *p); -} - -/* misc FPU operations */ -void helper_fchs_ST0(CPUX86State *env) -{ - ST0 = floatx80_chs(ST0); -} - -void helper_fabs_ST0(CPUX86State *env) -{ - ST0 = floatx80_abs(ST0); -} - -void helper_fld1_ST0(CPUX86State *env) -{ - ST0 = floatx80_one; -} - -void helper_fldl2t_ST0(CPUX86State *env) -{ - switch (env->fpuc & FPU_RC_MASK) { - case FPU_RC_UP: - ST0 = floatx80_l2t_u; - break; - default: - ST0 = floatx80_l2t; - break; - } -} - -void helper_fldl2e_ST0(CPUX86State *env) -{ - switch (env->fpuc & FPU_RC_MASK) { - case FPU_RC_DOWN: - case FPU_RC_CHOP: - ST0 = floatx80_l2e_d; - break; - default: - ST0 = floatx80_l2e; - break; - } -} - -void helper_fldpi_ST0(CPUX86State *env) -{ - switch (env->fpuc & FPU_RC_MASK) { - case FPU_RC_DOWN: - case FPU_RC_CHOP: - ST0 = floatx80_pi_d; - break; - default: - ST0 = floatx80_pi; - break; - } -} - -void helper_fldlg2_ST0(CPUX86State *env) -{ - switch (env->fpuc & FPU_RC_MASK) { - case FPU_RC_DOWN: - case FPU_RC_CHOP: - ST0 = floatx80_lg2_d; - break; - default: - ST0 = floatx80_lg2; - break; - } -} - -void helper_fldln2_ST0(CPUX86State *env) -{ - switch (env->fpuc & FPU_RC_MASK) { - case FPU_RC_DOWN: - case FPU_RC_CHOP: - ST0 = floatx80_ln2_d; - break; - default: - ST0 = floatx80_ln2; - break; - } -} - -void helper_fldz_ST0(CPUX86State *env) -{ - ST0 = floatx80_zero; -} - -void helper_fldz_FT0(CPUX86State *env) -{ - FT0 = floatx80_zero; -} - -uint32_t helper_fnstsw(CPUX86State *env) -{ - return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; -} - -uint32_t helper_fnstcw(CPUX86State *env) -{ - return env->fpuc; -} - -void update_fp_status(CPUX86State *env) -{ - int rnd_type; - - /* set rounding mode */ - switch (env->fpuc & FPU_RC_MASK) { - default: - case FPU_RC_NEAR: - rnd_type = float_round_nearest_even; - break; - case FPU_RC_DOWN: - rnd_type = float_round_down; - break; - case FPU_RC_UP: - rnd_type = float_round_up; - break; - case FPU_RC_CHOP: - rnd_type = float_round_to_zero; - break; - } - set_float_rounding_mode(rnd_type, &env->fp_status); - switch ((env->fpuc >> 8) & 3) { - case 0: - rnd_type = 32; - break; - case 2: - rnd_type = 64; - break; - case 3: - default: - rnd_type = 80; - break; - } - set_floatx80_rounding_precision(rnd_type, &env->fp_status); -} - -void helper_fldcw(CPUX86State *env, uint32_t val) -{ - cpu_set_fpuc(env, val); -} - -void helper_fclex(CPUX86State *env) -{ - env->fpus &= 0x7f00; -} - -void helper_fwait(CPUX86State *env) -{ - if (env->fpus & FPUS_SE) { - fpu_raise_exception(env, GETPC()); - } -} - -void helper_fninit(CPUX86State *env) -{ - env->fpus = 0; - env->fpstt = 0; - cpu_set_fpuc(env, 0x37f); - env->fptags[0] = 1; - env->fptags[1] = 1; - env->fptags[2] = 1; - env->fptags[3] = 1; - env->fptags[4] = 1; - env->fptags[5] = 1; - env->fptags[6] = 1; - env->fptags[7] = 1; -} - -/* BCD ops */ - -void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) -{ - floatx80 tmp; - uint64_t val; - unsigned int v; - int i; - - val = 0; - for (i = 8; i >= 0; i--) { - v = cpu_ldub_data_ra(env, ptr + i, GETPC()); - val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); - } - tmp = int64_to_floatx80(val, &env->fp_status); - if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { - tmp = floatx80_chs(tmp); - } - fpush(env); - ST0 = tmp; -} - -void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) -{ - uint8_t old_flags = save_exception_flags(env); - int v; - target_ulong mem_ref, mem_end; - int64_t val; - CPU_LDoubleU temp; - - temp.d = ST0; - - val = floatx80_to_int64(ST0, &env->fp_status); - mem_ref = ptr; - if (val >= 1000000000000000000LL || val <= -1000000000000000000LL) { - set_float_exception_flags(float_flag_invalid, &env->fp_status); - while (mem_ref < ptr + 7) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); - } - cpu_stb_data_ra(env, mem_ref++, 0xc0, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); - cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); - merge_exception_flags(env, old_flags); - return; - } - mem_end = mem_ref + 9; - if (SIGND(temp)) { - cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); - val = -val; - } else { - cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); - } - while (mem_ref < mem_end) { - if (val == 0) { - break; - } - v = val % 100; - val = val / 100; - v = ((v / 10) << 4) | (v % 10); - cpu_stb_data_ra(env, mem_ref++, v, GETPC()); - } - while (mem_ref < mem_end) { - cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); - } - merge_exception_flags(env, old_flags); -} - -/* 128-bit significand of log(2). */ -#define ln2_sig_high 0xb17217f7d1cf79abULL -#define ln2_sig_low 0xc9e3b39803f2f6afULL - -/* - * Polynomial coefficients for an approximation to (2^x - 1) / x, on - * the interval [-1/64, 1/64]. - */ -#define f2xm1_coeff_0 make_floatx80(0x3ffe, 0xb17217f7d1cf79acULL) -#define f2xm1_coeff_0_low make_floatx80(0xbfbc, 0xd87edabf495b3762ULL) -#define f2xm1_coeff_1 make_floatx80(0x3ffc, 0xf5fdeffc162c7543ULL) -#define f2xm1_coeff_2 make_floatx80(0x3ffa, 0xe35846b82505fcc7ULL) -#define f2xm1_coeff_3 make_floatx80(0x3ff8, 0x9d955b7dd273b899ULL) -#define f2xm1_coeff_4 make_floatx80(0x3ff5, 0xaec3ff3c4ef4ac0cULL) -#define f2xm1_coeff_5 make_floatx80(0x3ff2, 0xa184897c3a7f0de9ULL) -#define f2xm1_coeff_6 make_floatx80(0x3fee, 0xffe634d0ec30d504ULL) -#define f2xm1_coeff_7 make_floatx80(0x3feb, 0xb160111d2db515e4ULL) - -struct f2xm1_data { - /* - * A value very close to a multiple of 1/32, such that 2^t and 2^t - 1 - * are very close to exact floatx80 values. - */ - floatx80 t; - /* The value of 2^t. */ - floatx80 exp2; - /* The value of 2^t - 1. */ - floatx80 exp2m1; -}; - -static const struct f2xm1_data f2xm1_table[65] = { - { make_floatx80_init(0xbfff, 0x8000000000000000ULL), - make_floatx80_init(0x3ffe, 0x8000000000000000ULL), - make_floatx80_init(0xbffe, 0x8000000000000000ULL) }, - { make_floatx80_init(0xbffe, 0xf800000000002e7eULL), - make_floatx80_init(0x3ffe, 0x82cd8698ac2b9160ULL), - make_floatx80_init(0xbffd, 0xfa64f2cea7a8dd40ULL) }, - { make_floatx80_init(0xbffe, 0xefffffffffffe960ULL), - make_floatx80_init(0x3ffe, 0x85aac367cc488345ULL), - make_floatx80_init(0xbffd, 0xf4aa7930676ef976ULL) }, - { make_floatx80_init(0xbffe, 0xe800000000006f10ULL), - make_floatx80_init(0x3ffe, 0x88980e8092da5c14ULL), - make_floatx80_init(0xbffd, 0xeecfe2feda4b47d8ULL) }, - { make_floatx80_init(0xbffe, 0xe000000000008a45ULL), - make_floatx80_init(0x3ffe, 0x8b95c1e3ea8ba2a5ULL), - make_floatx80_init(0xbffd, 0xe8d47c382ae8bab6ULL) }, - { make_floatx80_init(0xbffe, 0xd7ffffffffff8a9eULL), - make_floatx80_init(0x3ffe, 0x8ea4398b45cd8116ULL), - make_floatx80_init(0xbffd, 0xe2b78ce97464fdd4ULL) }, - { make_floatx80_init(0xbffe, 0xd0000000000019a0ULL), - make_floatx80_init(0x3ffe, 0x91c3d373ab11b919ULL), - make_floatx80_init(0xbffd, 0xdc785918a9dc8dceULL) }, - { make_floatx80_init(0xbffe, 0xc7ffffffffff14dfULL), - make_floatx80_init(0x3ffe, 0x94f4efa8fef76836ULL), - make_floatx80_init(0xbffd, 0xd61620ae02112f94ULL) }, - { make_floatx80_init(0xbffe, 0xc000000000006530ULL), - make_floatx80_init(0x3ffe, 0x9837f0518db87fbbULL), - make_floatx80_init(0xbffd, 0xcf901f5ce48f008aULL) }, - { make_floatx80_init(0xbffe, 0xb7ffffffffff1723ULL), - make_floatx80_init(0x3ffe, 0x9b8d39b9d54eb74cULL), - make_floatx80_init(0xbffd, 0xc8e58c8c55629168ULL) }, - { make_floatx80_init(0xbffe, 0xb00000000000b5e1ULL), - make_floatx80_init(0x3ffe, 0x9ef5326091a0c366ULL), - make_floatx80_init(0xbffd, 0xc2159b3edcbe7934ULL) }, - { make_floatx80_init(0xbffe, 0xa800000000006f8aULL), - make_floatx80_init(0x3ffe, 0xa27043030c49370aULL), - make_floatx80_init(0xbffd, 0xbb1f79f9e76d91ecULL) }, - { make_floatx80_init(0xbffe, 0x9fffffffffff816aULL), - make_floatx80_init(0x3ffe, 0xa5fed6a9b15171cfULL), - make_floatx80_init(0xbffd, 0xb40252ac9d5d1c62ULL) }, - { make_floatx80_init(0xbffe, 0x97ffffffffffb621ULL), - make_floatx80_init(0x3ffe, 0xa9a15ab4ea7c30e6ULL), - make_floatx80_init(0xbffd, 0xacbd4a962b079e34ULL) }, - { make_floatx80_init(0xbffe, 0x8fffffffffff162bULL), - make_floatx80_init(0x3ffe, 0xad583eea42a1b886ULL), - make_floatx80_init(0xbffd, 0xa54f822b7abc8ef4ULL) }, - { make_floatx80_init(0xbffe, 0x87ffffffffff4d34ULL), - make_floatx80_init(0x3ffe, 0xb123f581d2ac7b51ULL), - make_floatx80_init(0xbffd, 0x9db814fc5aa7095eULL) }, - { make_floatx80_init(0xbffe, 0x800000000000227dULL), - make_floatx80_init(0x3ffe, 0xb504f333f9de539dULL), - make_floatx80_init(0xbffd, 0x95f619980c4358c6ULL) }, - { make_floatx80_init(0xbffd, 0xefffffffffff3978ULL), - make_floatx80_init(0x3ffe, 0xb8fbaf4762fbd0a1ULL), - make_floatx80_init(0xbffd, 0x8e08a1713a085ebeULL) }, - { make_floatx80_init(0xbffd, 0xe00000000000df81ULL), - make_floatx80_init(0x3ffe, 0xbd08a39f580bfd8cULL), - make_floatx80_init(0xbffd, 0x85eeb8c14fe804e8ULL) }, - { make_floatx80_init(0xbffd, 0xd00000000000bccfULL), - make_floatx80_init(0x3ffe, 0xc12c4cca667062f6ULL), - make_floatx80_init(0xbffc, 0xfb4eccd6663e7428ULL) }, - { make_floatx80_init(0xbffd, 0xc00000000000eff0ULL), - make_floatx80_init(0x3ffe, 0xc5672a1155069abeULL), - make_floatx80_init(0xbffc, 0xea6357baabe59508ULL) }, - { make_floatx80_init(0xbffd, 0xb000000000000fe6ULL), - make_floatx80_init(0x3ffe, 0xc9b9bd866e2f234bULL), - make_floatx80_init(0xbffc, 0xd91909e6474372d4ULL) }, - { make_floatx80_init(0xbffd, 0x9fffffffffff2172ULL), - make_floatx80_init(0x3ffe, 0xce248c151f84bf00ULL), - make_floatx80_init(0xbffc, 0xc76dcfab81ed0400ULL) }, - { make_floatx80_init(0xbffd, 0x8fffffffffffafffULL), - make_floatx80_init(0x3ffe, 0xd2a81d91f12afb2bULL), - make_floatx80_init(0xbffc, 0xb55f89b83b541354ULL) }, - { make_floatx80_init(0xbffc, 0xffffffffffff81a3ULL), - make_floatx80_init(0x3ffe, 0xd744fccad69d7d5eULL), - make_floatx80_init(0xbffc, 0xa2ec0cd4a58a0a88ULL) }, - { make_floatx80_init(0xbffc, 0xdfffffffffff1568ULL), - make_floatx80_init(0x3ffe, 0xdbfbb797daf25a44ULL), - make_floatx80_init(0xbffc, 0x901121a0943696f0ULL) }, - { make_floatx80_init(0xbffc, 0xbfffffffffff68daULL), - make_floatx80_init(0x3ffe, 0xe0ccdeec2a94f811ULL), - make_floatx80_init(0xbffb, 0xf999089eab583f78ULL) }, - { make_floatx80_init(0xbffc, 0x9fffffffffff4690ULL), - make_floatx80_init(0x3ffe, 0xe5b906e77c83657eULL), - make_floatx80_init(0xbffb, 0xd237c8c41be4d410ULL) }, - { make_floatx80_init(0xbffb, 0xffffffffffff8aeeULL), - make_floatx80_init(0x3ffe, 0xeac0c6e7dd24427cULL), - make_floatx80_init(0xbffb, 0xa9f9c8c116ddec20ULL) }, - { make_floatx80_init(0xbffb, 0xbfffffffffff2d18ULL), - make_floatx80_init(0x3ffe, 0xefe4b99bdcdb06ebULL), - make_floatx80_init(0xbffb, 0x80da33211927c8a8ULL) }, - { make_floatx80_init(0xbffa, 0xffffffffffff8ccbULL), - make_floatx80_init(0x3ffe, 0xf5257d152486d0f4ULL), - make_floatx80_init(0xbffa, 0xada82eadb792f0c0ULL) }, - { make_floatx80_init(0xbff9, 0xffffffffffff11feULL), - make_floatx80_init(0x3ffe, 0xfa83b2db722a0846ULL), - make_floatx80_init(0xbff9, 0xaf89a491babef740ULL) }, - { floatx80_zero_init, - make_floatx80_init(0x3fff, 0x8000000000000000ULL), - floatx80_zero_init }, - { make_floatx80_init(0x3ff9, 0xffffffffffff2680ULL), - make_floatx80_init(0x3fff, 0x82cd8698ac2b9f6fULL), - make_floatx80_init(0x3ff9, 0xb361a62b0ae7dbc0ULL) }, - { make_floatx80_init(0x3ffb, 0x800000000000b500ULL), - make_floatx80_init(0x3fff, 0x85aac367cc488345ULL), - make_floatx80_init(0x3ffa, 0xb5586cf9891068a0ULL) }, - { make_floatx80_init(0x3ffb, 0xbfffffffffff4b67ULL), - make_floatx80_init(0x3fff, 0x88980e8092da7cceULL), - make_floatx80_init(0x3ffb, 0x8980e8092da7cce0ULL) }, - { make_floatx80_init(0x3ffb, 0xffffffffffffff57ULL), - make_floatx80_init(0x3fff, 0x8b95c1e3ea8bd6dfULL), - make_floatx80_init(0x3ffb, 0xb95c1e3ea8bd6df0ULL) }, - { make_floatx80_init(0x3ffc, 0x9fffffffffff811fULL), - make_floatx80_init(0x3fff, 0x8ea4398b45cd4780ULL), - make_floatx80_init(0x3ffb, 0xea4398b45cd47800ULL) }, - { make_floatx80_init(0x3ffc, 0xbfffffffffff9980ULL), - make_floatx80_init(0x3fff, 0x91c3d373ab11b919ULL), - make_floatx80_init(0x3ffc, 0x8e1e9b9d588dc8c8ULL) }, - { make_floatx80_init(0x3ffc, 0xdffffffffffff631ULL), - make_floatx80_init(0x3fff, 0x94f4efa8fef70864ULL), - make_floatx80_init(0x3ffc, 0xa7a77d47f7b84320ULL) }, - { make_floatx80_init(0x3ffc, 0xffffffffffff2499ULL), - make_floatx80_init(0x3fff, 0x9837f0518db892d4ULL), - make_floatx80_init(0x3ffc, 0xc1bf828c6dc496a0ULL) }, - { make_floatx80_init(0x3ffd, 0x8fffffffffff80fbULL), - make_floatx80_init(0x3fff, 0x9b8d39b9d54e3a79ULL), - make_floatx80_init(0x3ffc, 0xdc69cdceaa71d3c8ULL) }, - { make_floatx80_init(0x3ffd, 0x9fffffffffffbc23ULL), - make_floatx80_init(0x3fff, 0x9ef5326091a10313ULL), - make_floatx80_init(0x3ffc, 0xf7a993048d081898ULL) }, - { make_floatx80_init(0x3ffd, 0xafffffffffff20ecULL), - make_floatx80_init(0x3fff, 0xa27043030c49370aULL), - make_floatx80_init(0x3ffd, 0x89c10c0c3124dc28ULL) }, - { make_floatx80_init(0x3ffd, 0xc00000000000fd2cULL), - make_floatx80_init(0x3fff, 0xa5fed6a9b15171cfULL), - make_floatx80_init(0x3ffd, 0x97fb5aa6c545c73cULL) }, - { make_floatx80_init(0x3ffd, 0xd0000000000093beULL), - make_floatx80_init(0x3fff, 0xa9a15ab4ea7c30e6ULL), - make_floatx80_init(0x3ffd, 0xa6856ad3a9f0c398ULL) }, - { make_floatx80_init(0x3ffd, 0xe00000000000c2aeULL), - make_floatx80_init(0x3fff, 0xad583eea42a17876ULL), - make_floatx80_init(0x3ffd, 0xb560fba90a85e1d8ULL) }, - { make_floatx80_init(0x3ffd, 0xefffffffffff1e3fULL), - make_floatx80_init(0x3fff, 0xb123f581d2abef6cULL), - make_floatx80_init(0x3ffd, 0xc48fd6074aafbdb0ULL) }, - { make_floatx80_init(0x3ffd, 0xffffffffffff1c23ULL), - make_floatx80_init(0x3fff, 0xb504f333f9de2cadULL), - make_floatx80_init(0x3ffd, 0xd413cccfe778b2b4ULL) }, - { make_floatx80_init(0x3ffe, 0x8800000000006344ULL), - make_floatx80_init(0x3fff, 0xb8fbaf4762fbd0a1ULL), - make_floatx80_init(0x3ffd, 0xe3eebd1d8bef4284ULL) }, - { make_floatx80_init(0x3ffe, 0x9000000000005d67ULL), - make_floatx80_init(0x3fff, 0xbd08a39f580c668dULL), - make_floatx80_init(0x3ffd, 0xf4228e7d60319a34ULL) }, - { make_floatx80_init(0x3ffe, 0x9800000000009127ULL), - make_floatx80_init(0x3fff, 0xc12c4cca6670e042ULL), - make_floatx80_init(0x3ffe, 0x82589994cce1c084ULL) }, - { make_floatx80_init(0x3ffe, 0x9fffffffffff06f9ULL), - make_floatx80_init(0x3fff, 0xc5672a11550655c3ULL), - make_floatx80_init(0x3ffe, 0x8ace5422aa0cab86ULL) }, - { make_floatx80_init(0x3ffe, 0xa7fffffffffff80dULL), - make_floatx80_init(0x3fff, 0xc9b9bd866e2f234bULL), - make_floatx80_init(0x3ffe, 0x93737b0cdc5e4696ULL) }, - { make_floatx80_init(0x3ffe, 0xafffffffffff1470ULL), - make_floatx80_init(0x3fff, 0xce248c151f83fd69ULL), - make_floatx80_init(0x3ffe, 0x9c49182a3f07fad2ULL) }, - { make_floatx80_init(0x3ffe, 0xb800000000000e0aULL), - make_floatx80_init(0x3fff, 0xd2a81d91f12aec5cULL), - make_floatx80_init(0x3ffe, 0xa5503b23e255d8b8ULL) }, - { make_floatx80_init(0x3ffe, 0xc00000000000b7faULL), - make_floatx80_init(0x3fff, 0xd744fccad69dd630ULL), - make_floatx80_init(0x3ffe, 0xae89f995ad3bac60ULL) }, - { make_floatx80_init(0x3ffe, 0xc800000000003aa6ULL), - make_floatx80_init(0x3fff, 0xdbfbb797daf25a44ULL), - make_floatx80_init(0x3ffe, 0xb7f76f2fb5e4b488ULL) }, - { make_floatx80_init(0x3ffe, 0xd00000000000a6aeULL), - make_floatx80_init(0x3fff, 0xe0ccdeec2a954685ULL), - make_floatx80_init(0x3ffe, 0xc199bdd8552a8d0aULL) }, - { make_floatx80_init(0x3ffe, 0xd800000000004165ULL), - make_floatx80_init(0x3fff, 0xe5b906e77c837155ULL), - make_floatx80_init(0x3ffe, 0xcb720dcef906e2aaULL) }, - { make_floatx80_init(0x3ffe, 0xe00000000000582cULL), - make_floatx80_init(0x3fff, 0xeac0c6e7dd24713aULL), - make_floatx80_init(0x3ffe, 0xd5818dcfba48e274ULL) }, - { make_floatx80_init(0x3ffe, 0xe800000000001a5dULL), - make_floatx80_init(0x3fff, 0xefe4b99bdcdb06ebULL), - make_floatx80_init(0x3ffe, 0xdfc97337b9b60dd6ULL) }, - { make_floatx80_init(0x3ffe, 0xefffffffffffc1efULL), - make_floatx80_init(0x3fff, 0xf5257d152486a2faULL), - make_floatx80_init(0x3ffe, 0xea4afa2a490d45f4ULL) }, - { make_floatx80_init(0x3ffe, 0xf800000000001069ULL), - make_floatx80_init(0x3fff, 0xfa83b2db722a0e5cULL), - make_floatx80_init(0x3ffe, 0xf50765b6e4541cb8ULL) }, - { make_floatx80_init(0x3fff, 0x8000000000000000ULL), - make_floatx80_init(0x4000, 0x8000000000000000ULL), - make_floatx80_init(0x3fff, 0x8000000000000000ULL) }, -}; - -void helper_f2xm1(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - uint64_t sig = extractFloatx80Frac(ST0); - int32_t exp = extractFloatx80Exp(ST0); - bool sign = extractFloatx80Sign(ST0); - - if (floatx80_invalid_encoding(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_any_nan(ST0)) { - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_silence_nan(ST0, &env->fp_status); - } - } else if (exp > 0x3fff || - (exp == 0x3fff && sig != (0x8000000000000000ULL))) { - /* Out of range for the instruction, treat as invalid. */ - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - } else if (exp == 0x3fff) { - /* Argument 1 or -1, exact result 1 or -0.5. */ - if (sign) { - ST0 = make_floatx80(0xbffe, 0x8000000000000000ULL); - } - } else if (exp < 0x3fb0) { - if (!floatx80_is_zero(ST0)) { - /* - * Multiplying the argument by an extra-precision version - * of log(2) is sufficiently precise. Zero arguments are - * returned unchanged. - */ - uint64_t sig0, sig1, sig2; - if (exp == 0) { - normalizeFloatx80Subnormal(sig, &exp, &sig); - } - mul128By64To192(ln2_sig_high, ln2_sig_low, sig, &sig0, &sig1, - &sig2); - /* This result is inexact. */ - sig1 |= 1; - ST0 = normalizeRoundAndPackFloatx80(80, sign, exp, sig0, sig1, - &env->fp_status); - } - } else { - floatx80 tmp, y, accum; - bool asign, bsign; - int32_t n, aexp, bexp; - uint64_t asig0, asig1, asig2, bsig0, bsig1; - FloatRoundMode save_mode = env->fp_status.float_rounding_mode; - signed char save_prec = env->fp_status.floatx80_rounding_precision; - env->fp_status.float_rounding_mode = float_round_nearest_even; - env->fp_status.floatx80_rounding_precision = 80; - - /* Find the nearest multiple of 1/32 to the argument. */ - tmp = floatx80_scalbn(ST0, 5, &env->fp_status); - n = 32 + floatx80_to_int32(tmp, &env->fp_status); - y = floatx80_sub(ST0, f2xm1_table[n].t, &env->fp_status); - - if (floatx80_is_zero(y)) { - /* - * Use the value of 2^t - 1 from the table, to avoid - * needing to special-case zero as a result of - * multiplication below. - */ - ST0 = f2xm1_table[n].t; - set_float_exception_flags(float_flag_inexact, &env->fp_status); - env->fp_status.float_rounding_mode = save_mode; - } else { - /* - * Compute the lower parts of a polynomial expansion for - * (2^y - 1) / y. - */ - accum = floatx80_mul(f2xm1_coeff_7, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_6, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_5, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_4, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_3, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_2, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_1, accum, &env->fp_status); - accum = floatx80_mul(accum, y, &env->fp_status); - accum = floatx80_add(f2xm1_coeff_0_low, accum, &env->fp_status); - - /* - * The full polynomial expansion is f2xm1_coeff_0 + accum - * (where accum has much lower magnitude, and so, in - * particular, carry out of the addition is not possible). - * (This expansion is only accurate to about 70 bits, not - * 128 bits.) - */ - aexp = extractFloatx80Exp(f2xm1_coeff_0); - asign = extractFloatx80Sign(f2xm1_coeff_0); - shift128RightJamming(extractFloatx80Frac(accum), 0, - aexp - extractFloatx80Exp(accum), - &asig0, &asig1); - bsig0 = extractFloatx80Frac(f2xm1_coeff_0); - bsig1 = 0; - if (asign == extractFloatx80Sign(accum)) { - add128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); - } else { - sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); - } - /* And thus compute an approximation to 2^y - 1. */ - mul128By64To192(asig0, asig1, extractFloatx80Frac(y), - &asig0, &asig1, &asig2); - aexp += extractFloatx80Exp(y) - 0x3ffe; - asign ^= extractFloatx80Sign(y); - if (n != 32) { - /* - * Multiply this by the precomputed value of 2^t and - * add that of 2^t - 1. - */ - mul128By64To192(asig0, asig1, - extractFloatx80Frac(f2xm1_table[n].exp2), - &asig0, &asig1, &asig2); - aexp += extractFloatx80Exp(f2xm1_table[n].exp2) - 0x3ffe; - bexp = extractFloatx80Exp(f2xm1_table[n].exp2m1); - bsig0 = extractFloatx80Frac(f2xm1_table[n].exp2m1); - bsig1 = 0; - if (bexp < aexp) { - shift128RightJamming(bsig0, bsig1, aexp - bexp, - &bsig0, &bsig1); - } else if (aexp < bexp) { - shift128RightJamming(asig0, asig1, bexp - aexp, - &asig0, &asig1); - aexp = bexp; - } - /* The sign of 2^t - 1 is always that of the result. */ - bsign = extractFloatx80Sign(f2xm1_table[n].exp2m1); - if (asign == bsign) { - /* Avoid possible carry out of the addition. */ - shift128RightJamming(asig0, asig1, 1, - &asig0, &asig1); - shift128RightJamming(bsig0, bsig1, 1, - &bsig0, &bsig1); - ++aexp; - add128(asig0, asig1, bsig0, bsig1, &asig0, &asig1); - } else { - sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); - asign = bsign; - } - } - env->fp_status.float_rounding_mode = save_mode; - /* This result is inexact. */ - asig1 |= 1; - ST0 = normalizeRoundAndPackFloatx80(80, asign, aexp, asig0, asig1, - &env->fp_status); - } - - env->fp_status.floatx80_rounding_precision = save_prec; - } - merge_exception_flags(env, old_flags); -} - -void helper_fptan(CPUX86State *env) -{ - double fptemp = floatx80_to_double(env, ST0); - - if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { - env->fpus |= 0x400; - } else { - fptemp = tan(fptemp); - ST0 = double_to_floatx80(env, fptemp); - fpush(env); - ST0 = floatx80_one; - env->fpus &= ~0x400; /* C2 <-- 0 */ - /* the above code is for |arg| < 2**52 only */ - } -} - -/* Values of pi/4, pi/2, 3pi/4 and pi, with 128-bit precision. */ -#define pi_4_exp 0x3ffe -#define pi_4_sig_high 0xc90fdaa22168c234ULL -#define pi_4_sig_low 0xc4c6628b80dc1cd1ULL -#define pi_2_exp 0x3fff -#define pi_2_sig_high 0xc90fdaa22168c234ULL -#define pi_2_sig_low 0xc4c6628b80dc1cd1ULL -#define pi_34_exp 0x4000 -#define pi_34_sig_high 0x96cbe3f9990e91a7ULL -#define pi_34_sig_low 0x9394c9e8a0a5159dULL -#define pi_exp 0x4000 -#define pi_sig_high 0xc90fdaa22168c234ULL -#define pi_sig_low 0xc4c6628b80dc1cd1ULL - -/* - * Polynomial coefficients for an approximation to atan(x), with only - * odd powers of x used, for x in the interval [-1/16, 1/16]. (Unlike - * for some other approximations, no low part is needed for the first - * coefficient here to achieve a sufficiently accurate result, because - * the coefficient in this minimax approximation is very close to - * exactly 1.) - */ -#define fpatan_coeff_0 make_floatx80(0x3fff, 0x8000000000000000ULL) -#define fpatan_coeff_1 make_floatx80(0xbffd, 0xaaaaaaaaaaaaaa43ULL) -#define fpatan_coeff_2 make_floatx80(0x3ffc, 0xccccccccccbfe4f8ULL) -#define fpatan_coeff_3 make_floatx80(0xbffc, 0x92492491fbab2e66ULL) -#define fpatan_coeff_4 make_floatx80(0x3ffb, 0xe38e372881ea1e0bULL) -#define fpatan_coeff_5 make_floatx80(0xbffb, 0xba2c0104bbdd0615ULL) -#define fpatan_coeff_6 make_floatx80(0x3ffb, 0x9baf7ebf898b42efULL) - -struct fpatan_data { - /* High and low parts of atan(x). */ - floatx80 atan_high, atan_low; -}; - -static const struct fpatan_data fpatan_table[9] = { - { floatx80_zero_init, - floatx80_zero_init }, - { make_floatx80_init(0x3ffb, 0xfeadd4d5617b6e33ULL), - make_floatx80_init(0xbfb9, 0xdda19d8305ddc420ULL) }, - { make_floatx80_init(0x3ffc, 0xfadbafc96406eb15ULL), - make_floatx80_init(0x3fbb, 0xdb8f3debef442fccULL) }, - { make_floatx80_init(0x3ffd, 0xb7b0ca0f26f78474ULL), - make_floatx80_init(0xbfbc, 0xeab9bdba460376faULL) }, - { make_floatx80_init(0x3ffd, 0xed63382b0dda7b45ULL), - make_floatx80_init(0x3fbc, 0xdfc88bd978751a06ULL) }, - { make_floatx80_init(0x3ffe, 0x8f005d5ef7f59f9bULL), - make_floatx80_init(0x3fbd, 0xb906bc2ccb886e90ULL) }, - { make_floatx80_init(0x3ffe, 0xa4bc7d1934f70924ULL), - make_floatx80_init(0x3fbb, 0xcd43f9522bed64f8ULL) }, - { make_floatx80_init(0x3ffe, 0xb8053e2bc2319e74ULL), - make_floatx80_init(0xbfbc, 0xd3496ab7bd6eef0cULL) }, - { make_floatx80_init(0x3ffe, 0xc90fdaa22168c235ULL), - make_floatx80_init(0xbfbc, 0xece675d1fc8f8cbcULL) }, -}; - -void helper_fpatan(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - uint64_t arg0_sig = extractFloatx80Frac(ST0); - int32_t arg0_exp = extractFloatx80Exp(ST0); - bool arg0_sign = extractFloatx80Sign(ST0); - uint64_t arg1_sig = extractFloatx80Frac(ST1); - int32_t arg1_exp = extractFloatx80Exp(ST1); - bool arg1_sign = extractFloatx80Sign(ST1); - - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST0, &env->fp_status); - } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_any_nan(ST0)) { - ST1 = ST0; - } else if (floatx80_is_any_nan(ST1)) { - /* Pass this NaN through. */ - } else if (floatx80_is_zero(ST1) && !arg0_sign) { - /* Pass this zero through. */ - } else if (((floatx80_is_infinity(ST0) && !floatx80_is_infinity(ST1)) || - arg0_exp - arg1_exp >= 80) && - !arg0_sign) { - /* - * Dividing ST1 by ST0 gives the correct result up to - * rounding, and avoids spurious underflow exceptions that - * might result from passing some small values through the - * polynomial approximation, but if a finite nonzero result of - * division is exact, the result of fpatan is still inexact - * (and underflowing where appropriate). - */ - signed char save_prec = env->fp_status.floatx80_rounding_precision; - env->fp_status.floatx80_rounding_precision = 80; - ST1 = floatx80_div(ST1, ST0, &env->fp_status); - env->fp_status.floatx80_rounding_precision = save_prec; - if (!floatx80_is_zero(ST1) && - !(get_float_exception_flags(&env->fp_status) & - float_flag_inexact)) { - /* - * The mathematical result is very slightly closer to zero - * than this exact result. Round a value with the - * significand adjusted accordingly to get the correct - * exceptions, and possibly an adjusted result depending - * on the rounding mode. - */ - uint64_t sig = extractFloatx80Frac(ST1); - int32_t exp = extractFloatx80Exp(ST1); - bool sign = extractFloatx80Sign(ST1); - if (exp == 0) { - normalizeFloatx80Subnormal(sig, &exp, &sig); - } - ST1 = normalizeRoundAndPackFloatx80(80, sign, exp, sig - 1, - -1, &env->fp_status); - } - } else { - /* The result is inexact. */ - bool rsign = arg1_sign; - int32_t rexp; - uint64_t rsig0, rsig1; - if (floatx80_is_zero(ST1)) { - /* - * ST0 is negative. The result is pi with the sign of - * ST1. - */ - rexp = pi_exp; - rsig0 = pi_sig_high; - rsig1 = pi_sig_low; - } else if (floatx80_is_infinity(ST1)) { - if (floatx80_is_infinity(ST0)) { - if (arg0_sign) { - rexp = pi_34_exp; - rsig0 = pi_34_sig_high; - rsig1 = pi_34_sig_low; - } else { - rexp = pi_4_exp; - rsig0 = pi_4_sig_high; - rsig1 = pi_4_sig_low; - } - } else { - rexp = pi_2_exp; - rsig0 = pi_2_sig_high; - rsig1 = pi_2_sig_low; - } - } else if (floatx80_is_zero(ST0) || arg1_exp - arg0_exp >= 80) { - rexp = pi_2_exp; - rsig0 = pi_2_sig_high; - rsig1 = pi_2_sig_low; - } else if (floatx80_is_infinity(ST0) || arg0_exp - arg1_exp >= 80) { - /* ST0 is negative. */ - rexp = pi_exp; - rsig0 = pi_sig_high; - rsig1 = pi_sig_low; - } else { - /* - * ST0 and ST1 are finite, nonzero and with exponents not - * too far apart. - */ - int32_t adj_exp, num_exp, den_exp, xexp, yexp, n, texp, zexp, aexp; - int32_t azexp, axexp; - bool adj_sub, ysign, zsign; - uint64_t adj_sig0, adj_sig1, num_sig, den_sig, xsig0, xsig1; - uint64_t msig0, msig1, msig2, remsig0, remsig1, remsig2; - uint64_t ysig0, ysig1, tsig, zsig0, zsig1, asig0, asig1; - uint64_t azsig0, azsig1; - uint64_t azsig2, azsig3, axsig0, axsig1; - floatx80 x8; - FloatRoundMode save_mode = env->fp_status.float_rounding_mode; - signed char save_prec = env->fp_status.floatx80_rounding_precision; - env->fp_status.float_rounding_mode = float_round_nearest_even; - env->fp_status.floatx80_rounding_precision = 80; - - if (arg0_exp == 0) { - normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); - } - if (arg1_exp == 0) { - normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); - } - if (arg0_exp > arg1_exp || - (arg0_exp == arg1_exp && arg0_sig >= arg1_sig)) { - /* Work with abs(ST1) / abs(ST0). */ - num_exp = arg1_exp; - num_sig = arg1_sig; - den_exp = arg0_exp; - den_sig = arg0_sig; - if (arg0_sign) { - /* The result is subtracted from pi. */ - adj_exp = pi_exp; - adj_sig0 = pi_sig_high; - adj_sig1 = pi_sig_low; - adj_sub = true; - } else { - /* The result is used as-is. */ - adj_exp = 0; - adj_sig0 = 0; - adj_sig1 = 0; - adj_sub = false; - } - } else { - /* Work with abs(ST0) / abs(ST1). */ - num_exp = arg0_exp; - num_sig = arg0_sig; - den_exp = arg1_exp; - den_sig = arg1_sig; - /* The result is added to or subtracted from pi/2. */ - adj_exp = pi_2_exp; - adj_sig0 = pi_2_sig_high; - adj_sig1 = pi_2_sig_low; - adj_sub = !arg0_sign; - } - - /* - * Compute x = num/den, where 0 < x <= 1 and x is not too - * small. - */ - xexp = num_exp - den_exp + 0x3ffe; - remsig0 = num_sig; - remsig1 = 0; - if (den_sig <= remsig0) { - shift128Right(remsig0, remsig1, 1, &remsig0, &remsig1); - ++xexp; - } - xsig0 = estimateDiv128To64(remsig0, remsig1, den_sig); - mul64To128(den_sig, xsig0, &msig0, &msig1); - sub128(remsig0, remsig1, msig0, msig1, &remsig0, &remsig1); - while ((int64_t) remsig0 < 0) { - --xsig0; - add128(remsig0, remsig1, 0, den_sig, &remsig0, &remsig1); - } - xsig1 = estimateDiv128To64(remsig1, 0, den_sig); - /* - * No need to correct any estimation error in xsig1; even - * with such error, it is accurate enough. - */ - - /* - * Split x as x = t + y, where t = n/8 is the nearest - * multiple of 1/8 to x. - */ - x8 = normalizeRoundAndPackFloatx80(80, false, xexp + 3, xsig0, - xsig1, &env->fp_status); - n = floatx80_to_int32(x8, &env->fp_status); - if (n == 0) { - ysign = false; - yexp = xexp; - ysig0 = xsig0; - ysig1 = xsig1; - texp = 0; - tsig = 0; - } else { - int shift = clz32(n) + 32; - texp = 0x403b - shift; - tsig = n; - tsig <<= shift; - if (texp == xexp) { - sub128(xsig0, xsig1, tsig, 0, &ysig0, &ysig1); - if ((int64_t) ysig0 >= 0) { - ysign = false; - if (ysig0 == 0) { - if (ysig1 == 0) { - yexp = 0; - } else { - shift = clz64(ysig1) + 64; - yexp = xexp - shift; - shift128Left(ysig0, ysig1, shift, - &ysig0, &ysig1); - } - } else { - shift = clz64(ysig0); - yexp = xexp - shift; - shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); - } - } else { - ysign = true; - sub128(0, 0, ysig0, ysig1, &ysig0, &ysig1); - if (ysig0 == 0) { - shift = clz64(ysig1) + 64; - } else { - shift = clz64(ysig0); - } - yexp = xexp - shift; - shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); - } - } else { - /* - * t's exponent must be greater than x's because t - * is positive and the nearest multiple of 1/8 to - * x, and if x has a greater exponent, the power - * of 2 with that exponent is also a multiple of - * 1/8. - */ - uint64_t usig0, usig1; - shift128RightJamming(xsig0, xsig1, texp - xexp, - &usig0, &usig1); - ysign = true; - sub128(tsig, 0, usig0, usig1, &ysig0, &ysig1); - if (ysig0 == 0) { - shift = clz64(ysig1) + 64; - } else { - shift = clz64(ysig0); - } - yexp = texp - shift; - shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); - } - } - - /* - * Compute z = y/(1+tx), so arctan(x) = arctan(t) + - * arctan(z). - */ - zsign = ysign; - if (texp == 0 || yexp == 0) { - zexp = yexp; - zsig0 = ysig0; - zsig1 = ysig1; - } else { - /* - * t <= 1, x <= 1 and if both are 1 then y is 0, so tx < 1. - */ - int32_t dexp = texp + xexp - 0x3ffe; - uint64_t dsig0, dsig1, dsig2; - mul128By64To192(xsig0, xsig1, tsig, &dsig0, &dsig1, &dsig2); - /* - * dexp <= 0x3fff (and if equal, dsig0 has a leading 0 - * bit). Add 1 to produce the denominator 1+tx. - */ - shift128RightJamming(dsig0, dsig1, 0x3fff - dexp, - &dsig0, &dsig1); - dsig0 |= 0x8000000000000000ULL; - zexp = yexp - 1; - remsig0 = ysig0; - remsig1 = ysig1; - remsig2 = 0; - if (dsig0 <= remsig0) { - shift128Right(remsig0, remsig1, 1, &remsig0, &remsig1); - ++zexp; - } - zsig0 = estimateDiv128To64(remsig0, remsig1, dsig0); - mul128By64To192(dsig0, dsig1, zsig0, &msig0, &msig1, &msig2); - sub192(remsig0, remsig1, remsig2, msig0, msig1, msig2, - &remsig0, &remsig1, &remsig2); - while ((int64_t) remsig0 < 0) { - --zsig0; - add192(remsig0, remsig1, remsig2, 0, dsig0, dsig1, - &remsig0, &remsig1, &remsig2); - } - zsig1 = estimateDiv128To64(remsig1, remsig2, dsig0); - /* No need to correct any estimation error in zsig1. */ - } - - if (zexp == 0) { - azexp = 0; - azsig0 = 0; - azsig1 = 0; - } else { - floatx80 z2, accum; - uint64_t z2sig0, z2sig1, z2sig2, z2sig3; - /* Compute z^2. */ - mul128To256(zsig0, zsig1, zsig0, zsig1, - &z2sig0, &z2sig1, &z2sig2, &z2sig3); - z2 = normalizeRoundAndPackFloatx80(80, false, - zexp + zexp - 0x3ffe, - z2sig0, z2sig1, - &env->fp_status); - - /* Compute the lower parts of the polynomial expansion. */ - accum = floatx80_mul(fpatan_coeff_6, z2, &env->fp_status); - accum = floatx80_add(fpatan_coeff_5, accum, &env->fp_status); - accum = floatx80_mul(accum, z2, &env->fp_status); - accum = floatx80_add(fpatan_coeff_4, accum, &env->fp_status); - accum = floatx80_mul(accum, z2, &env->fp_status); - accum = floatx80_add(fpatan_coeff_3, accum, &env->fp_status); - accum = floatx80_mul(accum, z2, &env->fp_status); - accum = floatx80_add(fpatan_coeff_2, accum, &env->fp_status); - accum = floatx80_mul(accum, z2, &env->fp_status); - accum = floatx80_add(fpatan_coeff_1, accum, &env->fp_status); - accum = floatx80_mul(accum, z2, &env->fp_status); - - /* - * The full polynomial expansion is z*(fpatan_coeff_0 + accum). - * fpatan_coeff_0 is 1, and accum is negative and much smaller. - */ - aexp = extractFloatx80Exp(fpatan_coeff_0); - shift128RightJamming(extractFloatx80Frac(accum), 0, - aexp - extractFloatx80Exp(accum), - &asig0, &asig1); - sub128(extractFloatx80Frac(fpatan_coeff_0), 0, asig0, asig1, - &asig0, &asig1); - /* Multiply by z to compute arctan(z). */ - azexp = aexp + zexp - 0x3ffe; - mul128To256(asig0, asig1, zsig0, zsig1, &azsig0, &azsig1, - &azsig2, &azsig3); - } - - /* Add arctan(t) (positive or zero) and arctan(z) (sign zsign). */ - if (texp == 0) { - /* z is positive. */ - axexp = azexp; - axsig0 = azsig0; - axsig1 = azsig1; - } else { - bool low_sign = extractFloatx80Sign(fpatan_table[n].atan_low); - int32_t low_exp = extractFloatx80Exp(fpatan_table[n].atan_low); - uint64_t low_sig0 = - extractFloatx80Frac(fpatan_table[n].atan_low); - uint64_t low_sig1 = 0; - axexp = extractFloatx80Exp(fpatan_table[n].atan_high); - axsig0 = extractFloatx80Frac(fpatan_table[n].atan_high); - axsig1 = 0; - shift128RightJamming(low_sig0, low_sig1, axexp - low_exp, - &low_sig0, &low_sig1); - if (low_sign) { - sub128(axsig0, axsig1, low_sig0, low_sig1, - &axsig0, &axsig1); - } else { - add128(axsig0, axsig1, low_sig0, low_sig1, - &axsig0, &axsig1); - } - if (azexp >= axexp) { - shift128RightJamming(axsig0, axsig1, azexp - axexp + 1, - &axsig0, &axsig1); - axexp = azexp + 1; - shift128RightJamming(azsig0, azsig1, 1, - &azsig0, &azsig1); - } else { - shift128RightJamming(axsig0, axsig1, 1, - &axsig0, &axsig1); - shift128RightJamming(azsig0, azsig1, axexp - azexp + 1, - &azsig0, &azsig1); - ++axexp; - } - if (zsign) { - sub128(axsig0, axsig1, azsig0, azsig1, - &axsig0, &axsig1); - } else { - add128(axsig0, axsig1, azsig0, azsig1, - &axsig0, &axsig1); - } - } - - if (adj_exp == 0) { - rexp = axexp; - rsig0 = axsig0; - rsig1 = axsig1; - } else { - /* - * Add or subtract arctan(x) (exponent axexp, - * significand axsig0 and axsig1, positive, not - * necessarily normalized) to the number given by - * adj_exp, adj_sig0 and adj_sig1, according to - * adj_sub. - */ - if (adj_exp >= axexp) { - shift128RightJamming(axsig0, axsig1, adj_exp - axexp + 1, - &axsig0, &axsig1); - rexp = adj_exp + 1; - shift128RightJamming(adj_sig0, adj_sig1, 1, - &adj_sig0, &adj_sig1); - } else { - shift128RightJamming(axsig0, axsig1, 1, - &axsig0, &axsig1); - shift128RightJamming(adj_sig0, adj_sig1, - axexp - adj_exp + 1, - &adj_sig0, &adj_sig1); - rexp = axexp + 1; - } - if (adj_sub) { - sub128(adj_sig0, adj_sig1, axsig0, axsig1, - &rsig0, &rsig1); - } else { - add128(adj_sig0, adj_sig1, axsig0, axsig1, - &rsig0, &rsig1); - } - } - - env->fp_status.float_rounding_mode = save_mode; - env->fp_status.floatx80_rounding_precision = save_prec; - } - /* This result is inexact. */ - rsig1 |= 1; - ST1 = normalizeRoundAndPackFloatx80(80, rsign, rexp, - rsig0, rsig1, &env->fp_status); - } - - fpop(env); - merge_exception_flags(env, old_flags); -} - -void helper_fxtract(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - CPU_LDoubleU temp; - - temp.d = ST0; - - if (floatx80_is_zero(ST0)) { - /* Easy way to generate -inf and raising division by 0 exception */ - ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero, - &env->fp_status); - fpush(env); - ST0 = temp.d; - } else if (floatx80_invalid_encoding(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - fpush(env); - ST0 = ST1; - } else if (floatx80_is_any_nan(ST0)) { - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_silence_nan(ST0, &env->fp_status); - } - fpush(env); - ST0 = ST1; - } else if (floatx80_is_infinity(ST0)) { - fpush(env); - ST0 = ST1; - ST1 = floatx80_infinity; - } else { - int expdif; - - if (EXPD(temp) == 0) { - int shift = clz64(temp.l.lower); - temp.l.lower <<= shift; - expdif = 1 - EXPBIAS - shift; - float_raise(float_flag_input_denormal, &env->fp_status); - } else { - expdif = EXPD(temp) - EXPBIAS; - } - /* DP exponent bias */ - ST0 = int32_to_floatx80(expdif, &env->fp_status); - fpush(env); - BIASEXPONENT(temp); - ST0 = temp.d; - } - merge_exception_flags(env, old_flags); -} - -static void helper_fprem_common(CPUX86State *env, bool mod) -{ - uint8_t old_flags = save_exception_flags(env); - uint64_t quotient; - CPU_LDoubleU temp0, temp1; - int exp0, exp1, expdiff; - - temp0.d = ST0; - temp1.d = ST1; - exp0 = EXPD(temp0); - exp1 = EXPD(temp1); - - env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ - if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || - exp0 == 0x7fff || exp1 == 0x7fff || - floatx80_invalid_encoding(ST0) || floatx80_invalid_encoding(ST1)) { - ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); - } else { - if (exp0 == 0) { - exp0 = 1 - clz64(temp0.l.lower); - } - if (exp1 == 0) { - exp1 = 1 - clz64(temp1.l.lower); - } - expdiff = exp0 - exp1; - if (expdiff < 64) { - ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); - env->fpus |= (quotient & 0x4) << (8 - 2); /* (C0) <-- q2 */ - env->fpus |= (quotient & 0x2) << (14 - 1); /* (C3) <-- q1 */ - env->fpus |= (quotient & 0x1) << (9 - 0); /* (C1) <-- q0 */ - } else { - /* - * Partial remainder. This choice of how many bits to - * process at once is specified in AMD instruction set - * manuals, and empirically is followed by Intel - * processors as well; it ensures that the final remainder - * operation in a loop does produce the correct low three - * bits of the quotient. AMD manuals specify that the - * flags other than C2 are cleared, and empirically Intel - * processors clear them as well. - */ - int n = 32 + (expdiff % 32); - temp1.d = floatx80_scalbn(temp1.d, expdiff - n, &env->fp_status); - ST0 = floatx80_mod(ST0, temp1.d, &env->fp_status); - env->fpus |= 0x400; /* C2 <-- 1 */ - } - } - merge_exception_flags(env, old_flags); -} - -void helper_fprem1(CPUX86State *env) -{ - helper_fprem_common(env, false); -} - -void helper_fprem(CPUX86State *env) -{ - helper_fprem_common(env, true); -} - -/* 128-bit significand of log2(e). */ -#define log2_e_sig_high 0xb8aa3b295c17f0bbULL -#define log2_e_sig_low 0xbe87fed0691d3e89ULL - -/* - * Polynomial coefficients for an approximation to log2((1+x)/(1-x)), - * with only odd powers of x used, for x in the interval [2*sqrt(2)-3, - * 3-2*sqrt(2)], which corresponds to logarithms of numbers in the - * interval [sqrt(2)/2, sqrt(2)]. - */ -#define fyl2x_coeff_0 make_floatx80(0x4000, 0xb8aa3b295c17f0bcULL) -#define fyl2x_coeff_0_low make_floatx80(0xbfbf, 0x834972fe2d7bab1bULL) -#define fyl2x_coeff_1 make_floatx80(0x3ffe, 0xf6384ee1d01febb8ULL) -#define fyl2x_coeff_2 make_floatx80(0x3ffe, 0x93bb62877cdfa2e3ULL) -#define fyl2x_coeff_3 make_floatx80(0x3ffd, 0xd30bb153d808f269ULL) -#define fyl2x_coeff_4 make_floatx80(0x3ffd, 0xa42589eaf451499eULL) -#define fyl2x_coeff_5 make_floatx80(0x3ffd, 0x864d42c0f8f17517ULL) -#define fyl2x_coeff_6 make_floatx80(0x3ffc, 0xe3476578adf26272ULL) -#define fyl2x_coeff_7 make_floatx80(0x3ffc, 0xc506c5f874e6d80fULL) -#define fyl2x_coeff_8 make_floatx80(0x3ffc, 0xac5cf50cc57d6372ULL) -#define fyl2x_coeff_9 make_floatx80(0x3ffc, 0xb1ed0066d971a103ULL) - -/* - * Compute an approximation of log2(1+arg), where 1+arg is in the - * interval [sqrt(2)/2, sqrt(2)]. It is assumed that when this - * function is called, rounding precision is set to 80 and the - * round-to-nearest mode is in effect. arg must not be exactly zero, - * and must not be so close to zero that underflow might occur. - */ -static void helper_fyl2x_common(CPUX86State *env, floatx80 arg, int32_t *exp, - uint64_t *sig0, uint64_t *sig1) -{ - uint64_t arg0_sig = extractFloatx80Frac(arg); - int32_t arg0_exp = extractFloatx80Exp(arg); - bool arg0_sign = extractFloatx80Sign(arg); - bool asign; - int32_t dexp, texp, aexp; - uint64_t dsig0, dsig1, tsig0, tsig1, rsig0, rsig1, rsig2; - uint64_t msig0, msig1, msig2, t2sig0, t2sig1, t2sig2, t2sig3; - uint64_t asig0, asig1, asig2, asig3, bsig0, bsig1; - floatx80 t2, accum; - - /* - * Compute an approximation of arg/(2+arg), with extra precision, - * as the argument to a polynomial approximation. The extra - * precision is only needed for the first term of the - * approximation, with subsequent terms being significantly - * smaller; the approximation only uses odd exponents, and the - * square of arg/(2+arg) is at most 17-12*sqrt(2) = 0.029.... - */ - if (arg0_sign) { - dexp = 0x3fff; - shift128RightJamming(arg0_sig, 0, dexp - arg0_exp, &dsig0, &dsig1); - sub128(0, 0, dsig0, dsig1, &dsig0, &dsig1); - } else { - dexp = 0x4000; - shift128RightJamming(arg0_sig, 0, dexp - arg0_exp, &dsig0, &dsig1); - dsig0 |= 0x8000000000000000ULL; - } - texp = arg0_exp - dexp + 0x3ffe; - rsig0 = arg0_sig; - rsig1 = 0; - rsig2 = 0; - if (dsig0 <= rsig0) { - shift128Right(rsig0, rsig1, 1, &rsig0, &rsig1); - ++texp; - } - tsig0 = estimateDiv128To64(rsig0, rsig1, dsig0); - mul128By64To192(dsig0, dsig1, tsig0, &msig0, &msig1, &msig2); - sub192(rsig0, rsig1, rsig2, msig0, msig1, msig2, - &rsig0, &rsig1, &rsig2); - while ((int64_t) rsig0 < 0) { - --tsig0; - add192(rsig0, rsig1, rsig2, 0, dsig0, dsig1, - &rsig0, &rsig1, &rsig2); - } - tsig1 = estimateDiv128To64(rsig1, rsig2, dsig0); - /* - * No need to correct any estimation error in tsig1; even with - * such error, it is accurate enough. Now compute the square of - * that approximation. - */ - mul128To256(tsig0, tsig1, tsig0, tsig1, - &t2sig0, &t2sig1, &t2sig2, &t2sig3); - t2 = normalizeRoundAndPackFloatx80(80, false, texp + texp - 0x3ffe, - t2sig0, t2sig1, &env->fp_status); - - /* Compute the lower parts of the polynomial expansion. */ - accum = floatx80_mul(fyl2x_coeff_9, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_8, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_7, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_6, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_5, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_4, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_3, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_2, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_1, accum, &env->fp_status); - accum = floatx80_mul(accum, t2, &env->fp_status); - accum = floatx80_add(fyl2x_coeff_0_low, accum, &env->fp_status); - - /* - * The full polynomial expansion is fyl2x_coeff_0 + accum (where - * accum has much lower magnitude, and so, in particular, carry - * out of the addition is not possible), multiplied by t. (This - * expansion is only accurate to about 70 bits, not 128 bits.) - */ - aexp = extractFloatx80Exp(fyl2x_coeff_0); - asign = extractFloatx80Sign(fyl2x_coeff_0); - shift128RightJamming(extractFloatx80Frac(accum), 0, - aexp - extractFloatx80Exp(accum), - &asig0, &asig1); - bsig0 = extractFloatx80Frac(fyl2x_coeff_0); - bsig1 = 0; - if (asign == extractFloatx80Sign(accum)) { - add128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); - } else { - sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); - } - /* Multiply by t to compute the required result. */ - mul128To256(asig0, asig1, tsig0, tsig1, - &asig0, &asig1, &asig2, &asig3); - aexp += texp - 0x3ffe; - *exp = aexp; - *sig0 = asig0; - *sig1 = asig1; -} - -void helper_fyl2xp1(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - uint64_t arg0_sig = extractFloatx80Frac(ST0); - int32_t arg0_exp = extractFloatx80Exp(ST0); - bool arg0_sign = extractFloatx80Sign(ST0); - uint64_t arg1_sig = extractFloatx80Frac(ST1); - int32_t arg1_exp = extractFloatx80Exp(ST1); - bool arg1_sign = extractFloatx80Sign(ST1); - - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST0, &env->fp_status); - } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_any_nan(ST0)) { - ST1 = ST0; - } else if (floatx80_is_any_nan(ST1)) { - /* Pass this NaN through. */ - } else if (arg0_exp > 0x3ffd || - (arg0_exp == 0x3ffd && arg0_sig > (arg0_sign ? - 0x95f619980c4336f7ULL : - 0xd413cccfe7799211ULL))) { - /* - * Out of range for the instruction (ST0 must have absolute - * value less than 1 - sqrt(2)/2 = 0.292..., according to - * Intel manuals; AMD manuals allow a range from sqrt(2)/2 - 1 - * to sqrt(2) - 1, which we allow here), treat as invalid. - */ - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || - arg1_exp == 0x7fff) { - /* - * One argument is zero, or multiplying by infinity; correct - * result is exact and can be obtained by multiplying the - * arguments. - */ - ST1 = floatx80_mul(ST0, ST1, &env->fp_status); - } else if (arg0_exp < 0x3fb0) { - /* - * Multiplying both arguments and an extra-precision version - * of log2(e) is sufficiently precise. - */ - uint64_t sig0, sig1, sig2; - int32_t exp; - if (arg0_exp == 0) { - normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); - } - if (arg1_exp == 0) { - normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); - } - mul128By64To192(log2_e_sig_high, log2_e_sig_low, arg0_sig, - &sig0, &sig1, &sig2); - exp = arg0_exp + 1; - mul128By64To192(sig0, sig1, arg1_sig, &sig0, &sig1, &sig2); - exp += arg1_exp - 0x3ffe; - /* This result is inexact. */ - sig1 |= 1; - ST1 = normalizeRoundAndPackFloatx80(80, arg0_sign ^ arg1_sign, exp, - sig0, sig1, &env->fp_status); - } else { - int32_t aexp; - uint64_t asig0, asig1, asig2; - FloatRoundMode save_mode = env->fp_status.float_rounding_mode; - signed char save_prec = env->fp_status.floatx80_rounding_precision; - env->fp_status.float_rounding_mode = float_round_nearest_even; - env->fp_status.floatx80_rounding_precision = 80; - - helper_fyl2x_common(env, ST0, &aexp, &asig0, &asig1); - /* - * Multiply by the second argument to compute the required - * result. - */ - if (arg1_exp == 0) { - normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); - } - mul128By64To192(asig0, asig1, arg1_sig, &asig0, &asig1, &asig2); - aexp += arg1_exp - 0x3ffe; - /* This result is inexact. */ - asig1 |= 1; - env->fp_status.float_rounding_mode = save_mode; - ST1 = normalizeRoundAndPackFloatx80(80, arg0_sign ^ arg1_sign, aexp, - asig0, asig1, &env->fp_status); - env->fp_status.floatx80_rounding_precision = save_prec; - } - fpop(env); - merge_exception_flags(env, old_flags); -} - -void helper_fyl2x(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - uint64_t arg0_sig = extractFloatx80Frac(ST0); - int32_t arg0_exp = extractFloatx80Exp(ST0); - bool arg0_sign = extractFloatx80Sign(ST0); - uint64_t arg1_sig = extractFloatx80Frac(ST1); - int32_t arg1_exp = extractFloatx80Exp(ST1); - bool arg1_sign = extractFloatx80Sign(ST1); - - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST0, &env->fp_status); - } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_silence_nan(ST1, &env->fp_status); - } else if (floatx80_invalid_encoding(ST0) || - floatx80_invalid_encoding(ST1)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_any_nan(ST0)) { - ST1 = ST0; - } else if (floatx80_is_any_nan(ST1)) { - /* Pass this NaN through. */ - } else if (arg0_sign && !floatx80_is_zero(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_infinity(ST1)) { - FloatRelation cmp = floatx80_compare(ST0, floatx80_one, - &env->fp_status); - switch (cmp) { - case float_relation_less: - ST1 = floatx80_chs(ST1); - break; - case float_relation_greater: - /* Result is infinity of the same sign as ST1. */ - break; - default: - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - break; - } - } else if (floatx80_is_infinity(ST0)) { - if (floatx80_is_zero(ST1)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else if (arg1_sign) { - ST1 = floatx80_chs(ST0); - } else { - ST1 = ST0; - } - } else if (floatx80_is_zero(ST0)) { - if (floatx80_is_zero(ST1)) { - float_raise(float_flag_invalid, &env->fp_status); - ST1 = floatx80_default_nan(&env->fp_status); - } else { - /* Result is infinity with opposite sign to ST1. */ - float_raise(float_flag_divbyzero, &env->fp_status); - ST1 = make_floatx80(arg1_sign ? 0x7fff : 0xffff, - 0x8000000000000000ULL); - } - } else if (floatx80_is_zero(ST1)) { - if (floatx80_lt(ST0, floatx80_one, &env->fp_status)) { - ST1 = floatx80_chs(ST1); - } - /* Otherwise, ST1 is already the correct result. */ - } else if (floatx80_eq(ST0, floatx80_one, &env->fp_status)) { - if (arg1_sign) { - ST1 = floatx80_chs(floatx80_zero); - } else { - ST1 = floatx80_zero; - } - } else { - int32_t int_exp; - floatx80 arg0_m1; - FloatRoundMode save_mode = env->fp_status.float_rounding_mode; - signed char save_prec = env->fp_status.floatx80_rounding_precision; - env->fp_status.float_rounding_mode = float_round_nearest_even; - env->fp_status.floatx80_rounding_precision = 80; - - if (arg0_exp == 0) { - normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); - } - if (arg1_exp == 0) { - normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); - } - int_exp = arg0_exp - 0x3fff; - if (arg0_sig > 0xb504f333f9de6484ULL) { - ++int_exp; - } - arg0_m1 = floatx80_sub(floatx80_scalbn(ST0, -int_exp, - &env->fp_status), - floatx80_one, &env->fp_status); - if (floatx80_is_zero(arg0_m1)) { - /* Exact power of 2; multiply by ST1. */ - env->fp_status.float_rounding_mode = save_mode; - ST1 = floatx80_mul(int32_to_floatx80(int_exp, &env->fp_status), - ST1, &env->fp_status); - } else { - bool asign = extractFloatx80Sign(arg0_m1); - int32_t aexp; - uint64_t asig0, asig1, asig2; - helper_fyl2x_common(env, arg0_m1, &aexp, &asig0, &asig1); - if (int_exp != 0) { - bool isign = (int_exp < 0); - int32_t iexp; - uint64_t isig; - int shift; - int_exp = isign ? -int_exp : int_exp; - shift = clz32(int_exp) + 32; - isig = int_exp; - isig <<= shift; - iexp = 0x403e - shift; - shift128RightJamming(asig0, asig1, iexp - aexp, - &asig0, &asig1); - if (asign == isign) { - add128(isig, 0, asig0, asig1, &asig0, &asig1); - } else { - sub128(isig, 0, asig0, asig1, &asig0, &asig1); - } - aexp = iexp; - asign = isign; - } - /* - * Multiply by the second argument to compute the required - * result. - */ - if (arg1_exp == 0) { - normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); - } - mul128By64To192(asig0, asig1, arg1_sig, &asig0, &asig1, &asig2); - aexp += arg1_exp - 0x3ffe; - /* This result is inexact. */ - asig1 |= 1; - env->fp_status.float_rounding_mode = save_mode; - ST1 = normalizeRoundAndPackFloatx80(80, asign ^ arg1_sign, aexp, - asig0, asig1, &env->fp_status); - } - - env->fp_status.floatx80_rounding_precision = save_prec; - } - fpop(env); - merge_exception_flags(env, old_flags); -} - -void helper_fsqrt(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - if (floatx80_is_neg(ST0)) { - env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ - env->fpus |= 0x400; - } - ST0 = floatx80_sqrt(ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fsincos(CPUX86State *env) -{ - double fptemp = floatx80_to_double(env, ST0); - - if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { - env->fpus |= 0x400; - } else { - ST0 = double_to_floatx80(env, sin(fptemp)); - fpush(env); - ST0 = double_to_floatx80(env, cos(fptemp)); - env->fpus &= ~0x400; /* C2 <-- 0 */ - /* the above code is for |arg| < 2**63 only */ - } -} - -void helper_frndint(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - ST0 = floatx80_round_to_int(ST0, &env->fp_status); - merge_exception_flags(env, old_flags); -} - -void helper_fscale(CPUX86State *env) -{ - uint8_t old_flags = save_exception_flags(env); - if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - } else if (floatx80_is_any_nan(ST1)) { - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - } - ST0 = ST1; - if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_silence_nan(ST0, &env->fp_status); - } - } else if (floatx80_is_infinity(ST1) && - !floatx80_invalid_encoding(ST0) && - !floatx80_is_any_nan(ST0)) { - if (floatx80_is_neg(ST1)) { - if (floatx80_is_infinity(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - } else { - ST0 = (floatx80_is_neg(ST0) ? - floatx80_chs(floatx80_zero) : - floatx80_zero); - } - } else { - if (floatx80_is_zero(ST0)) { - float_raise(float_flag_invalid, &env->fp_status); - ST0 = floatx80_default_nan(&env->fp_status); - } else { - ST0 = (floatx80_is_neg(ST0) ? - floatx80_chs(floatx80_infinity) : - floatx80_infinity); - } - } - } else { - int n; - signed char save = env->fp_status.floatx80_rounding_precision; - uint8_t save_flags = get_float_exception_flags(&env->fp_status); - set_float_exception_flags(0, &env->fp_status); - n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); - set_float_exception_flags(save_flags, &env->fp_status); - env->fp_status.floatx80_rounding_precision = 80; - ST0 = floatx80_scalbn(ST0, n, &env->fp_status); - env->fp_status.floatx80_rounding_precision = save; - } - merge_exception_flags(env, old_flags); -} - -void helper_fsin(CPUX86State *env) -{ - double fptemp = floatx80_to_double(env, ST0); - - if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { - env->fpus |= 0x400; - } else { - ST0 = double_to_floatx80(env, sin(fptemp)); - env->fpus &= ~0x400; /* C2 <-- 0 */ - /* the above code is for |arg| < 2**53 only */ - } -} - -void helper_fcos(CPUX86State *env) -{ - double fptemp = floatx80_to_double(env, ST0); - - if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { - env->fpus |= 0x400; - } else { - ST0 = double_to_floatx80(env, cos(fptemp)); - env->fpus &= ~0x400; /* C2 <-- 0 */ - /* the above code is for |arg| < 2**63 only */ - } -} - -void helper_fxam_ST0(CPUX86State *env) -{ - CPU_LDoubleU temp; - int expdif; - - temp.d = ST0; - - env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ - if (SIGND(temp)) { - env->fpus |= 0x200; /* C1 <-- 1 */ - } - - if (env->fptags[env->fpstt]) { - env->fpus |= 0x4100; /* Empty */ - return; - } - - expdif = EXPD(temp); - if (expdif == MAXEXPD) { - if (MANTD(temp) == 0x8000000000000000ULL) { - env->fpus |= 0x500; /* Infinity */ - } else if (MANTD(temp) & 0x8000000000000000ULL) { - env->fpus |= 0x100; /* NaN */ - } - } else if (expdif == 0) { - if (MANTD(temp) == 0) { - env->fpus |= 0x4000; /* Zero */ - } else { - env->fpus |= 0x4400; /* Denormal */ - } - } else if (MANTD(temp) & 0x8000000000000000ULL) { - env->fpus |= 0x400; - } -} - -static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) -{ - int fpus, fptag, exp, i; - uint64_t mant; - CPU_LDoubleU tmp; - - fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; - fptag = 0; - for (i = 7; i >= 0; i--) { - fptag <<= 2; - if (env->fptags[i]) { - fptag |= 3; - } else { - tmp.d = env->fpregs[i].d; - exp = EXPD(tmp); - mant = MANTD(tmp); - if (exp == 0 && mant == 0) { - /* zero */ - fptag |= 1; - } else if (exp == 0 || exp == MAXEXPD - || (mant & (1LL << 63)) == 0) { - /* NaNs, infinity, denormal */ - fptag |= 2; - } - } - } - if (data32) { - /* 32 bit */ - cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); - cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); - cpu_stl_data_ra(env, ptr + 12, 0, retaddr); /* fpip */ - cpu_stl_data_ra(env, ptr + 16, 0, retaddr); /* fpcs */ - cpu_stl_data_ra(env, ptr + 20, 0, retaddr); /* fpoo */ - cpu_stl_data_ra(env, ptr + 24, 0, retaddr); /* fpos */ - } else { - /* 16 bit */ - cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); - cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); - cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); - cpu_stw_data_ra(env, ptr + 6, 0, retaddr); - cpu_stw_data_ra(env, ptr + 8, 0, retaddr); - cpu_stw_data_ra(env, ptr + 10, 0, retaddr); - cpu_stw_data_ra(env, ptr + 12, 0, retaddr); - } -} - -void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) -{ - do_fstenv(env, ptr, data32, GETPC()); -} - -static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) -{ - env->fpstt = (fpus >> 11) & 7; - env->fpus = fpus & ~0x3800 & ~FPUS_B; - env->fpus |= env->fpus & FPUS_SE ? FPUS_B : 0; -#if !defined(CONFIG_USER_ONLY) - if (!(env->fpus & FPUS_SE)) { - /* - * Here the processor deasserts FERR#; in response, the chipset deasserts - * IGNNE#. - */ - cpu_clear_ignne(); - } -#endif -} - -static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, - uintptr_t retaddr) -{ - int i, fpus, fptag; - - if (data32) { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); - } else { - cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); - fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); - fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); - } - cpu_set_fpus(env, fpus); - for (i = 0; i < 8; i++) { - env->fptags[i] = ((fptag & 3) == 3); - fptag >>= 2; - } -} - -void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) -{ - do_fldenv(env, ptr, data32, GETPC()); -} - -void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) -{ - floatx80 tmp; - int i; - - do_fstenv(env, ptr, data32, GETPC()); - - ptr += (14 << data32); - for (i = 0; i < 8; i++) { - tmp = ST(i); - helper_fstt(env, tmp, ptr, GETPC()); - ptr += 10; - } - - /* fninit */ - env->fpus = 0; - env->fpstt = 0; - cpu_set_fpuc(env, 0x37f); - env->fptags[0] = 1; - env->fptags[1] = 1; - env->fptags[2] = 1; - env->fptags[3] = 1; - env->fptags[4] = 1; - env->fptags[5] = 1; - env->fptags[6] = 1; - env->fptags[7] = 1; -} - -void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) -{ - floatx80 tmp; - int i; - - do_fldenv(env, ptr, data32, GETPC()); - ptr += (14 << data32); - - for (i = 0; i < 8; i++) { - tmp = helper_fldt(env, ptr, GETPC()); - ST(i) = tmp; - ptr += 10; - } -} - -#if defined(CONFIG_USER_ONLY) -void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) -{ - helper_fsave(env, ptr, data32); -} - -void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) -{ - helper_frstor(env, ptr, data32); -} -#endif - -#define XO(X) offsetof(X86XSaveArea, X) - -static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - int fpus, fptag, i; - target_ulong addr; - - fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; - fptag = 0; - for (i = 0; i < 8; i++) { - fptag |= (env->fptags[i] << i); - } - - cpu_stw_data_ra(env, ptr + XO(legacy.fcw), env->fpuc, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.fsw), fpus, ra); - cpu_stw_data_ra(env, ptr + XO(legacy.ftw), fptag ^ 0xff, ra); - - /* In 32-bit mode this is eip, sel, dp, sel. - In 64-bit mode this is rip, rdp. - But in either case we don't write actual data, just zeros. */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpip), 0, ra); /* eip+sel; rip */ - cpu_stq_data_ra(env, ptr + XO(legacy.fpdp), 0, ra); /* edp+sel; rdp */ - - addr = ptr + XO(legacy.fpregs); - for (i = 0; i < 8; i++) { - floatx80 tmp = ST(i); - helper_fstt(env, tmp, addr, ra); - addr += 16; - } -} - -static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - update_mxcsr_from_sse_status(env); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra); - cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra); -} - -static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - int i, nb_xmm_regs; - target_ulong addr; - - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - - addr = ptr + XO(legacy.xmm_regs); - for (i = 0; i < nb_xmm_regs; i++) { - cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); - cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); - addr += 16; - } -} - -static void do_xsave_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); - int i; - - for (i = 0; i < 4; i++, addr += 16) { - cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); - cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); - } -} - -static void do_xsave_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), - env->bndcs_regs.cfgu, ra); - cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), - env->bndcs_regs.sts, ra); -} - -static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - cpu_stq_data_ra(env, ptr, env->pkru, ra); -} - -void helper_fxsave(CPUX86State *env, target_ulong ptr) -{ - uintptr_t ra = GETPC(); - - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - do_xsave_fpu(env, ptr, ra); - - if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xsave_mxcsr(env, ptr, ra); - /* Fast FXSAVE leaves out the XMM registers */ - if (!(env->efer & MSR_EFER_FFXSR) - || (env->hflags & HF_CPL_MASK) - || !(env->hflags & HF_LMA_MASK)) { - do_xsave_sse(env, ptr, ra); - } - } -} - -static uint64_t get_xinuse(CPUX86State *env) -{ - uint64_t inuse = -1; - - /* For the most part, we don't track XINUSE. We could calculate it - here for all components, but it's probably less work to simply - indicate in use. That said, the state of BNDREGS is important - enough to track in HFLAGS, so we might as well use that here. */ - if ((env->hflags & HF_MPX_IU_MASK) == 0) { - inuse &= ~XSTATE_BNDREGS_MASK; - } - return inuse; -} - -static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, - uint64_t inuse, uint64_t opt, uintptr_t ra) -{ - uint64_t old_bv, new_bv; - - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } - - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - /* Never save anything not enabled by XCR0. */ - rfbm &= env->xcr0; - opt &= rfbm; - - if (opt & XSTATE_FP_MASK) { - do_xsave_fpu(env, ptr, ra); - } - if (rfbm & XSTATE_SSE_MASK) { - /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ - do_xsave_mxcsr(env, ptr, ra); - } - if (opt & XSTATE_SSE_MASK) { - do_xsave_sse(env, ptr, ra); - } - if (opt & XSTATE_BNDREGS_MASK) { - do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); - } - if (opt & XSTATE_BNDCSR_MASK) { - do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); - } - if (opt & XSTATE_PKRU_MASK) { - do_xsave_pkru(env, ptr + XO(pkru_state), ra); - } - - /* Update the XSTATE_BV field. */ - old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); - new_bv = (old_bv & ~rfbm) | (inuse & rfbm); - cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); -} - -void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) -{ - do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); -} - -void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) -{ - uint64_t inuse = get_xinuse(env); - do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); -} - -static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - int i, fpuc, fpus, fptag; - target_ulong addr; - - fpuc = cpu_lduw_data_ra(env, ptr + XO(legacy.fcw), ra); - fpus = cpu_lduw_data_ra(env, ptr + XO(legacy.fsw), ra); - fptag = cpu_lduw_data_ra(env, ptr + XO(legacy.ftw), ra); - cpu_set_fpuc(env, fpuc); - cpu_set_fpus(env, fpus); - fptag ^= 0xff; - for (i = 0; i < 8; i++) { - env->fptags[i] = ((fptag >> i) & 1); - } - - addr = ptr + XO(legacy.fpregs); - for (i = 0; i < 8; i++) { - floatx80 tmp = helper_fldt(env, addr, ra); - ST(i) = tmp; - addr += 16; - } -} - -static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + XO(legacy.mxcsr), ra)); -} - -static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - int i, nb_xmm_regs; - target_ulong addr; - - if (env->hflags & HF_CS64_MASK) { - nb_xmm_regs = 16; - } else { - nb_xmm_regs = 8; - } - - addr = ptr + XO(legacy.xmm_regs); - for (i = 0; i < nb_xmm_regs; i++) { - env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); - env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); - addr += 16; - } -} - -static void do_xrstor_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); - int i; - - for (i = 0; i < 4; i++, addr += 16) { - env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); - env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); - } -} - -static void do_xrstor_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - /* FIXME: Extend highest implemented bit of linear address. */ - env->bndcs_regs.cfgu - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), ra); - env->bndcs_regs.sts - = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), ra); -} - -static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) -{ - env->pkru = cpu_ldq_data_ra(env, ptr, ra); -} - -void helper_fxrstor(CPUX86State *env, target_ulong ptr) -{ - uintptr_t ra = GETPC(); - - /* The operand must be 16 byte aligned */ - if (ptr & 0xf) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - do_xrstor_fpu(env, ptr, ra); - - if (env->cr[4] & CR4_OSFXSR_MASK) { - do_xrstor_mxcsr(env, ptr, ra); - /* Fast FXRSTOR leaves out the XMM registers */ - if (!(env->efer & MSR_EFER_FFXSR) - || (env->hflags & HF_CPL_MASK) - || !(env->hflags & HF_LMA_MASK)) { - do_xrstor_sse(env, ptr, ra); - } - } -} - -#if defined(CONFIG_USER_ONLY) -void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) -{ - helper_fxsave(env, ptr); -} - -void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) -{ - helper_fxrstor(env, ptr); -} -#endif - -void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) -{ - uintptr_t ra = GETPC(); - uint64_t xstate_bv, xcomp_bv, reserve0; - - rfbm &= env->xcr0; - - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, ra); - } - - /* The operand must be 64 byte aligned. */ - if (ptr & 63) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - xstate_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); - - if ((int64_t)xstate_bv < 0) { - /* FIXME: Compact form. */ - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - /* Standard form. */ - - /* The XSTATE_BV field must not set bits not present in XCR0. */ - if (xstate_bv & ~env->xcr0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - /* The XCOMP_BV field must be zero. Note that, as of the April 2016 - revision, the description of the XSAVE Header (Vol 1, Sec 13.4.2) - describes only XCOMP_BV, but the description of the standard form - of XRSTOR (Vol 1, Sec 13.8.1) checks bytes 23:8 for zero, which - includes the next 64-bit field. */ - xcomp_bv = cpu_ldq_data_ra(env, ptr + XO(header.xcomp_bv), ra); - reserve0 = cpu_ldq_data_ra(env, ptr + XO(header.reserve0), ra); - if (xcomp_bv || reserve0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } - - if (rfbm & XSTATE_FP_MASK) { - if (xstate_bv & XSTATE_FP_MASK) { - do_xrstor_fpu(env, ptr, ra); - } else { - helper_fninit(env); - memset(env->fpregs, 0, sizeof(env->fpregs)); - } - } - if (rfbm & XSTATE_SSE_MASK) { - /* Note that the standard form of XRSTOR loads MXCSR from memory - whether or not the XSTATE_BV bit is set. */ - do_xrstor_mxcsr(env, ptr, ra); - if (xstate_bv & XSTATE_SSE_MASK) { - do_xrstor_sse(env, ptr, ra); - } else { - /* ??? When AVX is implemented, we may have to be more - selective in the clearing. */ - memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); - } - } - if (rfbm & XSTATE_BNDREGS_MASK) { - if (xstate_bv & XSTATE_BNDREGS_MASK) { - do_xrstor_bndregs(env, ptr + XO(bndreg_state), ra); - env->hflags |= HF_MPX_IU_MASK; - } else { - memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); - env->hflags &= ~HF_MPX_IU_MASK; - } - } - if (rfbm & XSTATE_BNDCSR_MASK) { - if (xstate_bv & XSTATE_BNDCSR_MASK) { - do_xrstor_bndcsr(env, ptr + XO(bndcsr_state), ra); - } else { - memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); - } - cpu_sync_bndcs_hflags(env); - } - if (rfbm & XSTATE_PKRU_MASK) { - uint64_t old_pkru = env->pkru; - if (xstate_bv & XSTATE_PKRU_MASK) { - do_xrstor_pkru(env, ptr + XO(pkru_state), ra); - } else { - env->pkru = 0; - } - if (env->pkru != old_pkru) { - CPUState *cs = env_cpu(env); - tlb_flush(cs); - } - } -} - -#undef XO - -uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) -{ - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, GETPC()); - } - - switch (ecx) { - case 0: - return env->xcr0; - case 1: - if (env->features[FEAT_XSAVE] & CPUID_XSAVE_XGETBV1) { - return env->xcr0 & get_xinuse(env); - } - break; - } - raise_exception_ra(env, EXCP0D_GPF, GETPC()); -} - -void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) -{ - uint32_t dummy, ena_lo, ena_hi; - uint64_t ena; - - /* The OS must have enabled XSAVE. */ - if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { - raise_exception_ra(env, EXCP06_ILLOP, GETPC()); - } - - /* Only XCR0 is defined at present; the FPU may not be disabled. */ - if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) { - goto do_gpf; - } - - /* Disallow enabling unimplemented features. */ - cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); - ena = ((uint64_t)ena_hi << 32) | ena_lo; - if (mask & ~ena) { - goto do_gpf; - } - - /* Disallow enabling only half of MPX. */ - if ((mask ^ (mask * (XSTATE_BNDCSR_MASK / XSTATE_BNDREGS_MASK))) - & XSTATE_BNDCSR_MASK) { - goto do_gpf; - } - - env->xcr0 = mask; - cpu_sync_bndcs_hflags(env); - return; - - do_gpf: - raise_exception_ra(env, EXCP0D_GPF, GETPC()); -} - -/* MMX/SSE */ -/* XXX: optimize by storing fptt and fptags in the static cpu state */ - -#define SSE_DAZ 0x0040 -#define SSE_RC_MASK 0x6000 -#define SSE_RC_NEAR 0x0000 -#define SSE_RC_DOWN 0x2000 -#define SSE_RC_UP 0x4000 -#define SSE_RC_CHOP 0x6000 -#define SSE_FZ 0x8000 - -void update_mxcsr_status(CPUX86State *env) -{ - uint32_t mxcsr = env->mxcsr; - int rnd_type; - - /* set rounding mode */ - switch (mxcsr & SSE_RC_MASK) { - default: - case SSE_RC_NEAR: - rnd_type = float_round_nearest_even; - break; - case SSE_RC_DOWN: - rnd_type = float_round_down; - break; - case SSE_RC_UP: - rnd_type = float_round_up; - break; - case SSE_RC_CHOP: - rnd_type = float_round_to_zero; - break; - } - set_float_rounding_mode(rnd_type, &env->sse_status); - - /* Set exception flags. */ - set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) | - (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) | - (mxcsr & FPUS_OE ? float_flag_overflow : 0) | - (mxcsr & FPUS_UE ? float_flag_underflow : 0) | - (mxcsr & FPUS_PE ? float_flag_inexact : 0), - &env->sse_status); - - /* set denormals are zero */ - set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status); - - /* set flush to zero */ - set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->sse_status); -} - -void update_mxcsr_from_sse_status(CPUX86State *env) -{ - if (tcg_enabled()) { - uint8_t flags = get_float_exception_flags(&env->sse_status); - /* - * The MXCSR denormal flag has opposite semantics to - * float_flag_input_denormal (the softfloat code sets that flag - * only when flushing input denormals to zero, but SSE sets it - * only when not flushing them to zero), so is not converted - * here. - */ - env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) | - (flags & float_flag_divbyzero ? FPUS_ZE : 0) | - (flags & float_flag_overflow ? FPUS_OE : 0) | - (flags & float_flag_underflow ? FPUS_UE : 0) | - (flags & float_flag_inexact ? FPUS_PE : 0) | - (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE : - 0)); - } -} - -void helper_update_mxcsr(CPUX86State *env) -{ - update_mxcsr_from_sse_status(env); -} - -void helper_ldmxcsr(CPUX86State *env, uint32_t val) -{ - cpu_set_mxcsr(env, val); -} - -void helper_enter_mmx(CPUX86State *env) -{ - env->fpstt = 0; - *(uint32_t *)(env->fptags) = 0; - *(uint32_t *)(env->fptags + 4) = 0; -} - -void helper_emms(CPUX86State *env) -{ - /* set to empty state */ - *(uint32_t *)(env->fptags) = 0x01010101; - *(uint32_t *)(env->fptags + 4) = 0x01010101; -} - -/* XXX: suppress */ -void helper_movq(CPUX86State *env, void *d, void *s) -{ - *(uint64_t *)d = *(uint64_t *)s; -} - -#define SHIFT 0 -#include "ops_sse.h" - -#define SHIFT 1 -#include "ops_sse.h" diff --git a/target/i386/int_helper.c b/target/i386/int_helper.c deleted file mode 100644 index 4f89436b53..0000000000 --- a/target/i386/int_helper.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * x86 integer helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/exec-all.h" -#include "qemu/host-utils.h" -#include "exec/helper-proto.h" -#include "qapi/error.h" -#include "qemu/guest-random.h" - -//#define DEBUG_MULDIV - -/* modulo 9 table */ -static const uint8_t rclb_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 0, 1, 2, 3, 4, 5, - 6, 7, 8, 0, 1, 2, 3, 4, -}; - -/* modulo 17 table */ -static const uint8_t rclw_table[32] = { - 0, 1, 2, 3, 4, 5, 6, 7, - 8, 9, 10, 11, 12, 13, 14, 15, - 16, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, -}; - -/* division, flags are undefined */ - -void helper_divb_AL(CPUX86State *env, target_ulong t0) -{ - unsigned int num, den, q, r; - - num = (env->regs[R_EAX] & 0xffff); - den = (t0 & 0xff); - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - if (q > 0xff) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q &= 0xff; - r = (num % den) & 0xff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; -} - -void helper_idivb_AL(CPUX86State *env, target_ulong t0) -{ - int num, den, q, r; - - num = (int16_t)env->regs[R_EAX]; - den = (int8_t)t0; - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - if (q != (int8_t)q) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q &= 0xff; - r = (num % den) & 0xff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; -} - -void helper_divw_AX(CPUX86State *env, target_ulong t0) -{ - unsigned int num, den, q, r; - - num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); - den = (t0 & 0xffff); - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - if (q > 0xffff) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q &= 0xffff; - r = (num % den) & 0xffff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; - env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; -} - -void helper_idivw_AX(CPUX86State *env, target_ulong t0) -{ - int num, den, q, r; - - num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); - den = (int16_t)t0; - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - if (q != (int16_t)q) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q &= 0xffff; - r = (num % den) & 0xffff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; - env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; -} - -void helper_divl_EAX(CPUX86State *env, target_ulong t0) -{ - unsigned int den, r; - uint64_t num, q; - - num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); - den = t0; - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - r = (num % den); - if (q > 0xffffffff) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - env->regs[R_EAX] = (uint32_t)q; - env->regs[R_EDX] = (uint32_t)r; -} - -void helper_idivl_EAX(CPUX86State *env, target_ulong t0) -{ - int den, r; - int64_t num, q; - - num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); - den = t0; - if (den == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - q = (num / den); - r = (num % den); - if (q != (int32_t)q) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - env->regs[R_EAX] = (uint32_t)q; - env->regs[R_EDX] = (uint32_t)r; -} - -/* bcd */ - -/* XXX: exception */ -void helper_aam(CPUX86State *env, int base) -{ - int al, ah; - - al = env->regs[R_EAX] & 0xff; - ah = al / base; - al = al % base; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); - CC_DST = al; -} - -void helper_aad(CPUX86State *env, int base) -{ - int al, ah; - - al = env->regs[R_EAX] & 0xff; - ah = (env->regs[R_EAX] >> 8) & 0xff; - al = ((ah * base) + al) & 0xff; - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; - CC_DST = al; -} - -void helper_aaa(CPUX86State *env) -{ - int icarry; - int al, ah, af; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - af = eflags & CC_A; - al = env->regs[R_EAX] & 0xff; - ah = (env->regs[R_EAX] >> 8) & 0xff; - - icarry = (al > 0xf9); - if (((al & 0x0f) > 9) || af) { - al = (al + 6) & 0x0f; - ah = (ah + 1 + icarry) & 0xff; - eflags |= CC_C | CC_A; - } else { - eflags &= ~(CC_C | CC_A); - al &= 0x0f; - } - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); - CC_SRC = eflags; -} - -void helper_aas(CPUX86State *env) -{ - int icarry; - int al, ah, af; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - af = eflags & CC_A; - al = env->regs[R_EAX] & 0xff; - ah = (env->regs[R_EAX] >> 8) & 0xff; - - icarry = (al < 6); - if (((al & 0x0f) > 9) || af) { - al = (al - 6) & 0x0f; - ah = (ah - 1 - icarry) & 0xff; - eflags |= CC_C | CC_A; - } else { - eflags &= ~(CC_C | CC_A); - al &= 0x0f; - } - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); - CC_SRC = eflags; -} - -void helper_daa(CPUX86State *env) -{ - int old_al, al, af, cf; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - cf = eflags & CC_C; - af = eflags & CC_A; - old_al = al = env->regs[R_EAX] & 0xff; - - eflags = 0; - if (((al & 0x0f) > 9) || af) { - al = (al + 6) & 0xff; - eflags |= CC_A; - } - if ((old_al > 0x99) || cf) { - al = (al + 0x60) & 0xff; - eflags |= CC_C; - } - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; - /* well, speed is not an issue here, so we compute the flags by hand */ - eflags |= (al == 0) << 6; /* zf */ - eflags |= parity_table[al]; /* pf */ - eflags |= (al & 0x80); /* sf */ - CC_SRC = eflags; -} - -void helper_das(CPUX86State *env) -{ - int al, al1, af, cf; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - cf = eflags & CC_C; - af = eflags & CC_A; - al = env->regs[R_EAX] & 0xff; - - eflags = 0; - al1 = al; - if (((al & 0x0f) > 9) || af) { - eflags |= CC_A; - if (al < 6 || cf) { - eflags |= CC_C; - } - al = (al - 6) & 0xff; - } - if ((al1 > 0x99) || cf) { - al = (al - 0x60) & 0xff; - eflags |= CC_C; - } - env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; - /* well, speed is not an issue here, so we compute the flags by hand */ - eflags |= (al == 0) << 6; /* zf */ - eflags |= parity_table[al]; /* pf */ - eflags |= (al & 0x80); /* sf */ - CC_SRC = eflags; -} - -#ifdef TARGET_X86_64 -static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) -{ - *plow += a; - /* carry test */ - if (*plow < a) { - (*phigh)++; - } - *phigh += b; -} - -static void neg128(uint64_t *plow, uint64_t *phigh) -{ - *plow = ~*plow; - *phigh = ~*phigh; - add128(plow, phigh, 1, 0); -} - -/* return TRUE if overflow */ -static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) -{ - uint64_t q, r, a1, a0; - int i, qb, ab; - - a0 = *plow; - a1 = *phigh; - if (a1 == 0) { - q = a0 / b; - r = a0 % b; - *plow = q; - *phigh = r; - } else { - if (a1 >= b) { - return 1; - } - /* XXX: use a better algorithm */ - for (i = 0; i < 64; i++) { - ab = a1 >> 63; - a1 = (a1 << 1) | (a0 >> 63); - if (ab || a1 >= b) { - a1 -= b; - qb = 1; - } else { - qb = 0; - } - a0 = (a0 << 1) | qb; - } -#if defined(DEBUG_MULDIV) - printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 - ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n", - *phigh, *plow, b, a0, a1); -#endif - *plow = a0; - *phigh = a1; - } - return 0; -} - -/* return TRUE if overflow */ -static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) -{ - int sa, sb; - - sa = ((int64_t)*phigh < 0); - if (sa) { - neg128(plow, phigh); - } - sb = (b < 0); - if (sb) { - b = -b; - } - if (div64(plow, phigh, b) != 0) { - return 1; - } - if (sa ^ sb) { - if (*plow > (1ULL << 63)) { - return 1; - } - *plow = -*plow; - } else { - if (*plow >= (1ULL << 63)) { - return 1; - } - } - if (sa) { - *phigh = -*phigh; - } - return 0; -} - -void helper_divq_EAX(CPUX86State *env, target_ulong t0) -{ - uint64_t r0, r1; - - if (t0 == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - r0 = env->regs[R_EAX]; - r1 = env->regs[R_EDX]; - if (div64(&r0, &r1, t0)) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - env->regs[R_EAX] = r0; - env->regs[R_EDX] = r1; -} - -void helper_idivq_EAX(CPUX86State *env, target_ulong t0) -{ - uint64_t r0, r1; - - if (t0 == 0) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - r0 = env->regs[R_EAX]; - r1 = env->regs[R_EDX]; - if (idiv64(&r0, &r1, t0)) { - raise_exception_ra(env, EXCP00_DIVZ, GETPC()); - } - env->regs[R_EAX] = r0; - env->regs[R_EDX] = r1; -} -#endif - -#if TARGET_LONG_BITS == 32 -# define ctztl ctz32 -# define clztl clz32 -#else -# define ctztl ctz64 -# define clztl clz64 -#endif - -target_ulong helper_pdep(target_ulong src, target_ulong mask) -{ - target_ulong dest = 0; - int i, o; - - for (i = 0; mask != 0; i++) { - o = ctztl(mask); - mask &= mask - 1; - dest |= ((src >> i) & 1) << o; - } - return dest; -} - -target_ulong helper_pext(target_ulong src, target_ulong mask) -{ - target_ulong dest = 0; - int i, o; - - for (o = 0; mask != 0; o++) { - i = ctztl(mask); - mask &= mask - 1; - dest |= ((src >> i) & 1) << o; - } - return dest; -} - -#define SHIFT 0 -#include "shift_helper_template.h" -#undef SHIFT - -#define SHIFT 1 -#include "shift_helper_template.h" -#undef SHIFT - -#define SHIFT 2 -#include "shift_helper_template.h" -#undef SHIFT - -#ifdef TARGET_X86_64 -#define SHIFT 3 -#include "shift_helper_template.h" -#undef SHIFT -#endif - -/* Test that BIT is enabled in CR4. If not, raise an illegal opcode - exception. This reduces the requirements for rare CR4 bits being - mapped into HFLAGS. */ -void helper_cr4_testbit(CPUX86State *env, uint32_t bit) -{ - if (unlikely((env->cr[4] & bit) == 0)) { - raise_exception_ra(env, EXCP06_ILLOP, GETPC()); - } -} - -target_ulong HELPER(rdrand)(CPUX86State *env) -{ - Error *err = NULL; - target_ulong ret; - - if (qemu_guest_getrandom(&ret, sizeof(ret), &err) < 0) { - qemu_log_mask(LOG_UNIMP, "rdrand: Crypto failure: %s", - error_get_pretty(err)); - error_free(err); - /* Failure clears CF and all other flags, and returns 0. */ - env->cc_src = 0; - return 0; - } - - /* Success sets CF and clears all others. */ - env->cc_src = CC_C; - return ret; -} diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c deleted file mode 100644 index 21ca3e3e88..0000000000 --- a/target/i386/mem_helper.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * x86 memory access helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "qemu/int128.h" -#include "qemu/atomic128.h" -#include "tcg/tcg.h" - -void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - uint64_t oldv, cmpv, newv; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); - newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); - - oldv = cpu_ldq_data_ra(env, a0, ra); - newv = (cmpv == oldv ? newv : oldv); - /* always do the store */ - cpu_stq_data_ra(env, a0, newv, ra); - - if (oldv == cmpv) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = (uint32_t)oldv; - env->regs[R_EDX] = (uint32_t)(oldv >> 32); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -} - -void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) -{ -#ifdef CONFIG_ATOMIC64 - uint64_t oldv, cmpv, newv; - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); - newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); - -#ifdef CONFIG_USER_ONLY - { - uint64_t *haddr = g2h(a0); - cmpv = cpu_to_le64(cmpv); - newv = cpu_to_le64(newv); - oldv = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); - oldv = le64_to_cpu(oldv); - } -#else - { - uintptr_t ra = GETPC(); - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); - oldv = helper_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); - } -#endif - - if (oldv == cmpv) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = (uint32_t)oldv; - env->regs[R_EDX] = (uint32_t)(oldv >> 32); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -#else - cpu_loop_exit_atomic(env_cpu(env), GETPC()); -#endif /* CONFIG_ATOMIC64 */ -} - -#ifdef TARGET_X86_64 -void helper_cmpxchg16b_unlocked(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - Int128 oldv, cmpv, newv; - uint64_t o0, o1; - int eflags; - bool success; - - if ((a0 & 0xf) != 0) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - eflags = cpu_cc_compute_all(env, CC_OP); - - cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); - newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); - - o0 = cpu_ldq_data_ra(env, a0 + 0, ra); - o1 = cpu_ldq_data_ra(env, a0 + 8, ra); - - oldv = int128_make128(o0, o1); - success = int128_eq(oldv, cmpv); - if (!success) { - newv = oldv; - } - - cpu_stq_data_ra(env, a0 + 0, int128_getlo(newv), ra); - cpu_stq_data_ra(env, a0 + 8, int128_gethi(newv), ra); - - if (success) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = int128_getlo(oldv); - env->regs[R_EDX] = int128_gethi(oldv); - eflags &= ~CC_Z; - } - CC_SRC = eflags; -} - -void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) -{ - uintptr_t ra = GETPC(); - - if ((a0 & 0xf) != 0) { - raise_exception_ra(env, EXCP0D_GPF, ra); - } else if (HAVE_CMPXCHG128) { - int eflags = cpu_cc_compute_all(env, CC_OP); - - Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); - Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); - - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - Int128 oldv = helper_atomic_cmpxchgo_le_mmu(env, a0, cmpv, - newv, oi, ra); - - if (int128_eq(oldv, cmpv)) { - eflags |= CC_Z; - } else { - env->regs[R_EAX] = int128_getlo(oldv); - env->regs[R_EDX] = int128_gethi(oldv); - eflags &= ~CC_Z; - } - CC_SRC = eflags; - } else { - cpu_loop_exit_atomic(env_cpu(env), ra); - } -} -#endif - -void helper_boundw(CPUX86State *env, target_ulong a0, int v) -{ - int low, high; - - low = cpu_ldsw_data_ra(env, a0, GETPC()); - high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); - v = (int16_t)v; - if (v < low || v > high) { - if (env->hflags & HF_MPX_EN_MASK) { - env->bndcs_regs.sts = 0; - } - raise_exception_ra(env, EXCP05_BOUND, GETPC()); - } -} - -void helper_boundl(CPUX86State *env, target_ulong a0, int v) -{ - int low, high; - - low = cpu_ldl_data_ra(env, a0, GETPC()); - high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); - if (v < low || v > high) { - if (env->hflags & HF_MPX_EN_MASK) { - env->bndcs_regs.sts = 0; - } - raise_exception_ra(env, EXCP05_BOUND, GETPC()); - } -} diff --git a/target/i386/meson.build b/target/i386/meson.build index 284d52ab81..750471c9f3 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -5,19 +5,6 @@ i386_ss.add(files( 'helper.c', 'xsave_helper.c', )) -i386_ss.add(when: 'CONFIG_TCG', if_true: files( - 'bpt_helper.c', - 'cc_helper.c', - 'excp_helper.c', - 'fpu_helper.c', - 'int_helper.c', - 'mem_helper.c', - 'misc_helper.c', - 'mpx_helper.c', - 'seg_helper.c', - 'smm_helper.c', - 'svm_helper.c', - 'translate.c'), if_false: files('tcg-stub.c')) i386_ss.add(when: 'CONFIG_SEV', if_true: files('sev.c'), if_false: files('sev-stub.c')) i386_softmmu_ss = ss.source_set() @@ -32,6 +19,7 @@ subdir('kvm') subdir('hax') subdir('whpx') subdir('hvf') +subdir('tcg') target_arch += {'i386': i386_ss} target_softmmu_arch += {'i386': i386_softmmu_ss} diff --git a/target/i386/misc_helper.c b/target/i386/misc_helper.c deleted file mode 100644 index ae259d9145..0000000000 --- a/target/i386/misc_helper.c +++ /dev/null @@ -1,647 +0,0 @@ -/* - * x86 misc helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/address-spaces.h" - -void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "outb: port=0x%04x, data=%02x\n", port, data); -#else - address_space_stb(&address_space_io, port, data, - cpu_get_mem_attrs(env), NULL); -#endif -} - -target_ulong helper_inb(CPUX86State *env, uint32_t port) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "inb: port=0x%04x\n", port); - return 0; -#else - return address_space_ldub(&address_space_io, port, - cpu_get_mem_attrs(env), NULL); -#endif -} - -void helper_outw(CPUX86State *env, uint32_t port, uint32_t data) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "outw: port=0x%04x, data=%04x\n", port, data); -#else - address_space_stw(&address_space_io, port, data, - cpu_get_mem_attrs(env), NULL); -#endif -} - -target_ulong helper_inw(CPUX86State *env, uint32_t port) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "inw: port=0x%04x\n", port); - return 0; -#else - return address_space_lduw(&address_space_io, port, - cpu_get_mem_attrs(env), NULL); -#endif -} - -void helper_outl(CPUX86State *env, uint32_t port, uint32_t data) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "outl: port=0x%04x, data=%08x\n", port, data); -#else - address_space_stl(&address_space_io, port, data, - cpu_get_mem_attrs(env), NULL); -#endif -} - -target_ulong helper_inl(CPUX86State *env, uint32_t port) -{ -#ifdef CONFIG_USER_ONLY - fprintf(stderr, "inl: port=0x%04x\n", port); - return 0; -#else - return address_space_ldl(&address_space_io, port, - cpu_get_mem_attrs(env), NULL); -#endif -} - -void helper_into(CPUX86State *env, int next_eip_addend) -{ - int eflags; - - eflags = cpu_cc_compute_all(env, CC_OP); - if (eflags & CC_O) { - raise_interrupt(env, EXCP04_INTO, 1, 0, next_eip_addend); - } -} - -void helper_cpuid(CPUX86State *env) -{ - uint32_t eax, ebx, ecx, edx; - - cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0, GETPC()); - - cpu_x86_cpuid(env, (uint32_t)env->regs[R_EAX], (uint32_t)env->regs[R_ECX], - &eax, &ebx, &ecx, &edx); - env->regs[R_EAX] = eax; - env->regs[R_EBX] = ebx; - env->regs[R_ECX] = ecx; - env->regs[R_EDX] = edx; -} - -#if defined(CONFIG_USER_ONLY) -target_ulong helper_read_crN(CPUX86State *env, int reg) -{ - return 0; -} - -void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) -{ -} -#else -target_ulong helper_read_crN(CPUX86State *env, int reg) -{ - target_ulong val; - - cpu_svm_check_intercept_param(env, SVM_EXIT_READ_CR0 + reg, 0, GETPC()); - switch (reg) { - default: - val = env->cr[reg]; - break; - case 8: - if (!(env->hflags2 & HF2_VINTR_MASK)) { - val = cpu_get_apic_tpr(env_archcpu(env)->apic_state); - } else { - val = env->v_tpr; - } - break; - } - return val; -} - -void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_WRITE_CR0 + reg, 0, GETPC()); - switch (reg) { - case 0: - cpu_x86_update_cr0(env, t0); - break; - case 3: - cpu_x86_update_cr3(env, t0); - break; - case 4: - cpu_x86_update_cr4(env, t0); - break; - case 8: - if (!(env->hflags2 & HF2_VINTR_MASK)) { - qemu_mutex_lock_iothread(); - cpu_set_apic_tpr(env_archcpu(env)->apic_state, t0); - qemu_mutex_unlock_iothread(); - } - env->v_tpr = t0 & 0x0f; - break; - default: - env->cr[reg] = t0; - break; - } -} -#endif - -void helper_lmsw(CPUX86State *env, target_ulong t0) -{ - /* only 4 lower bits of CR0 are modified. PE cannot be set to zero - if already set to one. */ - t0 = (env->cr[0] & ~0xe) | (t0 & 0xf); - helper_write_crN(env, 0, t0); -} - -void helper_invlpg(CPUX86State *env, target_ulong addr) -{ - X86CPU *cpu = env_archcpu(env); - - cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPG, 0, GETPC()); - tlb_flush_page(CPU(cpu), addr); -} - -void helper_rdtsc(CPUX86State *env) -{ - uint64_t val; - - if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); - - val = cpu_get_tsc(env) + env->tsc_offset; - env->regs[R_EAX] = (uint32_t)(val); - env->regs[R_EDX] = (uint32_t)(val >> 32); -} - -void helper_rdtscp(CPUX86State *env) -{ - helper_rdtsc(env); - env->regs[R_ECX] = (uint32_t)(env->tsc_aux); -} - -void helper_rdpmc(CPUX86State *env) -{ - if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0, GETPC()); - - /* currently unimplemented */ - qemu_log_mask(LOG_UNIMP, "x86: unimplemented rdpmc\n"); - raise_exception_err(env, EXCP06_ILLOP, 0); -} - -#if defined(CONFIG_USER_ONLY) -void helper_wrmsr(CPUX86State *env) -{ -} - -void helper_rdmsr(CPUX86State *env) -{ -} -#else -void helper_wrmsr(CPUX86State *env) -{ - uint64_t val; - - cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC()); - - val = ((uint32_t)env->regs[R_EAX]) | - ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); - - switch ((uint32_t)env->regs[R_ECX]) { - case MSR_IA32_SYSENTER_CS: - env->sysenter_cs = val & 0xffff; - break; - case MSR_IA32_SYSENTER_ESP: - env->sysenter_esp = val; - break; - case MSR_IA32_SYSENTER_EIP: - env->sysenter_eip = val; - break; - case MSR_IA32_APICBASE: - cpu_set_apic_base(env_archcpu(env)->apic_state, val); - break; - case MSR_EFER: - { - uint64_t update_mask; - - update_mask = 0; - if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_SYSCALL) { - update_mask |= MSR_EFER_SCE; - } - if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { - update_mask |= MSR_EFER_LME; - } - if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { - update_mask |= MSR_EFER_FFXSR; - } - if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_NX) { - update_mask |= MSR_EFER_NXE; - } - if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { - update_mask |= MSR_EFER_SVME; - } - if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { - update_mask |= MSR_EFER_FFXSR; - } - cpu_load_efer(env, (env->efer & ~update_mask) | - (val & update_mask)); - } - break; - case MSR_STAR: - env->star = val; - break; - case MSR_PAT: - env->pat = val; - break; - case MSR_VM_HSAVE_PA: - env->vm_hsave = val; - break; -#ifdef TARGET_X86_64 - case MSR_LSTAR: - env->lstar = val; - break; - case MSR_CSTAR: - env->cstar = val; - break; - case MSR_FMASK: - env->fmask = val; - break; - case MSR_FSBASE: - env->segs[R_FS].base = val; - break; - case MSR_GSBASE: - env->segs[R_GS].base = val; - break; - case MSR_KERNELGSBASE: - env->kernelgsbase = val; - break; -#endif - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - env->mtrr_var[((uint32_t)env->regs[R_ECX] - - MSR_MTRRphysBase(0)) / 2].base = val; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - env->mtrr_var[((uint32_t)env->regs[R_ECX] - - MSR_MTRRphysMask(0)) / 2].mask = val; - break; - case MSR_MTRRfix64K_00000: - env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - - MSR_MTRRfix64K_00000] = val; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - - MSR_MTRRfix16K_80000 + 1] = val; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - - MSR_MTRRfix4K_C0000 + 3] = val; - break; - case MSR_MTRRdefType: - env->mtrr_deftype = val; - break; - case MSR_MCG_STATUS: - env->mcg_status = val; - break; - case MSR_MCG_CTL: - if ((env->mcg_cap & MCG_CTL_P) - && (val == 0 || val == ~(uint64_t)0)) { - env->mcg_ctl = val; - } - break; - case MSR_TSC_AUX: - env->tsc_aux = val; - break; - case MSR_IA32_MISC_ENABLE: - env->msr_ia32_misc_enable = val; - break; - case MSR_IA32_BNDCFGS: - /* FIXME: #GP if reserved bits are set. */ - /* FIXME: Extend highest implemented bit of linear address. */ - env->msr_bndcfgs = val; - cpu_sync_bndcs_hflags(env); - break; - default: - if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL - && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + - (4 * env->mcg_cap & 0xff)) { - uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; - if ((offset & 0x3) != 0 - || (val == 0 || val == ~(uint64_t)0)) { - env->mce_banks[offset] = val; - } - break; - } - /* XXX: exception? */ - break; - } -} - -void helper_rdmsr(CPUX86State *env) -{ - X86CPU *x86_cpu = env_archcpu(env); - uint64_t val; - - cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); - - switch ((uint32_t)env->regs[R_ECX]) { - case MSR_IA32_SYSENTER_CS: - val = env->sysenter_cs; - break; - case MSR_IA32_SYSENTER_ESP: - val = env->sysenter_esp; - break; - case MSR_IA32_SYSENTER_EIP: - val = env->sysenter_eip; - break; - case MSR_IA32_APICBASE: - val = cpu_get_apic_base(env_archcpu(env)->apic_state); - break; - case MSR_EFER: - val = env->efer; - break; - case MSR_STAR: - val = env->star; - break; - case MSR_PAT: - val = env->pat; - break; - case MSR_VM_HSAVE_PA: - val = env->vm_hsave; - break; - case MSR_IA32_PERF_STATUS: - /* tsc_increment_by_tick */ - val = 1000ULL; - /* CPU multiplier */ - val |= (((uint64_t)4ULL) << 40); - break; -#ifdef TARGET_X86_64 - case MSR_LSTAR: - val = env->lstar; - break; - case MSR_CSTAR: - val = env->cstar; - break; - case MSR_FMASK: - val = env->fmask; - break; - case MSR_FSBASE: - val = env->segs[R_FS].base; - break; - case MSR_GSBASE: - val = env->segs[R_GS].base; - break; - case MSR_KERNELGSBASE: - val = env->kernelgsbase; - break; - case MSR_TSC_AUX: - val = env->tsc_aux; - break; -#endif - case MSR_SMI_COUNT: - val = env->msr_smi_count; - break; - case MSR_MTRRphysBase(0): - case MSR_MTRRphysBase(1): - case MSR_MTRRphysBase(2): - case MSR_MTRRphysBase(3): - case MSR_MTRRphysBase(4): - case MSR_MTRRphysBase(5): - case MSR_MTRRphysBase(6): - case MSR_MTRRphysBase(7): - val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - - MSR_MTRRphysBase(0)) / 2].base; - break; - case MSR_MTRRphysMask(0): - case MSR_MTRRphysMask(1): - case MSR_MTRRphysMask(2): - case MSR_MTRRphysMask(3): - case MSR_MTRRphysMask(4): - case MSR_MTRRphysMask(5): - case MSR_MTRRphysMask(6): - case MSR_MTRRphysMask(7): - val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - - MSR_MTRRphysMask(0)) / 2].mask; - break; - case MSR_MTRRfix64K_00000: - val = env->mtrr_fixed[0]; - break; - case MSR_MTRRfix16K_80000: - case MSR_MTRRfix16K_A0000: - val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - - MSR_MTRRfix16K_80000 + 1]; - break; - case MSR_MTRRfix4K_C0000: - case MSR_MTRRfix4K_C8000: - case MSR_MTRRfix4K_D0000: - case MSR_MTRRfix4K_D8000: - case MSR_MTRRfix4K_E0000: - case MSR_MTRRfix4K_E8000: - case MSR_MTRRfix4K_F0000: - case MSR_MTRRfix4K_F8000: - val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - - MSR_MTRRfix4K_C0000 + 3]; - break; - case MSR_MTRRdefType: - val = env->mtrr_deftype; - break; - case MSR_MTRRcap: - if (env->features[FEAT_1_EDX] & CPUID_MTRR) { - val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | - MSR_MTRRcap_WC_SUPPORTED; - } else { - /* XXX: exception? */ - val = 0; - } - break; - case MSR_MCG_CAP: - val = env->mcg_cap; - break; - case MSR_MCG_CTL: - if (env->mcg_cap & MCG_CTL_P) { - val = env->mcg_ctl; - } else { - val = 0; - } - break; - case MSR_MCG_STATUS: - val = env->mcg_status; - break; - case MSR_IA32_MISC_ENABLE: - val = env->msr_ia32_misc_enable; - break; - case MSR_IA32_BNDCFGS: - val = env->msr_bndcfgs; - break; - case MSR_IA32_UCODE_REV: - val = x86_cpu->ucode_rev; - break; - default: - if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL - && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + - (4 * env->mcg_cap & 0xff)) { - uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; - val = env->mce_banks[offset]; - break; - } - /* XXX: exception? */ - val = 0; - break; - } - env->regs[R_EAX] = (uint32_t)(val); - env->regs[R_EDX] = (uint32_t)(val >> 32); -} -#endif - -static void do_pause(X86CPU *cpu) -{ - CPUState *cs = CPU(cpu); - - /* Just let another CPU run. */ - cs->exception_index = EXCP_INTERRUPT; - cpu_loop_exit(cs); -} - -static void do_hlt(X86CPU *cpu) -{ - CPUState *cs = CPU(cpu); - CPUX86State *env = &cpu->env; - - env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); -} - -void helper_hlt(CPUX86State *env, int next_eip_addend) -{ - X86CPU *cpu = env_archcpu(env); - - cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); - env->eip += next_eip_addend; - - do_hlt(cpu); -} - -void helper_monitor(CPUX86State *env, target_ulong ptr) -{ - if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - /* XXX: store address? */ - cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0, GETPC()); -} - -void helper_mwait(CPUX86State *env, int next_eip_addend) -{ - CPUState *cs = env_cpu(env); - X86CPU *cpu = env_archcpu(env); - - if ((uint32_t)env->regs[R_ECX] != 0) { - raise_exception_ra(env, EXCP0D_GPF, GETPC()); - } - cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0, GETPC()); - env->eip += next_eip_addend; - - /* XXX: not complete but not completely erroneous */ - if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { - do_pause(cpu); - } else { - do_hlt(cpu); - } -} - -void helper_pause(CPUX86State *env, int next_eip_addend) -{ - X86CPU *cpu = env_archcpu(env); - - cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); - env->eip += next_eip_addend; - - do_pause(cpu); -} - -void helper_debug(CPUX86State *env) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = EXCP_DEBUG; - cpu_loop_exit(cs); -} - -uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) -{ - if ((env->cr[4] & CR4_PKE_MASK) == 0) { - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); - } - if (ecx != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - - return env->pkru; -} - -void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val) -{ - CPUState *cs = env_cpu(env); - - if ((env->cr[4] & CR4_PKE_MASK) == 0) { - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); - } - if (ecx != 0 || (val & 0xFFFFFFFF00000000ull)) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - - env->pkru = val; - tlb_flush(cs); -} diff --git a/target/i386/mpx_helper.c b/target/i386/mpx_helper.c deleted file mode 100644 index fd966174b4..0000000000 --- a/target/i386/mpx_helper.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * x86 MPX helpers - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" -#include "exec/exec-all.h" - - -void helper_bndck(CPUX86State *env, uint32_t fail) -{ - if (unlikely(fail)) { - env->bndcs_regs.sts = 1; - raise_exception_ra(env, EXCP05_BOUND, GETPC()); - } -} - -static uint64_t lookup_bte64(CPUX86State *env, uint64_t base, uintptr_t ra) -{ - uint64_t bndcsr, bde, bt; - - if ((env->hflags & HF_CPL_MASK) == 3) { - bndcsr = env->bndcs_regs.cfgu; - } else { - bndcsr = env->msr_bndcfgs; - } - - bde = (extract64(base, 20, 28) << 3) + (extract64(bndcsr, 20, 44) << 12); - bt = cpu_ldq_data_ra(env, bde, ra); - if ((bt & 1) == 0) { - env->bndcs_regs.sts = bde | 2; - raise_exception_ra(env, EXCP05_BOUND, ra); - } - - return (extract64(base, 3, 17) << 5) + (bt & ~7); -} - -static uint32_t lookup_bte32(CPUX86State *env, uint32_t base, uintptr_t ra) -{ - uint32_t bndcsr, bde, bt; - - if ((env->hflags & HF_CPL_MASK) == 3) { - bndcsr = env->bndcs_regs.cfgu; - } else { - bndcsr = env->msr_bndcfgs; - } - - bde = (extract32(base, 12, 20) << 2) + (bndcsr & TARGET_PAGE_MASK); - bt = cpu_ldl_data_ra(env, bde, ra); - if ((bt & 1) == 0) { - env->bndcs_regs.sts = bde | 2; - raise_exception_ra(env, EXCP05_BOUND, ra); - } - - return (extract32(base, 2, 10) << 4) + (bt & ~3); -} - -uint64_t helper_bndldx64(CPUX86State *env, target_ulong base, target_ulong ptr) -{ - uintptr_t ra = GETPC(); - uint64_t bte, lb, ub, pt; - - bte = lookup_bte64(env, base, ra); - lb = cpu_ldq_data_ra(env, bte, ra); - ub = cpu_ldq_data_ra(env, bte + 8, ra); - pt = cpu_ldq_data_ra(env, bte + 16, ra); - - if (pt != ptr) { - lb = ub = 0; - } - env->mmx_t0.MMX_Q(0) = ub; - return lb; -} - -uint64_t helper_bndldx32(CPUX86State *env, target_ulong base, target_ulong ptr) -{ - uintptr_t ra = GETPC(); - uint32_t bte, lb, ub, pt; - - bte = lookup_bte32(env, base, ra); - lb = cpu_ldl_data_ra(env, bte, ra); - ub = cpu_ldl_data_ra(env, bte + 4, ra); - pt = cpu_ldl_data_ra(env, bte + 8, ra); - - if (pt != ptr) { - lb = ub = 0; - } - return ((uint64_t)ub << 32) | lb; -} - -void helper_bndstx64(CPUX86State *env, target_ulong base, target_ulong ptr, - uint64_t lb, uint64_t ub) -{ - uintptr_t ra = GETPC(); - uint64_t bte; - - bte = lookup_bte64(env, base, ra); - cpu_stq_data_ra(env, bte, lb, ra); - cpu_stq_data_ra(env, bte + 8, ub, ra); - cpu_stq_data_ra(env, bte + 16, ptr, ra); -} - -void helper_bndstx32(CPUX86State *env, target_ulong base, target_ulong ptr, - uint64_t lb, uint64_t ub) -{ - uintptr_t ra = GETPC(); - uint32_t bte; - - bte = lookup_bte32(env, base, ra); - cpu_stl_data_ra(env, bte, lb, ra); - cpu_stl_data_ra(env, bte + 4, ub, ra); - cpu_stl_data_ra(env, bte + 8, ptr, ra); -} - -void helper_bnd_jmp(CPUX86State *env) -{ - if (!(env->hflags2 & HF2_MPX_PR_MASK)) { - memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); - env->hflags &= ~HF_MPX_IU_MASK; - } -} diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c deleted file mode 100644 index e6ffa1f018..0000000000 --- a/target/i386/seg_helper.c +++ /dev/null @@ -1,2674 +0,0 @@ -/* - * x86 segmentation related helpers: - * TSS, interrupts, system calls, jumps and call/task gates, descriptors - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "qemu/log.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" -#include "exec/log.h" - -//#define DEBUG_PCALL - -#ifdef DEBUG_PCALL -# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__) -# define LOG_PCALL_STATE(cpu) \ - log_cpu_state_mask(CPU_LOG_PCALL, (cpu), CPU_DUMP_CCOP) -#else -# define LOG_PCALL(...) do { } while (0) -# define LOG_PCALL_STATE(cpu) do { } while (0) -#endif - -/* - * TODO: Convert callers to compute cpu_mmu_index_kernel once - * and use *_mmuidx_ra directly. - */ -#define cpu_ldub_kernel_ra(e, p, r) \ - cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define cpu_lduw_kernel_ra(e, p, r) \ - cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define cpu_ldl_kernel_ra(e, p, r) \ - cpu_ldl_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define cpu_ldq_kernel_ra(e, p, r) \ - cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) - -#define cpu_stb_kernel_ra(e, p, v, r) \ - cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define cpu_stw_kernel_ra(e, p, v, r) \ - cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define cpu_stl_kernel_ra(e, p, v, r) \ - cpu_stl_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define cpu_stq_kernel_ra(e, p, v, r) \ - cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) - -#define cpu_ldub_kernel(e, p) cpu_ldub_kernel_ra(e, p, 0) -#define cpu_lduw_kernel(e, p) cpu_lduw_kernel_ra(e, p, 0) -#define cpu_ldl_kernel(e, p) cpu_ldl_kernel_ra(e, p, 0) -#define cpu_ldq_kernel(e, p) cpu_ldq_kernel_ra(e, p, 0) - -#define cpu_stb_kernel(e, p, v) cpu_stb_kernel_ra(e, p, v, 0) -#define cpu_stw_kernel(e, p, v) cpu_stw_kernel_ra(e, p, v, 0) -#define cpu_stl_kernel(e, p, v) cpu_stl_kernel_ra(e, p, v, 0) -#define cpu_stq_kernel(e, p, v) cpu_stq_kernel_ra(e, p, v, 0) - -/* return non zero if error */ -static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, - uint32_t *e2_ptr, int selector, - uintptr_t retaddr) -{ - SegmentCache *dt; - int index; - target_ulong ptr; - - if (selector & 0x4) { - dt = &env->ldt; - } else { - dt = &env->gdt; - } - index = selector & ~7; - if ((index + 7) > dt->limit) { - return -1; - } - ptr = dt->base + index; - *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr); - *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - return 0; -} - -static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, - uint32_t *e2_ptr, int selector) -{ - return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0); -} - -static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) -{ - unsigned int limit; - - limit = (e1 & 0xffff) | (e2 & 0x000f0000); - if (e2 & DESC_G_MASK) { - limit = (limit << 12) | 0xfff; - } - return limit; -} - -static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2) -{ - return (e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000); -} - -static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, - uint32_t e2) -{ - sc->base = get_seg_base(e1, e2); - sc->limit = get_seg_limit(e1, e2); - sc->flags = e2; -} - -/* init the segment cache in vm86 mode. */ -static inline void load_seg_vm(CPUX86State *env, int seg, int selector) -{ - selector &= 0xffff; - - cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK | (3 << DESC_DPL_SHIFT)); -} - -static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, - uint32_t *esp_ptr, int dpl, - uintptr_t retaddr) -{ - X86CPU *cpu = env_archcpu(env); - int type, index, shift; - -#if 0 - { - int i; - printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit); - for (i = 0; i < env->tr.limit; i++) { - printf("%02x ", env->tr.base[i]); - if ((i & 7) == 7) { - printf("\n"); - } - } - printf("\n"); - } -#endif - - if (!(env->tr.flags & DESC_P_MASK)) { - cpu_abort(CPU(cpu), "invalid tss"); - } - type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - if ((type & 7) != 1) { - cpu_abort(CPU(cpu), "invalid tss type"); - } - shift = type >> 3; - index = (dpl * 4 + 2) << shift; - if (index + (4 << shift) - 1 > env->tr.limit) { - raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr); - } - if (shift == 0) { - *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr); - *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr); - } else { - *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr); - *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr); - } -} - -static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl, - uintptr_t retaddr) -{ - uint32_t e1, e2; - int rpl, dpl; - - if ((selector & 0xfffc) != 0) { - if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - if (!(e2 & DESC_S_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (seg_reg == R_CS) { - if (!(e2 & DESC_CS_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - if (dpl != rpl) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - } else if (seg_reg == R_SS) { - /* SS must be writable data */ - if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - if (dpl != cpl || dpl != rpl) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - } else { - /* not readable code */ - if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - /* if data or non conforming code, checks the rights */ - if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { - if (dpl < cpl || dpl < rpl) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - } - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr); - } - cpu_x86_load_seg_cache(env, seg_reg, selector, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - } else { - if (seg_reg == R_SS || seg_reg == R_CS) { - raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); - } - } -} - -#define SWITCH_TSS_JMP 0 -#define SWITCH_TSS_IRET 1 -#define SWITCH_TSS_CALL 2 - -/* XXX: restore CPU state in registers (PowerPC case) */ -static void switch_tss_ra(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip, uintptr_t retaddr) -{ - int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; - target_ulong tss_base; - uint32_t new_regs[8], new_segs[6]; - uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; - uint32_t old_eflags, eflags_mask; - SegmentCache *dt; - int index; - target_ulong ptr; - - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, - source); - - /* if task gate, we read the TSS segment and we load it */ - if (type == 5) { - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); - } - tss_selector = e1 >> 16; - if (tss_selector & 4) { - raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); - } - if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); - } - if (e2 & DESC_S_MASK) { - raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); - } - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - if ((type & 7) != 1) { - raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); - } - } - - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); - } - - if (type & 8) { - tss_limit_max = 103; - } else { - tss_limit_max = 43; - } - tss_limit = get_seg_limit(e1, e2); - tss_base = get_seg_base(e1, e2); - if ((tss_selector & 4) != 0 || - tss_limit < tss_limit_max) { - raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); - } - old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - if (old_type & 8) { - old_tss_limit_max = 103; - } else { - old_tss_limit_max = 43; - } - - /* read all the registers from the new TSS */ - if (type & 8) { - /* 32 bit */ - new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); - new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); - new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); - for (i = 0; i < 8; i++) { - new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), - retaddr); - } - for (i = 0; i < 6; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), - retaddr); - } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); - new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); - } else { - /* 16 bit */ - new_cr3 = 0; - new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); - new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); - for (i = 0; i < 8; i++) { - new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), - retaddr) | 0xffff0000; - } - for (i = 0; i < 4; i++) { - new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4), - retaddr); - } - new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); - new_segs[R_FS] = 0; - new_segs[R_GS] = 0; - new_trap = 0; - } - /* XXX: avoid a compiler warning, see - http://support.amd.com/us/Processor_TechDocs/24593.pdf - chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ - (void)new_trap; - - /* NOTE: we must avoid memory exceptions during the task switch, - so we make dummy accesses before */ - /* XXX: it can still fail in some cases, so a bigger hack is - necessary to valid the TLB after having done the accesses */ - - v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); - v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); - cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); - cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); - - /* clear busy bit (it is restartable) */ - if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (env->tr.selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 &= ~DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); - } - old_eflags = cpu_compute_eflags(env); - if (source == SWITCH_TSS_IRET) { - old_eflags &= ~NT_MASK; - } - - /* save the current state in the old TSS */ - if (type & 8) { - /* 32 bit */ - cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); - cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); - for (i = 0; i < 6; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), - env->segs[i].selector, retaddr); - } - } else { - /* 16 bit */ - cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); - cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); - for (i = 0; i < 4; i++) { - cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4), - env->segs[i].selector, retaddr); - } - } - - /* now if an exception occurs, it will occurs in the next task - context */ - - if (source == SWITCH_TSS_CALL) { - cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); - new_eflags |= NT_MASK; - } - - /* set busy bit */ - if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { - target_ulong ptr; - uint32_t e2; - - ptr = env->gdt.base + (tss_selector & ~7); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); - } - - /* set the new CPU state */ - /* from this point, any exception which occurs can give problems */ - env->cr[0] |= CR0_TS_MASK; - env->hflags |= HF_TS_MASK; - env->tr.selector = tss_selector; - env->tr.base = tss_base; - env->tr.limit = tss_limit; - env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK; - - if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) { - cpu_x86_update_cr3(env, new_cr3); - } - - /* load all registers without an exception, then reload them with - possible exception */ - env->eip = new_eip; - eflags_mask = TF_MASK | AC_MASK | ID_MASK | - IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK; - if (!(type & 8)) { - eflags_mask &= 0xffff; - } - cpu_load_eflags(env, new_eflags, eflags_mask); - /* XXX: what to do in 16 bit case? */ - env->regs[R_EAX] = new_regs[0]; - env->regs[R_ECX] = new_regs[1]; - env->regs[R_EDX] = new_regs[2]; - env->regs[R_EBX] = new_regs[3]; - env->regs[R_ESP] = new_regs[4]; - env->regs[R_EBP] = new_regs[5]; - env->regs[R_ESI] = new_regs[6]; - env->regs[R_EDI] = new_regs[7]; - if (new_eflags & VM_MASK) { - for (i = 0; i < 6; i++) { - load_seg_vm(env, i, new_segs[i]); - } - } else { - /* first just selectors as the rest may trigger exceptions */ - for (i = 0; i < 6; i++) { - cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0); - } - } - - env->ldt.selector = new_ldt & ~4; - env->ldt.base = 0; - env->ldt.limit = 0; - env->ldt.flags = 0; - - /* load the LDT */ - if (new_ldt & 4) { - raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); - } - - if ((new_ldt & 0xfffc) != 0) { - dt = &env->gdt; - index = new_ldt & ~7; - if ((index + 7) > dt->limit) { - raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); - } - ptr = dt->base + index; - e1 = cpu_ldl_kernel_ra(env, ptr, retaddr); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); - if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); - } - load_seg_cache_raw_dt(&env->ldt, e1, e2); - } - - /* load the segments */ - if (!(new_eflags & VM_MASK)) { - int cpl = new_segs[R_CS] & 3; - tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr); - tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr); - tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr); - tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr); - tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr); - tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr); - } - - /* check that env->eip is in the CS segment limits */ - if (new_eip > env->segs[R_CS].limit) { - /* XXX: different exception if CALL? */ - raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); - } - -#ifndef CONFIG_USER_ONLY - /* reset local breakpoints */ - if (env->dr[7] & DR7_LOCAL_BP_MASK) { - cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); - } -#endif -} - -static void switch_tss(CPUX86State *env, int tss_selector, - uint32_t e1, uint32_t e2, int source, - uint32_t next_eip) -{ - switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); -} - -static inline unsigned int get_sp_mask(unsigned int e2) -{ -#ifdef TARGET_X86_64 - if (e2 & DESC_L_MASK) { - return 0; - } else -#endif - if (e2 & DESC_B_MASK) { - return 0xffffffff; - } else { - return 0xffff; - } -} - -static int exception_has_error_code(int intno) -{ - switch (intno) { - case 8: - case 10: - case 11: - case 12: - case 13: - case 14: - case 17: - return 1; - } - return 0; -} - -#ifdef TARGET_X86_64 -#define SET_ESP(val, sp_mask) \ - do { \ - if ((sp_mask) == 0xffff) { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ - ((val) & 0xffff); \ - } else if ((sp_mask) == 0xffffffffLL) { \ - env->regs[R_ESP] = (uint32_t)(val); \ - } else { \ - env->regs[R_ESP] = (val); \ - } \ - } while (0) -#else -#define SET_ESP(val, sp_mask) \ - do { \ - env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ - ((val) & (sp_mask)); \ - } while (0) -#endif - -/* in 64-bit machines, this can overflow. So this segment addition macro - * can be used to trim the value to 32-bit whenever needed */ -#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) - -/* XXX: add a is_user flag to have proper security support */ -#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 2; \ - cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ - } - -#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - sp -= 4; \ - cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ - } - -#define POPW_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ - sp += 2; \ - } - -#define POPL_RA(ssp, sp, sp_mask, val, ra) \ - { \ - val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ - sp += 4; \ - } - -#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) -#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) -#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) -#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) - -/* protected mode interrupt */ -static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, - int error_code, unsigned int next_eip, - int is_hw) -{ - SegmentCache *dt; - target_ulong ptr, ssp; - int type, dpl, selector, ss_dpl, cpl; - int has_error_code, new_stack, shift; - uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; - uint32_t old_eip, sp_mask; - int vm86 = env->eflags & VM_MASK; - - has_error_code = 0; - if (!is_int && !is_hw) { - has_error_code = exception_has_error_code(intno); - } - if (is_int) { - old_eip = next_eip; - } else { - old_eip = env->eip; - } - - dt = &env->idt; - if (intno * 8 + 7 > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); - } - ptr = dt->base + intno * 8; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); - /* check gate type */ - type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; - switch (type) { - case 5: /* task gate */ - /* must do that check here to return the correct error code */ - if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); - } - switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); - if (has_error_code) { - int type; - uint32_t mask; - - /* push the error code */ - type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; - shift = type >> 3; - if (env->segs[R_SS].flags & DESC_B_MASK) { - mask = 0xffffffff; - } else { - mask = 0xffff; - } - esp = (env->regs[R_ESP] - (2 << shift)) & mask; - ssp = env->segs[R_SS].base + esp; - if (shift) { - cpu_stl_kernel(env, ssp, error_code); - } else { - cpu_stw_kernel(env, ssp, error_code); - } - SET_ESP(esp, mask); - } - return; - case 6: /* 286 interrupt gate */ - case 7: /* 286 trap gate */ - case 14: /* 386 interrupt gate */ - case 15: /* 386 trap gate */ - break; - default: - raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); - break; - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - /* check privilege if software int */ - if (is_int && dpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); - } - /* check valid bit */ - if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); - } - selector = e1 >> 16; - offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); - if ((selector & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); - } - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); - } - if (e2 & DESC_C_MASK) { - dpl = cpl; - } - if (dpl < cpl) { - /* to inner privilege */ - get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); - if ((ss & 0xfffc) == 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - if ((ss & 3) != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; - if (ss_dpl != dpl) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - if (!(ss_e2 & DESC_S_MASK) || - (ss_e2 & DESC_CS_MASK) || - !(ss_e2 & DESC_W_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); - } - new_stack = 1; - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); - } else { - /* to same privilege */ - if (vm86) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - new_stack = 0; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - esp = env->regs[R_ESP]; - } - - shift = type >> 3; - -#if 0 - /* XXX: check that enough room is available */ - push_size = 6 + (new_stack << 2) + (has_error_code << 1); - if (vm86) { - push_size += 8; - } - push_size <<= shift; -#endif - if (shift == 1) { - if (new_stack) { - if (vm86) { - PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); - } - PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); - } - PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env)); - PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHL(ssp, esp, sp_mask, old_eip); - if (has_error_code) { - PUSHL(ssp, esp, sp_mask, error_code); - } - } else { - if (new_stack) { - if (vm86) { - PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); - PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); - } - PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); - PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); - } - PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); - PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); - PUSHW(ssp, esp, sp_mask, old_eip); - if (has_error_code) { - PUSHW(ssp, esp, sp_mask, error_code); - } - } - - /* interrupt gate clear IF mask */ - if ((type & 1) == 0) { - env->eflags &= ~IF_MASK; - } - env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); - - if (new_stack) { - if (vm86) { - cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0); - cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0); - cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0); - cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); - } - ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); - } - SET_ESP(esp, sp_mask); - - selector = (selector & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_CS, selector, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - env->eip = offset; -} - -#ifdef TARGET_X86_64 - -#define PUSHQ_RA(sp, val, ra) \ - { \ - sp -= 8; \ - cpu_stq_kernel_ra(env, sp, (val), ra); \ - } - -#define POPQ_RA(sp, val, ra) \ - { \ - val = cpu_ldq_kernel_ra(env, sp, ra); \ - sp += 8; \ - } - -#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) -#define POPQ(sp, val) POPQ_RA(sp, val, 0) - -static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) -{ - X86CPU *cpu = env_archcpu(env); - int index; - -#if 0 - printf("TR: base=" TARGET_FMT_lx " limit=%x\n", - env->tr.base, env->tr.limit); -#endif - - if (!(env->tr.flags & DESC_P_MASK)) { - cpu_abort(CPU(cpu), "invalid tss"); - } - index = 8 * level + 4; - if ((index + 7) > env->tr.limit) { - raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); - } - return cpu_ldq_kernel(env, env->tr.base + index); -} - -/* 64 bit interrupt */ -static void do_interrupt64(CPUX86State *env, int intno, int is_int, - int error_code, target_ulong next_eip, int is_hw) -{ - SegmentCache *dt; - target_ulong ptr; - int type, dpl, selector, cpl, ist; - int has_error_code, new_stack; - uint32_t e1, e2, e3, ss; - target_ulong old_eip, esp, offset; - - has_error_code = 0; - if (!is_int && !is_hw) { - has_error_code = exception_has_error_code(intno); - } - if (is_int) { - old_eip = next_eip; - } else { - old_eip = env->eip; - } - - dt = &env->idt; - if (intno * 16 + 15 > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); - } - ptr = dt->base + intno * 16; - e1 = cpu_ldl_kernel(env, ptr); - e2 = cpu_ldl_kernel(env, ptr + 4); - e3 = cpu_ldl_kernel(env, ptr + 8); - /* check gate type */ - type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; - switch (type) { - case 14: /* 386 interrupt gate */ - case 15: /* 386 trap gate */ - break; - default: - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); - break; - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - /* check privilege if software int */ - if (is_int && dpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); - } - /* check valid bit */ - if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2); - } - selector = e1 >> 16; - offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff); - ist = e2 & 7; - if ((selector & 0xfffc) == 0) { - raise_exception_err(env, EXCP0D_GPF, 0); - } - - if (load_segment(env, &e1, &e2, selector) != 0) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (dpl > cpl) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); - } - if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - if (e2 & DESC_C_MASK) { - dpl = cpl; - } - if (dpl < cpl || ist != 0) { - /* to inner privilege */ - new_stack = 1; - esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); - ss = 0; - } else { - /* to same privilege */ - if (env->eflags & VM_MASK) { - raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); - } - new_stack = 0; - esp = env->regs[R_ESP]; - } - esp &= ~0xfLL; /* align stack */ - - PUSHQ(esp, env->segs[R_SS].selector); - PUSHQ(esp, env->regs[R_ESP]); - PUSHQ(esp, cpu_compute_eflags(env)); - PUSHQ(esp, env->segs[R_CS].selector); - PUSHQ(esp, old_eip); - if (has_error_code) { - PUSHQ(esp, error_code); - } - - /* interrupt gate clear IF mask */ - if ((type & 1) == 0) { - env->eflags &= ~IF_MASK; - } - env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); - - if (new_stack) { - ss = 0 | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT); - } - env->regs[R_ESP] = esp; - - selector = (selector & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_CS, selector, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - env->eip = offset; -} -#endif - -#ifdef TARGET_X86_64 -#if defined(CONFIG_USER_ONLY) -void helper_syscall(CPUX86State *env, int next_eip_addend) -{ - CPUState *cs = env_cpu(env); - - cs->exception_index = EXCP_SYSCALL; - env->exception_is_int = 0; - env->exception_next_eip = env->eip + next_eip_addend; - cpu_loop_exit(cs); -} -#else -void helper_syscall(CPUX86State *env, int next_eip_addend) -{ - int selector; - - if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); - } - selector = (env->star >> 32) & 0xffff; - if (env->hflags & HF_LMA_MASK) { - int code64; - - env->regs[R_ECX] = env->eip + next_eip_addend; - env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK; - - code64 = env->hflags & HF_CS64_MASK; - - env->eflags &= ~(env->fmask | RF_MASK); - cpu_load_eflags(env, env->eflags, 0); - cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | - DESC_L_MASK); - cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_W_MASK | DESC_A_MASK); - if (code64) { - env->eip = env->lstar; - } else { - env->eip = env->cstar; - } - } else { - env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend); - - env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); - cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_W_MASK | DESC_A_MASK); - env->eip = (uint32_t)env->star; - } -} -#endif -#endif - -#ifdef TARGET_X86_64 -void helper_sysret(CPUX86State *env, int dflag) -{ - int cpl, selector; - - if (!(env->efer & MSR_EFER_SCE)) { - raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); - } - cpl = env->hflags & HF_CPL_MASK; - if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - selector = (env->star >> 48) & 0xffff; - if (env->hflags & HF_LMA_MASK) { - cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK - | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | - NT_MASK); - if (dflag == 2) { - cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | - DESC_L_MASK); - env->eip = env->regs[R_ECX]; - } else { - cpu_x86_load_seg_cache(env, R_CS, selector | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - env->eip = (uint32_t)env->regs[R_ECX]; - } - cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); - } else { - env->eflags |= IF_MASK; - cpu_x86_load_seg_cache(env, R_CS, selector | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - env->eip = (uint32_t)env->regs[R_ECX]; - cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); - } -} -#endif - -/* real mode interrupt */ -static void do_interrupt_real(CPUX86State *env, int intno, int is_int, - int error_code, unsigned int next_eip) -{ - SegmentCache *dt; - target_ulong ptr, ssp; - int selector; - uint32_t offset, esp; - uint32_t old_cs, old_eip; - - /* real mode (simpler!) */ - dt = &env->idt; - if (intno * 4 + 3 > dt->limit) { - raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); - } - ptr = dt->base + intno * 4; - offset = cpu_lduw_kernel(env, ptr); - selector = cpu_lduw_kernel(env, ptr + 2); - esp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; - if (is_int) { - old_eip = next_eip; - } else { - old_eip = env->eip; - } - old_cs = env->segs[R_CS].selector; - /* XXX: use SS segment size? */ - PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env)); - PUSHW(ssp, esp, 0xffff, old_cs); - PUSHW(ssp, esp, 0xffff, old_eip); - - /* update processor state */ - env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff); - env->eip = offset; - env->segs[R_CS].selector = selector; - env->segs[R_CS].base = (selector << 4); - env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK); -} - -#if defined(CONFIG_USER_ONLY) -/* fake user mode interrupt. is_int is TRUE if coming from the int - * instruction. next_eip is the env->eip value AFTER the interrupt - * instruction. It is only relevant if is_int is TRUE or if intno - * is EXCP_SYSCALL. - */ -static void do_interrupt_user(CPUX86State *env, int intno, int is_int, - int error_code, target_ulong next_eip) -{ - if (is_int) { - SegmentCache *dt; - target_ulong ptr; - int dpl, cpl, shift; - uint32_t e2; - - dt = &env->idt; - if (env->hflags & HF_LMA_MASK) { - shift = 4; - } else { - shift = 3; - } - ptr = dt->base + (intno << shift); - e2 = cpu_ldl_kernel(env, ptr + 4); - - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - /* check privilege if software int */ - if (dpl < cpl) { - raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2); - } - } - - /* Since we emulate only user space, we cannot do more than - exiting the emulation with the suitable exception and error - code. So update EIP for INT 0x80 and EXCP_SYSCALL. */ - if (is_int || intno == EXCP_SYSCALL) { - env->eip = next_eip; - } -} - -#else - -static void handle_even_inj(CPUX86State *env, int intno, int is_int, - int error_code, int is_hw, int rm) -{ - CPUState *cs = env_cpu(env); - uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.event_inj)); - - if (!(event_inj & SVM_EVTINJ_VALID)) { - int type; - - if (is_int) { - type = SVM_EVTINJ_TYPE_SOFT; - } else { - type = SVM_EVTINJ_TYPE_EXEPT; - } - event_inj = intno | type | SVM_EVTINJ_VALID; - if (!rm && exception_has_error_code(intno)) { - event_inj |= SVM_EVTINJ_VALID_ERR; - x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.event_inj_err), - error_code); - } - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.event_inj), - event_inj); - } -} -#endif - -/* - * Begin execution of an interruption. is_int is TRUE if coming from - * the int instruction. next_eip is the env->eip value AFTER the interrupt - * instruction. It is only relevant if is_int is TRUE. - */ -static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, - int error_code, target_ulong next_eip, int is_hw) -{ - CPUX86State *env = &cpu->env; - - if (qemu_loglevel_mask(CPU_LOG_INT)) { - if ((env->cr[0] & CR0_PE_MASK)) { - static int count; - - qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx - " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx, - count, intno, error_code, is_int, - env->hflags & HF_CPL_MASK, - env->segs[R_CS].selector, env->eip, - (int)env->segs[R_CS].base + env->eip, - env->segs[R_SS].selector, env->regs[R_ESP]); - if (intno == 0x0e) { - qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]); - } else { - qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]); - } - qemu_log("\n"); - log_cpu_state(CPU(cpu), CPU_DUMP_CCOP); -#if 0 - { - int i; - target_ulong ptr; - - qemu_log(" code="); - ptr = env->segs[R_CS].base + env->eip; - for (i = 0; i < 16; i++) { - qemu_log(" %02x", ldub(ptr + i)); - } - qemu_log("\n"); - } -#endif - count++; - } - } - if (env->cr[0] & CR0_PE_MASK) { -#if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_GUEST_MASK) { - handle_even_inj(env, intno, is_int, error_code, is_hw, 0); - } -#endif -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw); - } else -#endif - { - do_interrupt_protected(env, intno, is_int, error_code, next_eip, - is_hw); - } - } else { -#if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_GUEST_MASK) { - handle_even_inj(env, intno, is_int, error_code, is_hw, 1); - } -#endif - do_interrupt_real(env, intno, is_int, error_code, next_eip); - } - -#if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_GUEST_MASK) { - CPUState *cs = CPU(cpu); - uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.event_inj)); - - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.event_inj), - event_inj & ~SVM_EVTINJ_VALID); - } -#endif -} - -void x86_cpu_do_interrupt(CPUState *cs) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - -#if defined(CONFIG_USER_ONLY) - /* if user mode only, we simulate a fake exception - which will be handled outside the cpu execution - loop */ - do_interrupt_user(env, cs->exception_index, - env->exception_is_int, - env->error_code, - env->exception_next_eip); - /* successfully delivered */ - env->old_exception = -1; -#else - if (cs->exception_index >= EXCP_VMEXIT) { - assert(env->old_exception == -1); - do_vmexit(env, cs->exception_index - EXCP_VMEXIT, env->error_code); - } else { - do_interrupt_all(cpu, cs->exception_index, - env->exception_is_int, - env->error_code, - env->exception_next_eip, 0); - /* successfully delivered */ - env->old_exception = -1; - } -#endif -} - -void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw) -{ - do_interrupt_all(env_archcpu(env), intno, 0, 0, 0, is_hw); -} - -bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - X86CPU *cpu = X86_CPU(cs); - CPUX86State *env = &cpu->env; - int intno; - - interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request); - if (!interrupt_request) { - return false; - } - - /* Don't process multiple interrupt requests in a single call. - * This is required to make icount-driven execution deterministic. - */ - switch (interrupt_request) { -#if !defined(CONFIG_USER_ONLY) - case CPU_INTERRUPT_POLL: - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; - apic_poll_irq(cpu->apic_state); - break; -#endif - case CPU_INTERRUPT_SIPI: - do_cpu_sipi(cpu); - break; - case CPU_INTERRUPT_SMI: - cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(cpu); - break; - case CPU_INTERRUPT_NMI: - cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; - env->hflags2 |= HF2_NMI_MASK; - do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); - break; - case CPU_INTERRUPT_MCE: - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; - do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); - break; - case CPU_INTERRUPT_HARD: - cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); - cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | - CPU_INTERRUPT_VIRQ); - intno = cpu_get_pic_interrupt(env); - qemu_log_mask(CPU_LOG_TB_IN_ASM, - "Servicing hardware INT=0x%02x\n", intno); - do_interrupt_x86_hardirq(env, intno, 1); - break; -#if !defined(CONFIG_USER_ONLY) - case CPU_INTERRUPT_VIRQ: - /* FIXME: this should respect TPR */ - cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); - intno = x86_ldl_phys(cs, env->vm_vmcb - + offsetof(struct vmcb, control.int_vector)); - qemu_log_mask(CPU_LOG_TB_IN_ASM, - "Servicing virtual hardware INT=0x%02x\n", intno); - do_interrupt_x86_hardirq(env, intno, 1); - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; - break; -#endif - } - - /* Ensure that no TB jump will be modified as the program flow was changed. */ - return true; -} - -void helper_lldt(CPUX86State *env, int selector) -{ - SegmentCache *dt; - uint32_t e1, e2; - int index, entry_limit; - target_ulong ptr; - - selector &= 0xffff; - if ((selector & 0xfffc) == 0) { - /* XXX: NULL selector case: invalid LDT */ - env->ldt.base = 0; - env->ldt.limit = 0; - } else { - if (selector & 0x4) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - dt = &env->gdt; - index = selector & ~7; -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - entry_limit = 15; - } else -#endif - { - entry_limit = 7; - } - if ((index + entry_limit) > dt->limit) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - ptr = dt->base + index; - e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); - if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); - } -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - uint32_t e3; - - e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); - load_seg_cache_raw_dt(&env->ldt, e1, e2); - env->ldt.base |= (target_ulong)e3 << 32; - } else -#endif - { - load_seg_cache_raw_dt(&env->ldt, e1, e2); - } - } - env->ldt.selector = selector; -} - -void helper_ltr(CPUX86State *env, int selector) -{ - SegmentCache *dt; - uint32_t e1, e2; - int index, type, entry_limit; - target_ulong ptr; - - selector &= 0xffff; - if ((selector & 0xfffc) == 0) { - /* NULL selector case: invalid TR */ - env->tr.base = 0; - env->tr.limit = 0; - env->tr.flags = 0; - } else { - if (selector & 0x4) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - dt = &env->gdt; - index = selector & ~7; -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - entry_limit = 15; - } else -#endif - { - entry_limit = 7; - } - if ((index + entry_limit) > dt->limit) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - ptr = dt->base + index; - e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - if ((e2 & DESC_S_MASK) || - (type != 1 && type != 9)) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); - } -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - uint32_t e3, e4; - - e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); - e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC()); - if ((e4 >> DESC_TYPE_SHIFT) & 0xf) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - load_seg_cache_raw_dt(&env->tr, e1, e2); - env->tr.base |= (target_ulong)e3 << 32; - } else -#endif - { - load_seg_cache_raw_dt(&env->tr, e1, e2); - } - e2 |= DESC_TSS_BUSY_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); - } - env->tr.selector = selector; -} - -/* only works if protected mode and not VM86. seg_reg must be != R_CS */ -void helper_load_seg(CPUX86State *env, int seg_reg, int selector) -{ - uint32_t e1, e2; - int cpl, dpl, rpl; - SegmentCache *dt; - int index; - target_ulong ptr; - - selector &= 0xffff; - cpl = env->hflags & HF_CPL_MASK; - if ((selector & 0xfffc) == 0) { - /* null selector case */ - if (seg_reg == R_SS -#ifdef TARGET_X86_64 - && (!(env->hflags & HF_CS64_MASK) || cpl == 3) -#endif - ) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); - } else { - - if (selector & 0x4) { - dt = &env->ldt; - } else { - dt = &env->gdt; - } - index = selector & ~7; - if ((index + 7) > dt->limit) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - ptr = dt->base + index; - e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); - e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); - - if (!(e2 & DESC_S_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (seg_reg == R_SS) { - /* must be writable segment */ - if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - if (rpl != cpl || dpl != cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - } else { - /* must be readable segment */ - if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - - if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { - /* if not conforming code, test rights */ - if (dpl < cpl || dpl < rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - } - } - - if (!(e2 & DESC_P_MASK)) { - if (seg_reg == R_SS) { - raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC()); - } else { - raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); - } - } - - /* set the access bit if not already set */ - if (!(e2 & DESC_A_MASK)) { - e2 |= DESC_A_MASK; - cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); - } - - cpu_x86_load_seg_cache(env, seg_reg, selector, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); -#if 0 - qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n", - selector, (unsigned long)sc->base, sc->limit, sc->flags); -#endif - } -} - -/* protected mode jump */ -void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - target_ulong next_eip) -{ - int gate_cs, type; - uint32_t e1, e2, cpl, dpl, rpl, limit; - - if ((new_cs & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - cpl = env->hflags & HF_CPL_MASK; - if (e2 & DESC_S_MASK) { - if (!(e2 & DESC_CS_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (e2 & DESC_C_MASK) { - /* conforming code segment */ - if (dpl > cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } else { - /* non conforming code segment */ - rpl = new_cs & 3; - if (rpl > cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - if (dpl != cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); - } - limit = get_seg_limit(e1, e2); - if (new_eip > limit && - (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, - get_seg_base(e1, e2), limit, e2); - env->eip = new_eip; - } else { - /* jump to call or task gate */ - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - rpl = new_cs & 3; - cpl = env->hflags & HF_CPL_MASK; - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - if (type != 12) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } -#endif - switch (type) { - case 1: /* 286 TSS */ - case 9: /* 386 TSS */ - case 5: /* task gate */ - if (dpl < cpl || dpl < rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC()); - break; - case 4: /* 286 call gate */ - case 12: /* 386 call gate */ - if ((dpl < cpl) || (dpl < rpl)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); - } - gate_cs = e1 >> 16; - new_eip = (e1 & 0xffff); - if (type == 12) { - new_eip |= (e2 & 0xffff0000); - } - -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - /* load the upper 8 bytes of the 64-bit call gate */ - if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, - GETPC()); - } - type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; - if (type != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, - GETPC()); - } - new_eip |= ((target_ulong)e1) << 32; - } -#endif - - if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - /* must be code segment */ - if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != - (DESC_S_MASK | DESC_CS_MASK))) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } - if (((e2 & DESC_C_MASK) && (dpl > cpl)) || - (!(e2 & DESC_C_MASK) && (dpl != cpl))) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - if (!(e2 & DESC_L_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } - if (e2 & DESC_B_MASK) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } - } -#endif - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); - } - limit = get_seg_limit(e1, e2); - if (new_eip > limit && - (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, - get_seg_base(e1, e2), limit, e2); - env->eip = new_eip; - break; - default: - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - break; - } - } -} - -/* real mode call */ -void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, - int shift, int next_eip) -{ - int new_eip; - uint32_t esp, esp_mask; - target_ulong ssp; - - new_eip = new_eip1; - esp = env->regs[R_ESP]; - esp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - if (shift) { - PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); - } else { - PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); - } - - SET_ESP(esp, esp_mask); - env->eip = new_eip; - env->segs[R_CS].selector = new_cs; - env->segs[R_CS].base = (new_cs << 4); -} - -/* protected mode call */ -void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, - int shift, target_ulong next_eip) -{ - int new_stack, i; - uint32_t e1, e2, cpl, dpl, rpl, selector, param_count; - uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; - uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp, offset, sp; - - LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); - LOG_PCALL_STATE(env_cpu(env)); - if ((new_cs & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - cpl = env->hflags & HF_CPL_MASK; - LOG_PCALL("desc=%08x:%08x\n", e1, e2); - if (e2 & DESC_S_MASK) { - if (!(e2 & DESC_CS_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (e2 & DESC_C_MASK) { - /* conforming code segment */ - if (dpl > cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } else { - /* non conforming code segment */ - rpl = new_cs & 3; - if (rpl > cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - if (dpl != cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); - } - -#ifdef TARGET_X86_64 - /* XXX: check 16/32 bit cases in long mode */ - if (shift == 2) { - target_ulong rsp; - - /* 64 bit case */ - rsp = env->regs[R_ESP]; - PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(rsp, next_eip, GETPC()); - /* from this point, not restartable */ - env->regs[R_ESP] = rsp; - cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), e2); - env->eip = new_eip; - } else -#endif - { - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - if (shift) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); - } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); - } - - limit = get_seg_limit(e1, e2); - if (new_eip > limit) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - /* from this point, not restartable */ - SET_ESP(sp, sp_mask); - cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, - get_seg_base(e1, e2), limit, e2); - env->eip = new_eip; - } - } else { - /* check gate type */ - type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - rpl = new_cs & 3; - -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - if (type != 12) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - } -#endif - - switch (type) { - case 1: /* available 286 TSS */ - case 9: /* available 386 TSS */ - case 5: /* task gate */ - if (dpl < cpl || dpl < rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC()); - return; - case 4: /* 286 call gate */ - case 12: /* 386 call gate */ - break; - default: - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - break; - } - shift = type >> 3; - - if (dpl < cpl || dpl < rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); - } - /* check valid bit */ - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); - } - selector = e1 >> 16; - param_count = e2 & 0x1f; - offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - /* load the upper 8 bytes of the 64-bit call gate */ - if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, - GETPC()); - } - type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; - if (type != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, - GETPC()); - } - offset |= ((target_ulong)e1) << 32; - } -#endif - if ((selector & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - - if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (dpl > cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } -#ifdef TARGET_X86_64 - if (env->efer & MSR_EFER_LMA) { - if (!(e2 & DESC_L_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - if (e2 & DESC_B_MASK) { - raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); - } - shift++; - } -#endif - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); - } - - if (!(e2 & DESC_C_MASK) && dpl < cpl) { - /* to inner privilege */ -#ifdef TARGET_X86_64 - if (shift == 2) { - sp = get_rsp_from_tss(env, dpl); - ss = dpl; /* SS = NULL selector with RPL = new CPL */ - new_stack = 1; - sp_mask = 0; - ssp = 0; /* SS base is always zero in IA-32e mode */ - LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" - TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); - } else -#endif - { - uint32_t sp32; - get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC()); - LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" - TARGET_FMT_lx "\n", ss, sp32, param_count, - env->regs[R_ESP]); - sp = sp32; - if ((ss & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if ((ss & 3) != dpl) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; - if (ss_dpl != dpl) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (!(ss_e2 & DESC_S_MASK) || - (ss_e2 & DESC_CS_MASK) || - !(ss_e2 & DESC_W_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); - } - - /* push_size = ((param_count * 2) + 8) << shift; */ - - old_sp_mask = get_sp_mask(env->segs[R_SS].flags); - old_ssp = env->segs[R_SS].base; -#ifdef TARGET_X86_64 - if (shift == 2) { - /* XXX: verify if new stack address is canonical */ - PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); - PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); - /* parameters aren't supported for 64-bit call gates */ - } else -#endif - if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); - for (i = param_count - 1; i >= 0; i--) { - val = cpu_ldl_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 4) & - old_sp_mask), GETPC()); - PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); - } - } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); - for (i = param_count - 1; i >= 0; i--) { - val = cpu_lduw_kernel_ra(env, old_ssp + - ((env->regs[R_ESP] + i * 2) & - old_sp_mask), GETPC()); - PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); - } - } - new_stack = 1; - } else { - /* to same privilege */ - sp = env->regs[R_ESP]; - sp_mask = get_sp_mask(env->segs[R_SS].flags); - ssp = env->segs[R_SS].base; - /* push_size = (4 << shift); */ - new_stack = 0; - } - -#ifdef TARGET_X86_64 - if (shift == 2) { - PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); - PUSHQ_RA(sp, next_eip, GETPC()); - } else -#endif - if (shift == 1) { - PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); - } else { - PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); - PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); - } - - /* from this point, not restartable */ - - if (new_stack) { -#ifdef TARGET_X86_64 - if (shift == 2) { - cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0); - } else -#endif - { - ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, - get_seg_limit(ss_e1, ss_e2), - ss_e2); - } - } - - selector = (selector & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_CS, selector, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - SET_ESP(sp, sp_mask); - env->eip = offset; - } -} - -/* real and vm86 mode iret */ -void helper_iret_real(CPUX86State *env, int shift) -{ - uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; - target_ulong ssp; - int eflags_mask; - - sp_mask = 0xffff; /* XXXX: use SS segment size? */ - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; - if (shift == 1) { - /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); - new_cs &= 0xffff; - POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); - } else { - /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); - POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); - } - env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); - env->segs[R_CS].selector = new_cs; - env->segs[R_CS].base = (new_cs << 4); - env->eip = new_eip; - if (env->eflags & VM_MASK) { - eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | - NT_MASK; - } else { - eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | - RF_MASK | NT_MASK; - } - if (shift == 0) { - eflags_mask &= 0xffff; - } - cpu_load_eflags(env, new_eflags, eflags_mask); - env->hflags2 &= ~HF2_NMI_MASK; -} - -static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl) -{ - int dpl; - uint32_t e2; - - /* XXX: on x86_64, we do not want to nullify FS and GS because - they may still contain a valid base. I would be interested to - know how a real x86_64 CPU behaves */ - if ((seg_reg == R_FS || seg_reg == R_GS) && - (env->segs[seg_reg].selector & 0xfffc) == 0) { - return; - } - - e2 = env->segs[seg_reg].flags; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { - /* data or non conforming code segment */ - if (dpl < cpl) { - cpu_x86_load_seg_cache(env, seg_reg, 0, - env->segs[seg_reg].base, - env->segs[seg_reg].limit, - env->segs[seg_reg].flags & ~DESC_P_MASK); - } - } -} - -/* protected mode iret */ -static inline void helper_ret_protected(CPUX86State *env, int shift, - int is_iret, int addend, - uintptr_t retaddr) -{ - uint32_t new_cs, new_eflags, new_ss; - uint32_t new_es, new_ds, new_fs, new_gs; - uint32_t e1, e2, ss_e1, ss_e2; - int cpl, dpl, rpl, eflags_mask, iopl; - target_ulong ssp, sp, new_eip, new_esp, sp_mask; - -#ifdef TARGET_X86_64 - if (shift == 2) { - sp_mask = -1; - } else -#endif - { - sp_mask = get_sp_mask(env->segs[R_SS].flags); - } - sp = env->regs[R_ESP]; - ssp = env->segs[R_SS].base; - new_eflags = 0; /* avoid warning */ -#ifdef TARGET_X86_64 - if (shift == 2) { - POPQ_RA(sp, new_eip, retaddr); - POPQ_RA(sp, new_cs, retaddr); - new_cs &= 0xffff; - if (is_iret) { - POPQ_RA(sp, new_eflags, retaddr); - } - } else -#endif - { - if (shift == 1) { - /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); - new_cs &= 0xffff; - if (is_iret) { - POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); - if (new_eflags & VM_MASK) { - goto return_to_vm86; - } - } - } else { - /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); - POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); - if (is_iret) { - POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); - } - } - } - LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n", - new_cs, new_eip, shift, addend); - LOG_PCALL_STATE(env_cpu(env)); - if ((new_cs & 0xfffc) == 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - if (!(e2 & DESC_S_MASK) || - !(e2 & DESC_CS_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - cpl = env->hflags & HF_CPL_MASK; - rpl = new_cs & 3; - if (rpl < cpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - if (e2 & DESC_C_MASK) { - if (dpl > rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - } else { - if (dpl != rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); - } - } - if (!(e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); - } - - sp += addend; - if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || - ((env->hflags & HF_CS64_MASK) && !is_iret))) { - /* return to same privilege level */ - cpu_x86_load_seg_cache(env, R_CS, new_cs, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - } else { - /* return to different privilege level */ -#ifdef TARGET_X86_64 - if (shift == 2) { - POPQ_RA(sp, new_esp, retaddr); - POPQ_RA(sp, new_ss, retaddr); - new_ss &= 0xffff; - } else -#endif - { - if (shift == 1) { - /* 32 bits */ - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - new_ss &= 0xffff; - } else { - /* 16 bits */ - POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); - } - } - LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", - new_ss, new_esp); - if ((new_ss & 0xfffc) == 0) { -#ifdef TARGET_X86_64 - /* NULL ss is allowed in long mode if cpl != 3 */ - /* XXX: test CS64? */ - if ((env->hflags & HF_LMA_MASK) && rpl != 3) { - cpu_x86_load_seg_cache(env, R_SS, new_ss, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (rpl << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); - ss_e2 = DESC_B_MASK; /* XXX: should not be needed? */ - } else -#endif - { - raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); - } - } else { - if ((new_ss & 3) != rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); - } - if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); - } - if (!(ss_e2 & DESC_S_MASK) || - (ss_e2 & DESC_CS_MASK) || - !(ss_e2 & DESC_W_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); - } - dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; - if (dpl != rpl) { - raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); - } - if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr); - } - cpu_x86_load_seg_cache(env, R_SS, new_ss, - get_seg_base(ss_e1, ss_e2), - get_seg_limit(ss_e1, ss_e2), - ss_e2); - } - - cpu_x86_load_seg_cache(env, R_CS, new_cs, - get_seg_base(e1, e2), - get_seg_limit(e1, e2), - e2); - sp = new_esp; -#ifdef TARGET_X86_64 - if (env->hflags & HF_CS64_MASK) { - sp_mask = -1; - } else -#endif - { - sp_mask = get_sp_mask(ss_e2); - } - - /* validate data segments */ - validate_seg(env, R_ES, rpl); - validate_seg(env, R_DS, rpl); - validate_seg(env, R_FS, rpl); - validate_seg(env, R_GS, rpl); - - sp += addend; - } - SET_ESP(sp, sp_mask); - env->eip = new_eip; - if (is_iret) { - /* NOTE: 'cpl' is the _old_ CPL */ - eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK; - if (cpl == 0) { - eflags_mask |= IOPL_MASK; - } - iopl = (env->eflags >> IOPL_SHIFT) & 3; - if (cpl <= iopl) { - eflags_mask |= IF_MASK; - } - if (shift == 0) { - eflags_mask &= 0xffff; - } - cpu_load_eflags(env, new_eflags, eflags_mask); - } - return; - - return_to_vm86: - POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); - POPL_RA(ssp, sp, sp_mask, new_es, retaddr); - POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); - POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); - POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); - - /* modify processor state */ - cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | - IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | - VIP_MASK); - load_seg_vm(env, R_CS, new_cs & 0xffff); - load_seg_vm(env, R_SS, new_ss & 0xffff); - load_seg_vm(env, R_ES, new_es & 0xffff); - load_seg_vm(env, R_DS, new_ds & 0xffff); - load_seg_vm(env, R_FS, new_fs & 0xffff); - load_seg_vm(env, R_GS, new_gs & 0xffff); - - env->eip = new_eip & 0xffff; - env->regs[R_ESP] = new_esp; -} - -void helper_iret_protected(CPUX86State *env, int shift, int next_eip) -{ - int tss_selector, type; - uint32_t e1, e2; - - /* specific case for TSS */ - if (env->eflags & NT_MASK) { -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } -#endif - tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC()); - if (tss_selector & 4) { - raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); - } - if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) { - raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); - } - type = (e2 >> DESC_TYPE_SHIFT) & 0x17; - /* NOTE: we check both segment and busy TSS */ - if (type != 3) { - raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); - } - switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC()); - } else { - helper_ret_protected(env, shift, 1, 0, GETPC()); - } - env->hflags2 &= ~HF2_NMI_MASK; -} - -void helper_lret_protected(CPUX86State *env, int shift, int addend) -{ - helper_ret_protected(env, shift, 0, addend, GETPC()); -} - -void helper_sysenter(CPUX86State *env) -{ - if (env->sysenter_cs == 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } - env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); - -#ifdef TARGET_X86_64 - if (env->hflags & HF_LMA_MASK) { - cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | - DESC_L_MASK); - } else -#endif - { - cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - } - cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc, - 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | - DESC_W_MASK | DESC_A_MASK); - env->regs[R_ESP] = env->sysenter_esp; - env->eip = env->sysenter_eip; -} - -void helper_sysexit(CPUX86State *env, int dflag) -{ - int cpl; - - cpl = env->hflags & HF_CPL_MASK; - if (env->sysenter_cs == 0 || cpl != 0) { - raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); - } -#ifdef TARGET_X86_64 - if (dflag == 2) { - cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | - 3, 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | - DESC_L_MASK); - cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | - 3, 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); - } else -#endif - { - cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | - 3, 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | - 3, 0, 0xffffffff, - DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | - DESC_S_MASK | (3 << DESC_DPL_SHIFT) | - DESC_W_MASK | DESC_A_MASK); - } - env->regs[R_ESP] = env->regs[R_ECX]; - env->eip = env->regs[R_EDX]; -} - -target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) -{ - unsigned int limit; - uint32_t e1, e2, eflags, selector; - int rpl, dpl, cpl, type; - - selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); - if ((selector & 0xfffc) == 0) { - goto fail; - } - if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { - goto fail; - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - if (e2 & DESC_S_MASK) { - if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { - /* conforming */ - } else { - if (dpl < cpl || dpl < rpl) { - goto fail; - } - } - } else { - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - switch (type) { - case 1: - case 2: - case 3: - case 9: - case 11: - break; - default: - goto fail; - } - if (dpl < cpl || dpl < rpl) { - fail: - CC_SRC = eflags & ~CC_Z; - return 0; - } - } - limit = get_seg_limit(e1, e2); - CC_SRC = eflags | CC_Z; - return limit; -} - -target_ulong helper_lar(CPUX86State *env, target_ulong selector1) -{ - uint32_t e1, e2, eflags, selector; - int rpl, dpl, cpl, type; - - selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); - if ((selector & 0xfffc) == 0) { - goto fail; - } - if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { - goto fail; - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - if (e2 & DESC_S_MASK) { - if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { - /* conforming */ - } else { - if (dpl < cpl || dpl < rpl) { - goto fail; - } - } - } else { - type = (e2 >> DESC_TYPE_SHIFT) & 0xf; - switch (type) { - case 1: - case 2: - case 3: - case 4: - case 5: - case 9: - case 11: - case 12: - break; - default: - goto fail; - } - if (dpl < cpl || dpl < rpl) { - fail: - CC_SRC = eflags & ~CC_Z; - return 0; - } - } - CC_SRC = eflags | CC_Z; - return e2 & 0x00f0ff00; -} - -void helper_verr(CPUX86State *env, target_ulong selector1) -{ - uint32_t e1, e2, eflags, selector; - int rpl, dpl, cpl; - - selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); - if ((selector & 0xfffc) == 0) { - goto fail; - } - if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { - goto fail; - } - if (!(e2 & DESC_S_MASK)) { - goto fail; - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - if (e2 & DESC_CS_MASK) { - if (!(e2 & DESC_R_MASK)) { - goto fail; - } - if (!(e2 & DESC_C_MASK)) { - if (dpl < cpl || dpl < rpl) { - goto fail; - } - } - } else { - if (dpl < cpl || dpl < rpl) { - fail: - CC_SRC = eflags & ~CC_Z; - return; - } - } - CC_SRC = eflags | CC_Z; -} - -void helper_verw(CPUX86State *env, target_ulong selector1) -{ - uint32_t e1, e2, eflags, selector; - int rpl, dpl, cpl; - - selector = selector1 & 0xffff; - eflags = cpu_cc_compute_all(env, CC_OP); - if ((selector & 0xfffc) == 0) { - goto fail; - } - if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { - goto fail; - } - if (!(e2 & DESC_S_MASK)) { - goto fail; - } - rpl = selector & 3; - dpl = (e2 >> DESC_DPL_SHIFT) & 3; - cpl = env->hflags & HF_CPL_MASK; - if (e2 & DESC_CS_MASK) { - goto fail; - } else { - if (dpl < cpl || dpl < rpl) { - goto fail; - } - if (!(e2 & DESC_W_MASK)) { - fail: - CC_SRC = eflags & ~CC_Z; - return; - } - } - CC_SRC = eflags | CC_Z; -} - -#if defined(CONFIG_USER_ONLY) -void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector) -{ - if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { - int dpl = (env->eflags & VM_MASK) ? 3 : 0; - selector &= 0xffff; - cpu_x86_load_seg_cache(env, seg_reg, selector, - (selector << 4), 0xffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_A_MASK | (dpl << DESC_DPL_SHIFT)); - } else { - helper_load_seg(env, seg_reg, selector); - } -} -#endif - -/* check if Port I/O is allowed in TSS */ -static inline void check_io(CPUX86State *env, int addr, int size, - uintptr_t retaddr) -{ - int io_offset, val, mask; - - /* TSS must be a valid 32 bit one */ - if (!(env->tr.flags & DESC_P_MASK) || - ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 || - env->tr.limit < 103) { - goto fail; - } - io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr); - io_offset += (addr >> 3); - /* Note: the check needs two bytes */ - if ((io_offset + 1) > env->tr.limit) { - goto fail; - } - val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr); - val >>= (addr & 7); - mask = (1 << size) - 1; - /* all bits must be zero to allow the I/O */ - if ((val & mask) != 0) { - fail: - raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); - } -} - -void helper_check_iob(CPUX86State *env, uint32_t t0) -{ - check_io(env, t0, 1, GETPC()); -} - -void helper_check_iow(CPUX86State *env, uint32_t t0) -{ - check_io(env, t0, 2, GETPC()); -} - -void helper_check_iol(CPUX86State *env, uint32_t t0) -{ - check_io(env, t0, 4, GETPC()); -} diff --git a/target/i386/smm_helper.c b/target/i386/smm_helper.c deleted file mode 100644 index d20e8edfdf..0000000000 --- a/target/i386/smm_helper.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * x86 SMM helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "qemu/main-loop.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/log.h" - -/* SMM support */ - -#if defined(CONFIG_USER_ONLY) - -void do_smm_enter(X86CPU *cpu) -{ -} - -void helper_rsm(CPUX86State *env) -{ -} - -#else - -#ifdef TARGET_X86_64 -#define SMM_REVISION_ID 0x00020064 -#else -#define SMM_REVISION_ID 0x00020000 -#endif - -void do_smm_enter(X86CPU *cpu) -{ - CPUX86State *env = &cpu->env; - CPUState *cs = CPU(cpu); - target_ulong sm_state; - SegmentCache *dt; - int i, offset; - - qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); - log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); - - env->msr_smi_count++; - env->hflags |= HF_SMM_MASK; - if (env->hflags2 & HF2_NMI_MASK) { - env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; - } else { - env->hflags2 |= HF2_NMI_MASK; - } - - sm_state = env->smbase + 0x8000; - -#ifdef TARGET_X86_64 - for (i = 0; i < 6; i++) { - dt = &env->segs[i]; - offset = 0x7e00 + i * 16; - x86_stw_phys(cs, sm_state + offset, dt->selector); - x86_stw_phys(cs, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff); - x86_stl_phys(cs, sm_state + offset + 4, dt->limit); - x86_stq_phys(cs, sm_state + offset + 8, dt->base); - } - - x86_stq_phys(cs, sm_state + 0x7e68, env->gdt.base); - x86_stl_phys(cs, sm_state + 0x7e64, env->gdt.limit); - - x86_stw_phys(cs, sm_state + 0x7e70, env->ldt.selector); - x86_stq_phys(cs, sm_state + 0x7e78, env->ldt.base); - x86_stl_phys(cs, sm_state + 0x7e74, env->ldt.limit); - x86_stw_phys(cs, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff); - - x86_stq_phys(cs, sm_state + 0x7e88, env->idt.base); - x86_stl_phys(cs, sm_state + 0x7e84, env->idt.limit); - - x86_stw_phys(cs, sm_state + 0x7e90, env->tr.selector); - x86_stq_phys(cs, sm_state + 0x7e98, env->tr.base); - x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); - x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); - - /* ??? Vol 1, 16.5.6 Intel MPX and SMM says that IA32_BNDCFGS - is saved at offset 7ED0. Vol 3, 34.4.1.1, Table 32-2, has - 7EA0-7ED7 as "reserved". What's this, and what's really - supposed to happen? */ - x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); - - x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); - x86_stq_phys(cs, sm_state + 0x7ff0, env->regs[R_ECX]); - x86_stq_phys(cs, sm_state + 0x7fe8, env->regs[R_EDX]); - x86_stq_phys(cs, sm_state + 0x7fe0, env->regs[R_EBX]); - x86_stq_phys(cs, sm_state + 0x7fd8, env->regs[R_ESP]); - x86_stq_phys(cs, sm_state + 0x7fd0, env->regs[R_EBP]); - x86_stq_phys(cs, sm_state + 0x7fc8, env->regs[R_ESI]); - x86_stq_phys(cs, sm_state + 0x7fc0, env->regs[R_EDI]); - for (i = 8; i < 16; i++) { - x86_stq_phys(cs, sm_state + 0x7ff8 - i * 8, env->regs[i]); - } - x86_stq_phys(cs, sm_state + 0x7f78, env->eip); - x86_stl_phys(cs, sm_state + 0x7f70, cpu_compute_eflags(env)); - x86_stl_phys(cs, sm_state + 0x7f68, env->dr[6]); - x86_stl_phys(cs, sm_state + 0x7f60, env->dr[7]); - - x86_stl_phys(cs, sm_state + 0x7f48, env->cr[4]); - x86_stq_phys(cs, sm_state + 0x7f50, env->cr[3]); - x86_stl_phys(cs, sm_state + 0x7f58, env->cr[0]); - - x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); - x86_stl_phys(cs, sm_state + 0x7f00, env->smbase); -#else - x86_stl_phys(cs, sm_state + 0x7ffc, env->cr[0]); - x86_stl_phys(cs, sm_state + 0x7ff8, env->cr[3]); - x86_stl_phys(cs, sm_state + 0x7ff4, cpu_compute_eflags(env)); - x86_stl_phys(cs, sm_state + 0x7ff0, env->eip); - x86_stl_phys(cs, sm_state + 0x7fec, env->regs[R_EDI]); - x86_stl_phys(cs, sm_state + 0x7fe8, env->regs[R_ESI]); - x86_stl_phys(cs, sm_state + 0x7fe4, env->regs[R_EBP]); - x86_stl_phys(cs, sm_state + 0x7fe0, env->regs[R_ESP]); - x86_stl_phys(cs, sm_state + 0x7fdc, env->regs[R_EBX]); - x86_stl_phys(cs, sm_state + 0x7fd8, env->regs[R_EDX]); - x86_stl_phys(cs, sm_state + 0x7fd4, env->regs[R_ECX]); - x86_stl_phys(cs, sm_state + 0x7fd0, env->regs[R_EAX]); - x86_stl_phys(cs, sm_state + 0x7fcc, env->dr[6]); - x86_stl_phys(cs, sm_state + 0x7fc8, env->dr[7]); - - x86_stl_phys(cs, sm_state + 0x7fc4, env->tr.selector); - x86_stl_phys(cs, sm_state + 0x7f64, env->tr.base); - x86_stl_phys(cs, sm_state + 0x7f60, env->tr.limit); - x86_stl_phys(cs, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff); - - x86_stl_phys(cs, sm_state + 0x7fc0, env->ldt.selector); - x86_stl_phys(cs, sm_state + 0x7f80, env->ldt.base); - x86_stl_phys(cs, sm_state + 0x7f7c, env->ldt.limit); - x86_stl_phys(cs, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff); - - x86_stl_phys(cs, sm_state + 0x7f74, env->gdt.base); - x86_stl_phys(cs, sm_state + 0x7f70, env->gdt.limit); - - x86_stl_phys(cs, sm_state + 0x7f58, env->idt.base); - x86_stl_phys(cs, sm_state + 0x7f54, env->idt.limit); - - for (i = 0; i < 6; i++) { - dt = &env->segs[i]; - if (i < 3) { - offset = 0x7f84 + i * 12; - } else { - offset = 0x7f2c + (i - 3) * 12; - } - x86_stl_phys(cs, sm_state + 0x7fa8 + i * 4, dt->selector); - x86_stl_phys(cs, sm_state + offset + 8, dt->base); - x86_stl_phys(cs, sm_state + offset + 4, dt->limit); - x86_stl_phys(cs, sm_state + offset, (dt->flags >> 8) & 0xf0ff); - } - x86_stl_phys(cs, sm_state + 0x7f14, env->cr[4]); - - x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); - x86_stl_phys(cs, sm_state + 0x7ef8, env->smbase); -#endif - /* init SMM cpu state */ - -#ifdef TARGET_X86_64 - cpu_load_efer(env, 0); -#endif - cpu_load_eflags(env, 0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | - DF_MASK)); - env->eip = 0x00008000; - cpu_x86_update_cr0(env, - env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | - CR0_PG_MASK)); - cpu_x86_update_cr4(env, 0); - env->dr[7] = 0x00000400; - - cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase, - 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); - cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, - DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | - DESC_G_MASK | DESC_A_MASK); -} - -void helper_rsm(CPUX86State *env) -{ - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - target_ulong sm_state; - int i, offset; - uint32_t val; - - sm_state = env->smbase + 0x8000; -#ifdef TARGET_X86_64 - cpu_load_efer(env, x86_ldq_phys(cs, sm_state + 0x7ed0)); - - env->gdt.base = x86_ldq_phys(cs, sm_state + 0x7e68); - env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7e64); - - env->ldt.selector = x86_lduw_phys(cs, sm_state + 0x7e70); - env->ldt.base = x86_ldq_phys(cs, sm_state + 0x7e78); - env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7e74); - env->ldt.flags = (x86_lduw_phys(cs, sm_state + 0x7e72) & 0xf0ff) << 8; - - env->idt.base = x86_ldq_phys(cs, sm_state + 0x7e88); - env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7e84); - - env->tr.selector = x86_lduw_phys(cs, sm_state + 0x7e90); - env->tr.base = x86_ldq_phys(cs, sm_state + 0x7e98); - env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7e94); - env->tr.flags = (x86_lduw_phys(cs, sm_state + 0x7e92) & 0xf0ff) << 8; - - env->regs[R_EAX] = x86_ldq_phys(cs, sm_state + 0x7ff8); - env->regs[R_ECX] = x86_ldq_phys(cs, sm_state + 0x7ff0); - env->regs[R_EDX] = x86_ldq_phys(cs, sm_state + 0x7fe8); - env->regs[R_EBX] = x86_ldq_phys(cs, sm_state + 0x7fe0); - env->regs[R_ESP] = x86_ldq_phys(cs, sm_state + 0x7fd8); - env->regs[R_EBP] = x86_ldq_phys(cs, sm_state + 0x7fd0); - env->regs[R_ESI] = x86_ldq_phys(cs, sm_state + 0x7fc8); - env->regs[R_EDI] = x86_ldq_phys(cs, sm_state + 0x7fc0); - for (i = 8; i < 16; i++) { - env->regs[i] = x86_ldq_phys(cs, sm_state + 0x7ff8 - i * 8); - } - env->eip = x86_ldq_phys(cs, sm_state + 0x7f78); - cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70), - ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68); - env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60); - - cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48)); - cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50)); - cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7f58)); - - for (i = 0; i < 6; i++) { - offset = 0x7e00 + i * 16; - cpu_x86_load_seg_cache(env, i, - x86_lduw_phys(cs, sm_state + offset), - x86_ldq_phys(cs, sm_state + offset + 8), - x86_ldl_phys(cs, sm_state + offset + 4), - (x86_lduw_phys(cs, sm_state + offset + 2) & - 0xf0ff) << 8); - } - - val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ - if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00); - } -#else - cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); - cpu_x86_update_cr3(env, x86_ldl_phys(cs, sm_state + 0x7ff8)); - cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7ff4), - ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - env->eip = x86_ldl_phys(cs, sm_state + 0x7ff0); - env->regs[R_EDI] = x86_ldl_phys(cs, sm_state + 0x7fec); - env->regs[R_ESI] = x86_ldl_phys(cs, sm_state + 0x7fe8); - env->regs[R_EBP] = x86_ldl_phys(cs, sm_state + 0x7fe4); - env->regs[R_ESP] = x86_ldl_phys(cs, sm_state + 0x7fe0); - env->regs[R_EBX] = x86_ldl_phys(cs, sm_state + 0x7fdc); - env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8); - env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4); - env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0); - env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc); - env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8); - - env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff; - env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64); - env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7f60); - env->tr.flags = (x86_ldl_phys(cs, sm_state + 0x7f5c) & 0xf0ff) << 8; - - env->ldt.selector = x86_ldl_phys(cs, sm_state + 0x7fc0) & 0xffff; - env->ldt.base = x86_ldl_phys(cs, sm_state + 0x7f80); - env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7f7c); - env->ldt.flags = (x86_ldl_phys(cs, sm_state + 0x7f78) & 0xf0ff) << 8; - - env->gdt.base = x86_ldl_phys(cs, sm_state + 0x7f74); - env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7f70); - - env->idt.base = x86_ldl_phys(cs, sm_state + 0x7f58); - env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7f54); - - for (i = 0; i < 6; i++) { - if (i < 3) { - offset = 0x7f84 + i * 12; - } else { - offset = 0x7f2c + (i - 3) * 12; - } - cpu_x86_load_seg_cache(env, i, - x86_ldl_phys(cs, - sm_state + 0x7fa8 + i * 4) & 0xffff, - x86_ldl_phys(cs, sm_state + offset + 8), - x86_ldl_phys(cs, sm_state + offset + 4), - (x86_ldl_phys(cs, - sm_state + offset) & 0xf0ff) << 8); - } - cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f14)); - - val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ - if (val & 0x20000) { - env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8); - } -#endif - if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { - env->hflags2 &= ~HF2_NMI_MASK; - } - env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK; - env->hflags &= ~HF_SMM_MASK; - - qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n"); - log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); -} - -#endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c deleted file mode 100644 index 38931586e5..0000000000 --- a/target/i386/svm_helper.c +++ /dev/null @@ -1,800 +0,0 @@ -/* - * x86 SVM helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" -#include "exec/exec-all.h" -#include "exec/cpu_ldst.h" - -/* Secure Virtual Machine helpers */ - -#if defined(CONFIG_USER_ONLY) - -void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) -{ -} - -void helper_vmmcall(CPUX86State *env) -{ -} - -void helper_vmload(CPUX86State *env, int aflag) -{ -} - -void helper_vmsave(CPUX86State *env, int aflag) -{ -} - -void helper_stgi(CPUX86State *env) -{ -} - -void helper_clgi(CPUX86State *env) -{ -} - -void helper_skinit(CPUX86State *env) -{ -} - -void helper_invlpga(CPUX86State *env, int aflag) -{ -} - -void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, - uintptr_t retaddr) -{ - assert(0); -} - -void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param) -{ -} - -void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param, uintptr_t retaddr) -{ -} - -void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, - uint32_t next_eip_addend) -{ -} -#else - -static inline void svm_save_seg(CPUX86State *env, hwaddr addr, - const SegmentCache *sc) -{ - CPUState *cs = env_cpu(env); - - x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), - sc->selector); - x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), - sc->base); - x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), - sc->limit); - x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), - ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); -} - -static inline void svm_load_seg(CPUX86State *env, hwaddr addr, - SegmentCache *sc) -{ - CPUState *cs = env_cpu(env); - unsigned int flags; - - sc->selector = x86_lduw_phys(cs, - addr + offsetof(struct vmcb_seg, selector)); - sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); - sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); - flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); - sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); -} - -static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, - int seg_reg) -{ - SegmentCache sc1, *sc = &sc1; - - svm_load_seg(env, addr, sc); - cpu_x86_load_seg_cache(env, seg_reg, sc->selector, - sc->base, sc->limit, sc->flags); -} - -void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) -{ - CPUState *cs = env_cpu(env); - target_ulong addr; - uint64_t nested_ctl; - uint32_t event_inj; - uint32_t int_ctl; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); - - if (aflag == 2) { - addr = env->regs[R_EAX]; - } else { - addr = (uint32_t)env->regs[R_EAX]; - } - - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr); - - env->vm_vmcb = addr; - - /* save the current CPU state in the hsave page */ - x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), - env->gdt.base); - x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), - env->gdt.limit); - - x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), - env->idt.base); - x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), - env->idt.limit); - - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]); - - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.rflags), - cpu_compute_eflags(env)); - - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.es), - &env->segs[R_ES]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.cs), - &env->segs[R_CS]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ss), - &env->segs[R_SS]); - svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), - &env->segs[R_DS]); - - x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), - env->eip + next_eip_addend); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); - x86_stq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); - - /* load the interception bitmaps so we do not need to access the - vmcb in svm mode */ - env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.intercept)); - env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.intercept_cr_read)); - env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.intercept_cr_write)); - env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.intercept_dr_read)); - env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.intercept_dr_write)); - env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.intercept_exceptions - )); - - nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.nested_ctl)); - - env->nested_pg_mode = 0; - - if (nested_ctl & SVM_NPT_ENABLED) { - env->nested_cr3 = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - control.nested_cr3)); - env->hflags2 |= HF2_NPT_MASK; - - if (env->cr[4] & CR4_PAE_MASK) { - env->nested_pg_mode |= SVM_NPT_PAE; - } - if (env->cr[4] & CR4_PSE_MASK) { - env->nested_pg_mode |= SVM_NPT_PSE; - } - if (env->hflags & HF_LMA_MASK) { - env->nested_pg_mode |= SVM_NPT_LMA; - } - if (env->efer & MSR_EFER_NXE) { - env->nested_pg_mode |= SVM_NPT_NXE; - } - } - - /* enable intercepts */ - env->hflags |= HF_GUEST_MASK; - - env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, control.tsc_offset)); - - env->gdt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - save.gdtr.base)); - env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - save.gdtr.limit)); - - env->idt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - save.idtr.base)); - env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - save.idtr.limit)); - - /* clear exit_info_2 so we behave like the real hardware */ - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); - - cpu_x86_update_cr0(env, x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - save.cr0))); - cpu_x86_update_cr4(env, x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - save.cr4))); - cpu_x86_update_cr3(env, x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - save.cr3))); - env->cr[2] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.cr2)); - int_ctl = x86_ldl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); - env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); - if (int_ctl & V_INTR_MASKING_MASK) { - env->v_tpr = int_ctl & V_TPR_MASK; - env->hflags2 |= HF2_VINTR_MASK; - if (env->eflags & IF_MASK) { - env->hflags2 |= HF2_HIF_MASK; - } - } - - cpu_load_efer(env, - x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.efer))); - env->eflags = 0; - cpu_load_eflags(env, x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, - save.rflags)), - ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); - - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es), - R_ES); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), - R_CS); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), - R_SS); - svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), - R_DS); - - env->eip = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.rip)); - - env->regs[R_ESP] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.rsp)); - env->regs[R_EAX] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.rax)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr7)); - env->dr[6] = x86_ldq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr6)); - - /* FIXME: guest state consistency checks */ - - switch (x86_ldub_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { - case TLB_CONTROL_DO_NOTHING: - break; - case TLB_CONTROL_FLUSH_ALL_ASID: - /* FIXME: this is not 100% correct but should work for now */ - tlb_flush(cs); - break; - } - - env->hflags2 |= HF2_GIF_MASK; - - if (int_ctl & V_IRQ_MASK) { - CPUState *cs = env_cpu(env); - - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; - } - - /* maybe we need to inject an event */ - event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.event_inj)); - if (event_inj & SVM_EVTINJ_VALID) { - uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK; - uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR; - uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.event_inj_err)); - - qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err); - /* FIXME: need to implement valid_err */ - switch (event_inj & SVM_EVTINJ_TYPE_MASK) { - case SVM_EVTINJ_TYPE_INTR: - cs->exception_index = vector; - env->error_code = event_inj_err; - env->exception_is_int = 0; - env->exception_next_eip = -1; - qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR"); - /* XXX: is it always correct? */ - do_interrupt_x86_hardirq(env, vector, 1); - break; - case SVM_EVTINJ_TYPE_NMI: - cs->exception_index = EXCP02_NMI; - env->error_code = event_inj_err; - env->exception_is_int = 0; - env->exception_next_eip = env->eip; - qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI"); - cpu_loop_exit(cs); - break; - case SVM_EVTINJ_TYPE_EXEPT: - cs->exception_index = vector; - env->error_code = event_inj_err; - env->exception_is_int = 0; - env->exception_next_eip = -1; - qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT"); - cpu_loop_exit(cs); - break; - case SVM_EVTINJ_TYPE_SOFT: - cs->exception_index = vector; - env->error_code = event_inj_err; - env->exception_is_int = 1; - env->exception_next_eip = env->eip; - qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT"); - cpu_loop_exit(cs); - break; - } - qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", cs->exception_index, - env->error_code); - } -} - -void helper_vmmcall(CPUX86State *env) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0, GETPC()); - raise_exception(env, EXCP06_ILLOP); -} - -void helper_vmload(CPUX86State *env, int aflag) -{ - CPUState *cs = env_cpu(env); - target_ulong addr; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); - - if (aflag == 2) { - addr = env->regs[R_EAX]; - } else { - addr = (uint32_t)env->regs[R_EAX]; - } - - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx - "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.fs.base)), - env->segs[R_FS].base); - - svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.fs), R_FS); - svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.gs), R_GS); - svm_load_seg(env, addr + offsetof(struct vmcb, save.tr), &env->tr); - svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); - -#ifdef TARGET_X86_64 - env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.kernel_gs_base)); - env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); - env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); - env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); -#endif - env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); - env->sysenter_cs = x86_ldq_phys(cs, - addr + offsetof(struct vmcb, save.sysenter_cs)); - env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.sysenter_esp)); - env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, - save.sysenter_eip)); -} - -void helper_vmsave(CPUX86State *env, int aflag) -{ - CPUState *cs = env_cpu(env); - target_ulong addr; - - cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); - - if (aflag == 2) { - addr = env->regs[R_EAX]; - } else { - addr = (uint32_t)env->regs[R_EAX]; - } - - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx - "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", - addr, x86_ldq_phys(cs, - addr + offsetof(struct vmcb, save.fs.base)), - env->segs[R_FS].base); - - svm_save_seg(env, addr + offsetof(struct vmcb, save.fs), - &env->segs[R_FS]); - svm_save_seg(env, addr + offsetof(struct vmcb, save.gs), - &env->segs[R_GS]); - svm_save_seg(env, addr + offsetof(struct vmcb, save.tr), - &env->tr); - svm_save_seg(env, addr + offsetof(struct vmcb, save.ldtr), - &env->ldt); - -#ifdef TARGET_X86_64 - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), - env->kernelgsbase); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); -#endif - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); - x86_stq_phys(cs, - addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), - env->sysenter_esp); - x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), - env->sysenter_eip); -} - -void helper_stgi(CPUX86State *env) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0, GETPC()); - env->hflags2 |= HF2_GIF_MASK; -} - -void helper_clgi(CPUX86State *env) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0, GETPC()); - env->hflags2 &= ~HF2_GIF_MASK; -} - -void helper_skinit(CPUX86State *env) -{ - cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0, GETPC()); - /* XXX: not implemented */ - raise_exception(env, EXCP06_ILLOP); -} - -void helper_invlpga(CPUX86State *env, int aflag) -{ - X86CPU *cpu = env_archcpu(env); - target_ulong addr; - - cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0, GETPC()); - - if (aflag == 2) { - addr = env->regs[R_EAX]; - } else { - addr = (uint32_t)env->regs[R_EAX]; - } - - /* XXX: could use the ASID to see if it is needed to do the - flush */ - tlb_flush_page(CPU(cpu), addr); -} - -void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param, uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - if (likely(!(env->hflags & HF_GUEST_MASK))) { - return; - } - switch (type) { - case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: - if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: - if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: - if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: - if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: - if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - case SVM_EXIT_MSR: - if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { - /* FIXME: this should be read in at vmrun (faster this way?) */ - uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, - control.msrpm_base_pa)); - uint32_t t0, t1; - - switch ((uint32_t)env->regs[R_ECX]) { - case 0 ... 0x1fff: - t0 = (env->regs[R_ECX] * 2) % 8; - t1 = (env->regs[R_ECX] * 2) / 8; - break; - case 0xc0000000 ... 0xc0001fff: - t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; - t1 = (t0 / 8); - t0 %= 8; - break; - case 0xc0010000 ... 0xc0011fff: - t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; - t1 = (t0 / 8); - t0 %= 8; - break; - default: - cpu_vmexit(env, type, param, retaddr); - t0 = 0; - t1 = 0; - break; - } - if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { - cpu_vmexit(env, type, param, retaddr); - } - } - break; - default: - if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { - cpu_vmexit(env, type, param, retaddr); - } - break; - } -} - -void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, - uint64_t param) -{ - cpu_svm_check_intercept_param(env, type, param, GETPC()); -} - -void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, - uint32_t next_eip_addend) -{ - CPUState *cs = env_cpu(env); - - if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { - /* FIXME: this should be read in at vmrun (faster this way?) */ - uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + - offsetof(struct vmcb, control.iopm_base_pa)); - uint16_t mask = (1 << ((param >> 4) & 7)) - 1; - - if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) { - /* next env->eip */ - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), - env->eip + next_eip_addend); - cpu_vmexit(env, SVM_EXIT_IOIO, param | (port << 16), GETPC()); - } - } -} - -void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1, - uintptr_t retaddr) -{ - CPUState *cs = env_cpu(env); - - cpu_restore_state(cs, retaddr, true); - - qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" - PRIx64 ", " TARGET_FMT_lx ")!\n", - exit_code, exit_info_1, - x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.exit_info_2)), - env->eip); - - cs->exception_index = EXCP_VMEXIT + exit_code; - env->error_code = exit_info_1; - - /* remove any pending exception */ - env->old_exception = -1; - cpu_loop_exit(cs); -} - -void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) -{ - CPUState *cs = env_cpu(env); - uint32_t int_ctl; - - if (env->hflags & HF_INHIBIT_IRQ_MASK) { - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.int_state), - SVM_INTERRUPT_SHADOW_MASK); - env->hflags &= ~HF_INHIBIT_IRQ_MASK; - } else { - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); - } - env->hflags2 &= ~HF2_NPT_MASK; - - /* Save the VM state in the vmcb */ - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), - &env->segs[R_ES]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), - &env->segs[R_CS]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), - &env->segs[R_SS]); - svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), - &env->segs[R_DS]); - - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), - env->gdt.base); - x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), - env->gdt.limit); - - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), - env->idt.base); - x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), - env->idt.limit); - - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]); - - int_ctl = x86_ldl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); - int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); - int_ctl |= env->v_tpr & V_TPR_MASK; - if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { - int_ctl |= V_IRQ_MASK; - } - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); - - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags), - cpu_compute_eflags(env)); - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip), - env->eip); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]); - x86_stq_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]); - x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl), - env->hflags & HF_CPL_MASK); - - /* Reload the host state from vm_hsave */ - env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); - env->hflags &= ~HF_GUEST_MASK; - env->intercept = 0; - env->intercept_exceptions = 0; - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; - env->tsc_offset = 0; - - env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, - save.gdtr.base)); - env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, - save.gdtr.limit)); - - env->idt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, - save.idtr.base)); - env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, - save.idtr.limit)); - - cpu_x86_update_cr0(env, x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, - save.cr0)) | - CR0_PE_MASK); - cpu_x86_update_cr4(env, x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, - save.cr4))); - cpu_x86_update_cr3(env, x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, - save.cr3))); - /* we need to set the efer after the crs so the hidden flags get - set properly */ - cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, - save.efer))); - env->eflags = 0; - cpu_load_eflags(env, x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, - save.rflags)), - ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | - VM_MASK)); - - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es), - R_ES); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.cs), - R_CS); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ss), - R_SS); - svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), - R_DS); - - env->eip = x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.rip)); - env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave + - offsetof(struct vmcb, save.rsp)); - env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave + - offsetof(struct vmcb, save.rax)); - - env->dr[6] = x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr6)); - env->dr[7] = x86_ldq_phys(cs, - env->vm_hsave + offsetof(struct vmcb, save.dr7)); - - /* other setups */ - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), - exit_code); - x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), - exit_info_1); - - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info), - x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.event_inj))); - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), - x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, - control.event_inj_err))); - x86_stl_phys(cs, - env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); - - env->hflags2 &= ~HF2_GIF_MASK; - /* FIXME: Resets the current ASID register to zero (host ASID). */ - - /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ - - /* Clears the TSC_OFFSET inside the processor. */ - - /* If the host is in PAE mode, the processor reloads the host's PDPEs - from the page table indicated the host's CR3. If the PDPEs contain - illegal state, the processor causes a shutdown. */ - - /* Disables all breakpoints in the host DR7 register. */ - - /* Checks the reloaded host state for consistency. */ - - /* If the host's rIP reloaded by #VMEXIT is outside the limit of the - host's code segment or non-canonical (in the case of long mode), a - #GP fault is delivered inside the host. */ -} - -#endif diff --git a/target/i386/tcg-stub.c b/target/i386/tcg-stub.c deleted file mode 100644 index 8d45579ada..0000000000 --- a/target/i386/tcg-stub.c +++ /dev/null @@ -1,25 +0,0 @@ -/* - * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#include "qemu/osdep.h" -#include "cpu.h" - -void update_mxcsr_from_sse_status(CPUX86State *env) -{ -} diff --git a/target/i386/tcg/bpt_helper.c b/target/i386/tcg/bpt_helper.c new file mode 100644 index 0000000000..e6cc2921e2 --- /dev/null +++ b/target/i386/tcg/bpt_helper.c @@ -0,0 +1,335 @@ +/* + * i386 breakpoint helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" + + +#ifndef CONFIG_USER_ONLY +static inline bool hw_local_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 1; +} + +static inline bool hw_global_breakpoint_enabled(unsigned long dr7, int index) +{ + return (dr7 >> (index * 2)) & 2; + +} +static inline bool hw_breakpoint_enabled(unsigned long dr7, int index) +{ + return hw_global_breakpoint_enabled(dr7, index) || + hw_local_breakpoint_enabled(dr7, index); +} + +static inline int hw_breakpoint_type(unsigned long dr7, int index) +{ + return (dr7 >> (DR7_TYPE_SHIFT + (index * 4))) & 3; +} + +static inline int hw_breakpoint_len(unsigned long dr7, int index) +{ + int len = ((dr7 >> (DR7_LEN_SHIFT + (index * 4))) & 3); + return (len == 2) ? 8 : len + 1; +} + +static int hw_breakpoint_insert(CPUX86State *env, int index) +{ + CPUState *cs = env_cpu(env); + target_ulong dr7 = env->dr[7]; + target_ulong drN = env->dr[index]; + int err = 0; + + switch (hw_breakpoint_type(dr7, index)) { + case DR7_TYPE_BP_INST: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_breakpoint_insert(cs, drN, BP_CPU, + &env->cpu_breakpoint[index]); + } + break; + + case DR7_TYPE_IO_RW: + /* Notice when we should enable calls to bpt_io. */ + return hw_breakpoint_enabled(env->dr[7], index) + ? HF_IOBPT_MASK : 0; + + case DR7_TYPE_DATA_WR: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_WRITE, + &env->cpu_watchpoint[index]); + } + break; + + case DR7_TYPE_DATA_RW: + if (hw_breakpoint_enabled(dr7, index)) { + err = cpu_watchpoint_insert(cs, drN, + hw_breakpoint_len(dr7, index), + BP_CPU | BP_MEM_ACCESS, + &env->cpu_watchpoint[index]); + } + break; + } + if (err) { + env->cpu_breakpoint[index] = NULL; + } + return 0; +} + +static void hw_breakpoint_remove(CPUX86State *env, int index) +{ + CPUState *cs = env_cpu(env); + + switch (hw_breakpoint_type(env->dr[7], index)) { + case DR7_TYPE_BP_INST: + if (env->cpu_breakpoint[index]) { + cpu_breakpoint_remove_by_ref(cs, env->cpu_breakpoint[index]); + env->cpu_breakpoint[index] = NULL; + } + break; + + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: + if (env->cpu_breakpoint[index]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[index]); + env->cpu_breakpoint[index] = NULL; + } + break; + + case DR7_TYPE_IO_RW: + /* HF_IOBPT_MASK cleared elsewhere. */ + break; + } +} + +void cpu_x86_update_dr7(CPUX86State *env, uint32_t new_dr7) +{ + target_ulong old_dr7 = env->dr[7]; + int iobpt = 0; + int i; + + new_dr7 |= DR7_FIXED_1; + + /* If nothing is changing except the global/local enable bits, + then we can make the change more efficient. */ + if (((old_dr7 ^ new_dr7) & ~0xff) == 0) { + /* Fold the global and local enable bits together into the + global fields, then xor to show which registers have + changed collective enable state. */ + int mod = ((old_dr7 | old_dr7 * 2) ^ (new_dr7 | new_dr7 * 2)) & 0xff; + + for (i = 0; i < DR7_MAX_BP; i++) { + if ((mod & (2 << i * 2)) && !hw_breakpoint_enabled(new_dr7, i)) { + hw_breakpoint_remove(env, i); + } + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + if (mod & (2 << i * 2) && hw_breakpoint_enabled(new_dr7, i)) { + iobpt |= hw_breakpoint_insert(env, i); + } else if (hw_breakpoint_type(new_dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(new_dr7, i)) { + iobpt |= HF_IOBPT_MASK; + } + } + } else { + for (i = 0; i < DR7_MAX_BP; i++) { + hw_breakpoint_remove(env, i); + } + env->dr[7] = new_dr7; + for (i = 0; i < DR7_MAX_BP; i++) { + iobpt |= hw_breakpoint_insert(env, i); + } + } + + env->hflags = (env->hflags & ~HF_IOBPT_MASK) | iobpt; +} + +static bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update) +{ + target_ulong dr6; + int reg; + bool hit_enabled = false; + + dr6 = env->dr[6] & ~0xf; + for (reg = 0; reg < DR7_MAX_BP; reg++) { + bool bp_match = false; + bool wp_match = false; + + switch (hw_breakpoint_type(env->dr[7], reg)) { + case DR7_TYPE_BP_INST: + if (env->dr[reg] == env->eip) { + bp_match = true; + } + break; + case DR7_TYPE_DATA_WR: + case DR7_TYPE_DATA_RW: + if (env->cpu_watchpoint[reg] && + env->cpu_watchpoint[reg]->flags & BP_WATCHPOINT_HIT) { + wp_match = true; + } + break; + case DR7_TYPE_IO_RW: + break; + } + if (bp_match || wp_match) { + dr6 |= 1 << reg; + if (hw_breakpoint_enabled(env->dr[7], reg)) { + hit_enabled = true; + } + } + } + + if (hit_enabled || force_dr6_update) { + env->dr[6] = dr6; + } + + return hit_enabled; +} + +void breakpoint_handler(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + CPUBreakpoint *bp; + + if (cs->watchpoint_hit) { + if (cs->watchpoint_hit->flags & BP_CPU) { + cs->watchpoint_hit = NULL; + if (check_hw_breakpoints(env, false)) { + raise_exception(env, EXCP01_DB); + } else { + cpu_loop_exit_noexc(cs); + } + } + } else { + QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { + if (bp->pc == env->eip) { + if (bp->flags & BP_CPU) { + check_hw_breakpoints(env, true); + raise_exception(env, EXCP01_DB); + } + break; + } + } + } +} +#endif + +void helper_single_step(CPUX86State *env) +{ +#ifndef CONFIG_USER_ONLY + check_hw_breakpoints(env, true); + env->dr[6] |= DR6_BS; +#endif + raise_exception(env, EXCP01_DB); +} + +void helper_rechecking_single_step(CPUX86State *env) +{ + if ((env->eflags & TF_MASK) != 0) { + helper_single_step(env); + } +} + +void helper_set_dr(CPUX86State *env, int reg, target_ulong t0) +{ +#ifndef CONFIG_USER_ONLY + switch (reg) { + case 0: case 1: case 2: case 3: + if (hw_breakpoint_enabled(env->dr[7], reg) + && hw_breakpoint_type(env->dr[7], reg) != DR7_TYPE_IO_RW) { + hw_breakpoint_remove(env, reg); + env->dr[reg] = t0; + hw_breakpoint_insert(env, reg); + } else { + env->dr[reg] = t0; + } + return; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 6: + env->dr[6] = t0 | DR6_FIXED_1; + return; + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } + /* fallthru */ + case 7: + cpu_x86_update_dr7(env, t0); + return; + } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); +#endif +} + +target_ulong helper_get_dr(CPUX86State *env, int reg) +{ + switch (reg) { + case 0: case 1: case 2: case 3: case 6: case 7: + return env->dr[reg]; + case 4: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[6]; + } + case 5: + if (env->cr[4] & CR4_DE_MASK) { + break; + } else { + return env->dr[7]; + } + } + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); +} + +/* Check if Port I/O is trapped by a breakpoint. */ +void helper_bpt_io(CPUX86State *env, uint32_t port, + uint32_t size, target_ulong next_eip) +{ +#ifndef CONFIG_USER_ONLY + target_ulong dr7 = env->dr[7]; + int i, hit = 0; + + for (i = 0; i < DR7_MAX_BP; ++i) { + if (hw_breakpoint_type(dr7, i) == DR7_TYPE_IO_RW + && hw_breakpoint_enabled(dr7, i)) { + int bpt_len = hw_breakpoint_len(dr7, i); + if (port + size - 1 >= env->dr[i] + && port <= env->dr[i] + bpt_len - 1) { + hit |= 1 << i; + } + } + } + + if (hit) { + env->dr[6] = (env->dr[6] & ~0xf) | hit; + env->eip = next_eip; + raise_exception(env, EXCP01_DB); + } +#endif +} diff --git a/target/i386/tcg/cc_helper.c b/target/i386/tcg/cc_helper.c new file mode 100644 index 0000000000..924dd3cd57 --- /dev/null +++ b/target/i386/tcg/cc_helper.c @@ -0,0 +1,388 @@ +/* + * x86 condition code helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" + +const uint8_t parity_table[256] = { + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, + 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, +}; + +#define SHIFT 0 +#include "cc_helper_template.h" +#undef SHIFT + +#define SHIFT 1 +#include "cc_helper_template.h" +#undef SHIFT + +#define SHIFT 2 +#include "cc_helper_template.h" +#undef SHIFT + +#ifdef TARGET_X86_64 + +#define SHIFT 3 +#include "cc_helper_template.h" +#undef SHIFT + +#endif + +static target_ulong compute_all_adcx(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + return (src1 & ~CC_C) | (dst * CC_C); +} + +static target_ulong compute_all_adox(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + return (src1 & ~CC_O) | (src2 * CC_O); +} + +static target_ulong compute_all_adcox(target_ulong dst, target_ulong src1, + target_ulong src2) +{ + return (src1 & ~(CC_C | CC_O)) | (dst * CC_C) | (src2 * CC_O); +} + +target_ulong helper_cc_compute_all(target_ulong dst, target_ulong src1, + target_ulong src2, int op) +{ + switch (op) { + default: /* should never happen */ + return 0; + + case CC_OP_EFLAGS: + return src1; + case CC_OP_CLR: + return CC_Z | CC_P; + case CC_OP_POPCNT: + return src1 ? 0 : CC_Z; + + case CC_OP_MULB: + return compute_all_mulb(dst, src1); + case CC_OP_MULW: + return compute_all_mulw(dst, src1); + case CC_OP_MULL: + return compute_all_mull(dst, src1); + + case CC_OP_ADDB: + return compute_all_addb(dst, src1); + case CC_OP_ADDW: + return compute_all_addw(dst, src1); + case CC_OP_ADDL: + return compute_all_addl(dst, src1); + + case CC_OP_ADCB: + return compute_all_adcb(dst, src1, src2); + case CC_OP_ADCW: + return compute_all_adcw(dst, src1, src2); + case CC_OP_ADCL: + return compute_all_adcl(dst, src1, src2); + + case CC_OP_SUBB: + return compute_all_subb(dst, src1); + case CC_OP_SUBW: + return compute_all_subw(dst, src1); + case CC_OP_SUBL: + return compute_all_subl(dst, src1); + + case CC_OP_SBBB: + return compute_all_sbbb(dst, src1, src2); + case CC_OP_SBBW: + return compute_all_sbbw(dst, src1, src2); + case CC_OP_SBBL: + return compute_all_sbbl(dst, src1, src2); + + case CC_OP_LOGICB: + return compute_all_logicb(dst, src1); + case CC_OP_LOGICW: + return compute_all_logicw(dst, src1); + case CC_OP_LOGICL: + return compute_all_logicl(dst, src1); + + case CC_OP_INCB: + return compute_all_incb(dst, src1); + case CC_OP_INCW: + return compute_all_incw(dst, src1); + case CC_OP_INCL: + return compute_all_incl(dst, src1); + + case CC_OP_DECB: + return compute_all_decb(dst, src1); + case CC_OP_DECW: + return compute_all_decw(dst, src1); + case CC_OP_DECL: + return compute_all_decl(dst, src1); + + case CC_OP_SHLB: + return compute_all_shlb(dst, src1); + case CC_OP_SHLW: + return compute_all_shlw(dst, src1); + case CC_OP_SHLL: + return compute_all_shll(dst, src1); + + case CC_OP_SARB: + return compute_all_sarb(dst, src1); + case CC_OP_SARW: + return compute_all_sarw(dst, src1); + case CC_OP_SARL: + return compute_all_sarl(dst, src1); + + case CC_OP_BMILGB: + return compute_all_bmilgb(dst, src1); + case CC_OP_BMILGW: + return compute_all_bmilgw(dst, src1); + case CC_OP_BMILGL: + return compute_all_bmilgl(dst, src1); + + case CC_OP_ADCX: + return compute_all_adcx(dst, src1, src2); + case CC_OP_ADOX: + return compute_all_adox(dst, src1, src2); + case CC_OP_ADCOX: + return compute_all_adcox(dst, src1, src2); + +#ifdef TARGET_X86_64 + case CC_OP_MULQ: + return compute_all_mulq(dst, src1); + case CC_OP_ADDQ: + return compute_all_addq(dst, src1); + case CC_OP_ADCQ: + return compute_all_adcq(dst, src1, src2); + case CC_OP_SUBQ: + return compute_all_subq(dst, src1); + case CC_OP_SBBQ: + return compute_all_sbbq(dst, src1, src2); + case CC_OP_LOGICQ: + return compute_all_logicq(dst, src1); + case CC_OP_INCQ: + return compute_all_incq(dst, src1); + case CC_OP_DECQ: + return compute_all_decq(dst, src1); + case CC_OP_SHLQ: + return compute_all_shlq(dst, src1); + case CC_OP_SARQ: + return compute_all_sarq(dst, src1); + case CC_OP_BMILGQ: + return compute_all_bmilgq(dst, src1); +#endif + } +} + +uint32_t cpu_cc_compute_all(CPUX86State *env, int op) +{ + return helper_cc_compute_all(CC_DST, CC_SRC, CC_SRC2, op); +} + +target_ulong helper_cc_compute_c(target_ulong dst, target_ulong src1, + target_ulong src2, int op) +{ + switch (op) { + default: /* should never happen */ + case CC_OP_LOGICB: + case CC_OP_LOGICW: + case CC_OP_LOGICL: + case CC_OP_LOGICQ: + case CC_OP_CLR: + case CC_OP_POPCNT: + return 0; + + case CC_OP_EFLAGS: + case CC_OP_SARB: + case CC_OP_SARW: + case CC_OP_SARL: + case CC_OP_SARQ: + case CC_OP_ADOX: + return src1 & 1; + + case CC_OP_INCB: + case CC_OP_INCW: + case CC_OP_INCL: + case CC_OP_INCQ: + case CC_OP_DECB: + case CC_OP_DECW: + case CC_OP_DECL: + case CC_OP_DECQ: + return src1; + + case CC_OP_MULB: + case CC_OP_MULW: + case CC_OP_MULL: + case CC_OP_MULQ: + return src1 != 0; + + case CC_OP_ADCX: + case CC_OP_ADCOX: + return dst; + + case CC_OP_ADDB: + return compute_c_addb(dst, src1); + case CC_OP_ADDW: + return compute_c_addw(dst, src1); + case CC_OP_ADDL: + return compute_c_addl(dst, src1); + + case CC_OP_ADCB: + return compute_c_adcb(dst, src1, src2); + case CC_OP_ADCW: + return compute_c_adcw(dst, src1, src2); + case CC_OP_ADCL: + return compute_c_adcl(dst, src1, src2); + + case CC_OP_SUBB: + return compute_c_subb(dst, src1); + case CC_OP_SUBW: + return compute_c_subw(dst, src1); + case CC_OP_SUBL: + return compute_c_subl(dst, src1); + + case CC_OP_SBBB: + return compute_c_sbbb(dst, src1, src2); + case CC_OP_SBBW: + return compute_c_sbbw(dst, src1, src2); + case CC_OP_SBBL: + return compute_c_sbbl(dst, src1, src2); + + case CC_OP_SHLB: + return compute_c_shlb(dst, src1); + case CC_OP_SHLW: + return compute_c_shlw(dst, src1); + case CC_OP_SHLL: + return compute_c_shll(dst, src1); + + case CC_OP_BMILGB: + return compute_c_bmilgb(dst, src1); + case CC_OP_BMILGW: + return compute_c_bmilgw(dst, src1); + case CC_OP_BMILGL: + return compute_c_bmilgl(dst, src1); + +#ifdef TARGET_X86_64 + case CC_OP_ADDQ: + return compute_c_addq(dst, src1); + case CC_OP_ADCQ: + return compute_c_adcq(dst, src1, src2); + case CC_OP_SUBQ: + return compute_c_subq(dst, src1); + case CC_OP_SBBQ: + return compute_c_sbbq(dst, src1, src2); + case CC_OP_SHLQ: + return compute_c_shlq(dst, src1); + case CC_OP_BMILGQ: + return compute_c_bmilgq(dst, src1); +#endif + } +} + +void helper_write_eflags(CPUX86State *env, target_ulong t0, + uint32_t update_mask) +{ + cpu_load_eflags(env, t0, update_mask); +} + +target_ulong helper_read_eflags(CPUX86State *env) +{ + uint32_t eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + eflags |= (env->df & DF_MASK); + eflags |= env->eflags & ~(VM_MASK | RF_MASK); + return eflags; +} + +void helper_clts(CPUX86State *env) +{ + env->cr[0] &= ~CR0_TS_MASK; + env->hflags &= ~HF_TS_MASK; +} + +void helper_reset_rf(CPUX86State *env) +{ + env->eflags &= ~RF_MASK; +} + +void helper_cli(CPUX86State *env) +{ + env->eflags &= ~IF_MASK; +} + +void helper_sti(CPUX86State *env) +{ + env->eflags |= IF_MASK; +} + +void helper_clac(CPUX86State *env) +{ + env->eflags &= ~AC_MASK; +} + +void helper_stac(CPUX86State *env) +{ + env->eflags |= AC_MASK; +} + +#if 0 +/* vm86plus instructions */ +void helper_cli_vm(CPUX86State *env) +{ + env->eflags &= ~VIF_MASK; +} + +void helper_sti_vm(CPUX86State *env) +{ + env->eflags |= VIF_MASK; + if (env->eflags & VIP_MASK) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } +} +#endif diff --git a/target/i386/tcg/cc_helper_template.h b/target/i386/tcg/cc_helper_template.h new file mode 100644 index 0000000000..bb611feb04 --- /dev/null +++ b/target/i386/tcg/cc_helper_template.h @@ -0,0 +1,242 @@ +/* + * x86 condition code helpers + * + * Copyright (c) 2008 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#define DATA_BITS (1 << (3 + SHIFT)) + +#if DATA_BITS == 8 +#define SUFFIX b +#define DATA_TYPE uint8_t +#elif DATA_BITS == 16 +#define SUFFIX w +#define DATA_TYPE uint16_t +#elif DATA_BITS == 32 +#define SUFFIX l +#define DATA_TYPE uint32_t +#elif DATA_BITS == 64 +#define SUFFIX q +#define DATA_TYPE uint64_t +#else +#error unhandled operand size +#endif + +#define SIGN_MASK (((DATA_TYPE)1) << (DATA_BITS - 1)) + +/* dynamic flags computation */ + +static int glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src2 = dst - src1; + + cf = dst < src1; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return dst < src1; +} + +static int glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src2 = dst - src1 - src3; + + cf = (src3 ? dst <= src1 : dst < src1); + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, + DATA_TYPE src3) +{ + return src3 ? dst <= src1 : dst < src1; +} + +static int glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src1 = dst + src2; + + cf = src1 < src2; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) +{ + DATA_TYPE src1 = dst + src2; + + return src1 < src2; +} + +static int glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src1 = dst + src2 + src3; + + cf = (src3 ? src1 <= src2 : src1 < src2); + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & 0x10; + zf = (dst == 0) << 6; + sf = lshift(dst, 8 - DATA_BITS) & 0x80; + of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, + DATA_TYPE src3) +{ + DATA_TYPE src1 = dst + src2 + src3; + + return (src3 ? src1 <= src2 : src1 < src2); +} + +static int glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = 0; + pf = parity_table[(uint8_t)dst]; + af = 0; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src2; + + cf = src1; + src1 = dst - 1; + src2 = 1; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK) * CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + DATA_TYPE src2; + + cf = src1; + src1 = dst + 1; + src2 = 1; + pf = parity_table[(uint8_t)dst]; + af = (dst ^ src1 ^ src2) & CC_A; + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = (dst == SIGN_MASK - 1) * CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = (src1 >> (DATA_BITS - 1)) & CC_C; + pf = parity_table[(uint8_t)dst]; + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_shl, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return (src1 >> (DATA_BITS - 1)) & CC_C; +} + +static int glue(compute_all_sar, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = src1 & 1; + pf = parity_table[(uint8_t)dst]; + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + /* of is defined iff shift count == 1 */ + of = lshift(src1 ^ dst, 12 - DATA_BITS) & CC_O; + return cf | pf | af | zf | sf | of; +} + +/* NOTE: we compute the flags like the P4. On olders CPUs, only OF and + CF are modified and it is slower to do that. Note as well that we + don't truncate SRC1 for computing carry to DATA_TYPE. */ +static int glue(compute_all_mul, SUFFIX)(DATA_TYPE dst, target_long src1) +{ + int cf, pf, af, zf, sf, of; + + cf = (src1 != 0); + pf = parity_table[(uint8_t)dst]; + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = cf * CC_O; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_all_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + int cf, pf, af, zf, sf, of; + + cf = (src1 == 0); + pf = 0; /* undefined */ + af = 0; /* undefined */ + zf = (dst == 0) * CC_Z; + sf = lshift(dst, 8 - DATA_BITS) & CC_S; + of = 0; + return cf | pf | af | zf | sf | of; +} + +static int glue(compute_c_bmilg, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + return src1 == 0; +} + +#undef DATA_BITS +#undef SIGN_MASK +#undef DATA_TYPE +#undef DATA_MASK +#undef SUFFIX diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c new file mode 100644 index 0000000000..191471749f --- /dev/null +++ b/target/i386/tcg/excp_helper.c @@ -0,0 +1,702 @@ +/* + * x86 exception helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qemu/log.h" +#include "sysemu/runstate.h" +#include "exec/helper-proto.h" + +void helper_raise_interrupt(CPUX86State *env, int intno, int next_eip_addend) +{ + raise_interrupt(env, intno, 1, 0, next_eip_addend); +} + +void helper_raise_exception(CPUX86State *env, int exception_index) +{ + raise_exception(env, exception_index); +} + +/* + * Check nested exceptions and change to double or triple fault if + * needed. It should only be called, if this is not an interrupt. + * Returns the new exception number. + */ +static int check_exception(CPUX86State *env, int intno, int *error_code, + uintptr_t retaddr) +{ + int first_contributory = env->old_exception == 0 || + (env->old_exception >= 10 && + env->old_exception <= 13); + int second_contributory = intno == 0 || + (intno >= 10 && intno <= 13); + + qemu_log_mask(CPU_LOG_INT, "check_exception old: 0x%x new 0x%x\n", + env->old_exception, intno); + +#if !defined(CONFIG_USER_ONLY) + if (env->old_exception == EXCP08_DBLE) { + if (env->hflags & HF_GUEST_MASK) { + cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */ + } + + qemu_log_mask(CPU_LOG_RESET, "Triple fault\n"); + + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + return EXCP_HLT; + } +#endif + + if ((first_contributory && second_contributory) + || (env->old_exception == EXCP0E_PAGE && + (second_contributory || (intno == EXCP0E_PAGE)))) { + intno = EXCP08_DBLE; + *error_code = 0; + } + + if (second_contributory || (intno == EXCP0E_PAGE) || + (intno == EXCP08_DBLE)) { + env->old_exception = intno; + } + + return intno; +} + +/* + * Signal an interruption. It is executed in the main CPU loop. + * is_int is TRUE if coming from the int instruction. next_eip is the + * env->eip value AFTER the interrupt instruction. It is only relevant if + * is_int is TRUE. + */ +static void QEMU_NORETURN raise_interrupt2(CPUX86State *env, int intno, + int is_int, int error_code, + int next_eip_addend, + uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + if (!is_int) { + cpu_svm_check_intercept_param(env, SVM_EXIT_EXCP_BASE + intno, + error_code, retaddr); + intno = check_exception(env, intno, &error_code, retaddr); + } else { + cpu_svm_check_intercept_param(env, SVM_EXIT_SWINT, 0, retaddr); + } + + cs->exception_index = intno; + env->error_code = error_code; + env->exception_is_int = is_int; + env->exception_next_eip = env->eip + next_eip_addend; + cpu_loop_exit_restore(cs, retaddr); +} + +/* shortcuts to generate exceptions */ + +void QEMU_NORETURN raise_interrupt(CPUX86State *env, int intno, int is_int, + int error_code, int next_eip_addend) +{ + raise_interrupt2(env, intno, is_int, error_code, next_eip_addend, 0); +} + +void raise_exception_err(CPUX86State *env, int exception_index, + int error_code) +{ + raise_interrupt2(env, exception_index, 0, error_code, 0, 0); +} + +void raise_exception_err_ra(CPUX86State *env, int exception_index, + int error_code, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, error_code, 0, retaddr); +} + +void raise_exception(CPUX86State *env, int exception_index) +{ + raise_interrupt2(env, exception_index, 0, 0, 0, 0); +} + +void raise_exception_ra(CPUX86State *env, int exception_index, uintptr_t retaddr) +{ + raise_interrupt2(env, exception_index, 0, 0, 0, retaddr); +} + +#if !defined(CONFIG_USER_ONLY) +static hwaddr get_hphys(CPUState *cs, hwaddr gphys, MMUAccessType access_type, + int *prot) +{ + CPUX86State *env = &X86_CPU(cs)->env; + uint64_t rsvd_mask = PG_HI_RSVD_MASK; + uint64_t ptep, pte; + uint64_t exit_info_1 = 0; + target_ulong pde_addr, pte_addr; + uint32_t page_offset; + int page_size; + + if (likely(!(env->hflags2 & HF2_NPT_MASK))) { + return gphys; + } + + if (!(env->nested_pg_mode & SVM_NPT_NXE)) { + rsvd_mask |= PG_NX_MASK; + } + + if (env->nested_pg_mode & SVM_NPT_PAE) { + uint64_t pde, pdpe; + target_ulong pdpe_addr; + +#ifdef TARGET_X86_64 + if (env->nested_pg_mode & SVM_NPT_LMA) { + uint64_t pml5e; + uint64_t pml4e_addr, pml4e; + + pml5e = env->nested_cr3; + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + + pml4e_addr = (pml5e & PG_ADDRESS_MASK) + + (((gphys >> 39) & 0x1ff) << 3); + pml4e = x86_ldq_phys(cs, pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pml4e & (rsvd_mask | PG_PSE_MASK)) { + goto do_fault_rsvd; + } + if (!(pml4e & PG_ACCESSED_MASK)) { + pml4e |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); + } + ptep &= pml4e ^ PG_NX_MASK; + pdpe_addr = (pml4e & PG_ADDRESS_MASK) + + (((gphys >> 30) & 0x1ff) << 3); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pdpe & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pdpe ^ PG_NX_MASK; + if (!(pdpe & PG_ACCESSED_MASK)) { + pdpe |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); + } + if (pdpe & PG_PSE_MASK) { + /* 1 GB page */ + page_size = 1024 * 1024 * 1024; + pte_addr = pdpe_addr; + pte = pdpe; + goto do_check_protect; + } + } else +#endif + { + pdpe_addr = (env->nested_cr3 & ~0x1f) + ((gphys >> 27) & 0x18); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + rsvd_mask |= PG_HI_USER_MASK; + if (pdpe & (rsvd_mask | PG_NX_MASK)) { + goto do_fault_rsvd; + } + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pde_addr = (pdpe & PG_ADDRESS_MASK) + (((gphys >> 21) & 0x1ff) << 3); + pde = x86_ldq_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pde & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pde ^ PG_NX_MASK; + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + pte_addr = pde_addr; + pte = pde; + goto do_check_protect; + } + /* 4 KB page */ + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + pte_addr = (pde & PG_ADDRESS_MASK) + (((gphys >> 12) & 0x1ff) << 3); + pte = x86_ldq_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + /* combine pde and pte nx, user and rw protections */ + ptep &= pte ^ PG_NX_MASK; + page_size = 4096; + } else { + uint32_t pde; + + /* page directory entry */ + pde_addr = (env->nested_cr3 & ~0xfff) + ((gphys >> 20) & 0xffc); + pde = x86_ldl_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + ptep = pde | PG_NX_MASK; + + /* if host cr4 PSE bit is set, then we use a 4MB page */ + if ((pde & PG_PSE_MASK) && (env->nested_pg_mode & SVM_NPT_PSE)) { + page_size = 4096 * 1024; + pte_addr = pde_addr; + + /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. + * Leave bits 20-13 in place for setting accessed/dirty bits below. + */ + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); + rsvd_mask = 0x200000; + goto do_check_protect_pse36; + } + + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + + /* page directory entry */ + pte_addr = (pde & ~0xfff) + ((gphys >> 10) & 0xffc); + pte = x86_ldl_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + /* combine pde and pte user and rw protections */ + ptep &= pte | PG_NX_MASK; + page_size = 4096; + rsvd_mask = 0; + } + + do_check_protect: + rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; + do_check_protect_pse36: + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + ptep ^= PG_NX_MASK; + + if (!(ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + if (ptep & PG_NX_MASK) { + if (access_type == MMU_INST_FETCH) { + goto do_fault_protect; + } + *prot &= ~PAGE_EXEC; + } + if (!(ptep & PG_RW_MASK)) { + if (access_type == MMU_DATA_STORE) { + goto do_fault_protect; + } + *prot &= ~PAGE_WRITE; + } + + pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = gphys & (page_size - 1); + return pte + page_offset; + + do_fault_rsvd: + exit_info_1 |= SVM_NPTEXIT_RSVD; + do_fault_protect: + exit_info_1 |= SVM_NPTEXIT_P; + do_fault: + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + gphys); + exit_info_1 |= SVM_NPTEXIT_US; + if (access_type == MMU_DATA_STORE) { + exit_info_1 |= SVM_NPTEXIT_RW; + } else if (access_type == MMU_INST_FETCH) { + exit_info_1 |= SVM_NPTEXIT_ID; + } + if (prot) { + exit_info_1 |= SVM_NPTEXIT_GPA; + } else { /* page table access */ + exit_info_1 |= SVM_NPTEXIT_GPT; + } + cpu_vmexit(env, SVM_EXIT_NPF, exit_info_1, env->retaddr); +} + +/* return value: + * -1 = cannot handle fault + * 0 = nothing more to do + * 1 = generate PF fault + */ +static int handle_mmu_fault(CPUState *cs, vaddr addr, int size, + int is_write1, int mmu_idx) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + uint64_t ptep, pte; + int32_t a20_mask; + target_ulong pde_addr, pte_addr; + int error_code = 0; + int is_dirty, prot, page_size, is_write, is_user; + hwaddr paddr; + uint64_t rsvd_mask = PG_HI_RSVD_MASK; + uint32_t page_offset; + target_ulong vaddr; + + is_user = mmu_idx == MMU_USER_IDX; +#if defined(DEBUG_MMU) + printf("MMU fault: addr=%" VADDR_PRIx " w=%d u=%d eip=" TARGET_FMT_lx "\n", + addr, is_write1, is_user, env->eip); +#endif + is_write = is_write1 & 1; + + a20_mask = x86_get_a20_mask(env); + if (!(env->cr[0] & CR0_PG_MASK)) { + pte = addr; +#ifdef TARGET_X86_64 + if (!(env->hflags & HF_LMA_MASK)) { + /* Without long mode we can only address 32bits in real mode */ + pte = (uint32_t)pte; + } +#endif + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + page_size = 4096; + goto do_mapping; + } + + if (!(env->efer & MSR_EFER_NXE)) { + rsvd_mask |= PG_NX_MASK; + } + + if (env->cr[4] & CR4_PAE_MASK) { + uint64_t pde, pdpe; + target_ulong pdpe_addr; + +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + bool la57 = env->cr[4] & CR4_LA57_MASK; + uint64_t pml5e_addr, pml5e; + uint64_t pml4e_addr, pml4e; + int32_t sext; + + /* test virtual address sign extension */ + sext = la57 ? (int64_t)addr >> 56 : (int64_t)addr >> 47; + if (sext != 0 && sext != -1) { + env->error_code = 0; + cs->exception_index = EXCP0D_GPF; + return 1; + } + + if (la57) { + pml5e_addr = ((env->cr[3] & ~0xfff) + + (((addr >> 48) & 0x1ff) << 3)) & a20_mask; + pml5e_addr = get_hphys(cs, pml5e_addr, MMU_DATA_STORE, NULL); + pml5e = x86_ldq_phys(cs, pml5e_addr); + if (!(pml5e & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pml5e & (rsvd_mask | PG_PSE_MASK)) { + goto do_fault_rsvd; + } + if (!(pml5e & PG_ACCESSED_MASK)) { + pml5e |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pml5e_addr, pml5e); + } + ptep = pml5e ^ PG_NX_MASK; + } else { + pml5e = env->cr[3]; + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pml4e_addr = ((pml5e & PG_ADDRESS_MASK) + + (((addr >> 39) & 0x1ff) << 3)) & a20_mask; + pml4e_addr = get_hphys(cs, pml4e_addr, MMU_DATA_STORE, false); + pml4e = x86_ldq_phys(cs, pml4e_addr); + if (!(pml4e & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pml4e & (rsvd_mask | PG_PSE_MASK)) { + goto do_fault_rsvd; + } + if (!(pml4e & PG_ACCESSED_MASK)) { + pml4e |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pml4e_addr, pml4e); + } + ptep &= pml4e ^ PG_NX_MASK; + pdpe_addr = ((pml4e & PG_ADDRESS_MASK) + (((addr >> 30) & 0x1ff) << 3)) & + a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, NULL); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pdpe & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pdpe ^ PG_NX_MASK; + if (!(pdpe & PG_ACCESSED_MASK)) { + pdpe |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pdpe_addr, pdpe); + } + if (pdpe & PG_PSE_MASK) { + /* 1 GB page */ + page_size = 1024 * 1024 * 1024; + pte_addr = pdpe_addr; + pte = pdpe; + goto do_check_protect; + } + } else +#endif + { + /* XXX: load them when cr3 is loaded ? */ + pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 27) & 0x18)) & + a20_mask; + pdpe_addr = get_hphys(cs, pdpe_addr, MMU_DATA_STORE, false); + pdpe = x86_ldq_phys(cs, pdpe_addr); + if (!(pdpe & PG_PRESENT_MASK)) { + goto do_fault; + } + rsvd_mask |= PG_HI_USER_MASK; + if (pdpe & (rsvd_mask | PG_NX_MASK)) { + goto do_fault_rsvd; + } + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; + } + + pde_addr = ((pdpe & PG_ADDRESS_MASK) + (((addr >> 21) & 0x1ff) << 3)) & + a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); + pde = x86_ldq_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pde & rsvd_mask) { + goto do_fault_rsvd; + } + ptep &= pde ^ PG_NX_MASK; + if (pde & PG_PSE_MASK) { + /* 2 MB page */ + page_size = 2048 * 1024; + pte_addr = pde_addr; + pte = pde; + goto do_check_protect; + } + /* 4 KB page */ + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + pte_addr = ((pde & PG_ADDRESS_MASK) + (((addr >> 12) & 0x1ff) << 3)) & + a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); + pte = x86_ldq_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + /* combine pde and pte nx, user and rw protections */ + ptep &= pte ^ PG_NX_MASK; + page_size = 4096; + } else { + uint32_t pde; + + /* page directory entry */ + pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & 0xffc)) & + a20_mask; + pde_addr = get_hphys(cs, pde_addr, MMU_DATA_STORE, NULL); + pde = x86_ldl_phys(cs, pde_addr); + if (!(pde & PG_PRESENT_MASK)) { + goto do_fault; + } + ptep = pde | PG_NX_MASK; + + /* if PSE bit is set, then we use a 4MB page */ + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { + page_size = 4096 * 1024; + pte_addr = pde_addr; + + /* Bits 20-13 provide bits 39-32 of the address, bit 21 is reserved. + * Leave bits 20-13 in place for setting accessed/dirty bits below. + */ + pte = pde | ((pde & 0x1fe000LL) << (32 - 13)); + rsvd_mask = 0x200000; + goto do_check_protect_pse36; + } + + if (!(pde & PG_ACCESSED_MASK)) { + pde |= PG_ACCESSED_MASK; + x86_stl_phys_notdirty(cs, pde_addr, pde); + } + + /* page directory entry */ + pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & + a20_mask; + pte_addr = get_hphys(cs, pte_addr, MMU_DATA_STORE, NULL); + pte = x86_ldl_phys(cs, pte_addr); + if (!(pte & PG_PRESENT_MASK)) { + goto do_fault; + } + /* combine pde and pte user and rw protections */ + ptep &= pte | PG_NX_MASK; + page_size = 4096; + rsvd_mask = 0; + } + +do_check_protect: + rsvd_mask |= (page_size - 1) & PG_ADDRESS_MASK & ~PG_PSE_PAT_MASK; +do_check_protect_pse36: + if (pte & rsvd_mask) { + goto do_fault_rsvd; + } + ptep ^= PG_NX_MASK; + + /* can the page can be put in the TLB? prot will tell us */ + if (is_user && !(ptep & PG_USER_MASK)) { + goto do_fault_protect; + } + + prot = 0; + if (mmu_idx != MMU_KSMAP_IDX || !(ptep & PG_USER_MASK)) { + prot |= PAGE_READ; + if ((ptep & PG_RW_MASK) || (!is_user && !(env->cr[0] & CR0_WP_MASK))) { + prot |= PAGE_WRITE; + } + } + if (!(ptep & PG_NX_MASK) && + (mmu_idx == MMU_USER_IDX || + !((env->cr[4] & CR4_SMEP_MASK) && (ptep & PG_USER_MASK)))) { + prot |= PAGE_EXEC; + } + if ((env->cr[4] & CR4_PKE_MASK) && (env->hflags & HF_LMA_MASK) && + (ptep & PG_USER_MASK) && env->pkru) { + uint32_t pk = (pte & PG_PKRU_MASK) >> PG_PKRU_BIT; + uint32_t pkru_ad = (env->pkru >> pk * 2) & 1; + uint32_t pkru_wd = (env->pkru >> pk * 2) & 2; + uint32_t pkru_prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + + if (pkru_ad) { + pkru_prot &= ~(PAGE_READ | PAGE_WRITE); + } else if (pkru_wd && (is_user || env->cr[0] & CR0_WP_MASK)) { + pkru_prot &= ~PAGE_WRITE; + } + + prot &= pkru_prot; + if ((pkru_prot & (1 << is_write1)) == 0) { + assert(is_write1 != 2); + error_code |= PG_ERROR_PK_MASK; + goto do_fault_protect; + } + } + + if ((prot & (1 << is_write1)) == 0) { + goto do_fault_protect; + } + + /* yes, it can! */ + is_dirty = is_write && !(pte & PG_DIRTY_MASK); + if (!(pte & PG_ACCESSED_MASK) || is_dirty) { + pte |= PG_ACCESSED_MASK; + if (is_dirty) { + pte |= PG_DIRTY_MASK; + } + x86_stl_phys_notdirty(cs, pte_addr, pte); + } + + if (!(pte & PG_DIRTY_MASK)) { + /* only set write access if already dirty... otherwise wait + for dirty access */ + assert(!is_write); + prot &= ~PAGE_WRITE; + } + + do_mapping: + pte = pte & a20_mask; + + /* align to page_size */ + pte &= PG_ADDRESS_MASK & ~(page_size - 1); + page_offset = addr & (page_size - 1); + paddr = get_hphys(cs, pte + page_offset, is_write1, &prot); + + /* Even if 4MB pages, we map only one 4KB page in the cache to + avoid filling it too fast */ + vaddr = addr & TARGET_PAGE_MASK; + paddr &= TARGET_PAGE_MASK; + + assert(prot & (1 << is_write1)); + tlb_set_page_with_attrs(cs, vaddr, paddr, cpu_get_mem_attrs(env), + prot, mmu_idx, page_size); + return 0; + do_fault_rsvd: + error_code |= PG_ERROR_RSVD_MASK; + do_fault_protect: + error_code |= PG_ERROR_P_MASK; + do_fault: + error_code |= (is_write << PG_ERROR_W_BIT); + if (is_user) + error_code |= PG_ERROR_U_MASK; + if (is_write1 == 2 && + (((env->efer & MSR_EFER_NXE) && + (env->cr[4] & CR4_PAE_MASK)) || + (env->cr[4] & CR4_SMEP_MASK))) + error_code |= PG_ERROR_I_D_MASK; + if (env->intercept_exceptions & (1 << EXCP0E_PAGE)) { + /* cr2 is not modified in case of exceptions */ + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + addr); + } else { + env->cr[2] = addr; + } + env->error_code = error_code; + cs->exception_index = EXCP0E_PAGE; + return 1; +} +#endif + +bool x86_cpu_tlb_fill(CPUState *cs, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + +#ifdef CONFIG_USER_ONLY + /* user mode only emulation */ + env->cr[2] = addr; + env->error_code = (access_type == MMU_DATA_STORE) << PG_ERROR_W_BIT; + env->error_code |= PG_ERROR_U_MASK; + cs->exception_index = EXCP0E_PAGE; + env->exception_is_int = 0; + env->exception_next_eip = -1; + cpu_loop_exit_restore(cs, retaddr); +#else + env->retaddr = retaddr; + if (handle_mmu_fault(cs, addr, size, access_type, mmu_idx)) { + /* FIXME: On error in get_hphys we have already jumped out. */ + g_assert(!probe); + raise_exception_err_ra(env, cs->exception_index, + env->error_code, retaddr); + } + return true; +#endif +} diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c new file mode 100644 index 0000000000..03b35443a6 --- /dev/null +++ b/target/i386/tcg/fpu_helper.c @@ -0,0 +1,3042 @@ +/* + * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "fpu/softfloat.h" +#include "fpu/softfloat-macros.h" + +#ifdef CONFIG_SOFTMMU +#include "hw/irq.h" +#endif + +#define FPU_RC_MASK 0xc00 +#define FPU_RC_NEAR 0x000 +#define FPU_RC_DOWN 0x400 +#define FPU_RC_UP 0x800 +#define FPU_RC_CHOP 0xc00 + +#define MAXTAN 9223372036854775808.0 + +/* the following deal with x86 long double-precision numbers */ +#define MAXEXPD 0x7fff +#define EXPBIAS 16383 +#define EXPD(fp) (fp.l.upper & 0x7fff) +#define SIGND(fp) ((fp.l.upper) & 0x8000) +#define MANTD(fp) (fp.l.lower) +#define BIASEXPONENT(fp) fp.l.upper = (fp.l.upper & ~(0x7fff)) | EXPBIAS + +#define FPUS_IE (1 << 0) +#define FPUS_DE (1 << 1) +#define FPUS_ZE (1 << 2) +#define FPUS_OE (1 << 3) +#define FPUS_UE (1 << 4) +#define FPUS_PE (1 << 5) +#define FPUS_SF (1 << 6) +#define FPUS_SE (1 << 7) +#define FPUS_B (1 << 15) + +#define FPUC_EM 0x3f + +#define floatx80_lg2 make_floatx80(0x3ffd, 0x9a209a84fbcff799LL) +#define floatx80_lg2_d make_floatx80(0x3ffd, 0x9a209a84fbcff798LL) +#define floatx80_l2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcLL) +#define floatx80_l2e_d make_floatx80(0x3fff, 0xb8aa3b295c17f0bbLL) +#define floatx80_l2t make_floatx80(0x4000, 0xd49a784bcd1b8afeLL) +#define floatx80_l2t_u make_floatx80(0x4000, 0xd49a784bcd1b8affLL) +#define floatx80_ln2_d make_floatx80(0x3ffe, 0xb17217f7d1cf79abLL) +#define floatx80_pi_d make_floatx80(0x4000, 0xc90fdaa22168c234LL) + +#if !defined(CONFIG_USER_ONLY) +static qemu_irq ferr_irq; + +void x86_register_ferr_irq(qemu_irq irq) +{ + ferr_irq = irq; +} + +static void cpu_clear_ignne(void) +{ + CPUX86State *env = &X86_CPU(first_cpu)->env; + env->hflags2 &= ~HF2_IGNNE_MASK; +} + +void cpu_set_ignne(void) +{ + CPUX86State *env = &X86_CPU(first_cpu)->env; + env->hflags2 |= HF2_IGNNE_MASK; + /* + * We get here in response to a write to port F0h. The chipset should + * deassert FP_IRQ and FERR# instead should stay signaled until FPSW_SE is + * cleared, because FERR# and FP_IRQ are two separate pins on real + * hardware. However, we don't model FERR# as a qemu_irq, so we just + * do directly what the chipset would do, i.e. deassert FP_IRQ. + */ + qemu_irq_lower(ferr_irq); +} +#endif + + +static inline void fpush(CPUX86State *env) +{ + env->fpstt = (env->fpstt - 1) & 7; + env->fptags[env->fpstt] = 0; /* validate stack entry */ +} + +static inline void fpop(CPUX86State *env) +{ + env->fptags[env->fpstt] = 1; /* invalidate stack entry */ + env->fpstt = (env->fpstt + 1) & 7; +} + +static inline floatx80 helper_fldt(CPUX86State *env, target_ulong ptr, + uintptr_t retaddr) +{ + CPU_LDoubleU temp; + + temp.l.lower = cpu_ldq_data_ra(env, ptr, retaddr); + temp.l.upper = cpu_lduw_data_ra(env, ptr + 8, retaddr); + return temp.d; +} + +static inline void helper_fstt(CPUX86State *env, floatx80 f, target_ulong ptr, + uintptr_t retaddr) +{ + CPU_LDoubleU temp; + + temp.d = f; + cpu_stq_data_ra(env, ptr, temp.l.lower, retaddr); + cpu_stw_data_ra(env, ptr + 8, temp.l.upper, retaddr); +} + +/* x87 FPU helpers */ + +static inline double floatx80_to_double(CPUX86State *env, floatx80 a) +{ + union { + float64 f64; + double d; + } u; + + u.f64 = floatx80_to_float64(a, &env->fp_status); + return u.d; +} + +static inline floatx80 double_to_floatx80(CPUX86State *env, double a) +{ + union { + float64 f64; + double d; + } u; + + u.d = a; + return float64_to_floatx80(u.f64, &env->fp_status); +} + +static void fpu_set_exception(CPUX86State *env, int mask) +{ + env->fpus |= mask; + if (env->fpus & (~env->fpuc & FPUC_EM)) { + env->fpus |= FPUS_SE | FPUS_B; + } +} + +static inline uint8_t save_exception_flags(CPUX86State *env) +{ + uint8_t old_flags = get_float_exception_flags(&env->fp_status); + set_float_exception_flags(0, &env->fp_status); + return old_flags; +} + +static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) +{ + uint8_t new_flags = get_float_exception_flags(&env->fp_status); + float_raise(old_flags, &env->fp_status); + fpu_set_exception(env, + ((new_flags & float_flag_invalid ? FPUS_IE : 0) | + (new_flags & float_flag_divbyzero ? FPUS_ZE : 0) | + (new_flags & float_flag_overflow ? FPUS_OE : 0) | + (new_flags & float_flag_underflow ? FPUS_UE : 0) | + (new_flags & float_flag_inexact ? FPUS_PE : 0) | + (new_flags & float_flag_input_denormal ? FPUS_DE : 0))); +} + +static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) +{ + uint8_t old_flags = save_exception_flags(env); + floatx80 ret = floatx80_div(a, b, &env->fp_status); + merge_exception_flags(env, old_flags); + return ret; +} + +static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) +{ + if (env->cr[0] & CR0_NE_MASK) { + raise_exception_ra(env, EXCP10_COPR, retaddr); + } +#if !defined(CONFIG_USER_ONLY) + else if (ferr_irq && !(env->hflags2 & HF2_IGNNE_MASK)) { + qemu_irq_raise(ferr_irq); + } +#endif +} + +void helper_flds_FT0(CPUX86State *env, uint32_t val) +{ + uint8_t old_flags = save_exception_flags(env); + union { + float32 f; + uint32_t i; + } u; + + u.i = val; + FT0 = float32_to_floatx80(u.f, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fldl_FT0(CPUX86State *env, uint64_t val) +{ + uint8_t old_flags = save_exception_flags(env); + union { + float64 f; + uint64_t i; + } u; + + u.i = val; + FT0 = float64_to_floatx80(u.f, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fildl_FT0(CPUX86State *env, int32_t val) +{ + FT0 = int32_to_floatx80(val, &env->fp_status); +} + +void helper_flds_ST0(CPUX86State *env, uint32_t val) +{ + uint8_t old_flags = save_exception_flags(env); + int new_fpstt; + union { + float32 f; + uint32_t i; + } u; + + new_fpstt = (env->fpstt - 1) & 7; + u.i = val; + env->fpregs[new_fpstt].d = float32_to_floatx80(u.f, &env->fp_status); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ + merge_exception_flags(env, old_flags); +} + +void helper_fldl_ST0(CPUX86State *env, uint64_t val) +{ + uint8_t old_flags = save_exception_flags(env); + int new_fpstt; + union { + float64 f; + uint64_t i; + } u; + + new_fpstt = (env->fpstt - 1) & 7; + u.i = val; + env->fpregs[new_fpstt].d = float64_to_floatx80(u.f, &env->fp_status); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ + merge_exception_flags(env, old_flags); +} + +void helper_fildl_ST0(CPUX86State *env, int32_t val) +{ + int new_fpstt; + + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = int32_to_floatx80(val, &env->fp_status); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fildll_ST0(CPUX86State *env, int64_t val) +{ + int new_fpstt; + + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = int64_to_floatx80(val, &env->fp_status); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +uint32_t helper_fsts_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + union { + float32 f; + uint32_t i; + } u; + + u.f = floatx80_to_float32(ST0, &env->fp_status); + merge_exception_flags(env, old_flags); + return u.i; +} + +uint64_t helper_fstl_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + union { + float64 f; + uint64_t i; + } u; + + u.f = floatx80_to_float64(ST0, &env->fp_status); + merge_exception_flags(env, old_flags); + return u.i; +} + +int32_t helper_fist_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int32_t val; + + val = floatx80_to_int32(ST0, &env->fp_status); + if (val != (int16_t)val) { + set_float_exception_flags(float_flag_invalid, &env->fp_status); + val = -32768; + } + merge_exception_flags(env, old_flags); + return val; +} + +int32_t helper_fistl_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int32_t val; + + val = floatx80_to_int32(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x80000000; + } + merge_exception_flags(env, old_flags); + return val; +} + +int64_t helper_fistll_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int64_t val; + + val = floatx80_to_int64(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x8000000000000000ULL; + } + merge_exception_flags(env, old_flags); + return val; +} + +int32_t helper_fistt_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int32_t val; + + val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); + if (val != (int16_t)val) { + set_float_exception_flags(float_flag_invalid, &env->fp_status); + val = -32768; + } + merge_exception_flags(env, old_flags); + return val; +} + +int32_t helper_fisttl_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int32_t val; + + val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x80000000; + } + merge_exception_flags(env, old_flags); + return val; +} + +int64_t helper_fisttll_ST0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int64_t val; + + val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); + if (get_float_exception_flags(&env->fp_status) & float_flag_invalid) { + val = 0x8000000000000000ULL; + } + merge_exception_flags(env, old_flags); + return val; +} + +void helper_fldt_ST0(CPUX86State *env, target_ulong ptr) +{ + int new_fpstt; + + new_fpstt = (env->fpstt - 1) & 7; + env->fpregs[new_fpstt].d = helper_fldt(env, ptr, GETPC()); + env->fpstt = new_fpstt; + env->fptags[new_fpstt] = 0; /* validate stack entry */ +} + +void helper_fstt_ST0(CPUX86State *env, target_ulong ptr) +{ + helper_fstt(env, ST0, ptr, GETPC()); +} + +void helper_fpush(CPUX86State *env) +{ + fpush(env); +} + +void helper_fpop(CPUX86State *env) +{ + fpop(env); +} + +void helper_fdecstp(CPUX86State *env) +{ + env->fpstt = (env->fpstt - 1) & 7; + env->fpus &= ~0x4700; +} + +void helper_fincstp(CPUX86State *env) +{ + env->fpstt = (env->fpstt + 1) & 7; + env->fpus &= ~0x4700; +} + +/* FPU move */ + +void helper_ffree_STN(CPUX86State *env, int st_index) +{ + env->fptags[(env->fpstt + st_index) & 7] = 1; +} + +void helper_fmov_ST0_FT0(CPUX86State *env) +{ + ST0 = FT0; +} + +void helper_fmov_FT0_STN(CPUX86State *env, int st_index) +{ + FT0 = ST(st_index); +} + +void helper_fmov_ST0_STN(CPUX86State *env, int st_index) +{ + ST0 = ST(st_index); +} + +void helper_fmov_STN_ST0(CPUX86State *env, int st_index) +{ + ST(st_index) = ST0; +} + +void helper_fxchg_ST0_STN(CPUX86State *env, int st_index) +{ + floatx80 tmp; + + tmp = ST(st_index); + ST(st_index) = ST0; + ST0 = tmp; +} + +/* FPU operations */ + +static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; + +void helper_fcom_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + FloatRelation ret; + + ret = floatx80_compare(ST0, FT0, &env->fp_status); + env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; + merge_exception_flags(env, old_flags); +} + +void helper_fucom_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + FloatRelation ret; + + ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); + env->fpus = (env->fpus & ~0x4500) | fcom_ccval[ret + 1]; + merge_exception_flags(env, old_flags); +} + +static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; + +void helper_fcomi_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int eflags; + FloatRelation ret; + + ret = floatx80_compare(ST0, FT0, &env->fp_status); + eflags = cpu_cc_compute_all(env, CC_OP); + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; + CC_SRC = eflags; + merge_exception_flags(env, old_flags); +} + +void helper_fucomi_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + int eflags; + FloatRelation ret; + + ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); + eflags = cpu_cc_compute_all(env, CC_OP); + eflags = (eflags & ~(CC_Z | CC_P | CC_C)) | fcomi_ccval[ret + 1]; + CC_SRC = eflags; + merge_exception_flags(env, old_flags); +} + +void helper_fadd_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + ST0 = floatx80_add(ST0, FT0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fmul_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + ST0 = floatx80_mul(ST0, FT0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fsub_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + ST0 = floatx80_sub(ST0, FT0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fsubr_ST0_FT0(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + ST0 = floatx80_sub(FT0, ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fdiv_ST0_FT0(CPUX86State *env) +{ + ST0 = helper_fdiv(env, ST0, FT0); +} + +void helper_fdivr_ST0_FT0(CPUX86State *env) +{ + ST0 = helper_fdiv(env, FT0, ST0); +} + +/* fp operations between STN and ST0 */ + +void helper_fadd_STN_ST0(CPUX86State *env, int st_index) +{ + uint8_t old_flags = save_exception_flags(env); + ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fmul_STN_ST0(CPUX86State *env, int st_index) +{ + uint8_t old_flags = save_exception_flags(env); + ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fsub_STN_ST0(CPUX86State *env, int st_index) +{ + uint8_t old_flags = save_exception_flags(env); + ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) +{ + uint8_t old_flags = save_exception_flags(env); + ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fdiv_STN_ST0(CPUX86State *env, int st_index) +{ + floatx80 *p; + + p = &ST(st_index); + *p = helper_fdiv(env, *p, ST0); +} + +void helper_fdivr_STN_ST0(CPUX86State *env, int st_index) +{ + floatx80 *p; + + p = &ST(st_index); + *p = helper_fdiv(env, ST0, *p); +} + +/* misc FPU operations */ +void helper_fchs_ST0(CPUX86State *env) +{ + ST0 = floatx80_chs(ST0); +} + +void helper_fabs_ST0(CPUX86State *env) +{ + ST0 = floatx80_abs(ST0); +} + +void helper_fld1_ST0(CPUX86State *env) +{ + ST0 = floatx80_one; +} + +void helper_fldl2t_ST0(CPUX86State *env) +{ + switch (env->fpuc & FPU_RC_MASK) { + case FPU_RC_UP: + ST0 = floatx80_l2t_u; + break; + default: + ST0 = floatx80_l2t; + break; + } +} + +void helper_fldl2e_ST0(CPUX86State *env) +{ + switch (env->fpuc & FPU_RC_MASK) { + case FPU_RC_DOWN: + case FPU_RC_CHOP: + ST0 = floatx80_l2e_d; + break; + default: + ST0 = floatx80_l2e; + break; + } +} + +void helper_fldpi_ST0(CPUX86State *env) +{ + switch (env->fpuc & FPU_RC_MASK) { + case FPU_RC_DOWN: + case FPU_RC_CHOP: + ST0 = floatx80_pi_d; + break; + default: + ST0 = floatx80_pi; + break; + } +} + +void helper_fldlg2_ST0(CPUX86State *env) +{ + switch (env->fpuc & FPU_RC_MASK) { + case FPU_RC_DOWN: + case FPU_RC_CHOP: + ST0 = floatx80_lg2_d; + break; + default: + ST0 = floatx80_lg2; + break; + } +} + +void helper_fldln2_ST0(CPUX86State *env) +{ + switch (env->fpuc & FPU_RC_MASK) { + case FPU_RC_DOWN: + case FPU_RC_CHOP: + ST0 = floatx80_ln2_d; + break; + default: + ST0 = floatx80_ln2; + break; + } +} + +void helper_fldz_ST0(CPUX86State *env) +{ + ST0 = floatx80_zero; +} + +void helper_fldz_FT0(CPUX86State *env) +{ + FT0 = floatx80_zero; +} + +uint32_t helper_fnstsw(CPUX86State *env) +{ + return (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; +} + +uint32_t helper_fnstcw(CPUX86State *env) +{ + return env->fpuc; +} + +void update_fp_status(CPUX86State *env) +{ + int rnd_type; + + /* set rounding mode */ + switch (env->fpuc & FPU_RC_MASK) { + default: + case FPU_RC_NEAR: + rnd_type = float_round_nearest_even; + break; + case FPU_RC_DOWN: + rnd_type = float_round_down; + break; + case FPU_RC_UP: + rnd_type = float_round_up; + break; + case FPU_RC_CHOP: + rnd_type = float_round_to_zero; + break; + } + set_float_rounding_mode(rnd_type, &env->fp_status); + switch ((env->fpuc >> 8) & 3) { + case 0: + rnd_type = 32; + break; + case 2: + rnd_type = 64; + break; + case 3: + default: + rnd_type = 80; + break; + } + set_floatx80_rounding_precision(rnd_type, &env->fp_status); +} + +void helper_fldcw(CPUX86State *env, uint32_t val) +{ + cpu_set_fpuc(env, val); +} + +void helper_fclex(CPUX86State *env) +{ + env->fpus &= 0x7f00; +} + +void helper_fwait(CPUX86State *env) +{ + if (env->fpus & FPUS_SE) { + fpu_raise_exception(env, GETPC()); + } +} + +void helper_fninit(CPUX86State *env) +{ + env->fpus = 0; + env->fpstt = 0; + cpu_set_fpuc(env, 0x37f); + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} + +/* BCD ops */ + +void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) +{ + floatx80 tmp; + uint64_t val; + unsigned int v; + int i; + + val = 0; + for (i = 8; i >= 0; i--) { + v = cpu_ldub_data_ra(env, ptr + i, GETPC()); + val = (val * 100) + ((v >> 4) * 10) + (v & 0xf); + } + tmp = int64_to_floatx80(val, &env->fp_status); + if (cpu_ldub_data_ra(env, ptr + 9, GETPC()) & 0x80) { + tmp = floatx80_chs(tmp); + } + fpush(env); + ST0 = tmp; +} + +void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) +{ + uint8_t old_flags = save_exception_flags(env); + int v; + target_ulong mem_ref, mem_end; + int64_t val; + CPU_LDoubleU temp; + + temp.d = ST0; + + val = floatx80_to_int64(ST0, &env->fp_status); + mem_ref = ptr; + if (val >= 1000000000000000000LL || val <= -1000000000000000000LL) { + set_float_exception_flags(float_flag_invalid, &env->fp_status); + while (mem_ref < ptr + 7) { + cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + } + cpu_stb_data_ra(env, mem_ref++, 0xc0, GETPC()); + cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); + cpu_stb_data_ra(env, mem_ref++, 0xff, GETPC()); + merge_exception_flags(env, old_flags); + return; + } + mem_end = mem_ref + 9; + if (SIGND(temp)) { + cpu_stb_data_ra(env, mem_end, 0x80, GETPC()); + val = -val; + } else { + cpu_stb_data_ra(env, mem_end, 0x00, GETPC()); + } + while (mem_ref < mem_end) { + if (val == 0) { + break; + } + v = val % 100; + val = val / 100; + v = ((v / 10) << 4) | (v % 10); + cpu_stb_data_ra(env, mem_ref++, v, GETPC()); + } + while (mem_ref < mem_end) { + cpu_stb_data_ra(env, mem_ref++, 0, GETPC()); + } + merge_exception_flags(env, old_flags); +} + +/* 128-bit significand of log(2). */ +#define ln2_sig_high 0xb17217f7d1cf79abULL +#define ln2_sig_low 0xc9e3b39803f2f6afULL + +/* + * Polynomial coefficients for an approximation to (2^x - 1) / x, on + * the interval [-1/64, 1/64]. + */ +#define f2xm1_coeff_0 make_floatx80(0x3ffe, 0xb17217f7d1cf79acULL) +#define f2xm1_coeff_0_low make_floatx80(0xbfbc, 0xd87edabf495b3762ULL) +#define f2xm1_coeff_1 make_floatx80(0x3ffc, 0xf5fdeffc162c7543ULL) +#define f2xm1_coeff_2 make_floatx80(0x3ffa, 0xe35846b82505fcc7ULL) +#define f2xm1_coeff_3 make_floatx80(0x3ff8, 0x9d955b7dd273b899ULL) +#define f2xm1_coeff_4 make_floatx80(0x3ff5, 0xaec3ff3c4ef4ac0cULL) +#define f2xm1_coeff_5 make_floatx80(0x3ff2, 0xa184897c3a7f0de9ULL) +#define f2xm1_coeff_6 make_floatx80(0x3fee, 0xffe634d0ec30d504ULL) +#define f2xm1_coeff_7 make_floatx80(0x3feb, 0xb160111d2db515e4ULL) + +struct f2xm1_data { + /* + * A value very close to a multiple of 1/32, such that 2^t and 2^t - 1 + * are very close to exact floatx80 values. + */ + floatx80 t; + /* The value of 2^t. */ + floatx80 exp2; + /* The value of 2^t - 1. */ + floatx80 exp2m1; +}; + +static const struct f2xm1_data f2xm1_table[65] = { + { make_floatx80_init(0xbfff, 0x8000000000000000ULL), + make_floatx80_init(0x3ffe, 0x8000000000000000ULL), + make_floatx80_init(0xbffe, 0x8000000000000000ULL) }, + { make_floatx80_init(0xbffe, 0xf800000000002e7eULL), + make_floatx80_init(0x3ffe, 0x82cd8698ac2b9160ULL), + make_floatx80_init(0xbffd, 0xfa64f2cea7a8dd40ULL) }, + { make_floatx80_init(0xbffe, 0xefffffffffffe960ULL), + make_floatx80_init(0x3ffe, 0x85aac367cc488345ULL), + make_floatx80_init(0xbffd, 0xf4aa7930676ef976ULL) }, + { make_floatx80_init(0xbffe, 0xe800000000006f10ULL), + make_floatx80_init(0x3ffe, 0x88980e8092da5c14ULL), + make_floatx80_init(0xbffd, 0xeecfe2feda4b47d8ULL) }, + { make_floatx80_init(0xbffe, 0xe000000000008a45ULL), + make_floatx80_init(0x3ffe, 0x8b95c1e3ea8ba2a5ULL), + make_floatx80_init(0xbffd, 0xe8d47c382ae8bab6ULL) }, + { make_floatx80_init(0xbffe, 0xd7ffffffffff8a9eULL), + make_floatx80_init(0x3ffe, 0x8ea4398b45cd8116ULL), + make_floatx80_init(0xbffd, 0xe2b78ce97464fdd4ULL) }, + { make_floatx80_init(0xbffe, 0xd0000000000019a0ULL), + make_floatx80_init(0x3ffe, 0x91c3d373ab11b919ULL), + make_floatx80_init(0xbffd, 0xdc785918a9dc8dceULL) }, + { make_floatx80_init(0xbffe, 0xc7ffffffffff14dfULL), + make_floatx80_init(0x3ffe, 0x94f4efa8fef76836ULL), + make_floatx80_init(0xbffd, 0xd61620ae02112f94ULL) }, + { make_floatx80_init(0xbffe, 0xc000000000006530ULL), + make_floatx80_init(0x3ffe, 0x9837f0518db87fbbULL), + make_floatx80_init(0xbffd, 0xcf901f5ce48f008aULL) }, + { make_floatx80_init(0xbffe, 0xb7ffffffffff1723ULL), + make_floatx80_init(0x3ffe, 0x9b8d39b9d54eb74cULL), + make_floatx80_init(0xbffd, 0xc8e58c8c55629168ULL) }, + { make_floatx80_init(0xbffe, 0xb00000000000b5e1ULL), + make_floatx80_init(0x3ffe, 0x9ef5326091a0c366ULL), + make_floatx80_init(0xbffd, 0xc2159b3edcbe7934ULL) }, + { make_floatx80_init(0xbffe, 0xa800000000006f8aULL), + make_floatx80_init(0x3ffe, 0xa27043030c49370aULL), + make_floatx80_init(0xbffd, 0xbb1f79f9e76d91ecULL) }, + { make_floatx80_init(0xbffe, 0x9fffffffffff816aULL), + make_floatx80_init(0x3ffe, 0xa5fed6a9b15171cfULL), + make_floatx80_init(0xbffd, 0xb40252ac9d5d1c62ULL) }, + { make_floatx80_init(0xbffe, 0x97ffffffffffb621ULL), + make_floatx80_init(0x3ffe, 0xa9a15ab4ea7c30e6ULL), + make_floatx80_init(0xbffd, 0xacbd4a962b079e34ULL) }, + { make_floatx80_init(0xbffe, 0x8fffffffffff162bULL), + make_floatx80_init(0x3ffe, 0xad583eea42a1b886ULL), + make_floatx80_init(0xbffd, 0xa54f822b7abc8ef4ULL) }, + { make_floatx80_init(0xbffe, 0x87ffffffffff4d34ULL), + make_floatx80_init(0x3ffe, 0xb123f581d2ac7b51ULL), + make_floatx80_init(0xbffd, 0x9db814fc5aa7095eULL) }, + { make_floatx80_init(0xbffe, 0x800000000000227dULL), + make_floatx80_init(0x3ffe, 0xb504f333f9de539dULL), + make_floatx80_init(0xbffd, 0x95f619980c4358c6ULL) }, + { make_floatx80_init(0xbffd, 0xefffffffffff3978ULL), + make_floatx80_init(0x3ffe, 0xb8fbaf4762fbd0a1ULL), + make_floatx80_init(0xbffd, 0x8e08a1713a085ebeULL) }, + { make_floatx80_init(0xbffd, 0xe00000000000df81ULL), + make_floatx80_init(0x3ffe, 0xbd08a39f580bfd8cULL), + make_floatx80_init(0xbffd, 0x85eeb8c14fe804e8ULL) }, + { make_floatx80_init(0xbffd, 0xd00000000000bccfULL), + make_floatx80_init(0x3ffe, 0xc12c4cca667062f6ULL), + make_floatx80_init(0xbffc, 0xfb4eccd6663e7428ULL) }, + { make_floatx80_init(0xbffd, 0xc00000000000eff0ULL), + make_floatx80_init(0x3ffe, 0xc5672a1155069abeULL), + make_floatx80_init(0xbffc, 0xea6357baabe59508ULL) }, + { make_floatx80_init(0xbffd, 0xb000000000000fe6ULL), + make_floatx80_init(0x3ffe, 0xc9b9bd866e2f234bULL), + make_floatx80_init(0xbffc, 0xd91909e6474372d4ULL) }, + { make_floatx80_init(0xbffd, 0x9fffffffffff2172ULL), + make_floatx80_init(0x3ffe, 0xce248c151f84bf00ULL), + make_floatx80_init(0xbffc, 0xc76dcfab81ed0400ULL) }, + { make_floatx80_init(0xbffd, 0x8fffffffffffafffULL), + make_floatx80_init(0x3ffe, 0xd2a81d91f12afb2bULL), + make_floatx80_init(0xbffc, 0xb55f89b83b541354ULL) }, + { make_floatx80_init(0xbffc, 0xffffffffffff81a3ULL), + make_floatx80_init(0x3ffe, 0xd744fccad69d7d5eULL), + make_floatx80_init(0xbffc, 0xa2ec0cd4a58a0a88ULL) }, + { make_floatx80_init(0xbffc, 0xdfffffffffff1568ULL), + make_floatx80_init(0x3ffe, 0xdbfbb797daf25a44ULL), + make_floatx80_init(0xbffc, 0x901121a0943696f0ULL) }, + { make_floatx80_init(0xbffc, 0xbfffffffffff68daULL), + make_floatx80_init(0x3ffe, 0xe0ccdeec2a94f811ULL), + make_floatx80_init(0xbffb, 0xf999089eab583f78ULL) }, + { make_floatx80_init(0xbffc, 0x9fffffffffff4690ULL), + make_floatx80_init(0x3ffe, 0xe5b906e77c83657eULL), + make_floatx80_init(0xbffb, 0xd237c8c41be4d410ULL) }, + { make_floatx80_init(0xbffb, 0xffffffffffff8aeeULL), + make_floatx80_init(0x3ffe, 0xeac0c6e7dd24427cULL), + make_floatx80_init(0xbffb, 0xa9f9c8c116ddec20ULL) }, + { make_floatx80_init(0xbffb, 0xbfffffffffff2d18ULL), + make_floatx80_init(0x3ffe, 0xefe4b99bdcdb06ebULL), + make_floatx80_init(0xbffb, 0x80da33211927c8a8ULL) }, + { make_floatx80_init(0xbffa, 0xffffffffffff8ccbULL), + make_floatx80_init(0x3ffe, 0xf5257d152486d0f4ULL), + make_floatx80_init(0xbffa, 0xada82eadb792f0c0ULL) }, + { make_floatx80_init(0xbff9, 0xffffffffffff11feULL), + make_floatx80_init(0x3ffe, 0xfa83b2db722a0846ULL), + make_floatx80_init(0xbff9, 0xaf89a491babef740ULL) }, + { floatx80_zero_init, + make_floatx80_init(0x3fff, 0x8000000000000000ULL), + floatx80_zero_init }, + { make_floatx80_init(0x3ff9, 0xffffffffffff2680ULL), + make_floatx80_init(0x3fff, 0x82cd8698ac2b9f6fULL), + make_floatx80_init(0x3ff9, 0xb361a62b0ae7dbc0ULL) }, + { make_floatx80_init(0x3ffb, 0x800000000000b500ULL), + make_floatx80_init(0x3fff, 0x85aac367cc488345ULL), + make_floatx80_init(0x3ffa, 0xb5586cf9891068a0ULL) }, + { make_floatx80_init(0x3ffb, 0xbfffffffffff4b67ULL), + make_floatx80_init(0x3fff, 0x88980e8092da7cceULL), + make_floatx80_init(0x3ffb, 0x8980e8092da7cce0ULL) }, + { make_floatx80_init(0x3ffb, 0xffffffffffffff57ULL), + make_floatx80_init(0x3fff, 0x8b95c1e3ea8bd6dfULL), + make_floatx80_init(0x3ffb, 0xb95c1e3ea8bd6df0ULL) }, + { make_floatx80_init(0x3ffc, 0x9fffffffffff811fULL), + make_floatx80_init(0x3fff, 0x8ea4398b45cd4780ULL), + make_floatx80_init(0x3ffb, 0xea4398b45cd47800ULL) }, + { make_floatx80_init(0x3ffc, 0xbfffffffffff9980ULL), + make_floatx80_init(0x3fff, 0x91c3d373ab11b919ULL), + make_floatx80_init(0x3ffc, 0x8e1e9b9d588dc8c8ULL) }, + { make_floatx80_init(0x3ffc, 0xdffffffffffff631ULL), + make_floatx80_init(0x3fff, 0x94f4efa8fef70864ULL), + make_floatx80_init(0x3ffc, 0xa7a77d47f7b84320ULL) }, + { make_floatx80_init(0x3ffc, 0xffffffffffff2499ULL), + make_floatx80_init(0x3fff, 0x9837f0518db892d4ULL), + make_floatx80_init(0x3ffc, 0xc1bf828c6dc496a0ULL) }, + { make_floatx80_init(0x3ffd, 0x8fffffffffff80fbULL), + make_floatx80_init(0x3fff, 0x9b8d39b9d54e3a79ULL), + make_floatx80_init(0x3ffc, 0xdc69cdceaa71d3c8ULL) }, + { make_floatx80_init(0x3ffd, 0x9fffffffffffbc23ULL), + make_floatx80_init(0x3fff, 0x9ef5326091a10313ULL), + make_floatx80_init(0x3ffc, 0xf7a993048d081898ULL) }, + { make_floatx80_init(0x3ffd, 0xafffffffffff20ecULL), + make_floatx80_init(0x3fff, 0xa27043030c49370aULL), + make_floatx80_init(0x3ffd, 0x89c10c0c3124dc28ULL) }, + { make_floatx80_init(0x3ffd, 0xc00000000000fd2cULL), + make_floatx80_init(0x3fff, 0xa5fed6a9b15171cfULL), + make_floatx80_init(0x3ffd, 0x97fb5aa6c545c73cULL) }, + { make_floatx80_init(0x3ffd, 0xd0000000000093beULL), + make_floatx80_init(0x3fff, 0xa9a15ab4ea7c30e6ULL), + make_floatx80_init(0x3ffd, 0xa6856ad3a9f0c398ULL) }, + { make_floatx80_init(0x3ffd, 0xe00000000000c2aeULL), + make_floatx80_init(0x3fff, 0xad583eea42a17876ULL), + make_floatx80_init(0x3ffd, 0xb560fba90a85e1d8ULL) }, + { make_floatx80_init(0x3ffd, 0xefffffffffff1e3fULL), + make_floatx80_init(0x3fff, 0xb123f581d2abef6cULL), + make_floatx80_init(0x3ffd, 0xc48fd6074aafbdb0ULL) }, + { make_floatx80_init(0x3ffd, 0xffffffffffff1c23ULL), + make_floatx80_init(0x3fff, 0xb504f333f9de2cadULL), + make_floatx80_init(0x3ffd, 0xd413cccfe778b2b4ULL) }, + { make_floatx80_init(0x3ffe, 0x8800000000006344ULL), + make_floatx80_init(0x3fff, 0xb8fbaf4762fbd0a1ULL), + make_floatx80_init(0x3ffd, 0xe3eebd1d8bef4284ULL) }, + { make_floatx80_init(0x3ffe, 0x9000000000005d67ULL), + make_floatx80_init(0x3fff, 0xbd08a39f580c668dULL), + make_floatx80_init(0x3ffd, 0xf4228e7d60319a34ULL) }, + { make_floatx80_init(0x3ffe, 0x9800000000009127ULL), + make_floatx80_init(0x3fff, 0xc12c4cca6670e042ULL), + make_floatx80_init(0x3ffe, 0x82589994cce1c084ULL) }, + { make_floatx80_init(0x3ffe, 0x9fffffffffff06f9ULL), + make_floatx80_init(0x3fff, 0xc5672a11550655c3ULL), + make_floatx80_init(0x3ffe, 0x8ace5422aa0cab86ULL) }, + { make_floatx80_init(0x3ffe, 0xa7fffffffffff80dULL), + make_floatx80_init(0x3fff, 0xc9b9bd866e2f234bULL), + make_floatx80_init(0x3ffe, 0x93737b0cdc5e4696ULL) }, + { make_floatx80_init(0x3ffe, 0xafffffffffff1470ULL), + make_floatx80_init(0x3fff, 0xce248c151f83fd69ULL), + make_floatx80_init(0x3ffe, 0x9c49182a3f07fad2ULL) }, + { make_floatx80_init(0x3ffe, 0xb800000000000e0aULL), + make_floatx80_init(0x3fff, 0xd2a81d91f12aec5cULL), + make_floatx80_init(0x3ffe, 0xa5503b23e255d8b8ULL) }, + { make_floatx80_init(0x3ffe, 0xc00000000000b7faULL), + make_floatx80_init(0x3fff, 0xd744fccad69dd630ULL), + make_floatx80_init(0x3ffe, 0xae89f995ad3bac60ULL) }, + { make_floatx80_init(0x3ffe, 0xc800000000003aa6ULL), + make_floatx80_init(0x3fff, 0xdbfbb797daf25a44ULL), + make_floatx80_init(0x3ffe, 0xb7f76f2fb5e4b488ULL) }, + { make_floatx80_init(0x3ffe, 0xd00000000000a6aeULL), + make_floatx80_init(0x3fff, 0xe0ccdeec2a954685ULL), + make_floatx80_init(0x3ffe, 0xc199bdd8552a8d0aULL) }, + { make_floatx80_init(0x3ffe, 0xd800000000004165ULL), + make_floatx80_init(0x3fff, 0xe5b906e77c837155ULL), + make_floatx80_init(0x3ffe, 0xcb720dcef906e2aaULL) }, + { make_floatx80_init(0x3ffe, 0xe00000000000582cULL), + make_floatx80_init(0x3fff, 0xeac0c6e7dd24713aULL), + make_floatx80_init(0x3ffe, 0xd5818dcfba48e274ULL) }, + { make_floatx80_init(0x3ffe, 0xe800000000001a5dULL), + make_floatx80_init(0x3fff, 0xefe4b99bdcdb06ebULL), + make_floatx80_init(0x3ffe, 0xdfc97337b9b60dd6ULL) }, + { make_floatx80_init(0x3ffe, 0xefffffffffffc1efULL), + make_floatx80_init(0x3fff, 0xf5257d152486a2faULL), + make_floatx80_init(0x3ffe, 0xea4afa2a490d45f4ULL) }, + { make_floatx80_init(0x3ffe, 0xf800000000001069ULL), + make_floatx80_init(0x3fff, 0xfa83b2db722a0e5cULL), + make_floatx80_init(0x3ffe, 0xf50765b6e4541cb8ULL) }, + { make_floatx80_init(0x3fff, 0x8000000000000000ULL), + make_floatx80_init(0x4000, 0x8000000000000000ULL), + make_floatx80_init(0x3fff, 0x8000000000000000ULL) }, +}; + +void helper_f2xm1(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + uint64_t sig = extractFloatx80Frac(ST0); + int32_t exp = extractFloatx80Exp(ST0); + bool sign = extractFloatx80Sign(ST0); + + if (floatx80_invalid_encoding(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_any_nan(ST0)) { + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_silence_nan(ST0, &env->fp_status); + } + } else if (exp > 0x3fff || + (exp == 0x3fff && sig != (0x8000000000000000ULL))) { + /* Out of range for the instruction, treat as invalid. */ + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + } else if (exp == 0x3fff) { + /* Argument 1 or -1, exact result 1 or -0.5. */ + if (sign) { + ST0 = make_floatx80(0xbffe, 0x8000000000000000ULL); + } + } else if (exp < 0x3fb0) { + if (!floatx80_is_zero(ST0)) { + /* + * Multiplying the argument by an extra-precision version + * of log(2) is sufficiently precise. Zero arguments are + * returned unchanged. + */ + uint64_t sig0, sig1, sig2; + if (exp == 0) { + normalizeFloatx80Subnormal(sig, &exp, &sig); + } + mul128By64To192(ln2_sig_high, ln2_sig_low, sig, &sig0, &sig1, + &sig2); + /* This result is inexact. */ + sig1 |= 1; + ST0 = normalizeRoundAndPackFloatx80(80, sign, exp, sig0, sig1, + &env->fp_status); + } + } else { + floatx80 tmp, y, accum; + bool asign, bsign; + int32_t n, aexp, bexp; + uint64_t asig0, asig1, asig2, bsig0, bsig1; + FloatRoundMode save_mode = env->fp_status.float_rounding_mode; + signed char save_prec = env->fp_status.floatx80_rounding_precision; + env->fp_status.float_rounding_mode = float_round_nearest_even; + env->fp_status.floatx80_rounding_precision = 80; + + /* Find the nearest multiple of 1/32 to the argument. */ + tmp = floatx80_scalbn(ST0, 5, &env->fp_status); + n = 32 + floatx80_to_int32(tmp, &env->fp_status); + y = floatx80_sub(ST0, f2xm1_table[n].t, &env->fp_status); + + if (floatx80_is_zero(y)) { + /* + * Use the value of 2^t - 1 from the table, to avoid + * needing to special-case zero as a result of + * multiplication below. + */ + ST0 = f2xm1_table[n].t; + set_float_exception_flags(float_flag_inexact, &env->fp_status); + env->fp_status.float_rounding_mode = save_mode; + } else { + /* + * Compute the lower parts of a polynomial expansion for + * (2^y - 1) / y. + */ + accum = floatx80_mul(f2xm1_coeff_7, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_6, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_5, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_4, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_3, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_2, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_1, accum, &env->fp_status); + accum = floatx80_mul(accum, y, &env->fp_status); + accum = floatx80_add(f2xm1_coeff_0_low, accum, &env->fp_status); + + /* + * The full polynomial expansion is f2xm1_coeff_0 + accum + * (where accum has much lower magnitude, and so, in + * particular, carry out of the addition is not possible). + * (This expansion is only accurate to about 70 bits, not + * 128 bits.) + */ + aexp = extractFloatx80Exp(f2xm1_coeff_0); + asign = extractFloatx80Sign(f2xm1_coeff_0); + shift128RightJamming(extractFloatx80Frac(accum), 0, + aexp - extractFloatx80Exp(accum), + &asig0, &asig1); + bsig0 = extractFloatx80Frac(f2xm1_coeff_0); + bsig1 = 0; + if (asign == extractFloatx80Sign(accum)) { + add128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); + } else { + sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); + } + /* And thus compute an approximation to 2^y - 1. */ + mul128By64To192(asig0, asig1, extractFloatx80Frac(y), + &asig0, &asig1, &asig2); + aexp += extractFloatx80Exp(y) - 0x3ffe; + asign ^= extractFloatx80Sign(y); + if (n != 32) { + /* + * Multiply this by the precomputed value of 2^t and + * add that of 2^t - 1. + */ + mul128By64To192(asig0, asig1, + extractFloatx80Frac(f2xm1_table[n].exp2), + &asig0, &asig1, &asig2); + aexp += extractFloatx80Exp(f2xm1_table[n].exp2) - 0x3ffe; + bexp = extractFloatx80Exp(f2xm1_table[n].exp2m1); + bsig0 = extractFloatx80Frac(f2xm1_table[n].exp2m1); + bsig1 = 0; + if (bexp < aexp) { + shift128RightJamming(bsig0, bsig1, aexp - bexp, + &bsig0, &bsig1); + } else if (aexp < bexp) { + shift128RightJamming(asig0, asig1, bexp - aexp, + &asig0, &asig1); + aexp = bexp; + } + /* The sign of 2^t - 1 is always that of the result. */ + bsign = extractFloatx80Sign(f2xm1_table[n].exp2m1); + if (asign == bsign) { + /* Avoid possible carry out of the addition. */ + shift128RightJamming(asig0, asig1, 1, + &asig0, &asig1); + shift128RightJamming(bsig0, bsig1, 1, + &bsig0, &bsig1); + ++aexp; + add128(asig0, asig1, bsig0, bsig1, &asig0, &asig1); + } else { + sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); + asign = bsign; + } + } + env->fp_status.float_rounding_mode = save_mode; + /* This result is inexact. */ + asig1 |= 1; + ST0 = normalizeRoundAndPackFloatx80(80, asign, aexp, asig0, asig1, + &env->fp_status); + } + + env->fp_status.floatx80_rounding_precision = save_prec; + } + merge_exception_flags(env, old_flags); +} + +void helper_fptan(CPUX86State *env) +{ + double fptemp = floatx80_to_double(env, ST0); + + if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + fptemp = tan(fptemp); + ST0 = double_to_floatx80(env, fptemp); + fpush(env); + ST0 = floatx80_one; + env->fpus &= ~0x400; /* C2 <-- 0 */ + /* the above code is for |arg| < 2**52 only */ + } +} + +/* Values of pi/4, pi/2, 3pi/4 and pi, with 128-bit precision. */ +#define pi_4_exp 0x3ffe +#define pi_4_sig_high 0xc90fdaa22168c234ULL +#define pi_4_sig_low 0xc4c6628b80dc1cd1ULL +#define pi_2_exp 0x3fff +#define pi_2_sig_high 0xc90fdaa22168c234ULL +#define pi_2_sig_low 0xc4c6628b80dc1cd1ULL +#define pi_34_exp 0x4000 +#define pi_34_sig_high 0x96cbe3f9990e91a7ULL +#define pi_34_sig_low 0x9394c9e8a0a5159dULL +#define pi_exp 0x4000 +#define pi_sig_high 0xc90fdaa22168c234ULL +#define pi_sig_low 0xc4c6628b80dc1cd1ULL + +/* + * Polynomial coefficients for an approximation to atan(x), with only + * odd powers of x used, for x in the interval [-1/16, 1/16]. (Unlike + * for some other approximations, no low part is needed for the first + * coefficient here to achieve a sufficiently accurate result, because + * the coefficient in this minimax approximation is very close to + * exactly 1.) + */ +#define fpatan_coeff_0 make_floatx80(0x3fff, 0x8000000000000000ULL) +#define fpatan_coeff_1 make_floatx80(0xbffd, 0xaaaaaaaaaaaaaa43ULL) +#define fpatan_coeff_2 make_floatx80(0x3ffc, 0xccccccccccbfe4f8ULL) +#define fpatan_coeff_3 make_floatx80(0xbffc, 0x92492491fbab2e66ULL) +#define fpatan_coeff_4 make_floatx80(0x3ffb, 0xe38e372881ea1e0bULL) +#define fpatan_coeff_5 make_floatx80(0xbffb, 0xba2c0104bbdd0615ULL) +#define fpatan_coeff_6 make_floatx80(0x3ffb, 0x9baf7ebf898b42efULL) + +struct fpatan_data { + /* High and low parts of atan(x). */ + floatx80 atan_high, atan_low; +}; + +static const struct fpatan_data fpatan_table[9] = { + { floatx80_zero_init, + floatx80_zero_init }, + { make_floatx80_init(0x3ffb, 0xfeadd4d5617b6e33ULL), + make_floatx80_init(0xbfb9, 0xdda19d8305ddc420ULL) }, + { make_floatx80_init(0x3ffc, 0xfadbafc96406eb15ULL), + make_floatx80_init(0x3fbb, 0xdb8f3debef442fccULL) }, + { make_floatx80_init(0x3ffd, 0xb7b0ca0f26f78474ULL), + make_floatx80_init(0xbfbc, 0xeab9bdba460376faULL) }, + { make_floatx80_init(0x3ffd, 0xed63382b0dda7b45ULL), + make_floatx80_init(0x3fbc, 0xdfc88bd978751a06ULL) }, + { make_floatx80_init(0x3ffe, 0x8f005d5ef7f59f9bULL), + make_floatx80_init(0x3fbd, 0xb906bc2ccb886e90ULL) }, + { make_floatx80_init(0x3ffe, 0xa4bc7d1934f70924ULL), + make_floatx80_init(0x3fbb, 0xcd43f9522bed64f8ULL) }, + { make_floatx80_init(0x3ffe, 0xb8053e2bc2319e74ULL), + make_floatx80_init(0xbfbc, 0xd3496ab7bd6eef0cULL) }, + { make_floatx80_init(0x3ffe, 0xc90fdaa22168c235ULL), + make_floatx80_init(0xbfbc, 0xece675d1fc8f8cbcULL) }, +}; + +void helper_fpatan(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + uint64_t arg0_sig = extractFloatx80Frac(ST0); + int32_t arg0_exp = extractFloatx80Exp(ST0); + bool arg0_sign = extractFloatx80Sign(ST0); + uint64_t arg1_sig = extractFloatx80Frac(ST1); + int32_t arg1_exp = extractFloatx80Exp(ST1); + bool arg1_sign = extractFloatx80Sign(ST1); + + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST0, &env->fp_status); + } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST1, &env->fp_status); + } else if (floatx80_invalid_encoding(ST0) || + floatx80_invalid_encoding(ST1)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_any_nan(ST0)) { + ST1 = ST0; + } else if (floatx80_is_any_nan(ST1)) { + /* Pass this NaN through. */ + } else if (floatx80_is_zero(ST1) && !arg0_sign) { + /* Pass this zero through. */ + } else if (((floatx80_is_infinity(ST0) && !floatx80_is_infinity(ST1)) || + arg0_exp - arg1_exp >= 80) && + !arg0_sign) { + /* + * Dividing ST1 by ST0 gives the correct result up to + * rounding, and avoids spurious underflow exceptions that + * might result from passing some small values through the + * polynomial approximation, but if a finite nonzero result of + * division is exact, the result of fpatan is still inexact + * (and underflowing where appropriate). + */ + signed char save_prec = env->fp_status.floatx80_rounding_precision; + env->fp_status.floatx80_rounding_precision = 80; + ST1 = floatx80_div(ST1, ST0, &env->fp_status); + env->fp_status.floatx80_rounding_precision = save_prec; + if (!floatx80_is_zero(ST1) && + !(get_float_exception_flags(&env->fp_status) & + float_flag_inexact)) { + /* + * The mathematical result is very slightly closer to zero + * than this exact result. Round a value with the + * significand adjusted accordingly to get the correct + * exceptions, and possibly an adjusted result depending + * on the rounding mode. + */ + uint64_t sig = extractFloatx80Frac(ST1); + int32_t exp = extractFloatx80Exp(ST1); + bool sign = extractFloatx80Sign(ST1); + if (exp == 0) { + normalizeFloatx80Subnormal(sig, &exp, &sig); + } + ST1 = normalizeRoundAndPackFloatx80(80, sign, exp, sig - 1, + -1, &env->fp_status); + } + } else { + /* The result is inexact. */ + bool rsign = arg1_sign; + int32_t rexp; + uint64_t rsig0, rsig1; + if (floatx80_is_zero(ST1)) { + /* + * ST0 is negative. The result is pi with the sign of + * ST1. + */ + rexp = pi_exp; + rsig0 = pi_sig_high; + rsig1 = pi_sig_low; + } else if (floatx80_is_infinity(ST1)) { + if (floatx80_is_infinity(ST0)) { + if (arg0_sign) { + rexp = pi_34_exp; + rsig0 = pi_34_sig_high; + rsig1 = pi_34_sig_low; + } else { + rexp = pi_4_exp; + rsig0 = pi_4_sig_high; + rsig1 = pi_4_sig_low; + } + } else { + rexp = pi_2_exp; + rsig0 = pi_2_sig_high; + rsig1 = pi_2_sig_low; + } + } else if (floatx80_is_zero(ST0) || arg1_exp - arg0_exp >= 80) { + rexp = pi_2_exp; + rsig0 = pi_2_sig_high; + rsig1 = pi_2_sig_low; + } else if (floatx80_is_infinity(ST0) || arg0_exp - arg1_exp >= 80) { + /* ST0 is negative. */ + rexp = pi_exp; + rsig0 = pi_sig_high; + rsig1 = pi_sig_low; + } else { + /* + * ST0 and ST1 are finite, nonzero and with exponents not + * too far apart. + */ + int32_t adj_exp, num_exp, den_exp, xexp, yexp, n, texp, zexp, aexp; + int32_t azexp, axexp; + bool adj_sub, ysign, zsign; + uint64_t adj_sig0, adj_sig1, num_sig, den_sig, xsig0, xsig1; + uint64_t msig0, msig1, msig2, remsig0, remsig1, remsig2; + uint64_t ysig0, ysig1, tsig, zsig0, zsig1, asig0, asig1; + uint64_t azsig0, azsig1; + uint64_t azsig2, azsig3, axsig0, axsig1; + floatx80 x8; + FloatRoundMode save_mode = env->fp_status.float_rounding_mode; + signed char save_prec = env->fp_status.floatx80_rounding_precision; + env->fp_status.float_rounding_mode = float_round_nearest_even; + env->fp_status.floatx80_rounding_precision = 80; + + if (arg0_exp == 0) { + normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); + } + if (arg1_exp == 0) { + normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); + } + if (arg0_exp > arg1_exp || + (arg0_exp == arg1_exp && arg0_sig >= arg1_sig)) { + /* Work with abs(ST1) / abs(ST0). */ + num_exp = arg1_exp; + num_sig = arg1_sig; + den_exp = arg0_exp; + den_sig = arg0_sig; + if (arg0_sign) { + /* The result is subtracted from pi. */ + adj_exp = pi_exp; + adj_sig0 = pi_sig_high; + adj_sig1 = pi_sig_low; + adj_sub = true; + } else { + /* The result is used as-is. */ + adj_exp = 0; + adj_sig0 = 0; + adj_sig1 = 0; + adj_sub = false; + } + } else { + /* Work with abs(ST0) / abs(ST1). */ + num_exp = arg0_exp; + num_sig = arg0_sig; + den_exp = arg1_exp; + den_sig = arg1_sig; + /* The result is added to or subtracted from pi/2. */ + adj_exp = pi_2_exp; + adj_sig0 = pi_2_sig_high; + adj_sig1 = pi_2_sig_low; + adj_sub = !arg0_sign; + } + + /* + * Compute x = num/den, where 0 < x <= 1 and x is not too + * small. + */ + xexp = num_exp - den_exp + 0x3ffe; + remsig0 = num_sig; + remsig1 = 0; + if (den_sig <= remsig0) { + shift128Right(remsig0, remsig1, 1, &remsig0, &remsig1); + ++xexp; + } + xsig0 = estimateDiv128To64(remsig0, remsig1, den_sig); + mul64To128(den_sig, xsig0, &msig0, &msig1); + sub128(remsig0, remsig1, msig0, msig1, &remsig0, &remsig1); + while ((int64_t) remsig0 < 0) { + --xsig0; + add128(remsig0, remsig1, 0, den_sig, &remsig0, &remsig1); + } + xsig1 = estimateDiv128To64(remsig1, 0, den_sig); + /* + * No need to correct any estimation error in xsig1; even + * with such error, it is accurate enough. + */ + + /* + * Split x as x = t + y, where t = n/8 is the nearest + * multiple of 1/8 to x. + */ + x8 = normalizeRoundAndPackFloatx80(80, false, xexp + 3, xsig0, + xsig1, &env->fp_status); + n = floatx80_to_int32(x8, &env->fp_status); + if (n == 0) { + ysign = false; + yexp = xexp; + ysig0 = xsig0; + ysig1 = xsig1; + texp = 0; + tsig = 0; + } else { + int shift = clz32(n) + 32; + texp = 0x403b - shift; + tsig = n; + tsig <<= shift; + if (texp == xexp) { + sub128(xsig0, xsig1, tsig, 0, &ysig0, &ysig1); + if ((int64_t) ysig0 >= 0) { + ysign = false; + if (ysig0 == 0) { + if (ysig1 == 0) { + yexp = 0; + } else { + shift = clz64(ysig1) + 64; + yexp = xexp - shift; + shift128Left(ysig0, ysig1, shift, + &ysig0, &ysig1); + } + } else { + shift = clz64(ysig0); + yexp = xexp - shift; + shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); + } + } else { + ysign = true; + sub128(0, 0, ysig0, ysig1, &ysig0, &ysig1); + if (ysig0 == 0) { + shift = clz64(ysig1) + 64; + } else { + shift = clz64(ysig0); + } + yexp = xexp - shift; + shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); + } + } else { + /* + * t's exponent must be greater than x's because t + * is positive and the nearest multiple of 1/8 to + * x, and if x has a greater exponent, the power + * of 2 with that exponent is also a multiple of + * 1/8. + */ + uint64_t usig0, usig1; + shift128RightJamming(xsig0, xsig1, texp - xexp, + &usig0, &usig1); + ysign = true; + sub128(tsig, 0, usig0, usig1, &ysig0, &ysig1); + if (ysig0 == 0) { + shift = clz64(ysig1) + 64; + } else { + shift = clz64(ysig0); + } + yexp = texp - shift; + shift128Left(ysig0, ysig1, shift, &ysig0, &ysig1); + } + } + + /* + * Compute z = y/(1+tx), so arctan(x) = arctan(t) + + * arctan(z). + */ + zsign = ysign; + if (texp == 0 || yexp == 0) { + zexp = yexp; + zsig0 = ysig0; + zsig1 = ysig1; + } else { + /* + * t <= 1, x <= 1 and if both are 1 then y is 0, so tx < 1. + */ + int32_t dexp = texp + xexp - 0x3ffe; + uint64_t dsig0, dsig1, dsig2; + mul128By64To192(xsig0, xsig1, tsig, &dsig0, &dsig1, &dsig2); + /* + * dexp <= 0x3fff (and if equal, dsig0 has a leading 0 + * bit). Add 1 to produce the denominator 1+tx. + */ + shift128RightJamming(dsig0, dsig1, 0x3fff - dexp, + &dsig0, &dsig1); + dsig0 |= 0x8000000000000000ULL; + zexp = yexp - 1; + remsig0 = ysig0; + remsig1 = ysig1; + remsig2 = 0; + if (dsig0 <= remsig0) { + shift128Right(remsig0, remsig1, 1, &remsig0, &remsig1); + ++zexp; + } + zsig0 = estimateDiv128To64(remsig0, remsig1, dsig0); + mul128By64To192(dsig0, dsig1, zsig0, &msig0, &msig1, &msig2); + sub192(remsig0, remsig1, remsig2, msig0, msig1, msig2, + &remsig0, &remsig1, &remsig2); + while ((int64_t) remsig0 < 0) { + --zsig0; + add192(remsig0, remsig1, remsig2, 0, dsig0, dsig1, + &remsig0, &remsig1, &remsig2); + } + zsig1 = estimateDiv128To64(remsig1, remsig2, dsig0); + /* No need to correct any estimation error in zsig1. */ + } + + if (zexp == 0) { + azexp = 0; + azsig0 = 0; + azsig1 = 0; + } else { + floatx80 z2, accum; + uint64_t z2sig0, z2sig1, z2sig2, z2sig3; + /* Compute z^2. */ + mul128To256(zsig0, zsig1, zsig0, zsig1, + &z2sig0, &z2sig1, &z2sig2, &z2sig3); + z2 = normalizeRoundAndPackFloatx80(80, false, + zexp + zexp - 0x3ffe, + z2sig0, z2sig1, + &env->fp_status); + + /* Compute the lower parts of the polynomial expansion. */ + accum = floatx80_mul(fpatan_coeff_6, z2, &env->fp_status); + accum = floatx80_add(fpatan_coeff_5, accum, &env->fp_status); + accum = floatx80_mul(accum, z2, &env->fp_status); + accum = floatx80_add(fpatan_coeff_4, accum, &env->fp_status); + accum = floatx80_mul(accum, z2, &env->fp_status); + accum = floatx80_add(fpatan_coeff_3, accum, &env->fp_status); + accum = floatx80_mul(accum, z2, &env->fp_status); + accum = floatx80_add(fpatan_coeff_2, accum, &env->fp_status); + accum = floatx80_mul(accum, z2, &env->fp_status); + accum = floatx80_add(fpatan_coeff_1, accum, &env->fp_status); + accum = floatx80_mul(accum, z2, &env->fp_status); + + /* + * The full polynomial expansion is z*(fpatan_coeff_0 + accum). + * fpatan_coeff_0 is 1, and accum is negative and much smaller. + */ + aexp = extractFloatx80Exp(fpatan_coeff_0); + shift128RightJamming(extractFloatx80Frac(accum), 0, + aexp - extractFloatx80Exp(accum), + &asig0, &asig1); + sub128(extractFloatx80Frac(fpatan_coeff_0), 0, asig0, asig1, + &asig0, &asig1); + /* Multiply by z to compute arctan(z). */ + azexp = aexp + zexp - 0x3ffe; + mul128To256(asig0, asig1, zsig0, zsig1, &azsig0, &azsig1, + &azsig2, &azsig3); + } + + /* Add arctan(t) (positive or zero) and arctan(z) (sign zsign). */ + if (texp == 0) { + /* z is positive. */ + axexp = azexp; + axsig0 = azsig0; + axsig1 = azsig1; + } else { + bool low_sign = extractFloatx80Sign(fpatan_table[n].atan_low); + int32_t low_exp = extractFloatx80Exp(fpatan_table[n].atan_low); + uint64_t low_sig0 = + extractFloatx80Frac(fpatan_table[n].atan_low); + uint64_t low_sig1 = 0; + axexp = extractFloatx80Exp(fpatan_table[n].atan_high); + axsig0 = extractFloatx80Frac(fpatan_table[n].atan_high); + axsig1 = 0; + shift128RightJamming(low_sig0, low_sig1, axexp - low_exp, + &low_sig0, &low_sig1); + if (low_sign) { + sub128(axsig0, axsig1, low_sig0, low_sig1, + &axsig0, &axsig1); + } else { + add128(axsig0, axsig1, low_sig0, low_sig1, + &axsig0, &axsig1); + } + if (azexp >= axexp) { + shift128RightJamming(axsig0, axsig1, azexp - axexp + 1, + &axsig0, &axsig1); + axexp = azexp + 1; + shift128RightJamming(azsig0, azsig1, 1, + &azsig0, &azsig1); + } else { + shift128RightJamming(axsig0, axsig1, 1, + &axsig0, &axsig1); + shift128RightJamming(azsig0, azsig1, axexp - azexp + 1, + &azsig0, &azsig1); + ++axexp; + } + if (zsign) { + sub128(axsig0, axsig1, azsig0, azsig1, + &axsig0, &axsig1); + } else { + add128(axsig0, axsig1, azsig0, azsig1, + &axsig0, &axsig1); + } + } + + if (adj_exp == 0) { + rexp = axexp; + rsig0 = axsig0; + rsig1 = axsig1; + } else { + /* + * Add or subtract arctan(x) (exponent axexp, + * significand axsig0 and axsig1, positive, not + * necessarily normalized) to the number given by + * adj_exp, adj_sig0 and adj_sig1, according to + * adj_sub. + */ + if (adj_exp >= axexp) { + shift128RightJamming(axsig0, axsig1, adj_exp - axexp + 1, + &axsig0, &axsig1); + rexp = adj_exp + 1; + shift128RightJamming(adj_sig0, adj_sig1, 1, + &adj_sig0, &adj_sig1); + } else { + shift128RightJamming(axsig0, axsig1, 1, + &axsig0, &axsig1); + shift128RightJamming(adj_sig0, adj_sig1, + axexp - adj_exp + 1, + &adj_sig0, &adj_sig1); + rexp = axexp + 1; + } + if (adj_sub) { + sub128(adj_sig0, adj_sig1, axsig0, axsig1, + &rsig0, &rsig1); + } else { + add128(adj_sig0, adj_sig1, axsig0, axsig1, + &rsig0, &rsig1); + } + } + + env->fp_status.float_rounding_mode = save_mode; + env->fp_status.floatx80_rounding_precision = save_prec; + } + /* This result is inexact. */ + rsig1 |= 1; + ST1 = normalizeRoundAndPackFloatx80(80, rsign, rexp, + rsig0, rsig1, &env->fp_status); + } + + fpop(env); + merge_exception_flags(env, old_flags); +} + +void helper_fxtract(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + CPU_LDoubleU temp; + + temp.d = ST0; + + if (floatx80_is_zero(ST0)) { + /* Easy way to generate -inf and raising division by 0 exception */ + ST0 = floatx80_div(floatx80_chs(floatx80_one), floatx80_zero, + &env->fp_status); + fpush(env); + ST0 = temp.d; + } else if (floatx80_invalid_encoding(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + fpush(env); + ST0 = ST1; + } else if (floatx80_is_any_nan(ST0)) { + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_silence_nan(ST0, &env->fp_status); + } + fpush(env); + ST0 = ST1; + } else if (floatx80_is_infinity(ST0)) { + fpush(env); + ST0 = ST1; + ST1 = floatx80_infinity; + } else { + int expdif; + + if (EXPD(temp) == 0) { + int shift = clz64(temp.l.lower); + temp.l.lower <<= shift; + expdif = 1 - EXPBIAS - shift; + float_raise(float_flag_input_denormal, &env->fp_status); + } else { + expdif = EXPD(temp) - EXPBIAS; + } + /* DP exponent bias */ + ST0 = int32_to_floatx80(expdif, &env->fp_status); + fpush(env); + BIASEXPONENT(temp); + ST0 = temp.d; + } + merge_exception_flags(env, old_flags); +} + +static void helper_fprem_common(CPUX86State *env, bool mod) +{ + uint8_t old_flags = save_exception_flags(env); + uint64_t quotient; + CPU_LDoubleU temp0, temp1; + int exp0, exp1, expdiff; + + temp0.d = ST0; + temp1.d = ST1; + exp0 = EXPD(temp0); + exp1 = EXPD(temp1); + + env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ + if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || + exp0 == 0x7fff || exp1 == 0x7fff || + floatx80_invalid_encoding(ST0) || floatx80_invalid_encoding(ST1)) { + ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); + } else { + if (exp0 == 0) { + exp0 = 1 - clz64(temp0.l.lower); + } + if (exp1 == 0) { + exp1 = 1 - clz64(temp1.l.lower); + } + expdiff = exp0 - exp1; + if (expdiff < 64) { + ST0 = floatx80_modrem(ST0, ST1, mod, "ient, &env->fp_status); + env->fpus |= (quotient & 0x4) << (8 - 2); /* (C0) <-- q2 */ + env->fpus |= (quotient & 0x2) << (14 - 1); /* (C3) <-- q1 */ + env->fpus |= (quotient & 0x1) << (9 - 0); /* (C1) <-- q0 */ + } else { + /* + * Partial remainder. This choice of how many bits to + * process at once is specified in AMD instruction set + * manuals, and empirically is followed by Intel + * processors as well; it ensures that the final remainder + * operation in a loop does produce the correct low three + * bits of the quotient. AMD manuals specify that the + * flags other than C2 are cleared, and empirically Intel + * processors clear them as well. + */ + int n = 32 + (expdiff % 32); + temp1.d = floatx80_scalbn(temp1.d, expdiff - n, &env->fp_status); + ST0 = floatx80_mod(ST0, temp1.d, &env->fp_status); + env->fpus |= 0x400; /* C2 <-- 1 */ + } + } + merge_exception_flags(env, old_flags); +} + +void helper_fprem1(CPUX86State *env) +{ + helper_fprem_common(env, false); +} + +void helper_fprem(CPUX86State *env) +{ + helper_fprem_common(env, true); +} + +/* 128-bit significand of log2(e). */ +#define log2_e_sig_high 0xb8aa3b295c17f0bbULL +#define log2_e_sig_low 0xbe87fed0691d3e89ULL + +/* + * Polynomial coefficients for an approximation to log2((1+x)/(1-x)), + * with only odd powers of x used, for x in the interval [2*sqrt(2)-3, + * 3-2*sqrt(2)], which corresponds to logarithms of numbers in the + * interval [sqrt(2)/2, sqrt(2)]. + */ +#define fyl2x_coeff_0 make_floatx80(0x4000, 0xb8aa3b295c17f0bcULL) +#define fyl2x_coeff_0_low make_floatx80(0xbfbf, 0x834972fe2d7bab1bULL) +#define fyl2x_coeff_1 make_floatx80(0x3ffe, 0xf6384ee1d01febb8ULL) +#define fyl2x_coeff_2 make_floatx80(0x3ffe, 0x93bb62877cdfa2e3ULL) +#define fyl2x_coeff_3 make_floatx80(0x3ffd, 0xd30bb153d808f269ULL) +#define fyl2x_coeff_4 make_floatx80(0x3ffd, 0xa42589eaf451499eULL) +#define fyl2x_coeff_5 make_floatx80(0x3ffd, 0x864d42c0f8f17517ULL) +#define fyl2x_coeff_6 make_floatx80(0x3ffc, 0xe3476578adf26272ULL) +#define fyl2x_coeff_7 make_floatx80(0x3ffc, 0xc506c5f874e6d80fULL) +#define fyl2x_coeff_8 make_floatx80(0x3ffc, 0xac5cf50cc57d6372ULL) +#define fyl2x_coeff_9 make_floatx80(0x3ffc, 0xb1ed0066d971a103ULL) + +/* + * Compute an approximation of log2(1+arg), where 1+arg is in the + * interval [sqrt(2)/2, sqrt(2)]. It is assumed that when this + * function is called, rounding precision is set to 80 and the + * round-to-nearest mode is in effect. arg must not be exactly zero, + * and must not be so close to zero that underflow might occur. + */ +static void helper_fyl2x_common(CPUX86State *env, floatx80 arg, int32_t *exp, + uint64_t *sig0, uint64_t *sig1) +{ + uint64_t arg0_sig = extractFloatx80Frac(arg); + int32_t arg0_exp = extractFloatx80Exp(arg); + bool arg0_sign = extractFloatx80Sign(arg); + bool asign; + int32_t dexp, texp, aexp; + uint64_t dsig0, dsig1, tsig0, tsig1, rsig0, rsig1, rsig2; + uint64_t msig0, msig1, msig2, t2sig0, t2sig1, t2sig2, t2sig3; + uint64_t asig0, asig1, asig2, asig3, bsig0, bsig1; + floatx80 t2, accum; + + /* + * Compute an approximation of arg/(2+arg), with extra precision, + * as the argument to a polynomial approximation. The extra + * precision is only needed for the first term of the + * approximation, with subsequent terms being significantly + * smaller; the approximation only uses odd exponents, and the + * square of arg/(2+arg) is at most 17-12*sqrt(2) = 0.029.... + */ + if (arg0_sign) { + dexp = 0x3fff; + shift128RightJamming(arg0_sig, 0, dexp - arg0_exp, &dsig0, &dsig1); + sub128(0, 0, dsig0, dsig1, &dsig0, &dsig1); + } else { + dexp = 0x4000; + shift128RightJamming(arg0_sig, 0, dexp - arg0_exp, &dsig0, &dsig1); + dsig0 |= 0x8000000000000000ULL; + } + texp = arg0_exp - dexp + 0x3ffe; + rsig0 = arg0_sig; + rsig1 = 0; + rsig2 = 0; + if (dsig0 <= rsig0) { + shift128Right(rsig0, rsig1, 1, &rsig0, &rsig1); + ++texp; + } + tsig0 = estimateDiv128To64(rsig0, rsig1, dsig0); + mul128By64To192(dsig0, dsig1, tsig0, &msig0, &msig1, &msig2); + sub192(rsig0, rsig1, rsig2, msig0, msig1, msig2, + &rsig0, &rsig1, &rsig2); + while ((int64_t) rsig0 < 0) { + --tsig0; + add192(rsig0, rsig1, rsig2, 0, dsig0, dsig1, + &rsig0, &rsig1, &rsig2); + } + tsig1 = estimateDiv128To64(rsig1, rsig2, dsig0); + /* + * No need to correct any estimation error in tsig1; even with + * such error, it is accurate enough. Now compute the square of + * that approximation. + */ + mul128To256(tsig0, tsig1, tsig0, tsig1, + &t2sig0, &t2sig1, &t2sig2, &t2sig3); + t2 = normalizeRoundAndPackFloatx80(80, false, texp + texp - 0x3ffe, + t2sig0, t2sig1, &env->fp_status); + + /* Compute the lower parts of the polynomial expansion. */ + accum = floatx80_mul(fyl2x_coeff_9, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_8, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_7, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_6, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_5, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_4, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_3, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_2, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_1, accum, &env->fp_status); + accum = floatx80_mul(accum, t2, &env->fp_status); + accum = floatx80_add(fyl2x_coeff_0_low, accum, &env->fp_status); + + /* + * The full polynomial expansion is fyl2x_coeff_0 + accum (where + * accum has much lower magnitude, and so, in particular, carry + * out of the addition is not possible), multiplied by t. (This + * expansion is only accurate to about 70 bits, not 128 bits.) + */ + aexp = extractFloatx80Exp(fyl2x_coeff_0); + asign = extractFloatx80Sign(fyl2x_coeff_0); + shift128RightJamming(extractFloatx80Frac(accum), 0, + aexp - extractFloatx80Exp(accum), + &asig0, &asig1); + bsig0 = extractFloatx80Frac(fyl2x_coeff_0); + bsig1 = 0; + if (asign == extractFloatx80Sign(accum)) { + add128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); + } else { + sub128(bsig0, bsig1, asig0, asig1, &asig0, &asig1); + } + /* Multiply by t to compute the required result. */ + mul128To256(asig0, asig1, tsig0, tsig1, + &asig0, &asig1, &asig2, &asig3); + aexp += texp - 0x3ffe; + *exp = aexp; + *sig0 = asig0; + *sig1 = asig1; +} + +void helper_fyl2xp1(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + uint64_t arg0_sig = extractFloatx80Frac(ST0); + int32_t arg0_exp = extractFloatx80Exp(ST0); + bool arg0_sign = extractFloatx80Sign(ST0); + uint64_t arg1_sig = extractFloatx80Frac(ST1); + int32_t arg1_exp = extractFloatx80Exp(ST1); + bool arg1_sign = extractFloatx80Sign(ST1); + + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST0, &env->fp_status); + } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST1, &env->fp_status); + } else if (floatx80_invalid_encoding(ST0) || + floatx80_invalid_encoding(ST1)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_any_nan(ST0)) { + ST1 = ST0; + } else if (floatx80_is_any_nan(ST1)) { + /* Pass this NaN through. */ + } else if (arg0_exp > 0x3ffd || + (arg0_exp == 0x3ffd && arg0_sig > (arg0_sign ? + 0x95f619980c4336f7ULL : + 0xd413cccfe7799211ULL))) { + /* + * Out of range for the instruction (ST0 must have absolute + * value less than 1 - sqrt(2)/2 = 0.292..., according to + * Intel manuals; AMD manuals allow a range from sqrt(2)/2 - 1 + * to sqrt(2) - 1, which we allow here), treat as invalid. + */ + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_zero(ST0) || floatx80_is_zero(ST1) || + arg1_exp == 0x7fff) { + /* + * One argument is zero, or multiplying by infinity; correct + * result is exact and can be obtained by multiplying the + * arguments. + */ + ST1 = floatx80_mul(ST0, ST1, &env->fp_status); + } else if (arg0_exp < 0x3fb0) { + /* + * Multiplying both arguments and an extra-precision version + * of log2(e) is sufficiently precise. + */ + uint64_t sig0, sig1, sig2; + int32_t exp; + if (arg0_exp == 0) { + normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); + } + if (arg1_exp == 0) { + normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); + } + mul128By64To192(log2_e_sig_high, log2_e_sig_low, arg0_sig, + &sig0, &sig1, &sig2); + exp = arg0_exp + 1; + mul128By64To192(sig0, sig1, arg1_sig, &sig0, &sig1, &sig2); + exp += arg1_exp - 0x3ffe; + /* This result is inexact. */ + sig1 |= 1; + ST1 = normalizeRoundAndPackFloatx80(80, arg0_sign ^ arg1_sign, exp, + sig0, sig1, &env->fp_status); + } else { + int32_t aexp; + uint64_t asig0, asig1, asig2; + FloatRoundMode save_mode = env->fp_status.float_rounding_mode; + signed char save_prec = env->fp_status.floatx80_rounding_precision; + env->fp_status.float_rounding_mode = float_round_nearest_even; + env->fp_status.floatx80_rounding_precision = 80; + + helper_fyl2x_common(env, ST0, &aexp, &asig0, &asig1); + /* + * Multiply by the second argument to compute the required + * result. + */ + if (arg1_exp == 0) { + normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); + } + mul128By64To192(asig0, asig1, arg1_sig, &asig0, &asig1, &asig2); + aexp += arg1_exp - 0x3ffe; + /* This result is inexact. */ + asig1 |= 1; + env->fp_status.float_rounding_mode = save_mode; + ST1 = normalizeRoundAndPackFloatx80(80, arg0_sign ^ arg1_sign, aexp, + asig0, asig1, &env->fp_status); + env->fp_status.floatx80_rounding_precision = save_prec; + } + fpop(env); + merge_exception_flags(env, old_flags); +} + +void helper_fyl2x(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + uint64_t arg0_sig = extractFloatx80Frac(ST0); + int32_t arg0_exp = extractFloatx80Exp(ST0); + bool arg0_sign = extractFloatx80Sign(ST0); + uint64_t arg1_sig = extractFloatx80Frac(ST1); + int32_t arg1_exp = extractFloatx80Exp(ST1); + bool arg1_sign = extractFloatx80Sign(ST1); + + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST0, &env->fp_status); + } else if (floatx80_is_signaling_nan(ST1, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_silence_nan(ST1, &env->fp_status); + } else if (floatx80_invalid_encoding(ST0) || + floatx80_invalid_encoding(ST1)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_any_nan(ST0)) { + ST1 = ST0; + } else if (floatx80_is_any_nan(ST1)) { + /* Pass this NaN through. */ + } else if (arg0_sign && !floatx80_is_zero(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_infinity(ST1)) { + FloatRelation cmp = floatx80_compare(ST0, floatx80_one, + &env->fp_status); + switch (cmp) { + case float_relation_less: + ST1 = floatx80_chs(ST1); + break; + case float_relation_greater: + /* Result is infinity of the same sign as ST1. */ + break; + default: + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + break; + } + } else if (floatx80_is_infinity(ST0)) { + if (floatx80_is_zero(ST1)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else if (arg1_sign) { + ST1 = floatx80_chs(ST0); + } else { + ST1 = ST0; + } + } else if (floatx80_is_zero(ST0)) { + if (floatx80_is_zero(ST1)) { + float_raise(float_flag_invalid, &env->fp_status); + ST1 = floatx80_default_nan(&env->fp_status); + } else { + /* Result is infinity with opposite sign to ST1. */ + float_raise(float_flag_divbyzero, &env->fp_status); + ST1 = make_floatx80(arg1_sign ? 0x7fff : 0xffff, + 0x8000000000000000ULL); + } + } else if (floatx80_is_zero(ST1)) { + if (floatx80_lt(ST0, floatx80_one, &env->fp_status)) { + ST1 = floatx80_chs(ST1); + } + /* Otherwise, ST1 is already the correct result. */ + } else if (floatx80_eq(ST0, floatx80_one, &env->fp_status)) { + if (arg1_sign) { + ST1 = floatx80_chs(floatx80_zero); + } else { + ST1 = floatx80_zero; + } + } else { + int32_t int_exp; + floatx80 arg0_m1; + FloatRoundMode save_mode = env->fp_status.float_rounding_mode; + signed char save_prec = env->fp_status.floatx80_rounding_precision; + env->fp_status.float_rounding_mode = float_round_nearest_even; + env->fp_status.floatx80_rounding_precision = 80; + + if (arg0_exp == 0) { + normalizeFloatx80Subnormal(arg0_sig, &arg0_exp, &arg0_sig); + } + if (arg1_exp == 0) { + normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); + } + int_exp = arg0_exp - 0x3fff; + if (arg0_sig > 0xb504f333f9de6484ULL) { + ++int_exp; + } + arg0_m1 = floatx80_sub(floatx80_scalbn(ST0, -int_exp, + &env->fp_status), + floatx80_one, &env->fp_status); + if (floatx80_is_zero(arg0_m1)) { + /* Exact power of 2; multiply by ST1. */ + env->fp_status.float_rounding_mode = save_mode; + ST1 = floatx80_mul(int32_to_floatx80(int_exp, &env->fp_status), + ST1, &env->fp_status); + } else { + bool asign = extractFloatx80Sign(arg0_m1); + int32_t aexp; + uint64_t asig0, asig1, asig2; + helper_fyl2x_common(env, arg0_m1, &aexp, &asig0, &asig1); + if (int_exp != 0) { + bool isign = (int_exp < 0); + int32_t iexp; + uint64_t isig; + int shift; + int_exp = isign ? -int_exp : int_exp; + shift = clz32(int_exp) + 32; + isig = int_exp; + isig <<= shift; + iexp = 0x403e - shift; + shift128RightJamming(asig0, asig1, iexp - aexp, + &asig0, &asig1); + if (asign == isign) { + add128(isig, 0, asig0, asig1, &asig0, &asig1); + } else { + sub128(isig, 0, asig0, asig1, &asig0, &asig1); + } + aexp = iexp; + asign = isign; + } + /* + * Multiply by the second argument to compute the required + * result. + */ + if (arg1_exp == 0) { + normalizeFloatx80Subnormal(arg1_sig, &arg1_exp, &arg1_sig); + } + mul128By64To192(asig0, asig1, arg1_sig, &asig0, &asig1, &asig2); + aexp += arg1_exp - 0x3ffe; + /* This result is inexact. */ + asig1 |= 1; + env->fp_status.float_rounding_mode = save_mode; + ST1 = normalizeRoundAndPackFloatx80(80, asign ^ arg1_sign, aexp, + asig0, asig1, &env->fp_status); + } + + env->fp_status.floatx80_rounding_precision = save_prec; + } + fpop(env); + merge_exception_flags(env, old_flags); +} + +void helper_fsqrt(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + if (floatx80_is_neg(ST0)) { + env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ + env->fpus |= 0x400; + } + ST0 = floatx80_sqrt(ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fsincos(CPUX86State *env) +{ + double fptemp = floatx80_to_double(env, ST0); + + if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = double_to_floatx80(env, sin(fptemp)); + fpush(env); + ST0 = double_to_floatx80(env, cos(fptemp)); + env->fpus &= ~0x400; /* C2 <-- 0 */ + /* the above code is for |arg| < 2**63 only */ + } +} + +void helper_frndint(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + ST0 = floatx80_round_to_int(ST0, &env->fp_status); + merge_exception_flags(env, old_flags); +} + +void helper_fscale(CPUX86State *env) +{ + uint8_t old_flags = save_exception_flags(env); + if (floatx80_invalid_encoding(ST1) || floatx80_invalid_encoding(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + } else if (floatx80_is_any_nan(ST1)) { + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + } + ST0 = ST1; + if (floatx80_is_signaling_nan(ST0, &env->fp_status)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_silence_nan(ST0, &env->fp_status); + } + } else if (floatx80_is_infinity(ST1) && + !floatx80_invalid_encoding(ST0) && + !floatx80_is_any_nan(ST0)) { + if (floatx80_is_neg(ST1)) { + if (floatx80_is_infinity(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + } else { + ST0 = (floatx80_is_neg(ST0) ? + floatx80_chs(floatx80_zero) : + floatx80_zero); + } + } else { + if (floatx80_is_zero(ST0)) { + float_raise(float_flag_invalid, &env->fp_status); + ST0 = floatx80_default_nan(&env->fp_status); + } else { + ST0 = (floatx80_is_neg(ST0) ? + floatx80_chs(floatx80_infinity) : + floatx80_infinity); + } + } + } else { + int n; + signed char save = env->fp_status.floatx80_rounding_precision; + uint8_t save_flags = get_float_exception_flags(&env->fp_status); + set_float_exception_flags(0, &env->fp_status); + n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); + set_float_exception_flags(save_flags, &env->fp_status); + env->fp_status.floatx80_rounding_precision = 80; + ST0 = floatx80_scalbn(ST0, n, &env->fp_status); + env->fp_status.floatx80_rounding_precision = save; + } + merge_exception_flags(env, old_flags); +} + +void helper_fsin(CPUX86State *env) +{ + double fptemp = floatx80_to_double(env, ST0); + + if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = double_to_floatx80(env, sin(fptemp)); + env->fpus &= ~0x400; /* C2 <-- 0 */ + /* the above code is for |arg| < 2**53 only */ + } +} + +void helper_fcos(CPUX86State *env) +{ + double fptemp = floatx80_to_double(env, ST0); + + if ((fptemp > MAXTAN) || (fptemp < -MAXTAN)) { + env->fpus |= 0x400; + } else { + ST0 = double_to_floatx80(env, cos(fptemp)); + env->fpus &= ~0x400; /* C2 <-- 0 */ + /* the above code is for |arg| < 2**63 only */ + } +} + +void helper_fxam_ST0(CPUX86State *env) +{ + CPU_LDoubleU temp; + int expdif; + + temp.d = ST0; + + env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ + if (SIGND(temp)) { + env->fpus |= 0x200; /* C1 <-- 1 */ + } + + if (env->fptags[env->fpstt]) { + env->fpus |= 0x4100; /* Empty */ + return; + } + + expdif = EXPD(temp); + if (expdif == MAXEXPD) { + if (MANTD(temp) == 0x8000000000000000ULL) { + env->fpus |= 0x500; /* Infinity */ + } else if (MANTD(temp) & 0x8000000000000000ULL) { + env->fpus |= 0x100; /* NaN */ + } + } else if (expdif == 0) { + if (MANTD(temp) == 0) { + env->fpus |= 0x4000; /* Zero */ + } else { + env->fpus |= 0x4400; /* Denormal */ + } + } else if (MANTD(temp) & 0x8000000000000000ULL) { + env->fpus |= 0x400; + } +} + +static void do_fstenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) +{ + int fpus, fptag, exp, i; + uint64_t mant; + CPU_LDoubleU tmp; + + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i = 7; i >= 0; i--) { + fptag <<= 2; + if (env->fptags[i]) { + fptag |= 3; + } else { + tmp.d = env->fpregs[i].d; + exp = EXPD(tmp); + mant = MANTD(tmp); + if (exp == 0 && mant == 0) { + /* zero */ + fptag |= 1; + } else if (exp == 0 || exp == MAXEXPD + || (mant & (1LL << 63)) == 0) { + /* NaNs, infinity, denormal */ + fptag |= 2; + } + } + } + if (data32) { + /* 32 bit */ + cpu_stl_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stl_data_ra(env, ptr + 4, fpus, retaddr); + cpu_stl_data_ra(env, ptr + 8, fptag, retaddr); + cpu_stl_data_ra(env, ptr + 12, 0, retaddr); /* fpip */ + cpu_stl_data_ra(env, ptr + 16, 0, retaddr); /* fpcs */ + cpu_stl_data_ra(env, ptr + 20, 0, retaddr); /* fpoo */ + cpu_stl_data_ra(env, ptr + 24, 0, retaddr); /* fpos */ + } else { + /* 16 bit */ + cpu_stw_data_ra(env, ptr, env->fpuc, retaddr); + cpu_stw_data_ra(env, ptr + 2, fpus, retaddr); + cpu_stw_data_ra(env, ptr + 4, fptag, retaddr); + cpu_stw_data_ra(env, ptr + 6, 0, retaddr); + cpu_stw_data_ra(env, ptr + 8, 0, retaddr); + cpu_stw_data_ra(env, ptr + 10, 0, retaddr); + cpu_stw_data_ra(env, ptr + 12, 0, retaddr); + } +} + +void helper_fstenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fstenv(env, ptr, data32, GETPC()); +} + +static void cpu_set_fpus(CPUX86State *env, uint16_t fpus) +{ + env->fpstt = (fpus >> 11) & 7; + env->fpus = fpus & ~0x3800 & ~FPUS_B; + env->fpus |= env->fpus & FPUS_SE ? FPUS_B : 0; +#if !defined(CONFIG_USER_ONLY) + if (!(env->fpus & FPUS_SE)) { + /* + * Here the processor deasserts FERR#; in response, the chipset deasserts + * IGNNE#. + */ + cpu_clear_ignne(); + } +#endif +} + +static void do_fldenv(CPUX86State *env, target_ulong ptr, int data32, + uintptr_t retaddr) +{ + int i, fpus, fptag; + + if (data32) { + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 4, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 8, retaddr); + } else { + cpu_set_fpuc(env, cpu_lduw_data_ra(env, ptr, retaddr)); + fpus = cpu_lduw_data_ra(env, ptr + 2, retaddr); + fptag = cpu_lduw_data_ra(env, ptr + 4, retaddr); + } + cpu_set_fpus(env, fpus); + for (i = 0; i < 8; i++) { + env->fptags[i] = ((fptag & 3) == 3); + fptag >>= 2; + } +} + +void helper_fldenv(CPUX86State *env, target_ulong ptr, int data32) +{ + do_fldenv(env, ptr, data32, GETPC()); +} + +void helper_fsave(CPUX86State *env, target_ulong ptr, int data32) +{ + floatx80 tmp; + int i; + + do_fstenv(env, ptr, data32, GETPC()); + + ptr += (14 << data32); + for (i = 0; i < 8; i++) { + tmp = ST(i); + helper_fstt(env, tmp, ptr, GETPC()); + ptr += 10; + } + + /* fninit */ + env->fpus = 0; + env->fpstt = 0; + cpu_set_fpuc(env, 0x37f); + env->fptags[0] = 1; + env->fptags[1] = 1; + env->fptags[2] = 1; + env->fptags[3] = 1; + env->fptags[4] = 1; + env->fptags[5] = 1; + env->fptags[6] = 1; + env->fptags[7] = 1; +} + +void helper_frstor(CPUX86State *env, target_ulong ptr, int data32) +{ + floatx80 tmp; + int i; + + do_fldenv(env, ptr, data32, GETPC()); + ptr += (14 << data32); + + for (i = 0; i < 8; i++) { + tmp = helper_fldt(env, ptr, GETPC()); + ST(i) = tmp; + ptr += 10; + } +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_fsave(CPUX86State *env, target_ulong ptr, int data32) +{ + helper_fsave(env, ptr, data32); +} + +void cpu_x86_frstor(CPUX86State *env, target_ulong ptr, int data32) +{ + helper_frstor(env, ptr, data32); +} +#endif + +#define XO(X) offsetof(X86XSaveArea, X) + +static void do_xsave_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int fpus, fptag, i; + target_ulong addr; + + fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; + fptag = 0; + for (i = 0; i < 8; i++) { + fptag |= (env->fptags[i] << i); + } + + cpu_stw_data_ra(env, ptr + XO(legacy.fcw), env->fpuc, ra); + cpu_stw_data_ra(env, ptr + XO(legacy.fsw), fpus, ra); + cpu_stw_data_ra(env, ptr + XO(legacy.ftw), fptag ^ 0xff, ra); + + /* In 32-bit mode this is eip, sel, dp, sel. + In 64-bit mode this is rip, rdp. + But in either case we don't write actual data, just zeros. */ + cpu_stq_data_ra(env, ptr + XO(legacy.fpip), 0, ra); /* eip+sel; rip */ + cpu_stq_data_ra(env, ptr + XO(legacy.fpdp), 0, ra); /* edp+sel; rdp */ + + addr = ptr + XO(legacy.fpregs); + for (i = 0; i < 8; i++) { + floatx80 tmp = ST(i); + helper_fstt(env, tmp, addr, ra); + addr += 16; + } +} + +static void do_xsave_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + update_mxcsr_from_sse_status(env); + cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr), env->mxcsr, ra); + cpu_stl_data_ra(env, ptr + XO(legacy.mxcsr_mask), 0x0000ffff, ra); +} + +static void do_xsave_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + XO(legacy.xmm_regs); + for (i = 0; i < nb_xmm_regs; i++) { + cpu_stq_data_ra(env, addr, env->xmm_regs[i].ZMM_Q(0), ra); + cpu_stq_data_ra(env, addr + 8, env->xmm_regs[i].ZMM_Q(1), ra); + addr += 16; + } +} + +static void do_xsave_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); + int i; + + for (i = 0; i < 4; i++, addr += 16) { + cpu_stq_data_ra(env, addr, env->bnd_regs[i].lb, ra); + cpu_stq_data_ra(env, addr + 8, env->bnd_regs[i].ub, ra); + } +} + +static void do_xsave_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), + env->bndcs_regs.cfgu, ra); + cpu_stq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), + env->bndcs_regs.sts, ra); +} + +static void do_xsave_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_stq_data_ra(env, ptr, env->pkru, ra); +} + +void helper_fxsave(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xsave_fpu(env, ptr, ra); + + if (env->cr[4] & CR4_OSFXSR_MASK) { + do_xsave_mxcsr(env, ptr, ra); + /* Fast FXSAVE leaves out the XMM registers */ + if (!(env->efer & MSR_EFER_FFXSR) + || (env->hflags & HF_CPL_MASK) + || !(env->hflags & HF_LMA_MASK)) { + do_xsave_sse(env, ptr, ra); + } + } +} + +static uint64_t get_xinuse(CPUX86State *env) +{ + uint64_t inuse = -1; + + /* For the most part, we don't track XINUSE. We could calculate it + here for all components, but it's probably less work to simply + indicate in use. That said, the state of BNDREGS is important + enough to track in HFLAGS, so we might as well use that here. */ + if ((env->hflags & HF_MPX_IU_MASK) == 0) { + inuse &= ~XSTATE_BNDREGS_MASK; + } + return inuse; +} + +static void do_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm, + uint64_t inuse, uint64_t opt, uintptr_t ra) +{ + uint64_t old_bv, new_bv; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Never save anything not enabled by XCR0. */ + rfbm &= env->xcr0; + opt &= rfbm; + + if (opt & XSTATE_FP_MASK) { + do_xsave_fpu(env, ptr, ra); + } + if (rfbm & XSTATE_SSE_MASK) { + /* Note that saving MXCSR is not suppressed by XSAVEOPT. */ + do_xsave_mxcsr(env, ptr, ra); + } + if (opt & XSTATE_SSE_MASK) { + do_xsave_sse(env, ptr, ra); + } + if (opt & XSTATE_BNDREGS_MASK) { + do_xsave_bndregs(env, ptr + XO(bndreg_state), ra); + } + if (opt & XSTATE_BNDCSR_MASK) { + do_xsave_bndcsr(env, ptr + XO(bndcsr_state), ra); + } + if (opt & XSTATE_PKRU_MASK) { + do_xsave_pkru(env, ptr + XO(pkru_state), ra); + } + + /* Update the XSTATE_BV field. */ + old_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); + new_bv = (old_bv & ~rfbm) | (inuse & rfbm); + cpu_stq_data_ra(env, ptr + XO(header.xstate_bv), new_bv, ra); +} + +void helper_xsave(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + do_xsave(env, ptr, rfbm, get_xinuse(env), -1, GETPC()); +} + +void helper_xsaveopt(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uint64_t inuse = get_xinuse(env); + do_xsave(env, ptr, rfbm, inuse, inuse, GETPC()); +} + +static void do_xrstor_fpu(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, fpuc, fpus, fptag; + target_ulong addr; + + fpuc = cpu_lduw_data_ra(env, ptr + XO(legacy.fcw), ra); + fpus = cpu_lduw_data_ra(env, ptr + XO(legacy.fsw), ra); + fptag = cpu_lduw_data_ra(env, ptr + XO(legacy.ftw), ra); + cpu_set_fpuc(env, fpuc); + cpu_set_fpus(env, fpus); + fptag ^= 0xff; + for (i = 0; i < 8; i++) { + env->fptags[i] = ((fptag >> i) & 1); + } + + addr = ptr + XO(legacy.fpregs); + for (i = 0; i < 8; i++) { + floatx80 tmp = helper_fldt(env, addr, ra); + ST(i) = tmp; + addr += 16; + } +} + +static void do_xrstor_mxcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + cpu_set_mxcsr(env, cpu_ldl_data_ra(env, ptr + XO(legacy.mxcsr), ra)); +} + +static void do_xrstor_sse(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + int i, nb_xmm_regs; + target_ulong addr; + + if (env->hflags & HF_CS64_MASK) { + nb_xmm_regs = 16; + } else { + nb_xmm_regs = 8; + } + + addr = ptr + XO(legacy.xmm_regs); + for (i = 0; i < nb_xmm_regs; i++) { + env->xmm_regs[i].ZMM_Q(0) = cpu_ldq_data_ra(env, addr, ra); + env->xmm_regs[i].ZMM_Q(1) = cpu_ldq_data_ra(env, addr + 8, ra); + addr += 16; + } +} + +static void do_xrstor_bndregs(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + target_ulong addr = ptr + offsetof(XSaveBNDREG, bnd_regs); + int i; + + for (i = 0; i < 4; i++, addr += 16) { + env->bnd_regs[i].lb = cpu_ldq_data_ra(env, addr, ra); + env->bnd_regs[i].ub = cpu_ldq_data_ra(env, addr + 8, ra); + } +} + +static void do_xrstor_bndcsr(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + /* FIXME: Extend highest implemented bit of linear address. */ + env->bndcs_regs.cfgu + = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.cfgu), ra); + env->bndcs_regs.sts + = cpu_ldq_data_ra(env, ptr + offsetof(XSaveBNDCSR, bndcsr.sts), ra); +} + +static void do_xrstor_pkru(CPUX86State *env, target_ulong ptr, uintptr_t ra) +{ + env->pkru = cpu_ldq_data_ra(env, ptr, ra); +} + +void helper_fxrstor(CPUX86State *env, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + + /* The operand must be 16 byte aligned */ + if (ptr & 0xf) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + do_xrstor_fpu(env, ptr, ra); + + if (env->cr[4] & CR4_OSFXSR_MASK) { + do_xrstor_mxcsr(env, ptr, ra); + /* Fast FXRSTOR leaves out the XMM registers */ + if (!(env->efer & MSR_EFER_FFXSR) + || (env->hflags & HF_CPL_MASK) + || !(env->hflags & HF_LMA_MASK)) { + do_xrstor_sse(env, ptr, ra); + } + } +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_fxsave(CPUX86State *env, target_ulong ptr) +{ + helper_fxsave(env, ptr); +} + +void cpu_x86_fxrstor(CPUX86State *env, target_ulong ptr) +{ + helper_fxrstor(env, ptr); +} +#endif + +void helper_xrstor(CPUX86State *env, target_ulong ptr, uint64_t rfbm) +{ + uintptr_t ra = GETPC(); + uint64_t xstate_bv, xcomp_bv, reserve0; + + rfbm &= env->xcr0; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, ra); + } + + /* The operand must be 64 byte aligned. */ + if (ptr & 63) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + xstate_bv = cpu_ldq_data_ra(env, ptr + XO(header.xstate_bv), ra); + + if ((int64_t)xstate_bv < 0) { + /* FIXME: Compact form. */ + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* Standard form. */ + + /* The XSTATE_BV field must not set bits not present in XCR0. */ + if (xstate_bv & ~env->xcr0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + /* The XCOMP_BV field must be zero. Note that, as of the April 2016 + revision, the description of the XSAVE Header (Vol 1, Sec 13.4.2) + describes only XCOMP_BV, but the description of the standard form + of XRSTOR (Vol 1, Sec 13.8.1) checks bytes 23:8 for zero, which + includes the next 64-bit field. */ + xcomp_bv = cpu_ldq_data_ra(env, ptr + XO(header.xcomp_bv), ra); + reserve0 = cpu_ldq_data_ra(env, ptr + XO(header.reserve0), ra); + if (xcomp_bv || reserve0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } + + if (rfbm & XSTATE_FP_MASK) { + if (xstate_bv & XSTATE_FP_MASK) { + do_xrstor_fpu(env, ptr, ra); + } else { + helper_fninit(env); + memset(env->fpregs, 0, sizeof(env->fpregs)); + } + } + if (rfbm & XSTATE_SSE_MASK) { + /* Note that the standard form of XRSTOR loads MXCSR from memory + whether or not the XSTATE_BV bit is set. */ + do_xrstor_mxcsr(env, ptr, ra); + if (xstate_bv & XSTATE_SSE_MASK) { + do_xrstor_sse(env, ptr, ra); + } else { + /* ??? When AVX is implemented, we may have to be more + selective in the clearing. */ + memset(env->xmm_regs, 0, sizeof(env->xmm_regs)); + } + } + if (rfbm & XSTATE_BNDREGS_MASK) { + if (xstate_bv & XSTATE_BNDREGS_MASK) { + do_xrstor_bndregs(env, ptr + XO(bndreg_state), ra); + env->hflags |= HF_MPX_IU_MASK; + } else { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } + } + if (rfbm & XSTATE_BNDCSR_MASK) { + if (xstate_bv & XSTATE_BNDCSR_MASK) { + do_xrstor_bndcsr(env, ptr + XO(bndcsr_state), ra); + } else { + memset(&env->bndcs_regs, 0, sizeof(env->bndcs_regs)); + } + cpu_sync_bndcs_hflags(env); + } + if (rfbm & XSTATE_PKRU_MASK) { + uint64_t old_pkru = env->pkru; + if (xstate_bv & XSTATE_PKRU_MASK) { + do_xrstor_pkru(env, ptr + XO(pkru_state), ra); + } else { + env->pkru = 0; + } + if (env->pkru != old_pkru) { + CPUState *cs = env_cpu(env); + tlb_flush(cs); + } + } +} + +#undef XO + +uint64_t helper_xgetbv(CPUX86State *env, uint32_t ecx) +{ + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + switch (ecx) { + case 0: + return env->xcr0; + case 1: + if (env->features[FEAT_XSAVE] & CPUID_XSAVE_XGETBV1) { + return env->xcr0 & get_xinuse(env); + } + break; + } + raise_exception_ra(env, EXCP0D_GPF, GETPC()); +} + +void helper_xsetbv(CPUX86State *env, uint32_t ecx, uint64_t mask) +{ + uint32_t dummy, ena_lo, ena_hi; + uint64_t ena; + + /* The OS must have enabled XSAVE. */ + if (!(env->cr[4] & CR4_OSXSAVE_MASK)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } + + /* Only XCR0 is defined at present; the FPU may not be disabled. */ + if (ecx != 0 || (mask & XSTATE_FP_MASK) == 0) { + goto do_gpf; + } + + /* Disallow enabling unimplemented features. */ + cpu_x86_cpuid(env, 0x0d, 0, &ena_lo, &dummy, &dummy, &ena_hi); + ena = ((uint64_t)ena_hi << 32) | ena_lo; + if (mask & ~ena) { + goto do_gpf; + } + + /* Disallow enabling only half of MPX. */ + if ((mask ^ (mask * (XSTATE_BNDCSR_MASK / XSTATE_BNDREGS_MASK))) + & XSTATE_BNDCSR_MASK) { + goto do_gpf; + } + + env->xcr0 = mask; + cpu_sync_bndcs_hflags(env); + return; + + do_gpf: + raise_exception_ra(env, EXCP0D_GPF, GETPC()); +} + +/* MMX/SSE */ +/* XXX: optimize by storing fptt and fptags in the static cpu state */ + +#define SSE_DAZ 0x0040 +#define SSE_RC_MASK 0x6000 +#define SSE_RC_NEAR 0x0000 +#define SSE_RC_DOWN 0x2000 +#define SSE_RC_UP 0x4000 +#define SSE_RC_CHOP 0x6000 +#define SSE_FZ 0x8000 + +void update_mxcsr_status(CPUX86State *env) +{ + uint32_t mxcsr = env->mxcsr; + int rnd_type; + + /* set rounding mode */ + switch (mxcsr & SSE_RC_MASK) { + default: + case SSE_RC_NEAR: + rnd_type = float_round_nearest_even; + break; + case SSE_RC_DOWN: + rnd_type = float_round_down; + break; + case SSE_RC_UP: + rnd_type = float_round_up; + break; + case SSE_RC_CHOP: + rnd_type = float_round_to_zero; + break; + } + set_float_rounding_mode(rnd_type, &env->sse_status); + + /* Set exception flags. */ + set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) | + (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) | + (mxcsr & FPUS_OE ? float_flag_overflow : 0) | + (mxcsr & FPUS_UE ? float_flag_underflow : 0) | + (mxcsr & FPUS_PE ? float_flag_inexact : 0), + &env->sse_status); + + /* set denormals are zero */ + set_flush_inputs_to_zero((mxcsr & SSE_DAZ) ? 1 : 0, &env->sse_status); + + /* set flush to zero */ + set_flush_to_zero((mxcsr & SSE_FZ) ? 1 : 0, &env->sse_status); +} + +void update_mxcsr_from_sse_status(CPUX86State *env) +{ + if (tcg_enabled()) { + uint8_t flags = get_float_exception_flags(&env->sse_status); + /* + * The MXCSR denormal flag has opposite semantics to + * float_flag_input_denormal (the softfloat code sets that flag + * only when flushing input denormals to zero, but SSE sets it + * only when not flushing them to zero), so is not converted + * here. + */ + env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) | + (flags & float_flag_divbyzero ? FPUS_ZE : 0) | + (flags & float_flag_overflow ? FPUS_OE : 0) | + (flags & float_flag_underflow ? FPUS_UE : 0) | + (flags & float_flag_inexact ? FPUS_PE : 0) | + (flags & float_flag_output_denormal ? FPUS_UE | FPUS_PE : + 0)); + } +} + +void helper_update_mxcsr(CPUX86State *env) +{ + update_mxcsr_from_sse_status(env); +} + +void helper_ldmxcsr(CPUX86State *env, uint32_t val) +{ + cpu_set_mxcsr(env, val); +} + +void helper_enter_mmx(CPUX86State *env) +{ + env->fpstt = 0; + *(uint32_t *)(env->fptags) = 0; + *(uint32_t *)(env->fptags + 4) = 0; +} + +void helper_emms(CPUX86State *env) +{ + /* set to empty state */ + *(uint32_t *)(env->fptags) = 0x01010101; + *(uint32_t *)(env->fptags + 4) = 0x01010101; +} + +/* XXX: suppress */ +void helper_movq(CPUX86State *env, void *d, void *s) +{ + *(uint64_t *)d = *(uint64_t *)s; +} + +#define SHIFT 0 +#include "ops_sse.h" + +#define SHIFT 1 +#include "ops_sse.h" diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c new file mode 100644 index 0000000000..4f89436b53 --- /dev/null +++ b/target/i386/tcg/int_helper.c @@ -0,0 +1,493 @@ +/* + * x86 integer helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "qemu/host-utils.h" +#include "exec/helper-proto.h" +#include "qapi/error.h" +#include "qemu/guest-random.h" + +//#define DEBUG_MULDIV + +/* modulo 9 table */ +static const uint8_t rclb_table[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 0, 1, 2, 3, 4, +}; + +/* modulo 17 table */ +static const uint8_t rclw_table[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, +}; + +/* division, flags are undefined */ + +void helper_divb_AL(CPUX86State *env, target_ulong t0) +{ + unsigned int num, den, q, r; + + num = (env->regs[R_EAX] & 0xffff); + den = (t0 & 0xff); + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + if (q > 0xff) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q &= 0xff; + r = (num % den) & 0xff; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; +} + +void helper_idivb_AL(CPUX86State *env, target_ulong t0) +{ + int num, den, q, r; + + num = (int16_t)env->regs[R_EAX]; + den = (int8_t)t0; + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + if (q != (int8_t)q) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q &= 0xff; + r = (num % den) & 0xff; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | (r << 8) | q; +} + +void helper_divw_AX(CPUX86State *env, target_ulong t0) +{ + unsigned int num, den, q, r; + + num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); + den = (t0 & 0xffff); + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + if (q > 0xffff) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q &= 0xffff; + r = (num % den) & 0xffff; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; + env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; +} + +void helper_idivw_AX(CPUX86State *env, target_ulong t0) +{ + int num, den, q, r; + + num = (env->regs[R_EAX] & 0xffff) | ((env->regs[R_EDX] & 0xffff) << 16); + den = (int16_t)t0; + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + if (q != (int16_t)q) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q &= 0xffff; + r = (num % den) & 0xffff; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | q; + env->regs[R_EDX] = (env->regs[R_EDX] & ~0xffff) | r; +} + +void helper_divl_EAX(CPUX86State *env, target_ulong t0) +{ + unsigned int den, r; + uint64_t num, q; + + num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); + den = t0; + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + r = (num % den); + if (q > 0xffffffff) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + env->regs[R_EAX] = (uint32_t)q; + env->regs[R_EDX] = (uint32_t)r; +} + +void helper_idivl_EAX(CPUX86State *env, target_ulong t0) +{ + int den, r; + int64_t num, q; + + num = ((uint32_t)env->regs[R_EAX]) | ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); + den = t0; + if (den == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + q = (num / den); + r = (num % den); + if (q != (int32_t)q) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + env->regs[R_EAX] = (uint32_t)q; + env->regs[R_EDX] = (uint32_t)r; +} + +/* bcd */ + +/* XXX: exception */ +void helper_aam(CPUX86State *env, int base) +{ + int al, ah; + + al = env->regs[R_EAX] & 0xff; + ah = al / base; + al = al % base; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); + CC_DST = al; +} + +void helper_aad(CPUX86State *env, int base) +{ + int al, ah; + + al = env->regs[R_EAX] & 0xff; + ah = (env->regs[R_EAX] >> 8) & 0xff; + al = ((ah * base) + al) & 0xff; + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al; + CC_DST = al; +} + +void helper_aaa(CPUX86State *env) +{ + int icarry; + int al, ah, af; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + af = eflags & CC_A; + al = env->regs[R_EAX] & 0xff; + ah = (env->regs[R_EAX] >> 8) & 0xff; + + icarry = (al > 0xf9); + if (((al & 0x0f) > 9) || af) { + al = (al + 6) & 0x0f; + ah = (ah + 1 + icarry) & 0xff; + eflags |= CC_C | CC_A; + } else { + eflags &= ~(CC_C | CC_A); + al &= 0x0f; + } + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); + CC_SRC = eflags; +} + +void helper_aas(CPUX86State *env) +{ + int icarry; + int al, ah, af; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + af = eflags & CC_A; + al = env->regs[R_EAX] & 0xff; + ah = (env->regs[R_EAX] >> 8) & 0xff; + + icarry = (al < 6); + if (((al & 0x0f) > 9) || af) { + al = (al - 6) & 0x0f; + ah = (ah - 1 - icarry) & 0xff; + eflags |= CC_C | CC_A; + } else { + eflags &= ~(CC_C | CC_A); + al &= 0x0f; + } + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xffff) | al | (ah << 8); + CC_SRC = eflags; +} + +void helper_daa(CPUX86State *env) +{ + int old_al, al, af, cf; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + cf = eflags & CC_C; + af = eflags & CC_A; + old_al = al = env->regs[R_EAX] & 0xff; + + eflags = 0; + if (((al & 0x0f) > 9) || af) { + al = (al + 6) & 0xff; + eflags |= CC_A; + } + if ((old_al > 0x99) || cf) { + al = (al + 0x60) & 0xff; + eflags |= CC_C; + } + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; + /* well, speed is not an issue here, so we compute the flags by hand */ + eflags |= (al == 0) << 6; /* zf */ + eflags |= parity_table[al]; /* pf */ + eflags |= (al & 0x80); /* sf */ + CC_SRC = eflags; +} + +void helper_das(CPUX86State *env) +{ + int al, al1, af, cf; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + cf = eflags & CC_C; + af = eflags & CC_A; + al = env->regs[R_EAX] & 0xff; + + eflags = 0; + al1 = al; + if (((al & 0x0f) > 9) || af) { + eflags |= CC_A; + if (al < 6 || cf) { + eflags |= CC_C; + } + al = (al - 6) & 0xff; + } + if ((al1 > 0x99) || cf) { + al = (al - 0x60) & 0xff; + eflags |= CC_C; + } + env->regs[R_EAX] = (env->regs[R_EAX] & ~0xff) | al; + /* well, speed is not an issue here, so we compute the flags by hand */ + eflags |= (al == 0) << 6; /* zf */ + eflags |= parity_table[al]; /* pf */ + eflags |= (al & 0x80); /* sf */ + CC_SRC = eflags; +} + +#ifdef TARGET_X86_64 +static void add128(uint64_t *plow, uint64_t *phigh, uint64_t a, uint64_t b) +{ + *plow += a; + /* carry test */ + if (*plow < a) { + (*phigh)++; + } + *phigh += b; +} + +static void neg128(uint64_t *plow, uint64_t *phigh) +{ + *plow = ~*plow; + *phigh = ~*phigh; + add128(plow, phigh, 1, 0); +} + +/* return TRUE if overflow */ +static int div64(uint64_t *plow, uint64_t *phigh, uint64_t b) +{ + uint64_t q, r, a1, a0; + int i, qb, ab; + + a0 = *plow; + a1 = *phigh; + if (a1 == 0) { + q = a0 / b; + r = a0 % b; + *plow = q; + *phigh = r; + } else { + if (a1 >= b) { + return 1; + } + /* XXX: use a better algorithm */ + for (i = 0; i < 64; i++) { + ab = a1 >> 63; + a1 = (a1 << 1) | (a0 >> 63); + if (ab || a1 >= b) { + a1 -= b; + qb = 1; + } else { + qb = 0; + } + a0 = (a0 << 1) | qb; + } +#if defined(DEBUG_MULDIV) + printf("div: 0x%016" PRIx64 "%016" PRIx64 " / 0x%016" PRIx64 + ": q=0x%016" PRIx64 " r=0x%016" PRIx64 "\n", + *phigh, *plow, b, a0, a1); +#endif + *plow = a0; + *phigh = a1; + } + return 0; +} + +/* return TRUE if overflow */ +static int idiv64(uint64_t *plow, uint64_t *phigh, int64_t b) +{ + int sa, sb; + + sa = ((int64_t)*phigh < 0); + if (sa) { + neg128(plow, phigh); + } + sb = (b < 0); + if (sb) { + b = -b; + } + if (div64(plow, phigh, b) != 0) { + return 1; + } + if (sa ^ sb) { + if (*plow > (1ULL << 63)) { + return 1; + } + *plow = -*plow; + } else { + if (*plow >= (1ULL << 63)) { + return 1; + } + } + if (sa) { + *phigh = -*phigh; + } + return 0; +} + +void helper_divq_EAX(CPUX86State *env, target_ulong t0) +{ + uint64_t r0, r1; + + if (t0 == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + r0 = env->regs[R_EAX]; + r1 = env->regs[R_EDX]; + if (div64(&r0, &r1, t0)) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + env->regs[R_EAX] = r0; + env->regs[R_EDX] = r1; +} + +void helper_idivq_EAX(CPUX86State *env, target_ulong t0) +{ + uint64_t r0, r1; + + if (t0 == 0) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + r0 = env->regs[R_EAX]; + r1 = env->regs[R_EDX]; + if (idiv64(&r0, &r1, t0)) { + raise_exception_ra(env, EXCP00_DIVZ, GETPC()); + } + env->regs[R_EAX] = r0; + env->regs[R_EDX] = r1; +} +#endif + +#if TARGET_LONG_BITS == 32 +# define ctztl ctz32 +# define clztl clz32 +#else +# define ctztl ctz64 +# define clztl clz64 +#endif + +target_ulong helper_pdep(target_ulong src, target_ulong mask) +{ + target_ulong dest = 0; + int i, o; + + for (i = 0; mask != 0; i++) { + o = ctztl(mask); + mask &= mask - 1; + dest |= ((src >> i) & 1) << o; + } + return dest; +} + +target_ulong helper_pext(target_ulong src, target_ulong mask) +{ + target_ulong dest = 0; + int i, o; + + for (o = 0; mask != 0; o++) { + i = ctztl(mask); + mask &= mask - 1; + dest |= ((src >> i) & 1) << o; + } + return dest; +} + +#define SHIFT 0 +#include "shift_helper_template.h" +#undef SHIFT + +#define SHIFT 1 +#include "shift_helper_template.h" +#undef SHIFT + +#define SHIFT 2 +#include "shift_helper_template.h" +#undef SHIFT + +#ifdef TARGET_X86_64 +#define SHIFT 3 +#include "shift_helper_template.h" +#undef SHIFT +#endif + +/* Test that BIT is enabled in CR4. If not, raise an illegal opcode + exception. This reduces the requirements for rare CR4 bits being + mapped into HFLAGS. */ +void helper_cr4_testbit(CPUX86State *env, uint32_t bit) +{ + if (unlikely((env->cr[4] & bit) == 0)) { + raise_exception_ra(env, EXCP06_ILLOP, GETPC()); + } +} + +target_ulong HELPER(rdrand)(CPUX86State *env) +{ + Error *err = NULL; + target_ulong ret; + + if (qemu_guest_getrandom(&ret, sizeof(ret), &err) < 0) { + qemu_log_mask(LOG_UNIMP, "rdrand: Crypto failure: %s", + error_get_pretty(err)); + error_free(err); + /* Failure clears CF and all other flags, and returns 0. */ + env->cc_src = 0; + return 0; + } + + /* Success sets CF and clears all others. */ + env->cc_src = CC_C; + return ret; +} diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c new file mode 100644 index 0000000000..21ca3e3e88 --- /dev/null +++ b/target/i386/tcg/mem_helper.c @@ -0,0 +1,193 @@ +/* + * x86 memory access helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "qemu/int128.h" +#include "qemu/atomic128.h" +#include "tcg/tcg.h" + +void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) +{ + uintptr_t ra = GETPC(); + uint64_t oldv, cmpv, newv; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + + cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); + newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); + + oldv = cpu_ldq_data_ra(env, a0, ra); + newv = (cmpv == oldv ? newv : oldv); + /* always do the store */ + cpu_stq_data_ra(env, a0, newv, ra); + + if (oldv == cmpv) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = (uint32_t)oldv; + env->regs[R_EDX] = (uint32_t)(oldv >> 32); + eflags &= ~CC_Z; + } + CC_SRC = eflags; +} + +void helper_cmpxchg8b(CPUX86State *env, target_ulong a0) +{ +#ifdef CONFIG_ATOMIC64 + uint64_t oldv, cmpv, newv; + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + + cmpv = deposit64(env->regs[R_EAX], 32, 32, env->regs[R_EDX]); + newv = deposit64(env->regs[R_EBX], 32, 32, env->regs[R_ECX]); + +#ifdef CONFIG_USER_ONLY + { + uint64_t *haddr = g2h(a0); + cmpv = cpu_to_le64(cmpv); + newv = cpu_to_le64(newv); + oldv = qatomic_cmpxchg__nocheck(haddr, cmpv, newv); + oldv = le64_to_cpu(oldv); + } +#else + { + uintptr_t ra = GETPC(); + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_TEQ, mem_idx); + oldv = helper_atomic_cmpxchgq_le_mmu(env, a0, cmpv, newv, oi, ra); + } +#endif + + if (oldv == cmpv) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = (uint32_t)oldv; + env->regs[R_EDX] = (uint32_t)(oldv >> 32); + eflags &= ~CC_Z; + } + CC_SRC = eflags; +#else + cpu_loop_exit_atomic(env_cpu(env), GETPC()); +#endif /* CONFIG_ATOMIC64 */ +} + +#ifdef TARGET_X86_64 +void helper_cmpxchg16b_unlocked(CPUX86State *env, target_ulong a0) +{ + uintptr_t ra = GETPC(); + Int128 oldv, cmpv, newv; + uint64_t o0, o1; + int eflags; + bool success; + + if ((a0 & 0xf) != 0) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + eflags = cpu_cc_compute_all(env, CC_OP); + + cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); + newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); + + o0 = cpu_ldq_data_ra(env, a0 + 0, ra); + o1 = cpu_ldq_data_ra(env, a0 + 8, ra); + + oldv = int128_make128(o0, o1); + success = int128_eq(oldv, cmpv); + if (!success) { + newv = oldv; + } + + cpu_stq_data_ra(env, a0 + 0, int128_getlo(newv), ra); + cpu_stq_data_ra(env, a0 + 8, int128_gethi(newv), ra); + + if (success) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = int128_getlo(oldv); + env->regs[R_EDX] = int128_gethi(oldv); + eflags &= ~CC_Z; + } + CC_SRC = eflags; +} + +void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) +{ + uintptr_t ra = GETPC(); + + if ((a0 & 0xf) != 0) { + raise_exception_ra(env, EXCP0D_GPF, ra); + } else if (HAVE_CMPXCHG128) { + int eflags = cpu_cc_compute_all(env, CC_OP); + + Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); + Int128 newv = int128_make128(env->regs[R_EBX], env->regs[R_ECX]); + + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + Int128 oldv = helper_atomic_cmpxchgo_le_mmu(env, a0, cmpv, + newv, oi, ra); + + if (int128_eq(oldv, cmpv)) { + eflags |= CC_Z; + } else { + env->regs[R_EAX] = int128_getlo(oldv); + env->regs[R_EDX] = int128_gethi(oldv); + eflags &= ~CC_Z; + } + CC_SRC = eflags; + } else { + cpu_loop_exit_atomic(env_cpu(env), ra); + } +} +#endif + +void helper_boundw(CPUX86State *env, target_ulong a0, int v) +{ + int low, high; + + low = cpu_ldsw_data_ra(env, a0, GETPC()); + high = cpu_ldsw_data_ra(env, a0 + 2, GETPC()); + v = (int16_t)v; + if (v < low || v > high) { + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } + raise_exception_ra(env, EXCP05_BOUND, GETPC()); + } +} + +void helper_boundl(CPUX86State *env, target_ulong a0, int v) +{ + int low, high; + + low = cpu_ldl_data_ra(env, a0, GETPC()); + high = cpu_ldl_data_ra(env, a0 + 4, GETPC()); + if (v < low || v > high) { + if (env->hflags & HF_MPX_EN_MASK) { + env->bndcs_regs.sts = 0; + } + raise_exception_ra(env, EXCP05_BOUND, GETPC()); + } +} diff --git a/target/i386/tcg/meson.build b/target/i386/tcg/meson.build new file mode 100644 index 0000000000..02794226c2 --- /dev/null +++ b/target/i386/tcg/meson.build @@ -0,0 +1,13 @@ +i386_ss.add(when: 'CONFIG_TCG', if_true: files( + 'bpt_helper.c', + 'cc_helper.c', + 'excp_helper.c', + 'fpu_helper.c', + 'int_helper.c', + 'mem_helper.c', + 'misc_helper.c', + 'mpx_helper.c', + 'seg_helper.c', + 'smm_helper.c', + 'svm_helper.c', + 'translate.c'), if_false: files('tcg-stub.c')) diff --git a/target/i386/tcg/misc_helper.c b/target/i386/tcg/misc_helper.c new file mode 100644 index 0000000000..ae259d9145 --- /dev/null +++ b/target/i386/tcg/misc_helper.c @@ -0,0 +1,647 @@ +/* + * x86 misc helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/address-spaces.h" + +void helper_outb(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outb: port=0x%04x, data=%02x\n", port, data); +#else + address_space_stb(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inb(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inb: port=0x%04x\n", port); + return 0; +#else + return address_space_ldub(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_outw(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outw: port=0x%04x, data=%04x\n", port, data); +#else + address_space_stw(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inw(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inw: port=0x%04x\n", port); + return 0; +#else + return address_space_lduw(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_outl(CPUX86State *env, uint32_t port, uint32_t data) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "outl: port=0x%04x, data=%08x\n", port, data); +#else + address_space_stl(&address_space_io, port, data, + cpu_get_mem_attrs(env), NULL); +#endif +} + +target_ulong helper_inl(CPUX86State *env, uint32_t port) +{ +#ifdef CONFIG_USER_ONLY + fprintf(stderr, "inl: port=0x%04x\n", port); + return 0; +#else + return address_space_ldl(&address_space_io, port, + cpu_get_mem_attrs(env), NULL); +#endif +} + +void helper_into(CPUX86State *env, int next_eip_addend) +{ + int eflags; + + eflags = cpu_cc_compute_all(env, CC_OP); + if (eflags & CC_O) { + raise_interrupt(env, EXCP04_INTO, 1, 0, next_eip_addend); + } +} + +void helper_cpuid(CPUX86State *env) +{ + uint32_t eax, ebx, ecx, edx; + + cpu_svm_check_intercept_param(env, SVM_EXIT_CPUID, 0, GETPC()); + + cpu_x86_cpuid(env, (uint32_t)env->regs[R_EAX], (uint32_t)env->regs[R_ECX], + &eax, &ebx, &ecx, &edx); + env->regs[R_EAX] = eax; + env->regs[R_EBX] = ebx; + env->regs[R_ECX] = ecx; + env->regs[R_EDX] = edx; +} + +#if defined(CONFIG_USER_ONLY) +target_ulong helper_read_crN(CPUX86State *env, int reg) +{ + return 0; +} + +void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) +{ +} +#else +target_ulong helper_read_crN(CPUX86State *env, int reg) +{ + target_ulong val; + + cpu_svm_check_intercept_param(env, SVM_EXIT_READ_CR0 + reg, 0, GETPC()); + switch (reg) { + default: + val = env->cr[reg]; + break; + case 8: + if (!(env->hflags2 & HF2_VINTR_MASK)) { + val = cpu_get_apic_tpr(env_archcpu(env)->apic_state); + } else { + val = env->v_tpr; + } + break; + } + return val; +} + +void helper_write_crN(CPUX86State *env, int reg, target_ulong t0) +{ + cpu_svm_check_intercept_param(env, SVM_EXIT_WRITE_CR0 + reg, 0, GETPC()); + switch (reg) { + case 0: + cpu_x86_update_cr0(env, t0); + break; + case 3: + cpu_x86_update_cr3(env, t0); + break; + case 4: + cpu_x86_update_cr4(env, t0); + break; + case 8: + if (!(env->hflags2 & HF2_VINTR_MASK)) { + qemu_mutex_lock_iothread(); + cpu_set_apic_tpr(env_archcpu(env)->apic_state, t0); + qemu_mutex_unlock_iothread(); + } + env->v_tpr = t0 & 0x0f; + break; + default: + env->cr[reg] = t0; + break; + } +} +#endif + +void helper_lmsw(CPUX86State *env, target_ulong t0) +{ + /* only 4 lower bits of CR0 are modified. PE cannot be set to zero + if already set to one. */ + t0 = (env->cr[0] & ~0xe) | (t0 & 0xf); + helper_write_crN(env, 0, t0); +} + +void helper_invlpg(CPUX86State *env, target_ulong addr) +{ + X86CPU *cpu = env_archcpu(env); + + cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPG, 0, GETPC()); + tlb_flush_page(CPU(cpu), addr); +} + +void helper_rdtsc(CPUX86State *env) +{ + uint64_t val; + + if ((env->cr[4] & CR4_TSD_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + cpu_svm_check_intercept_param(env, SVM_EXIT_RDTSC, 0, GETPC()); + + val = cpu_get_tsc(env) + env->tsc_offset; + env->regs[R_EAX] = (uint32_t)(val); + env->regs[R_EDX] = (uint32_t)(val >> 32); +} + +void helper_rdtscp(CPUX86State *env) +{ + helper_rdtsc(env); + env->regs[R_ECX] = (uint32_t)(env->tsc_aux); +} + +void helper_rdpmc(CPUX86State *env) +{ + if ((env->cr[4] & CR4_PCE_MASK) && ((env->hflags & HF_CPL_MASK) != 0)) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + cpu_svm_check_intercept_param(env, SVM_EXIT_RDPMC, 0, GETPC()); + + /* currently unimplemented */ + qemu_log_mask(LOG_UNIMP, "x86: unimplemented rdpmc\n"); + raise_exception_err(env, EXCP06_ILLOP, 0); +} + +#if defined(CONFIG_USER_ONLY) +void helper_wrmsr(CPUX86State *env) +{ +} + +void helper_rdmsr(CPUX86State *env) +{ +} +#else +void helper_wrmsr(CPUX86State *env) +{ + uint64_t val; + + cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 1, GETPC()); + + val = ((uint32_t)env->regs[R_EAX]) | + ((uint64_t)((uint32_t)env->regs[R_EDX]) << 32); + + switch ((uint32_t)env->regs[R_ECX]) { + case MSR_IA32_SYSENTER_CS: + env->sysenter_cs = val & 0xffff; + break; + case MSR_IA32_SYSENTER_ESP: + env->sysenter_esp = val; + break; + case MSR_IA32_SYSENTER_EIP: + env->sysenter_eip = val; + break; + case MSR_IA32_APICBASE: + cpu_set_apic_base(env_archcpu(env)->apic_state, val); + break; + case MSR_EFER: + { + uint64_t update_mask; + + update_mask = 0; + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_SYSCALL) { + update_mask |= MSR_EFER_SCE; + } + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) { + update_mask |= MSR_EFER_LME; + } + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { + update_mask |= MSR_EFER_FFXSR; + } + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_NX) { + update_mask |= MSR_EFER_NXE; + } + if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { + update_mask |= MSR_EFER_SVME; + } + if (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_FFXSR) { + update_mask |= MSR_EFER_FFXSR; + } + cpu_load_efer(env, (env->efer & ~update_mask) | + (val & update_mask)); + } + break; + case MSR_STAR: + env->star = val; + break; + case MSR_PAT: + env->pat = val; + break; + case MSR_VM_HSAVE_PA: + env->vm_hsave = val; + break; +#ifdef TARGET_X86_64 + case MSR_LSTAR: + env->lstar = val; + break; + case MSR_CSTAR: + env->cstar = val; + break; + case MSR_FMASK: + env->fmask = val; + break; + case MSR_FSBASE: + env->segs[R_FS].base = val; + break; + case MSR_GSBASE: + env->segs[R_GS].base = val; + break; + case MSR_KERNELGSBASE: + env->kernelgsbase = val; + break; +#endif + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + env->mtrr_var[((uint32_t)env->regs[R_ECX] - + MSR_MTRRphysBase(0)) / 2].base = val; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + env->mtrr_var[((uint32_t)env->regs[R_ECX] - + MSR_MTRRphysMask(0)) / 2].mask = val; + break; + case MSR_MTRRfix64K_00000: + env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - + MSR_MTRRfix64K_00000] = val; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - + MSR_MTRRfix16K_80000 + 1] = val; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - + MSR_MTRRfix4K_C0000 + 3] = val; + break; + case MSR_MTRRdefType: + env->mtrr_deftype = val; + break; + case MSR_MCG_STATUS: + env->mcg_status = val; + break; + case MSR_MCG_CTL: + if ((env->mcg_cap & MCG_CTL_P) + && (val == 0 || val == ~(uint64_t)0)) { + env->mcg_ctl = val; + } + break; + case MSR_TSC_AUX: + env->tsc_aux = val; + break; + case MSR_IA32_MISC_ENABLE: + env->msr_ia32_misc_enable = val; + break; + case MSR_IA32_BNDCFGS: + /* FIXME: #GP if reserved bits are set. */ + /* FIXME: Extend highest implemented bit of linear address. */ + env->msr_bndcfgs = val; + cpu_sync_bndcs_hflags(env); + break; + default: + if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL + && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + + (4 * env->mcg_cap & 0xff)) { + uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; + if ((offset & 0x3) != 0 + || (val == 0 || val == ~(uint64_t)0)) { + env->mce_banks[offset] = val; + } + break; + } + /* XXX: exception? */ + break; + } +} + +void helper_rdmsr(CPUX86State *env) +{ + X86CPU *x86_cpu = env_archcpu(env); + uint64_t val; + + cpu_svm_check_intercept_param(env, SVM_EXIT_MSR, 0, GETPC()); + + switch ((uint32_t)env->regs[R_ECX]) { + case MSR_IA32_SYSENTER_CS: + val = env->sysenter_cs; + break; + case MSR_IA32_SYSENTER_ESP: + val = env->sysenter_esp; + break; + case MSR_IA32_SYSENTER_EIP: + val = env->sysenter_eip; + break; + case MSR_IA32_APICBASE: + val = cpu_get_apic_base(env_archcpu(env)->apic_state); + break; + case MSR_EFER: + val = env->efer; + break; + case MSR_STAR: + val = env->star; + break; + case MSR_PAT: + val = env->pat; + break; + case MSR_VM_HSAVE_PA: + val = env->vm_hsave; + break; + case MSR_IA32_PERF_STATUS: + /* tsc_increment_by_tick */ + val = 1000ULL; + /* CPU multiplier */ + val |= (((uint64_t)4ULL) << 40); + break; +#ifdef TARGET_X86_64 + case MSR_LSTAR: + val = env->lstar; + break; + case MSR_CSTAR: + val = env->cstar; + break; + case MSR_FMASK: + val = env->fmask; + break; + case MSR_FSBASE: + val = env->segs[R_FS].base; + break; + case MSR_GSBASE: + val = env->segs[R_GS].base; + break; + case MSR_KERNELGSBASE: + val = env->kernelgsbase; + break; + case MSR_TSC_AUX: + val = env->tsc_aux; + break; +#endif + case MSR_SMI_COUNT: + val = env->msr_smi_count; + break; + case MSR_MTRRphysBase(0): + case MSR_MTRRphysBase(1): + case MSR_MTRRphysBase(2): + case MSR_MTRRphysBase(3): + case MSR_MTRRphysBase(4): + case MSR_MTRRphysBase(5): + case MSR_MTRRphysBase(6): + case MSR_MTRRphysBase(7): + val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - + MSR_MTRRphysBase(0)) / 2].base; + break; + case MSR_MTRRphysMask(0): + case MSR_MTRRphysMask(1): + case MSR_MTRRphysMask(2): + case MSR_MTRRphysMask(3): + case MSR_MTRRphysMask(4): + case MSR_MTRRphysMask(5): + case MSR_MTRRphysMask(6): + case MSR_MTRRphysMask(7): + val = env->mtrr_var[((uint32_t)env->regs[R_ECX] - + MSR_MTRRphysMask(0)) / 2].mask; + break; + case MSR_MTRRfix64K_00000: + val = env->mtrr_fixed[0]; + break; + case MSR_MTRRfix16K_80000: + case MSR_MTRRfix16K_A0000: + val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - + MSR_MTRRfix16K_80000 + 1]; + break; + case MSR_MTRRfix4K_C0000: + case MSR_MTRRfix4K_C8000: + case MSR_MTRRfix4K_D0000: + case MSR_MTRRfix4K_D8000: + case MSR_MTRRfix4K_E0000: + case MSR_MTRRfix4K_E8000: + case MSR_MTRRfix4K_F0000: + case MSR_MTRRfix4K_F8000: + val = env->mtrr_fixed[(uint32_t)env->regs[R_ECX] - + MSR_MTRRfix4K_C0000 + 3]; + break; + case MSR_MTRRdefType: + val = env->mtrr_deftype; + break; + case MSR_MTRRcap: + if (env->features[FEAT_1_EDX] & CPUID_MTRR) { + val = MSR_MTRRcap_VCNT | MSR_MTRRcap_FIXRANGE_SUPPORT | + MSR_MTRRcap_WC_SUPPORTED; + } else { + /* XXX: exception? */ + val = 0; + } + break; + case MSR_MCG_CAP: + val = env->mcg_cap; + break; + case MSR_MCG_CTL: + if (env->mcg_cap & MCG_CTL_P) { + val = env->mcg_ctl; + } else { + val = 0; + } + break; + case MSR_MCG_STATUS: + val = env->mcg_status; + break; + case MSR_IA32_MISC_ENABLE: + val = env->msr_ia32_misc_enable; + break; + case MSR_IA32_BNDCFGS: + val = env->msr_bndcfgs; + break; + case MSR_IA32_UCODE_REV: + val = x86_cpu->ucode_rev; + break; + default: + if ((uint32_t)env->regs[R_ECX] >= MSR_MC0_CTL + && (uint32_t)env->regs[R_ECX] < MSR_MC0_CTL + + (4 * env->mcg_cap & 0xff)) { + uint32_t offset = (uint32_t)env->regs[R_ECX] - MSR_MC0_CTL; + val = env->mce_banks[offset]; + break; + } + /* XXX: exception? */ + val = 0; + break; + } + env->regs[R_EAX] = (uint32_t)(val); + env->regs[R_EDX] = (uint32_t)(val >> 32); +} +#endif + +static void do_pause(X86CPU *cpu) +{ + CPUState *cs = CPU(cpu); + + /* Just let another CPU run. */ + cs->exception_index = EXCP_INTERRUPT; + cpu_loop_exit(cs); +} + +static void do_hlt(X86CPU *cpu) +{ + CPUState *cs = CPU(cpu); + CPUX86State *env = &cpu->env; + + env->hflags &= ~HF_INHIBIT_IRQ_MASK; /* needed if sti is just before */ + cs->halted = 1; + cs->exception_index = EXCP_HLT; + cpu_loop_exit(cs); +} + +void helper_hlt(CPUX86State *env, int next_eip_addend) +{ + X86CPU *cpu = env_archcpu(env); + + cpu_svm_check_intercept_param(env, SVM_EXIT_HLT, 0, GETPC()); + env->eip += next_eip_addend; + + do_hlt(cpu); +} + +void helper_monitor(CPUX86State *env, target_ulong ptr) +{ + if ((uint32_t)env->regs[R_ECX] != 0) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + /* XXX: store address? */ + cpu_svm_check_intercept_param(env, SVM_EXIT_MONITOR, 0, GETPC()); +} + +void helper_mwait(CPUX86State *env, int next_eip_addend) +{ + CPUState *cs = env_cpu(env); + X86CPU *cpu = env_archcpu(env); + + if ((uint32_t)env->regs[R_ECX] != 0) { + raise_exception_ra(env, EXCP0D_GPF, GETPC()); + } + cpu_svm_check_intercept_param(env, SVM_EXIT_MWAIT, 0, GETPC()); + env->eip += next_eip_addend; + + /* XXX: not complete but not completely erroneous */ + if (cs->cpu_index != 0 || CPU_NEXT(cs) != NULL) { + do_pause(cpu); + } else { + do_hlt(cpu); + } +} + +void helper_pause(CPUX86State *env, int next_eip_addend) +{ + X86CPU *cpu = env_archcpu(env); + + cpu_svm_check_intercept_param(env, SVM_EXIT_PAUSE, 0, GETPC()); + env->eip += next_eip_addend; + + do_pause(cpu); +} + +void helper_debug(CPUX86State *env) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_DEBUG; + cpu_loop_exit(cs); +} + +uint64_t helper_rdpkru(CPUX86State *env, uint32_t ecx) +{ + if ((env->cr[4] & CR4_PKE_MASK) == 0) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + if (ecx != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + return env->pkru; +} + +void helper_wrpkru(CPUX86State *env, uint32_t ecx, uint64_t val) +{ + CPUState *cs = env_cpu(env); + + if ((env->cr[4] & CR4_PKE_MASK) == 0) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + if (ecx != 0 || (val & 0xFFFFFFFF00000000ull)) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + env->pkru = val; + tlb_flush(cs); +} diff --git a/target/i386/tcg/mpx_helper.c b/target/i386/tcg/mpx_helper.c new file mode 100644 index 0000000000..fd966174b4 --- /dev/null +++ b/target/i386/tcg/mpx_helper.c @@ -0,0 +1,138 @@ +/* + * x86 MPX helpers + * + * Copyright (c) 2015 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" + + +void helper_bndck(CPUX86State *env, uint32_t fail) +{ + if (unlikely(fail)) { + env->bndcs_regs.sts = 1; + raise_exception_ra(env, EXCP05_BOUND, GETPC()); + } +} + +static uint64_t lookup_bte64(CPUX86State *env, uint64_t base, uintptr_t ra) +{ + uint64_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract64(base, 20, 28) << 3) + (extract64(bndcsr, 20, 44) << 12); + bt = cpu_ldq_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract64(base, 3, 17) << 5) + (bt & ~7); +} + +static uint32_t lookup_bte32(CPUX86State *env, uint32_t base, uintptr_t ra) +{ + uint32_t bndcsr, bde, bt; + + if ((env->hflags & HF_CPL_MASK) == 3) { + bndcsr = env->bndcs_regs.cfgu; + } else { + bndcsr = env->msr_bndcfgs; + } + + bde = (extract32(base, 12, 20) << 2) + (bndcsr & TARGET_PAGE_MASK); + bt = cpu_ldl_data_ra(env, bde, ra); + if ((bt & 1) == 0) { + env->bndcs_regs.sts = bde | 2; + raise_exception_ra(env, EXCP05_BOUND, ra); + } + + return (extract32(base, 2, 10) << 4) + (bt & ~3); +} + +uint64_t helper_bndldx64(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint64_t bte, lb, ub, pt; + + bte = lookup_bte64(env, base, ra); + lb = cpu_ldq_data_ra(env, bte, ra); + ub = cpu_ldq_data_ra(env, bte + 8, ra); + pt = cpu_ldq_data_ra(env, bte + 16, ra); + + if (pt != ptr) { + lb = ub = 0; + } + env->mmx_t0.MMX_Q(0) = ub; + return lb; +} + +uint64_t helper_bndldx32(CPUX86State *env, target_ulong base, target_ulong ptr) +{ + uintptr_t ra = GETPC(); + uint32_t bte, lb, ub, pt; + + bte = lookup_bte32(env, base, ra); + lb = cpu_ldl_data_ra(env, bte, ra); + ub = cpu_ldl_data_ra(env, bte + 4, ra); + pt = cpu_ldl_data_ra(env, bte + 8, ra); + + if (pt != ptr) { + lb = ub = 0; + } + return ((uint64_t)ub << 32) | lb; +} + +void helper_bndstx64(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint64_t bte; + + bte = lookup_bte64(env, base, ra); + cpu_stq_data_ra(env, bte, lb, ra); + cpu_stq_data_ra(env, bte + 8, ub, ra); + cpu_stq_data_ra(env, bte + 16, ptr, ra); +} + +void helper_bndstx32(CPUX86State *env, target_ulong base, target_ulong ptr, + uint64_t lb, uint64_t ub) +{ + uintptr_t ra = GETPC(); + uint32_t bte; + + bte = lookup_bte32(env, base, ra); + cpu_stl_data_ra(env, bte, lb, ra); + cpu_stl_data_ra(env, bte + 4, ub, ra); + cpu_stl_data_ra(env, bte + 8, ptr, ra); +} + +void helper_bnd_jmp(CPUX86State *env) +{ + if (!(env->hflags2 & HF2_MPX_PR_MASK)) { + memset(env->bnd_regs, 0, sizeof(env->bnd_regs)); + env->hflags &= ~HF_MPX_IU_MASK; + } +} diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c new file mode 100644 index 0000000000..e6ffa1f018 --- /dev/null +++ b/target/i386/tcg/seg_helper.c @@ -0,0 +1,2674 @@ +/* + * x86 segmentation related helpers: + * TSS, interrupts, system calls, jumps and call/task gates, descriptors + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "qemu/log.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/log.h" + +//#define DEBUG_PCALL + +#ifdef DEBUG_PCALL +# define LOG_PCALL(...) qemu_log_mask(CPU_LOG_PCALL, ## __VA_ARGS__) +# define LOG_PCALL_STATE(cpu) \ + log_cpu_state_mask(CPU_LOG_PCALL, (cpu), CPU_DUMP_CCOP) +#else +# define LOG_PCALL(...) do { } while (0) +# define LOG_PCALL_STATE(cpu) do { } while (0) +#endif + +/* + * TODO: Convert callers to compute cpu_mmu_index_kernel once + * and use *_mmuidx_ra directly. + */ +#define cpu_ldub_kernel_ra(e, p, r) \ + cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_lduw_kernel_ra(e, p, r) \ + cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_ldl_kernel_ra(e, p, r) \ + cpu_ldl_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) +#define cpu_ldq_kernel_ra(e, p, r) \ + cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) + +#define cpu_stb_kernel_ra(e, p, v, r) \ + cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stw_kernel_ra(e, p, v, r) \ + cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stl_kernel_ra(e, p, v, r) \ + cpu_stl_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) +#define cpu_stq_kernel_ra(e, p, v, r) \ + cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) + +#define cpu_ldub_kernel(e, p) cpu_ldub_kernel_ra(e, p, 0) +#define cpu_lduw_kernel(e, p) cpu_lduw_kernel_ra(e, p, 0) +#define cpu_ldl_kernel(e, p) cpu_ldl_kernel_ra(e, p, 0) +#define cpu_ldq_kernel(e, p) cpu_ldq_kernel_ra(e, p, 0) + +#define cpu_stb_kernel(e, p, v) cpu_stb_kernel_ra(e, p, v, 0) +#define cpu_stw_kernel(e, p, v) cpu_stw_kernel_ra(e, p, v, 0) +#define cpu_stl_kernel(e, p, v) cpu_stl_kernel_ra(e, p, v, 0) +#define cpu_stq_kernel(e, p, v) cpu_stq_kernel_ra(e, p, v, 0) + +/* return non zero if error */ +static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector, + uintptr_t retaddr) +{ + SegmentCache *dt; + int index; + target_ulong ptr; + + if (selector & 0x4) { + dt = &env->ldt; + } else { + dt = &env->gdt; + } + index = selector & ~7; + if ((index + 7) > dt->limit) { + return -1; + } + ptr = dt->base + index; + *e1_ptr = cpu_ldl_kernel_ra(env, ptr, retaddr); + *e2_ptr = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + return 0; +} + +static inline int load_segment(CPUX86State *env, uint32_t *e1_ptr, + uint32_t *e2_ptr, int selector) +{ + return load_segment_ra(env, e1_ptr, e2_ptr, selector, 0); +} + +static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2) +{ + unsigned int limit; + + limit = (e1 & 0xffff) | (e2 & 0x000f0000); + if (e2 & DESC_G_MASK) { + limit = (limit << 12) | 0xfff; + } + return limit; +} + +static inline uint32_t get_seg_base(uint32_t e1, uint32_t e2) +{ + return (e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000); +} + +static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, + uint32_t e2) +{ + sc->base = get_seg_base(e1, e2); + sc->limit = get_seg_limit(e1, e2); + sc->flags = e2; +} + +/* init the segment cache in vm86 mode. */ +static inline void load_seg_vm(CPUX86State *env, int seg, int selector) +{ + selector &= 0xffff; + + cpu_x86_load_seg_cache(env, seg, selector, (selector << 4), 0xffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_A_MASK | (3 << DESC_DPL_SHIFT)); +} + +static inline void get_ss_esp_from_tss(CPUX86State *env, uint32_t *ss_ptr, + uint32_t *esp_ptr, int dpl, + uintptr_t retaddr) +{ + X86CPU *cpu = env_archcpu(env); + int type, index, shift; + +#if 0 + { + int i; + printf("TR: base=%p limit=%x\n", env->tr.base, env->tr.limit); + for (i = 0; i < env->tr.limit; i++) { + printf("%02x ", env->tr.base[i]); + if ((i & 7) == 7) { + printf("\n"); + } + } + printf("\n"); + } +#endif + + if (!(env->tr.flags & DESC_P_MASK)) { + cpu_abort(CPU(cpu), "invalid tss"); + } + type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + if ((type & 7) != 1) { + cpu_abort(CPU(cpu), "invalid tss type"); + } + shift = type >> 3; + index = (dpl * 4 + 2) << shift; + if (index + (4 << shift) - 1 > env->tr.limit) { + raise_exception_err_ra(env, EXCP0A_TSS, env->tr.selector & 0xfffc, retaddr); + } + if (shift == 0) { + *esp_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 2, retaddr); + } else { + *esp_ptr = cpu_ldl_kernel_ra(env, env->tr.base + index, retaddr); + *ss_ptr = cpu_lduw_kernel_ra(env, env->tr.base + index + 4, retaddr); + } +} + +static void tss_load_seg(CPUX86State *env, int seg_reg, int selector, int cpl, + uintptr_t retaddr) +{ + uint32_t e1, e2; + int rpl, dpl; + + if ((selector & 0xfffc) != 0) { + if (load_segment_ra(env, &e1, &e2, selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + if (!(e2 & DESC_S_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (seg_reg == R_CS) { + if (!(e2 & DESC_CS_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + if (dpl != rpl) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + } else if (seg_reg == R_SS) { + /* SS must be writable data */ + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + if (dpl != cpl || dpl != rpl) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + } else { + /* not readable code */ + if ((e2 & DESC_CS_MASK) && !(e2 & DESC_R_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + /* if data or non conforming code, checks the rights */ + if (((e2 >> DESC_TYPE_SHIFT) & 0xf) < 12) { + if (dpl < cpl || dpl < rpl) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + } + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, retaddr); + } + cpu_x86_load_seg_cache(env, seg_reg, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + } else { + if (seg_reg == R_SS || seg_reg == R_CS) { + raise_exception_err_ra(env, EXCP0A_TSS, selector & 0xfffc, retaddr); + } + } +} + +#define SWITCH_TSS_JMP 0 +#define SWITCH_TSS_IRET 1 +#define SWITCH_TSS_CALL 2 + +/* XXX: restore CPU state in registers (PowerPC case) */ +static void switch_tss_ra(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip, uintptr_t retaddr) +{ + int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; + target_ulong tss_base; + uint32_t new_regs[8], new_segs[6]; + uint32_t new_eflags, new_eip, new_cr3, new_ldt, new_trap; + uint32_t old_eflags, eflags_mask; + SegmentCache *dt; + int index; + target_ulong ptr; + + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + LOG_PCALL("switch_tss: sel=0x%04x type=%d src=%d\n", tss_selector, type, + source); + + /* if task gate, we read the TSS segment and we load it */ + if (type == 5) { + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); + } + tss_selector = e1 >> 16; + if (tss_selector & 4) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); + } + if (load_segment_ra(env, &e1, &e2, tss_selector, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); + } + if (e2 & DESC_S_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + if ((type & 7) != 1) { + raise_exception_err_ra(env, EXCP0D_GPF, tss_selector & 0xfffc, retaddr); + } + } + + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, tss_selector & 0xfffc, retaddr); + } + + if (type & 8) { + tss_limit_max = 103; + } else { + tss_limit_max = 43; + } + tss_limit = get_seg_limit(e1, e2); + tss_base = get_seg_base(e1, e2); + if ((tss_selector & 4) != 0 || + tss_limit < tss_limit_max) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, retaddr); + } + old_type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + if (old_type & 8) { + old_tss_limit_max = 103; + } else { + old_tss_limit_max = 43; + } + + /* read all the registers from the new TSS */ + if (type & 8) { + /* 32 bit */ + new_cr3 = cpu_ldl_kernel_ra(env, tss_base + 0x1c, retaddr); + new_eip = cpu_ldl_kernel_ra(env, tss_base + 0x20, retaddr); + new_eflags = cpu_ldl_kernel_ra(env, tss_base + 0x24, retaddr); + for (i = 0; i < 8; i++) { + new_regs[i] = cpu_ldl_kernel_ra(env, tss_base + (0x28 + i * 4), + retaddr); + } + for (i = 0; i < 6; i++) { + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x48 + i * 4), + retaddr); + } + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x60, retaddr); + new_trap = cpu_ldl_kernel_ra(env, tss_base + 0x64, retaddr); + } else { + /* 16 bit */ + new_cr3 = 0; + new_eip = cpu_lduw_kernel_ra(env, tss_base + 0x0e, retaddr); + new_eflags = cpu_lduw_kernel_ra(env, tss_base + 0x10, retaddr); + for (i = 0; i < 8; i++) { + new_regs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x12 + i * 2), + retaddr) | 0xffff0000; + } + for (i = 0; i < 4; i++) { + new_segs[i] = cpu_lduw_kernel_ra(env, tss_base + (0x22 + i * 4), + retaddr); + } + new_ldt = cpu_lduw_kernel_ra(env, tss_base + 0x2a, retaddr); + new_segs[R_FS] = 0; + new_segs[R_GS] = 0; + new_trap = 0; + } + /* XXX: avoid a compiler warning, see + http://support.amd.com/us/Processor_TechDocs/24593.pdf + chapters 12.2.5 and 13.2.4 on how to implement TSS Trap bit */ + (void)new_trap; + + /* NOTE: we must avoid memory exceptions during the task switch, + so we make dummy accesses before */ + /* XXX: it can still fail in some cases, so a bigger hack is + necessary to valid the TLB after having done the accesses */ + + v1 = cpu_ldub_kernel_ra(env, env->tr.base, retaddr); + v2 = cpu_ldub_kernel_ra(env, env->tr.base + old_tss_limit_max, retaddr); + cpu_stb_kernel_ra(env, env->tr.base, v1, retaddr); + cpu_stb_kernel_ra(env, env->tr.base + old_tss_limit_max, v2, retaddr); + + /* clear busy bit (it is restartable) */ + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { + target_ulong ptr; + uint32_t e2; + + ptr = env->gdt.base + (env->tr.selector & ~7); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + e2 &= ~DESC_TSS_BUSY_MASK; + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + } + old_eflags = cpu_compute_eflags(env); + if (source == SWITCH_TSS_IRET) { + old_eflags &= ~NT_MASK; + } + + /* save the current state in the old TSS */ + if (type & 8) { + /* 32 bit */ + cpu_stl_kernel_ra(env, env->tr.base + 0x20, next_eip, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + 0x24, old_eflags, retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 0 * 4), env->regs[R_EAX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 1 * 4), env->regs[R_ECX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 2 * 4), env->regs[R_EDX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 3 * 4), env->regs[R_EBX], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 4 * 4), env->regs[R_ESP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 5 * 4), env->regs[R_EBP], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 6 * 4), env->regs[R_ESI], retaddr); + cpu_stl_kernel_ra(env, env->tr.base + (0x28 + 7 * 4), env->regs[R_EDI], retaddr); + for (i = 0; i < 6; i++) { + cpu_stw_kernel_ra(env, env->tr.base + (0x48 + i * 4), + env->segs[i].selector, retaddr); + } + } else { + /* 16 bit */ + cpu_stw_kernel_ra(env, env->tr.base + 0x0e, next_eip, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + 0x10, old_eflags, retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 0 * 2), env->regs[R_EAX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 1 * 2), env->regs[R_ECX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 2 * 2), env->regs[R_EDX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 3 * 2), env->regs[R_EBX], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 4 * 2), env->regs[R_ESP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 5 * 2), env->regs[R_EBP], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 6 * 2), env->regs[R_ESI], retaddr); + cpu_stw_kernel_ra(env, env->tr.base + (0x12 + 7 * 2), env->regs[R_EDI], retaddr); + for (i = 0; i < 4; i++) { + cpu_stw_kernel_ra(env, env->tr.base + (0x22 + i * 4), + env->segs[i].selector, retaddr); + } + } + + /* now if an exception occurs, it will occurs in the next task + context */ + + if (source == SWITCH_TSS_CALL) { + cpu_stw_kernel_ra(env, tss_base, env->tr.selector, retaddr); + new_eflags |= NT_MASK; + } + + /* set busy bit */ + if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { + target_ulong ptr; + uint32_t e2; + + ptr = env->gdt.base + (tss_selector & ~7); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + e2 |= DESC_TSS_BUSY_MASK; + cpu_stl_kernel_ra(env, ptr + 4, e2, retaddr); + } + + /* set the new CPU state */ + /* from this point, any exception which occurs can give problems */ + env->cr[0] |= CR0_TS_MASK; + env->hflags |= HF_TS_MASK; + env->tr.selector = tss_selector; + env->tr.base = tss_base; + env->tr.limit = tss_limit; + env->tr.flags = e2 & ~DESC_TSS_BUSY_MASK; + + if ((type & 8) && (env->cr[0] & CR0_PG_MASK)) { + cpu_x86_update_cr3(env, new_cr3); + } + + /* load all registers without an exception, then reload them with + possible exception */ + env->eip = new_eip; + eflags_mask = TF_MASK | AC_MASK | ID_MASK | + IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | NT_MASK; + if (!(type & 8)) { + eflags_mask &= 0xffff; + } + cpu_load_eflags(env, new_eflags, eflags_mask); + /* XXX: what to do in 16 bit case? */ + env->regs[R_EAX] = new_regs[0]; + env->regs[R_ECX] = new_regs[1]; + env->regs[R_EDX] = new_regs[2]; + env->regs[R_EBX] = new_regs[3]; + env->regs[R_ESP] = new_regs[4]; + env->regs[R_EBP] = new_regs[5]; + env->regs[R_ESI] = new_regs[6]; + env->regs[R_EDI] = new_regs[7]; + if (new_eflags & VM_MASK) { + for (i = 0; i < 6; i++) { + load_seg_vm(env, i, new_segs[i]); + } + } else { + /* first just selectors as the rest may trigger exceptions */ + for (i = 0; i < 6; i++) { + cpu_x86_load_seg_cache(env, i, new_segs[i], 0, 0, 0); + } + } + + env->ldt.selector = new_ldt & ~4; + env->ldt.base = 0; + env->ldt.limit = 0; + env->ldt.flags = 0; + + /* load the LDT */ + if (new_ldt & 4) { + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); + } + + if ((new_ldt & 0xfffc) != 0) { + dt = &env->gdt; + index = new_ldt & ~7; + if ((index + 7) > dt->limit) { + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); + } + ptr = dt->base + index; + e1 = cpu_ldl_kernel_ra(env, ptr, retaddr); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, retaddr); + if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, new_ldt & 0xfffc, retaddr); + } + load_seg_cache_raw_dt(&env->ldt, e1, e2); + } + + /* load the segments */ + if (!(new_eflags & VM_MASK)) { + int cpl = new_segs[R_CS] & 3; + tss_load_seg(env, R_CS, new_segs[R_CS], cpl, retaddr); + tss_load_seg(env, R_SS, new_segs[R_SS], cpl, retaddr); + tss_load_seg(env, R_ES, new_segs[R_ES], cpl, retaddr); + tss_load_seg(env, R_DS, new_segs[R_DS], cpl, retaddr); + tss_load_seg(env, R_FS, new_segs[R_FS], cpl, retaddr); + tss_load_seg(env, R_GS, new_segs[R_GS], cpl, retaddr); + } + + /* check that env->eip is in the CS segment limits */ + if (new_eip > env->segs[R_CS].limit) { + /* XXX: different exception if CALL? */ + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); + } + +#ifndef CONFIG_USER_ONLY + /* reset local breakpoints */ + if (env->dr[7] & DR7_LOCAL_BP_MASK) { + cpu_x86_update_dr7(env, env->dr[7] & ~DR7_LOCAL_BP_MASK); + } +#endif +} + +static void switch_tss(CPUX86State *env, int tss_selector, + uint32_t e1, uint32_t e2, int source, + uint32_t next_eip) +{ + switch_tss_ra(env, tss_selector, e1, e2, source, next_eip, 0); +} + +static inline unsigned int get_sp_mask(unsigned int e2) +{ +#ifdef TARGET_X86_64 + if (e2 & DESC_L_MASK) { + return 0; + } else +#endif + if (e2 & DESC_B_MASK) { + return 0xffffffff; + } else { + return 0xffff; + } +} + +static int exception_has_error_code(int intno) +{ + switch (intno) { + case 8: + case 10: + case 11: + case 12: + case 13: + case 14: + case 17: + return 1; + } + return 0; +} + +#ifdef TARGET_X86_64 +#define SET_ESP(val, sp_mask) \ + do { \ + if ((sp_mask) == 0xffff) { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | \ + ((val) & 0xffff); \ + } else if ((sp_mask) == 0xffffffffLL) { \ + env->regs[R_ESP] = (uint32_t)(val); \ + } else { \ + env->regs[R_ESP] = (val); \ + } \ + } while (0) +#else +#define SET_ESP(val, sp_mask) \ + do { \ + env->regs[R_ESP] = (env->regs[R_ESP] & ~(sp_mask)) | \ + ((val) & (sp_mask)); \ + } while (0) +#endif + +/* in 64-bit machines, this can overflow. So this segment addition macro + * can be used to trim the value to 32-bit whenever needed */ +#define SEG_ADDL(ssp, sp, sp_mask) ((uint32_t)((ssp) + (sp & (sp_mask)))) + +/* XXX: add a is_user flag to have proper security support */ +#define PUSHW_RA(ssp, sp, sp_mask, val, ra) \ + { \ + sp -= 2; \ + cpu_stw_kernel_ra(env, (ssp) + (sp & (sp_mask)), (val), ra); \ + } + +#define PUSHL_RA(ssp, sp, sp_mask, val, ra) \ + { \ + sp -= 4; \ + cpu_stl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), (uint32_t)(val), ra); \ + } + +#define POPW_RA(ssp, sp, sp_mask, val, ra) \ + { \ + val = cpu_lduw_kernel_ra(env, (ssp) + (sp & (sp_mask)), ra); \ + sp += 2; \ + } + +#define POPL_RA(ssp, sp, sp_mask, val, ra) \ + { \ + val = (uint32_t)cpu_ldl_kernel_ra(env, SEG_ADDL(ssp, sp, sp_mask), ra); \ + sp += 4; \ + } + +#define PUSHW(ssp, sp, sp_mask, val) PUSHW_RA(ssp, sp, sp_mask, val, 0) +#define PUSHL(ssp, sp, sp_mask, val) PUSHL_RA(ssp, sp, sp_mask, val, 0) +#define POPW(ssp, sp, sp_mask, val) POPW_RA(ssp, sp, sp_mask, val, 0) +#define POPL(ssp, sp, sp_mask, val) POPL_RA(ssp, sp, sp_mask, val, 0) + +/* protected mode interrupt */ +static void do_interrupt_protected(CPUX86State *env, int intno, int is_int, + int error_code, unsigned int next_eip, + int is_hw) +{ + SegmentCache *dt; + target_ulong ptr, ssp; + int type, dpl, selector, ss_dpl, cpl; + int has_error_code, new_stack, shift; + uint32_t e1, e2, offset, ss = 0, esp, ss_e1 = 0, ss_e2 = 0; + uint32_t old_eip, sp_mask; + int vm86 = env->eflags & VM_MASK; + + has_error_code = 0; + if (!is_int && !is_hw) { + has_error_code = exception_has_error_code(intno); + } + if (is_int) { + old_eip = next_eip; + } else { + old_eip = env->eip; + } + + dt = &env->idt; + if (intno * 8 + 7 > dt->limit) { + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); + } + ptr = dt->base + intno * 8; + e1 = cpu_ldl_kernel(env, ptr); + e2 = cpu_ldl_kernel(env, ptr + 4); + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + switch (type) { + case 5: /* task gate */ + /* must do that check here to return the correct error code */ + if (!(e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); + } + switch_tss(env, intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); + if (has_error_code) { + int type; + uint32_t mask; + + /* push the error code */ + type = (env->tr.flags >> DESC_TYPE_SHIFT) & 0xf; + shift = type >> 3; + if (env->segs[R_SS].flags & DESC_B_MASK) { + mask = 0xffffffff; + } else { + mask = 0xffff; + } + esp = (env->regs[R_ESP] - (2 << shift)) & mask; + ssp = env->segs[R_SS].base + esp; + if (shift) { + cpu_stl_kernel(env, ssp, error_code); + } else { + cpu_stw_kernel(env, ssp, error_code); + } + SET_ESP(esp, mask); + } + return; + case 6: /* 286 interrupt gate */ + case 7: /* 286 trap gate */ + case 14: /* 386 interrupt gate */ + case 15: /* 386 trap gate */ + break; + default: + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); + break; + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privilege if software int */ + if (is_int && dpl < cpl) { + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); + } + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0B_NOSEG, intno * 8 + 2); + } + selector = e1 >> 16; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); + if ((selector & 0xfffc) == 0) { + raise_exception_err(env, EXCP0D_GPF, 0); + } + if (load_segment(env, &e1, &e2, selector) != 0) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + } + if (e2 & DESC_C_MASK) { + dpl = cpl; + } + if (dpl < cpl) { + /* to inner privilege */ + get_ss_esp_from_tss(env, &ss, &esp, dpl, 0); + if ((ss & 0xfffc) == 0) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + if ((ss & 3) != dpl) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + if (load_segment(env, &ss_e1, &ss_e2, ss) != 0) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (ss_dpl != dpl) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + if (!(ss_e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0A_TSS, ss & 0xfffc); + } + new_stack = 1; + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); + } else { + /* to same privilege */ + if (vm86) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + new_stack = 0; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + esp = env->regs[R_ESP]; + } + + shift = type >> 3; + +#if 0 + /* XXX: check that enough room is available */ + push_size = 6 + (new_stack << 2) + (has_error_code << 1); + if (vm86) { + push_size += 8; + } + push_size <<= shift; +#endif + if (shift == 1) { + if (new_stack) { + if (vm86) { + PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector); + PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector); + } + PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector); + PUSHL(ssp, esp, sp_mask, env->regs[R_ESP]); + } + PUSHL(ssp, esp, sp_mask, cpu_compute_eflags(env)); + PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector); + PUSHL(ssp, esp, sp_mask, old_eip); + if (has_error_code) { + PUSHL(ssp, esp, sp_mask, error_code); + } + } else { + if (new_stack) { + if (vm86) { + PUSHW(ssp, esp, sp_mask, env->segs[R_GS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_FS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_DS].selector); + PUSHW(ssp, esp, sp_mask, env->segs[R_ES].selector); + } + PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector); + PUSHW(ssp, esp, sp_mask, env->regs[R_ESP]); + } + PUSHW(ssp, esp, sp_mask, cpu_compute_eflags(env)); + PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector); + PUSHW(ssp, esp, sp_mask, old_eip); + if (has_error_code) { + PUSHW(ssp, esp, sp_mask, error_code); + } + } + + /* interrupt gate clear IF mask */ + if ((type & 1) == 0) { + env->eflags &= ~IF_MASK; + } + env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); + + if (new_stack) { + if (vm86) { + cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0, 0); + cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0, 0); + } + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, get_seg_limit(ss_e1, ss_e2), ss_e2); + } + SET_ESP(esp, sp_mask); + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + env->eip = offset; +} + +#ifdef TARGET_X86_64 + +#define PUSHQ_RA(sp, val, ra) \ + { \ + sp -= 8; \ + cpu_stq_kernel_ra(env, sp, (val), ra); \ + } + +#define POPQ_RA(sp, val, ra) \ + { \ + val = cpu_ldq_kernel_ra(env, sp, ra); \ + sp += 8; \ + } + +#define PUSHQ(sp, val) PUSHQ_RA(sp, val, 0) +#define POPQ(sp, val) POPQ_RA(sp, val, 0) + +static inline target_ulong get_rsp_from_tss(CPUX86State *env, int level) +{ + X86CPU *cpu = env_archcpu(env); + int index; + +#if 0 + printf("TR: base=" TARGET_FMT_lx " limit=%x\n", + env->tr.base, env->tr.limit); +#endif + + if (!(env->tr.flags & DESC_P_MASK)) { + cpu_abort(CPU(cpu), "invalid tss"); + } + index = 8 * level + 4; + if ((index + 7) > env->tr.limit) { + raise_exception_err(env, EXCP0A_TSS, env->tr.selector & 0xfffc); + } + return cpu_ldq_kernel(env, env->tr.base + index); +} + +/* 64 bit interrupt */ +static void do_interrupt64(CPUX86State *env, int intno, int is_int, + int error_code, target_ulong next_eip, int is_hw) +{ + SegmentCache *dt; + target_ulong ptr; + int type, dpl, selector, cpl, ist; + int has_error_code, new_stack; + uint32_t e1, e2, e3, ss; + target_ulong old_eip, esp, offset; + + has_error_code = 0; + if (!is_int && !is_hw) { + has_error_code = exception_has_error_code(intno); + } + if (is_int) { + old_eip = next_eip; + } else { + old_eip = env->eip; + } + + dt = &env->idt; + if (intno * 16 + 15 > dt->limit) { + raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + } + ptr = dt->base + intno * 16; + e1 = cpu_ldl_kernel(env, ptr); + e2 = cpu_ldl_kernel(env, ptr + 4); + e3 = cpu_ldl_kernel(env, ptr + 8); + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + switch (type) { + case 14: /* 386 interrupt gate */ + case 15: /* 386 trap gate */ + break; + default: + raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + break; + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privilege if software int */ + if (is_int && dpl < cpl) { + raise_exception_err(env, EXCP0D_GPF, intno * 16 + 2); + } + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0B_NOSEG, intno * 16 + 2); + } + selector = e1 >> 16; + offset = ((target_ulong)e3 << 32) | (e2 & 0xffff0000) | (e1 & 0x0000ffff); + ist = e2 & 7; + if ((selector & 0xfffc) == 0) { + raise_exception_err(env, EXCP0D_GPF, 0); + } + + if (load_segment(env, &e1, &e2, selector) != 0) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err(env, EXCP0B_NOSEG, selector & 0xfffc); + } + if (!(e2 & DESC_L_MASK) || (e2 & DESC_B_MASK)) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + if (e2 & DESC_C_MASK) { + dpl = cpl; + } + if (dpl < cpl || ist != 0) { + /* to inner privilege */ + new_stack = 1; + esp = get_rsp_from_tss(env, ist != 0 ? ist + 3 : dpl); + ss = 0; + } else { + /* to same privilege */ + if (env->eflags & VM_MASK) { + raise_exception_err(env, EXCP0D_GPF, selector & 0xfffc); + } + new_stack = 0; + esp = env->regs[R_ESP]; + } + esp &= ~0xfLL; /* align stack */ + + PUSHQ(esp, env->segs[R_SS].selector); + PUSHQ(esp, env->regs[R_ESP]); + PUSHQ(esp, cpu_compute_eflags(env)); + PUSHQ(esp, env->segs[R_CS].selector); + PUSHQ(esp, old_eip); + if (has_error_code) { + PUSHQ(esp, error_code); + } + + /* interrupt gate clear IF mask */ + if ((type & 1) == 0) { + env->eflags &= ~IF_MASK; + } + env->eflags &= ~(TF_MASK | VM_MASK | RF_MASK | NT_MASK); + + if (new_stack) { + ss = 0 | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, dpl << DESC_DPL_SHIFT); + } + env->regs[R_ESP] = esp; + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + env->eip = offset; +} +#endif + +#ifdef TARGET_X86_64 +#if defined(CONFIG_USER_ONLY) +void helper_syscall(CPUX86State *env, int next_eip_addend) +{ + CPUState *cs = env_cpu(env); + + cs->exception_index = EXCP_SYSCALL; + env->exception_is_int = 0; + env->exception_next_eip = env->eip + next_eip_addend; + cpu_loop_exit(cs); +} +#else +void helper_syscall(CPUX86State *env, int next_eip_addend) +{ + int selector; + + if (!(env->efer & MSR_EFER_SCE)) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + selector = (env->star >> 32) & 0xffff; + if (env->hflags & HF_LMA_MASK) { + int code64; + + env->regs[R_ECX] = env->eip + next_eip_addend; + env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK; + + code64 = env->hflags & HF_CS64_MASK; + + env->eflags &= ~(env->fmask | RF_MASK); + cpu_load_eflags(env, env->eflags, 0); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | + DESC_L_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + if (code64) { + env->eip = env->lstar; + } else { + env->eip = env->cstar; + } + } else { + env->regs[R_ECX] = (uint32_t)(env->eip + next_eip_addend); + + env->eflags &= ~(IF_MASK | RF_MASK | VM_MASK); + cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->eip = (uint32_t)env->star; + } +} +#endif +#endif + +#ifdef TARGET_X86_64 +void helper_sysret(CPUX86State *env, int dflag) +{ + int cpl, selector; + + if (!(env->efer & MSR_EFER_SCE)) { + raise_exception_err_ra(env, EXCP06_ILLOP, 0, GETPC()); + } + cpl = env->hflags & HF_CPL_MASK; + if (!(env->cr[0] & CR0_PE_MASK) || cpl != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + selector = (env->star >> 48) & 0xffff; + if (env->hflags & HF_LMA_MASK) { + cpu_load_eflags(env, (uint32_t)(env->regs[11]), TF_MASK | AC_MASK + | ID_MASK | IF_MASK | IOPL_MASK | VM_MASK | RF_MASK | + NT_MASK); + if (dflag == 2) { + cpu_x86_load_seg_cache(env, R_CS, (selector + 16) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | + DESC_L_MASK); + env->eip = env->regs[R_ECX]; + } else { + cpu_x86_load_seg_cache(env, R_CS, selector | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + env->eip = (uint32_t)env->regs[R_ECX]; + } + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } else { + env->eflags |= IF_MASK; + cpu_x86_load_seg_cache(env, R_CS, selector | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + env->eip = (uint32_t)env->regs[R_ECX]; + cpu_x86_load_seg_cache(env, R_SS, (selector + 8) | 3, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } +} +#endif + +/* real mode interrupt */ +static void do_interrupt_real(CPUX86State *env, int intno, int is_int, + int error_code, unsigned int next_eip) +{ + SegmentCache *dt; + target_ulong ptr, ssp; + int selector; + uint32_t offset, esp; + uint32_t old_cs, old_eip; + + /* real mode (simpler!) */ + dt = &env->idt; + if (intno * 4 + 3 > dt->limit) { + raise_exception_err(env, EXCP0D_GPF, intno * 8 + 2); + } + ptr = dt->base + intno * 4; + offset = cpu_lduw_kernel(env, ptr); + selector = cpu_lduw_kernel(env, ptr + 2); + esp = env->regs[R_ESP]; + ssp = env->segs[R_SS].base; + if (is_int) { + old_eip = next_eip; + } else { + old_eip = env->eip; + } + old_cs = env->segs[R_CS].selector; + /* XXX: use SS segment size? */ + PUSHW(ssp, esp, 0xffff, cpu_compute_eflags(env)); + PUSHW(ssp, esp, 0xffff, old_cs); + PUSHW(ssp, esp, 0xffff, old_eip); + + /* update processor state */ + env->regs[R_ESP] = (env->regs[R_ESP] & ~0xffff) | (esp & 0xffff); + env->eip = offset; + env->segs[R_CS].selector = selector; + env->segs[R_CS].base = (selector << 4); + env->eflags &= ~(IF_MASK | TF_MASK | AC_MASK | RF_MASK); +} + +#if defined(CONFIG_USER_ONLY) +/* fake user mode interrupt. is_int is TRUE if coming from the int + * instruction. next_eip is the env->eip value AFTER the interrupt + * instruction. It is only relevant if is_int is TRUE or if intno + * is EXCP_SYSCALL. + */ +static void do_interrupt_user(CPUX86State *env, int intno, int is_int, + int error_code, target_ulong next_eip) +{ + if (is_int) { + SegmentCache *dt; + target_ulong ptr; + int dpl, cpl, shift; + uint32_t e2; + + dt = &env->idt; + if (env->hflags & HF_LMA_MASK) { + shift = 4; + } else { + shift = 3; + } + ptr = dt->base + (intno << shift); + e2 = cpu_ldl_kernel(env, ptr + 4); + + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + /* check privilege if software int */ + if (dpl < cpl) { + raise_exception_err(env, EXCP0D_GPF, (intno << shift) + 2); + } + } + + /* Since we emulate only user space, we cannot do more than + exiting the emulation with the suitable exception and error + code. So update EIP for INT 0x80 and EXCP_SYSCALL. */ + if (is_int || intno == EXCP_SYSCALL) { + env->eip = next_eip; + } +} + +#else + +static void handle_even_inj(CPUX86State *env, int intno, int is_int, + int error_code, int is_hw, int rm) +{ + CPUState *cs = env_cpu(env); + uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.event_inj)); + + if (!(event_inj & SVM_EVTINJ_VALID)) { + int type; + + if (is_int) { + type = SVM_EVTINJ_TYPE_SOFT; + } else { + type = SVM_EVTINJ_TYPE_EXEPT; + } + event_inj = intno | type | SVM_EVTINJ_VALID; + if (!rm && exception_has_error_code(intno)) { + event_inj |= SVM_EVTINJ_VALID_ERR; + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.event_inj_err), + error_code); + } + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.event_inj), + event_inj); + } +} +#endif + +/* + * Begin execution of an interruption. is_int is TRUE if coming from + * the int instruction. next_eip is the env->eip value AFTER the interrupt + * instruction. It is only relevant if is_int is TRUE. + */ +static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, + int error_code, target_ulong next_eip, int is_hw) +{ + CPUX86State *env = &cpu->env; + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + if ((env->cr[0] & CR0_PE_MASK)) { + static int count; + + qemu_log("%6d: v=%02x e=%04x i=%d cpl=%d IP=%04x:" TARGET_FMT_lx + " pc=" TARGET_FMT_lx " SP=%04x:" TARGET_FMT_lx, + count, intno, error_code, is_int, + env->hflags & HF_CPL_MASK, + env->segs[R_CS].selector, env->eip, + (int)env->segs[R_CS].base + env->eip, + env->segs[R_SS].selector, env->regs[R_ESP]); + if (intno == 0x0e) { + qemu_log(" CR2=" TARGET_FMT_lx, env->cr[2]); + } else { + qemu_log(" env->regs[R_EAX]=" TARGET_FMT_lx, env->regs[R_EAX]); + } + qemu_log("\n"); + log_cpu_state(CPU(cpu), CPU_DUMP_CCOP); +#if 0 + { + int i; + target_ulong ptr; + + qemu_log(" code="); + ptr = env->segs[R_CS].base + env->eip; + for (i = 0; i < 16; i++) { + qemu_log(" %02x", ldub(ptr + i)); + } + qemu_log("\n"); + } +#endif + count++; + } + } + if (env->cr[0] & CR0_PE_MASK) { +#if !defined(CONFIG_USER_ONLY) + if (env->hflags & HF_GUEST_MASK) { + handle_even_inj(env, intno, is_int, error_code, is_hw, 0); + } +#endif +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + do_interrupt64(env, intno, is_int, error_code, next_eip, is_hw); + } else +#endif + { + do_interrupt_protected(env, intno, is_int, error_code, next_eip, + is_hw); + } + } else { +#if !defined(CONFIG_USER_ONLY) + if (env->hflags & HF_GUEST_MASK) { + handle_even_inj(env, intno, is_int, error_code, is_hw, 1); + } +#endif + do_interrupt_real(env, intno, is_int, error_code, next_eip); + } + +#if !defined(CONFIG_USER_ONLY) + if (env->hflags & HF_GUEST_MASK) { + CPUState *cs = CPU(cpu); + uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.event_inj)); + + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.event_inj), + event_inj & ~SVM_EVTINJ_VALID); + } +#endif +} + +void x86_cpu_do_interrupt(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + +#if defined(CONFIG_USER_ONLY) + /* if user mode only, we simulate a fake exception + which will be handled outside the cpu execution + loop */ + do_interrupt_user(env, cs->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip); + /* successfully delivered */ + env->old_exception = -1; +#else + if (cs->exception_index >= EXCP_VMEXIT) { + assert(env->old_exception == -1); + do_vmexit(env, cs->exception_index - EXCP_VMEXIT, env->error_code); + } else { + do_interrupt_all(cpu, cs->exception_index, + env->exception_is_int, + env->error_code, + env->exception_next_eip, 0); + /* successfully delivered */ + env->old_exception = -1; + } +#endif +} + +void do_interrupt_x86_hardirq(CPUX86State *env, int intno, int is_hw) +{ + do_interrupt_all(env_archcpu(env), intno, 0, 0, 0, is_hw); +} + +bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + int intno; + + interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request); + if (!interrupt_request) { + return false; + } + + /* Don't process multiple interrupt requests in a single call. + * This is required to make icount-driven execution deterministic. + */ + switch (interrupt_request) { +#if !defined(CONFIG_USER_ONLY) + case CPU_INTERRUPT_POLL: + cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + apic_poll_irq(cpu->apic_state); + break; +#endif + case CPU_INTERRUPT_SIPI: + do_cpu_sipi(cpu); + break; + case CPU_INTERRUPT_SMI: + cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); + cs->interrupt_request &= ~CPU_INTERRUPT_SMI; + do_smm_enter(cpu); + break; + case CPU_INTERRUPT_NMI: + cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); + cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + env->hflags2 |= HF2_NMI_MASK; + do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); + break; + case CPU_INTERRUPT_MCE: + cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); + break; + case CPU_INTERRUPT_HARD: + cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); + cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | + CPU_INTERRUPT_VIRQ); + intno = cpu_get_pic_interrupt(env); + qemu_log_mask(CPU_LOG_TB_IN_ASM, + "Servicing hardware INT=0x%02x\n", intno); + do_interrupt_x86_hardirq(env, intno, 1); + break; +#if !defined(CONFIG_USER_ONLY) + case CPU_INTERRUPT_VIRQ: + /* FIXME: this should respect TPR */ + cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); + intno = x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.int_vector)); + qemu_log_mask(CPU_LOG_TB_IN_ASM, + "Servicing virtual hardware INT=0x%02x\n", intno); + do_interrupt_x86_hardirq(env, intno, 1); + cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + break; +#endif + } + + /* Ensure that no TB jump will be modified as the program flow was changed. */ + return true; +} + +void helper_lldt(CPUX86State *env, int selector) +{ + SegmentCache *dt; + uint32_t e1, e2; + int index, entry_limit; + target_ulong ptr; + + selector &= 0xffff; + if ((selector & 0xfffc) == 0) { + /* XXX: NULL selector case: invalid LDT */ + env->ldt.base = 0; + env->ldt.limit = 0; + } else { + if (selector & 0x4) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + dt = &env->gdt; + index = selector & ~7; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + entry_limit = 15; + } else +#endif + { + entry_limit = 7; + } + if ((index + entry_limit) > dt->limit) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + ptr = dt->base + index; + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); + if ((e2 & DESC_S_MASK) || ((e2 >> DESC_TYPE_SHIFT) & 0xf) != 2) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); + } +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t e3; + + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); + load_seg_cache_raw_dt(&env->ldt, e1, e2); + env->ldt.base |= (target_ulong)e3 << 32; + } else +#endif + { + load_seg_cache_raw_dt(&env->ldt, e1, e2); + } + } + env->ldt.selector = selector; +} + +void helper_ltr(CPUX86State *env, int selector) +{ + SegmentCache *dt; + uint32_t e1, e2; + int index, type, entry_limit; + target_ulong ptr; + + selector &= 0xffff; + if ((selector & 0xfffc) == 0) { + /* NULL selector case: invalid TR */ + env->tr.base = 0; + env->tr.limit = 0; + env->tr.flags = 0; + } else { + if (selector & 0x4) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + dt = &env->gdt; + index = selector & ~7; +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + entry_limit = 15; + } else +#endif + { + entry_limit = 7; + } + if ((index + entry_limit) > dt->limit) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + ptr = dt->base + index; + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + if ((e2 & DESC_S_MASK) || + (type != 1 && type != 9)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); + } +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + uint32_t e3, e4; + + e3 = cpu_ldl_kernel_ra(env, ptr + 8, GETPC()); + e4 = cpu_ldl_kernel_ra(env, ptr + 12, GETPC()); + if ((e4 >> DESC_TYPE_SHIFT) & 0xf) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + load_seg_cache_raw_dt(&env->tr, e1, e2); + env->tr.base |= (target_ulong)e3 << 32; + } else +#endif + { + load_seg_cache_raw_dt(&env->tr, e1, e2); + } + e2 |= DESC_TSS_BUSY_MASK; + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); + } + env->tr.selector = selector; +} + +/* only works if protected mode and not VM86. seg_reg must be != R_CS */ +void helper_load_seg(CPUX86State *env, int seg_reg, int selector) +{ + uint32_t e1, e2; + int cpl, dpl, rpl; + SegmentCache *dt; + int index; + target_ulong ptr; + + selector &= 0xffff; + cpl = env->hflags & HF_CPL_MASK; + if ((selector & 0xfffc) == 0) { + /* null selector case */ + if (seg_reg == R_SS +#ifdef TARGET_X86_64 + && (!(env->hflags & HF_CS64_MASK) || cpl == 3) +#endif + ) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + cpu_x86_load_seg_cache(env, seg_reg, selector, 0, 0, 0); + } else { + + if (selector & 0x4) { + dt = &env->ldt; + } else { + dt = &env->gdt; + } + index = selector & ~7; + if ((index + 7) > dt->limit) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + ptr = dt->base + index; + e1 = cpu_ldl_kernel_ra(env, ptr, GETPC()); + e2 = cpu_ldl_kernel_ra(env, ptr + 4, GETPC()); + + if (!(e2 & DESC_S_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (seg_reg == R_SS) { + /* must be writable segment */ + if ((e2 & DESC_CS_MASK) || !(e2 & DESC_W_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (rpl != cpl || dpl != cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + } else { + /* must be readable segment */ + if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + + if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { + /* if not conforming code, test rights */ + if (dpl < cpl || dpl < rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + } + } + + if (!(e2 & DESC_P_MASK)) { + if (seg_reg == R_SS) { + raise_exception_err_ra(env, EXCP0C_STACK, selector & 0xfffc, GETPC()); + } else { + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); + } + } + + /* set the access bit if not already set */ + if (!(e2 & DESC_A_MASK)) { + e2 |= DESC_A_MASK; + cpu_stl_kernel_ra(env, ptr + 4, e2, GETPC()); + } + + cpu_x86_load_seg_cache(env, seg_reg, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); +#if 0 + qemu_log("load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n", + selector, (unsigned long)sc->base, sc->limit, sc->flags); +#endif + } +} + +/* protected mode jump */ +void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, + target_ulong next_eip) +{ + int gate_cs, type; + uint32_t e1, e2, cpl, dpl, rpl, limit; + + if ((new_cs & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if (!(e2 & DESC_CS_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + /* conforming code segment */ + if (dpl > cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } else { + /* non conforming code segment */ + rpl = new_cs & 3; + if (rpl > cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + if (dpl != cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); + } + limit = get_seg_limit(e1, e2); + if (new_eip > limit && + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + env->eip = new_eip; + } else { + /* jump to call or task gate */ + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + rpl = new_cs & 3; + cpl = env->hflags & HF_CPL_MASK; + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif + switch (type) { + case 1: /* 286 TSS */ + case 9: /* 386 TSS */ + case 5: /* task gate */ + if (dpl < cpl || dpl < rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_JMP, next_eip, GETPC()); + break; + case 4: /* 286 call gate */ + case 12: /* 386 call gate */ + if ((dpl < cpl) || (dpl < rpl)) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); + } + gate_cs = e1 >> 16; + new_eip = (e1 & 0xffff); + if (type == 12) { + new_eip |= (e2 & 0xffff0000); + } + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + new_eip |= ((target_ulong)e1) << 32; + } +#endif + + if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + /* must be code segment */ + if (((e2 & (DESC_S_MASK | DESC_CS_MASK)) != + (DESC_S_MASK | DESC_CS_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + if (((e2 & DESC_C_MASK) && (dpl > cpl)) || + (!(e2 & DESC_C_MASK) && (dpl != cpl))) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + } +#endif + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + limit = get_seg_limit(e1, e2); + if (new_eip > limit && + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + env->eip = new_eip; + break; + default: + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + break; + } + } +} + +/* real mode call */ +void helper_lcall_real(CPUX86State *env, int new_cs, target_ulong new_eip1, + int shift, int next_eip) +{ + int new_eip; + uint32_t esp, esp_mask; + target_ulong ssp; + + new_eip = new_eip1; + esp = env->regs[R_ESP]; + esp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + if (shift) { + PUSHL_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, esp, esp_mask, next_eip, GETPC()); + } else { + PUSHW_RA(ssp, esp, esp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, esp, esp_mask, next_eip, GETPC()); + } + + SET_ESP(esp, esp_mask); + env->eip = new_eip; + env->segs[R_CS].selector = new_cs; + env->segs[R_CS].base = (new_cs << 4); +} + +/* protected mode call */ +void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, + int shift, target_ulong next_eip) +{ + int new_stack, i; + uint32_t e1, e2, cpl, dpl, rpl, selector, param_count; + uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; + uint32_t val, limit, old_sp_mask; + target_ulong ssp, old_ssp, offset, sp; + + LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); + LOG_PCALL_STATE(env_cpu(env)); + if ((new_cs & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + if (load_segment_ra(env, &e1, &e2, new_cs, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + cpl = env->hflags & HF_CPL_MASK; + LOG_PCALL("desc=%08x:%08x\n", e1, e2); + if (e2 & DESC_S_MASK) { + if (!(e2 & DESC_CS_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + /* conforming code segment */ + if (dpl > cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } else { + /* non conforming code segment */ + rpl = new_cs & 3; + if (rpl > cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + if (dpl != cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); + } + +#ifdef TARGET_X86_64 + /* XXX: check 16/32 bit cases in long mode */ + if (shift == 2) { + target_ulong rsp; + + /* 64 bit case */ + rsp = env->regs[R_ESP]; + PUSHQ_RA(rsp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(rsp, next_eip, GETPC()); + /* from this point, not restartable */ + env->regs[R_ESP] = rsp; + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), e2); + env->eip = new_eip; + } else +#endif + { + sp = env->regs[R_ESP]; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + if (shift) { + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + } else { + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + } + + limit = get_seg_limit(e1, e2); + if (new_eip > limit) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + /* from this point, not restartable */ + SET_ESP(sp, sp_mask); + cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, + get_seg_base(e1, e2), limit, e2); + env->eip = new_eip; + } + } else { + /* check gate type */ + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + rpl = new_cs & 3; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif + + switch (type) { + case 1: /* available 286 TSS */ + case 9: /* available 386 TSS */ + case 5: /* task gate */ + if (dpl < cpl || dpl < rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + switch_tss_ra(env, new_cs, e1, e2, SWITCH_TSS_CALL, next_eip, GETPC()); + return; + case 4: /* 286 call gate */ + case 12: /* 386 call gate */ + break; + default: + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + break; + } + shift = type >> 3; + + if (dpl < cpl || dpl < rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + /* check valid bit */ + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); + } + selector = e1 >> 16; + param_count = e2 & 0x1f; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + offset |= ((target_ulong)e1) << 32; + } +#endif + if ((selector & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (!(e2 & DESC_S_MASK) || !(e2 & (DESC_CS_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (dpl > cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + shift++; + } +#endif + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, selector & 0xfffc, GETPC()); + } + + if (!(e2 & DESC_C_MASK) && dpl < cpl) { + /* to inner privilege */ +#ifdef TARGET_X86_64 + if (shift == 2) { + sp = get_rsp_from_tss(env, dpl); + ss = dpl; /* SS = NULL selector with RPL = new CPL */ + new_stack = 1; + sp_mask = 0; + ssp = 0; /* SS base is always zero in IA-32e mode */ + LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); + } else +#endif + { + uint32_t sp32; + get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC()); + LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp32, param_count, + env->regs[R_ESP]); + sp = sp32; + if ((ss & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if ((ss & 3) != dpl) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (load_segment_ra(env, &ss_e1, &ss_e2, ss, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + ss_dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (ss_dpl != dpl) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (!(ss_e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); + } + + /* push_size = ((param_count * 2) + 8) << shift; */ + + old_sp_mask = get_sp_mask(env->segs[R_SS].flags); + old_ssp = env->segs[R_SS].base; +#ifdef TARGET_X86_64 + if (shift == 2) { + /* XXX: verify if new stack address is canonical */ + PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); + PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); + /* parameters aren't supported for 64-bit call gates */ + } else +#endif + if (shift == 1) { + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + for (i = param_count - 1; i >= 0; i--) { + val = cpu_ldl_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 4) & + old_sp_mask), GETPC()); + PUSHL_RA(ssp, sp, sp_mask, val, GETPC()); + } + } else { + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_SS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, env->regs[R_ESP], GETPC()); + for (i = param_count - 1; i >= 0; i--) { + val = cpu_lduw_kernel_ra(env, old_ssp + + ((env->regs[R_ESP] + i * 2) & + old_sp_mask), GETPC()); + PUSHW_RA(ssp, sp, sp_mask, val, GETPC()); + } + } + new_stack = 1; + } else { + /* to same privilege */ + sp = env->regs[R_ESP]; + sp_mask = get_sp_mask(env->segs[R_SS].flags); + ssp = env->segs[R_SS].base; + /* push_size = (4 << shift); */ + new_stack = 0; + } + +#ifdef TARGET_X86_64 + if (shift == 2) { + PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(sp, next_eip, GETPC()); + } else +#endif + if (shift == 1) { + PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); + } else { + PUSHW_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); + PUSHW_RA(ssp, sp, sp_mask, next_eip, GETPC()); + } + + /* from this point, not restartable */ + + if (new_stack) { +#ifdef TARGET_X86_64 + if (shift == 2) { + cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0); + } else +#endif + { + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } + } + + selector = (selector & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_CS, selector, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + SET_ESP(sp, sp_mask); + env->eip = offset; + } +} + +/* real and vm86 mode iret */ +void helper_iret_real(CPUX86State *env, int shift) +{ + uint32_t sp, new_cs, new_eip, new_eflags, sp_mask; + target_ulong ssp; + int eflags_mask; + + sp_mask = 0xffff; /* XXXX: use SS segment size? */ + sp = env->regs[R_ESP]; + ssp = env->segs[R_SS].base; + if (shift == 1) { + /* 32 bits */ + POPL_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPL_RA(ssp, sp, sp_mask, new_cs, GETPC()); + new_cs &= 0xffff; + POPL_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + } else { + /* 16 bits */ + POPW_RA(ssp, sp, sp_mask, new_eip, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_cs, GETPC()); + POPW_RA(ssp, sp, sp_mask, new_eflags, GETPC()); + } + env->regs[R_ESP] = (env->regs[R_ESP] & ~sp_mask) | (sp & sp_mask); + env->segs[R_CS].selector = new_cs; + env->segs[R_CS].base = (new_cs << 4); + env->eip = new_eip; + if (env->eflags & VM_MASK) { + eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | RF_MASK | + NT_MASK; + } else { + eflags_mask = TF_MASK | AC_MASK | ID_MASK | IF_MASK | IOPL_MASK | + RF_MASK | NT_MASK; + } + if (shift == 0) { + eflags_mask &= 0xffff; + } + cpu_load_eflags(env, new_eflags, eflags_mask); + env->hflags2 &= ~HF2_NMI_MASK; +} + +static inline void validate_seg(CPUX86State *env, int seg_reg, int cpl) +{ + int dpl; + uint32_t e2; + + /* XXX: on x86_64, we do not want to nullify FS and GS because + they may still contain a valid base. I would be interested to + know how a real x86_64 CPU behaves */ + if ((seg_reg == R_FS || seg_reg == R_GS) && + (env->segs[seg_reg].selector & 0xfffc) == 0) { + return; + } + + e2 = env->segs[seg_reg].flags; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (!(e2 & DESC_CS_MASK) || !(e2 & DESC_C_MASK)) { + /* data or non conforming code segment */ + if (dpl < cpl) { + cpu_x86_load_seg_cache(env, seg_reg, 0, + env->segs[seg_reg].base, + env->segs[seg_reg].limit, + env->segs[seg_reg].flags & ~DESC_P_MASK); + } + } +} + +/* protected mode iret */ +static inline void helper_ret_protected(CPUX86State *env, int shift, + int is_iret, int addend, + uintptr_t retaddr) +{ + uint32_t new_cs, new_eflags, new_ss; + uint32_t new_es, new_ds, new_fs, new_gs; + uint32_t e1, e2, ss_e1, ss_e2; + int cpl, dpl, rpl, eflags_mask, iopl; + target_ulong ssp, sp, new_eip, new_esp, sp_mask; + +#ifdef TARGET_X86_64 + if (shift == 2) { + sp_mask = -1; + } else +#endif + { + sp_mask = get_sp_mask(env->segs[R_SS].flags); + } + sp = env->regs[R_ESP]; + ssp = env->segs[R_SS].base; + new_eflags = 0; /* avoid warning */ +#ifdef TARGET_X86_64 + if (shift == 2) { + POPQ_RA(sp, new_eip, retaddr); + POPQ_RA(sp, new_cs, retaddr); + new_cs &= 0xffff; + if (is_iret) { + POPQ_RA(sp, new_eflags, retaddr); + } + } else +#endif + { + if (shift == 1) { + /* 32 bits */ + POPL_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPL_RA(ssp, sp, sp_mask, new_cs, retaddr); + new_cs &= 0xffff; + if (is_iret) { + POPL_RA(ssp, sp, sp_mask, new_eflags, retaddr); + if (new_eflags & VM_MASK) { + goto return_to_vm86; + } + } + } else { + /* 16 bits */ + POPW_RA(ssp, sp, sp_mask, new_eip, retaddr); + POPW_RA(ssp, sp, sp_mask, new_cs, retaddr); + if (is_iret) { + POPW_RA(ssp, sp, sp_mask, new_eflags, retaddr); + } + } + } + LOG_PCALL("lret new %04x:" TARGET_FMT_lx " s=%d addend=0x%x\n", + new_cs, new_eip, shift, addend); + LOG_PCALL_STATE(env_cpu(env)); + if ((new_cs & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + if (load_segment_ra(env, &e1, &e2, new_cs, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + if (!(e2 & DESC_S_MASK) || + !(e2 & DESC_CS_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + cpl = env->hflags & HF_CPL_MASK; + rpl = new_cs & 3; + if (rpl < cpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + if (e2 & DESC_C_MASK) { + if (dpl > rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + } else { + if (dpl != rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, retaddr); + } + } + if (!(e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, retaddr); + } + + sp += addend; + if (rpl == cpl && (!(env->hflags & HF_CS64_MASK) || + ((env->hflags & HF_CS64_MASK) && !is_iret))) { + /* return to same privilege level */ + cpu_x86_load_seg_cache(env, R_CS, new_cs, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + } else { + /* return to different privilege level */ +#ifdef TARGET_X86_64 + if (shift == 2) { + POPQ_RA(sp, new_esp, retaddr); + POPQ_RA(sp, new_ss, retaddr); + new_ss &= 0xffff; + } else +#endif + { + if (shift == 1) { + /* 32 bits */ + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); + new_ss &= 0xffff; + } else { + /* 16 bits */ + POPW_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPW_RA(ssp, sp, sp_mask, new_ss, retaddr); + } + } + LOG_PCALL("new ss:esp=%04x:" TARGET_FMT_lx "\n", + new_ss, new_esp); + if ((new_ss & 0xfffc) == 0) { +#ifdef TARGET_X86_64 + /* NULL ss is allowed in long mode if cpl != 3 */ + /* XXX: test CS64? */ + if ((env->hflags & HF_LMA_MASK) && rpl != 3) { + cpu_x86_load_seg_cache(env, R_SS, new_ss, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (rpl << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + ss_e2 = DESC_B_MASK; /* XXX: should not be needed? */ + } else +#endif + { + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); + } + } else { + if ((new_ss & 3) != rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); + } + if (load_segment_ra(env, &ss_e1, &ss_e2, new_ss, retaddr) != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); + } + if (!(ss_e2 & DESC_S_MASK) || + (ss_e2 & DESC_CS_MASK) || + !(ss_e2 & DESC_W_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); + } + dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3; + if (dpl != rpl) { + raise_exception_err_ra(env, EXCP0D_GPF, new_ss & 0xfffc, retaddr); + } + if (!(ss_e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0B_NOSEG, new_ss & 0xfffc, retaddr); + } + cpu_x86_load_seg_cache(env, R_SS, new_ss, + get_seg_base(ss_e1, ss_e2), + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } + + cpu_x86_load_seg_cache(env, R_CS, new_cs, + get_seg_base(e1, e2), + get_seg_limit(e1, e2), + e2); + sp = new_esp; +#ifdef TARGET_X86_64 + if (env->hflags & HF_CS64_MASK) { + sp_mask = -1; + } else +#endif + { + sp_mask = get_sp_mask(ss_e2); + } + + /* validate data segments */ + validate_seg(env, R_ES, rpl); + validate_seg(env, R_DS, rpl); + validate_seg(env, R_FS, rpl); + validate_seg(env, R_GS, rpl); + + sp += addend; + } + SET_ESP(sp, sp_mask); + env->eip = new_eip; + if (is_iret) { + /* NOTE: 'cpl' is the _old_ CPL */ + eflags_mask = TF_MASK | AC_MASK | ID_MASK | RF_MASK | NT_MASK; + if (cpl == 0) { + eflags_mask |= IOPL_MASK; + } + iopl = (env->eflags >> IOPL_SHIFT) & 3; + if (cpl <= iopl) { + eflags_mask |= IF_MASK; + } + if (shift == 0) { + eflags_mask &= 0xffff; + } + cpu_load_eflags(env, new_eflags, eflags_mask); + } + return; + + return_to_vm86: + POPL_RA(ssp, sp, sp_mask, new_esp, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ss, retaddr); + POPL_RA(ssp, sp, sp_mask, new_es, retaddr); + POPL_RA(ssp, sp, sp_mask, new_ds, retaddr); + POPL_RA(ssp, sp, sp_mask, new_fs, retaddr); + POPL_RA(ssp, sp, sp_mask, new_gs, retaddr); + + /* modify processor state */ + cpu_load_eflags(env, new_eflags, TF_MASK | AC_MASK | ID_MASK | + IF_MASK | IOPL_MASK | VM_MASK | NT_MASK | VIF_MASK | + VIP_MASK); + load_seg_vm(env, R_CS, new_cs & 0xffff); + load_seg_vm(env, R_SS, new_ss & 0xffff); + load_seg_vm(env, R_ES, new_es & 0xffff); + load_seg_vm(env, R_DS, new_ds & 0xffff); + load_seg_vm(env, R_FS, new_fs & 0xffff); + load_seg_vm(env, R_GS, new_gs & 0xffff); + + env->eip = new_eip & 0xffff; + env->regs[R_ESP] = new_esp; +} + +void helper_iret_protected(CPUX86State *env, int shift, int next_eip) +{ + int tss_selector, type; + uint32_t e1, e2; + + /* specific case for TSS */ + if (env->eflags & NT_MASK) { +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } +#endif + tss_selector = cpu_lduw_kernel_ra(env, env->tr.base + 0, GETPC()); + if (tss_selector & 4) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); + } + if (load_segment_ra(env, &e1, &e2, tss_selector, GETPC()) != 0) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x17; + /* NOTE: we check both segment and busy TSS */ + if (type != 3) { + raise_exception_err_ra(env, EXCP0A_TSS, tss_selector & 0xfffc, GETPC()); + } + switch_tss_ra(env, tss_selector, e1, e2, SWITCH_TSS_IRET, next_eip, GETPC()); + } else { + helper_ret_protected(env, shift, 1, 0, GETPC()); + } + env->hflags2 &= ~HF2_NMI_MASK; +} + +void helper_lret_protected(CPUX86State *env, int shift, int addend) +{ + helper_ret_protected(env, shift, 0, addend, GETPC()); +} + +void helper_sysenter(CPUX86State *env) +{ + if (env->sysenter_cs == 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } + env->eflags &= ~(VM_MASK | IF_MASK | RF_MASK); + +#ifdef TARGET_X86_64 + if (env->hflags & HF_LMA_MASK) { + cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | + DESC_L_MASK); + } else +#endif + { + cpu_x86_load_seg_cache(env, R_CS, env->sysenter_cs & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + } + cpu_x86_load_seg_cache(env, R_SS, (env->sysenter_cs + 8) & 0xfffc, + 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | + DESC_W_MASK | DESC_A_MASK); + env->regs[R_ESP] = env->sysenter_esp; + env->eip = env->sysenter_eip; +} + +void helper_sysexit(CPUX86State *env, int dflag) +{ + int cpl; + + cpl = env->hflags & HF_CPL_MASK; + if (env->sysenter_cs == 0 || cpl != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); + } +#ifdef TARGET_X86_64 + if (dflag == 2) { + cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 32) & 0xfffc) | + 3, 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK | + DESC_L_MASK); + cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 40) & 0xfffc) | + 3, 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } else +#endif + { + cpu_x86_load_seg_cache(env, R_CS, ((env->sysenter_cs + 16) & 0xfffc) | + 3, 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_CS_MASK | DESC_R_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, ((env->sysenter_cs + 24) & 0xfffc) | + 3, 0, 0xffffffff, + DESC_G_MASK | DESC_B_MASK | DESC_P_MASK | + DESC_S_MASK | (3 << DESC_DPL_SHIFT) | + DESC_W_MASK | DESC_A_MASK); + } + env->regs[R_ESP] = env->regs[R_ECX]; + env->eip = env->regs[R_EDX]; +} + +target_ulong helper_lsl(CPUX86State *env, target_ulong selector1) +{ + unsigned int limit; + uint32_t e1, e2, eflags, selector; + int rpl, dpl, cpl, type; + + selector = selector1 & 0xffff; + eflags = cpu_cc_compute_all(env, CC_OP); + if ((selector & 0xfffc) == 0) { + goto fail; + } + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + goto fail; + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { + /* conforming */ + } else { + if (dpl < cpl || dpl < rpl) { + goto fail; + } + } + } else { + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + switch (type) { + case 1: + case 2: + case 3: + case 9: + case 11: + break; + default: + goto fail; + } + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return 0; + } + } + limit = get_seg_limit(e1, e2); + CC_SRC = eflags | CC_Z; + return limit; +} + +target_ulong helper_lar(CPUX86State *env, target_ulong selector1) +{ + uint32_t e1, e2, eflags, selector; + int rpl, dpl, cpl, type; + + selector = selector1 & 0xffff; + eflags = cpu_cc_compute_all(env, CC_OP); + if ((selector & 0xfffc) == 0) { + goto fail; + } + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + goto fail; + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_S_MASK) { + if ((e2 & DESC_CS_MASK) && (e2 & DESC_C_MASK)) { + /* conforming */ + } else { + if (dpl < cpl || dpl < rpl) { + goto fail; + } + } + } else { + type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + switch (type) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 9: + case 11: + case 12: + break; + default: + goto fail; + } + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return 0; + } + } + CC_SRC = eflags | CC_Z; + return e2 & 0x00f0ff00; +} + +void helper_verr(CPUX86State *env, target_ulong selector1) +{ + uint32_t e1, e2, eflags, selector; + int rpl, dpl, cpl; + + selector = selector1 & 0xffff; + eflags = cpu_cc_compute_all(env, CC_OP); + if ((selector & 0xfffc) == 0) { + goto fail; + } + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + goto fail; + } + if (!(e2 & DESC_S_MASK)) { + goto fail; + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_CS_MASK) { + if (!(e2 & DESC_R_MASK)) { + goto fail; + } + if (!(e2 & DESC_C_MASK)) { + if (dpl < cpl || dpl < rpl) { + goto fail; + } + } + } else { + if (dpl < cpl || dpl < rpl) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + CC_SRC = eflags | CC_Z; +} + +void helper_verw(CPUX86State *env, target_ulong selector1) +{ + uint32_t e1, e2, eflags, selector; + int rpl, dpl, cpl; + + selector = selector1 & 0xffff; + eflags = cpu_cc_compute_all(env, CC_OP); + if ((selector & 0xfffc) == 0) { + goto fail; + } + if (load_segment_ra(env, &e1, &e2, selector, GETPC()) != 0) { + goto fail; + } + if (!(e2 & DESC_S_MASK)) { + goto fail; + } + rpl = selector & 3; + dpl = (e2 >> DESC_DPL_SHIFT) & 3; + cpl = env->hflags & HF_CPL_MASK; + if (e2 & DESC_CS_MASK) { + goto fail; + } else { + if (dpl < cpl || dpl < rpl) { + goto fail; + } + if (!(e2 & DESC_W_MASK)) { + fail: + CC_SRC = eflags & ~CC_Z; + return; + } + } + CC_SRC = eflags | CC_Z; +} + +#if defined(CONFIG_USER_ONLY) +void cpu_x86_load_seg(CPUX86State *env, int seg_reg, int selector) +{ + if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) { + int dpl = (env->eflags & VM_MASK) ? 3 : 0; + selector &= 0xffff; + cpu_x86_load_seg_cache(env, seg_reg, selector, + (selector << 4), 0xffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_A_MASK | (dpl << DESC_DPL_SHIFT)); + } else { + helper_load_seg(env, seg_reg, selector); + } +} +#endif + +/* check if Port I/O is allowed in TSS */ +static inline void check_io(CPUX86State *env, int addr, int size, + uintptr_t retaddr) +{ + int io_offset, val, mask; + + /* TSS must be a valid 32 bit one */ + if (!(env->tr.flags & DESC_P_MASK) || + ((env->tr.flags >> DESC_TYPE_SHIFT) & 0xf) != 9 || + env->tr.limit < 103) { + goto fail; + } + io_offset = cpu_lduw_kernel_ra(env, env->tr.base + 0x66, retaddr); + io_offset += (addr >> 3); + /* Note: the check needs two bytes */ + if ((io_offset + 1) > env->tr.limit) { + goto fail; + } + val = cpu_lduw_kernel_ra(env, env->tr.base + io_offset, retaddr); + val >>= (addr & 7); + mask = (1 << size) - 1; + /* all bits must be zero to allow the I/O */ + if ((val & mask) != 0) { + fail: + raise_exception_err_ra(env, EXCP0D_GPF, 0, retaddr); + } +} + +void helper_check_iob(CPUX86State *env, uint32_t t0) +{ + check_io(env, t0, 1, GETPC()); +} + +void helper_check_iow(CPUX86State *env, uint32_t t0) +{ + check_io(env, t0, 2, GETPC()); +} + +void helper_check_iol(CPUX86State *env, uint32_t t0) +{ + check_io(env, t0, 4, GETPC()); +} diff --git a/target/i386/tcg/smm_helper.c b/target/i386/tcg/smm_helper.c new file mode 100644 index 0000000000..d20e8edfdf --- /dev/null +++ b/target/i386/tcg/smm_helper.c @@ -0,0 +1,332 @@ +/* + * x86 SMM helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/log.h" + +/* SMM support */ + +#if defined(CONFIG_USER_ONLY) + +void do_smm_enter(X86CPU *cpu) +{ +} + +void helper_rsm(CPUX86State *env) +{ +} + +#else + +#ifdef TARGET_X86_64 +#define SMM_REVISION_ID 0x00020064 +#else +#define SMM_REVISION_ID 0x00020000 +#endif + +void do_smm_enter(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + CPUState *cs = CPU(cpu); + target_ulong sm_state; + SegmentCache *dt; + int i, offset; + + qemu_log_mask(CPU_LOG_INT, "SMM: enter\n"); + log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); + + env->msr_smi_count++; + env->hflags |= HF_SMM_MASK; + if (env->hflags2 & HF2_NMI_MASK) { + env->hflags2 |= HF2_SMM_INSIDE_NMI_MASK; + } else { + env->hflags2 |= HF2_NMI_MASK; + } + + sm_state = env->smbase + 0x8000; + +#ifdef TARGET_X86_64 + for (i = 0; i < 6; i++) { + dt = &env->segs[i]; + offset = 0x7e00 + i * 16; + x86_stw_phys(cs, sm_state + offset, dt->selector); + x86_stw_phys(cs, sm_state + offset + 2, (dt->flags >> 8) & 0xf0ff); + x86_stl_phys(cs, sm_state + offset + 4, dt->limit); + x86_stq_phys(cs, sm_state + offset + 8, dt->base); + } + + x86_stq_phys(cs, sm_state + 0x7e68, env->gdt.base); + x86_stl_phys(cs, sm_state + 0x7e64, env->gdt.limit); + + x86_stw_phys(cs, sm_state + 0x7e70, env->ldt.selector); + x86_stq_phys(cs, sm_state + 0x7e78, env->ldt.base); + x86_stl_phys(cs, sm_state + 0x7e74, env->ldt.limit); + x86_stw_phys(cs, sm_state + 0x7e72, (env->ldt.flags >> 8) & 0xf0ff); + + x86_stq_phys(cs, sm_state + 0x7e88, env->idt.base); + x86_stl_phys(cs, sm_state + 0x7e84, env->idt.limit); + + x86_stw_phys(cs, sm_state + 0x7e90, env->tr.selector); + x86_stq_phys(cs, sm_state + 0x7e98, env->tr.base); + x86_stl_phys(cs, sm_state + 0x7e94, env->tr.limit); + x86_stw_phys(cs, sm_state + 0x7e92, (env->tr.flags >> 8) & 0xf0ff); + + /* ??? Vol 1, 16.5.6 Intel MPX and SMM says that IA32_BNDCFGS + is saved at offset 7ED0. Vol 3, 34.4.1.1, Table 32-2, has + 7EA0-7ED7 as "reserved". What's this, and what's really + supposed to happen? */ + x86_stq_phys(cs, sm_state + 0x7ed0, env->efer); + + x86_stq_phys(cs, sm_state + 0x7ff8, env->regs[R_EAX]); + x86_stq_phys(cs, sm_state + 0x7ff0, env->regs[R_ECX]); + x86_stq_phys(cs, sm_state + 0x7fe8, env->regs[R_EDX]); + x86_stq_phys(cs, sm_state + 0x7fe0, env->regs[R_EBX]); + x86_stq_phys(cs, sm_state + 0x7fd8, env->regs[R_ESP]); + x86_stq_phys(cs, sm_state + 0x7fd0, env->regs[R_EBP]); + x86_stq_phys(cs, sm_state + 0x7fc8, env->regs[R_ESI]); + x86_stq_phys(cs, sm_state + 0x7fc0, env->regs[R_EDI]); + for (i = 8; i < 16; i++) { + x86_stq_phys(cs, sm_state + 0x7ff8 - i * 8, env->regs[i]); + } + x86_stq_phys(cs, sm_state + 0x7f78, env->eip); + x86_stl_phys(cs, sm_state + 0x7f70, cpu_compute_eflags(env)); + x86_stl_phys(cs, sm_state + 0x7f68, env->dr[6]); + x86_stl_phys(cs, sm_state + 0x7f60, env->dr[7]); + + x86_stl_phys(cs, sm_state + 0x7f48, env->cr[4]); + x86_stq_phys(cs, sm_state + 0x7f50, env->cr[3]); + x86_stl_phys(cs, sm_state + 0x7f58, env->cr[0]); + + x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); + x86_stl_phys(cs, sm_state + 0x7f00, env->smbase); +#else + x86_stl_phys(cs, sm_state + 0x7ffc, env->cr[0]); + x86_stl_phys(cs, sm_state + 0x7ff8, env->cr[3]); + x86_stl_phys(cs, sm_state + 0x7ff4, cpu_compute_eflags(env)); + x86_stl_phys(cs, sm_state + 0x7ff0, env->eip); + x86_stl_phys(cs, sm_state + 0x7fec, env->regs[R_EDI]); + x86_stl_phys(cs, sm_state + 0x7fe8, env->regs[R_ESI]); + x86_stl_phys(cs, sm_state + 0x7fe4, env->regs[R_EBP]); + x86_stl_phys(cs, sm_state + 0x7fe0, env->regs[R_ESP]); + x86_stl_phys(cs, sm_state + 0x7fdc, env->regs[R_EBX]); + x86_stl_phys(cs, sm_state + 0x7fd8, env->regs[R_EDX]); + x86_stl_phys(cs, sm_state + 0x7fd4, env->regs[R_ECX]); + x86_stl_phys(cs, sm_state + 0x7fd0, env->regs[R_EAX]); + x86_stl_phys(cs, sm_state + 0x7fcc, env->dr[6]); + x86_stl_phys(cs, sm_state + 0x7fc8, env->dr[7]); + + x86_stl_phys(cs, sm_state + 0x7fc4, env->tr.selector); + x86_stl_phys(cs, sm_state + 0x7f64, env->tr.base); + x86_stl_phys(cs, sm_state + 0x7f60, env->tr.limit); + x86_stl_phys(cs, sm_state + 0x7f5c, (env->tr.flags >> 8) & 0xf0ff); + + x86_stl_phys(cs, sm_state + 0x7fc0, env->ldt.selector); + x86_stl_phys(cs, sm_state + 0x7f80, env->ldt.base); + x86_stl_phys(cs, sm_state + 0x7f7c, env->ldt.limit); + x86_stl_phys(cs, sm_state + 0x7f78, (env->ldt.flags >> 8) & 0xf0ff); + + x86_stl_phys(cs, sm_state + 0x7f74, env->gdt.base); + x86_stl_phys(cs, sm_state + 0x7f70, env->gdt.limit); + + x86_stl_phys(cs, sm_state + 0x7f58, env->idt.base); + x86_stl_phys(cs, sm_state + 0x7f54, env->idt.limit); + + for (i = 0; i < 6; i++) { + dt = &env->segs[i]; + if (i < 3) { + offset = 0x7f84 + i * 12; + } else { + offset = 0x7f2c + (i - 3) * 12; + } + x86_stl_phys(cs, sm_state + 0x7fa8 + i * 4, dt->selector); + x86_stl_phys(cs, sm_state + offset + 8, dt->base); + x86_stl_phys(cs, sm_state + offset + 4, dt->limit); + x86_stl_phys(cs, sm_state + offset, (dt->flags >> 8) & 0xf0ff); + } + x86_stl_phys(cs, sm_state + 0x7f14, env->cr[4]); + + x86_stl_phys(cs, sm_state + 0x7efc, SMM_REVISION_ID); + x86_stl_phys(cs, sm_state + 0x7ef8, env->smbase); +#endif + /* init SMM cpu state */ + +#ifdef TARGET_X86_64 + cpu_load_efer(env, 0); +#endif + cpu_load_eflags(env, 0, ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | + DF_MASK)); + env->eip = 0x00008000; + cpu_x86_update_cr0(env, + env->cr[0] & ~(CR0_PE_MASK | CR0_EM_MASK | CR0_TS_MASK | + CR0_PG_MASK)); + cpu_x86_update_cr4(env, 0); + env->dr[7] = 0x00000400; + + cpu_x86_load_seg_cache(env, R_CS, (env->smbase >> 4) & 0xffff, env->smbase, + 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_DS, 0, 0, 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_ES, 0, 0, 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_SS, 0, 0, 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_FS, 0, 0, 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); + cpu_x86_load_seg_cache(env, R_GS, 0, 0, 0xffffffff, + DESC_P_MASK | DESC_S_MASK | DESC_W_MASK | + DESC_G_MASK | DESC_A_MASK); +} + +void helper_rsm(CPUX86State *env) +{ + X86CPU *cpu = env_archcpu(env); + CPUState *cs = env_cpu(env); + target_ulong sm_state; + int i, offset; + uint32_t val; + + sm_state = env->smbase + 0x8000; +#ifdef TARGET_X86_64 + cpu_load_efer(env, x86_ldq_phys(cs, sm_state + 0x7ed0)); + + env->gdt.base = x86_ldq_phys(cs, sm_state + 0x7e68); + env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7e64); + + env->ldt.selector = x86_lduw_phys(cs, sm_state + 0x7e70); + env->ldt.base = x86_ldq_phys(cs, sm_state + 0x7e78); + env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7e74); + env->ldt.flags = (x86_lduw_phys(cs, sm_state + 0x7e72) & 0xf0ff) << 8; + + env->idt.base = x86_ldq_phys(cs, sm_state + 0x7e88); + env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7e84); + + env->tr.selector = x86_lduw_phys(cs, sm_state + 0x7e90); + env->tr.base = x86_ldq_phys(cs, sm_state + 0x7e98); + env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7e94); + env->tr.flags = (x86_lduw_phys(cs, sm_state + 0x7e92) & 0xf0ff) << 8; + + env->regs[R_EAX] = x86_ldq_phys(cs, sm_state + 0x7ff8); + env->regs[R_ECX] = x86_ldq_phys(cs, sm_state + 0x7ff0); + env->regs[R_EDX] = x86_ldq_phys(cs, sm_state + 0x7fe8); + env->regs[R_EBX] = x86_ldq_phys(cs, sm_state + 0x7fe0); + env->regs[R_ESP] = x86_ldq_phys(cs, sm_state + 0x7fd8); + env->regs[R_EBP] = x86_ldq_phys(cs, sm_state + 0x7fd0); + env->regs[R_ESI] = x86_ldq_phys(cs, sm_state + 0x7fc8); + env->regs[R_EDI] = x86_ldq_phys(cs, sm_state + 0x7fc0); + for (i = 8; i < 16; i++) { + env->regs[i] = x86_ldq_phys(cs, sm_state + 0x7ff8 - i * 8); + } + env->eip = x86_ldq_phys(cs, sm_state + 0x7f78); + cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7f70), + ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); + env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7f68); + env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7f60); + + cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f48)); + cpu_x86_update_cr3(env, x86_ldq_phys(cs, sm_state + 0x7f50)); + cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7f58)); + + for (i = 0; i < 6; i++) { + offset = 0x7e00 + i * 16; + cpu_x86_load_seg_cache(env, i, + x86_lduw_phys(cs, sm_state + offset), + x86_ldq_phys(cs, sm_state + offset + 8), + x86_ldl_phys(cs, sm_state + offset + 4), + (x86_lduw_phys(cs, sm_state + offset + 2) & + 0xf0ff) << 8); + } + + val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ + if (val & 0x20000) { + env->smbase = x86_ldl_phys(cs, sm_state + 0x7f00); + } +#else + cpu_x86_update_cr0(env, x86_ldl_phys(cs, sm_state + 0x7ffc)); + cpu_x86_update_cr3(env, x86_ldl_phys(cs, sm_state + 0x7ff8)); + cpu_load_eflags(env, x86_ldl_phys(cs, sm_state + 0x7ff4), + ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); + env->eip = x86_ldl_phys(cs, sm_state + 0x7ff0); + env->regs[R_EDI] = x86_ldl_phys(cs, sm_state + 0x7fec); + env->regs[R_ESI] = x86_ldl_phys(cs, sm_state + 0x7fe8); + env->regs[R_EBP] = x86_ldl_phys(cs, sm_state + 0x7fe4); + env->regs[R_ESP] = x86_ldl_phys(cs, sm_state + 0x7fe0); + env->regs[R_EBX] = x86_ldl_phys(cs, sm_state + 0x7fdc); + env->regs[R_EDX] = x86_ldl_phys(cs, sm_state + 0x7fd8); + env->regs[R_ECX] = x86_ldl_phys(cs, sm_state + 0x7fd4); + env->regs[R_EAX] = x86_ldl_phys(cs, sm_state + 0x7fd0); + env->dr[6] = x86_ldl_phys(cs, sm_state + 0x7fcc); + env->dr[7] = x86_ldl_phys(cs, sm_state + 0x7fc8); + + env->tr.selector = x86_ldl_phys(cs, sm_state + 0x7fc4) & 0xffff; + env->tr.base = x86_ldl_phys(cs, sm_state + 0x7f64); + env->tr.limit = x86_ldl_phys(cs, sm_state + 0x7f60); + env->tr.flags = (x86_ldl_phys(cs, sm_state + 0x7f5c) & 0xf0ff) << 8; + + env->ldt.selector = x86_ldl_phys(cs, sm_state + 0x7fc0) & 0xffff; + env->ldt.base = x86_ldl_phys(cs, sm_state + 0x7f80); + env->ldt.limit = x86_ldl_phys(cs, sm_state + 0x7f7c); + env->ldt.flags = (x86_ldl_phys(cs, sm_state + 0x7f78) & 0xf0ff) << 8; + + env->gdt.base = x86_ldl_phys(cs, sm_state + 0x7f74); + env->gdt.limit = x86_ldl_phys(cs, sm_state + 0x7f70); + + env->idt.base = x86_ldl_phys(cs, sm_state + 0x7f58); + env->idt.limit = x86_ldl_phys(cs, sm_state + 0x7f54); + + for (i = 0; i < 6; i++) { + if (i < 3) { + offset = 0x7f84 + i * 12; + } else { + offset = 0x7f2c + (i - 3) * 12; + } + cpu_x86_load_seg_cache(env, i, + x86_ldl_phys(cs, + sm_state + 0x7fa8 + i * 4) & 0xffff, + x86_ldl_phys(cs, sm_state + offset + 8), + x86_ldl_phys(cs, sm_state + offset + 4), + (x86_ldl_phys(cs, + sm_state + offset) & 0xf0ff) << 8); + } + cpu_x86_update_cr4(env, x86_ldl_phys(cs, sm_state + 0x7f14)); + + val = x86_ldl_phys(cs, sm_state + 0x7efc); /* revision ID */ + if (val & 0x20000) { + env->smbase = x86_ldl_phys(cs, sm_state + 0x7ef8); + } +#endif + if ((env->hflags2 & HF2_SMM_INSIDE_NMI_MASK) == 0) { + env->hflags2 &= ~HF2_NMI_MASK; + } + env->hflags2 &= ~HF2_SMM_INSIDE_NMI_MASK; + env->hflags &= ~HF_SMM_MASK; + + qemu_log_mask(CPU_LOG_INT, "SMM: after RSM\n"); + log_cpu_state_mask(CPU_LOG_INT, CPU(cpu), CPU_DUMP_CCOP); +} + +#endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/tcg/svm_helper.c b/target/i386/tcg/svm_helper.c new file mode 100644 index 0000000000..38931586e5 --- /dev/null +++ b/target/i386/tcg/svm_helper.c @@ -0,0 +1,800 @@ +/* + * x86 SVM helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" + +/* Secure Virtual Machine helpers */ + +#if defined(CONFIG_USER_ONLY) + +void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) +{ +} + +void helper_vmmcall(CPUX86State *env) +{ +} + +void helper_vmload(CPUX86State *env, int aflag) +{ +} + +void helper_vmsave(CPUX86State *env, int aflag) +{ +} + +void helper_stgi(CPUX86State *env) +{ +} + +void helper_clgi(CPUX86State *env) +{ +} + +void helper_skinit(CPUX86State *env) +{ +} + +void helper_invlpga(CPUX86State *env, int aflag) +{ +} + +void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, + uintptr_t retaddr) +{ + assert(0); +} + +void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param) +{ +} + +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param, uintptr_t retaddr) +{ +} + +void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, + uint32_t next_eip_addend) +{ +} +#else + +static inline void svm_save_seg(CPUX86State *env, hwaddr addr, + const SegmentCache *sc) +{ + CPUState *cs = env_cpu(env); + + x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, selector), + sc->selector); + x86_stq_phys(cs, addr + offsetof(struct vmcb_seg, base), + sc->base); + x86_stl_phys(cs, addr + offsetof(struct vmcb_seg, limit), + sc->limit); + x86_stw_phys(cs, addr + offsetof(struct vmcb_seg, attrib), + ((sc->flags >> 8) & 0xff) | ((sc->flags >> 12) & 0x0f00)); +} + +static inline void svm_load_seg(CPUX86State *env, hwaddr addr, + SegmentCache *sc) +{ + CPUState *cs = env_cpu(env); + unsigned int flags; + + sc->selector = x86_lduw_phys(cs, + addr + offsetof(struct vmcb_seg, selector)); + sc->base = x86_ldq_phys(cs, addr + offsetof(struct vmcb_seg, base)); + sc->limit = x86_ldl_phys(cs, addr + offsetof(struct vmcb_seg, limit)); + flags = x86_lduw_phys(cs, addr + offsetof(struct vmcb_seg, attrib)); + sc->flags = ((flags & 0xff) << 8) | ((flags & 0x0f00) << 12); +} + +static inline void svm_load_seg_cache(CPUX86State *env, hwaddr addr, + int seg_reg) +{ + SegmentCache sc1, *sc = &sc1; + + svm_load_seg(env, addr, sc); + cpu_x86_load_seg_cache(env, seg_reg, sc->selector, + sc->base, sc->limit, sc->flags); +} + +void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) +{ + CPUState *cs = env_cpu(env); + target_ulong addr; + uint64_t nested_ctl; + uint32_t event_inj; + uint32_t int_ctl; + + cpu_svm_check_intercept_param(env, SVM_EXIT_VMRUN, 0, GETPC()); + + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmrun! " TARGET_FMT_lx "\n", addr); + + env->vm_vmcb = addr; + + /* save the current CPU state in the hsave page */ + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.base), + env->gdt.base); + x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.gdtr.limit), + env->gdt.limit); + + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.base), + env->idt.base); + x86_stl_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.idtr.limit), + env->idt.limit); + + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.cr0), env->cr[0]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.cr2), env->cr[2]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.cr3), env->cr[3]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.cr4), env->cr[4]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr6), env->dr[6]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr7), env->dr[7]); + + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.efer), env->efer); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.rflags), + cpu_compute_eflags(env)); + + svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.es), + &env->segs[R_ES]); + svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.cs), + &env->segs[R_CS]); + svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ss), + &env->segs[R_SS]); + svm_save_seg(env, env->vm_hsave + offsetof(struct vmcb, save.ds), + &env->segs[R_DS]); + + x86_stq_phys(cs, env->vm_hsave + offsetof(struct vmcb, save.rip), + env->eip + next_eip_addend); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); + x86_stq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); + + /* load the interception bitmaps so we do not need to access the + vmcb in svm mode */ + env->intercept = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.intercept)); + env->intercept_cr_read = x86_lduw_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.intercept_cr_read)); + env->intercept_cr_write = x86_lduw_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.intercept_cr_write)); + env->intercept_dr_read = x86_lduw_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.intercept_dr_read)); + env->intercept_dr_write = x86_lduw_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.intercept_dr_write)); + env->intercept_exceptions = x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.intercept_exceptions + )); + + nested_ctl = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.nested_ctl)); + + env->nested_pg_mode = 0; + + if (nested_ctl & SVM_NPT_ENABLED) { + env->nested_cr3 = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + control.nested_cr3)); + env->hflags2 |= HF2_NPT_MASK; + + if (env->cr[4] & CR4_PAE_MASK) { + env->nested_pg_mode |= SVM_NPT_PAE; + } + if (env->cr[4] & CR4_PSE_MASK) { + env->nested_pg_mode |= SVM_NPT_PSE; + } + if (env->hflags & HF_LMA_MASK) { + env->nested_pg_mode |= SVM_NPT_LMA; + } + if (env->efer & MSR_EFER_NXE) { + env->nested_pg_mode |= SVM_NPT_NXE; + } + } + + /* enable intercepts */ + env->hflags |= HF_GUEST_MASK; + + env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.tsc_offset)); + + env->gdt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + save.gdtr.base)); + env->gdt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + save.gdtr.limit)); + + env->idt.base = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + save.idtr.base)); + env->idt.limit = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + save.idtr.limit)); + + /* clear exit_info_2 so we behave like the real hardware */ + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), 0); + + cpu_x86_update_cr0(env, x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + save.cr0))); + cpu_x86_update_cr4(env, x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + save.cr4))); + cpu_x86_update_cr3(env, x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + save.cr3))); + env->cr[2] = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.cr2)); + int_ctl = x86_ldl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); + env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); + if (int_ctl & V_INTR_MASKING_MASK) { + env->v_tpr = int_ctl & V_TPR_MASK; + env->hflags2 |= HF2_VINTR_MASK; + if (env->eflags & IF_MASK) { + env->hflags2 |= HF2_HIF_MASK; + } + } + + cpu_load_efer(env, + x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.efer))); + env->eflags = 0; + cpu_load_eflags(env, x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, + save.rflags)), + ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK)); + + svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.es), + R_ES); + svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), + R_CS); + svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), + R_SS); + svm_load_seg_cache(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), + R_DS); + + env->eip = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.rip)); + + env->regs[R_ESP] = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.rsp)); + env->regs[R_EAX] = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.rax)); + env->dr[7] = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.dr7)); + env->dr[6] = x86_ldq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.dr6)); + + /* FIXME: guest state consistency checks */ + + switch (x86_ldub_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.tlb_ctl))) { + case TLB_CONTROL_DO_NOTHING: + break; + case TLB_CONTROL_FLUSH_ALL_ASID: + /* FIXME: this is not 100% correct but should work for now */ + tlb_flush(cs); + break; + } + + env->hflags2 |= HF2_GIF_MASK; + + if (int_ctl & V_IRQ_MASK) { + CPUState *cs = env_cpu(env); + + cs->interrupt_request |= CPU_INTERRUPT_VIRQ; + } + + /* maybe we need to inject an event */ + event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.event_inj)); + if (event_inj & SVM_EVTINJ_VALID) { + uint8_t vector = event_inj & SVM_EVTINJ_VEC_MASK; + uint16_t valid_err = event_inj & SVM_EVTINJ_VALID_ERR; + uint32_t event_inj_err = x86_ldl_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.event_inj_err)); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "Injecting(%#hx): ", valid_err); + /* FIXME: need to implement valid_err */ + switch (event_inj & SVM_EVTINJ_TYPE_MASK) { + case SVM_EVTINJ_TYPE_INTR: + cs->exception_index = vector; + env->error_code = event_inj_err; + env->exception_is_int = 0; + env->exception_next_eip = -1; + qemu_log_mask(CPU_LOG_TB_IN_ASM, "INTR"); + /* XXX: is it always correct? */ + do_interrupt_x86_hardirq(env, vector, 1); + break; + case SVM_EVTINJ_TYPE_NMI: + cs->exception_index = EXCP02_NMI; + env->error_code = event_inj_err; + env->exception_is_int = 0; + env->exception_next_eip = env->eip; + qemu_log_mask(CPU_LOG_TB_IN_ASM, "NMI"); + cpu_loop_exit(cs); + break; + case SVM_EVTINJ_TYPE_EXEPT: + cs->exception_index = vector; + env->error_code = event_inj_err; + env->exception_is_int = 0; + env->exception_next_eip = -1; + qemu_log_mask(CPU_LOG_TB_IN_ASM, "EXEPT"); + cpu_loop_exit(cs); + break; + case SVM_EVTINJ_TYPE_SOFT: + cs->exception_index = vector; + env->error_code = event_inj_err; + env->exception_is_int = 1; + env->exception_next_eip = env->eip; + qemu_log_mask(CPU_LOG_TB_IN_ASM, "SOFT"); + cpu_loop_exit(cs); + break; + } + qemu_log_mask(CPU_LOG_TB_IN_ASM, " %#x %#x\n", cs->exception_index, + env->error_code); + } +} + +void helper_vmmcall(CPUX86State *env) +{ + cpu_svm_check_intercept_param(env, SVM_EXIT_VMMCALL, 0, GETPC()); + raise_exception(env, EXCP06_ILLOP); +} + +void helper_vmload(CPUX86State *env, int aflag) +{ + CPUState *cs = env_cpu(env); + target_ulong addr; + + cpu_svm_check_intercept_param(env, SVM_EXIT_VMLOAD, 0, GETPC()); + + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmload! " TARGET_FMT_lx + "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", + addr, x86_ldq_phys(cs, addr + offsetof(struct vmcb, + save.fs.base)), + env->segs[R_FS].base); + + svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.fs), R_FS); + svm_load_seg_cache(env, addr + offsetof(struct vmcb, save.gs), R_GS); + svm_load_seg(env, addr + offsetof(struct vmcb, save.tr), &env->tr); + svm_load_seg(env, addr + offsetof(struct vmcb, save.ldtr), &env->ldt); + +#ifdef TARGET_X86_64 + env->kernelgsbase = x86_ldq_phys(cs, addr + offsetof(struct vmcb, + save.kernel_gs_base)); + env->lstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.lstar)); + env->cstar = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.cstar)); + env->fmask = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.sfmask)); +#endif + env->star = x86_ldq_phys(cs, addr + offsetof(struct vmcb, save.star)); + env->sysenter_cs = x86_ldq_phys(cs, + addr + offsetof(struct vmcb, save.sysenter_cs)); + env->sysenter_esp = x86_ldq_phys(cs, addr + offsetof(struct vmcb, + save.sysenter_esp)); + env->sysenter_eip = x86_ldq_phys(cs, addr + offsetof(struct vmcb, + save.sysenter_eip)); +} + +void helper_vmsave(CPUX86State *env, int aflag) +{ + CPUState *cs = env_cpu(env); + target_ulong addr; + + cpu_svm_check_intercept_param(env, SVM_EXIT_VMSAVE, 0, GETPC()); + + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmsave! " TARGET_FMT_lx + "\nFS: %016" PRIx64 " | " TARGET_FMT_lx "\n", + addr, x86_ldq_phys(cs, + addr + offsetof(struct vmcb, save.fs.base)), + env->segs[R_FS].base); + + svm_save_seg(env, addr + offsetof(struct vmcb, save.fs), + &env->segs[R_FS]); + svm_save_seg(env, addr + offsetof(struct vmcb, save.gs), + &env->segs[R_GS]); + svm_save_seg(env, addr + offsetof(struct vmcb, save.tr), + &env->tr); + svm_save_seg(env, addr + offsetof(struct vmcb, save.ldtr), + &env->ldt); + +#ifdef TARGET_X86_64 + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.kernel_gs_base), + env->kernelgsbase); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.lstar), env->lstar); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.cstar), env->cstar); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sfmask), env->fmask); +#endif + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.star), env->star); + x86_stq_phys(cs, + addr + offsetof(struct vmcb, save.sysenter_cs), env->sysenter_cs); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_esp), + env->sysenter_esp); + x86_stq_phys(cs, addr + offsetof(struct vmcb, save.sysenter_eip), + env->sysenter_eip); +} + +void helper_stgi(CPUX86State *env) +{ + cpu_svm_check_intercept_param(env, SVM_EXIT_STGI, 0, GETPC()); + env->hflags2 |= HF2_GIF_MASK; +} + +void helper_clgi(CPUX86State *env) +{ + cpu_svm_check_intercept_param(env, SVM_EXIT_CLGI, 0, GETPC()); + env->hflags2 &= ~HF2_GIF_MASK; +} + +void helper_skinit(CPUX86State *env) +{ + cpu_svm_check_intercept_param(env, SVM_EXIT_SKINIT, 0, GETPC()); + /* XXX: not implemented */ + raise_exception(env, EXCP06_ILLOP); +} + +void helper_invlpga(CPUX86State *env, int aflag) +{ + X86CPU *cpu = env_archcpu(env); + target_ulong addr; + + cpu_svm_check_intercept_param(env, SVM_EXIT_INVLPGA, 0, GETPC()); + + if (aflag == 2) { + addr = env->regs[R_EAX]; + } else { + addr = (uint32_t)env->regs[R_EAX]; + } + + /* XXX: could use the ASID to see if it is needed to do the + flush */ + tlb_flush_page(CPU(cpu), addr); +} + +void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param, uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + if (likely(!(env->hflags & HF_GUEST_MASK))) { + return; + } + switch (type) { + case SVM_EXIT_READ_CR0 ... SVM_EXIT_READ_CR0 + 8: + if (env->intercept_cr_read & (1 << (type - SVM_EXIT_READ_CR0))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + case SVM_EXIT_WRITE_CR0 ... SVM_EXIT_WRITE_CR0 + 8: + if (env->intercept_cr_write & (1 << (type - SVM_EXIT_WRITE_CR0))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + case SVM_EXIT_READ_DR0 ... SVM_EXIT_READ_DR0 + 7: + if (env->intercept_dr_read & (1 << (type - SVM_EXIT_READ_DR0))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + case SVM_EXIT_WRITE_DR0 ... SVM_EXIT_WRITE_DR0 + 7: + if (env->intercept_dr_write & (1 << (type - SVM_EXIT_WRITE_DR0))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + case SVM_EXIT_EXCP_BASE ... SVM_EXIT_EXCP_BASE + 31: + if (env->intercept_exceptions & (1 << (type - SVM_EXIT_EXCP_BASE))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + case SVM_EXIT_MSR: + if (env->intercept & (1ULL << (SVM_EXIT_MSR - SVM_EXIT_INTR))) { + /* FIXME: this should be read in at vmrun (faster this way?) */ + uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, + control.msrpm_base_pa)); + uint32_t t0, t1; + + switch ((uint32_t)env->regs[R_ECX]) { + case 0 ... 0x1fff: + t0 = (env->regs[R_ECX] * 2) % 8; + t1 = (env->regs[R_ECX] * 2) / 8; + break; + case 0xc0000000 ... 0xc0001fff: + t0 = (8192 + env->regs[R_ECX] - 0xc0000000) * 2; + t1 = (t0 / 8); + t0 %= 8; + break; + case 0xc0010000 ... 0xc0011fff: + t0 = (16384 + env->regs[R_ECX] - 0xc0010000) * 2; + t1 = (t0 / 8); + t0 %= 8; + break; + default: + cpu_vmexit(env, type, param, retaddr); + t0 = 0; + t1 = 0; + break; + } + if (x86_ldub_phys(cs, addr + t1) & ((1 << param) << t0)) { + cpu_vmexit(env, type, param, retaddr); + } + } + break; + default: + if (env->intercept & (1ULL << (type - SVM_EXIT_INTR))) { + cpu_vmexit(env, type, param, retaddr); + } + break; + } +} + +void helper_svm_check_intercept_param(CPUX86State *env, uint32_t type, + uint64_t param) +{ + cpu_svm_check_intercept_param(env, type, param, GETPC()); +} + +void helper_svm_check_io(CPUX86State *env, uint32_t port, uint32_t param, + uint32_t next_eip_addend) +{ + CPUState *cs = env_cpu(env); + + if (env->intercept & (1ULL << (SVM_EXIT_IOIO - SVM_EXIT_INTR))) { + /* FIXME: this should be read in at vmrun (faster this way?) */ + uint64_t addr = x86_ldq_phys(cs, env->vm_vmcb + + offsetof(struct vmcb, control.iopm_base_pa)); + uint16_t mask = (1 << ((param >> 4) & 7)) - 1; + + if (x86_lduw_phys(cs, addr + port / 8) & (mask << (port & 7))) { + /* next env->eip */ + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.exit_info_2), + env->eip + next_eip_addend); + cpu_vmexit(env, SVM_EXIT_IOIO, param | (port << 16), GETPC()); + } + } +} + +void cpu_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1, + uintptr_t retaddr) +{ + CPUState *cs = env_cpu(env); + + cpu_restore_state(cs, retaddr, true); + + qemu_log_mask(CPU_LOG_TB_IN_ASM, "vmexit(%08x, %016" PRIx64 ", %016" + PRIx64 ", " TARGET_FMT_lx ")!\n", + exit_code, exit_info_1, + x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.exit_info_2)), + env->eip); + + cs->exception_index = EXCP_VMEXIT + exit_code; + env->error_code = exit_info_1; + + /* remove any pending exception */ + env->old_exception = -1; + cpu_loop_exit(cs); +} + +void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) +{ + CPUState *cs = env_cpu(env); + uint32_t int_ctl; + + if (env->hflags & HF_INHIBIT_IRQ_MASK) { + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.int_state), + SVM_INTERRUPT_SHADOW_MASK); + env->hflags &= ~HF_INHIBIT_IRQ_MASK; + } else { + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.int_state), 0); + } + env->hflags2 &= ~HF2_NPT_MASK; + + /* Save the VM state in the vmcb */ + svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.es), + &env->segs[R_ES]); + svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.cs), + &env->segs[R_CS]); + svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ss), + &env->segs[R_SS]); + svm_save_seg(env, env->vm_vmcb + offsetof(struct vmcb, save.ds), + &env->segs[R_DS]); + + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.base), + env->gdt.base); + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.gdtr.limit), + env->gdt.limit); + + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.base), + env->idt.base); + x86_stl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.idtr.limit), + env->idt.limit); + + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.efer), env->efer); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.cr0), env->cr[0]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.cr2), env->cr[2]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.cr3), env->cr[3]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.cr4), env->cr[4]); + + int_ctl = x86_ldl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.int_ctl)); + int_ctl &= ~(V_TPR_MASK | V_IRQ_MASK); + int_ctl |= env->v_tpr & V_TPR_MASK; + if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { + int_ctl |= V_IRQ_MASK; + } + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.int_ctl), int_ctl); + + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rflags), + cpu_compute_eflags(env)); + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.rip), + env->eip); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.rsp), env->regs[R_ESP]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.rax), env->regs[R_EAX]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.dr7), env->dr[7]); + x86_stq_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, save.dr6), env->dr[6]); + x86_stb_phys(cs, env->vm_vmcb + offsetof(struct vmcb, save.cpl), + env->hflags & HF_CPL_MASK); + + /* Reload the host state from vm_hsave */ + env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); + env->hflags &= ~HF_GUEST_MASK; + env->intercept = 0; + env->intercept_exceptions = 0; + cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + env->tsc_offset = 0; + + env->gdt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, + save.gdtr.base)); + env->gdt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, + save.gdtr.limit)); + + env->idt.base = x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, + save.idtr.base)); + env->idt.limit = x86_ldl_phys(cs, env->vm_hsave + offsetof(struct vmcb, + save.idtr.limit)); + + cpu_x86_update_cr0(env, x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, + save.cr0)) | + CR0_PE_MASK); + cpu_x86_update_cr4(env, x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, + save.cr4))); + cpu_x86_update_cr3(env, x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, + save.cr3))); + /* we need to set the efer after the crs so the hidden flags get + set properly */ + cpu_load_efer(env, x86_ldq_phys(cs, env->vm_hsave + offsetof(struct vmcb, + save.efer))); + env->eflags = 0; + cpu_load_eflags(env, x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, + save.rflags)), + ~(CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C | DF_MASK | + VM_MASK)); + + svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.es), + R_ES); + svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.cs), + R_CS); + svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ss), + R_SS); + svm_load_seg_cache(env, env->vm_hsave + offsetof(struct vmcb, save.ds), + R_DS); + + env->eip = x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.rip)); + env->regs[R_ESP] = x86_ldq_phys(cs, env->vm_hsave + + offsetof(struct vmcb, save.rsp)); + env->regs[R_EAX] = x86_ldq_phys(cs, env->vm_hsave + + offsetof(struct vmcb, save.rax)); + + env->dr[6] = x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr6)); + env->dr[7] = x86_ldq_phys(cs, + env->vm_hsave + offsetof(struct vmcb, save.dr7)); + + /* other setups */ + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_code), + exit_code); + x86_stq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.exit_info_1), + exit_info_1); + + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info), + x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.event_inj))); + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.exit_int_info_err), + x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, + control.event_inj_err))); + x86_stl_phys(cs, + env->vm_vmcb + offsetof(struct vmcb, control.event_inj), 0); + + env->hflags2 &= ~HF2_GIF_MASK; + /* FIXME: Resets the current ASID register to zero (host ASID). */ + + /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ + + /* Clears the TSC_OFFSET inside the processor. */ + + /* If the host is in PAE mode, the processor reloads the host's PDPEs + from the page table indicated the host's CR3. If the PDPEs contain + illegal state, the processor causes a shutdown. */ + + /* Disables all breakpoints in the host DR7 register. */ + + /* Checks the reloaded host state for consistency. */ + + /* If the host's rIP reloaded by #VMEXIT is outside the limit of the + host's code segment or non-canonical (in the case of long mode), a + #GP fault is delivered inside the host. */ +} + +#endif diff --git a/target/i386/tcg/tcg-stub.c b/target/i386/tcg/tcg-stub.c new file mode 100644 index 0000000000..8d45579ada --- /dev/null +++ b/target/i386/tcg/tcg-stub.c @@ -0,0 +1,25 @@ +/* + * x86 FPU, MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4/PNI helpers + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "qemu/osdep.h" +#include "cpu.h" + +void update_mxcsr_from_sse_status(CPUX86State *env) +{ +} diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c new file mode 100644 index 0000000000..e8f5f5803a --- /dev/null +++ b/target/i386/tcg/translate.c @@ -0,0 +1,8642 @@ +/* + * i386 translation + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#include "qemu/osdep.h" + +#include "qemu/host-utils.h" +#include "cpu.h" +#include "disas/disas.h" +#include "exec/exec-all.h" +#include "tcg/tcg-op.h" +#include "exec/cpu_ldst.h" +#include "exec/translator.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" +#include "exec/log.h" + +#define PREFIX_REPZ 0x01 +#define PREFIX_REPNZ 0x02 +#define PREFIX_LOCK 0x04 +#define PREFIX_DATA 0x08 +#define PREFIX_ADR 0x10 +#define PREFIX_VEX 0x20 + +#ifdef TARGET_X86_64 +#define CODE64(s) ((s)->code64) +#define REX_X(s) ((s)->rex_x) +#define REX_B(s) ((s)->rex_b) +#else +#define CODE64(s) 0 +#define REX_X(s) 0 +#define REX_B(s) 0 +#endif + +#ifdef TARGET_X86_64 +# define ctztl ctz64 +# define clztl clz64 +#else +# define ctztl ctz32 +# define clztl clz32 +#endif + +/* For a switch indexed by MODRM, match all memory operands for a given OP. */ +#define CASE_MODRM_MEM_OP(OP) \ + case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ + case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ + case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7 + +#define CASE_MODRM_OP(OP) \ + case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ + case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ + case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7: \ + case (3 << 6) | (OP << 3) | 0 ... (3 << 6) | (OP << 3) | 7 + +//#define MACRO_TEST 1 + +/* global register indexes */ +static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2; +static TCGv_i32 cpu_cc_op; +static TCGv cpu_regs[CPU_NB_REGS]; +static TCGv cpu_seg_base[6]; +static TCGv_i64 cpu_bndl[4]; +static TCGv_i64 cpu_bndu[4]; + +#include "exec/gen-icount.h" + +typedef struct DisasContext { + DisasContextBase base; + + /* current insn context */ + int override; /* -1 if no override */ + int prefix; + MemOp aflag; + MemOp dflag; + target_ulong pc_start; + target_ulong pc; /* pc = eip + cs_base */ + /* current block context */ + target_ulong cs_base; /* base of CS segment */ + int pe; /* protected mode */ + int code32; /* 32 bit code segment */ +#ifdef TARGET_X86_64 + int lma; /* long mode active */ + int code64; /* 64 bit code segment */ + int rex_x, rex_b; +#endif + int vex_l; /* vex vector length */ + int vex_v; /* vex vvvv register, without 1's complement. */ + int ss32; /* 32 bit stack segment */ + CCOp cc_op; /* current CC operation */ + bool cc_op_dirty; +#ifdef TARGET_X86_64 + bool x86_64_hregs; +#endif + int addseg; /* non zero if either DS/ES/SS have a non zero base */ + int f_st; /* currently unused */ + int vm86; /* vm86 mode */ + int cpl; + int iopl; + int tf; /* TF cpu flag */ + int jmp_opt; /* use direct block chaining for direct jumps */ + int repz_opt; /* optimize jumps within repz instructions */ + int mem_index; /* select memory access functions */ + uint64_t flags; /* all execution flags */ + int popl_esp_hack; /* for correct popl with esp base handling */ + int rip_offset; /* only used in x86_64, but left for simplicity */ + int cpuid_features; + int cpuid_ext_features; + int cpuid_ext2_features; + int cpuid_ext3_features; + int cpuid_7_0_ebx_features; + int cpuid_xsave_features; + + /* TCG local temps */ + TCGv cc_srcT; + TCGv A0; + TCGv T0; + TCGv T1; + + /* TCG local register indexes (only used inside old micro ops) */ + TCGv tmp0; + TCGv tmp4; + TCGv_ptr ptr0; + TCGv_ptr ptr1; + TCGv_i32 tmp2_i32; + TCGv_i32 tmp3_i32; + TCGv_i64 tmp1_i64; + + sigjmp_buf jmpbuf; +} DisasContext; + +static void gen_eob(DisasContext *s); +static void gen_jr(DisasContext *s, TCGv dest); +static void gen_jmp(DisasContext *s, target_ulong eip); +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); +static void gen_op(DisasContext *s1, int op, MemOp ot, int d); + +/* i386 arith/logic operations */ +enum { + OP_ADDL, + OP_ORL, + OP_ADCL, + OP_SBBL, + OP_ANDL, + OP_SUBL, + OP_XORL, + OP_CMPL, +}; + +/* i386 shift ops */ +enum { + OP_ROL, + OP_ROR, + OP_RCL, + OP_RCR, + OP_SHL, + OP_SHR, + OP_SHL1, /* undocumented */ + OP_SAR = 7, +}; + +enum { + JCC_O, + JCC_B, + JCC_Z, + JCC_BE, + JCC_S, + JCC_P, + JCC_L, + JCC_LE, +}; + +enum { + /* I386 int registers */ + OR_EAX, /* MUST be even numbered */ + OR_ECX, + OR_EDX, + OR_EBX, + OR_ESP, + OR_EBP, + OR_ESI, + OR_EDI, + + OR_TMP0 = 16, /* temporary operand register */ + OR_TMP1, + OR_A0, /* temporary register used when doing address evaluation */ +}; + +enum { + USES_CC_DST = 1, + USES_CC_SRC = 2, + USES_CC_SRC2 = 4, + USES_CC_SRCT = 8, +}; + +/* Bit set if the global variable is live after setting CC_OP to X. */ +static const uint8_t cc_op_live[CC_OP_NB] = { + [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_EFLAGS] = USES_CC_SRC, + [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADDB ... CC_OP_ADDQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADCB ... CC_OP_ADCQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_SUBB ... CC_OP_SUBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRCT, + [CC_OP_SBBB ... CC_OP_SBBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_LOGICB ... CC_OP_LOGICQ] = USES_CC_DST, + [CC_OP_INCB ... CC_OP_INCQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_DECB ... CC_OP_DECQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, + [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, + [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, + [CC_OP_CLR] = 0, + [CC_OP_POPCNT] = USES_CC_SRC, +}; + +static void set_cc_op(DisasContext *s, CCOp op) +{ + int dead; + + if (s->cc_op == op) { + return; + } + + /* Discard CC computation that will no longer be used. */ + dead = cc_op_live[s->cc_op] & ~cc_op_live[op]; + if (dead & USES_CC_DST) { + tcg_gen_discard_tl(cpu_cc_dst); + } + if (dead & USES_CC_SRC) { + tcg_gen_discard_tl(cpu_cc_src); + } + if (dead & USES_CC_SRC2) { + tcg_gen_discard_tl(cpu_cc_src2); + } + if (dead & USES_CC_SRCT) { + tcg_gen_discard_tl(s->cc_srcT); + } + + if (op == CC_OP_DYNAMIC) { + /* The DYNAMIC setting is translator only, and should never be + stored. Thus we always consider it clean. */ + s->cc_op_dirty = false; + } else { + /* Discard any computed CC_OP value (see shifts). */ + if (s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_discard_i32(cpu_cc_op); + } + s->cc_op_dirty = true; + } + s->cc_op = op; +} + +static void gen_update_cc_op(DisasContext *s) +{ + if (s->cc_op_dirty) { + tcg_gen_movi_i32(cpu_cc_op, s->cc_op); + s->cc_op_dirty = false; + } +} + +#ifdef TARGET_X86_64 + +#define NB_OP_SIZES 4 + +#else /* !TARGET_X86_64 */ + +#define NB_OP_SIZES 3 + +#endif /* !TARGET_X86_64 */ + +#if defined(HOST_WORDS_BIGENDIAN) +#define REG_B_OFFSET (sizeof(target_ulong) - 1) +#define REG_H_OFFSET (sizeof(target_ulong) - 2) +#define REG_W_OFFSET (sizeof(target_ulong) - 2) +#define REG_L_OFFSET (sizeof(target_ulong) - 4) +#define REG_LH_OFFSET (sizeof(target_ulong) - 8) +#else +#define REG_B_OFFSET 0 +#define REG_H_OFFSET 1 +#define REG_W_OFFSET 0 +#define REG_L_OFFSET 0 +#define REG_LH_OFFSET 4 +#endif + +/* In instruction encodings for byte register accesses the + * register number usually indicates "low 8 bits of register N"; + * however there are some special cases where N 4..7 indicates + * [AH, CH, DH, BH], ie "bits 15..8 of register N-4". Return + * true for this special case, false otherwise. + */ +static inline bool byte_reg_is_xH(DisasContext *s, int reg) +{ + if (reg < 4) { + return false; + } +#ifdef TARGET_X86_64 + if (reg >= 8 || s->x86_64_hregs) { + return false; + } +#endif + return true; +} + +/* Select the size of a push/pop operation. */ +static inline MemOp mo_pushpop(DisasContext *s, MemOp ot) +{ + if (CODE64(s)) { + return ot == MO_16 ? MO_16 : MO_64; + } else { + return ot; + } +} + +/* Select the size of the stack pointer. */ +static inline MemOp mo_stacksize(DisasContext *s) +{ + return CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; +} + +/* Select only size 64 else 32. Used for SSE operand sizes. */ +static inline MemOp mo_64_32(MemOp ot) +{ +#ifdef TARGET_X86_64 + return ot == MO_64 ? MO_64 : MO_32; +#else + return MO_32; +#endif +} + +/* Select size 8 if lsb of B is clear, else OT. Used for decoding + byte vs word opcodes. */ +static inline MemOp mo_b_d(int b, MemOp ot) +{ + return b & 1 ? ot : MO_8; +} + +/* Select size 8 if lsb of B is clear, else OT capped at 32. + Used for decoding operand size of port opcodes. */ +static inline MemOp mo_b_d32(int b, MemOp ot) +{ + return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; +} + +static void gen_op_mov_reg_v(DisasContext *s, MemOp ot, int reg, TCGv t0) +{ + switch(ot) { + case MO_8: + if (!byte_reg_is_xH(s, reg)) { + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); + } else { + tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); + } + break; + case MO_16: + tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); + break; + case MO_32: + /* For x86_64, this sets the higher half of register to zero. + For i386, this is equivalent to a mov. */ + tcg_gen_ext32u_tl(cpu_regs[reg], t0); + break; +#ifdef TARGET_X86_64 + case MO_64: + tcg_gen_mov_tl(cpu_regs[reg], t0); + break; +#endif + default: + tcg_abort(); + } +} + +static inline +void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg) +{ + if (ot == MO_8 && byte_reg_is_xH(s, reg)) { + tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); + } else { + tcg_gen_mov_tl(t0, cpu_regs[reg]); + } +} + +static void gen_add_A0_im(DisasContext *s, int val) +{ + tcg_gen_addi_tl(s->A0, s->A0, val); + if (!CODE64(s)) { + tcg_gen_ext32u_tl(s->A0, s->A0); + } +} + +static inline void gen_op_jmp_v(TCGv dest) +{ + tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip)); +} + +static inline +void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) +{ + tcg_gen_addi_tl(s->tmp0, cpu_regs[reg], val); + gen_op_mov_reg_v(s, size, reg, s->tmp0); +} + +static inline void gen_op_add_reg_T0(DisasContext *s, MemOp size, int reg) +{ + tcg_gen_add_tl(s->tmp0, cpu_regs[reg], s->T0); + gen_op_mov_reg_v(s, size, reg, s->tmp0); +} + +static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) +{ + tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE); +} + +static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) +{ + tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE); +} + +static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) +{ + if (d == OR_TMP0) { + gen_op_st_v(s, idx, s->T0, s->A0); + } else { + gen_op_mov_reg_v(s, idx, d, s->T0); + } +} + +static inline void gen_jmp_im(DisasContext *s, target_ulong pc) +{ + tcg_gen_movi_tl(s->tmp0, pc); + gen_op_jmp_v(s->tmp0); +} + +/* Compute SEG:REG into A0. SEG is selected from the override segment + (OVR_SEG) and the default segment (DEF_SEG). OVR_SEG may be -1 to + indicate no override. */ +static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, + int def_seg, int ovr_seg) +{ + switch (aflag) { +#ifdef TARGET_X86_64 + case MO_64: + if (ovr_seg < 0) { + tcg_gen_mov_tl(s->A0, a0); + return; + } + break; +#endif + case MO_32: + /* 32 bit address */ + if (ovr_seg < 0 && s->addseg) { + ovr_seg = def_seg; + } + if (ovr_seg < 0) { + tcg_gen_ext32u_tl(s->A0, a0); + return; + } + break; + case MO_16: + /* 16 bit address */ + tcg_gen_ext16u_tl(s->A0, a0); + a0 = s->A0; + if (ovr_seg < 0) { + if (s->addseg) { + ovr_seg = def_seg; + } else { + return; + } + } + break; + default: + tcg_abort(); + } + + if (ovr_seg >= 0) { + TCGv seg = cpu_seg_base[ovr_seg]; + + if (aflag == MO_64) { + tcg_gen_add_tl(s->A0, a0, seg); + } else if (CODE64(s)) { + tcg_gen_ext32u_tl(s->A0, a0); + tcg_gen_add_tl(s->A0, s->A0, seg); + } else { + tcg_gen_add_tl(s->A0, a0, seg); + tcg_gen_ext32u_tl(s->A0, s->A0); + } + } +} + +static inline void gen_string_movl_A0_ESI(DisasContext *s) +{ + gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); +} + +static inline void gen_string_movl_A0_EDI(DisasContext *s) +{ + gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); +} + +static inline void gen_op_movl_T0_Dshift(DisasContext *s, MemOp ot) +{ + tcg_gen_ld32s_tl(s->T0, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(s->T0, s->T0, ot); +}; + +static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) +{ + switch (size) { + case MO_8: + if (sign) { + tcg_gen_ext8s_tl(dst, src); + } else { + tcg_gen_ext8u_tl(dst, src); + } + return dst; + case MO_16: + if (sign) { + tcg_gen_ext16s_tl(dst, src); + } else { + tcg_gen_ext16u_tl(dst, src); + } + return dst; +#ifdef TARGET_X86_64 + case MO_32: + if (sign) { + tcg_gen_ext32s_tl(dst, src); + } else { + tcg_gen_ext32u_tl(dst, src); + } + return dst; +#endif + default: + return src; + } +} + +static void gen_extu(MemOp ot, TCGv reg) +{ + gen_ext_tl(reg, reg, ot, false); +} + +static void gen_exts(MemOp ot, TCGv reg) +{ + gen_ext_tl(reg, reg, ot, true); +} + +static inline +void gen_op_jnz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) +{ + tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); + gen_extu(size, s->tmp0); + tcg_gen_brcondi_tl(TCG_COND_NE, s->tmp0, 0, label1); +} + +static inline +void gen_op_jz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) +{ + tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); + gen_extu(size, s->tmp0); + tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); +} + +static void gen_helper_in_func(MemOp ot, TCGv v, TCGv_i32 n) +{ + switch (ot) { + case MO_8: + gen_helper_inb(v, cpu_env, n); + break; + case MO_16: + gen_helper_inw(v, cpu_env, n); + break; + case MO_32: + gen_helper_inl(v, cpu_env, n); + break; + default: + tcg_abort(); + } +} + +static void gen_helper_out_func(MemOp ot, TCGv_i32 v, TCGv_i32 n) +{ + switch (ot) { + case MO_8: + gen_helper_outb(cpu_env, v, n); + break; + case MO_16: + gen_helper_outw(cpu_env, v, n); + break; + case MO_32: + gen_helper_outl(cpu_env, v, n); + break; + default: + tcg_abort(); + } +} + +static void gen_check_io(DisasContext *s, MemOp ot, target_ulong cur_eip, + uint32_t svm_flags) +{ + target_ulong next_eip; + + if (s->pe && (s->cpl > s->iopl || s->vm86)) { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + switch (ot) { + case MO_8: + gen_helper_check_iob(cpu_env, s->tmp2_i32); + break; + case MO_16: + gen_helper_check_iow(cpu_env, s->tmp2_i32); + break; + case MO_32: + gen_helper_check_iol(cpu_env, s->tmp2_i32); + break; + default: + tcg_abort(); + } + } + if(s->flags & HF_GUEST_MASK) { + gen_update_cc_op(s); + gen_jmp_im(s, cur_eip); + svm_flags |= (1 << (4 + ot)); + next_eip = s->pc - s->cs_base; + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_svm_check_io(cpu_env, s->tmp2_i32, + tcg_const_i32(svm_flags), + tcg_const_i32(next_eip - cur_eip)); + } +} + +static inline void gen_movs(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_string_movl_A0_EDI(s); + gen_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg_T0(s, s->aflag, R_EDI); +} + +static void gen_op_update1_cc(DisasContext *s) +{ + tcg_gen_mov_tl(cpu_cc_dst, s->T0); +} + +static void gen_op_update2_cc(DisasContext *s) +{ + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); +} + +static void gen_op_update3_cc(DisasContext *s, TCGv reg) +{ + tcg_gen_mov_tl(cpu_cc_src2, reg); + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); +} + +static inline void gen_op_testl_T0_T1_cc(DisasContext *s) +{ + tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1); +} + +static void gen_op_update_neg_cc(DisasContext *s) +{ + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_neg_tl(cpu_cc_src, s->T0); + tcg_gen_movi_tl(s->cc_srcT, 0); +} + +/* compute all eflags to cc_src */ +static void gen_compute_eflags(DisasContext *s) +{ + TCGv zero, dst, src1, src2; + int live, dead; + + if (s->cc_op == CC_OP_EFLAGS) { + return; + } + if (s->cc_op == CC_OP_CLR) { + tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); + set_cc_op(s, CC_OP_EFLAGS); + return; + } + + zero = NULL; + dst = cpu_cc_dst; + src1 = cpu_cc_src; + src2 = cpu_cc_src2; + + /* Take care to not read values that are not live. */ + live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; + dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2); + if (dead) { + zero = tcg_const_tl(0); + if (dead & USES_CC_DST) { + dst = zero; + } + if (dead & USES_CC_SRC) { + src1 = zero; + } + if (dead & USES_CC_SRC2) { + src2 = zero; + } + } + + gen_update_cc_op(s); + gen_helper_cc_compute_all(cpu_cc_src, dst, src1, src2, cpu_cc_op); + set_cc_op(s, CC_OP_EFLAGS); + + if (dead) { + tcg_temp_free(zero); + } +} + +typedef struct CCPrepare { + TCGCond cond; + TCGv reg; + TCGv reg2; + target_ulong imm; + target_ulong mask; + bool use_reg2; + bool no_setcond; +} CCPrepare; + +/* compute eflags.C to reg */ +static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) +{ + TCGv t0, t1; + int size, shift; + + switch (s->cc_op) { + case CC_OP_SUBB ... CC_OP_SUBQ: + /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ + size = s->cc_op - CC_OP_SUBB; + t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); + /* If no temporary was used, be careful not to alias t1 and t0. */ + t0 = t1 == cpu_cc_src ? s->tmp0 : reg; + tcg_gen_mov_tl(t0, s->cc_srcT); + gen_extu(size, t0); + goto add_sub; + + case CC_OP_ADDB ... CC_OP_ADDQ: + /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ + size = s->cc_op - CC_OP_ADDB; + t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); + t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); + add_sub: + return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, + .reg2 = t1, .mask = -1, .use_reg2 = true }; + + case CC_OP_LOGICB ... CC_OP_LOGICQ: + case CC_OP_CLR: + case CC_OP_POPCNT: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + + case CC_OP_INCB ... CC_OP_INCQ: + case CC_OP_DECB ... CC_OP_DECQ: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = -1, .no_setcond = true }; + + case CC_OP_SHLB ... CC_OP_SHLQ: + /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ + size = s->cc_op - CC_OP_SHLB; + shift = (8 << size) - 1; + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = (target_ulong)1 << shift }; + + case CC_OP_MULB ... CC_OP_MULQ: + return (CCPrepare) { .cond = TCG_COND_NE, + .reg = cpu_cc_src, .mask = -1 }; + + case CC_OP_BMILGB ... CC_OP_BMILGQ: + size = s->cc_op - CC_OP_BMILGB; + t0 = gen_ext_tl(reg, cpu_cc_src, size, false); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + + case CC_OP_ADCX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, + .mask = -1, .no_setcond = true }; + + case CC_OP_EFLAGS: + case CC_OP_SARB ... CC_OP_SARQ: + /* CC_SRC & 1 */ + return (CCPrepare) { .cond = TCG_COND_NE, + .reg = cpu_cc_src, .mask = CC_C }; + + default: + /* The need to compute only C from CC_OP_DYNAMIC is important + in efficiently implementing e.g. INC at the start of a TB. */ + gen_update_cc_op(s); + gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, + cpu_cc_src2, cpu_cc_op); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = -1, .no_setcond = true }; + } +} + +/* compute eflags.P to reg */ +static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) +{ + gen_compute_eflags(s); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_P }; +} + +/* compute eflags.S to reg */ +static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_DYNAMIC: + gen_compute_eflags(s); + /* FALLTHRU */ + case CC_OP_EFLAGS: + case CC_OP_ADCX: + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_S }; + case CC_OP_CLR: + case CC_OP_POPCNT: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + default: + { + MemOp size = (s->cc_op - CC_OP_ADDB) & 3; + TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); + return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; + } + } +} + +/* compute eflags.O to reg */ +static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, + .mask = -1, .no_setcond = true }; + case CC_OP_CLR: + case CC_OP_POPCNT: + return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; + default: + gen_compute_eflags(s); + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_O }; + } +} + +/* compute eflags.Z to reg */ +static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) +{ + switch (s->cc_op) { + case CC_OP_DYNAMIC: + gen_compute_eflags(s); + /* FALLTHRU */ + case CC_OP_EFLAGS: + case CC_OP_ADCX: + case CC_OP_ADOX: + case CC_OP_ADCOX: + return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_Z }; + case CC_OP_CLR: + return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; + case CC_OP_POPCNT: + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src, + .mask = -1 }; + default: + { + MemOp size = (s->cc_op - CC_OP_ADDB) & 3; + TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); + return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; + } + } +} + +/* perform a conditional store into register 'reg' according to jump opcode + value 'b'. In the fast case, T0 is guaranted not to be used. */ +static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) +{ + int inv, jcc_op, cond; + MemOp size; + CCPrepare cc; + TCGv t0; + + inv = b & 1; + jcc_op = (b >> 1) & 7; + + switch (s->cc_op) { + case CC_OP_SUBB ... CC_OP_SUBQ: + /* We optimize relational operators for the cmp/jcc case. */ + size = s->cc_op - CC_OP_SUBB; + switch (jcc_op) { + case JCC_BE: + tcg_gen_mov_tl(s->tmp4, s->cc_srcT); + gen_extu(size, s->tmp4); + t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); + cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, + .reg2 = t0, .mask = -1, .use_reg2 = true }; + break; + + case JCC_L: + cond = TCG_COND_LT; + goto fast_jcc_l; + case JCC_LE: + cond = TCG_COND_LE; + fast_jcc_l: + tcg_gen_mov_tl(s->tmp4, s->cc_srcT); + gen_exts(size, s->tmp4); + t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); + cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, + .reg2 = t0, .mask = -1, .use_reg2 = true }; + break; + + default: + goto slow_jcc; + } + break; + + default: + slow_jcc: + /* This actually generates good code for JC, JZ and JS. */ + switch (jcc_op) { + case JCC_O: + cc = gen_prepare_eflags_o(s, reg); + break; + case JCC_B: + cc = gen_prepare_eflags_c(s, reg); + break; + case JCC_Z: + cc = gen_prepare_eflags_z(s, reg); + break; + case JCC_BE: + gen_compute_eflags(s); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, + .mask = CC_Z | CC_C }; + break; + case JCC_S: + cc = gen_prepare_eflags_s(s, reg); + break; + case JCC_P: + cc = gen_prepare_eflags_p(s, reg); + break; + case JCC_L: + gen_compute_eflags(s); + if (reg == cpu_cc_src) { + reg = s->tmp0; + } + tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ + tcg_gen_xor_tl(reg, reg, cpu_cc_src); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = CC_S }; + break; + default: + case JCC_LE: + gen_compute_eflags(s); + if (reg == cpu_cc_src) { + reg = s->tmp0; + } + tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ + tcg_gen_xor_tl(reg, reg, cpu_cc_src); + cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, + .mask = CC_S | CC_Z }; + break; + } + break; + } + + if (inv) { + cc.cond = tcg_invert_cond(cc.cond); + } + return cc; +} + +static void gen_setcc1(DisasContext *s, int b, TCGv reg) +{ + CCPrepare cc = gen_prepare_cc(s, b, reg); + + if (cc.no_setcond) { + if (cc.cond == TCG_COND_EQ) { + tcg_gen_xori_tl(reg, cc.reg, 1); + } else { + tcg_gen_mov_tl(reg, cc.reg); + } + return; + } + + if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && + cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { + tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); + tcg_gen_andi_tl(reg, reg, 1); + return; + } + if (cc.mask != -1) { + tcg_gen_andi_tl(reg, cc.reg, cc.mask); + cc.reg = reg; + } + if (cc.use_reg2) { + tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); + } else { + tcg_gen_setcondi_tl(cc.cond, reg, cc.reg, cc.imm); + } +} + +static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) +{ + gen_setcc1(s, JCC_B << 1, reg); +} + +/* generate a conditional jump to label 'l1' according to jump opcode + value 'b'. In the fast case, T0 is guaranted not to be used. */ +static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) +{ + CCPrepare cc = gen_prepare_cc(s, b, s->T0); + + if (cc.mask != -1) { + tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); + cc.reg = s->T0; + } + if (cc.use_reg2) { + tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); + } else { + tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); + } +} + +/* Generate a conditional jump to label 'l1' according to jump opcode + value 'b'. In the fast case, T0 is guaranted not to be used. + A translation block must end soon. */ +static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) +{ + CCPrepare cc = gen_prepare_cc(s, b, s->T0); + + gen_update_cc_op(s); + if (cc.mask != -1) { + tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); + cc.reg = s->T0; + } + set_cc_op(s, CC_OP_DYNAMIC); + if (cc.use_reg2) { + tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); + } else { + tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); + } +} + +/* XXX: does not work with gdbstub "ice" single step - not a + serious problem */ +static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) +{ + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + gen_op_jnz_ecx(s, s->aflag, l1); + gen_set_label(l2); + gen_jmp_tb(s, next_eip, 1); + gen_set_label(l1); + return l2; +} + +static inline void gen_stos(DisasContext *s, MemOp ot) +{ + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); + gen_string_movl_A0_EDI(s); + gen_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); +} + +static inline void gen_lods(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, s->T0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); +} + +static inline void gen_scas(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_EDI(s); + gen_op_ld_v(s, ot, s->T1, s->A0); + gen_op(s, OP_CMPL, ot, R_EAX); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); +} + +static inline void gen_cmps(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_EDI(s); + gen_op_ld_v(s, ot, s->T1, s->A0); + gen_string_movl_A0_ESI(s); + gen_op(s, OP_CMPL, ot, OR_TMP0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg_T0(s, s->aflag, R_EDI); +} + +static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) +{ + if (s->flags & HF_IOBPT_MASK) { + TCGv_i32 t_size = tcg_const_i32(1 << ot); + TCGv t_next = tcg_const_tl(s->pc - s->cs_base); + + gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); + tcg_temp_free_i32(t_size); + tcg_temp_free(t_next); + } +} + + +static inline void gen_ins(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_EDI(s); + /* Note: we must do this dummy write first to be restartable in + case of page fault. */ + tcg_gen_movi_tl(s->T0, 0); + gen_op_st_v(s, ot, s->T0, s->A0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); + tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); + gen_helper_in_func(ot, s->T0, s->tmp2_i32); + gen_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); + gen_bpt_io(s, s->tmp2_i32, ot); +} + +static inline void gen_outs(DisasContext *s, MemOp ot) +{ + gen_string_movl_A0_ESI(s); + gen_op_ld_v(s, ot, s->T0, s->A0); + + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); + tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_bpt_io(s, s->tmp2_i32, ot); +} + +/* same method as Valgrind : we generate jumps to current or next + instruction */ +#define GEN_REPZ(op) \ +static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ + target_ulong cur_eip, target_ulong next_eip) \ +{ \ + TCGLabel *l2; \ + gen_update_cc_op(s); \ + l2 = gen_jz_ecx_string(s, next_eip); \ + gen_ ## op(s, ot); \ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ + /* a loop would cause two single step exceptions if ECX = 1 \ + before rep string_insn */ \ + if (s->repz_opt) \ + gen_op_jz_ecx(s, s->aflag, l2); \ + gen_jmp(s, cur_eip); \ +} + +#define GEN_REPZ2(op) \ +static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ + target_ulong cur_eip, \ + target_ulong next_eip, \ + int nz) \ +{ \ + TCGLabel *l2; \ + gen_update_cc_op(s); \ + l2 = gen_jz_ecx_string(s, next_eip); \ + gen_ ## op(s, ot); \ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ + gen_update_cc_op(s); \ + gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ + if (s->repz_opt) \ + gen_op_jz_ecx(s, s->aflag, l2); \ + gen_jmp(s, cur_eip); \ +} + +GEN_REPZ(movs) +GEN_REPZ(stos) +GEN_REPZ(lods) +GEN_REPZ(ins) +GEN_REPZ(outs) +GEN_REPZ2(scas) +GEN_REPZ2(cmps) + +static void gen_helper_fp_arith_ST0_FT0(int op) +{ + switch (op) { + case 0: + gen_helper_fadd_ST0_FT0(cpu_env); + break; + case 1: + gen_helper_fmul_ST0_FT0(cpu_env); + break; + case 2: + gen_helper_fcom_ST0_FT0(cpu_env); + break; + case 3: + gen_helper_fcom_ST0_FT0(cpu_env); + break; + case 4: + gen_helper_fsub_ST0_FT0(cpu_env); + break; + case 5: + gen_helper_fsubr_ST0_FT0(cpu_env); + break; + case 6: + gen_helper_fdiv_ST0_FT0(cpu_env); + break; + case 7: + gen_helper_fdivr_ST0_FT0(cpu_env); + break; + } +} + +/* NOTE the exception in "r" op ordering */ +static void gen_helper_fp_arith_STN_ST0(int op, int opreg) +{ + TCGv_i32 tmp = tcg_const_i32(opreg); + switch (op) { + case 0: + gen_helper_fadd_STN_ST0(cpu_env, tmp); + break; + case 1: + gen_helper_fmul_STN_ST0(cpu_env, tmp); + break; + case 4: + gen_helper_fsubr_STN_ST0(cpu_env, tmp); + break; + case 5: + gen_helper_fsub_STN_ST0(cpu_env, tmp); + break; + case 6: + gen_helper_fdivr_STN_ST0(cpu_env, tmp); + break; + case 7: + gen_helper_fdiv_STN_ST0(cpu_env, tmp); + break; + } +} + +static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) +{ + gen_update_cc_op(s); + gen_jmp_im(s, cur_eip); + gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); + s->base.is_jmp = DISAS_NORETURN; +} + +/* Generate #UD for the current instruction. The assumption here is that + the instruction is known, but it isn't allowed in the current cpu mode. */ +static void gen_illegal_opcode(DisasContext *s) +{ + gen_exception(s, EXCP06_ILLOP, s->pc_start - s->cs_base); +} + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_op(DisasContext *s1, int op, MemOp ot, int d) +{ + if (d != OR_TMP0) { + if (s1->prefix & PREFIX_LOCK) { + /* Lock prefix when destination is not memory. */ + gen_illegal_opcode(s1); + return; + } + gen_op_mov_v_reg(s1, ot, s1->T0, d); + } else if (!(s1->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s1, ot, s1->T0, s1->A0); + } + switch(op) { + case OP_ADCL: + gen_compute_eflags_c(s1, s1->tmp4); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); + tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update3_cc(s1, s1->tmp4); + set_cc_op(s1, CC_OP_ADCB + ot); + break; + case OP_SBBL: + gen_compute_eflags_c(s1, s1->tmp4); + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4); + tcg_gen_neg_tl(s1->T0, s1->T0); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); + tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update3_cc(s1, s1->tmp4); + set_cc_op(s1, CC_OP_SBBB + ot); + break; + case OP_ADDL: + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update2_cc(s1); + set_cc_op(s1, CC_OP_ADDB + ot); + break; + case OP_SUBL: + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_neg_tl(s1->T0, s1->T1); + tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0, + s1->mem_index, ot | MO_LE); + tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1); + } else { + tcg_gen_mov_tl(s1->cc_srcT, s1->T0); + tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update2_cc(s1); + set_cc_op(s1, CC_OP_SUBB + ot); + break; + default: + case OP_ANDL: + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_and_tl(s1->T0, s1->T0, s1->T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update1_cc(s1); + set_cc_op(s1, CC_OP_LOGICB + ot); + break; + case OP_ORL: + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_or_tl(s1->T0, s1->T0, s1->T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update1_cc(s1); + set_cc_op(s1, CC_OP_LOGICB + ot); + break; + case OP_XORL: + if (s1->prefix & PREFIX_LOCK) { + tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1, + s1->mem_index, ot | MO_LE); + } else { + tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1); + gen_op_st_rm_T0_A0(s1, ot, d); + } + gen_op_update1_cc(s1); + set_cc_op(s1, CC_OP_LOGICB + ot); + break; + case OP_CMPL: + tcg_gen_mov_tl(cpu_cc_src, s1->T1); + tcg_gen_mov_tl(s1->cc_srcT, s1->T0); + tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1); + set_cc_op(s1, CC_OP_SUBB + ot); + break; + } +} + +/* if d == OR_TMP0, it means memory operand (address in A0) */ +static void gen_inc(DisasContext *s1, MemOp ot, int d, int c) +{ + if (s1->prefix & PREFIX_LOCK) { + if (d != OR_TMP0) { + /* Lock prefix when destination is not memory */ + gen_illegal_opcode(s1); + return; + } + tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, + s1->mem_index, ot | MO_LE); + } else { + if (d != OR_TMP0) { + gen_op_mov_v_reg(s1, ot, s1->T0, d); + } else { + gen_op_ld_v(s1, ot, s1->T0, s1->A0); + } + tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1)); + gen_op_st_rm_T0_A0(s1, ot, d); + } + + gen_compute_eflags_c(s1, cpu_cc_src); + tcg_gen_mov_tl(cpu_cc_dst, s1->T0); + set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); +} + +static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, + TCGv shm1, TCGv count, bool is_right) +{ + TCGv_i32 z32, s32, oldop; + TCGv z_tl; + + /* Store the results into the CC variables. If we know that the + variable must be dead, store unconditionally. Otherwise we'll + need to not disrupt the current contents. */ + z_tl = tcg_const_tl(0); + if (cc_op_live[s->cc_op] & USES_CC_DST) { + tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, + result, cpu_cc_dst); + } else { + tcg_gen_mov_tl(cpu_cc_dst, result); + } + if (cc_op_live[s->cc_op] & USES_CC_SRC) { + tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, + shm1, cpu_cc_src); + } else { + tcg_gen_mov_tl(cpu_cc_src, shm1); + } + tcg_temp_free(z_tl); + + /* Get the two potential CC_OP values into temporaries. */ + tcg_gen_movi_i32(s->tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); + if (s->cc_op == CC_OP_DYNAMIC) { + oldop = cpu_cc_op; + } else { + tcg_gen_movi_i32(s->tmp3_i32, s->cc_op); + oldop = s->tmp3_i32; + } + + /* Conditionally store the CC_OP value. */ + z32 = tcg_const_i32(0); + s32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(s32, count); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); + tcg_temp_free_i32(z32); + tcg_temp_free_i32(s32); + + /* The CC_OP value is no longer predictable. */ + set_cc_op(s, CC_OP_DYNAMIC); +} + +static void gen_shift_rm_T1(DisasContext *s, MemOp ot, int op1, + int is_right, int is_arith) +{ + target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); + + /* load */ + if (op1 == OR_TMP0) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, op1); + } + + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_subi_tl(s->tmp0, s->T1, 1); + + if (is_right) { + if (is_arith) { + gen_exts(ot, s->T0); + tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_sar_tl(s->T0, s->T0, s->T1); + } else { + gen_extu(ot, s->T0); + tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); + } + } else { + tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_tl(s->T0, s->T0, s->T1); + } + + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); + + gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right); +} + +static void gen_shift_rm_im(DisasContext *s, MemOp ot, int op1, int op2, + int is_right, int is_arith) +{ + int mask = (ot == MO_64 ? 0x3f : 0x1f); + + /* load */ + if (op1 == OR_TMP0) + gen_op_ld_v(s, ot, s->T0, s->A0); + else + gen_op_mov_v_reg(s, ot, s->T0, op1); + + op2 &= mask; + if (op2 != 0) { + if (is_right) { + if (is_arith) { + gen_exts(ot, s->T0); + tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_sari_tl(s->T0, s->T0, op2); + } else { + gen_extu(ot, s->T0); + tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_shri_tl(s->T0, s->T0, op2); + } + } else { + tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_shli_tl(s->T0, s->T0, op2); + } + } + + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); + + /* update eflags if non zero shift */ + if (op2 != 0) { + tcg_gen_mov_tl(cpu_cc_src, s->tmp4); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); + } +} + +static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right) +{ + target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); + TCGv_i32 t0, t1; + + /* load */ + if (op1 == OR_TMP0) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, op1); + } + + tcg_gen_andi_tl(s->T1, s->T1, mask); + + switch (ot) { + case MO_8: + /* Replicate the 8-bit input so that a 32-bit rotate works. */ + tcg_gen_ext8u_tl(s->T0, s->T0); + tcg_gen_muli_tl(s->T0, s->T0, 0x01010101); + goto do_long; + case MO_16: + /* Replicate the 16-bit input so that a 32-bit rotate works. */ + tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16); + goto do_long; + do_long: +#ifdef TARGET_X86_64 + case MO_32: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + if (is_right) { + tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + } else { + tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + } + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + break; +#endif + default: + if (is_right) { + tcg_gen_rotr_tl(s->T0, s->T0, s->T1); + } else { + tcg_gen_rotl_tl(s->T0, s->T0, s->T1); + } + break; + } + + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); + + /* We'll need the flags computed into CC_SRC. */ + gen_compute_eflags(s); + + /* The value that was "rotated out" is now present at the other end + of the word. Compute C into CC_DST and O into CC_SRC2. Note that + since we've computed the flags into CC_SRC, these variables are + currently dead. */ + if (is_right) { + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); + } else { + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); + } + tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); + tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); + + /* Now conditionally store the new CC_OP value. If the shift count + is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. + Otherwise reuse CC_OP_ADCOX which have the C and O flags split out + exactly as we computed above. */ + t0 = tcg_const_i32(0); + t1 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t1, s->T1); + tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); + tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, + s->tmp2_i32, s->tmp3_i32); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + + /* The CC_OP value is no longer predictable. */ + set_cc_op(s, CC_OP_DYNAMIC); +} + +static void gen_rot_rm_im(DisasContext *s, MemOp ot, int op1, int op2, + int is_right) +{ + int mask = (ot == MO_64 ? 0x3f : 0x1f); + int shift; + + /* load */ + if (op1 == OR_TMP0) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, op1); + } + + op2 &= mask; + if (op2 != 0) { + switch (ot) { +#ifdef TARGET_X86_64 + case MO_32: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + if (is_right) { + tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2); + } else { + tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2); + } + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + break; +#endif + default: + if (is_right) { + tcg_gen_rotri_tl(s->T0, s->T0, op2); + } else { + tcg_gen_rotli_tl(s->T0, s->T0, op2); + } + break; + case MO_8: + mask = 7; + goto do_shifts; + case MO_16: + mask = 15; + do_shifts: + shift = op2 & mask; + if (is_right) { + shift = mask + 1 - shift; + } + gen_extu(ot, s->T0); + tcg_gen_shli_tl(s->tmp0, s->T0, shift); + tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift); + tcg_gen_or_tl(s->T0, s->T0, s->tmp0); + break; + } + } + + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); + + if (op2 != 0) { + /* Compute the flags into CC_SRC. */ + gen_compute_eflags(s); + + /* The value that was "rotated out" is now present at the other end + of the word. Compute C into CC_DST and O into CC_SRC2. Note that + since we've computed the flags into CC_SRC, these variables are + currently dead. */ + if (is_right) { + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); + } else { + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); + } + tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); + tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); + set_cc_op(s, CC_OP_ADCOX); + } +} + +/* XXX: add faster immediate = 1 case */ +static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1, + int is_right) +{ + gen_compute_eflags(s); + assert(s->cc_op == CC_OP_EFLAGS); + + /* load */ + if (op1 == OR_TMP0) + gen_op_ld_v(s, ot, s->T0, s->A0); + else + gen_op_mov_v_reg(s, ot, s->T0, op1); + + if (is_right) { + switch (ot) { + case MO_8: + gen_helper_rcrb(s->T0, cpu_env, s->T0, s->T1); + break; + case MO_16: + gen_helper_rcrw(s->T0, cpu_env, s->T0, s->T1); + break; + case MO_32: + gen_helper_rcrl(s->T0, cpu_env, s->T0, s->T1); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_rcrq(s->T0, cpu_env, s->T0, s->T1); + break; +#endif + default: + tcg_abort(); + } + } else { + switch (ot) { + case MO_8: + gen_helper_rclb(s->T0, cpu_env, s->T0, s->T1); + break; + case MO_16: + gen_helper_rclw(s->T0, cpu_env, s->T0, s->T1); + break; + case MO_32: + gen_helper_rcll(s->T0, cpu_env, s->T0, s->T1); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_rclq(s->T0, cpu_env, s->T0, s->T1); + break; +#endif + default: + tcg_abort(); + } + } + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); +} + +/* XXX: add faster immediate case */ +static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, + bool is_right, TCGv count_in) +{ + target_ulong mask = (ot == MO_64 ? 63 : 31); + TCGv count; + + /* load */ + if (op1 == OR_TMP0) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, op1); + } + + count = tcg_temp_new(); + tcg_gen_andi_tl(count, count_in, mask); + + switch (ot) { + case MO_16: + /* Note: we implement the Intel behaviour for shift count > 16. + This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A + portion by constructing it as a 32-bit value. */ + if (is_right) { + tcg_gen_deposit_tl(s->tmp0, s->T0, s->T1, 16, 16); + tcg_gen_mov_tl(s->T1, s->T0); + tcg_gen_mov_tl(s->T0, s->tmp0); + } else { + tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 16, 16); + } + /* FALLTHRU */ +#ifdef TARGET_X86_64 + case MO_32: + /* Concatenate the two 32-bit values and use a 64-bit shift. */ + tcg_gen_subi_tl(s->tmp0, count, 1); + if (is_right) { + tcg_gen_concat_tl_i64(s->T0, s->T0, s->T1); + tcg_gen_shr_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_i64(s->T0, s->T0, count); + } else { + tcg_gen_concat_tl_i64(s->T0, s->T1, s->T0); + tcg_gen_shl_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_i64(s->T0, s->T0, count); + tcg_gen_shri_i64(s->tmp0, s->tmp0, 32); + tcg_gen_shri_i64(s->T0, s->T0, 32); + } + break; +#endif + default: + tcg_gen_subi_tl(s->tmp0, count, 1); + if (is_right) { + tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); + + tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + tcg_gen_shr_tl(s->T0, s->T0, count); + tcg_gen_shl_tl(s->T1, s->T1, s->tmp4); + } else { + tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); + if (ot == MO_16) { + /* Only needed if count > 16, for Intel behaviour. */ + tcg_gen_subfi_tl(s->tmp4, 33, count); + tcg_gen_shr_tl(s->tmp4, s->T1, s->tmp4); + tcg_gen_or_tl(s->tmp0, s->tmp0, s->tmp4); + } + + tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + tcg_gen_shl_tl(s->T0, s->T0, count); + tcg_gen_shr_tl(s->T1, s->T1, s->tmp4); + } + tcg_gen_movi_tl(s->tmp4, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, s->tmp4, + s->tmp4, s->T1); + tcg_gen_or_tl(s->T0, s->T0, s->T1); + break; + } + + /* store */ + gen_op_st_rm_T0_A0(s, ot, op1); + + gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right); + tcg_temp_free(count); +} + +static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s) +{ + if (s != OR_TMP1) + gen_op_mov_v_reg(s1, ot, s1->T1, s); + switch(op) { + case OP_ROL: + gen_rot_rm_T1(s1, ot, d, 0); + break; + case OP_ROR: + gen_rot_rm_T1(s1, ot, d, 1); + break; + case OP_SHL: + case OP_SHL1: + gen_shift_rm_T1(s1, ot, d, 0, 0); + break; + case OP_SHR: + gen_shift_rm_T1(s1, ot, d, 1, 0); + break; + case OP_SAR: + gen_shift_rm_T1(s1, ot, d, 1, 1); + break; + case OP_RCL: + gen_rotc_rm_T1(s1, ot, d, 0); + break; + case OP_RCR: + gen_rotc_rm_T1(s1, ot, d, 1); + break; + } +} + +static void gen_shifti(DisasContext *s1, int op, MemOp ot, int d, int c) +{ + switch(op) { + case OP_ROL: + gen_rot_rm_im(s1, ot, d, c, 0); + break; + case OP_ROR: + gen_rot_rm_im(s1, ot, d, c, 1); + break; + case OP_SHL: + case OP_SHL1: + gen_shift_rm_im(s1, ot, d, c, 0, 0); + break; + case OP_SHR: + gen_shift_rm_im(s1, ot, d, c, 1, 0); + break; + case OP_SAR: + gen_shift_rm_im(s1, ot, d, c, 1, 1); + break; + default: + /* currently not optimized */ + tcg_gen_movi_tl(s1->T1, c); + gen_shift(s1, op, ot, d, OR_TMP1); + break; + } +} + +#define X86_MAX_INSN_LENGTH 15 + +static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) +{ + uint64_t pc = s->pc; + + s->pc += num_bytes; + if (unlikely(s->pc - s->pc_start > X86_MAX_INSN_LENGTH)) { + /* If the instruction's 16th byte is on a different page than the 1st, a + * page fault on the second page wins over the general protection fault + * caused by the instruction being too long. + * This can happen even if the operand is only one byte long! + */ + if (((s->pc - 1) ^ (pc - 1)) & TARGET_PAGE_MASK) { + volatile uint8_t unused = + cpu_ldub_code(env, (s->pc - 1) & TARGET_PAGE_MASK); + (void) unused; + } + siglongjmp(s->jmpbuf, 1); + } + + return pc; +} + +static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s) +{ + return translator_ldub(env, advance_pc(env, s, 1)); +} + +static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s) +{ + return translator_ldsw(env, advance_pc(env, s, 2)); +} + +static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s) +{ + return translator_lduw(env, advance_pc(env, s, 2)); +} + +static inline uint32_t x86_ldl_code(CPUX86State *env, DisasContext *s) +{ + return translator_ldl(env, advance_pc(env, s, 4)); +} + +#ifdef TARGET_X86_64 +static inline uint64_t x86_ldq_code(CPUX86State *env, DisasContext *s) +{ + return translator_ldq(env, advance_pc(env, s, 8)); +} +#endif + +/* Decompose an address. */ + +typedef struct AddressParts { + int def_seg; + int base; + int index; + int scale; + target_long disp; +} AddressParts; + +static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, + int modrm) +{ + int def_seg, base, index, scale, mod, rm; + target_long disp; + bool havesib; + + def_seg = R_DS; + index = -1; + scale = 0; + disp = 0; + + mod = (modrm >> 6) & 3; + rm = modrm & 7; + base = rm | REX_B(s); + + if (mod == 3) { + /* Normally filtered out earlier, but including this path + simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */ + goto done; + } + + switch (s->aflag) { + case MO_64: + case MO_32: + havesib = 0; + if (rm == 4) { + int code = x86_ldub_code(env, s); + scale = (code >> 6) & 3; + index = ((code >> 3) & 7) | REX_X(s); + if (index == 4) { + index = -1; /* no index */ + } + base = (code & 7) | REX_B(s); + havesib = 1; + } + + switch (mod) { + case 0: + if ((base & 7) == 5) { + base = -1; + disp = (int32_t)x86_ldl_code(env, s); + if (CODE64(s) && !havesib) { + base = -2; + disp += s->pc + s->rip_offset; + } + } + break; + case 1: + disp = (int8_t)x86_ldub_code(env, s); + break; + default: + case 2: + disp = (int32_t)x86_ldl_code(env, s); + break; + } + + /* For correct popl handling with esp. */ + if (base == R_ESP && s->popl_esp_hack) { + disp += s->popl_esp_hack; + } + if (base == R_EBP || base == R_ESP) { + def_seg = R_SS; + } + break; + + case MO_16: + if (mod == 0) { + if (rm == 6) { + base = -1; + disp = x86_lduw_code(env, s); + break; + } + } else if (mod == 1) { + disp = (int8_t)x86_ldub_code(env, s); + } else { + disp = (int16_t)x86_lduw_code(env, s); + } + + switch (rm) { + case 0: + base = R_EBX; + index = R_ESI; + break; + case 1: + base = R_EBX; + index = R_EDI; + break; + case 2: + base = R_EBP; + index = R_ESI; + def_seg = R_SS; + break; + case 3: + base = R_EBP; + index = R_EDI; + def_seg = R_SS; + break; + case 4: + base = R_ESI; + break; + case 5: + base = R_EDI; + break; + case 6: + base = R_EBP; + def_seg = R_SS; + break; + default: + case 7: + base = R_EBX; + break; + } + break; + + default: + tcg_abort(); + } + + done: + return (AddressParts){ def_seg, base, index, scale, disp }; +} + +/* Compute the address, with a minimum number of TCG ops. */ +static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) +{ + TCGv ea = NULL; + + if (a.index >= 0) { + if (a.scale == 0) { + ea = cpu_regs[a.index]; + } else { + tcg_gen_shli_tl(s->A0, cpu_regs[a.index], a.scale); + ea = s->A0; + } + if (a.base >= 0) { + tcg_gen_add_tl(s->A0, ea, cpu_regs[a.base]); + ea = s->A0; + } + } else if (a.base >= 0) { + ea = cpu_regs[a.base]; + } + if (!ea) { + tcg_gen_movi_tl(s->A0, a.disp); + ea = s->A0; + } else if (a.disp != 0) { + tcg_gen_addi_tl(s->A0, ea, a.disp); + ea = s->A0; + } + + return ea; +} + +static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(s, a); + gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); +} + +static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) +{ + (void)gen_lea_modrm_0(env, s, modrm); +} + +/* Used for BNDCL, BNDCU, BNDCN. */ +static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, + TCGCond cond, TCGv_i64 bndv) +{ + TCGv ea = gen_lea_modrm_1(s, gen_lea_modrm_0(env, s, modrm)); + + tcg_gen_extu_tl_i64(s->tmp1_i64, ea); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(s->tmp1_i64, s->tmp1_i64); + } + tcg_gen_setcond_i64(cond, s->tmp1_i64, s->tmp1_i64, bndv); + tcg_gen_extrl_i64_i32(s->tmp2_i32, s->tmp1_i64); + gen_helper_bndck(cpu_env, s->tmp2_i32); +} + +/* used for LEA and MOV AX, mem */ +static void gen_add_A0_ds_seg(DisasContext *s) +{ + gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); +} + +/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == + OR_TMP0 */ +static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, + MemOp ot, int reg, int is_store) +{ + int mod, rm; + + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod == 3) { + if (is_store) { + if (reg != OR_TMP0) + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + if (reg != OR_TMP0) + gen_op_mov_reg_v(s, ot, reg, s->T0); + } + } else { + gen_lea_modrm(env, s, modrm); + if (is_store) { + if (reg != OR_TMP0) + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_st_v(s, ot, s->T0, s->A0); + } else { + gen_op_ld_v(s, ot, s->T0, s->A0); + if (reg != OR_TMP0) + gen_op_mov_reg_v(s, ot, reg, s->T0); + } + } +} + +static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot) +{ + uint32_t ret; + + switch (ot) { + case MO_8: + ret = x86_ldub_code(env, s); + break; + case MO_16: + ret = x86_lduw_code(env, s); + break; + case MO_32: +#ifdef TARGET_X86_64 + case MO_64: +#endif + ret = x86_ldl_code(env, s); + break; + default: + tcg_abort(); + } + return ret; +} + +static inline int insn_const_size(MemOp ot) +{ + if (ot <= MO_32) { + return 1 << ot; + } else { + return 4; + } +} + +static inline bool use_goto_tb(DisasContext *s, target_ulong pc) +{ +#ifndef CONFIG_USER_ONLY + return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || + (pc & TARGET_PAGE_MASK) == (s->pc_start & TARGET_PAGE_MASK); +#else + return true; +#endif +} + +static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) +{ + target_ulong pc = s->cs_base + eip; + + if (use_goto_tb(s, pc)) { + /* jump to same page: we can use a direct jump */ + tcg_gen_goto_tb(tb_num); + gen_jmp_im(s, eip); + tcg_gen_exit_tb(s->base.tb, tb_num); + s->base.is_jmp = DISAS_NORETURN; + } else { + /* jump to another page */ + gen_jmp_im(s, eip); + gen_jr(s, s->tmp0); + } +} + +static inline void gen_jcc(DisasContext *s, int b, + target_ulong val, target_ulong next_eip) +{ + TCGLabel *l1, *l2; + + if (s->jmp_opt) { + l1 = gen_new_label(); + gen_jcc1(s, b, l1); + + gen_goto_tb(s, 0, next_eip); + + gen_set_label(l1); + gen_goto_tb(s, 1, val); + } else { + l1 = gen_new_label(); + l2 = gen_new_label(); + gen_jcc1(s, b, l1); + + gen_jmp_im(s, next_eip); + tcg_gen_br(l2); + + gen_set_label(l1); + gen_jmp_im(s, val); + gen_set_label(l2); + gen_eob(s); + } +} + +static void gen_cmovcc1(CPUX86State *env, DisasContext *s, MemOp ot, int b, + int modrm, int reg) +{ + CCPrepare cc; + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + cc = gen_prepare_cc(s, b, s->T1); + if (cc.mask != -1) { + TCGv t0 = tcg_temp_new(); + tcg_gen_andi_tl(t0, cc.reg, cc.mask); + cc.reg = t0; + } + if (!cc.use_reg2) { + cc.reg2 = tcg_const_tl(cc.imm); + } + + tcg_gen_movcond_tl(cc.cond, s->T0, cc.reg, cc.reg2, + s->T0, cpu_regs[reg]); + gen_op_mov_reg_v(s, ot, reg, s->T0); + + if (cc.mask != -1) { + tcg_temp_free(cc.reg); + } + if (!cc.use_reg2) { + tcg_temp_free(cc.reg2); + } +} + +static inline void gen_op_movl_T0_seg(DisasContext *s, int seg_reg) +{ + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State,segs[seg_reg].selector)); +} + +static inline void gen_op_movl_seg_T0_vm(DisasContext *s, int seg_reg) +{ + tcg_gen_ext16u_tl(s->T0, s->T0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State,segs[seg_reg].selector)); + tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); +} + +/* move T0 to seg_reg and compute if the CPU state may change. Never + call this function with seg_reg == R_CS */ +static void gen_movl_seg_T0(DisasContext *s, int seg_reg) +{ + if (s->pe && !s->vm86) { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), s->tmp2_i32); + /* abort translation because the addseg value may change or + because ss32 may change. For R_SS, translation must always + stop as a special handling must be done to disable hardware + interrupts for the next instruction */ + if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) { + s->base.is_jmp = DISAS_TOO_MANY; + } + } else { + gen_op_movl_seg_T0_vm(s, seg_reg); + if (seg_reg == R_SS) { + s->base.is_jmp = DISAS_TOO_MANY; + } + } +} + +static inline int svm_is_rep(int prefixes) +{ + return ((prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) ? 8 : 0); +} + +static inline void +gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start, + uint32_t type, uint64_t param) +{ + /* no SVM activated; fast case */ + if (likely(!(s->flags & HF_GUEST_MASK))) + return; + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_svm_check_intercept_param(cpu_env, tcg_const_i32(type), + tcg_const_i64(param)); +} + +static inline void +gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type) +{ + gen_svm_check_intercept_param(s, pc_start, type, 0); +} + +static inline void gen_stack_update(DisasContext *s, int addend) +{ + gen_op_add_reg_im(s, mo_stacksize(s), R_ESP, addend); +} + +/* Generate a push. It depends on ss32, addseg and dflag. */ +static void gen_push_v(DisasContext *s, TCGv val) +{ + MemOp d_ot = mo_pushpop(s, s->dflag); + MemOp a_ot = mo_stacksize(s); + int size = 1 << d_ot; + TCGv new_esp = s->A0; + + tcg_gen_subi_tl(s->A0, cpu_regs[R_ESP], size); + + if (!CODE64(s)) { + if (s->addseg) { + new_esp = s->tmp4; + tcg_gen_mov_tl(new_esp, s->A0); + } + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + } + + gen_op_st_v(s, d_ot, val, s->A0); + gen_op_mov_reg_v(s, a_ot, R_ESP, new_esp); +} + +/* two step pop is necessary for precise exceptions */ +static MemOp gen_pop_T0(DisasContext *s) +{ + MemOp d_ot = mo_pushpop(s, s->dflag); + + gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); + gen_op_ld_v(s, d_ot, s->T0, s->A0); + + return d_ot; +} + +static inline void gen_pop_update(DisasContext *s, MemOp ot) +{ + gen_stack_update(s, 1 << ot); +} + +static inline void gen_stack_A0(DisasContext *s) +{ + gen_lea_v_seg(s, s->ss32 ? MO_32 : MO_16, cpu_regs[R_ESP], R_SS, -1); +} + +static void gen_pusha(DisasContext *s) +{ + MemOp s_ot = s->ss32 ? MO_32 : MO_16; + MemOp d_ot = s->dflag; + int size = 1 << d_ot; + int i; + + for (i = 0; i < 8; i++) { + tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], (i - 8) * size); + gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[7 - i], s->A0); + } + + gen_stack_update(s, -8 * size); +} + +static void gen_popa(DisasContext *s) +{ + MemOp s_ot = s->ss32 ? MO_32 : MO_16; + MemOp d_ot = s->dflag; + int size = 1 << d_ot; + int i; + + for (i = 0; i < 8; i++) { + /* ESP is not reloaded */ + if (7 - i == R_ESP) { + continue; + } + tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], i * size); + gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_op_ld_v(s, d_ot, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, 7 - i, s->T0); + } + + gen_stack_update(s, 8 * size); +} + +static void gen_enter(DisasContext *s, int esp_addend, int level) +{ + MemOp d_ot = mo_pushpop(s, s->dflag); + MemOp a_ot = CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; + int size = 1 << d_ot; + + /* Push BP; compute FrameTemp into T1. */ + tcg_gen_subi_tl(s->T1, cpu_regs[R_ESP], size); + gen_lea_v_seg(s, a_ot, s->T1, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[R_EBP], s->A0); + + level &= 31; + if (level != 0) { + int i; + + /* Copy level-1 pointers from the previous frame. */ + for (i = 1; i < level; ++i) { + tcg_gen_subi_tl(s->A0, cpu_regs[R_EBP], size * i); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_ld_v(s, d_ot, s->tmp0, s->A0); + + tcg_gen_subi_tl(s->A0, s->T1, size * i); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, s->tmp0, s->A0); + } + + /* Push the current FrameTemp as the last level. */ + tcg_gen_subi_tl(s->A0, s->T1, size * level); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, s->T1, s->A0); + } + + /* Copy the FrameTemp value to EBP. */ + gen_op_mov_reg_v(s, a_ot, R_EBP, s->T1); + + /* Compute the final value of ESP. */ + tcg_gen_subi_tl(s->T1, s->T1, esp_addend + size * level); + gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); +} + +static void gen_leave(DisasContext *s) +{ + MemOp d_ot = mo_pushpop(s, s->dflag); + MemOp a_ot = mo_stacksize(s); + + gen_lea_v_seg(s, a_ot, cpu_regs[R_EBP], R_SS, -1); + gen_op_ld_v(s, d_ot, s->T0, s->A0); + + tcg_gen_addi_tl(s->T1, cpu_regs[R_EBP], 1 << d_ot); + + gen_op_mov_reg_v(s, d_ot, R_EBP, s->T0); + gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); +} + +/* Similarly, except that the assumption here is that we don't decode + the instruction at all -- either a missing opcode, an unimplemented + feature, or just a bogus instruction stream. */ +static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) +{ + gen_illegal_opcode(s); + + if (qemu_loglevel_mask(LOG_UNIMP)) { + FILE *logfile = qemu_log_lock(); + target_ulong pc = s->pc_start, end = s->pc; + + qemu_log("ILLOPC: " TARGET_FMT_lx ":", pc); + for (; pc < end; ++pc) { + qemu_log(" %02x", cpu_ldub_code(env, pc)); + } + qemu_log("\n"); + qemu_log_unlock(logfile); + } +} + +/* an interrupt is different from an exception because of the + privilege checks */ +static void gen_interrupt(DisasContext *s, int intno, + target_ulong cur_eip, target_ulong next_eip) +{ + gen_update_cc_op(s); + gen_jmp_im(s, cur_eip); + gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), + tcg_const_i32(next_eip - cur_eip)); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_debug(DisasContext *s, target_ulong cur_eip) +{ + gen_update_cc_op(s); + gen_jmp_im(s, cur_eip); + gen_helper_debug(cpu_env); + s->base.is_jmp = DISAS_NORETURN; +} + +static void gen_set_hflag(DisasContext *s, uint32_t mask) +{ + if ((s->flags & mask) == 0) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_ori_i32(t, t, mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags |= mask; + } +} + +static void gen_reset_hflag(DisasContext *s, uint32_t mask) +{ + if (s->flags & mask) { + TCGv_i32 t = tcg_temp_new_i32(); + tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_gen_andi_i32(t, t, ~mask); + tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); + tcg_temp_free_i32(t); + s->flags &= ~mask; + } +} + +/* Clear BND registers during legacy branches. */ +static void gen_bnd_jmp(DisasContext *s) +{ + /* Clear the registers only if BND prefix is missing, MPX is enabled, + and if the BNDREGs are known to be in use (non-zero) already. + The helper itself will check BNDPRESERVE at runtime. */ + if ((s->prefix & PREFIX_REPNZ) == 0 + && (s->flags & HF_MPX_EN_MASK) != 0 + && (s->flags & HF_MPX_IU_MASK) != 0) { + gen_helper_bnd_jmp(cpu_env); + } +} + +/* Generate an end of block. Trace exception is also generated if needed. + If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. + If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of + S->TF. This is used by the syscall/sysret insns. */ +static void +do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) +{ + gen_update_cc_op(s); + + /* If several instructions disable interrupts, only the first does it. */ + if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) { + gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); + } else { + gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); + } + + if (s->base.tb->flags & HF_RF_MASK) { + gen_helper_reset_rf(cpu_env); + } + if (s->base.singlestep_enabled) { + gen_helper_debug(cpu_env); + } else if (recheck_tf) { + gen_helper_rechecking_single_step(cpu_env); + tcg_gen_exit_tb(NULL, 0); + } else if (s->tf) { + gen_helper_single_step(cpu_env); + } else if (jr) { + tcg_gen_lookup_and_goto_ptr(); + } else { + tcg_gen_exit_tb(NULL, 0); + } + s->base.is_jmp = DISAS_NORETURN; +} + +static inline void +gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf) +{ + do_gen_eob_worker(s, inhibit, recheck_tf, false); +} + +/* End of block. + If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. */ +static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit) +{ + gen_eob_worker(s, inhibit, false); +} + +/* End of block, resetting the inhibit irq flag. */ +static void gen_eob(DisasContext *s) +{ + gen_eob_worker(s, false, false); +} + +/* Jump to register */ +static void gen_jr(DisasContext *s, TCGv dest) +{ + do_gen_eob_worker(s, false, false, true); +} + +/* generate a jump to eip. No segment change must happen before as a + direct call to the next block may occur */ +static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) +{ + gen_update_cc_op(s); + set_cc_op(s, CC_OP_DYNAMIC); + if (s->jmp_opt) { + gen_goto_tb(s, tb_num, eip); + } else { + gen_jmp_im(s, eip); + gen_eob(s); + } +} + +static void gen_jmp(DisasContext *s, target_ulong eip) +{ + gen_jmp_tb(s, eip, 0); +} + +static inline void gen_ldq_env_A0(DisasContext *s, int offset) +{ + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset); +} + +static inline void gen_stq_env_A0(DisasContext *s, int offset) +{ + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); +} + +static inline void gen_ldo_env_A0(DisasContext *s, int offset) +{ + int mem_index = s->mem_index; + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_addi_tl(s->tmp0, s->A0, 8); + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); +} + +static inline void gen_sto_env_A0(DisasContext *s, int offset) +{ + int mem_index = s->mem_index; + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); + tcg_gen_addi_tl(s->tmp0, s->A0, 8); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); +} + +static inline void gen_op_movo(DisasContext *s, int d_offset, int s_offset) +{ + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(1))); +} + +static inline void gen_op_movq(DisasContext *s, int d_offset, int s_offset) +{ + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); +} + +static inline void gen_op_movl(DisasContext *s, int d_offset, int s_offset) +{ + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, s_offset); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, d_offset); +} + +static inline void gen_op_movq_env_0(DisasContext *s, int d_offset) +{ + tcg_gen_movi_i64(s->tmp1_i64, 0); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); +} + +typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); +typedef void (*SSEFunc_0_epi)(TCGv_ptr env, TCGv_ptr reg, TCGv_i32 val); +typedef void (*SSEFunc_0_epl)(TCGv_ptr env, TCGv_ptr reg, TCGv_i64 val); +typedef void (*SSEFunc_0_epp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b); +typedef void (*SSEFunc_0_eppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv_i32 val); +typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val); +typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, + TCGv val); + +#define SSE_SPECIAL ((void *)1) +#define SSE_DUMMY ((void *)2) + +#define MMX_OP2(x) { gen_helper_ ## x ## _mmx, gen_helper_ ## x ## _xmm } +#define SSE_FOP(x) { gen_helper_ ## x ## ps, gen_helper_ ## x ## pd, \ + gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, } + +static const SSEFunc_0_epp sse_op_table1[256][4] = { + /* 3DNow! extensions */ + [0x0e] = { SSE_DUMMY }, /* femms */ + [0x0f] = { SSE_DUMMY }, /* pf... */ + /* pure SSE operations */ + [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ + [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ + [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */ + [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */ + [0x14] = { gen_helper_punpckldq_xmm, gen_helper_punpcklqdq_xmm }, + [0x15] = { gen_helper_punpckhdq_xmm, gen_helper_punpckhqdq_xmm }, + [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */ + [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */ + + [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ + [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ + [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */ + [0x2b] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd, movntss, movntsd */ + [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */ + [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */ + [0x2e] = { gen_helper_ucomiss, gen_helper_ucomisd }, + [0x2f] = { gen_helper_comiss, gen_helper_comisd }, + [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */ + [0x51] = SSE_FOP(sqrt), + [0x52] = { gen_helper_rsqrtps, NULL, gen_helper_rsqrtss, NULL }, + [0x53] = { gen_helper_rcpps, NULL, gen_helper_rcpss, NULL }, + [0x54] = { gen_helper_pand_xmm, gen_helper_pand_xmm }, /* andps, andpd */ + [0x55] = { gen_helper_pandn_xmm, gen_helper_pandn_xmm }, /* andnps, andnpd */ + [0x56] = { gen_helper_por_xmm, gen_helper_por_xmm }, /* orps, orpd */ + [0x57] = { gen_helper_pxor_xmm, gen_helper_pxor_xmm }, /* xorps, xorpd */ + [0x58] = SSE_FOP(add), + [0x59] = SSE_FOP(mul), + [0x5a] = { gen_helper_cvtps2pd, gen_helper_cvtpd2ps, + gen_helper_cvtss2sd, gen_helper_cvtsd2ss }, + [0x5b] = { gen_helper_cvtdq2ps, gen_helper_cvtps2dq, gen_helper_cvttps2dq }, + [0x5c] = SSE_FOP(sub), + [0x5d] = SSE_FOP(min), + [0x5e] = SSE_FOP(div), + [0x5f] = SSE_FOP(max), + + [0xc2] = SSE_FOP(cmpeq), + [0xc6] = { (SSEFunc_0_epp)gen_helper_shufps, + (SSEFunc_0_epp)gen_helper_shufpd }, /* XXX: casts */ + + /* SSSE3, SSE4, MOVBE, CRC32, BMI1, BMI2, ADX. */ + [0x38] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + [0x3a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + + /* MMX ops and their SSE extensions */ + [0x60] = MMX_OP2(punpcklbw), + [0x61] = MMX_OP2(punpcklwd), + [0x62] = MMX_OP2(punpckldq), + [0x63] = MMX_OP2(packsswb), + [0x64] = MMX_OP2(pcmpgtb), + [0x65] = MMX_OP2(pcmpgtw), + [0x66] = MMX_OP2(pcmpgtl), + [0x67] = MMX_OP2(packuswb), + [0x68] = MMX_OP2(punpckhbw), + [0x69] = MMX_OP2(punpckhwd), + [0x6a] = MMX_OP2(punpckhdq), + [0x6b] = MMX_OP2(packssdw), + [0x6c] = { NULL, gen_helper_punpcklqdq_xmm }, + [0x6d] = { NULL, gen_helper_punpckhqdq_xmm }, + [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */ + [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */ + [0x70] = { (SSEFunc_0_epp)gen_helper_pshufw_mmx, + (SSEFunc_0_epp)gen_helper_pshufd_xmm, + (SSEFunc_0_epp)gen_helper_pshufhw_xmm, + (SSEFunc_0_epp)gen_helper_pshuflw_xmm }, /* XXX: casts */ + [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */ + [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */ + [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */ + [0x74] = MMX_OP2(pcmpeqb), + [0x75] = MMX_OP2(pcmpeqw), + [0x76] = MMX_OP2(pcmpeql), + [0x77] = { SSE_DUMMY }, /* emms */ + [0x78] = { NULL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* extrq_i, insertq_i */ + [0x79] = { NULL, gen_helper_extrq_r, NULL, gen_helper_insertq_r }, + [0x7c] = { NULL, gen_helper_haddpd, NULL, gen_helper_haddps }, + [0x7d] = { NULL, gen_helper_hsubpd, NULL, gen_helper_hsubps }, + [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */ + [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */ + [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */ + [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */ + [0xd0] = { NULL, gen_helper_addsubpd, NULL, gen_helper_addsubps }, + [0xd1] = MMX_OP2(psrlw), + [0xd2] = MMX_OP2(psrld), + [0xd3] = MMX_OP2(psrlq), + [0xd4] = MMX_OP2(paddq), + [0xd5] = MMX_OP2(pmullw), + [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, + [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */ + [0xd8] = MMX_OP2(psubusb), + [0xd9] = MMX_OP2(psubusw), + [0xda] = MMX_OP2(pminub), + [0xdb] = MMX_OP2(pand), + [0xdc] = MMX_OP2(paddusb), + [0xdd] = MMX_OP2(paddusw), + [0xde] = MMX_OP2(pmaxub), + [0xdf] = MMX_OP2(pandn), + [0xe0] = MMX_OP2(pavgb), + [0xe1] = MMX_OP2(psraw), + [0xe2] = MMX_OP2(psrad), + [0xe3] = MMX_OP2(pavgw), + [0xe4] = MMX_OP2(pmulhuw), + [0xe5] = MMX_OP2(pmulhw), + [0xe6] = { NULL, gen_helper_cvttpd2dq, gen_helper_cvtdq2pd, gen_helper_cvtpd2dq }, + [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */ + [0xe8] = MMX_OP2(psubsb), + [0xe9] = MMX_OP2(psubsw), + [0xea] = MMX_OP2(pminsw), + [0xeb] = MMX_OP2(por), + [0xec] = MMX_OP2(paddsb), + [0xed] = MMX_OP2(paddsw), + [0xee] = MMX_OP2(pmaxsw), + [0xef] = MMX_OP2(pxor), + [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */ + [0xf1] = MMX_OP2(psllw), + [0xf2] = MMX_OP2(pslld), + [0xf3] = MMX_OP2(psllq), + [0xf4] = MMX_OP2(pmuludq), + [0xf5] = MMX_OP2(pmaddwd), + [0xf6] = MMX_OP2(psadbw), + [0xf7] = { (SSEFunc_0_epp)gen_helper_maskmov_mmx, + (SSEFunc_0_epp)gen_helper_maskmov_xmm }, /* XXX: casts */ + [0xf8] = MMX_OP2(psubb), + [0xf9] = MMX_OP2(psubw), + [0xfa] = MMX_OP2(psubl), + [0xfb] = MMX_OP2(psubq), + [0xfc] = MMX_OP2(paddb), + [0xfd] = MMX_OP2(paddw), + [0xfe] = MMX_OP2(paddl), +}; + +static const SSEFunc_0_epp sse_op_table2[3 * 8][2] = { + [0 + 2] = MMX_OP2(psrlw), + [0 + 4] = MMX_OP2(psraw), + [0 + 6] = MMX_OP2(psllw), + [8 + 2] = MMX_OP2(psrld), + [8 + 4] = MMX_OP2(psrad), + [8 + 6] = MMX_OP2(pslld), + [16 + 2] = MMX_OP2(psrlq), + [16 + 3] = { NULL, gen_helper_psrldq_xmm }, + [16 + 6] = MMX_OP2(psllq), + [16 + 7] = { NULL, gen_helper_pslldq_xmm }, +}; + +static const SSEFunc_0_epi sse_op_table3ai[] = { + gen_helper_cvtsi2ss, + gen_helper_cvtsi2sd +}; + +#ifdef TARGET_X86_64 +static const SSEFunc_0_epl sse_op_table3aq[] = { + gen_helper_cvtsq2ss, + gen_helper_cvtsq2sd +}; +#endif + +static const SSEFunc_i_ep sse_op_table3bi[] = { + gen_helper_cvttss2si, + gen_helper_cvtss2si, + gen_helper_cvttsd2si, + gen_helper_cvtsd2si +}; + +#ifdef TARGET_X86_64 +static const SSEFunc_l_ep sse_op_table3bq[] = { + gen_helper_cvttss2sq, + gen_helper_cvtss2sq, + gen_helper_cvttsd2sq, + gen_helper_cvtsd2sq +}; +#endif + +static const SSEFunc_0_epp sse_op_table4[8][4] = { + SSE_FOP(cmpeq), + SSE_FOP(cmplt), + SSE_FOP(cmple), + SSE_FOP(cmpunord), + SSE_FOP(cmpneq), + SSE_FOP(cmpnlt), + SSE_FOP(cmpnle), + SSE_FOP(cmpord), +}; + +static const SSEFunc_0_epp sse_op_table5[256] = { + [0x0c] = gen_helper_pi2fw, + [0x0d] = gen_helper_pi2fd, + [0x1c] = gen_helper_pf2iw, + [0x1d] = gen_helper_pf2id, + [0x8a] = gen_helper_pfnacc, + [0x8e] = gen_helper_pfpnacc, + [0x90] = gen_helper_pfcmpge, + [0x94] = gen_helper_pfmin, + [0x96] = gen_helper_pfrcp, + [0x97] = gen_helper_pfrsqrt, + [0x9a] = gen_helper_pfsub, + [0x9e] = gen_helper_pfadd, + [0xa0] = gen_helper_pfcmpgt, + [0xa4] = gen_helper_pfmax, + [0xa6] = gen_helper_movq, /* pfrcpit1; no need to actually increase precision */ + [0xa7] = gen_helper_movq, /* pfrsqit1 */ + [0xaa] = gen_helper_pfsubr, + [0xae] = gen_helper_pfacc, + [0xb0] = gen_helper_pfcmpeq, + [0xb4] = gen_helper_pfmul, + [0xb6] = gen_helper_movq, /* pfrcpit2 */ + [0xb7] = gen_helper_pmulhrw_mmx, + [0xbb] = gen_helper_pswapd, + [0xbf] = gen_helper_pavgb_mmx /* pavgusb */ +}; + +struct SSEOpHelper_epp { + SSEFunc_0_epp op[2]; + uint32_t ext_mask; +}; + +struct SSEOpHelper_eppi { + SSEFunc_0_eppi op[2]; + uint32_t ext_mask; +}; + +#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 } +#define SSE41_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE41 } +#define SSE42_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE42 } +#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 } +#define PCLMULQDQ_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, \ + CPUID_EXT_PCLMULQDQ } +#define AESNI_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_AES } + +static const struct SSEOpHelper_epp sse_op_table6[256] = { + [0x00] = SSSE3_OP(pshufb), + [0x01] = SSSE3_OP(phaddw), + [0x02] = SSSE3_OP(phaddd), + [0x03] = SSSE3_OP(phaddsw), + [0x04] = SSSE3_OP(pmaddubsw), + [0x05] = SSSE3_OP(phsubw), + [0x06] = SSSE3_OP(phsubd), + [0x07] = SSSE3_OP(phsubsw), + [0x08] = SSSE3_OP(psignb), + [0x09] = SSSE3_OP(psignw), + [0x0a] = SSSE3_OP(psignd), + [0x0b] = SSSE3_OP(pmulhrsw), + [0x10] = SSE41_OP(pblendvb), + [0x14] = SSE41_OP(blendvps), + [0x15] = SSE41_OP(blendvpd), + [0x17] = SSE41_OP(ptest), + [0x1c] = SSSE3_OP(pabsb), + [0x1d] = SSSE3_OP(pabsw), + [0x1e] = SSSE3_OP(pabsd), + [0x20] = SSE41_OP(pmovsxbw), + [0x21] = SSE41_OP(pmovsxbd), + [0x22] = SSE41_OP(pmovsxbq), + [0x23] = SSE41_OP(pmovsxwd), + [0x24] = SSE41_OP(pmovsxwq), + [0x25] = SSE41_OP(pmovsxdq), + [0x28] = SSE41_OP(pmuldq), + [0x29] = SSE41_OP(pcmpeqq), + [0x2a] = SSE41_SPECIAL, /* movntqda */ + [0x2b] = SSE41_OP(packusdw), + [0x30] = SSE41_OP(pmovzxbw), + [0x31] = SSE41_OP(pmovzxbd), + [0x32] = SSE41_OP(pmovzxbq), + [0x33] = SSE41_OP(pmovzxwd), + [0x34] = SSE41_OP(pmovzxwq), + [0x35] = SSE41_OP(pmovzxdq), + [0x37] = SSE42_OP(pcmpgtq), + [0x38] = SSE41_OP(pminsb), + [0x39] = SSE41_OP(pminsd), + [0x3a] = SSE41_OP(pminuw), + [0x3b] = SSE41_OP(pminud), + [0x3c] = SSE41_OP(pmaxsb), + [0x3d] = SSE41_OP(pmaxsd), + [0x3e] = SSE41_OP(pmaxuw), + [0x3f] = SSE41_OP(pmaxud), + [0x40] = SSE41_OP(pmulld), + [0x41] = SSE41_OP(phminposuw), + [0xdb] = AESNI_OP(aesimc), + [0xdc] = AESNI_OP(aesenc), + [0xdd] = AESNI_OP(aesenclast), + [0xde] = AESNI_OP(aesdec), + [0xdf] = AESNI_OP(aesdeclast), +}; + +static const struct SSEOpHelper_eppi sse_op_table7[256] = { + [0x08] = SSE41_OP(roundps), + [0x09] = SSE41_OP(roundpd), + [0x0a] = SSE41_OP(roundss), + [0x0b] = SSE41_OP(roundsd), + [0x0c] = SSE41_OP(blendps), + [0x0d] = SSE41_OP(blendpd), + [0x0e] = SSE41_OP(pblendw), + [0x0f] = SSSE3_OP(palignr), + [0x14] = SSE41_SPECIAL, /* pextrb */ + [0x15] = SSE41_SPECIAL, /* pextrw */ + [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */ + [0x17] = SSE41_SPECIAL, /* extractps */ + [0x20] = SSE41_SPECIAL, /* pinsrb */ + [0x21] = SSE41_SPECIAL, /* insertps */ + [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */ + [0x40] = SSE41_OP(dpps), + [0x41] = SSE41_OP(dppd), + [0x42] = SSE41_OP(mpsadbw), + [0x44] = PCLMULQDQ_OP(pclmulqdq), + [0x60] = SSE42_OP(pcmpestrm), + [0x61] = SSE42_OP(pcmpestri), + [0x62] = SSE42_OP(pcmpistrm), + [0x63] = SSE42_OP(pcmpistri), + [0xdf] = AESNI_OP(aeskeygenassist), +}; + +static void gen_sse(CPUX86State *env, DisasContext *s, int b, + target_ulong pc_start, int rex_r) +{ + int b1, op1_offset, op2_offset, is_xmm, val; + int modrm, mod, rm, reg; + SSEFunc_0_epp sse_fn_epp; + SSEFunc_0_eppi sse_fn_eppi; + SSEFunc_0_ppi sse_fn_ppi; + SSEFunc_0_eppt sse_fn_eppt; + MemOp ot; + + b &= 0xff; + if (s->prefix & PREFIX_DATA) + b1 = 1; + else if (s->prefix & PREFIX_REPZ) + b1 = 2; + else if (s->prefix & PREFIX_REPNZ) + b1 = 3; + else + b1 = 0; + sse_fn_epp = sse_op_table1[b][b1]; + if (!sse_fn_epp) { + goto unknown_op; + } + if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) { + is_xmm = 1; + } else { + if (b1 == 0) { + /* MMX case */ + is_xmm = 0; + } else { + is_xmm = 1; + } + } + /* simple MMX/SSE operation */ + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + return; + } + if (s->flags & HF_EM_MASK) { + illegal_op: + gen_illegal_opcode(s); + return; + } + if (is_xmm + && !(s->flags & HF_OSFXSR_MASK) + && ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA))) { + goto unknown_op; + } + if (b == 0x0e) { + if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { + /* If we were fully decoding this we might use illegal_op. */ + goto unknown_op; + } + /* femms */ + gen_helper_emms(cpu_env); + return; + } + if (b == 0x77) { + /* emms */ + gen_helper_emms(cpu_env); + return; + } + /* prepare MMX state (XXX: optimize by storing fptt and fptags in + the static cpu state) */ + if (!is_xmm) { + gen_helper_enter_mmx(cpu_env); + } + + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7); + if (is_xmm) + reg |= rex_r; + mod = (modrm >> 6) & 3; + if (sse_fn_epp == SSE_SPECIAL) { + b |= (b1 << 8); + switch(b) { + case 0x0e7: /* movntq */ + if (mod == 3) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); + break; + case 0x1e7: /* movntdq */ + case 0x02b: /* movntps */ + case 0x12b: /* movntps */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(env, s, modrm); + gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + break; + case 0x3f0: /* lddqu */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(env, s, modrm); + gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + break; + case 0x22b: /* movntss */ + case 0x32b: /* movntsd */ + if (mod == 3) + goto illegal_op; + gen_lea_modrm(env, s, modrm); + if (b1 & 1) { + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(0))); + gen_op_st_v(s, MO_32, s->T0, s->A0); + } + break; + case 0x6e: /* movd mm, ea */ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); + tcg_gen_st_tl(s->T0, cpu_env, + offsetof(CPUX86State, fpregs[reg].mmx)); + } else +#endif + { + gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,fpregs[reg].mmx)); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_movl_mm_T0_mmx(s->ptr0, s->tmp2_i32); + } + break; + case 0x16e: /* movd xmm, ea */ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg])); + gen_helper_movq_mm_T0_xmm(s->ptr0, s->T0); + } else +#endif + { + gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg])); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_movl_mm_T0_xmm(s->ptr0, s->tmp2_i32); + } + break; + case 0x6f: /* movq mm, ea */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); + } else { + rm = (modrm & 7); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, + offsetof(CPUX86State,fpregs[rm].mmx)); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, + offsetof(CPUX86State,fpregs[reg].mmx)); + } + break; + case 0x010: /* movups */ + case 0x110: /* movupd */ + case 0x028: /* movaps */ + case 0x128: /* movapd */ + case 0x16f: /* movdqa xmm, ea */ + case 0x26f: /* movdqu xmm, ea */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movo(s, offsetof(CPUX86State, xmm_regs[reg]), + offsetof(CPUX86State,xmm_regs[rm])); + } + break; + case 0x210: /* movss xmm, ea */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1))); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); + } + break; + case 0x310: /* movsd xmm, ea */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); + } + break; + case 0x012: /* movlps */ + case 0x112: /* movlpd */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + /* movhlps */ + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); + } + break; + case 0x212: /* movsldup */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(2))); + } + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); + break; + case 0x312: /* movddup */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); + } + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); + break; + case 0x016: /* movhps */ + case 0x116: /* movhpd */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(1))); + } else { + /* movlhps */ + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); + } + break; + case 0x216: /* movshdup */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(1))); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_L(3))); + } + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1))); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); + break; + case 0x178: + case 0x378: + { + int bit_index, field_length; + + if (b1 == 1 && reg != 0) + goto illegal_op; + field_length = x86_ldub_code(env, s) & 0x3F; + bit_index = x86_ldub_code(env, s) & 0x3F; + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg])); + if (b1 == 1) + gen_helper_extrq_i(cpu_env, s->ptr0, + tcg_const_i32(bit_index), + tcg_const_i32(field_length)); + else + gen_helper_insertq_i(cpu_env, s->ptr0, + tcg_const_i32(bit_index), + tcg_const_i32(field_length)); + } + break; + case 0x7e: /* movd ea, mm */ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + tcg_gen_ld_i64(s->T0, cpu_env, + offsetof(CPUX86State,fpregs[reg].mmx)); + gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); + } else +#endif + { + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); + gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); + } + break; + case 0x17e: /* movd ea, xmm */ +#ifdef TARGET_X86_64 + if (s->dflag == MO_64) { + tcg_gen_ld_i64(s->T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); + gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); + } else +#endif + { + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); + } + break; + case 0x27e: /* movq xmm, ea */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); + } + gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); + break; + case 0x7f: /* movq ea, mm */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); + } else { + rm = (modrm & 7); + gen_op_movq(s, offsetof(CPUX86State, fpregs[rm].mmx), + offsetof(CPUX86State,fpregs[reg].mmx)); + } + break; + case 0x011: /* movups */ + case 0x111: /* movupd */ + case 0x029: /* movaps */ + case 0x129: /* movapd */ + case 0x17f: /* movdqa ea, xmm */ + case 0x27f: /* movdqu ea, xmm */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movo(s, offsetof(CPUX86State, xmm_regs[rm]), + offsetof(CPUX86State,xmm_regs[reg])); + } + break; + case 0x211: /* movss ea, xmm */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); + gen_op_st_v(s, MO_32, s->T0, s->A0); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_L(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); + } + break; + case 0x311: /* movsd ea, xmm */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); + } + break; + case 0x013: /* movlps */ + case 0x113: /* movlpd */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + goto illegal_op; + } + break; + case 0x017: /* movhps */ + case 0x117: /* movhpd */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(1))); + } else { + goto illegal_op; + } + break; + case 0x71: /* shift mm, im */ + case 0x72: + case 0x73: + case 0x171: /* shift xmm, im */ + case 0x172: + case 0x173: + if (b1 >= 2) { + goto unknown_op; + } + val = x86_ldub_code(env, s); + if (is_xmm) { + tcg_gen_movi_tl(s->T0, val); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(1))); + op1_offset = offsetof(CPUX86State,xmm_t0); + } else { + tcg_gen_movi_tl(s->T0, val); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_L(1))); + op1_offset = offsetof(CPUX86State,mmx_t0); + } + sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 + + (((modrm >> 3)) & 7)][b1]; + if (!sse_fn_epp) { + goto unknown_op; + } + if (is_xmm) { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op1_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); + break; + case 0x050: /* movmskps */ + rm = (modrm & 7) | REX_B(s); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,xmm_regs[rm])); + gen_helper_movmskps(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); + break; + case 0x150: /* movmskpd */ + rm = (modrm & 7) | REX_B(s); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State,xmm_regs[rm])); + gen_helper_movmskpd(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); + break; + case 0x02a: /* cvtpi2ps */ + case 0x12a: /* cvtpi2pd */ + gen_helper_enter_mmx(cpu_env); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_ldq_env_A0(s, op2_offset); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + switch(b >> 8) { + case 0x0: + gen_helper_cvtpi2ps(cpu_env, s->ptr0, s->ptr1); + break; + default: + case 0x1: + gen_helper_cvtpi2pd(cpu_env, s->ptr0, s->ptr1); + break; + } + break; + case 0x22a: /* cvtsi2ss */ + case 0x32a: /* cvtsi2sd */ + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + if (ot == MO_32) { + SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1]; + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + sse_fn_epi(cpu_env, s->ptr0, s->tmp2_i32); + } else { +#ifdef TARGET_X86_64 + SSEFunc_0_epl sse_fn_epl = sse_op_table3aq[(b >> 8) & 1]; + sse_fn_epl(cpu_env, s->ptr0, s->T0); +#else + goto illegal_op; +#endif + } + break; + case 0x02c: /* cvttps2pi */ + case 0x12c: /* cvttpd2pi */ + case 0x02d: /* cvtps2pi */ + case 0x12d: /* cvtpd2pi */ + gen_helper_enter_mmx(cpu_env); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_ldo_env_A0(s, op2_offset); + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + switch(b) { + case 0x02c: + gen_helper_cvttps2pi(cpu_env, s->ptr0, s->ptr1); + break; + case 0x12c: + gen_helper_cvttpd2pi(cpu_env, s->ptr0, s->ptr1); + break; + case 0x02d: + gen_helper_cvtps2pi(cpu_env, s->ptr0, s->ptr1); + break; + case 0x12d: + gen_helper_cvtpd2pi(cpu_env, s->ptr0, s->ptr1); + break; + } + break; + case 0x22c: /* cvttss2si */ + case 0x32c: /* cvttsd2si */ + case 0x22d: /* cvtss2si */ + case 0x32d: /* cvtsd2si */ + ot = mo_64_32(s->dflag); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + if ((b >> 8) & 1) { + gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_Q(0))); + } else { + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(0))); + } + op2_offset = offsetof(CPUX86State,xmm_t0); + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); + if (ot == MO_32) { + SSEFunc_i_ep sse_fn_i_ep = + sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; + sse_fn_i_ep(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + } else { +#ifdef TARGET_X86_64 + SSEFunc_l_ep sse_fn_l_ep = + sse_op_table3bq[((b >> 7) & 2) | (b & 1)]; + sse_fn_l_ep(s->T0, cpu_env, s->ptr0); +#else + goto illegal_op; +#endif + } + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + case 0xc4: /* pinsrw */ + case 0x1c4: + s->rip_offset = 1; + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + val = x86_ldub_code(env, s); + if (b1) { + val &= 7; + tcg_gen_st16_tl(s->T0, cpu_env, + offsetof(CPUX86State,xmm_regs[reg].ZMM_W(val))); + } else { + val &= 3; + tcg_gen_st16_tl(s->T0, cpu_env, + offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); + } + break; + case 0xc5: /* pextrw */ + case 0x1c5: + if (mod != 3) + goto illegal_op; + ot = mo_64_32(s->dflag); + val = x86_ldub_code(env, s); + if (b1) { + val &= 7; + rm = (modrm & 7) | REX_B(s); + tcg_gen_ld16u_tl(s->T0, cpu_env, + offsetof(CPUX86State,xmm_regs[rm].ZMM_W(val))); + } else { + val &= 3; + rm = (modrm & 7); + tcg_gen_ld16u_tl(s->T0, cpu_env, + offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); + } + reg = ((modrm >> 3) & 7) | rex_r; + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + case 0x1d6: /* movq ea, xmm */ + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_stq_env_A0(s, offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(0))); + } else { + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), + offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); + gen_op_movq_env_0(s, + offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(1))); + } + break; + case 0x2d6: /* movq2dq */ + gen_helper_enter_mmx(cpu_env); + rm = (modrm & 7); + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), + offsetof(CPUX86State,fpregs[rm].mmx)); + gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); + break; + case 0x3d6: /* movdq2q */ + gen_helper_enter_mmx(cpu_env); + rm = (modrm & 7) | REX_B(s); + gen_op_movq(s, offsetof(CPUX86State, fpregs[reg & 7].mmx), + offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); + break; + case 0xd7: /* pmovmskb */ + case 0x1d7: + if (mod != 3) + goto illegal_op; + if (b1) { + rm = (modrm & 7) | REX_B(s); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State, xmm_regs[rm])); + gen_helper_pmovmskb_xmm(s->tmp2_i32, cpu_env, s->ptr0); + } else { + rm = (modrm & 7); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State, fpregs[rm].mmx)); + gen_helper_pmovmskb_mmx(s->tmp2_i32, cpu_env, s->ptr0); + } + reg = ((modrm >> 3) & 7) | rex_r; + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); + break; + + case 0x138: + case 0x038: + b = modrm; + if ((b & 0xf0) == 0xf0) { + goto do_0f_38_fx; + } + modrm = x86_ldub_code(env, s); + rm = modrm & 7; + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (b1 >= 2) { + goto unknown_op; + } + + sse_fn_epp = sse_op_table6[b].op[b1]; + if (!sse_fn_epp) { + goto unknown_op; + } + if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) + goto illegal_op; + + if (b1) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); + } else { + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_lea_modrm(env, s, modrm); + switch (b) { + case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */ + case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ + case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ + gen_ldq_env_A0(s, op2_offset + + offsetof(ZMMReg, ZMM_Q(0))); + break; + case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ + case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, op2_offset + + offsetof(ZMMReg, ZMM_L(0))); + break; + case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ + tcg_gen_qemu_ld_tl(s->tmp0, s->A0, + s->mem_index, MO_LEUW); + tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset + + offsetof(ZMMReg, ZMM_W(0))); + break; + case 0x2a: /* movntqda */ + gen_ldo_env_A0(s, op1_offset); + return; + default: + gen_ldo_env_A0(s, op2_offset); + } + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } else { + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, op2_offset); + } + } + if (sse_fn_epp == SSE_SPECIAL) { + goto unknown_op; + } + + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); + + if (b == 0x17) { + set_cc_op(s, CC_OP_EFLAGS); + } + break; + + case 0x238: + case 0x338: + do_0f_38_fx: + /* Various integer extensions at 0f 38 f[0-f]. */ + b = modrm | (b1 << 8); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + switch (b) { + case 0x3f0: /* crc32 Gd,Eb */ + case 0x3f1: /* crc32 Gd,Ey */ + do_crc32: + if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) { + goto illegal_op; + } + if ((b & 0xff) == 0xf0) { + ot = MO_8; + } else if (s->dflag != MO_64) { + ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); + } else { + ot = MO_64; + } + + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[reg]); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_helper_crc32(s->T0, s->tmp2_i32, + s->T0, tcg_const_i32(8 << ot)); + + ot = mo_64_32(s->dflag); + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + + case 0x1f0: /* crc32 or movbe */ + case 0x1f1: + /* For these insns, the f3 prefix is supposed to have priority + over the 66 prefix, but that's not what we implement above + setting b1. */ + if (s->prefix & PREFIX_REPNZ) { + goto do_crc32; + } + /* FALLTHRU */ + case 0x0f0: /* movbe Gy,My */ + case 0x0f1: /* movbe My,Gy */ + if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) { + goto illegal_op; + } + if (s->dflag != MO_64) { + ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); + } else { + ot = MO_64; + } + + gen_lea_modrm(env, s, modrm); + if ((b & 1) == 0) { + tcg_gen_qemu_ld_tl(s->T0, s->A0, + s->mem_index, ot | MO_BE); + gen_op_mov_reg_v(s, ot, reg, s->T0); + } else { + tcg_gen_qemu_st_tl(cpu_regs[reg], s->A0, + s->mem_index, ot | MO_BE); + } + break; + + case 0x0f2: /* andn Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_andc_tl(s->T0, s->T0, cpu_regs[s->vex_v]); + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); + set_cc_op(s, CC_OP_LOGICB + ot); + break; + + case 0x0f7: /* bextr Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + { + TCGv bound, zero; + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Extract START, and shift the operand. + Shifts larger than operand size get zeros. */ + tcg_gen_ext8u_tl(s->A0, cpu_regs[s->vex_v]); + tcg_gen_shr_tl(s->T0, s->T0, s->A0); + + bound = tcg_const_tl(ot == MO_64 ? 63 : 31); + zero = tcg_const_tl(0); + tcg_gen_movcond_tl(TCG_COND_LEU, s->T0, s->A0, bound, + s->T0, zero); + tcg_temp_free(zero); + + /* Extract the LEN into a mask. Lengths larger than + operand size get all ones. */ + tcg_gen_extract_tl(s->A0, cpu_regs[s->vex_v], 8, 8); + tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->A0, bound, + s->A0, bound); + tcg_temp_free(bound); + tcg_gen_movi_tl(s->T1, 1); + tcg_gen_shl_tl(s->T1, s->T1, s->A0); + tcg_gen_subi_tl(s->T1, s->T1, 1); + tcg_gen_and_tl(s->T0, s->T0, s->T1); + + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); + set_cc_op(s, CC_OP_LOGICB + ot); + } + break; + + case 0x0f5: /* bzhi Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + tcg_gen_ext8u_tl(s->T1, cpu_regs[s->vex_v]); + { + TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31); + /* Note that since we're using BMILG (in order to get O + cleared) we need to store the inverse into C. */ + tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, + s->T1, bound); + tcg_gen_movcond_tl(TCG_COND_GT, s->T1, s->T1, + bound, bound, s->T1); + tcg_temp_free(bound); + } + tcg_gen_movi_tl(s->A0, -1); + tcg_gen_shl_tl(s->A0, s->A0, s->T1); + tcg_gen_andc_tl(s->T0, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + case 0x3f6: /* mulx By, Gy, rdx, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + switch (ot) { + default: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EDX]); + tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp3_i32); + break; +#ifdef TARGET_X86_64 + case MO_64: + tcg_gen_mulu2_i64(s->T0, s->T1, + s->T0, cpu_regs[R_EDX]); + tcg_gen_mov_i64(cpu_regs[s->vex_v], s->T0); + tcg_gen_mov_i64(cpu_regs[reg], s->T1); + break; +#endif + } + break; + + case 0x3f5: /* pdep Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Note that by zero-extending the source operand, we + automatically handle zero-extending the result. */ + if (ot == MO_64) { + tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); + } else { + tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); + } + gen_helper_pdep(cpu_regs[reg], s->T1, s->T0); + break; + + case 0x2f5: /* pext Gy, By, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + /* Note that by zero-extending the source operand, we + automatically handle zero-extending the result. */ + if (ot == MO_64) { + tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); + } else { + tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); + } + gen_helper_pext(cpu_regs[reg], s->T1, s->T0); + break; + + case 0x1f6: /* adcx Gy, Ey */ + case 0x2f6: /* adox Gy, Ey */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX)) { + goto illegal_op; + } else { + TCGv carry_in, carry_out, zero; + int end_op; + + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + /* Re-use the carry-out from a previous round. */ + carry_in = NULL; + carry_out = (b == 0x1f6 ? cpu_cc_dst : cpu_cc_src2); + switch (s->cc_op) { + case CC_OP_ADCX: + if (b == 0x1f6) { + carry_in = cpu_cc_dst; + end_op = CC_OP_ADCX; + } else { + end_op = CC_OP_ADCOX; + } + break; + case CC_OP_ADOX: + if (b == 0x1f6) { + end_op = CC_OP_ADCOX; + } else { + carry_in = cpu_cc_src2; + end_op = CC_OP_ADOX; + } + break; + case CC_OP_ADCOX: + end_op = CC_OP_ADCOX; + carry_in = carry_out; + break; + default: + end_op = (b == 0x1f6 ? CC_OP_ADCX : CC_OP_ADOX); + break; + } + /* If we can't reuse carry-out, get it out of EFLAGS. */ + if (!carry_in) { + if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { + gen_compute_eflags(s); + } + carry_in = s->tmp0; + tcg_gen_extract_tl(carry_in, cpu_cc_src, + ctz32(b == 0x1f6 ? CC_C : CC_O), 1); + } + + switch (ot) { +#ifdef TARGET_X86_64 + case MO_32: + /* If we know TL is 64-bit, and we want a 32-bit + result, just do everything in 64-bit arithmetic. */ + tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); + tcg_gen_ext32u_i64(s->T0, s->T0); + tcg_gen_add_i64(s->T0, s->T0, cpu_regs[reg]); + tcg_gen_add_i64(s->T0, s->T0, carry_in); + tcg_gen_ext32u_i64(cpu_regs[reg], s->T0); + tcg_gen_shri_i64(carry_out, s->T0, 32); + break; +#endif + default: + /* Otherwise compute the carry-out in two steps. */ + zero = tcg_const_tl(0); + tcg_gen_add2_tl(s->T0, carry_out, + s->T0, zero, + carry_in, zero); + tcg_gen_add2_tl(cpu_regs[reg], carry_out, + cpu_regs[reg], carry_out, + s->T0, zero); + tcg_temp_free(zero); + break; + } + set_cc_op(s, end_op); + } + break; + + case 0x1f7: /* shlx Gy, Ey, By */ + case 0x2f7: /* sarx Gy, Ey, By */ + case 0x3f7: /* shrx Gy, Ey, By */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + if (ot == MO_64) { + tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 63); + } else { + tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 31); + } + if (b == 0x1f7) { + tcg_gen_shl_tl(s->T0, s->T0, s->T1); + } else if (b == 0x2f7) { + if (ot != MO_64) { + tcg_gen_ext32s_tl(s->T0, s->T0); + } + tcg_gen_sar_tl(s->T0, s->T0, s->T1); + } else { + if (ot != MO_64) { + tcg_gen_ext32u_tl(s->T0, s->T0); + } + tcg_gen_shr_tl(s->T0, s->T0, s->T1); + } + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + + case 0x0f3: + case 0x1f3: + case 0x2f3: + case 0x3f3: /* Group 17 */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + + tcg_gen_mov_tl(cpu_cc_src, s->T0); + switch (reg & 7) { + case 1: /* blsr By,Ey */ + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_and_tl(s->T0, s->T0, s->T1); + break; + case 2: /* blsmsk By,Ey */ + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_xor_tl(s->T0, s->T0, s->T1); + break; + case 3: /* blsi By, Ey */ + tcg_gen_neg_tl(s->T1, s->T0); + tcg_gen_and_tl(s->T0, s->T0, s->T1); + break; + default: + goto unknown_op; + } + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + gen_op_mov_reg_v(s, ot, s->vex_v, s->T0); + set_cc_op(s, CC_OP_BMILGB + ot); + break; + + default: + goto unknown_op; + } + break; + + case 0x03a: + case 0x13a: + b = modrm; + modrm = x86_ldub_code(env, s); + rm = modrm & 7; + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (b1 >= 2) { + goto unknown_op; + } + + sse_fn_eppi = sse_op_table7[b].op[b1]; + if (!sse_fn_eppi) { + goto unknown_op; + } + if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) + goto illegal_op; + + s->rip_offset = 1; + + if (sse_fn_eppi == SSE_SPECIAL) { + ot = mo_64_32(s->dflag); + rm = (modrm & 7) | REX_B(s); + if (mod != 3) + gen_lea_modrm(env, s, modrm); + reg = ((modrm >> 3) & 7) | rex_r; + val = x86_ldub_code(env, s); + switch (b) { + case 0x14: /* pextrb */ + tcg_gen_ld8u_tl(s->T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_B(val & 15))); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + tcg_gen_qemu_st_tl(s->T0, s->A0, + s->mem_index, MO_UB); + } + break; + case 0x15: /* pextrw */ + tcg_gen_ld16u_tl(s->T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_W(val & 7))); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + tcg_gen_qemu_st_tl(s->T0, s->A0, + s->mem_index, MO_LEUW); + } + break; + case 0x16: + if (ot == MO_32) { /* pextrd */ + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(val & 3))); + if (mod == 3) { + tcg_gen_extu_i32_tl(cpu_regs[rm], s->tmp2_i32); + } else { + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + } + } else { /* pextrq */ +#ifdef TARGET_X86_64 + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(val & 1))); + if (mod == 3) { + tcg_gen_mov_i64(cpu_regs[rm], s->tmp1_i64); + } else { + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + } +#else + goto illegal_op; +#endif + } + break; + case 0x17: /* extractps */ + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(val & 3))); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + tcg_gen_qemu_st_tl(s->T0, s->A0, + s->mem_index, MO_LEUL); + } + break; + case 0x20: /* pinsrb */ + if (mod == 3) { + gen_op_mov_v_reg(s, MO_32, s->T0, rm); + } else { + tcg_gen_qemu_ld_tl(s->T0, s->A0, + s->mem_index, MO_UB); + } + tcg_gen_st8_tl(s->T0, cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_B(val & 15))); + break; + case 0x21: /* insertps */ + if (mod == 3) { + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, + offsetof(CPUX86State,xmm_regs[rm] + .ZMM_L((val >> 6) & 3))); + } else { + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + } + tcg_gen_st_i32(s->tmp2_i32, cpu_env, + offsetof(CPUX86State,xmm_regs[reg] + .ZMM_L((val >> 4) & 3))); + if ((val >> 0) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(0))); + if ((val >> 1) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(1))); + if ((val >> 2) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(2))); + if ((val >> 3) & 1) + tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), + cpu_env, offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(3))); + break; + case 0x22: + if (ot == MO_32) { /* pinsrd */ + if (mod == 3) { + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[rm]); + } else { + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + } + tcg_gen_st_i32(s->tmp2_i32, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].ZMM_L(val & 3))); + } else { /* pinsrq */ +#ifdef TARGET_X86_64 + if (mod == 3) { + gen_op_mov_v_reg(s, ot, s->tmp1_i64, rm); + } else { + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + } + tcg_gen_st_i64(s->tmp1_i64, cpu_env, + offsetof(CPUX86State, + xmm_regs[reg].ZMM_Q(val & 1))); +#else + goto illegal_op; +#endif + } + break; + } + return; + } + + if (b1) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); + } else { + op2_offset = offsetof(CPUX86State,xmm_t0); + gen_lea_modrm(env, s, modrm); + gen_ldo_env_A0(s, op2_offset); + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod == 3) { + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } else { + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_lea_modrm(env, s, modrm); + gen_ldq_env_A0(s, op2_offset); + } + } + val = x86_ldub_code(env, s); + + if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ + set_cc_op(s, CC_OP_EFLAGS); + + if (s->dflag == MO_64) { + /* The helper must use entire 64-bit gp registers */ + val |= 1 << 8; + } + } + + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_eppi(cpu_env, s->ptr0, s->ptr1, tcg_const_i32(val)); + break; + + case 0x33a: + /* Various integer extensions at 0f 3a f[0-f]. */ + b = modrm | (b1 << 8); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + switch (b) { + case 0x3f0: /* rorx Gy,Ey, Ib */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) + || !(s->prefix & PREFIX_VEX) + || s->vex_l != 0) { + goto illegal_op; + } + ot = mo_64_32(s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + b = x86_ldub_code(env, s); + if (ot == MO_64) { + tcg_gen_rotri_tl(s->T0, s->T0, b & 63); + } else { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b & 31); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + } + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + + default: + goto unknown_op; + } + break; + + default: + unknown_op: + gen_unknown_opcode(env, s); + return; + } + } else { + /* generic MMX or SSE operation */ + switch(b) { + case 0x70: /* pshufx insn */ + case 0xc6: /* pshufx insn */ + case 0xc2: /* compare insns */ + s->rip_offset = 1; + break; + default: + break; + } + if (is_xmm) { + op1_offset = offsetof(CPUX86State,xmm_regs[reg]); + if (mod != 3) { + int sz = 4; + + gen_lea_modrm(env, s, modrm); + op2_offset = offsetof(CPUX86State,xmm_t0); + + switch (b) { + case 0x50 ... 0x5a: + case 0x5c ... 0x5f: + case 0xc2: + /* Most sse scalar operations. */ + if (b1 == 2) { + sz = 2; + } else if (b1 == 3) { + sz = 3; + } + break; + + case 0x2e: /* ucomis[sd] */ + case 0x2f: /* comis[sd] */ + if (b1 == 0) { + sz = 2; + } else { + sz = 3; + } + break; + } + + switch (sz) { + case 2: + /* 32 bit access */ + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State,xmm_t0.ZMM_L(0))); + break; + case 3: + /* 64 bit access */ + gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_D(0))); + break; + default: + /* 128 bit access */ + gen_ldo_env_A0(s, op2_offset); + break; + } + } else { + rm = (modrm & 7) | REX_B(s); + op2_offset = offsetof(CPUX86State,xmm_regs[rm]); + } + } else { + op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + op2_offset = offsetof(CPUX86State,mmx_t0); + gen_ldq_env_A0(s, op2_offset); + } else { + rm = (modrm & 7); + op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); + } + } + switch(b) { + case 0x0f: /* 3DNow! data insns */ + val = x86_ldub_code(env, s); + sse_fn_epp = sse_op_table5[val]; + if (!sse_fn_epp) { + goto unknown_op; + } + if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { + goto illegal_op; + } + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); + break; + case 0x70: /* pshufx insn */ + case 0xc6: /* pshufx insn */ + val = x86_ldub_code(env, s); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + /* XXX: introduce a new table? */ + sse_fn_ppi = (SSEFunc_0_ppi)sse_fn_epp; + sse_fn_ppi(s->ptr0, s->ptr1, tcg_const_i32(val)); + break; + case 0xc2: + /* compare insns */ + val = x86_ldub_code(env, s); + if (val >= 8) + goto unknown_op; + sse_fn_epp = sse_op_table4[val][b1]; + + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); + break; + case 0xf7: + /* maskmov : we must prepare A0 */ + if (mod != 3) + goto illegal_op; + tcg_gen_mov_tl(s->A0, cpu_regs[R_EDI]); + gen_extu(s->aflag, s->A0); + gen_add_A0_ds_seg(s); + + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + /* XXX: introduce a new table? */ + sse_fn_eppt = (SSEFunc_0_eppt)sse_fn_epp; + sse_fn_eppt(cpu_env, s->ptr0, s->ptr1, s->A0); + break; + default: + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); + break; + } + if (b == 0x2e || b == 0x2f) { + set_cc_op(s, CC_OP_EFLAGS); + } + } +} + +/* convert one instruction. s->base.is_jmp is set if the translation must + be stopped. Return the next pc value */ +static target_ulong disas_insn(DisasContext *s, CPUState *cpu) +{ + CPUX86State *env = cpu->env_ptr; + int b, prefixes; + int shift; + MemOp ot, aflag, dflag; + int modrm, reg, rm, mod, op, opreg, val; + target_ulong next_eip, tval; + int rex_w, rex_r; + target_ulong pc_start = s->base.pc_next; + + s->pc_start = s->pc = pc_start; + s->override = -1; +#ifdef TARGET_X86_64 + s->rex_x = 0; + s->rex_b = 0; + s->x86_64_hregs = false; +#endif + s->rip_offset = 0; /* for relative ip address */ + s->vex_l = 0; + s->vex_v = 0; + if (sigsetjmp(s->jmpbuf, 0) != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + return s->pc; + } + + prefixes = 0; + rex_w = -1; + rex_r = 0; + + next_byte: + b = x86_ldub_code(env, s); + /* Collect prefixes. */ + switch (b) { + case 0xf3: + prefixes |= PREFIX_REPZ; + goto next_byte; + case 0xf2: + prefixes |= PREFIX_REPNZ; + goto next_byte; + case 0xf0: + prefixes |= PREFIX_LOCK; + goto next_byte; + case 0x2e: + s->override = R_CS; + goto next_byte; + case 0x36: + s->override = R_SS; + goto next_byte; + case 0x3e: + s->override = R_DS; + goto next_byte; + case 0x26: + s->override = R_ES; + goto next_byte; + case 0x64: + s->override = R_FS; + goto next_byte; + case 0x65: + s->override = R_GS; + goto next_byte; + case 0x66: + prefixes |= PREFIX_DATA; + goto next_byte; + case 0x67: + prefixes |= PREFIX_ADR; + goto next_byte; +#ifdef TARGET_X86_64 + case 0x40 ... 0x4f: + if (CODE64(s)) { + /* REX prefix */ + rex_w = (b >> 3) & 1; + rex_r = (b & 0x4) << 1; + s->rex_x = (b & 0x2) << 2; + REX_B(s) = (b & 0x1) << 3; + /* select uniform byte register addressing */ + s->x86_64_hregs = true; + goto next_byte; + } + break; +#endif + case 0xc5: /* 2-byte VEX */ + case 0xc4: /* 3-byte VEX */ + /* VEX prefixes cannot be used except in 32-bit mode. + Otherwise the instruction is LES or LDS. */ + if (s->code32 && !s->vm86) { + static const int pp_prefix[4] = { + 0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ + }; + int vex3, vex2 = x86_ldub_code(env, s); + + if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { + /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, + otherwise the instruction is LES or LDS. */ + s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ + break; + } + + /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ + | PREFIX_LOCK | PREFIX_DATA)) { + goto illegal_op; + } +#ifdef TARGET_X86_64 + if (s->x86_64_hregs) { + goto illegal_op; + } +#endif + rex_r = (~vex2 >> 4) & 8; + if (b == 0xc5) { + /* 2-byte VEX prefix: RVVVVlpp, implied 0f leading opcode byte */ + vex3 = vex2; + b = x86_ldub_code(env, s) | 0x100; + } else { + /* 3-byte VEX prefix: RXBmmmmm wVVVVlpp */ +#ifdef TARGET_X86_64 + s->rex_x = (~vex2 >> 3) & 8; + s->rex_b = (~vex2 >> 2) & 8; +#endif + vex3 = x86_ldub_code(env, s); + rex_w = (vex3 >> 7) & 1; + switch (vex2 & 0x1f) { + case 0x01: /* Implied 0f leading opcode bytes. */ + b = x86_ldub_code(env, s) | 0x100; + break; + case 0x02: /* Implied 0f 38 leading opcode bytes. */ + b = 0x138; + break; + case 0x03: /* Implied 0f 3a leading opcode bytes. */ + b = 0x13a; + break; + default: /* Reserved for future use. */ + goto unknown_op; + } + } + s->vex_v = (~vex3 >> 3) & 0xf; + s->vex_l = (vex3 >> 2) & 1; + prefixes |= pp_prefix[vex3 & 3] | PREFIX_VEX; + } + break; + } + + /* Post-process prefixes. */ + if (CODE64(s)) { + /* In 64-bit mode, the default data size is 32-bit. Select 64-bit + data with rex_w, and 16-bit data with 0x66; rex_w takes precedence + over 0x66 if both are present. */ + dflag = (rex_w > 0 ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32); + /* In 64-bit mode, 0x67 selects 32-bit addressing. */ + aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64); + } else { + /* In 16/32-bit mode, 0x66 selects the opposite data size. */ + if (s->code32 ^ ((prefixes & PREFIX_DATA) != 0)) { + dflag = MO_32; + } else { + dflag = MO_16; + } + /* In 16/32-bit mode, 0x67 selects the opposite addressing. */ + if (s->code32 ^ ((prefixes & PREFIX_ADR) != 0)) { + aflag = MO_32; + } else { + aflag = MO_16; + } + } + + s->prefix = prefixes; + s->aflag = aflag; + s->dflag = dflag; + + /* now check op code */ + reswitch: + switch(b) { + case 0x0f: + /**************************/ + /* extended op code */ + b = x86_ldub_code(env, s) | 0x100; + goto reswitch; + + /**************************/ + /* arith & logic */ + case 0x00 ... 0x05: + case 0x08 ... 0x0d: + case 0x10 ... 0x15: + case 0x18 ... 0x1d: + case 0x20 ... 0x25: + case 0x28 ... 0x2d: + case 0x30 ... 0x35: + case 0x38 ... 0x3d: + { + int op, f, val; + op = (b >> 3) & 7; + f = (b >> 1) & 3; + + ot = mo_b_d(b, dflag); + + switch(f) { + case 0: /* OP Ev, Gv */ + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + opreg = OR_TMP0; + } else if (op == OP_XORL && rm == reg) { + xor_zero: + /* xor reg, reg optimisation */ + set_cc_op(s, CC_OP_CLR); + tcg_gen_movi_tl(s->T0, 0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + } else { + opreg = rm; + } + gen_op_mov_v_reg(s, ot, s->T1, reg); + gen_op(s, op, ot, opreg); + break; + case 1: /* OP Gv, Ev */ + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, ot, s->T1, s->A0); + } else if (op == OP_XORL && rm == reg) { + goto xor_zero; + } else { + gen_op_mov_v_reg(s, ot, s->T1, rm); + } + gen_op(s, op, ot, reg); + break; + case 2: /* OP A, Iv */ + val = insn_get(env, s, ot); + tcg_gen_movi_tl(s->T1, val); + gen_op(s, op, ot, OR_EAX); + break; + } + } + break; + + case 0x82: + if (CODE64(s)) + goto illegal_op; + /* fall through */ + case 0x80: /* GRP1 */ + case 0x81: + case 0x83: + { + int val; + + ot = mo_b_d(b, dflag); + + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + + if (mod != 3) { + if (b == 0x83) + s->rip_offset = 1; + else + s->rip_offset = insn_const_size(ot); + gen_lea_modrm(env, s, modrm); + opreg = OR_TMP0; + } else { + opreg = rm; + } + + switch(b) { + default: + case 0x80: + case 0x81: + case 0x82: + val = insn_get(env, s, ot); + break; + case 0x83: + val = (int8_t)insn_get(env, s, MO_8); + break; + } + tcg_gen_movi_tl(s->T1, val); + gen_op(s, op, ot, opreg); + } + break; + + /**************************/ + /* inc, dec, and other misc arith */ + case 0x40 ... 0x47: /* inc Gv */ + ot = dflag; + gen_inc(s, ot, OR_EAX + (b & 7), 1); + break; + case 0x48 ... 0x4f: /* dec Gv */ + ot = dflag; + gen_inc(s, ot, OR_EAX + (b & 7), -1); + break; + case 0xf6: /* GRP3 */ + case 0xf7: + ot = mo_b_d(b, dflag); + + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + if (mod != 3) { + if (op == 0) { + s->rip_offset = insn_const_size(ot); + } + gen_lea_modrm(env, s, modrm); + /* For those below that handle locked memory, don't load here. */ + if (!(s->prefix & PREFIX_LOCK) + || op != 2) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + } + + switch(op) { + case 0: /* test */ + val = insn_get(env, s, ot); + tcg_gen_movi_tl(s->T1, val); + gen_op_testl_T0_T1_cc(s); + set_cc_op(s, CC_OP_LOGICB + ot); + break; + case 2: /* not */ + if (s->prefix & PREFIX_LOCK) { + if (mod == 3) { + goto illegal_op; + } + tcg_gen_movi_tl(s->T0, ~0); + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, + s->mem_index, ot | MO_LE); + } else { + tcg_gen_not_tl(s->T0, s->T0); + if (mod != 3) { + gen_op_st_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } + } + break; + case 3: /* neg */ + if (s->prefix & PREFIX_LOCK) { + TCGLabel *label1; + TCGv a0, t0, t1, t2; + + if (mod == 3) { + goto illegal_op; + } + a0 = tcg_temp_local_new(); + t0 = tcg_temp_local_new(); + label1 = gen_new_label(); + + tcg_gen_mov_tl(a0, s->A0); + tcg_gen_mov_tl(t0, s->T0); + + gen_set_label(label1); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + tcg_gen_mov_tl(t2, t0); + tcg_gen_neg_tl(t1, t0); + tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1, + s->mem_index, ot | MO_LE); + tcg_temp_free(t1); + tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); + + tcg_temp_free(t2); + tcg_temp_free(a0); + tcg_gen_mov_tl(s->T0, t0); + tcg_temp_free(t0); + } else { + tcg_gen_neg_tl(s->T0, s->T0); + if (mod != 3) { + gen_op_st_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } + } + gen_op_update_neg_cc(s); + set_cc_op(s, CC_OP_SUBB + ot); + break; + case 4: /* mul */ + switch(ot) { + case MO_8: + gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); + tcg_gen_ext8u_tl(s->T0, s->T0); + tcg_gen_ext8u_tl(s->T1, s->T1); + /* XXX: use 32 bit mul which could be faster */ + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00); + set_cc_op(s, CC_OP_MULB); + break; + case MO_16: + gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); + tcg_gen_ext16u_tl(s->T0, s->T0); + tcg_gen_ext16u_tl(s->T1, s->T1); + /* XXX: use 32 bit mul which could be faster */ + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_shri_tl(s->T0, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); + set_cc_op(s, CC_OP_MULW); + break; + default: + case MO_32: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULL); + break; +#ifdef TARGET_X86_64 + case MO_64: + tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], + s->T0, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULQ); + break; +#endif + } + break; + case 5: /* imul */ + switch(ot) { + case MO_8: + gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); + tcg_gen_ext8s_tl(s->T0, s->T0); + tcg_gen_ext8s_tl(s->T1, s->T1); + /* XXX: use 32 bit mul which could be faster */ + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext8s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); + set_cc_op(s, CC_OP_MULB); + break; + case MO_16: + gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_ext16s_tl(s->T1, s->T1); + /* XXX: use 32 bit mul which could be faster */ + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext16s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); + tcg_gen_shri_tl(s->T0, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); + set_cc_op(s, CC_OP_MULW); + break; + default: + case MO_32: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); + tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); + set_cc_op(s, CC_OP_MULL); + break; +#ifdef TARGET_X86_64 + case MO_64: + tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], + s->T0, cpu_regs[R_EAX]); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); + tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); + set_cc_op(s, CC_OP_MULQ); + break; +#endif + } + break; + case 6: /* div */ + switch(ot) { + case MO_8: + gen_helper_divb_AL(cpu_env, s->T0); + break; + case MO_16: + gen_helper_divw_AX(cpu_env, s->T0); + break; + default: + case MO_32: + gen_helper_divl_EAX(cpu_env, s->T0); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_divq_EAX(cpu_env, s->T0); + break; +#endif + } + break; + case 7: /* idiv */ + switch(ot) { + case MO_8: + gen_helper_idivb_AL(cpu_env, s->T0); + break; + case MO_16: + gen_helper_idivw_AX(cpu_env, s->T0); + break; + default: + case MO_32: + gen_helper_idivl_EAX(cpu_env, s->T0); + break; +#ifdef TARGET_X86_64 + case MO_64: + gen_helper_idivq_EAX(cpu_env, s->T0); + break; +#endif + } + break; + default: + goto unknown_op; + } + break; + + case 0xfe: /* GRP4 */ + case 0xff: /* GRP5 */ + ot = mo_b_d(b, dflag); + + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + op = (modrm >> 3) & 7; + if (op >= 2 && b == 0xfe) { + goto unknown_op; + } + if (CODE64(s)) { + if (op == 2 || op == 4) { + /* operand size for jumps is 64 bit */ + ot = MO_64; + } else if (op == 3 || op == 5) { + ot = dflag != MO_16 ? MO_32 + (rex_w == 1) : MO_16; + } else if (op == 6) { + /* default push size is 64 bit */ + ot = mo_pushpop(s, dflag); + } + } + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + if (op >= 2 && op != 3 && op != 5) + gen_op_ld_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + } + + switch(op) { + case 0: /* inc Ev */ + if (mod != 3) + opreg = OR_TMP0; + else + opreg = rm; + gen_inc(s, ot, opreg, 1); + break; + case 1: /* dec Ev */ + if (mod != 3) + opreg = OR_TMP0; + else + opreg = rm; + gen_inc(s, ot, opreg, -1); + break; + case 2: /* call Ev */ + /* XXX: optimize if memory (no 'and' is necessary) */ + if (dflag == MO_16) { + tcg_gen_ext16u_tl(s->T0, s->T0); + } + next_eip = s->pc - s->cs_base; + tcg_gen_movi_tl(s->T1, next_eip); + gen_push_v(s, s->T1); + gen_op_jmp_v(s->T0); + gen_bnd_jmp(s); + gen_jr(s, s->T0); + break; + case 3: /* lcall Ev */ + gen_op_ld_v(s, ot, s->T1, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T0, s->A0); + do_lcall: + if (s->pe && !s->vm86) { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lcall_protected(cpu_env, s->tmp2_i32, s->T1, + tcg_const_i32(dflag - 1), + tcg_const_tl(s->pc - s->cs_base)); + } else { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lcall_real(cpu_env, s->tmp2_i32, s->T1, + tcg_const_i32(dflag - 1), + tcg_const_i32(s->pc - s->cs_base)); + } + tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); + gen_jr(s, s->tmp4); + break; + case 4: /* jmp Ev */ + if (dflag == MO_16) { + tcg_gen_ext16u_tl(s->T0, s->T0); + } + gen_op_jmp_v(s->T0); + gen_bnd_jmp(s); + gen_jr(s, s->T0); + break; + case 5: /* ljmp Ev */ + gen_op_ld_v(s, ot, s->T1, s->A0); + gen_add_A0_im(s, 1 << ot); + gen_op_ld_v(s, MO_16, s->T0, s->A0); + do_ljmp: + if (s->pe && !s->vm86) { + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_ljmp_protected(cpu_env, s->tmp2_i32, s->T1, + tcg_const_tl(s->pc - s->cs_base)); + } else { + gen_op_movl_seg_T0_vm(s, R_CS); + gen_op_jmp_v(s->T1); + } + tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); + gen_jr(s, s->tmp4); + break; + case 6: /* push Ev */ + gen_push_v(s, s->T0); + break; + default: + goto unknown_op; + } + break; + + case 0x84: /* test Ev, Gv */ + case 0x85: + ot = mo_b_d(b, dflag); + + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_op_mov_v_reg(s, ot, s->T1, reg); + gen_op_testl_T0_T1_cc(s); + set_cc_op(s, CC_OP_LOGICB + ot); + break; + + case 0xa8: /* test eAX, Iv */ + case 0xa9: + ot = mo_b_d(b, dflag); + val = insn_get(env, s, ot); + + gen_op_mov_v_reg(s, ot, s->T0, OR_EAX); + tcg_gen_movi_tl(s->T1, val); + gen_op_testl_T0_T1_cc(s); + set_cc_op(s, CC_OP_LOGICB + ot); + break; + + case 0x98: /* CWDE/CBW */ + switch (dflag) { +#ifdef TARGET_X86_64 + case MO_64: + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); + tcg_gen_ext32s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0); + break; +#endif + case MO_32: + gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0); + break; + case MO_16: + gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX); + tcg_gen_ext8s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + break; + default: + tcg_abort(); + } + break; + case 0x99: /* CDQ/CWD */ + switch (dflag) { +#ifdef TARGET_X86_64 + case MO_64: + gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX); + tcg_gen_sari_tl(s->T0, s->T0, 63); + gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0); + break; +#endif + case MO_32: + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); + tcg_gen_ext32s_tl(s->T0, s->T0); + tcg_gen_sari_tl(s->T0, s->T0, 31); + gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0); + break; + case MO_16: + gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_sari_tl(s->T0, s->T0, 15); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); + break; + default: + tcg_abort(); + } + break; + case 0x1af: /* imul Gv, Ev */ + case 0x69: /* imul Gv, Ev, I */ + case 0x6b: + ot = dflag; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + if (b == 0x69) + s->rip_offset = insn_const_size(ot); + else if (b == 0x6b) + s->rip_offset = 1; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + if (b == 0x69) { + val = insn_get(env, s, ot); + tcg_gen_movi_tl(s->T1, val); + } else if (b == 0x6b) { + val = (int8_t)insn_get(env, s, MO_8); + tcg_gen_movi_tl(s->T1, val); + } else { + gen_op_mov_v_reg(s, ot, s->T1, reg); + } + switch (ot) { +#ifdef TARGET_X86_64 + case MO_64: + tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); + tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, s->T1); + break; +#endif + case MO_32: + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); + tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); + tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); + tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); + break; + default: + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_ext16s_tl(s->T1, s->T1); + /* XXX: use 32 bit mul which could be faster */ + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext16s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + } + set_cc_op(s, CC_OP_MULB + ot); + break; + case 0x1c0: + case 0x1c1: /* xadd Ev, Gv */ + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + gen_op_mov_v_reg(s, ot, s->T0, reg); + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_v_reg(s, ot, s->T1, rm); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, ot, reg, s->T1); + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + gen_lea_modrm(env, s, modrm); + if (s->prefix & PREFIX_LOCK) { + tcg_gen_atomic_fetch_add_tl(s->T1, s->A0, s->T0, + s->mem_index, ot | MO_LE); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + } else { + gen_op_ld_v(s, ot, s->T1, s->A0); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + gen_op_st_v(s, ot, s->T0, s->A0); + } + gen_op_mov_reg_v(s, ot, reg, s->T1); + } + gen_op_update2_cc(s); + set_cc_op(s, CC_OP_ADDB + ot); + break; + case 0x1b0: + case 0x1b1: /* cmpxchg Ev, Gv */ + { + TCGv oldv, newv, cmpv; + + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + oldv = tcg_temp_new(); + newv = tcg_temp_new(); + cmpv = tcg_temp_new(); + gen_op_mov_v_reg(s, ot, newv, reg); + tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); + + if (s->prefix & PREFIX_LOCK) { + if (mod == 3) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, + s->mem_index, ot | MO_LE); + gen_op_mov_reg_v(s, ot, R_EAX, oldv); + } else { + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + gen_op_mov_v_reg(s, ot, oldv, rm); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, ot, oldv, s->A0); + rm = 0; /* avoid warning */ + } + gen_extu(ot, oldv); + gen_extu(ot, cmpv); + /* store value = (old == cmp ? new : old); */ + tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); + if (mod == 3) { + gen_op_mov_reg_v(s, ot, R_EAX, oldv); + gen_op_mov_reg_v(s, ot, rm, newv); + } else { + /* Perform an unconditional store cycle like physical cpu; + must be before changing accumulator to ensure + idempotency if the store faults and the instruction + is restarted */ + gen_op_st_v(s, ot, newv, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, oldv); + } + } + tcg_gen_mov_tl(cpu_cc_src, oldv); + tcg_gen_mov_tl(s->cc_srcT, cmpv); + tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); + set_cc_op(s, CC_OP_SUBB + ot); + tcg_temp_free(oldv); + tcg_temp_free(newv); + tcg_temp_free(cmpv); + } + break; + case 0x1c7: /* cmpxchg8b */ + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + switch ((modrm >> 3) & 7) { + case 1: /* CMPXCHG8, CMPXCHG16 */ + if (mod == 3) { + goto illegal_op; + } +#ifdef TARGET_X86_64 + if (dflag == MO_64) { + if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + if ((s->prefix & PREFIX_LOCK) && + (tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_helper_cmpxchg16b(cpu_env, s->A0); + } else { + gen_helper_cmpxchg16b_unlocked(cpu_env, s->A0); + } + set_cc_op(s, CC_OP_EFLAGS); + break; + } +#endif + if (!(s->cpuid_features & CPUID_CX8)) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + if ((s->prefix & PREFIX_LOCK) && + (tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_helper_cmpxchg8b(cpu_env, s->A0); + } else { + gen_helper_cmpxchg8b_unlocked(cpu_env, s->A0); + } + set_cc_op(s, CC_OP_EFLAGS); + break; + + case 7: /* RDSEED */ + case 6: /* RDRAND */ + if (mod != 3 || + (s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) || + !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { + goto illegal_op; + } + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_rdrand(s->T0, cpu_env); + rm = (modrm & 7) | REX_B(s); + gen_op_mov_reg_v(s, dflag, rm, s->T0); + set_cc_op(s, CC_OP_EFLAGS); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + + default: + goto illegal_op; + } + break; + + /**************************/ + /* push/pop */ + case 0x50 ... 0x57: /* push */ + gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s)); + gen_push_v(s, s->T0); + break; + case 0x58 ... 0x5f: /* pop */ + ot = gen_pop_T0(s); + /* NOTE: order is important for pop %sp */ + gen_pop_update(s, ot); + gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0); + break; + case 0x60: /* pusha */ + if (CODE64(s)) + goto illegal_op; + gen_pusha(s); + break; + case 0x61: /* popa */ + if (CODE64(s)) + goto illegal_op; + gen_popa(s); + break; + case 0x68: /* push Iv */ + case 0x6a: + ot = mo_pushpop(s, dflag); + if (b == 0x68) + val = insn_get(env, s, ot); + else + val = (int8_t)insn_get(env, s, MO_8); + tcg_gen_movi_tl(s->T0, val); + gen_push_v(s, s->T0); + break; + case 0x8f: /* pop Ev */ + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + ot = gen_pop_T0(s); + if (mod == 3) { + /* NOTE: order is important for pop %sp */ + gen_pop_update(s, ot); + rm = (modrm & 7) | REX_B(s); + gen_op_mov_reg_v(s, ot, rm, s->T0); + } else { + /* NOTE: order is important too for MMU exceptions */ + s->popl_esp_hack = 1 << ot; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + s->popl_esp_hack = 0; + gen_pop_update(s, ot); + } + break; + case 0xc8: /* enter */ + { + int level; + val = x86_lduw_code(env, s); + level = x86_ldub_code(env, s); + gen_enter(s, val, level); + } + break; + case 0xc9: /* leave */ + gen_leave(s); + break; + case 0x06: /* push es */ + case 0x0e: /* push cs */ + case 0x16: /* push ss */ + case 0x1e: /* push ds */ + if (CODE64(s)) + goto illegal_op; + gen_op_movl_T0_seg(s, b >> 3); + gen_push_v(s, s->T0); + break; + case 0x1a0: /* push fs */ + case 0x1a8: /* push gs */ + gen_op_movl_T0_seg(s, (b >> 3) & 7); + gen_push_v(s, s->T0); + break; + case 0x07: /* pop es */ + case 0x17: /* pop ss */ + case 0x1f: /* pop ds */ + if (CODE64(s)) + goto illegal_op; + reg = b >> 3; + ot = gen_pop_T0(s); + gen_movl_seg_T0(s, reg); + gen_pop_update(s, ot); + /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ + if (s->base.is_jmp) { + gen_jmp_im(s, s->pc - s->cs_base); + if (reg == R_SS) { + s->tf = 0; + gen_eob_inhibit_irq(s, true); + } else { + gen_eob(s); + } + } + break; + case 0x1a1: /* pop fs */ + case 0x1a9: /* pop gs */ + ot = gen_pop_T0(s); + gen_movl_seg_T0(s, (b >> 3) & 7); + gen_pop_update(s, ot); + if (s->base.is_jmp) { + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } + break; + + /**************************/ + /* mov */ + case 0x88: + case 0x89: /* mov Gv, Ev */ + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + /* generate a generic store */ + gen_ldst_modrm(env, s, modrm, ot, reg, 1); + break; + case 0xc6: + case 0xc7: /* mov Ev, Iv */ + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + if (mod != 3) { + s->rip_offset = insn_const_size(ot); + gen_lea_modrm(env, s, modrm); + } + val = insn_get(env, s, ot); + tcg_gen_movi_tl(s->T0, val); + if (mod != 3) { + gen_op_st_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0); + } + break; + case 0x8a: + case 0x8b: /* mov Ev, Gv */ + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + case 0x8e: /* mov seg, Gv */ + modrm = x86_ldub_code(env, s); + reg = (modrm >> 3) & 7; + if (reg >= 6 || reg == R_CS) + goto illegal_op; + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_movl_seg_T0(s, reg); + /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ + if (s->base.is_jmp) { + gen_jmp_im(s, s->pc - s->cs_base); + if (reg == R_SS) { + s->tf = 0; + gen_eob_inhibit_irq(s, true); + } else { + gen_eob(s); + } + } + break; + case 0x8c: /* mov Gv, seg */ + modrm = x86_ldub_code(env, s); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + if (reg >= 6) + goto illegal_op; + gen_op_movl_T0_seg(s, reg); + ot = mod == 3 ? dflag : MO_16; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + break; + + case 0x1b6: /* movzbS Gv, Eb */ + case 0x1b7: /* movzwS Gv, Eb */ + case 0x1be: /* movsbS Gv, Eb */ + case 0x1bf: /* movswS Gv, Eb */ + { + MemOp d_ot; + MemOp s_ot; + + /* d_ot is the size of destination */ + d_ot = dflag; + /* ot is the size of source */ + ot = (b & 1) + MO_8; + /* s_ot is the sign+size of source */ + s_ot = b & 8 ? MO_SIGN | ot : ot; + + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + + if (mod == 3) { + if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) { + tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8); + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + switch (s_ot) { + case MO_UB: + tcg_gen_ext8u_tl(s->T0, s->T0); + break; + case MO_SB: + tcg_gen_ext8s_tl(s->T0, s->T0); + break; + case MO_UW: + tcg_gen_ext16u_tl(s->T0, s->T0); + break; + default: + case MO_SW: + tcg_gen_ext16s_tl(s->T0, s->T0); + break; + } + } + gen_op_mov_reg_v(s, d_ot, reg, s->T0); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, s_ot, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); + } + } + break; + + case 0x8d: /* lea */ + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + reg = ((modrm >> 3) & 7) | rex_r; + { + AddressParts a = gen_lea_modrm_0(env, s, modrm); + TCGv ea = gen_lea_modrm_1(s, a); + gen_lea_v_seg(s, s->aflag, ea, -1, -1); + gen_op_mov_reg_v(s, dflag, reg, s->A0); + } + break; + + case 0xa0: /* mov EAX, Ov */ + case 0xa1: + case 0xa2: /* mov Ov, EAX */ + case 0xa3: + { + target_ulong offset_addr; + + ot = mo_b_d(b, dflag); + switch (s->aflag) { +#ifdef TARGET_X86_64 + case MO_64: + offset_addr = x86_ldq_code(env, s); + break; +#endif + default: + offset_addr = insn_get(env, s, s->aflag); + break; + } + tcg_gen_movi_tl(s->A0, offset_addr); + gen_add_A0_ds_seg(s); + if ((b & 2) == 0) { + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, s->T0); + } else { + gen_op_mov_v_reg(s, ot, s->T0, R_EAX); + gen_op_st_v(s, ot, s->T0, s->A0); + } + } + break; + case 0xd7: /* xlat */ + tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]); + tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]); + tcg_gen_add_tl(s->A0, s->A0, s->T0); + gen_extu(s->aflag, s->A0); + gen_add_A0_ds_seg(s); + gen_op_ld_v(s, MO_8, s->T0, s->A0); + gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); + break; + case 0xb0 ... 0xb7: /* mov R, Ib */ + val = insn_get(env, s, MO_8); + tcg_gen_movi_tl(s->T0, val); + gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0); + break; + case 0xb8 ... 0xbf: /* mov R, Iv */ +#ifdef TARGET_X86_64 + if (dflag == MO_64) { + uint64_t tmp; + /* 64 bit case */ + tmp = x86_ldq_code(env, s); + reg = (b & 7) | REX_B(s); + tcg_gen_movi_tl(s->T0, tmp); + gen_op_mov_reg_v(s, MO_64, reg, s->T0); + } else +#endif + { + ot = dflag; + val = insn_get(env, s, ot); + reg = (b & 7) | REX_B(s); + tcg_gen_movi_tl(s->T0, val); + gen_op_mov_reg_v(s, ot, reg, s->T0); + } + break; + + case 0x91 ... 0x97: /* xchg R, EAX */ + do_xchg_reg_eax: + ot = dflag; + reg = (b & 7) | REX_B(s); + rm = R_EAX; + goto do_xchg_reg; + case 0x86: + case 0x87: /* xchg Ev, Gv */ + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (mod == 3) { + rm = (modrm & 7) | REX_B(s); + do_xchg_reg: + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_mov_v_reg(s, ot, s->T1, rm); + gen_op_mov_reg_v(s, ot, rm, s->T0); + gen_op_mov_reg_v(s, ot, reg, s->T1); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_mov_v_reg(s, ot, s->T0, reg); + /* for xchg, lock is implicit */ + tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0, + s->mem_index, ot | MO_LE); + gen_op_mov_reg_v(s, ot, reg, s->T1); + } + break; + case 0xc4: /* les Gv */ + /* In CODE64 this is VEX3; see above. */ + op = R_ES; + goto do_lxx; + case 0xc5: /* lds Gv */ + /* In CODE64 this is VEX2; see above. */ + op = R_DS; + goto do_lxx; + case 0x1b2: /* lss Gv */ + op = R_SS; + goto do_lxx; + case 0x1b4: /* lfs Gv */ + op = R_FS; + goto do_lxx; + case 0x1b5: /* lgs Gv */ + op = R_GS; + do_lxx: + ot = dflag != MO_16 ? MO_32 : MO_16; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, ot, s->T1, s->A0); + gen_add_A0_im(s, 1 << ot); + /* load the segment first to handle exceptions properly */ + gen_op_ld_v(s, MO_16, s->T0, s->A0); + gen_movl_seg_T0(s, op); + /* then put the data */ + gen_op_mov_reg_v(s, ot, reg, s->T1); + if (s->base.is_jmp) { + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } + break; + + /************************/ + /* shifts */ + case 0xc0: + case 0xc1: + /* shift Ev,Ib */ + shift = 2; + grp2: + { + ot = mo_b_d(b, dflag); + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + + if (mod != 3) { + if (shift == 2) { + s->rip_offset = 1; + } + gen_lea_modrm(env, s, modrm); + opreg = OR_TMP0; + } else { + opreg = (modrm & 7) | REX_B(s); + } + + /* simpler op */ + if (shift == 0) { + gen_shift(s, op, ot, opreg, OR_ECX); + } else { + if (shift == 2) { + shift = x86_ldub_code(env, s); + } + gen_shifti(s, op, ot, opreg, shift); + } + } + break; + case 0xd0: + case 0xd1: + /* shift Ev,1 */ + shift = 1; + goto grp2; + case 0xd2: + case 0xd3: + /* shift Ev,cl */ + shift = 0; + goto grp2; + + case 0x1a4: /* shld imm */ + op = 0; + shift = 1; + goto do_shiftd; + case 0x1a5: /* shld cl */ + op = 0; + shift = 0; + goto do_shiftd; + case 0x1ac: /* shrd imm */ + op = 1; + shift = 1; + goto do_shiftd; + case 0x1ad: /* shrd cl */ + op = 1; + shift = 0; + do_shiftd: + ot = dflag; + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + opreg = OR_TMP0; + } else { + opreg = rm; + } + gen_op_mov_v_reg(s, ot, s->T1, reg); + + if (shift) { + TCGv imm = tcg_const_tl(x86_ldub_code(env, s)); + gen_shiftd_rm_T1(s, ot, opreg, op, imm); + tcg_temp_free(imm); + } else { + gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); + } + break; + + /************************/ + /* floats */ + case 0xd8 ... 0xdf: + if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { + /* if CR0.EM or CR0.TS are set, generate an FPU exception */ + /* XXX: what to do if illegal op ? */ + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + rm = modrm & 7; + op = ((b & 7) << 3) | ((modrm >> 3) & 7); + if (mod != 3) { + /* memory op */ + gen_lea_modrm(env, s, modrm); + switch(op) { + case 0x00 ... 0x07: /* fxxxs */ + case 0x10 ... 0x17: /* fixxxl */ + case 0x20 ... 0x27: /* fxxxl */ + case 0x30 ... 0x37: /* fixxx */ + { + int op1; + op1 = op & 7; + + switch(op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_flds_FT0(cpu_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + gen_helper_fldl_FT0(cpu_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); + break; + } + + gen_helper_fp_arith_ST0_FT0(op1); + if (op1 == 3) { + /* fcomp needs pop */ + gen_helper_fpop(cpu_env); + } + } + break; + case 0x08: /* flds */ + case 0x0a: /* fsts */ + case 0x0b: /* fstps */ + case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ + case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ + case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ + switch(op & 7) { + case 0: + switch(op >> 4) { + case 0: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_flds_ST0(cpu_env, s->tmp2_i32); + break; + case 1: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); + break; + case 2: + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + gen_helper_fldl_ST0(cpu_env, s->tmp1_i64); + break; + case 3: + default: + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LESW); + gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); + break; + } + break; + case 1: + /* XXX: the corresponding CPUID bit must be tested ! */ + switch(op >> 4) { + case 1: + gen_helper_fisttl_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_helper_fisttll_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + break; + case 3: + default: + gen_helper_fistt_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + gen_helper_fpop(cpu_env); + break; + default: + switch(op >> 4) { + case 0: + gen_helper_fsts_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 1: + gen_helper_fistl_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUL); + break; + case 2: + gen_helper_fstl_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, + s->mem_index, MO_LEQ); + break; + case 3: + default: + gen_helper_fist_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + } + if ((op & 7) == 3) + gen_helper_fpop(cpu_env); + break; + } + break; + case 0x0c: /* fldenv mem */ + gen_helper_fldenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); + break; + case 0x0d: /* fldcw mem */ + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + gen_helper_fldcw(cpu_env, s->tmp2_i32); + break; + case 0x0e: /* fnstenv mem */ + gen_helper_fstenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); + break; + case 0x0f: /* fnstcw mem */ + gen_helper_fnstcw(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + case 0x1d: /* fldt mem */ + gen_helper_fldt_ST0(cpu_env, s->A0); + break; + case 0x1f: /* fstpt mem */ + gen_helper_fstt_ST0(cpu_env, s->A0); + gen_helper_fpop(cpu_env); + break; + case 0x2c: /* frstor mem */ + gen_helper_frstor(cpu_env, s->A0, tcg_const_i32(dflag - 1)); + break; + case 0x2e: /* fnsave mem */ + gen_helper_fsave(cpu_env, s->A0, tcg_const_i32(dflag - 1)); + break; + case 0x2f: /* fnstsw mem */ + gen_helper_fnstsw(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, + s->mem_index, MO_LEUW); + break; + case 0x3c: /* fbld */ + gen_helper_fbld_ST0(cpu_env, s->A0); + break; + case 0x3e: /* fbstp */ + gen_helper_fbst_ST0(cpu_env, s->A0); + gen_helper_fpop(cpu_env); + break; + case 0x3d: /* fildll */ + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); + gen_helper_fildll_ST0(cpu_env, s->tmp1_i64); + break; + case 0x3f: /* fistpll */ + gen_helper_fistll_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); + gen_helper_fpop(cpu_env); + break; + default: + goto unknown_op; + } + } else { + /* register float ops */ + opreg = rm; + + switch(op) { + case 0x08: /* fld sti */ + gen_helper_fpush(cpu_env); + gen_helper_fmov_ST0_STN(cpu_env, + tcg_const_i32((opreg + 1) & 7)); + break; + case 0x09: /* fxchg sti */ + case 0x29: /* fxchg4 sti, undocumented op */ + case 0x39: /* fxchg7 sti, undocumented op */ + gen_helper_fxchg_ST0_STN(cpu_env, tcg_const_i32(opreg)); + break; + case 0x0a: /* grp d9/2 */ + switch(rm) { + case 0: /* fnop */ + /* check exceptions (FreeBSD FPU probe) */ + gen_helper_fwait(cpu_env); + break; + default: + goto unknown_op; + } + break; + case 0x0c: /* grp d9/4 */ + switch(rm) { + case 0: /* fchs */ + gen_helper_fchs_ST0(cpu_env); + break; + case 1: /* fabs */ + gen_helper_fabs_ST0(cpu_env); + break; + case 4: /* ftst */ + gen_helper_fldz_FT0(cpu_env); + gen_helper_fcom_ST0_FT0(cpu_env); + break; + case 5: /* fxam */ + gen_helper_fxam_ST0(cpu_env); + break; + default: + goto unknown_op; + } + break; + case 0x0d: /* grp d9/5 */ + { + switch(rm) { + case 0: + gen_helper_fpush(cpu_env); + gen_helper_fld1_ST0(cpu_env); + break; + case 1: + gen_helper_fpush(cpu_env); + gen_helper_fldl2t_ST0(cpu_env); + break; + case 2: + gen_helper_fpush(cpu_env); + gen_helper_fldl2e_ST0(cpu_env); + break; + case 3: + gen_helper_fpush(cpu_env); + gen_helper_fldpi_ST0(cpu_env); + break; + case 4: + gen_helper_fpush(cpu_env); + gen_helper_fldlg2_ST0(cpu_env); + break; + case 5: + gen_helper_fpush(cpu_env); + gen_helper_fldln2_ST0(cpu_env); + break; + case 6: + gen_helper_fpush(cpu_env); + gen_helper_fldz_ST0(cpu_env); + break; + default: + goto unknown_op; + } + } + break; + case 0x0e: /* grp d9/6 */ + switch(rm) { + case 0: /* f2xm1 */ + gen_helper_f2xm1(cpu_env); + break; + case 1: /* fyl2x */ + gen_helper_fyl2x(cpu_env); + break; + case 2: /* fptan */ + gen_helper_fptan(cpu_env); + break; + case 3: /* fpatan */ + gen_helper_fpatan(cpu_env); + break; + case 4: /* fxtract */ + gen_helper_fxtract(cpu_env); + break; + case 5: /* fprem1 */ + gen_helper_fprem1(cpu_env); + break; + case 6: /* fdecstp */ + gen_helper_fdecstp(cpu_env); + break; + default: + case 7: /* fincstp */ + gen_helper_fincstp(cpu_env); + break; + } + break; + case 0x0f: /* grp d9/7 */ + switch(rm) { + case 0: /* fprem */ + gen_helper_fprem(cpu_env); + break; + case 1: /* fyl2xp1 */ + gen_helper_fyl2xp1(cpu_env); + break; + case 2: /* fsqrt */ + gen_helper_fsqrt(cpu_env); + break; + case 3: /* fsincos */ + gen_helper_fsincos(cpu_env); + break; + case 5: /* fscale */ + gen_helper_fscale(cpu_env); + break; + case 4: /* frndint */ + gen_helper_frndint(cpu_env); + break; + case 6: /* fsin */ + gen_helper_fsin(cpu_env); + break; + default: + case 7: /* fcos */ + gen_helper_fcos(cpu_env); + break; + } + break; + case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ + case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ + case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ + { + int op1; + + op1 = op & 7; + if (op >= 0x20) { + gen_helper_fp_arith_STN_ST0(op1, opreg); + if (op >= 0x30) + gen_helper_fpop(cpu_env); + } else { + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fp_arith_ST0_FT0(op1); + } + } + break; + case 0x02: /* fcom */ + case 0x22: /* fcom2, undocumented op */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fcom_ST0_FT0(cpu_env); + break; + case 0x03: /* fcomp */ + case 0x23: /* fcomp3, undocumented op */ + case 0x32: /* fcomp5, undocumented op */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + break; + case 0x15: /* da/5 */ + switch(rm) { + case 1: /* fucompp */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); + gen_helper_fucom_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + gen_helper_fpop(cpu_env); + break; + default: + goto unknown_op; + } + break; + case 0x1c: + switch(rm) { + case 0: /* feni (287 only, just do nop here) */ + break; + case 1: /* fdisi (287 only, just do nop here) */ + break; + case 2: /* fclex */ + gen_helper_fclex(cpu_env); + break; + case 3: /* fninit */ + gen_helper_fninit(cpu_env); + break; + case 4: /* fsetpm (287 only, just do nop here) */ + break; + default: + goto unknown_op; + } + break; + case 0x1d: /* fucomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fucomi_ST0_FT0(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x1e: /* fcomi */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fcomi_ST0_FT0(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x28: /* ffree sti */ + gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); + break; + case 0x2a: /* fst sti */ + gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); + break; + case 0x2b: /* fstp sti */ + case 0x0b: /* fstp1 sti, undocumented op */ + case 0x3a: /* fstp8 sti, undocumented op */ + case 0x3b: /* fstp9 sti, undocumented op */ + gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); + gen_helper_fpop(cpu_env); + break; + case 0x2c: /* fucom st(i) */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fucom_ST0_FT0(cpu_env); + break; + case 0x2d: /* fucomp st(i) */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fucom_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + break; + case 0x33: /* de/3 */ + switch(rm) { + case 1: /* fcompp */ + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); + gen_helper_fcom_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + gen_helper_fpop(cpu_env); + break; + default: + goto unknown_op; + } + break; + case 0x38: /* ffreep sti, undocumented op */ + gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fpop(cpu_env); + break; + case 0x3c: /* df/4 */ + switch(rm) { + case 0: + gen_helper_fnstsw(s->tmp2_i32, cpu_env); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + break; + default: + goto unknown_op; + } + break; + case 0x3d: /* fucomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fucomi_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x3e: /* fcomip */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); + gen_helper_fcomi_ST0_FT0(cpu_env); + gen_helper_fpop(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x10 ... 0x13: /* fcmovxx */ + case 0x18 ... 0x1b: + { + int op1; + TCGLabel *l1; + static const uint8_t fcmov_cc[8] = { + (JCC_B << 1), + (JCC_Z << 1), + (JCC_BE << 1), + (JCC_P << 1), + }; + + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); + l1 = gen_new_label(); + gen_jcc1_noeob(s, op1, l1); + gen_helper_fmov_ST0_STN(cpu_env, tcg_const_i32(opreg)); + gen_set_label(l1); + } + break; + default: + goto unknown_op; + } + } + break; + /************************/ + /* string ops */ + + case 0xa4: /* movsS */ + case 0xa5: + ot = mo_b_d(b, dflag); + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_movs(s, ot); + } + break; + + case 0xaa: /* stosS */ + case 0xab: + ot = mo_b_d(b, dflag); + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_stos(s, ot); + } + break; + case 0xac: /* lodsS */ + case 0xad: + ot = mo_b_d(b, dflag); + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + } else { + gen_lods(s, ot); + } + break; + case 0xae: /* scasS */ + case 0xaf: + ot = mo_b_d(b, dflag); + if (prefixes & PREFIX_REPNZ) { + gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + } else if (prefixes & PREFIX_REPZ) { + gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + } else { + gen_scas(s, ot); + } + break; + + case 0xa6: /* cmpsS */ + case 0xa7: + ot = mo_b_d(b, dflag); + if (prefixes & PREFIX_REPNZ) { + gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); + } else if (prefixes & PREFIX_REPZ) { + gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); + } else { + gen_cmps(s, ot); + } + break; + case 0x6c: /* insS */ + case 0x6d: + ot = mo_b_d32(b, dflag); + tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); + gen_check_io(s, ot, pc_start - s->cs_base, + SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + /* jump generated by gen_repz_ins */ + } else { + gen_ins(s, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + } + break; + case 0x6e: /* outsS */ + case 0x6f: + ot = mo_b_d32(b, dflag); + tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); + gen_check_io(s, ot, pc_start - s->cs_base, + svm_is_rep(prefixes) | 4); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { + gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); + /* jump generated by gen_repz_outs */ + } else { + gen_outs(s, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + } + break; + + /************************/ + /* port I/O */ + + case 0xe4: + case 0xe5: + ot = mo_b_d32(b, dflag); + val = x86_ldub_code(env, s); + tcg_gen_movi_tl(s->T0, val); + gen_check_io(s, ot, pc_start - s->cs_base, + SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + tcg_gen_movi_i32(s->tmp2_i32, val); + gen_helper_in_func(ot, s->T1, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, R_EAX, s->T1); + gen_bpt_io(s, s->tmp2_i32, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + case 0xe6: + case 0xe7: + ot = mo_b_d32(b, dflag); + val = x86_ldub_code(env, s); + tcg_gen_movi_tl(s->T0, val); + gen_check_io(s, ot, pc_start - s->cs_base, + svm_is_rep(prefixes)); + gen_op_mov_v_reg(s, ot, s->T1, R_EAX); + + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + tcg_gen_movi_i32(s->tmp2_i32, val); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_bpt_io(s, s->tmp2_i32, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + case 0xec: + case 0xed: + ot = mo_b_d32(b, dflag); + tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); + gen_check_io(s, ot, pc_start - s->cs_base, + SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_in_func(ot, s->T1, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, R_EAX, s->T1); + gen_bpt_io(s, s->tmp2_i32, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + case 0xee: + case 0xef: + ot = mo_b_d32(b, dflag); + tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); + gen_check_io(s, ot, pc_start - s->cs_base, + svm_is_rep(prefixes)); + gen_op_mov_v_reg(s, ot, s->T1, R_EAX); + + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_bpt_io(s, s->tmp2_i32, ot); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + + /************************/ + /* control */ + case 0xc2: /* ret im */ + val = x86_ldsw_code(env, s); + ot = gen_pop_T0(s); + gen_stack_update(s, val + (1 << ot)); + /* Note that gen_pop_T0 uses a zero-extending load. */ + gen_op_jmp_v(s->T0); + gen_bnd_jmp(s); + gen_jr(s, s->T0); + break; + case 0xc3: /* ret */ + ot = gen_pop_T0(s); + gen_pop_update(s, ot); + /* Note that gen_pop_T0 uses a zero-extending load. */ + gen_op_jmp_v(s->T0); + gen_bnd_jmp(s); + gen_jr(s, s->T0); + break; + case 0xca: /* lret im */ + val = x86_ldsw_code(env, s); + do_lret: + if (s->pe && !s->vm86) { + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), + tcg_const_i32(val)); + } else { + gen_stack_A0(s); + /* pop offset */ + gen_op_ld_v(s, dflag, s->T0, s->A0); + /* NOTE: keeping EIP updated is not a problem in case of + exception */ + gen_op_jmp_v(s->T0); + /* pop selector */ + gen_add_A0_im(s, 1 << dflag); + gen_op_ld_v(s, dflag, s->T0, s->A0); + gen_op_movl_seg_T0_vm(s, R_CS); + /* add stack offset */ + gen_stack_update(s, val + (2 << dflag)); + } + gen_eob(s); + break; + case 0xcb: /* lret */ + val = 0; + goto do_lret; + case 0xcf: /* iret */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IRET); + if (!s->pe) { + /* real mode */ + gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); + set_cc_op(s, CC_OP_EFLAGS); + } else if (s->vm86) { + if (s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); + set_cc_op(s, CC_OP_EFLAGS); + } + } else { + gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), + tcg_const_i32(s->pc - s->cs_base)); + set_cc_op(s, CC_OP_EFLAGS); + } + gen_eob(s); + break; + case 0xe8: /* call im */ + { + if (dflag != MO_16) { + tval = (int32_t)insn_get(env, s, MO_32); + } else { + tval = (int16_t)insn_get(env, s, MO_16); + } + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (dflag == MO_16) { + tval &= 0xffff; + } else if (!CODE64(s)) { + tval &= 0xffffffff; + } + tcg_gen_movi_tl(s->T0, next_eip); + gen_push_v(s, s->T0); + gen_bnd_jmp(s); + gen_jmp(s, tval); + } + break; + case 0x9a: /* lcall im */ + { + unsigned int selector, offset; + + if (CODE64(s)) + goto illegal_op; + ot = dflag; + offset = insn_get(env, s, ot); + selector = insn_get(env, s, MO_16); + + tcg_gen_movi_tl(s->T0, selector); + tcg_gen_movi_tl(s->T1, offset); + } + goto do_lcall; + case 0xe9: /* jmp im */ + if (dflag != MO_16) { + tval = (int32_t)insn_get(env, s, MO_32); + } else { + tval = (int16_t)insn_get(env, s, MO_16); + } + tval += s->pc - s->cs_base; + if (dflag == MO_16) { + tval &= 0xffff; + } else if (!CODE64(s)) { + tval &= 0xffffffff; + } + gen_bnd_jmp(s); + gen_jmp(s, tval); + break; + case 0xea: /* ljmp im */ + { + unsigned int selector, offset; + + if (CODE64(s)) + goto illegal_op; + ot = dflag; + offset = insn_get(env, s, ot); + selector = insn_get(env, s, MO_16); + + tcg_gen_movi_tl(s->T0, selector); + tcg_gen_movi_tl(s->T1, offset); + } + goto do_ljmp; + case 0xeb: /* jmp Jb */ + tval = (int8_t)insn_get(env, s, MO_8); + tval += s->pc - s->cs_base; + if (dflag == MO_16) { + tval &= 0xffff; + } + gen_jmp(s, tval); + break; + case 0x70 ... 0x7f: /* jcc Jb */ + tval = (int8_t)insn_get(env, s, MO_8); + goto do_jcc; + case 0x180 ... 0x18f: /* jcc Jv */ + if (dflag != MO_16) { + tval = (int32_t)insn_get(env, s, MO_32); + } else { + tval = (int16_t)insn_get(env, s, MO_16); + } + do_jcc: + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (dflag == MO_16) { + tval &= 0xffff; + } + gen_bnd_jmp(s); + gen_jcc(s, b, tval, next_eip); + break; + + case 0x190 ... 0x19f: /* setcc Gv */ + modrm = x86_ldub_code(env, s); + gen_setcc1(s, b, s->T0); + gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); + break; + case 0x140 ... 0x14f: /* cmov Gv, Ev */ + if (!(s->cpuid_features & CPUID_CMOV)) { + goto illegal_op; + } + ot = dflag; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + gen_cmovcc1(env, s, ot, b, modrm, reg); + break; + + /************************/ + /* flags */ + case 0x9c: /* pushf */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_PUSHF); + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_update_cc_op(s); + gen_helper_read_eflags(s->T0, cpu_env); + gen_push_v(s, s->T0); + } + break; + case 0x9d: /* popf */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_POPF); + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + ot = gen_pop_T0(s); + if (s->cpl == 0) { + if (dflag != MO_16) { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | AC_MASK | + ID_MASK | NT_MASK | + IF_MASK | + IOPL_MASK))); + } else { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | AC_MASK | + ID_MASK | NT_MASK | + IF_MASK | IOPL_MASK) + & 0xffff)); + } + } else { + if (s->cpl <= s->iopl) { + if (dflag != MO_16) { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | + AC_MASK | + ID_MASK | + NT_MASK | + IF_MASK))); + } else { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | + AC_MASK | + ID_MASK | + NT_MASK | + IF_MASK) + & 0xffff)); + } + } else { + if (dflag != MO_16) { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | AC_MASK | + ID_MASK | NT_MASK))); + } else { + gen_helper_write_eflags(cpu_env, s->T0, + tcg_const_i32((TF_MASK | AC_MASK | + ID_MASK | NT_MASK) + & 0xffff)); + } + } + } + gen_pop_update(s, ot); + set_cc_op(s, CC_OP_EFLAGS); + /* abort translation because TF/AC flag may change */ + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } + break; + case 0x9e: /* sahf */ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) + goto illegal_op; + gen_op_mov_v_reg(s, MO_8, s->T0, R_AH); + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); + tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); + break; + case 0x9f: /* lahf */ + if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) + goto illegal_op; + gen_compute_eflags(s); + /* Note: gen_compute_eflags() only gives the condition codes */ + tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); + gen_op_mov_reg_v(s, MO_8, R_AH, s->T0); + break; + case 0xf5: /* cmc */ + gen_compute_eflags(s); + tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); + break; + case 0xf8: /* clc */ + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); + break; + case 0xf9: /* stc */ + gen_compute_eflags(s); + tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); + break; + case 0xfc: /* cld */ + tcg_gen_movi_i32(s->tmp2_i32, 1); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + break; + case 0xfd: /* std */ + tcg_gen_movi_i32(s->tmp2_i32, -1); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + break; + + /************************/ + /* bit operations */ + case 0x1ba: /* bt/bts/btr/btc Gv, im */ + ot = dflag; + modrm = x86_ldub_code(env, s); + op = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + if (mod != 3) { + s->rip_offset = 1; + gen_lea_modrm(env, s, modrm); + if (!(s->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + } + /* load shift */ + val = x86_ldub_code(env, s); + tcg_gen_movi_tl(s->T1, val); + if (op < 4) + goto unknown_op; + op -= 4; + goto bt_op; + case 0x1a3: /* bt Gv, Ev */ + op = 0; + goto do_btx; + case 0x1ab: /* bts */ + op = 1; + goto do_btx; + case 0x1b3: /* btr */ + op = 2; + goto do_btx; + case 0x1bb: /* btc */ + op = 3; + do_btx: + ot = dflag; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + gen_op_mov_v_reg(s, MO_32, s->T1, reg); + if (mod != 3) { + AddressParts a = gen_lea_modrm_0(env, s, modrm); + /* specific case: we need to add a displacement */ + gen_exts(ot, s->T1); + tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); + tcg_gen_shli_tl(s->tmp0, s->tmp0, ot); + tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a), s->tmp0); + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + if (!(s->prefix & PREFIX_LOCK)) { + gen_op_ld_v(s, ot, s->T0, s->A0); + } + } else { + gen_op_mov_v_reg(s, ot, s->T0, rm); + } + bt_op: + tcg_gen_andi_tl(s->T1, s->T1, (1 << (3 + ot)) - 1); + tcg_gen_movi_tl(s->tmp0, 1); + tcg_gen_shl_tl(s->tmp0, s->tmp0, s->T1); + if (s->prefix & PREFIX_LOCK) { + switch (op) { + case 0: /* bt */ + /* Needs no atomic ops; we surpressed the normal + memory load for LOCK above so do it now. */ + gen_op_ld_v(s, ot, s->T0, s->A0); + break; + case 1: /* bts */ + tcg_gen_atomic_fetch_or_tl(s->T0, s->A0, s->tmp0, + s->mem_index, ot | MO_LE); + break; + case 2: /* btr */ + tcg_gen_not_tl(s->tmp0, s->tmp0); + tcg_gen_atomic_fetch_and_tl(s->T0, s->A0, s->tmp0, + s->mem_index, ot | MO_LE); + break; + default: + case 3: /* btc */ + tcg_gen_atomic_fetch_xor_tl(s->T0, s->A0, s->tmp0, + s->mem_index, ot | MO_LE); + break; + } + tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); + } else { + tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); + switch (op) { + case 0: /* bt */ + /* Data already loaded; nothing to do. */ + break; + case 1: /* bts */ + tcg_gen_or_tl(s->T0, s->T0, s->tmp0); + break; + case 2: /* btr */ + tcg_gen_andc_tl(s->T0, s->T0, s->tmp0); + break; + default: + case 3: /* btc */ + tcg_gen_xor_tl(s->T0, s->T0, s->tmp0); + break; + } + if (op != 0) { + if (mod != 3) { + gen_op_st_v(s, ot, s->T0, s->A0); + } else { + gen_op_mov_reg_v(s, ot, rm, s->T0); + } + } + } + + /* Delay all CC updates until after the store above. Note that + C is the result of the test, Z is unchanged, and the others + are all undefined. */ + switch (s->cc_op) { + case CC_OP_MULB ... CC_OP_MULQ: + case CC_OP_ADDB ... CC_OP_ADDQ: + case CC_OP_ADCB ... CC_OP_ADCQ: + case CC_OP_SUBB ... CC_OP_SUBQ: + case CC_OP_SBBB ... CC_OP_SBBQ: + case CC_OP_LOGICB ... CC_OP_LOGICQ: + case CC_OP_INCB ... CC_OP_INCQ: + case CC_OP_DECB ... CC_OP_DECQ: + case CC_OP_SHLB ... CC_OP_SHLQ: + case CC_OP_SARB ... CC_OP_SARQ: + case CC_OP_BMILGB ... CC_OP_BMILGQ: + /* Z was going to be computed from the non-zero status of CC_DST. + We can get that same Z value (and the new C value) by leaving + CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the + same width. */ + tcg_gen_mov_tl(cpu_cc_src, s->tmp4); + set_cc_op(s, ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB); + break; + default: + /* Otherwise, generate EFLAGS and replace the C bit. */ + gen_compute_eflags(s); + tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, s->tmp4, + ctz32(CC_C), 1); + break; + } + break; + case 0x1bc: /* bsf / tzcnt */ + case 0x1bd: /* bsr / lzcnt */ + ot = dflag; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_extu(ot, s->T0); + + /* Note that lzcnt and tzcnt are in different extensions. */ + if ((prefixes & PREFIX_REPZ) + && (b & 1 + ? s->cpuid_ext3_features & CPUID_EXT3_ABM + : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { + int size = 8 << ot; + /* For lzcnt/tzcnt, C bit is defined related to the input. */ + tcg_gen_mov_tl(cpu_cc_src, s->T0); + if (b & 1) { + /* For lzcnt, reduce the target_ulong result by the + number of zeros that we expect to find at the top. */ + tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); + tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - size); + } else { + /* For tzcnt, a zero input must return the operand size. */ + tcg_gen_ctzi_tl(s->T0, s->T0, size); + } + /* For lzcnt/tzcnt, Z bit is defined related to the result. */ + gen_op_update1_cc(s); + set_cc_op(s, CC_OP_BMILGB + ot); + } else { + /* For bsr/bsf, only the Z bit is defined and it is related + to the input and not the result. */ + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + set_cc_op(s, CC_OP_LOGICB + ot); + + /* ??? The manual says that the output is undefined when the + input is zero, but real hardware leaves it unchanged, and + real programs appear to depend on that. Accomplish this + by passing the output as the value to return upon zero. */ + if (b & 1) { + /* For bsr, return the bit index of the first 1 bit, + not the count of leading zeros. */ + tcg_gen_xori_tl(s->T1, cpu_regs[reg], TARGET_LONG_BITS - 1); + tcg_gen_clz_tl(s->T0, s->T0, s->T1); + tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); + } else { + tcg_gen_ctz_tl(s->T0, s->T0, cpu_regs[reg]); + } + } + gen_op_mov_reg_v(s, ot, reg, s->T0); + break; + /************************/ + /* bcd */ + case 0x27: /* daa */ + if (CODE64(s)) + goto illegal_op; + gen_update_cc_op(s); + gen_helper_daa(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x2f: /* das */ + if (CODE64(s)) + goto illegal_op; + gen_update_cc_op(s); + gen_helper_das(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x37: /* aaa */ + if (CODE64(s)) + goto illegal_op; + gen_update_cc_op(s); + gen_helper_aaa(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0x3f: /* aas */ + if (CODE64(s)) + goto illegal_op; + gen_update_cc_op(s); + gen_helper_aas(cpu_env); + set_cc_op(s, CC_OP_EFLAGS); + break; + case 0xd4: /* aam */ + if (CODE64(s)) + goto illegal_op; + val = x86_ldub_code(env, s); + if (val == 0) { + gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base); + } else { + gen_helper_aam(cpu_env, tcg_const_i32(val)); + set_cc_op(s, CC_OP_LOGICB); + } + break; + case 0xd5: /* aad */ + if (CODE64(s)) + goto illegal_op; + val = x86_ldub_code(env, s); + gen_helper_aad(cpu_env, tcg_const_i32(val)); + set_cc_op(s, CC_OP_LOGICB); + break; + /************************/ + /* misc */ + case 0x90: /* nop */ + /* XXX: correct lock test for all insn */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + /* If REX_B is set, then this is xchg eax, r8d, not a nop. */ + if (REX_B(s)) { + goto do_xchg_reg_eax; + } + if (prefixes & PREFIX_REPZ) { + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start)); + s->base.is_jmp = DISAS_NORETURN; + } + break; + case 0x9b: /* fwait */ + if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == + (HF_MP_MASK | HF_TS_MASK)) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + } else { + gen_helper_fwait(cpu_env); + } + break; + case 0xcc: /* int3 */ + gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); + break; + case 0xcd: /* int N */ + val = x86_ldub_code(env, s); + if (s->vm86 && s->iopl != 3) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); + } + break; + case 0xce: /* into */ + if (CODE64(s)) + goto illegal_op; + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); + break; +#ifdef WANT_ICEBP + case 0xf1: /* icebp (undocumented, exits to external debugger) */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_ICEBP); + gen_debug(s, pc_start - s->cs_base); + break; +#endif + case 0xfa: /* cli */ + if (!s->vm86) { + if (s->cpl <= s->iopl) { + gen_helper_cli(cpu_env); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } else { + if (s->iopl == 3) { + gen_helper_cli(cpu_env); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + } + break; + case 0xfb: /* sti */ + if (s->vm86 ? s->iopl == 3 : s->cpl <= s->iopl) { + gen_helper_sti(cpu_env); + /* interruptions are enabled only the first insn after sti */ + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob_inhibit_irq(s, true); + } else { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } + break; + case 0x62: /* bound */ + if (CODE64(s)) + goto illegal_op; + ot = dflag; + modrm = x86_ldub_code(env, s); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_lea_modrm(env, s, modrm); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + if (ot == MO_16) { + gen_helper_boundw(cpu_env, s->A0, s->tmp2_i32); + } else { + gen_helper_boundl(cpu_env, s->A0, s->tmp2_i32); + } + break; + case 0x1c8 ... 0x1cf: /* bswap reg */ + reg = (b & 7) | REX_B(s); +#ifdef TARGET_X86_64 + if (dflag == MO_64) { + gen_op_mov_v_reg(s, MO_64, s->T0, reg); + tcg_gen_bswap64_i64(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_64, reg, s->T0); + } else +#endif + { + gen_op_mov_v_reg(s, MO_32, s->T0, reg); + tcg_gen_ext32u_tl(s->T0, s->T0); + tcg_gen_bswap32_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_32, reg, s->T0); + } + break; + case 0xd6: /* salc */ + if (CODE64(s)) + goto illegal_op; + gen_compute_eflags_c(s, s->T0); + tcg_gen_neg_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); + break; + case 0xe0: /* loopnz */ + case 0xe1: /* loopz */ + case 0xe2: /* loop */ + case 0xe3: /* jecxz */ + { + TCGLabel *l1, *l2, *l3; + + tval = (int8_t)insn_get(env, s, MO_8); + next_eip = s->pc - s->cs_base; + tval += next_eip; + if (dflag == MO_16) { + tval &= 0xffff; + } + + l1 = gen_new_label(); + l2 = gen_new_label(); + l3 = gen_new_label(); + gen_update_cc_op(s); + b &= 3; + switch(b) { + case 0: /* loopnz */ + case 1: /* loopz */ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, s->aflag, l3); + gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); + break; + case 2: /* loop */ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jnz_ecx(s, s->aflag, l1); + break; + default: + case 3: /* jcxz */ + gen_op_jz_ecx(s, s->aflag, l1); + break; + } + + gen_set_label(l3); + gen_jmp_im(s, next_eip); + tcg_gen_br(l2); + + gen_set_label(l1); + gen_jmp_im(s, tval); + gen_set_label(l2); + gen_eob(s); + } + break; + case 0x130: /* wrmsr */ + case 0x132: /* rdmsr */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + if (b & 2) { + gen_helper_rdmsr(cpu_env); + } else { + gen_helper_wrmsr(cpu_env); + } + } + break; + case 0x131: /* rdtsc */ + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_rdtsc(cpu_env); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + case 0x133: /* rdpmc */ + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_rdpmc(cpu_env); + break; + case 0x134: /* sysenter */ + /* For Intel SYSENTER is valid on 64-bit */ + if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) + goto illegal_op; + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_helper_sysenter(cpu_env); + gen_eob(s); + } + break; + case 0x135: /* sysexit */ + /* For Intel SYSEXIT is valid on 64-bit */ + if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) + goto illegal_op; + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); + gen_eob(s); + } + break; +#ifdef TARGET_X86_64 + case 0x105: /* syscall */ + /* XXX: is it usable in real mode ? */ + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_syscall(cpu_env, tcg_const_i32(s->pc - pc_start)); + /* TF handling for the syscall insn is different. The TF bit is checked + after the syscall insn completes. This allows #DB to not be + generated after one has entered CPL0 if TF is set in FMASK. */ + gen_eob_worker(s, false, true); + break; + case 0x107: /* sysret */ + if (!s->pe) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); + /* condition codes are modified only in long mode */ + if (s->lma) { + set_cc_op(s, CC_OP_EFLAGS); + } + /* TF handling for the sysret insn is different. The TF bit is + checked after the sysret insn completes. This allows #DB to be + generated "as if" the syscall insn in userspace has just + completed. */ + gen_eob_worker(s, false, true); + } + break; +#endif + case 0x1a2: /* cpuid */ + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_cpuid(cpu_env); + break; + case 0xf4: /* hlt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); + s->base.is_jmp = DISAS_NORETURN; + } + break; + case 0x100: + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* sldt */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ); + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State, ldt.selector)); + ot = mod == 3 ? dflag : MO_16; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + break; + case 2: /* lldt */ + if (!s->pe || s->vm86) + goto illegal_op; + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lldt(cpu_env, s->tmp2_i32); + } + break; + case 1: /* str */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ); + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State, tr.selector)); + ot = mod == 3 ? dflag : MO_16; + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + break; + case 3: /* ltr */ + if (!s->pe || s->vm86) + goto illegal_op; + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_ltr(cpu_env, s->tmp2_i32); + } + break; + case 4: /* verr */ + case 5: /* verw */ + if (!s->pe || s->vm86) + goto illegal_op; + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_update_cc_op(s); + if (op == 4) { + gen_helper_verr(cpu_env, s->T0); + } else { + gen_helper_verw(cpu_env, s->T0); + } + set_cc_op(s, CC_OP_EFLAGS); + break; + default: + goto unknown_op; + } + break; + + case 0x101: + modrm = x86_ldub_code(env, s); + switch (modrm) { + CASE_MODRM_MEM_OP(0): /* sgdt */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ); + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(s->T0, + cpu_env, offsetof(CPUX86State, gdt.limit)); + gen_op_st_v(s, MO_16, s->T0, s->A0); + gen_add_A0_im(s, 2); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); + if (dflag == MO_16) { + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); + } + gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); + break; + + case 0xc8: /* monitor */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + tcg_gen_mov_tl(s->A0, cpu_regs[R_EAX]); + gen_extu(s->aflag, s->A0); + gen_add_A0_ds_seg(s); + gen_helper_monitor(cpu_env, s->A0); + break; + + case 0xc9: /* mwait */ + if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); + gen_eob(s); + break; + + case 0xca: /* clac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; + } + gen_helper_clac(cpu_env); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xcb: /* stac */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) + || s->cpl != 0) { + goto illegal_op; + } + gen_helper_stac(cpu_env); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + CASE_MODRM_MEM_OP(1): /* sidt */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.limit)); + gen_op_st_v(s, MO_16, s->T0, s->A0); + gen_add_A0_im(s, 2); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); + if (dflag == MO_16) { + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); + } + gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); + break; + + case 0xd0: /* xgetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xgetbv(s->tmp1_i64, cpu_env, s->tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); + break; + + case 0xd1: /* xsetbv */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->prefix & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xsetbv(cpu_env, s->tmp2_i32, s->tmp1_i64); + /* End TB because translation flags may change. */ + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xd8: /* VMRUN */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), + tcg_const_i32(s->pc - pc_start)); + tcg_gen_exit_tb(NULL, 0); + s->base.is_jmp = DISAS_NORETURN; + break; + + case 0xd9: /* VMMCALL */ + if (!(s->flags & HF_SVME_MASK)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_vmmcall(cpu_env); + break; + + case 0xda: /* VMLOAD */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + case 0xdb: /* VMSAVE */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + case 0xdc: /* STGI */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_helper_stgi(cpu_env); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xdd: /* CLGI */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_clgi(cpu_env); + break; + + case 0xde: /* SKINIT */ + if ((!(s->flags & HF_SVME_MASK) + && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) + || !s->pe) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_skinit(cpu_env); + break; + + case 0xdf: /* INVLPGA */ + if (!(s->flags & HF_SVME_MASK) || !s->pe) { + goto illegal_op; + } + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag - 1)); + break; + + CASE_MODRM_MEM_OP(2): /* lgdt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); + } + tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, gdt.limit)); + break; + + CASE_MODRM_MEM_OP(3): /* lidt */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_WRITE); + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_16, s->T1, s->A0); + gen_add_A0_im(s, 2); + gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); + if (dflag == MO_16) { + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); + } + tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, idt.limit)); + break; + + CASE_MODRM_OP(4): /* smsw */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0])); + /* + * In 32-bit mode, the higher 16 bits of the destination + * register are undefined. In practice CR0[31:0] is stored + * just like in 64-bit mode. + */ + mod = (modrm >> 6) & 3; + ot = (mod != 3 ? MO_16 : s->dflag); + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); + break; + case 0xee: /* rdpkru */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_rdpkru(s->tmp1_i64, cpu_env, s->tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); + break; + case 0xef: /* wrpkru */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_wrpkru(cpu_env, s->tmp2_i32, s->tmp1_i64); + break; + CASE_MODRM_OP(6): /* lmsw */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + gen_helper_lmsw(cpu_env, s->T0); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + CASE_MODRM_MEM_OP(7): /* invlpg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + break; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + gen_lea_modrm(env, s, modrm); + gen_helper_invlpg(cpu_env, s->A0); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + case 0xf8: /* swapgs */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + tcg_gen_mov_tl(s->T0, cpu_seg_base[R_GS]); + tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, + offsetof(CPUX86State, kernelgsbase)); + tcg_gen_st_tl(s->T0, cpu_env, + offsetof(CPUX86State, kernelgsbase)); + } + break; + } +#endif + goto illegal_op; + + case 0xf9: /* rdtscp */ + if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) { + goto illegal_op; + } + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_rdtscp(cpu_env); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + break; + + default: + goto unknown_op; + } + break; + + case 0x108: /* invd */ + case 0x109: /* wbinvd */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_svm_check_intercept(s, pc_start, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD); + /* nothing to do */ + } + break; + case 0x63: /* arpl or movslS (x86_64) */ +#ifdef TARGET_X86_64 + if (CODE64(s)) { + int d_ot; + /* d_ot is the size of destination */ + d_ot = dflag; + + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + mod = (modrm >> 6) & 3; + rm = (modrm & 7) | REX_B(s); + + if (mod == 3) { + gen_op_mov_v_reg(s, MO_32, s->T0, rm); + /* sign extend */ + if (d_ot == MO_64) { + tcg_gen_ext32s_tl(s->T0, s->T0); + } + gen_op_mov_reg_v(s, d_ot, reg, s->T0); + } else { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); + } + } else +#endif + { + TCGLabel *label1; + TCGv t0, t1, t2, a0; + + if (!s->pe || s->vm86) + goto illegal_op; + t0 = tcg_temp_local_new(); + t1 = tcg_temp_local_new(); + t2 = tcg_temp_local_new(); + ot = MO_16; + modrm = x86_ldub_code(env, s); + reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; + rm = modrm & 7; + if (mod != 3) { + gen_lea_modrm(env, s, modrm); + gen_op_ld_v(s, ot, t0, s->A0); + a0 = tcg_temp_local_new(); + tcg_gen_mov_tl(a0, s->A0); + } else { + gen_op_mov_v_reg(s, ot, t0, rm); + a0 = NULL; + } + gen_op_mov_v_reg(s, ot, t1, reg); + tcg_gen_andi_tl(s->tmp0, t0, 3); + tcg_gen_andi_tl(t1, t1, 3); + tcg_gen_movi_tl(t2, 0); + label1 = gen_new_label(); + tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1); + tcg_gen_andi_tl(t0, t0, ~3); + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_movi_tl(t2, CC_Z); + gen_set_label(label1); + if (mod != 3) { + gen_op_st_v(s, ot, t0, a0); + tcg_temp_free(a0); + } else { + gen_op_mov_reg_v(s, ot, rm, t0); + } + gen_compute_eflags(s); + tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + } + break; + case 0x102: /* lar */ + case 0x103: /* lsl */ + { + TCGLabel *label1; + TCGv t0; + if (!s->pe || s->vm86) + goto illegal_op; + ot = dflag != MO_16 ? MO_32 : MO_16; + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); + t0 = tcg_temp_local_new(); + gen_update_cc_op(s); + if (b == 0x102) { + gen_helper_lar(t0, cpu_env, s->T0); + } else { + gen_helper_lsl(t0, cpu_env, s->T0); + } + tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); + label1 = gen_new_label(); + tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); + gen_op_mov_reg_v(s, ot, reg, t0); + gen_set_label(label1); + set_cc_op(s, CC_OP_EFLAGS); + tcg_temp_free(t0); + } + break; + case 0x118: + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + op = (modrm >> 3) & 7; + switch(op) { + case 0: /* prefetchnta */ + case 1: /* prefetchnt0 */ + case 2: /* prefetchnt0 */ + case 3: /* prefetchnt0 */ + if (mod == 3) + goto illegal_op; + gen_nop_modrm(env, s, modrm); + /* nothing more to do */ + break; + default: /* nop (multi byte) */ + gen_nop_modrm(env, s, modrm); + break; + } + break; + case 0x11a: + modrm = x86_ldub_code(env, s); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (prefixes & PREFIX_REPZ) { + /* bndcl */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_LTU, cpu_bndl[reg]); + } else if (prefixes & PREFIX_REPNZ) { + /* bndcu */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + TCGv_i64 notu = tcg_temp_new_i64(); + tcg_gen_not_i64(notu, cpu_bndu[reg]); + gen_bndck(env, s, modrm, TCG_COND_GTU, notu); + tcg_temp_free_i64(notu); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- from reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg], cpu_bndl[reg2]); + tcg_gen_mov_i64(cpu_bndu[reg], cpu_bndu[reg2]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(s->A0, s->A0, 8); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(s->A0, s->A0, 4); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, + s->mem_index, MO_LEUL); + } + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } else if (mod != 3) { + /* bndldx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(s->A0, 0); + } + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(s->T0, 0); + } + if (CODE64(s)) { + gen_helper_bndldx64(cpu_bndl[reg], cpu_env, s->A0, s->T0); + tcg_gen_ld_i64(cpu_bndu[reg], cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_Q(0))); + } else { + gen_helper_bndldx32(cpu_bndu[reg], cpu_env, s->A0, s->T0); + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); + tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); + } + gen_set_hflag(s, HF_MPX_IU_MASK); + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x11b: + modrm = x86_ldub_code(env, s); + if (s->flags & HF_MPX_EN_MASK) { + mod = (modrm >> 6) & 3; + reg = ((modrm >> 3) & 7) | rex_r; + if (mod != 3 && (prefixes & PREFIX_REPZ)) { + /* bndmk */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (a.base >= 0) { + tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); + if (!CODE64(s)) { + tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndl[reg]); + } + } else if (a.base == -1) { + /* no base register has lower bound of 0 */ + tcg_gen_movi_i64(cpu_bndl[reg], 0); + } else { + /* rip-relative generates #ud */ + goto illegal_op; + } + tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, a)); + if (!CODE64(s)) { + tcg_gen_ext32u_tl(s->A0, s->A0); + } + tcg_gen_extu_tl_i64(cpu_bndu[reg], s->A0); + /* bnd registers are now in-use */ + gen_set_hflag(s, HF_MPX_IU_MASK); + break; + } else if (prefixes & PREFIX_REPNZ) { + /* bndcn */ + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16) { + goto illegal_op; + } + gen_bndck(env, s, modrm, TCG_COND_GTU, cpu_bndu[reg]); + } else if (prefixes & PREFIX_DATA) { + /* bndmov -- to reg/mem */ + if (reg >= 4 || s->aflag == MO_16) { + goto illegal_op; + } + if (mod == 3) { + int reg2 = (modrm & 7) | REX_B(s); + if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if (s->flags & HF_MPX_IU_MASK) { + tcg_gen_mov_i64(cpu_bndl[reg2], cpu_bndl[reg]); + tcg_gen_mov_i64(cpu_bndu[reg2], cpu_bndu[reg]); + } + } else { + gen_lea_modrm(env, s, modrm); + if (CODE64(s)) { + tcg_gen_qemu_st_i64(cpu_bndl[reg], s->A0, + s->mem_index, MO_LEQ); + tcg_gen_addi_tl(s->A0, s->A0, 8); + tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, + s->mem_index, MO_LEQ); + } else { + tcg_gen_qemu_st_i64(cpu_bndl[reg], s->A0, + s->mem_index, MO_LEUL); + tcg_gen_addi_tl(s->A0, s->A0, 4); + tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, + s->mem_index, MO_LEUL); + } + } + } else if (mod != 3) { + /* bndstx */ + AddressParts a = gen_lea_modrm_0(env, s, modrm); + if (reg >= 4 + || (prefixes & PREFIX_LOCK) + || s->aflag == MO_16 + || a.base < -1) { + goto illegal_op; + } + if (a.base >= 0) { + tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); + } else { + tcg_gen_movi_tl(s->A0, 0); + } + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); + if (a.index >= 0) { + tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); + } else { + tcg_gen_movi_tl(s->T0, 0); + } + if (CODE64(s)) { + gen_helper_bndstx64(cpu_env, s->A0, s->T0, + cpu_bndl[reg], cpu_bndu[reg]); + } else { + gen_helper_bndstx32(cpu_env, s->A0, s->T0, + cpu_bndl[reg], cpu_bndu[reg]); + } + } + } + gen_nop_modrm(env, s, modrm); + break; + case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ + modrm = x86_ldub_code(env, s); + gen_nop_modrm(env, s, modrm); + break; + case 0x120: /* mov reg, crN */ + case 0x122: /* mov crN, reg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + modrm = x86_ldub_code(env, s); + /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). + * AMD documentation (24594.pdf) and testing of + * intel 386 and 486 processors all show that the mod bits + * are assumed to be 1's, regardless of actual values. + */ + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + if (CODE64(s)) + ot = MO_64; + else + ot = MO_32; + if ((prefixes & PREFIX_LOCK) && (reg == 0) && + (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { + reg = 8; + } + switch(reg) { + case 0: + case 2: + case 3: + case 4: + case 8: + gen_update_cc_op(s); + gen_jmp_im(s, pc_start - s->cs_base); + if (b & 2) { + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_op_mov_v_reg(s, ot, s->T0, rm); + gen_helper_write_crN(cpu_env, tcg_const_i32(reg), + s->T0); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } else { + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_read_crN(s->T0, cpu_env, tcg_const_i32(reg)); + gen_op_mov_reg_v(s, ot, rm, s->T0); + if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { + gen_jmp(s, s->pc - s->cs_base); + } + } + break; + default: + goto unknown_op; + } + } + break; + case 0x121: /* mov reg, drN */ + case 0x123: /* mov drN, reg */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + modrm = x86_ldub_code(env, s); + /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). + * AMD documentation (24594.pdf) and testing of + * intel 386 and 486 processors all show that the mod bits + * are assumed to be 1's, regardless of actual values. + */ + rm = (modrm & 7) | REX_B(s); + reg = ((modrm >> 3) & 7) | rex_r; + if (CODE64(s)) + ot = MO_64; + else + ot = MO_32; + if (reg >= 8) { + goto illegal_op; + } + if (b & 2) { + gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); + gen_op_mov_v_reg(s, ot, s->T0, rm); + tcg_gen_movi_i32(s->tmp2_i32, reg); + gen_helper_set_dr(cpu_env, s->tmp2_i32, s->T0); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } else { + gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); + tcg_gen_movi_i32(s->tmp2_i32, reg); + gen_helper_get_dr(s->T0, cpu_env, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, rm, s->T0); + } + } + break; + case 0x106: /* clts */ + if (s->cpl != 0) { + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); + } else { + gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); + gen_helper_clts(cpu_env); + /* abort block because static cpu state changed */ + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + } + break; + /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ + case 0x1c3: /* MOVNTI reg, mem */ + if (!(s->cpuid_features & CPUID_SSE2)) + goto illegal_op; + ot = mo_64_32(dflag); + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + reg = ((modrm >> 3) & 7) | rex_r; + /* generate a generic store */ + gen_ldst_modrm(env, s, modrm, ot, reg, 1); + break; + case 0x1ae: + modrm = x86_ldub_code(env, s); + switch (modrm) { + CASE_MODRM_MEM_OP(0): /* fxsave */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_lea_modrm(env, s, modrm); + gen_helper_fxsave(cpu_env, s->A0); + break; + + CASE_MODRM_MEM_OP(1): /* fxrstor */ + if (!(s->cpuid_features & CPUID_FXSR) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_lea_modrm(env, s, modrm); + gen_helper_fxrstor(cpu_env, s->A0); + break; + + CASE_MODRM_MEM_OP(2): /* ldmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { + goto illegal_op; + } + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); + break; + + CASE_MODRM_MEM_OP(3): /* stmxcsr */ + if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { + goto illegal_op; + } + if (s->flags & HF_TS_MASK) { + gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); + break; + } + gen_helper_update_mxcsr(cpu_env); + gen_lea_modrm(env, s, modrm); + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); + gen_op_st_v(s, MO_32, s->T0, s->A0); + break; + + CASE_MODRM_MEM_OP(4): /* xsave */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsave(cpu_env, s->A0, s->tmp1_i64); + break; + + CASE_MODRM_MEM_OP(5): /* xrstor */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (prefixes & (PREFIX_LOCK | PREFIX_DATA + | PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xrstor(cpu_env, s->A0, s->tmp1_i64); + /* XRSTOR is how MPX is enabled, which changes how + we translate. Thus we need to end the TB. */ + gen_update_cc_op(s); + gen_jmp_im(s, s->pc - s->cs_base); + gen_eob(s); + break; + + CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { + /* clwb */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { + goto illegal_op; + } + gen_nop_modrm(env, s, modrm); + } else { + /* xsaveopt */ + if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 + || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 + || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { + goto illegal_op; + } + gen_lea_modrm(env, s, modrm); + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], + cpu_regs[R_EDX]); + gen_helper_xsaveopt(cpu_env, s->A0, s->tmp1_i64); + } + break; + + CASE_MODRM_MEM_OP(7): /* clflush / clflushopt */ + if (prefixes & PREFIX_LOCK) { + goto illegal_op; + } + if (prefixes & PREFIX_DATA) { + /* clflushopt */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { + goto illegal_op; + } + } else { + /* clflush */ + if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) + || !(s->cpuid_features & CPUID_CLFLUSH)) { + goto illegal_op; + } + } + gen_nop_modrm(env, s, modrm); + break; + + case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ + case 0xc8 ... 0xcf: /* rdgsbase (f3 0f ae /1) */ + case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ + case 0xd8 ... 0xdf: /* wrgsbase (f3 0f ae /3) */ + if (CODE64(s) + && (prefixes & PREFIX_REPZ) + && !(prefixes & PREFIX_LOCK) + && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { + TCGv base, treg, src, dst; + + /* Preserve hflags bits by testing CR4 at runtime. */ + tcg_gen_movi_i32(s->tmp2_i32, CR4_FSGSBASE_MASK); + gen_helper_cr4_testbit(cpu_env, s->tmp2_i32); + + base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; + treg = cpu_regs[(modrm & 7) | REX_B(s)]; + + if (modrm & 0x10) { + /* wr*base */ + dst = base, src = treg; + } else { + /* rd*base */ + dst = treg, src = base; + } + + if (s->dflag == MO_32) { + tcg_gen_ext32u_tl(dst, src); + } else { + tcg_gen_mov_tl(dst, src); + } + break; + } + goto unknown_op; + + case 0xf8: /* sfence / pcommit */ + if (prefixes & PREFIX_DATA) { + /* pcommit */ + if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + break; + } + /* fallthru */ + case 0xf9 ... 0xff: /* sfence */ + if (!(s->cpuid_features & CPUID_SSE) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); + break; + case 0xe8 ... 0xef: /* lfence */ + if (!(s->cpuid_features & CPUID_SSE) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); + break; + case 0xf0 ... 0xf7: /* mfence */ + if (!(s->cpuid_features & CPUID_SSE2) + || (prefixes & PREFIX_LOCK)) { + goto illegal_op; + } + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + break; + + default: + goto unknown_op; + } + break; + + case 0x10d: /* 3DNow! prefetch(w) */ + modrm = x86_ldub_code(env, s); + mod = (modrm >> 6) & 3; + if (mod == 3) + goto illegal_op; + gen_nop_modrm(env, s, modrm); + break; + case 0x1aa: /* rsm */ + gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); + if (!(s->flags & HF_SMM_MASK)) + goto illegal_op; + gen_update_cc_op(s); + gen_jmp_im(s, s->pc - s->cs_base); + gen_helper_rsm(cpu_env); + gen_eob(s); + break; + case 0x1b8: /* SSE4.2 popcnt */ + if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != + PREFIX_REPZ) + goto illegal_op; + if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) + goto illegal_op; + + modrm = x86_ldub_code(env, s); + reg = ((modrm >> 3) & 7) | rex_r; + + if (s->prefix & PREFIX_DATA) { + ot = MO_16; + } else { + ot = mo_64_32(dflag); + } + + gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); + gen_extu(ot, s->T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); + tcg_gen_ctpop_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + + set_cc_op(s, CC_OP_POPCNT); + break; + case 0x10e ... 0x10f: + /* 3DNow! instructions, ignore prefixes */ + s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); + /* fall through */ + case 0x110 ... 0x117: + case 0x128 ... 0x12f: + case 0x138 ... 0x13a: + case 0x150 ... 0x179: + case 0x17c ... 0x17f: + case 0x1c2: + case 0x1c4 ... 0x1c6: + case 0x1d0 ... 0x1fe: + gen_sse(env, s, b, pc_start, rex_r); + break; + default: + goto unknown_op; + } + return s->pc; + illegal_op: + gen_illegal_opcode(s); + return s->pc; + unknown_op: + gen_unknown_opcode(env, s); + return s->pc; +} + +void tcg_x86_init(void) +{ + static const char reg_names[CPU_NB_REGS][4] = { +#ifdef TARGET_X86_64 + [R_EAX] = "rax", + [R_EBX] = "rbx", + [R_ECX] = "rcx", + [R_EDX] = "rdx", + [R_ESI] = "rsi", + [R_EDI] = "rdi", + [R_EBP] = "rbp", + [R_ESP] = "rsp", + [8] = "r8", + [9] = "r9", + [10] = "r10", + [11] = "r11", + [12] = "r12", + [13] = "r13", + [14] = "r14", + [15] = "r15", +#else + [R_EAX] = "eax", + [R_EBX] = "ebx", + [R_ECX] = "ecx", + [R_EDX] = "edx", + [R_ESI] = "esi", + [R_EDI] = "edi", + [R_EBP] = "ebp", + [R_ESP] = "esp", +#endif + }; + static const char seg_base_names[6][8] = { + [R_CS] = "cs_base", + [R_DS] = "ds_base", + [R_ES] = "es_base", + [R_FS] = "fs_base", + [R_GS] = "gs_base", + [R_SS] = "ss_base", + }; + static const char bnd_regl_names[4][8] = { + "bnd0_lb", "bnd1_lb", "bnd2_lb", "bnd3_lb" + }; + static const char bnd_regu_names[4][8] = { + "bnd0_ub", "bnd1_ub", "bnd2_ub", "bnd3_ub" + }; + int i; + + cpu_cc_op = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUX86State, cc_op), "cc_op"); + cpu_cc_dst = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_dst), + "cc_dst"); + cpu_cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src), + "cc_src"); + cpu_cc_src2 = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src2), + "cc_src2"); + + for (i = 0; i < CPU_NB_REGS; ++i) { + cpu_regs[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUX86State, regs[i]), + reg_names[i]); + } + + for (i = 0; i < 6; ++i) { + cpu_seg_base[i] + = tcg_global_mem_new(cpu_env, + offsetof(CPUX86State, segs[i].base), + seg_base_names[i]); + } + + for (i = 0; i < 4; ++i) { + cpu_bndl[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].lb), + bnd_regl_names[i]); + cpu_bndu[i] + = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUX86State, bnd_regs[i].ub), + bnd_regu_names[i]); + } +} + +static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + CPUX86State *env = cpu->env_ptr; + uint32_t flags = dc->base.tb->flags; + target_ulong cs_base = dc->base.tb->cs_base; + + dc->pe = (flags >> HF_PE_SHIFT) & 1; + dc->code32 = (flags >> HF_CS32_SHIFT) & 1; + dc->ss32 = (flags >> HF_SS32_SHIFT) & 1; + dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1; + dc->f_st = 0; + dc->vm86 = (flags >> VM_SHIFT) & 1; + dc->cpl = (flags >> HF_CPL_SHIFT) & 3; + dc->iopl = (flags >> IOPL_SHIFT) & 3; + dc->tf = (flags >> TF_SHIFT) & 1; + dc->cc_op = CC_OP_DYNAMIC; + dc->cc_op_dirty = false; + dc->cs_base = cs_base; + dc->popl_esp_hack = 0; + /* select memory access functions */ + dc->mem_index = 0; +#ifdef CONFIG_SOFTMMU + dc->mem_index = cpu_mmu_index(env, false); +#endif + dc->cpuid_features = env->features[FEAT_1_EDX]; + dc->cpuid_ext_features = env->features[FEAT_1_ECX]; + dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; + dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; + dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; + dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; +#ifdef TARGET_X86_64 + dc->lma = (flags >> HF_LMA_SHIFT) & 1; + dc->code64 = (flags >> HF_CS64_SHIFT) & 1; +#endif + dc->flags = flags; + dc->jmp_opt = !(dc->tf || dc->base.singlestep_enabled || + (flags & HF_INHIBIT_IRQ_MASK)); + /* Do not optimize repz jumps at all in icount mode, because + rep movsS instructions are execured with different paths + in !repz_opt and repz_opt modes. The first one was used + always except single step mode. And this setting + disables jumps optimization and control paths become + equivalent in run and single step modes. + Now there will be no jump optimization for repz in + record/replay modes and there will always be an + additional step for ecx=0 when icount is enabled. + */ + dc->repz_opt = !dc->jmp_opt && !(tb_cflags(dc->base.tb) & CF_USE_ICOUNT); +#if 0 + /* check addseg logic */ + if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32)) + printf("ERROR addseg\n"); +#endif + + dc->T0 = tcg_temp_new(); + dc->T1 = tcg_temp_new(); + dc->A0 = tcg_temp_new(); + + dc->tmp0 = tcg_temp_new(); + dc->tmp1_i64 = tcg_temp_new_i64(); + dc->tmp2_i32 = tcg_temp_new_i32(); + dc->tmp3_i32 = tcg_temp_new_i32(); + dc->tmp4 = tcg_temp_new(); + dc->ptr0 = tcg_temp_new_ptr(); + dc->ptr1 = tcg_temp_new_ptr(); + dc->cc_srcT = tcg_temp_local_new(); +} + +static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) +{ +} + +static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); +} + +static bool i386_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, + const CPUBreakpoint *bp) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + /* If RF is set, suppress an internally generated breakpoint. */ + int flags = dc->base.tb->flags & HF_RF_MASK ? BP_GDB : BP_ANY; + if (bp->flags & flags) { + gen_debug(dc, dc->base.pc_next - dc->cs_base); + dc->base.is_jmp = DISAS_NORETURN; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the generic logic setting tb->size later does the right thing. */ + dc->base.pc_next += 1; + return true; + } else { + return false; + } +} + +static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + target_ulong pc_next; + +#ifdef TARGET_VSYSCALL_PAGE + /* + * Detect entry into the vsyscall page and invoke the syscall. + */ + if ((dc->base.pc_next & TARGET_PAGE_MASK) == TARGET_VSYSCALL_PAGE) { + gen_exception(dc, EXCP_VSYSCALL, dc->base.pc_next); + return; + } +#endif + + pc_next = disas_insn(dc, cpu); + + if (dc->tf || (dc->base.tb->flags & HF_INHIBIT_IRQ_MASK)) { + /* if single step mode, we generate only one instruction and + generate an exception */ + /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear + the flag and abort the translation to give the irqs a + chance to happen */ + dc->base.is_jmp = DISAS_TOO_MANY; + } else if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) + && ((pc_next & TARGET_PAGE_MASK) + != ((pc_next + TARGET_MAX_INSN_SIZE - 1) + & TARGET_PAGE_MASK) + || (pc_next & ~TARGET_PAGE_MASK) == 0)) { + /* Do not cross the boundary of the pages in icount mode, + it can cause an exception. Do it only when boundary is + crossed by the first instruction in the block. + If current instruction already crossed the bound - it's ok, + because an exception hasn't stopped this code. + */ + dc->base.is_jmp = DISAS_TOO_MANY; + } else if ((pc_next - dc->base.pc_first) >= (TARGET_PAGE_SIZE - 32)) { + dc->base.is_jmp = DISAS_TOO_MANY; + } + + dc->base.pc_next = pc_next; +} + +static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + if (dc->base.is_jmp == DISAS_TOO_MANY) { + gen_jmp_im(dc, dc->base.pc_next - dc->cs_base); + gen_eob(dc); + } +} + +static void i386_tr_disas_log(const DisasContextBase *dcbase, + CPUState *cpu) +{ + DisasContext *dc = container_of(dcbase, DisasContext, base); + + qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); + log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); +} + +static const TranslatorOps i386_tr_ops = { + .init_disas_context = i386_tr_init_disas_context, + .tb_start = i386_tr_tb_start, + .insn_start = i386_tr_insn_start, + .breakpoint_check = i386_tr_breakpoint_check, + .translate_insn = i386_tr_translate_insn, + .tb_stop = i386_tr_tb_stop, + .disas_log = i386_tr_disas_log, +}; + +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + + translator_loop(&i386_tr_ops, &dc.base, cpu, tb, max_insns); +} + +void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, + target_ulong *data) +{ + int cc_op = data[1]; + env->eip = data[0] - tb->cs_base; + if (cc_op != CC_OP_DYNAMIC) { + env->cc_op = cc_op; + } +} diff --git a/target/i386/translate.c b/target/i386/translate.c deleted file mode 100644 index e8f5f5803a..0000000000 --- a/target/i386/translate.c +++ /dev/null @@ -1,8642 +0,0 @@ -/* - * i386 translation - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#include "qemu/osdep.h" - -#include "qemu/host-utils.h" -#include "cpu.h" -#include "disas/disas.h" -#include "exec/exec-all.h" -#include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" -#include "exec/translator.h" - -#include "exec/helper-proto.h" -#include "exec/helper-gen.h" - -#include "trace-tcg.h" -#include "exec/log.h" - -#define PREFIX_REPZ 0x01 -#define PREFIX_REPNZ 0x02 -#define PREFIX_LOCK 0x04 -#define PREFIX_DATA 0x08 -#define PREFIX_ADR 0x10 -#define PREFIX_VEX 0x20 - -#ifdef TARGET_X86_64 -#define CODE64(s) ((s)->code64) -#define REX_X(s) ((s)->rex_x) -#define REX_B(s) ((s)->rex_b) -#else -#define CODE64(s) 0 -#define REX_X(s) 0 -#define REX_B(s) 0 -#endif - -#ifdef TARGET_X86_64 -# define ctztl ctz64 -# define clztl clz64 -#else -# define ctztl ctz32 -# define clztl clz32 -#endif - -/* For a switch indexed by MODRM, match all memory operands for a given OP. */ -#define CASE_MODRM_MEM_OP(OP) \ - case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ - case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ - case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7 - -#define CASE_MODRM_OP(OP) \ - case (0 << 6) | (OP << 3) | 0 ... (0 << 6) | (OP << 3) | 7: \ - case (1 << 6) | (OP << 3) | 0 ... (1 << 6) | (OP << 3) | 7: \ - case (2 << 6) | (OP << 3) | 0 ... (2 << 6) | (OP << 3) | 7: \ - case (3 << 6) | (OP << 3) | 0 ... (3 << 6) | (OP << 3) | 7 - -//#define MACRO_TEST 1 - -/* global register indexes */ -static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2; -static TCGv_i32 cpu_cc_op; -static TCGv cpu_regs[CPU_NB_REGS]; -static TCGv cpu_seg_base[6]; -static TCGv_i64 cpu_bndl[4]; -static TCGv_i64 cpu_bndu[4]; - -#include "exec/gen-icount.h" - -typedef struct DisasContext { - DisasContextBase base; - - /* current insn context */ - int override; /* -1 if no override */ - int prefix; - MemOp aflag; - MemOp dflag; - target_ulong pc_start; - target_ulong pc; /* pc = eip + cs_base */ - /* current block context */ - target_ulong cs_base; /* base of CS segment */ - int pe; /* protected mode */ - int code32; /* 32 bit code segment */ -#ifdef TARGET_X86_64 - int lma; /* long mode active */ - int code64; /* 64 bit code segment */ - int rex_x, rex_b; -#endif - int vex_l; /* vex vector length */ - int vex_v; /* vex vvvv register, without 1's complement. */ - int ss32; /* 32 bit stack segment */ - CCOp cc_op; /* current CC operation */ - bool cc_op_dirty; -#ifdef TARGET_X86_64 - bool x86_64_hregs; -#endif - int addseg; /* non zero if either DS/ES/SS have a non zero base */ - int f_st; /* currently unused */ - int vm86; /* vm86 mode */ - int cpl; - int iopl; - int tf; /* TF cpu flag */ - int jmp_opt; /* use direct block chaining for direct jumps */ - int repz_opt; /* optimize jumps within repz instructions */ - int mem_index; /* select memory access functions */ - uint64_t flags; /* all execution flags */ - int popl_esp_hack; /* for correct popl with esp base handling */ - int rip_offset; /* only used in x86_64, but left for simplicity */ - int cpuid_features; - int cpuid_ext_features; - int cpuid_ext2_features; - int cpuid_ext3_features; - int cpuid_7_0_ebx_features; - int cpuid_xsave_features; - - /* TCG local temps */ - TCGv cc_srcT; - TCGv A0; - TCGv T0; - TCGv T1; - - /* TCG local register indexes (only used inside old micro ops) */ - TCGv tmp0; - TCGv tmp4; - TCGv_ptr ptr0; - TCGv_ptr ptr1; - TCGv_i32 tmp2_i32; - TCGv_i32 tmp3_i32; - TCGv_i64 tmp1_i64; - - sigjmp_buf jmpbuf; -} DisasContext; - -static void gen_eob(DisasContext *s); -static void gen_jr(DisasContext *s, TCGv dest); -static void gen_jmp(DisasContext *s, target_ulong eip); -static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num); -static void gen_op(DisasContext *s1, int op, MemOp ot, int d); - -/* i386 arith/logic operations */ -enum { - OP_ADDL, - OP_ORL, - OP_ADCL, - OP_SBBL, - OP_ANDL, - OP_SUBL, - OP_XORL, - OP_CMPL, -}; - -/* i386 shift ops */ -enum { - OP_ROL, - OP_ROR, - OP_RCL, - OP_RCR, - OP_SHL, - OP_SHR, - OP_SHL1, /* undocumented */ - OP_SAR = 7, -}; - -enum { - JCC_O, - JCC_B, - JCC_Z, - JCC_BE, - JCC_S, - JCC_P, - JCC_L, - JCC_LE, -}; - -enum { - /* I386 int registers */ - OR_EAX, /* MUST be even numbered */ - OR_ECX, - OR_EDX, - OR_EBX, - OR_ESP, - OR_EBP, - OR_ESI, - OR_EDI, - - OR_TMP0 = 16, /* temporary operand register */ - OR_TMP1, - OR_A0, /* temporary register used when doing address evaluation */ -}; - -enum { - USES_CC_DST = 1, - USES_CC_SRC = 2, - USES_CC_SRC2 = 4, - USES_CC_SRCT = 8, -}; - -/* Bit set if the global variable is live after setting CC_OP to X. */ -static const uint8_t cc_op_live[CC_OP_NB] = { - [CC_OP_DYNAMIC] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, - [CC_OP_EFLAGS] = USES_CC_SRC, - [CC_OP_MULB ... CC_OP_MULQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_ADDB ... CC_OP_ADDQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_ADCB ... CC_OP_ADCQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, - [CC_OP_SUBB ... CC_OP_SUBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRCT, - [CC_OP_SBBB ... CC_OP_SBBQ] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, - [CC_OP_LOGICB ... CC_OP_LOGICQ] = USES_CC_DST, - [CC_OP_INCB ... CC_OP_INCQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_DECB ... CC_OP_DECQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_SHLB ... CC_OP_SHLQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_SARB ... CC_OP_SARQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_BMILGB ... CC_OP_BMILGQ] = USES_CC_DST | USES_CC_SRC, - [CC_OP_ADCX] = USES_CC_DST | USES_CC_SRC, - [CC_OP_ADOX] = USES_CC_SRC | USES_CC_SRC2, - [CC_OP_ADCOX] = USES_CC_DST | USES_CC_SRC | USES_CC_SRC2, - [CC_OP_CLR] = 0, - [CC_OP_POPCNT] = USES_CC_SRC, -}; - -static void set_cc_op(DisasContext *s, CCOp op) -{ - int dead; - - if (s->cc_op == op) { - return; - } - - /* Discard CC computation that will no longer be used. */ - dead = cc_op_live[s->cc_op] & ~cc_op_live[op]; - if (dead & USES_CC_DST) { - tcg_gen_discard_tl(cpu_cc_dst); - } - if (dead & USES_CC_SRC) { - tcg_gen_discard_tl(cpu_cc_src); - } - if (dead & USES_CC_SRC2) { - tcg_gen_discard_tl(cpu_cc_src2); - } - if (dead & USES_CC_SRCT) { - tcg_gen_discard_tl(s->cc_srcT); - } - - if (op == CC_OP_DYNAMIC) { - /* The DYNAMIC setting is translator only, and should never be - stored. Thus we always consider it clean. */ - s->cc_op_dirty = false; - } else { - /* Discard any computed CC_OP value (see shifts). */ - if (s->cc_op == CC_OP_DYNAMIC) { - tcg_gen_discard_i32(cpu_cc_op); - } - s->cc_op_dirty = true; - } - s->cc_op = op; -} - -static void gen_update_cc_op(DisasContext *s) -{ - if (s->cc_op_dirty) { - tcg_gen_movi_i32(cpu_cc_op, s->cc_op); - s->cc_op_dirty = false; - } -} - -#ifdef TARGET_X86_64 - -#define NB_OP_SIZES 4 - -#else /* !TARGET_X86_64 */ - -#define NB_OP_SIZES 3 - -#endif /* !TARGET_X86_64 */ - -#if defined(HOST_WORDS_BIGENDIAN) -#define REG_B_OFFSET (sizeof(target_ulong) - 1) -#define REG_H_OFFSET (sizeof(target_ulong) - 2) -#define REG_W_OFFSET (sizeof(target_ulong) - 2) -#define REG_L_OFFSET (sizeof(target_ulong) - 4) -#define REG_LH_OFFSET (sizeof(target_ulong) - 8) -#else -#define REG_B_OFFSET 0 -#define REG_H_OFFSET 1 -#define REG_W_OFFSET 0 -#define REG_L_OFFSET 0 -#define REG_LH_OFFSET 4 -#endif - -/* In instruction encodings for byte register accesses the - * register number usually indicates "low 8 bits of register N"; - * however there are some special cases where N 4..7 indicates - * [AH, CH, DH, BH], ie "bits 15..8 of register N-4". Return - * true for this special case, false otherwise. - */ -static inline bool byte_reg_is_xH(DisasContext *s, int reg) -{ - if (reg < 4) { - return false; - } -#ifdef TARGET_X86_64 - if (reg >= 8 || s->x86_64_hregs) { - return false; - } -#endif - return true; -} - -/* Select the size of a push/pop operation. */ -static inline MemOp mo_pushpop(DisasContext *s, MemOp ot) -{ - if (CODE64(s)) { - return ot == MO_16 ? MO_16 : MO_64; - } else { - return ot; - } -} - -/* Select the size of the stack pointer. */ -static inline MemOp mo_stacksize(DisasContext *s) -{ - return CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; -} - -/* Select only size 64 else 32. Used for SSE operand sizes. */ -static inline MemOp mo_64_32(MemOp ot) -{ -#ifdef TARGET_X86_64 - return ot == MO_64 ? MO_64 : MO_32; -#else - return MO_32; -#endif -} - -/* Select size 8 if lsb of B is clear, else OT. Used for decoding - byte vs word opcodes. */ -static inline MemOp mo_b_d(int b, MemOp ot) -{ - return b & 1 ? ot : MO_8; -} - -/* Select size 8 if lsb of B is clear, else OT capped at 32. - Used for decoding operand size of port opcodes. */ -static inline MemOp mo_b_d32(int b, MemOp ot) -{ - return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; -} - -static void gen_op_mov_reg_v(DisasContext *s, MemOp ot, int reg, TCGv t0) -{ - switch(ot) { - case MO_8: - if (!byte_reg_is_xH(s, reg)) { - tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); - } else { - tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); - } - break; - case MO_16: - tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 16); - break; - case MO_32: - /* For x86_64, this sets the higher half of register to zero. - For i386, this is equivalent to a mov. */ - tcg_gen_ext32u_tl(cpu_regs[reg], t0); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mov_tl(cpu_regs[reg], t0); - break; -#endif - default: - tcg_abort(); - } -} - -static inline -void gen_op_mov_v_reg(DisasContext *s, MemOp ot, TCGv t0, int reg) -{ - if (ot == MO_8 && byte_reg_is_xH(s, reg)) { - tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); - } else { - tcg_gen_mov_tl(t0, cpu_regs[reg]); - } -} - -static void gen_add_A0_im(DisasContext *s, int val) -{ - tcg_gen_addi_tl(s->A0, s->A0, val); - if (!CODE64(s)) { - tcg_gen_ext32u_tl(s->A0, s->A0); - } -} - -static inline void gen_op_jmp_v(TCGv dest) -{ - tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip)); -} - -static inline -void gen_op_add_reg_im(DisasContext *s, MemOp size, int reg, int32_t val) -{ - tcg_gen_addi_tl(s->tmp0, cpu_regs[reg], val); - gen_op_mov_reg_v(s, size, reg, s->tmp0); -} - -static inline void gen_op_add_reg_T0(DisasContext *s, MemOp size, int reg) -{ - tcg_gen_add_tl(s->tmp0, cpu_regs[reg], s->T0); - gen_op_mov_reg_v(s, size, reg, s->tmp0); -} - -static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) -{ - tcg_gen_qemu_ld_tl(t0, a0, s->mem_index, idx | MO_LE); -} - -static inline void gen_op_st_v(DisasContext *s, int idx, TCGv t0, TCGv a0) -{ - tcg_gen_qemu_st_tl(t0, a0, s->mem_index, idx | MO_LE); -} - -static inline void gen_op_st_rm_T0_A0(DisasContext *s, int idx, int d) -{ - if (d == OR_TMP0) { - gen_op_st_v(s, idx, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, idx, d, s->T0); - } -} - -static inline void gen_jmp_im(DisasContext *s, target_ulong pc) -{ - tcg_gen_movi_tl(s->tmp0, pc); - gen_op_jmp_v(s->tmp0); -} - -/* Compute SEG:REG into A0. SEG is selected from the override segment - (OVR_SEG) and the default segment (DEF_SEG). OVR_SEG may be -1 to - indicate no override. */ -static void gen_lea_v_seg(DisasContext *s, MemOp aflag, TCGv a0, - int def_seg, int ovr_seg) -{ - switch (aflag) { -#ifdef TARGET_X86_64 - case MO_64: - if (ovr_seg < 0) { - tcg_gen_mov_tl(s->A0, a0); - return; - } - break; -#endif - case MO_32: - /* 32 bit address */ - if (ovr_seg < 0 && s->addseg) { - ovr_seg = def_seg; - } - if (ovr_seg < 0) { - tcg_gen_ext32u_tl(s->A0, a0); - return; - } - break; - case MO_16: - /* 16 bit address */ - tcg_gen_ext16u_tl(s->A0, a0); - a0 = s->A0; - if (ovr_seg < 0) { - if (s->addseg) { - ovr_seg = def_seg; - } else { - return; - } - } - break; - default: - tcg_abort(); - } - - if (ovr_seg >= 0) { - TCGv seg = cpu_seg_base[ovr_seg]; - - if (aflag == MO_64) { - tcg_gen_add_tl(s->A0, a0, seg); - } else if (CODE64(s)) { - tcg_gen_ext32u_tl(s->A0, a0); - tcg_gen_add_tl(s->A0, s->A0, seg); - } else { - tcg_gen_add_tl(s->A0, a0, seg); - tcg_gen_ext32u_tl(s->A0, s->A0); - } - } -} - -static inline void gen_string_movl_A0_ESI(DisasContext *s) -{ - gen_lea_v_seg(s, s->aflag, cpu_regs[R_ESI], R_DS, s->override); -} - -static inline void gen_string_movl_A0_EDI(DisasContext *s) -{ - gen_lea_v_seg(s, s->aflag, cpu_regs[R_EDI], R_ES, -1); -} - -static inline void gen_op_movl_T0_Dshift(DisasContext *s, MemOp ot) -{ - tcg_gen_ld32s_tl(s->T0, cpu_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(s->T0, s->T0, ot); -}; - -static TCGv gen_ext_tl(TCGv dst, TCGv src, MemOp size, bool sign) -{ - switch (size) { - case MO_8: - if (sign) { - tcg_gen_ext8s_tl(dst, src); - } else { - tcg_gen_ext8u_tl(dst, src); - } - return dst; - case MO_16: - if (sign) { - tcg_gen_ext16s_tl(dst, src); - } else { - tcg_gen_ext16u_tl(dst, src); - } - return dst; -#ifdef TARGET_X86_64 - case MO_32: - if (sign) { - tcg_gen_ext32s_tl(dst, src); - } else { - tcg_gen_ext32u_tl(dst, src); - } - return dst; -#endif - default: - return src; - } -} - -static void gen_extu(MemOp ot, TCGv reg) -{ - gen_ext_tl(reg, reg, ot, false); -} - -static void gen_exts(MemOp ot, TCGv reg) -{ - gen_ext_tl(reg, reg, ot, true); -} - -static inline -void gen_op_jnz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) -{ - tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); - gen_extu(size, s->tmp0); - tcg_gen_brcondi_tl(TCG_COND_NE, s->tmp0, 0, label1); -} - -static inline -void gen_op_jz_ecx(DisasContext *s, MemOp size, TCGLabel *label1) -{ - tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); - gen_extu(size, s->tmp0); - tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); -} - -static void gen_helper_in_func(MemOp ot, TCGv v, TCGv_i32 n) -{ - switch (ot) { - case MO_8: - gen_helper_inb(v, cpu_env, n); - break; - case MO_16: - gen_helper_inw(v, cpu_env, n); - break; - case MO_32: - gen_helper_inl(v, cpu_env, n); - break; - default: - tcg_abort(); - } -} - -static void gen_helper_out_func(MemOp ot, TCGv_i32 v, TCGv_i32 n) -{ - switch (ot) { - case MO_8: - gen_helper_outb(cpu_env, v, n); - break; - case MO_16: - gen_helper_outw(cpu_env, v, n); - break; - case MO_32: - gen_helper_outl(cpu_env, v, n); - break; - default: - tcg_abort(); - } -} - -static void gen_check_io(DisasContext *s, MemOp ot, target_ulong cur_eip, - uint32_t svm_flags) -{ - target_ulong next_eip; - - if (s->pe && (s->cpl > s->iopl || s->vm86)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - switch (ot) { - case MO_8: - gen_helper_check_iob(cpu_env, s->tmp2_i32); - break; - case MO_16: - gen_helper_check_iow(cpu_env, s->tmp2_i32); - break; - case MO_32: - gen_helper_check_iol(cpu_env, s->tmp2_i32); - break; - default: - tcg_abort(); - } - } - if(s->flags & HF_GUEST_MASK) { - gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - svm_flags |= (1 << (4 + ot)); - next_eip = s->pc - s->cs_base; - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_svm_check_io(cpu_env, s->tmp2_i32, - tcg_const_i32(svm_flags), - tcg_const_i32(next_eip - cur_eip)); - } -} - -static inline void gen_movs(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_string_movl_A0_EDI(s); - gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); -} - -static void gen_op_update1_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static void gen_op_update2_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static void gen_op_update3_cc(DisasContext *s, TCGv reg) -{ - tcg_gen_mov_tl(cpu_cc_src2, reg); - tcg_gen_mov_tl(cpu_cc_src, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); -} - -static inline void gen_op_testl_T0_T1_cc(DisasContext *s) -{ - tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1); -} - -static void gen_op_update_neg_cc(DisasContext *s) -{ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_neg_tl(cpu_cc_src, s->T0); - tcg_gen_movi_tl(s->cc_srcT, 0); -} - -/* compute all eflags to cc_src */ -static void gen_compute_eflags(DisasContext *s) -{ - TCGv zero, dst, src1, src2; - int live, dead; - - if (s->cc_op == CC_OP_EFLAGS) { - return; - } - if (s->cc_op == CC_OP_CLR) { - tcg_gen_movi_tl(cpu_cc_src, CC_Z | CC_P); - set_cc_op(s, CC_OP_EFLAGS); - return; - } - - zero = NULL; - dst = cpu_cc_dst; - src1 = cpu_cc_src; - src2 = cpu_cc_src2; - - /* Take care to not read values that are not live. */ - live = cc_op_live[s->cc_op] & ~USES_CC_SRCT; - dead = live ^ (USES_CC_DST | USES_CC_SRC | USES_CC_SRC2); - if (dead) { - zero = tcg_const_tl(0); - if (dead & USES_CC_DST) { - dst = zero; - } - if (dead & USES_CC_SRC) { - src1 = zero; - } - if (dead & USES_CC_SRC2) { - src2 = zero; - } - } - - gen_update_cc_op(s); - gen_helper_cc_compute_all(cpu_cc_src, dst, src1, src2, cpu_cc_op); - set_cc_op(s, CC_OP_EFLAGS); - - if (dead) { - tcg_temp_free(zero); - } -} - -typedef struct CCPrepare { - TCGCond cond; - TCGv reg; - TCGv reg2; - target_ulong imm; - target_ulong mask; - bool use_reg2; - bool no_setcond; -} CCPrepare; - -/* compute eflags.C to reg */ -static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) -{ - TCGv t0, t1; - int size, shift; - - switch (s->cc_op) { - case CC_OP_SUBB ... CC_OP_SUBQ: - /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ - size = s->cc_op - CC_OP_SUBB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - /* If no temporary was used, be careful not to alias t1 and t0. */ - t0 = t1 == cpu_cc_src ? s->tmp0 : reg; - tcg_gen_mov_tl(t0, s->cc_srcT); - gen_extu(size, t0); - goto add_sub; - - case CC_OP_ADDB ... CC_OP_ADDQ: - /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ - size = s->cc_op - CC_OP_ADDB; - t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - add_sub: - return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, - .reg2 = t1, .mask = -1, .use_reg2 = true }; - - case CC_OP_LOGICB ... CC_OP_LOGICQ: - case CC_OP_CLR: - case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; - - case CC_OP_INCB ... CC_OP_INCQ: - case CC_OP_DECB ... CC_OP_DECQ: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = -1, .no_setcond = true }; - - case CC_OP_SHLB ... CC_OP_SHLQ: - /* (CC_SRC >> (DATA_BITS - 1)) & 1 */ - size = s->cc_op - CC_OP_SHLB; - shift = (8 << size) - 1; - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = (target_ulong)1 << shift }; - - case CC_OP_MULB ... CC_OP_MULQ: - return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = -1 }; - - case CC_OP_BMILGB ... CC_OP_BMILGQ: - size = s->cc_op - CC_OP_BMILGB; - t0 = gen_ext_tl(reg, cpu_cc_src, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; - - case CC_OP_ADCX: - case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_dst, - .mask = -1, .no_setcond = true }; - - case CC_OP_EFLAGS: - case CC_OP_SARB ... CC_OP_SARQ: - /* CC_SRC & 1 */ - return (CCPrepare) { .cond = TCG_COND_NE, - .reg = cpu_cc_src, .mask = CC_C }; - - default: - /* The need to compute only C from CC_OP_DYNAMIC is important - in efficiently implementing e.g. INC at the start of a TB. */ - gen_update_cc_op(s); - gen_helper_cc_compute_c(reg, cpu_cc_dst, cpu_cc_src, - cpu_cc_src2, cpu_cc_op); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = -1, .no_setcond = true }; - } -} - -/* compute eflags.P to reg */ -static CCPrepare gen_prepare_eflags_p(DisasContext *s, TCGv reg) -{ - gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_P }; -} - -/* compute eflags.S to reg */ -static CCPrepare gen_prepare_eflags_s(DisasContext *s, TCGv reg) -{ - switch (s->cc_op) { - case CC_OP_DYNAMIC: - gen_compute_eflags(s); - /* FALLTHRU */ - case CC_OP_EFLAGS: - case CC_OP_ADCX: - case CC_OP_ADOX: - case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_S }; - case CC_OP_CLR: - case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; - default: - { - MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, true); - return (CCPrepare) { .cond = TCG_COND_LT, .reg = t0, .mask = -1 }; - } - } -} - -/* compute eflags.O to reg */ -static CCPrepare gen_prepare_eflags_o(DisasContext *s, TCGv reg) -{ - switch (s->cc_op) { - case CC_OP_ADOX: - case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src2, - .mask = -1, .no_setcond = true }; - case CC_OP_CLR: - case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_NEVER, .mask = -1 }; - default: - gen_compute_eflags(s); - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_O }; - } -} - -/* compute eflags.Z to reg */ -static CCPrepare gen_prepare_eflags_z(DisasContext *s, TCGv reg) -{ - switch (s->cc_op) { - case CC_OP_DYNAMIC: - gen_compute_eflags(s); - /* FALLTHRU */ - case CC_OP_EFLAGS: - case CC_OP_ADCX: - case CC_OP_ADOX: - case CC_OP_ADCOX: - return (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z }; - case CC_OP_CLR: - return (CCPrepare) { .cond = TCG_COND_ALWAYS, .mask = -1 }; - case CC_OP_POPCNT: - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = cpu_cc_src, - .mask = -1 }; - default: - { - MemOp size = (s->cc_op - CC_OP_ADDB) & 3; - TCGv t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); - return (CCPrepare) { .cond = TCG_COND_EQ, .reg = t0, .mask = -1 }; - } - } -} - -/* perform a conditional store into register 'reg' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. */ -static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) -{ - int inv, jcc_op, cond; - MemOp size; - CCPrepare cc; - TCGv t0; - - inv = b & 1; - jcc_op = (b >> 1) & 7; - - switch (s->cc_op) { - case CC_OP_SUBB ... CC_OP_SUBQ: - /* We optimize relational operators for the cmp/jcc case. */ - size = s->cc_op - CC_OP_SUBB; - switch (jcc_op) { - case JCC_BE: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_extu(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); - cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; - break; - - case JCC_L: - cond = TCG_COND_LT; - goto fast_jcc_l; - case JCC_LE: - cond = TCG_COND_LE; - fast_jcc_l: - tcg_gen_mov_tl(s->tmp4, s->cc_srcT); - gen_exts(size, s->tmp4); - t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); - cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, - .reg2 = t0, .mask = -1, .use_reg2 = true }; - break; - - default: - goto slow_jcc; - } - break; - - default: - slow_jcc: - /* This actually generates good code for JC, JZ and JS. */ - switch (jcc_op) { - case JCC_O: - cc = gen_prepare_eflags_o(s, reg); - break; - case JCC_B: - cc = gen_prepare_eflags_c(s, reg); - break; - case JCC_Z: - cc = gen_prepare_eflags_z(s, reg); - break; - case JCC_BE: - gen_compute_eflags(s); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = cpu_cc_src, - .mask = CC_Z | CC_C }; - break; - case JCC_S: - cc = gen_prepare_eflags_s(s, reg); - break; - case JCC_P: - cc = gen_prepare_eflags_p(s, reg); - break; - case JCC_L: - gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; - } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S }; - break; - default: - case JCC_LE: - gen_compute_eflags(s); - if (reg == cpu_cc_src) { - reg = s->tmp0; - } - tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ - tcg_gen_xor_tl(reg, reg, cpu_cc_src); - cc = (CCPrepare) { .cond = TCG_COND_NE, .reg = reg, - .mask = CC_S | CC_Z }; - break; - } - break; - } - - if (inv) { - cc.cond = tcg_invert_cond(cc.cond); - } - return cc; -} - -static void gen_setcc1(DisasContext *s, int b, TCGv reg) -{ - CCPrepare cc = gen_prepare_cc(s, b, reg); - - if (cc.no_setcond) { - if (cc.cond == TCG_COND_EQ) { - tcg_gen_xori_tl(reg, cc.reg, 1); - } else { - tcg_gen_mov_tl(reg, cc.reg); - } - return; - } - - if (cc.cond == TCG_COND_NE && !cc.use_reg2 && cc.imm == 0 && - cc.mask != 0 && (cc.mask & (cc.mask - 1)) == 0) { - tcg_gen_shri_tl(reg, cc.reg, ctztl(cc.mask)); - tcg_gen_andi_tl(reg, reg, 1); - return; - } - if (cc.mask != -1) { - tcg_gen_andi_tl(reg, cc.reg, cc.mask); - cc.reg = reg; - } - if (cc.use_reg2) { - tcg_gen_setcond_tl(cc.cond, reg, cc.reg, cc.reg2); - } else { - tcg_gen_setcondi_tl(cc.cond, reg, cc.reg, cc.imm); - } -} - -static inline void gen_compute_eflags_c(DisasContext *s, TCGv reg) -{ - gen_setcc1(s, JCC_B << 1, reg); -} - -/* generate a conditional jump to label 'l1' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. */ -static inline void gen_jcc1_noeob(DisasContext *s, int b, TCGLabel *l1) -{ - CCPrepare cc = gen_prepare_cc(s, b, s->T0); - - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } - if (cc.use_reg2) { - tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); - } else { - tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); - } -} - -/* Generate a conditional jump to label 'l1' according to jump opcode - value 'b'. In the fast case, T0 is guaranted not to be used. - A translation block must end soon. */ -static inline void gen_jcc1(DisasContext *s, int b, TCGLabel *l1) -{ - CCPrepare cc = gen_prepare_cc(s, b, s->T0); - - gen_update_cc_op(s); - if (cc.mask != -1) { - tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); - cc.reg = s->T0; - } - set_cc_op(s, CC_OP_DYNAMIC); - if (cc.use_reg2) { - tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); - } else { - tcg_gen_brcondi_tl(cc.cond, cc.reg, cc.imm, l1); - } -} - -/* XXX: does not work with gdbstub "ice" single step - not a - serious problem */ -static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) -{ - TCGLabel *l1 = gen_new_label(); - TCGLabel *l2 = gen_new_label(); - gen_op_jnz_ecx(s, s->aflag, l1); - gen_set_label(l2); - gen_jmp_tb(s, next_eip, 1); - gen_set_label(l1); - return l2; -} - -static inline void gen_stos(DisasContext *s, MemOp ot) -{ - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - gen_string_movl_A0_EDI(s); - gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); -} - -static inline void gen_lods(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); -} - -static inline void gen_scas(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_op(s, OP_CMPL, ot, R_EAX); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); -} - -static inline void gen_cmps(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_string_movl_A0_ESI(s); - gen_op(s, OP_CMPL, ot, OR_TMP0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_op_add_reg_T0(s, s->aflag, R_EDI); -} - -static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) -{ - if (s->flags & HF_IOBPT_MASK) { - TCGv_i32 t_size = tcg_const_i32(1 << ot); - TCGv t_next = tcg_const_tl(s->pc - s->cs_base); - - gen_helper_bpt_io(cpu_env, t_port, t_size, t_next); - tcg_temp_free_i32(t_size); - tcg_temp_free(t_next); - } -} - - -static inline void gen_ins(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_EDI(s); - /* Note: we must do this dummy write first to be restartable in - case of page fault. */ - tcg_gen_movi_tl(s->T0, 0); - gen_op_st_v(s, ot, s->T0, s->A0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); - gen_helper_in_func(ot, s->T0, s->tmp2_i32); - gen_op_st_v(s, ot, s->T0, s->A0); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_EDI); - gen_bpt_io(s, s->tmp2_i32, ot); -} - -static inline void gen_outs(DisasContext *s, MemOp ot) -{ - gen_string_movl_A0_ESI(s); - gen_op_ld_v(s, ot, s->T0, s->A0); - - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_op_movl_T0_Dshift(s, ot); - gen_op_add_reg_T0(s, s->aflag, R_ESI); - gen_bpt_io(s, s->tmp2_i32, ot); -} - -/* same method as Valgrind : we generate jumps to current or next - instruction */ -#define GEN_REPZ(op) \ -static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ - target_ulong cur_eip, target_ulong next_eip) \ -{ \ - TCGLabel *l2; \ - gen_update_cc_op(s); \ - l2 = gen_jz_ecx_string(s, next_eip); \ - gen_ ## op(s, ot); \ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ - /* a loop would cause two single step exceptions if ECX = 1 \ - before rep string_insn */ \ - if (s->repz_opt) \ - gen_op_jz_ecx(s, s->aflag, l2); \ - gen_jmp(s, cur_eip); \ -} - -#define GEN_REPZ2(op) \ -static inline void gen_repz_ ## op(DisasContext *s, MemOp ot, \ - target_ulong cur_eip, \ - target_ulong next_eip, \ - int nz) \ -{ \ - TCGLabel *l2; \ - gen_update_cc_op(s); \ - l2 = gen_jz_ecx_string(s, next_eip); \ - gen_ ## op(s, ot); \ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ - gen_update_cc_op(s); \ - gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ - if (s->repz_opt) \ - gen_op_jz_ecx(s, s->aflag, l2); \ - gen_jmp(s, cur_eip); \ -} - -GEN_REPZ(movs) -GEN_REPZ(stos) -GEN_REPZ(lods) -GEN_REPZ(ins) -GEN_REPZ(outs) -GEN_REPZ2(scas) -GEN_REPZ2(cmps) - -static void gen_helper_fp_arith_ST0_FT0(int op) -{ - switch (op) { - case 0: - gen_helper_fadd_ST0_FT0(cpu_env); - break; - case 1: - gen_helper_fmul_ST0_FT0(cpu_env); - break; - case 2: - gen_helper_fcom_ST0_FT0(cpu_env); - break; - case 3: - gen_helper_fcom_ST0_FT0(cpu_env); - break; - case 4: - gen_helper_fsub_ST0_FT0(cpu_env); - break; - case 5: - gen_helper_fsubr_ST0_FT0(cpu_env); - break; - case 6: - gen_helper_fdiv_ST0_FT0(cpu_env); - break; - case 7: - gen_helper_fdivr_ST0_FT0(cpu_env); - break; - } -} - -/* NOTE the exception in "r" op ordering */ -static void gen_helper_fp_arith_STN_ST0(int op, int opreg) -{ - TCGv_i32 tmp = tcg_const_i32(opreg); - switch (op) { - case 0: - gen_helper_fadd_STN_ST0(cpu_env, tmp); - break; - case 1: - gen_helper_fmul_STN_ST0(cpu_env, tmp); - break; - case 4: - gen_helper_fsubr_STN_ST0(cpu_env, tmp); - break; - case 5: - gen_helper_fsub_STN_ST0(cpu_env, tmp); - break; - case 6: - gen_helper_fdivr_STN_ST0(cpu_env, tmp); - break; - case 7: - gen_helper_fdiv_STN_ST0(cpu_env, tmp); - break; - } -} - -static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) -{ - gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); - s->base.is_jmp = DISAS_NORETURN; -} - -/* Generate #UD for the current instruction. The assumption here is that - the instruction is known, but it isn't allowed in the current cpu mode. */ -static void gen_illegal_opcode(DisasContext *s) -{ - gen_exception(s, EXCP06_ILLOP, s->pc_start - s->cs_base); -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_op(DisasContext *s1, int op, MemOp ot, int d) -{ - if (d != OR_TMP0) { - if (s1->prefix & PREFIX_LOCK) { - /* Lock prefix when destination is not memory. */ - gen_illegal_opcode(s1); - return; - } - gen_op_mov_v_reg(s1, ot, s1->T0, d); - } else if (!(s1->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s1, ot, s1->T0, s1->A0); - } - switch(op) { - case OP_ADCL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_ADCB + ot); - break; - case OP_SBBL: - gen_compute_eflags_c(s1, s1->tmp4); - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4); - tcg_gen_neg_tl(s1->T0, s1->T0); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update3_cc(s1, s1->tmp4); - set_cc_op(s1, CC_OP_SBBB + ot); - break; - case OP_ADDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_ADDB + ot); - break; - case OP_SUBL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_neg_tl(s1->T0, s1->T1); - tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1); - } else { - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update2_cc(s1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - default: - case OP_ANDL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_and_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_ORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_or_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_XORL: - if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1, - s1->mem_index, ot | MO_LE); - } else { - tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1); - gen_op_st_rm_T0_A0(s1, ot, d); - } - gen_op_update1_cc(s1); - set_cc_op(s1, CC_OP_LOGICB + ot); - break; - case OP_CMPL: - tcg_gen_mov_tl(cpu_cc_src, s1->T1); - tcg_gen_mov_tl(s1->cc_srcT, s1->T0); - tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1); - set_cc_op(s1, CC_OP_SUBB + ot); - break; - } -} - -/* if d == OR_TMP0, it means memory operand (address in A0) */ -static void gen_inc(DisasContext *s1, MemOp ot, int d, int c) -{ - if (s1->prefix & PREFIX_LOCK) { - if (d != OR_TMP0) { - /* Lock prefix when destination is not memory */ - gen_illegal_opcode(s1); - return; - } - tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1); - tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, - s1->mem_index, ot | MO_LE); - } else { - if (d != OR_TMP0) { - gen_op_mov_v_reg(s1, ot, s1->T0, d); - } else { - gen_op_ld_v(s1, ot, s1->T0, s1->A0); - } - tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1)); - gen_op_st_rm_T0_A0(s1, ot, d); - } - - gen_compute_eflags_c(s1, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, s1->T0); - set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); -} - -static void gen_shift_flags(DisasContext *s, MemOp ot, TCGv result, - TCGv shm1, TCGv count, bool is_right) -{ - TCGv_i32 z32, s32, oldop; - TCGv z_tl; - - /* Store the results into the CC variables. If we know that the - variable must be dead, store unconditionally. Otherwise we'll - need to not disrupt the current contents. */ - z_tl = tcg_const_tl(0); - if (cc_op_live[s->cc_op] & USES_CC_DST) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_dst, count, z_tl, - result, cpu_cc_dst); - } else { - tcg_gen_mov_tl(cpu_cc_dst, result); - } - if (cc_op_live[s->cc_op] & USES_CC_SRC) { - tcg_gen_movcond_tl(TCG_COND_NE, cpu_cc_src, count, z_tl, - shm1, cpu_cc_src); - } else { - tcg_gen_mov_tl(cpu_cc_src, shm1); - } - tcg_temp_free(z_tl); - - /* Get the two potential CC_OP values into temporaries. */ - tcg_gen_movi_i32(s->tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); - if (s->cc_op == CC_OP_DYNAMIC) { - oldop = cpu_cc_op; - } else { - tcg_gen_movi_i32(s->tmp3_i32, s->cc_op); - oldop = s->tmp3_i32; - } - - /* Conditionally store the CC_OP value. */ - z32 = tcg_const_i32(0); - s32 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(s32, count); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); - tcg_temp_free_i32(z32); - tcg_temp_free_i32(s32); - - /* The CC_OP value is no longer predictable. */ - set_cc_op(s, CC_OP_DYNAMIC); -} - -static void gen_shift_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right, int is_arith) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - tcg_gen_subi_tl(s->tmp0, s->T1, 1); - - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_sar_tl(s->T0, s->T0, s->T1); - } else { - gen_extu(ot, s->T0); - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shr_tl(s->T0, s->T0, s->T1); - } - } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_shl_tl(s->T0, s->T0, s->T1); - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right); -} - -static void gen_shift_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right, int is_arith) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - - /* load */ - if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, s->T0, s->A0); - else - gen_op_mov_v_reg(s, ot, s->T0, op1); - - op2 &= mask; - if (op2 != 0) { - if (is_right) { - if (is_arith) { - gen_exts(ot, s->T0); - tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_sari_tl(s->T0, s->T0, op2); - } else { - gen_extu(ot, s->T0); - tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shri_tl(s->T0, s->T0, op2); - } - } else { - tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1); - tcg_gen_shli_tl(s->T0, s->T0, op2); - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* update eflags if non zero shift */ - if (op2 != 0) { - tcg_gen_mov_tl(cpu_cc_src, s->tmp4); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); - } -} - -static void gen_rot_rm_T1(DisasContext *s, MemOp ot, int op1, int is_right) -{ - target_ulong mask = (ot == MO_64 ? 0x3f : 0x1f); - TCGv_i32 t0, t1; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - tcg_gen_andi_tl(s->T1, s->T1, mask); - - switch (ot) { - case MO_8: - /* Replicate the 8-bit input so that a 32-bit rotate works. */ - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_muli_tl(s->T0, s->T0, 0x01010101); - goto do_long; - case MO_16: - /* Replicate the 16-bit input so that a 32-bit rotate works. */ - tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16); - goto do_long; - do_long: -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - if (is_right) { - tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } else { - tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotr_tl(s->T0, s->T0, s->T1); - } else { - tcg_gen_rotl_tl(s->T0, s->T0, s->T1); - } - break; - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - /* We'll need the flags computed into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - - /* Now conditionally store the new CC_OP value. If the shift count - is 0 we keep the CC_OP_EFLAGS setting so that only CC_SRC is live. - Otherwise reuse CC_OP_ADCOX which have the C and O flags split out - exactly as we computed above. */ - t0 = tcg_const_i32(0); - t1 = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(t1, s->T1); - tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); - tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, - s->tmp2_i32, s->tmp3_i32); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - - /* The CC_OP value is no longer predictable. */ - set_cc_op(s, CC_OP_DYNAMIC); -} - -static void gen_rot_rm_im(DisasContext *s, MemOp ot, int op1, int op2, - int is_right) -{ - int mask = (ot == MO_64 ? 0x3f : 0x1f); - int shift; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - op2 &= mask; - if (op2 != 0) { - switch (ot) { -#ifdef TARGET_X86_64 - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - if (is_right) { - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2); - } else { - tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2); - } - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; -#endif - default: - if (is_right) { - tcg_gen_rotri_tl(s->T0, s->T0, op2); - } else { - tcg_gen_rotli_tl(s->T0, s->T0, op2); - } - break; - case MO_8: - mask = 7; - goto do_shifts; - case MO_16: - mask = 15; - do_shifts: - shift = op2 & mask; - if (is_right) { - shift = mask + 1 - shift; - } - gen_extu(ot, s->T0); - tcg_gen_shli_tl(s->tmp0, s->T0, shift); - tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift); - tcg_gen_or_tl(s->T0, s->T0, s->tmp0); - break; - } - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - if (op2 != 0) { - /* Compute the flags into CC_SRC. */ - gen_compute_eflags(s); - - /* The value that was "rotated out" is now present at the other end - of the word. Compute C into CC_DST and O into CC_SRC2. Note that - since we've computed the flags into CC_SRC, these variables are - currently dead. */ - if (is_right) { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); - } else { - tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); - } - tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); - tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); - set_cc_op(s, CC_OP_ADCOX); - } -} - -/* XXX: add faster immediate = 1 case */ -static void gen_rotc_rm_T1(DisasContext *s, MemOp ot, int op1, - int is_right) -{ - gen_compute_eflags(s); - assert(s->cc_op == CC_OP_EFLAGS); - - /* load */ - if (op1 == OR_TMP0) - gen_op_ld_v(s, ot, s->T0, s->A0); - else - gen_op_mov_v_reg(s, ot, s->T0, op1); - - if (is_right) { - switch (ot) { - case MO_8: - gen_helper_rcrb(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_16: - gen_helper_rcrw(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcrl(s->T0, cpu_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rcrq(s->T0, cpu_env, s->T0, s->T1); - break; -#endif - default: - tcg_abort(); - } - } else { - switch (ot) { - case MO_8: - gen_helper_rclb(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_16: - gen_helper_rclw(s->T0, cpu_env, s->T0, s->T1); - break; - case MO_32: - gen_helper_rcll(s->T0, cpu_env, s->T0, s->T1); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_rclq(s->T0, cpu_env, s->T0, s->T1); - break; -#endif - default: - tcg_abort(); - } - } - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); -} - -/* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, int op1, - bool is_right, TCGv count_in) -{ - target_ulong mask = (ot == MO_64 ? 63 : 31); - TCGv count; - - /* load */ - if (op1 == OR_TMP0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, op1); - } - - count = tcg_temp_new(); - tcg_gen_andi_tl(count, count_in, mask); - - switch (ot) { - case MO_16: - /* Note: we implement the Intel behaviour for shift count > 16. - This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A - portion by constructing it as a 32-bit value. */ - if (is_right) { - tcg_gen_deposit_tl(s->tmp0, s->T0, s->T1, 16, 16); - tcg_gen_mov_tl(s->T1, s->T0); - tcg_gen_mov_tl(s->T0, s->tmp0); - } else { - tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 16, 16); - } - /* FALLTHRU */ -#ifdef TARGET_X86_64 - case MO_32: - /* Concatenate the two 32-bit values and use a 64-bit shift. */ - tcg_gen_subi_tl(s->tmp0, count, 1); - if (is_right) { - tcg_gen_concat_tl_i64(s->T0, s->T0, s->T1); - tcg_gen_shr_i64(s->tmp0, s->T0, s->tmp0); - tcg_gen_shr_i64(s->T0, s->T0, count); - } else { - tcg_gen_concat_tl_i64(s->T0, s->T1, s->T0); - tcg_gen_shl_i64(s->tmp0, s->T0, s->tmp0); - tcg_gen_shl_i64(s->T0, s->T0, count); - tcg_gen_shri_i64(s->tmp0, s->tmp0, 32); - tcg_gen_shri_i64(s->T0, s->T0, 32); - } - break; -#endif - default: - tcg_gen_subi_tl(s->tmp0, count, 1); - if (is_right) { - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); - - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); - tcg_gen_shr_tl(s->T0, s->T0, count); - tcg_gen_shl_tl(s->T1, s->T1, s->tmp4); - } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); - if (ot == MO_16) { - /* Only needed if count > 16, for Intel behaviour. */ - tcg_gen_subfi_tl(s->tmp4, 33, count); - tcg_gen_shr_tl(s->tmp4, s->T1, s->tmp4); - tcg_gen_or_tl(s->tmp0, s->tmp0, s->tmp4); - } - - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); - tcg_gen_shl_tl(s->T0, s->T0, count); - tcg_gen_shr_tl(s->T1, s->T1, s->tmp4); - } - tcg_gen_movi_tl(s->tmp4, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, s->tmp4, - s->tmp4, s->T1); - tcg_gen_or_tl(s->T0, s->T0, s->T1); - break; - } - - /* store */ - gen_op_st_rm_T0_A0(s, ot, op1); - - gen_shift_flags(s, ot, s->T0, s->tmp0, count, is_right); - tcg_temp_free(count); -} - -static void gen_shift(DisasContext *s1, int op, MemOp ot, int d, int s) -{ - if (s != OR_TMP1) - gen_op_mov_v_reg(s1, ot, s1->T1, s); - switch(op) { - case OP_ROL: - gen_rot_rm_T1(s1, ot, d, 0); - break; - case OP_ROR: - gen_rot_rm_T1(s1, ot, d, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_T1(s1, ot, d, 0, 0); - break; - case OP_SHR: - gen_shift_rm_T1(s1, ot, d, 1, 0); - break; - case OP_SAR: - gen_shift_rm_T1(s1, ot, d, 1, 1); - break; - case OP_RCL: - gen_rotc_rm_T1(s1, ot, d, 0); - break; - case OP_RCR: - gen_rotc_rm_T1(s1, ot, d, 1); - break; - } -} - -static void gen_shifti(DisasContext *s1, int op, MemOp ot, int d, int c) -{ - switch(op) { - case OP_ROL: - gen_rot_rm_im(s1, ot, d, c, 0); - break; - case OP_ROR: - gen_rot_rm_im(s1, ot, d, c, 1); - break; - case OP_SHL: - case OP_SHL1: - gen_shift_rm_im(s1, ot, d, c, 0, 0); - break; - case OP_SHR: - gen_shift_rm_im(s1, ot, d, c, 1, 0); - break; - case OP_SAR: - gen_shift_rm_im(s1, ot, d, c, 1, 1); - break; - default: - /* currently not optimized */ - tcg_gen_movi_tl(s1->T1, c); - gen_shift(s1, op, ot, d, OR_TMP1); - break; - } -} - -#define X86_MAX_INSN_LENGTH 15 - -static uint64_t advance_pc(CPUX86State *env, DisasContext *s, int num_bytes) -{ - uint64_t pc = s->pc; - - s->pc += num_bytes; - if (unlikely(s->pc - s->pc_start > X86_MAX_INSN_LENGTH)) { - /* If the instruction's 16th byte is on a different page than the 1st, a - * page fault on the second page wins over the general protection fault - * caused by the instruction being too long. - * This can happen even if the operand is only one byte long! - */ - if (((s->pc - 1) ^ (pc - 1)) & TARGET_PAGE_MASK) { - volatile uint8_t unused = - cpu_ldub_code(env, (s->pc - 1) & TARGET_PAGE_MASK); - (void) unused; - } - siglongjmp(s->jmpbuf, 1); - } - - return pc; -} - -static inline uint8_t x86_ldub_code(CPUX86State *env, DisasContext *s) -{ - return translator_ldub(env, advance_pc(env, s, 1)); -} - -static inline int16_t x86_ldsw_code(CPUX86State *env, DisasContext *s) -{ - return translator_ldsw(env, advance_pc(env, s, 2)); -} - -static inline uint16_t x86_lduw_code(CPUX86State *env, DisasContext *s) -{ - return translator_lduw(env, advance_pc(env, s, 2)); -} - -static inline uint32_t x86_ldl_code(CPUX86State *env, DisasContext *s) -{ - return translator_ldl(env, advance_pc(env, s, 4)); -} - -#ifdef TARGET_X86_64 -static inline uint64_t x86_ldq_code(CPUX86State *env, DisasContext *s) -{ - return translator_ldq(env, advance_pc(env, s, 8)); -} -#endif - -/* Decompose an address. */ - -typedef struct AddressParts { - int def_seg; - int base; - int index; - int scale; - target_long disp; -} AddressParts; - -static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, - int modrm) -{ - int def_seg, base, index, scale, mod, rm; - target_long disp; - bool havesib; - - def_seg = R_DS; - index = -1; - scale = 0; - disp = 0; - - mod = (modrm >> 6) & 3; - rm = modrm & 7; - base = rm | REX_B(s); - - if (mod == 3) { - /* Normally filtered out earlier, but including this path - simplifies multi-byte nop, as well as bndcl, bndcu, bndcn. */ - goto done; - } - - switch (s->aflag) { - case MO_64: - case MO_32: - havesib = 0; - if (rm == 4) { - int code = x86_ldub_code(env, s); - scale = (code >> 6) & 3; - index = ((code >> 3) & 7) | REX_X(s); - if (index == 4) { - index = -1; /* no index */ - } - base = (code & 7) | REX_B(s); - havesib = 1; - } - - switch (mod) { - case 0: - if ((base & 7) == 5) { - base = -1; - disp = (int32_t)x86_ldl_code(env, s); - if (CODE64(s) && !havesib) { - base = -2; - disp += s->pc + s->rip_offset; - } - } - break; - case 1: - disp = (int8_t)x86_ldub_code(env, s); - break; - default: - case 2: - disp = (int32_t)x86_ldl_code(env, s); - break; - } - - /* For correct popl handling with esp. */ - if (base == R_ESP && s->popl_esp_hack) { - disp += s->popl_esp_hack; - } - if (base == R_EBP || base == R_ESP) { - def_seg = R_SS; - } - break; - - case MO_16: - if (mod == 0) { - if (rm == 6) { - base = -1; - disp = x86_lduw_code(env, s); - break; - } - } else if (mod == 1) { - disp = (int8_t)x86_ldub_code(env, s); - } else { - disp = (int16_t)x86_lduw_code(env, s); - } - - switch (rm) { - case 0: - base = R_EBX; - index = R_ESI; - break; - case 1: - base = R_EBX; - index = R_EDI; - break; - case 2: - base = R_EBP; - index = R_ESI; - def_seg = R_SS; - break; - case 3: - base = R_EBP; - index = R_EDI; - def_seg = R_SS; - break; - case 4: - base = R_ESI; - break; - case 5: - base = R_EDI; - break; - case 6: - base = R_EBP; - def_seg = R_SS; - break; - default: - case 7: - base = R_EBX; - break; - } - break; - - default: - tcg_abort(); - } - - done: - return (AddressParts){ def_seg, base, index, scale, disp }; -} - -/* Compute the address, with a minimum number of TCG ops. */ -static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) -{ - TCGv ea = NULL; - - if (a.index >= 0) { - if (a.scale == 0) { - ea = cpu_regs[a.index]; - } else { - tcg_gen_shli_tl(s->A0, cpu_regs[a.index], a.scale); - ea = s->A0; - } - if (a.base >= 0) { - tcg_gen_add_tl(s->A0, ea, cpu_regs[a.base]); - ea = s->A0; - } - } else if (a.base >= 0) { - ea = cpu_regs[a.base]; - } - if (!ea) { - tcg_gen_movi_tl(s->A0, a.disp); - ea = s->A0; - } else if (a.disp != 0) { - tcg_gen_addi_tl(s->A0, ea, a.disp); - ea = s->A0; - } - - return ea; -} - -static void gen_lea_modrm(CPUX86State *env, DisasContext *s, int modrm) -{ - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a); - gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); -} - -static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) -{ - (void)gen_lea_modrm_0(env, s, modrm); -} - -/* Used for BNDCL, BNDCU, BNDCN. */ -static void gen_bndck(CPUX86State *env, DisasContext *s, int modrm, - TCGCond cond, TCGv_i64 bndv) -{ - TCGv ea = gen_lea_modrm_1(s, gen_lea_modrm_0(env, s, modrm)); - - tcg_gen_extu_tl_i64(s->tmp1_i64, ea); - if (!CODE64(s)) { - tcg_gen_ext32u_i64(s->tmp1_i64, s->tmp1_i64); - } - tcg_gen_setcond_i64(cond, s->tmp1_i64, s->tmp1_i64, bndv); - tcg_gen_extrl_i64_i32(s->tmp2_i32, s->tmp1_i64); - gen_helper_bndck(cpu_env, s->tmp2_i32); -} - -/* used for LEA and MOV AX, mem */ -static void gen_add_A0_ds_seg(DisasContext *s) -{ - gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); -} - -/* generate modrm memory load or store of 'reg'. TMP0 is used if reg == - OR_TMP0 */ -static void gen_ldst_modrm(CPUX86State *env, DisasContext *s, int modrm, - MemOp ot, int reg, int is_store) -{ - int mod, rm; - - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod == 3) { - if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); - } - } else { - gen_lea_modrm(env, s, modrm); - if (is_store) { - if (reg != OR_TMP0) - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_ld_v(s, ot, s->T0, s->A0); - if (reg != OR_TMP0) - gen_op_mov_reg_v(s, ot, reg, s->T0); - } - } -} - -static inline uint32_t insn_get(CPUX86State *env, DisasContext *s, MemOp ot) -{ - uint32_t ret; - - switch (ot) { - case MO_8: - ret = x86_ldub_code(env, s); - break; - case MO_16: - ret = x86_lduw_code(env, s); - break; - case MO_32: -#ifdef TARGET_X86_64 - case MO_64: -#endif - ret = x86_ldl_code(env, s); - break; - default: - tcg_abort(); - } - return ret; -} - -static inline int insn_const_size(MemOp ot) -{ - if (ot <= MO_32) { - return 1 << ot; - } else { - return 4; - } -} - -static inline bool use_goto_tb(DisasContext *s, target_ulong pc) -{ -#ifndef CONFIG_USER_ONLY - return (pc & TARGET_PAGE_MASK) == (s->base.tb->pc & TARGET_PAGE_MASK) || - (pc & TARGET_PAGE_MASK) == (s->pc_start & TARGET_PAGE_MASK); -#else - return true; -#endif -} - -static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) -{ - target_ulong pc = s->cs_base + eip; - - if (use_goto_tb(s, pc)) { - /* jump to same page: we can use a direct jump */ - tcg_gen_goto_tb(tb_num); - gen_jmp_im(s, eip); - tcg_gen_exit_tb(s->base.tb, tb_num); - s->base.is_jmp = DISAS_NORETURN; - } else { - /* jump to another page */ - gen_jmp_im(s, eip); - gen_jr(s, s->tmp0); - } -} - -static inline void gen_jcc(DisasContext *s, int b, - target_ulong val, target_ulong next_eip) -{ - TCGLabel *l1, *l2; - - if (s->jmp_opt) { - l1 = gen_new_label(); - gen_jcc1(s, b, l1); - - gen_goto_tb(s, 0, next_eip); - - gen_set_label(l1); - gen_goto_tb(s, 1, val); - } else { - l1 = gen_new_label(); - l2 = gen_new_label(); - gen_jcc1(s, b, l1); - - gen_jmp_im(s, next_eip); - tcg_gen_br(l2); - - gen_set_label(l1); - gen_jmp_im(s, val); - gen_set_label(l2); - gen_eob(s); - } -} - -static void gen_cmovcc1(CPUX86State *env, DisasContext *s, MemOp ot, int b, - int modrm, int reg) -{ - CCPrepare cc; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - cc = gen_prepare_cc(s, b, s->T1); - if (cc.mask != -1) { - TCGv t0 = tcg_temp_new(); - tcg_gen_andi_tl(t0, cc.reg, cc.mask); - cc.reg = t0; - } - if (!cc.use_reg2) { - cc.reg2 = tcg_const_tl(cc.imm); - } - - tcg_gen_movcond_tl(cc.cond, s->T0, cc.reg, cc.reg2, - s->T0, cpu_regs[reg]); - gen_op_mov_reg_v(s, ot, reg, s->T0); - - if (cc.mask != -1) { - tcg_temp_free(cc.reg); - } - if (!cc.use_reg2) { - tcg_temp_free(cc.reg2); - } -} - -static inline void gen_op_movl_T0_seg(DisasContext *s, int seg_reg) -{ - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,segs[seg_reg].selector)); -} - -static inline void gen_op_movl_seg_T0_vm(DisasContext *s, int seg_reg) -{ - tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State,segs[seg_reg].selector)); - tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); -} - -/* move T0 to seg_reg and compute if the CPU state may change. Never - call this function with seg_reg == R_CS */ -static void gen_movl_seg_T0(DisasContext *s, int seg_reg) -{ - if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), s->tmp2_i32); - /* abort translation because the addseg value may change or - because ss32 may change. For R_SS, translation must always - stop as a special handling must be done to disable hardware - interrupts for the next instruction */ - if (seg_reg == R_SS || (s->code32 && seg_reg < R_FS)) { - s->base.is_jmp = DISAS_TOO_MANY; - } - } else { - gen_op_movl_seg_T0_vm(s, seg_reg); - if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_TOO_MANY; - } - } -} - -static inline int svm_is_rep(int prefixes) -{ - return ((prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) ? 8 : 0); -} - -static inline void -gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start, - uint32_t type, uint64_t param) -{ - /* no SVM activated; fast case */ - if (likely(!(s->flags & HF_GUEST_MASK))) - return; - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_svm_check_intercept_param(cpu_env, tcg_const_i32(type), - tcg_const_i64(param)); -} - -static inline void -gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type) -{ - gen_svm_check_intercept_param(s, pc_start, type, 0); -} - -static inline void gen_stack_update(DisasContext *s, int addend) -{ - gen_op_add_reg_im(s, mo_stacksize(s), R_ESP, addend); -} - -/* Generate a push. It depends on ss32, addseg and dflag. */ -static void gen_push_v(DisasContext *s, TCGv val) -{ - MemOp d_ot = mo_pushpop(s, s->dflag); - MemOp a_ot = mo_stacksize(s); - int size = 1 << d_ot; - TCGv new_esp = s->A0; - - tcg_gen_subi_tl(s->A0, cpu_regs[R_ESP], size); - - if (!CODE64(s)) { - if (s->addseg) { - new_esp = s->tmp4; - tcg_gen_mov_tl(new_esp, s->A0); - } - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - } - - gen_op_st_v(s, d_ot, val, s->A0); - gen_op_mov_reg_v(s, a_ot, R_ESP, new_esp); -} - -/* two step pop is necessary for precise exceptions */ -static MemOp gen_pop_T0(DisasContext *s) -{ - MemOp d_ot = mo_pushpop(s, s->dflag); - - gen_lea_v_seg(s, mo_stacksize(s), cpu_regs[R_ESP], R_SS, -1); - gen_op_ld_v(s, d_ot, s->T0, s->A0); - - return d_ot; -} - -static inline void gen_pop_update(DisasContext *s, MemOp ot) -{ - gen_stack_update(s, 1 << ot); -} - -static inline void gen_stack_A0(DisasContext *s) -{ - gen_lea_v_seg(s, s->ss32 ? MO_32 : MO_16, cpu_regs[R_ESP], R_SS, -1); -} - -static void gen_pusha(DisasContext *s) -{ - MemOp s_ot = s->ss32 ? MO_32 : MO_16; - MemOp d_ot = s->dflag; - int size = 1 << d_ot; - int i; - - for (i = 0; i < 8; i++) { - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], (i - 8) * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); - gen_op_st_v(s, d_ot, cpu_regs[7 - i], s->A0); - } - - gen_stack_update(s, -8 * size); -} - -static void gen_popa(DisasContext *s) -{ - MemOp s_ot = s->ss32 ? MO_32 : MO_16; - MemOp d_ot = s->dflag; - int size = 1 << d_ot; - int i; - - for (i = 0; i < 8; i++) { - /* ESP is not reloaded */ - if (7 - i == R_ESP) { - continue; - } - tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], i * size); - gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); - gen_op_ld_v(s, d_ot, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, 7 - i, s->T0); - } - - gen_stack_update(s, 8 * size); -} - -static void gen_enter(DisasContext *s, int esp_addend, int level) -{ - MemOp d_ot = mo_pushpop(s, s->dflag); - MemOp a_ot = CODE64(s) ? MO_64 : s->ss32 ? MO_32 : MO_16; - int size = 1 << d_ot; - - /* Push BP; compute FrameTemp into T1. */ - tcg_gen_subi_tl(s->T1, cpu_regs[R_ESP], size); - gen_lea_v_seg(s, a_ot, s->T1, R_SS, -1); - gen_op_st_v(s, d_ot, cpu_regs[R_EBP], s->A0); - - level &= 31; - if (level != 0) { - int i; - - /* Copy level-1 pointers from the previous frame. */ - for (i = 1; i < level; ++i) { - tcg_gen_subi_tl(s->A0, cpu_regs[R_EBP], size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - gen_op_ld_v(s, d_ot, s->tmp0, s->A0); - - tcg_gen_subi_tl(s->A0, s->T1, size * i); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - gen_op_st_v(s, d_ot, s->tmp0, s->A0); - } - - /* Push the current FrameTemp as the last level. */ - tcg_gen_subi_tl(s->A0, s->T1, size * level); - gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); - gen_op_st_v(s, d_ot, s->T1, s->A0); - } - - /* Copy the FrameTemp value to EBP. */ - gen_op_mov_reg_v(s, a_ot, R_EBP, s->T1); - - /* Compute the final value of ESP. */ - tcg_gen_subi_tl(s->T1, s->T1, esp_addend + size * level); - gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); -} - -static void gen_leave(DisasContext *s) -{ - MemOp d_ot = mo_pushpop(s, s->dflag); - MemOp a_ot = mo_stacksize(s); - - gen_lea_v_seg(s, a_ot, cpu_regs[R_EBP], R_SS, -1); - gen_op_ld_v(s, d_ot, s->T0, s->A0); - - tcg_gen_addi_tl(s->T1, cpu_regs[R_EBP], 1 << d_ot); - - gen_op_mov_reg_v(s, d_ot, R_EBP, s->T0); - gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); -} - -/* Similarly, except that the assumption here is that we don't decode - the instruction at all -- either a missing opcode, an unimplemented - feature, or just a bogus instruction stream. */ -static void gen_unknown_opcode(CPUX86State *env, DisasContext *s) -{ - gen_illegal_opcode(s); - - if (qemu_loglevel_mask(LOG_UNIMP)) { - FILE *logfile = qemu_log_lock(); - target_ulong pc = s->pc_start, end = s->pc; - - qemu_log("ILLOPC: " TARGET_FMT_lx ":", pc); - for (; pc < end; ++pc) { - qemu_log(" %02x", cpu_ldub_code(env, pc)); - } - qemu_log("\n"); - qemu_log_unlock(logfile); - } -} - -/* an interrupt is different from an exception because of the - privilege checks */ -static void gen_interrupt(DisasContext *s, int intno, - target_ulong cur_eip, target_ulong next_eip) -{ - gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), - tcg_const_i32(next_eip - cur_eip)); - s->base.is_jmp = DISAS_NORETURN; -} - -static void gen_debug(DisasContext *s, target_ulong cur_eip) -{ - gen_update_cc_op(s); - gen_jmp_im(s, cur_eip); - gen_helper_debug(cpu_env); - s->base.is_jmp = DISAS_NORETURN; -} - -static void gen_set_hflag(DisasContext *s, uint32_t mask) -{ - if ((s->flags & mask) == 0) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_gen_ori_i32(t, t, mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_temp_free_i32(t); - s->flags |= mask; - } -} - -static void gen_reset_hflag(DisasContext *s, uint32_t mask) -{ - if (s->flags & mask) { - TCGv_i32 t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_gen_andi_i32(t, t, ~mask); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUX86State, hflags)); - tcg_temp_free_i32(t); - s->flags &= ~mask; - } -} - -/* Clear BND registers during legacy branches. */ -static void gen_bnd_jmp(DisasContext *s) -{ - /* Clear the registers only if BND prefix is missing, MPX is enabled, - and if the BNDREGs are known to be in use (non-zero) already. - The helper itself will check BNDPRESERVE at runtime. */ - if ((s->prefix & PREFIX_REPNZ) == 0 - && (s->flags & HF_MPX_EN_MASK) != 0 - && (s->flags & HF_MPX_IU_MASK) != 0) { - gen_helper_bnd_jmp(cpu_env); - } -} - -/* Generate an end of block. Trace exception is also generated if needed. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. - If RECHECK_TF, emit a rechecking helper for #DB, ignoring the state of - S->TF. This is used by the syscall/sysret insns. */ -static void -do_gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf, bool jr) -{ - gen_update_cc_op(s); - - /* If several instructions disable interrupts, only the first does it. */ - if (inhibit && !(s->flags & HF_INHIBIT_IRQ_MASK)) { - gen_set_hflag(s, HF_INHIBIT_IRQ_MASK); - } else { - gen_reset_hflag(s, HF_INHIBIT_IRQ_MASK); - } - - if (s->base.tb->flags & HF_RF_MASK) { - gen_helper_reset_rf(cpu_env); - } - if (s->base.singlestep_enabled) { - gen_helper_debug(cpu_env); - } else if (recheck_tf) { - gen_helper_rechecking_single_step(cpu_env); - tcg_gen_exit_tb(NULL, 0); - } else if (s->tf) { - gen_helper_single_step(cpu_env); - } else if (jr) { - tcg_gen_lookup_and_goto_ptr(); - } else { - tcg_gen_exit_tb(NULL, 0); - } - s->base.is_jmp = DISAS_NORETURN; -} - -static inline void -gen_eob_worker(DisasContext *s, bool inhibit, bool recheck_tf) -{ - do_gen_eob_worker(s, inhibit, recheck_tf, false); -} - -/* End of block. - If INHIBIT, set HF_INHIBIT_IRQ_MASK if it isn't already set. */ -static void gen_eob_inhibit_irq(DisasContext *s, bool inhibit) -{ - gen_eob_worker(s, inhibit, false); -} - -/* End of block, resetting the inhibit irq flag. */ -static void gen_eob(DisasContext *s) -{ - gen_eob_worker(s, false, false); -} - -/* Jump to register */ -static void gen_jr(DisasContext *s, TCGv dest) -{ - do_gen_eob_worker(s, false, false, true); -} - -/* generate a jump to eip. No segment change must happen before as a - direct call to the next block may occur */ -static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) -{ - gen_update_cc_op(s); - set_cc_op(s, CC_OP_DYNAMIC); - if (s->jmp_opt) { - gen_goto_tb(s, tb_num, eip); - } else { - gen_jmp_im(s, eip); - gen_eob(s); - } -} - -static void gen_jmp(DisasContext *s, target_ulong eip) -{ - gen_jmp_tb(s, eip, 0); -} - -static inline void gen_ldq_env_A0(DisasContext *s, int offset) -{ - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset); -} - -static inline void gen_stq_env_A0(DisasContext *s, int offset) -{ - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); -} - -static inline void gen_ldo_env_A0(DisasContext *s, int offset) -{ - int mem_index = s->mem_index; - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); -} - -static inline void gen_sto_env_A0(DisasContext *s, int offset) -{ - int mem_index = s->mem_index; - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); - tcg_gen_addi_tl(s->tmp0, s->A0, 8); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); -} - -static inline void gen_op_movo(DisasContext *s, int d_offset, int s_offset) -{ - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(0))); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(1))); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(1))); -} - -static inline void gen_op_movq(DisasContext *s, int d_offset, int s_offset) -{ - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); -} - -static inline void gen_op_movl(DisasContext *s, int d_offset, int s_offset) -{ - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, s_offset); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, d_offset); -} - -static inline void gen_op_movq_env_0(DisasContext *s, int d_offset) -{ - tcg_gen_movi_i64(s->tmp1_i64, 0); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); -} - -typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); -typedef void (*SSEFunc_l_ep)(TCGv_i64 val, TCGv_ptr env, TCGv_ptr reg); -typedef void (*SSEFunc_0_epi)(TCGv_ptr env, TCGv_ptr reg, TCGv_i32 val); -typedef void (*SSEFunc_0_epl)(TCGv_ptr env, TCGv_ptr reg, TCGv_i64 val); -typedef void (*SSEFunc_0_epp)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b); -typedef void (*SSEFunc_0_eppi)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, - TCGv_i32 val); -typedef void (*SSEFunc_0_ppi)(TCGv_ptr reg_a, TCGv_ptr reg_b, TCGv_i32 val); -typedef void (*SSEFunc_0_eppt)(TCGv_ptr env, TCGv_ptr reg_a, TCGv_ptr reg_b, - TCGv val); - -#define SSE_SPECIAL ((void *)1) -#define SSE_DUMMY ((void *)2) - -#define MMX_OP2(x) { gen_helper_ ## x ## _mmx, gen_helper_ ## x ## _xmm } -#define SSE_FOP(x) { gen_helper_ ## x ## ps, gen_helper_ ## x ## pd, \ - gen_helper_ ## x ## ss, gen_helper_ ## x ## sd, } - -static const SSEFunc_0_epp sse_op_table1[256][4] = { - /* 3DNow! extensions */ - [0x0e] = { SSE_DUMMY }, /* femms */ - [0x0f] = { SSE_DUMMY }, /* pf... */ - /* pure SSE operations */ - [0x10] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ - [0x11] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movups, movupd, movss, movsd */ - [0x12] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd, movsldup, movddup */ - [0x13] = { SSE_SPECIAL, SSE_SPECIAL }, /* movlps, movlpd */ - [0x14] = { gen_helper_punpckldq_xmm, gen_helper_punpcklqdq_xmm }, - [0x15] = { gen_helper_punpckhdq_xmm, gen_helper_punpckhqdq_xmm }, - [0x16] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd, movshdup */ - [0x17] = { SSE_SPECIAL, SSE_SPECIAL }, /* movhps, movhpd */ - - [0x28] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ - [0x29] = { SSE_SPECIAL, SSE_SPECIAL }, /* movaps, movapd */ - [0x2a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtpi2ps, cvtpi2pd, cvtsi2ss, cvtsi2sd */ - [0x2b] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movntps, movntpd, movntss, movntsd */ - [0x2c] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvttps2pi, cvttpd2pi, cvttsd2si, cvttss2si */ - [0x2d] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* cvtps2pi, cvtpd2pi, cvtsd2si, cvtss2si */ - [0x2e] = { gen_helper_ucomiss, gen_helper_ucomisd }, - [0x2f] = { gen_helper_comiss, gen_helper_comisd }, - [0x50] = { SSE_SPECIAL, SSE_SPECIAL }, /* movmskps, movmskpd */ - [0x51] = SSE_FOP(sqrt), - [0x52] = { gen_helper_rsqrtps, NULL, gen_helper_rsqrtss, NULL }, - [0x53] = { gen_helper_rcpps, NULL, gen_helper_rcpss, NULL }, - [0x54] = { gen_helper_pand_xmm, gen_helper_pand_xmm }, /* andps, andpd */ - [0x55] = { gen_helper_pandn_xmm, gen_helper_pandn_xmm }, /* andnps, andnpd */ - [0x56] = { gen_helper_por_xmm, gen_helper_por_xmm }, /* orps, orpd */ - [0x57] = { gen_helper_pxor_xmm, gen_helper_pxor_xmm }, /* xorps, xorpd */ - [0x58] = SSE_FOP(add), - [0x59] = SSE_FOP(mul), - [0x5a] = { gen_helper_cvtps2pd, gen_helper_cvtpd2ps, - gen_helper_cvtss2sd, gen_helper_cvtsd2ss }, - [0x5b] = { gen_helper_cvtdq2ps, gen_helper_cvtps2dq, gen_helper_cvttps2dq }, - [0x5c] = SSE_FOP(sub), - [0x5d] = SSE_FOP(min), - [0x5e] = SSE_FOP(div), - [0x5f] = SSE_FOP(max), - - [0xc2] = SSE_FOP(cmpeq), - [0xc6] = { (SSEFunc_0_epp)gen_helper_shufps, - (SSEFunc_0_epp)gen_helper_shufpd }, /* XXX: casts */ - - /* SSSE3, SSE4, MOVBE, CRC32, BMI1, BMI2, ADX. */ - [0x38] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - [0x3a] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - - /* MMX ops and their SSE extensions */ - [0x60] = MMX_OP2(punpcklbw), - [0x61] = MMX_OP2(punpcklwd), - [0x62] = MMX_OP2(punpckldq), - [0x63] = MMX_OP2(packsswb), - [0x64] = MMX_OP2(pcmpgtb), - [0x65] = MMX_OP2(pcmpgtw), - [0x66] = MMX_OP2(pcmpgtl), - [0x67] = MMX_OP2(packuswb), - [0x68] = MMX_OP2(punpckhbw), - [0x69] = MMX_OP2(punpckhwd), - [0x6a] = MMX_OP2(punpckhdq), - [0x6b] = MMX_OP2(packssdw), - [0x6c] = { NULL, gen_helper_punpcklqdq_xmm }, - [0x6d] = { NULL, gen_helper_punpckhqdq_xmm }, - [0x6e] = { SSE_SPECIAL, SSE_SPECIAL }, /* movd mm, ea */ - [0x6f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, , movqdu */ - [0x70] = { (SSEFunc_0_epp)gen_helper_pshufw_mmx, - (SSEFunc_0_epp)gen_helper_pshufd_xmm, - (SSEFunc_0_epp)gen_helper_pshufhw_xmm, - (SSEFunc_0_epp)gen_helper_pshuflw_xmm }, /* XXX: casts */ - [0x71] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftw */ - [0x72] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftd */ - [0x73] = { SSE_SPECIAL, SSE_SPECIAL }, /* shiftq */ - [0x74] = MMX_OP2(pcmpeqb), - [0x75] = MMX_OP2(pcmpeqw), - [0x76] = MMX_OP2(pcmpeql), - [0x77] = { SSE_DUMMY }, /* emms */ - [0x78] = { NULL, SSE_SPECIAL, NULL, SSE_SPECIAL }, /* extrq_i, insertq_i */ - [0x79] = { NULL, gen_helper_extrq_r, NULL, gen_helper_insertq_r }, - [0x7c] = { NULL, gen_helper_haddpd, NULL, gen_helper_haddps }, - [0x7d] = { NULL, gen_helper_hsubpd, NULL, gen_helper_hsubps }, - [0x7e] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movd, movd, , movq */ - [0x7f] = { SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, /* movq, movdqa, movdqu */ - [0xc4] = { SSE_SPECIAL, SSE_SPECIAL }, /* pinsrw */ - [0xc5] = { SSE_SPECIAL, SSE_SPECIAL }, /* pextrw */ - [0xd0] = { NULL, gen_helper_addsubpd, NULL, gen_helper_addsubps }, - [0xd1] = MMX_OP2(psrlw), - [0xd2] = MMX_OP2(psrld), - [0xd3] = MMX_OP2(psrlq), - [0xd4] = MMX_OP2(paddq), - [0xd5] = MMX_OP2(pmullw), - [0xd6] = { NULL, SSE_SPECIAL, SSE_SPECIAL, SSE_SPECIAL }, - [0xd7] = { SSE_SPECIAL, SSE_SPECIAL }, /* pmovmskb */ - [0xd8] = MMX_OP2(psubusb), - [0xd9] = MMX_OP2(psubusw), - [0xda] = MMX_OP2(pminub), - [0xdb] = MMX_OP2(pand), - [0xdc] = MMX_OP2(paddusb), - [0xdd] = MMX_OP2(paddusw), - [0xde] = MMX_OP2(pmaxub), - [0xdf] = MMX_OP2(pandn), - [0xe0] = MMX_OP2(pavgb), - [0xe1] = MMX_OP2(psraw), - [0xe2] = MMX_OP2(psrad), - [0xe3] = MMX_OP2(pavgw), - [0xe4] = MMX_OP2(pmulhuw), - [0xe5] = MMX_OP2(pmulhw), - [0xe6] = { NULL, gen_helper_cvttpd2dq, gen_helper_cvtdq2pd, gen_helper_cvtpd2dq }, - [0xe7] = { SSE_SPECIAL , SSE_SPECIAL }, /* movntq, movntq */ - [0xe8] = MMX_OP2(psubsb), - [0xe9] = MMX_OP2(psubsw), - [0xea] = MMX_OP2(pminsw), - [0xeb] = MMX_OP2(por), - [0xec] = MMX_OP2(paddsb), - [0xed] = MMX_OP2(paddsw), - [0xee] = MMX_OP2(pmaxsw), - [0xef] = MMX_OP2(pxor), - [0xf0] = { NULL, NULL, NULL, SSE_SPECIAL }, /* lddqu */ - [0xf1] = MMX_OP2(psllw), - [0xf2] = MMX_OP2(pslld), - [0xf3] = MMX_OP2(psllq), - [0xf4] = MMX_OP2(pmuludq), - [0xf5] = MMX_OP2(pmaddwd), - [0xf6] = MMX_OP2(psadbw), - [0xf7] = { (SSEFunc_0_epp)gen_helper_maskmov_mmx, - (SSEFunc_0_epp)gen_helper_maskmov_xmm }, /* XXX: casts */ - [0xf8] = MMX_OP2(psubb), - [0xf9] = MMX_OP2(psubw), - [0xfa] = MMX_OP2(psubl), - [0xfb] = MMX_OP2(psubq), - [0xfc] = MMX_OP2(paddb), - [0xfd] = MMX_OP2(paddw), - [0xfe] = MMX_OP2(paddl), -}; - -static const SSEFunc_0_epp sse_op_table2[3 * 8][2] = { - [0 + 2] = MMX_OP2(psrlw), - [0 + 4] = MMX_OP2(psraw), - [0 + 6] = MMX_OP2(psllw), - [8 + 2] = MMX_OP2(psrld), - [8 + 4] = MMX_OP2(psrad), - [8 + 6] = MMX_OP2(pslld), - [16 + 2] = MMX_OP2(psrlq), - [16 + 3] = { NULL, gen_helper_psrldq_xmm }, - [16 + 6] = MMX_OP2(psllq), - [16 + 7] = { NULL, gen_helper_pslldq_xmm }, -}; - -static const SSEFunc_0_epi sse_op_table3ai[] = { - gen_helper_cvtsi2ss, - gen_helper_cvtsi2sd -}; - -#ifdef TARGET_X86_64 -static const SSEFunc_0_epl sse_op_table3aq[] = { - gen_helper_cvtsq2ss, - gen_helper_cvtsq2sd -}; -#endif - -static const SSEFunc_i_ep sse_op_table3bi[] = { - gen_helper_cvttss2si, - gen_helper_cvtss2si, - gen_helper_cvttsd2si, - gen_helper_cvtsd2si -}; - -#ifdef TARGET_X86_64 -static const SSEFunc_l_ep sse_op_table3bq[] = { - gen_helper_cvttss2sq, - gen_helper_cvtss2sq, - gen_helper_cvttsd2sq, - gen_helper_cvtsd2sq -}; -#endif - -static const SSEFunc_0_epp sse_op_table4[8][4] = { - SSE_FOP(cmpeq), - SSE_FOP(cmplt), - SSE_FOP(cmple), - SSE_FOP(cmpunord), - SSE_FOP(cmpneq), - SSE_FOP(cmpnlt), - SSE_FOP(cmpnle), - SSE_FOP(cmpord), -}; - -static const SSEFunc_0_epp sse_op_table5[256] = { - [0x0c] = gen_helper_pi2fw, - [0x0d] = gen_helper_pi2fd, - [0x1c] = gen_helper_pf2iw, - [0x1d] = gen_helper_pf2id, - [0x8a] = gen_helper_pfnacc, - [0x8e] = gen_helper_pfpnacc, - [0x90] = gen_helper_pfcmpge, - [0x94] = gen_helper_pfmin, - [0x96] = gen_helper_pfrcp, - [0x97] = gen_helper_pfrsqrt, - [0x9a] = gen_helper_pfsub, - [0x9e] = gen_helper_pfadd, - [0xa0] = gen_helper_pfcmpgt, - [0xa4] = gen_helper_pfmax, - [0xa6] = gen_helper_movq, /* pfrcpit1; no need to actually increase precision */ - [0xa7] = gen_helper_movq, /* pfrsqit1 */ - [0xaa] = gen_helper_pfsubr, - [0xae] = gen_helper_pfacc, - [0xb0] = gen_helper_pfcmpeq, - [0xb4] = gen_helper_pfmul, - [0xb6] = gen_helper_movq, /* pfrcpit2 */ - [0xb7] = gen_helper_pmulhrw_mmx, - [0xbb] = gen_helper_pswapd, - [0xbf] = gen_helper_pavgb_mmx /* pavgusb */ -}; - -struct SSEOpHelper_epp { - SSEFunc_0_epp op[2]; - uint32_t ext_mask; -}; - -struct SSEOpHelper_eppi { - SSEFunc_0_eppi op[2]; - uint32_t ext_mask; -}; - -#define SSSE3_OP(x) { MMX_OP2(x), CPUID_EXT_SSSE3 } -#define SSE41_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE41 } -#define SSE42_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_SSE42 } -#define SSE41_SPECIAL { { NULL, SSE_SPECIAL }, CPUID_EXT_SSE41 } -#define PCLMULQDQ_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, \ - CPUID_EXT_PCLMULQDQ } -#define AESNI_OP(x) { { NULL, gen_helper_ ## x ## _xmm }, CPUID_EXT_AES } - -static const struct SSEOpHelper_epp sse_op_table6[256] = { - [0x00] = SSSE3_OP(pshufb), - [0x01] = SSSE3_OP(phaddw), - [0x02] = SSSE3_OP(phaddd), - [0x03] = SSSE3_OP(phaddsw), - [0x04] = SSSE3_OP(pmaddubsw), - [0x05] = SSSE3_OP(phsubw), - [0x06] = SSSE3_OP(phsubd), - [0x07] = SSSE3_OP(phsubsw), - [0x08] = SSSE3_OP(psignb), - [0x09] = SSSE3_OP(psignw), - [0x0a] = SSSE3_OP(psignd), - [0x0b] = SSSE3_OP(pmulhrsw), - [0x10] = SSE41_OP(pblendvb), - [0x14] = SSE41_OP(blendvps), - [0x15] = SSE41_OP(blendvpd), - [0x17] = SSE41_OP(ptest), - [0x1c] = SSSE3_OP(pabsb), - [0x1d] = SSSE3_OP(pabsw), - [0x1e] = SSSE3_OP(pabsd), - [0x20] = SSE41_OP(pmovsxbw), - [0x21] = SSE41_OP(pmovsxbd), - [0x22] = SSE41_OP(pmovsxbq), - [0x23] = SSE41_OP(pmovsxwd), - [0x24] = SSE41_OP(pmovsxwq), - [0x25] = SSE41_OP(pmovsxdq), - [0x28] = SSE41_OP(pmuldq), - [0x29] = SSE41_OP(pcmpeqq), - [0x2a] = SSE41_SPECIAL, /* movntqda */ - [0x2b] = SSE41_OP(packusdw), - [0x30] = SSE41_OP(pmovzxbw), - [0x31] = SSE41_OP(pmovzxbd), - [0x32] = SSE41_OP(pmovzxbq), - [0x33] = SSE41_OP(pmovzxwd), - [0x34] = SSE41_OP(pmovzxwq), - [0x35] = SSE41_OP(pmovzxdq), - [0x37] = SSE42_OP(pcmpgtq), - [0x38] = SSE41_OP(pminsb), - [0x39] = SSE41_OP(pminsd), - [0x3a] = SSE41_OP(pminuw), - [0x3b] = SSE41_OP(pminud), - [0x3c] = SSE41_OP(pmaxsb), - [0x3d] = SSE41_OP(pmaxsd), - [0x3e] = SSE41_OP(pmaxuw), - [0x3f] = SSE41_OP(pmaxud), - [0x40] = SSE41_OP(pmulld), - [0x41] = SSE41_OP(phminposuw), - [0xdb] = AESNI_OP(aesimc), - [0xdc] = AESNI_OP(aesenc), - [0xdd] = AESNI_OP(aesenclast), - [0xde] = AESNI_OP(aesdec), - [0xdf] = AESNI_OP(aesdeclast), -}; - -static const struct SSEOpHelper_eppi sse_op_table7[256] = { - [0x08] = SSE41_OP(roundps), - [0x09] = SSE41_OP(roundpd), - [0x0a] = SSE41_OP(roundss), - [0x0b] = SSE41_OP(roundsd), - [0x0c] = SSE41_OP(blendps), - [0x0d] = SSE41_OP(blendpd), - [0x0e] = SSE41_OP(pblendw), - [0x0f] = SSSE3_OP(palignr), - [0x14] = SSE41_SPECIAL, /* pextrb */ - [0x15] = SSE41_SPECIAL, /* pextrw */ - [0x16] = SSE41_SPECIAL, /* pextrd/pextrq */ - [0x17] = SSE41_SPECIAL, /* extractps */ - [0x20] = SSE41_SPECIAL, /* pinsrb */ - [0x21] = SSE41_SPECIAL, /* insertps */ - [0x22] = SSE41_SPECIAL, /* pinsrd/pinsrq */ - [0x40] = SSE41_OP(dpps), - [0x41] = SSE41_OP(dppd), - [0x42] = SSE41_OP(mpsadbw), - [0x44] = PCLMULQDQ_OP(pclmulqdq), - [0x60] = SSE42_OP(pcmpestrm), - [0x61] = SSE42_OP(pcmpestri), - [0x62] = SSE42_OP(pcmpistrm), - [0x63] = SSE42_OP(pcmpistri), - [0xdf] = AESNI_OP(aeskeygenassist), -}; - -static void gen_sse(CPUX86State *env, DisasContext *s, int b, - target_ulong pc_start, int rex_r) -{ - int b1, op1_offset, op2_offset, is_xmm, val; - int modrm, mod, rm, reg; - SSEFunc_0_epp sse_fn_epp; - SSEFunc_0_eppi sse_fn_eppi; - SSEFunc_0_ppi sse_fn_ppi; - SSEFunc_0_eppt sse_fn_eppt; - MemOp ot; - - b &= 0xff; - if (s->prefix & PREFIX_DATA) - b1 = 1; - else if (s->prefix & PREFIX_REPZ) - b1 = 2; - else if (s->prefix & PREFIX_REPNZ) - b1 = 3; - else - b1 = 0; - sse_fn_epp = sse_op_table1[b][b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if ((b <= 0x5f && b >= 0x10) || b == 0xc6 || b == 0xc2) { - is_xmm = 1; - } else { - if (b1 == 0) { - /* MMX case */ - is_xmm = 0; - } else { - is_xmm = 1; - } - } - /* simple MMX/SSE operation */ - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - return; - } - if (s->flags & HF_EM_MASK) { - illegal_op: - gen_illegal_opcode(s); - return; - } - if (is_xmm - && !(s->flags & HF_OSFXSR_MASK) - && ((b != 0x38 && b != 0x3a) || (s->prefix & PREFIX_DATA))) { - goto unknown_op; - } - if (b == 0x0e) { - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { - /* If we were fully decoding this we might use illegal_op. */ - goto unknown_op; - } - /* femms */ - gen_helper_emms(cpu_env); - return; - } - if (b == 0x77) { - /* emms */ - gen_helper_emms(cpu_env); - return; - } - /* prepare MMX state (XXX: optimize by storing fptt and fptags in - the static cpu state) */ - if (!is_xmm) { - gen_helper_enter_mmx(cpu_env); - } - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7); - if (is_xmm) - reg |= rex_r; - mod = (modrm >> 6) & 3; - if (sse_fn_epp == SSE_SPECIAL) { - b |= (b1 << 8); - switch(b) { - case 0x0e7: /* movntq */ - if (mod == 3) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - break; - case 0x1e7: /* movntdq */ - case 0x02b: /* movntps */ - case 0x12b: /* movntps */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - break; - case 0x3f0: /* lddqu */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - break; - case 0x22b: /* movntss */ - case 0x32b: /* movntsd */ - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - if (b1 & 1) { - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(0))); - gen_op_st_v(s, MO_32, s->T0, s->A0); - } - break; - case 0x6e: /* movd mm, ea */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_st_tl(s->T0, cpu_env, - offsetof(CPUX86State, fpregs[reg].mmx)); - } else -#endif - { - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_movl_mm_T0_mmx(s->ptr0, s->tmp2_i32); - } - break; - case 0x16e: /* movd xmm, ea */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - gen_helper_movq_mm_T0_xmm(s->ptr0, s->T0); - } else -#endif - { - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_movl_mm_T0_xmm(s->ptr0, s->tmp2_i32); - } - break; - case 0x6f: /* movq mm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - } else { - rm = (modrm & 7); - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State,fpregs[rm].mmx)); - tcg_gen_st_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - } - break; - case 0x010: /* movups */ - case 0x110: /* movupd */ - case 0x028: /* movaps */ - case 0x128: /* movapd */ - case 0x16f: /* movdqa xmm, ea */ - case 0x26f: /* movdqu xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movo(s, offsetof(CPUX86State, xmm_regs[reg]), - offsetof(CPUX86State,xmm_regs[rm])); - } - break; - case 0x210: /* movss xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); - } - break; - case 0x310: /* movsd xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - break; - case 0x012: /* movlps */ - case 0x112: /* movlpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - /* movhlps */ - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); - } - break; - case 0x212: /* movsldup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(2))); - } - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); - break; - case 0x312: /* movddup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - break; - case 0x016: /* movhps */ - case 0x116: /* movhpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(1))); - } else { - /* movlhps */ - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - break; - case 0x216: /* movshdup */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(1))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_L(3))); - } - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1))); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); - break; - case 0x178: - case 0x378: - { - int bit_index, field_length; - - if (b1 == 1 && reg != 0) - goto illegal_op; - field_length = x86_ldub_code(env, s) & 0x3F; - bit_index = x86_ldub_code(env, s) & 0x3F; - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg])); - if (b1 == 1) - gen_helper_extrq_i(cpu_env, s->ptr0, - tcg_const_i32(bit_index), - tcg_const_i32(field_length)); - else - gen_helper_insertq_i(cpu_env, s->ptr0, - tcg_const_i32(bit_index), - tcg_const_i32(field_length)); - } - break; - case 0x7e: /* movd ea, mm */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - tcg_gen_ld_i64(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx)); - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); - } else -#endif - { - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); - } - break; - case 0x17e: /* movd ea, xmm */ -#ifdef TARGET_X86_64 - if (s->dflag == MO_64) { - tcg_gen_ld_i64(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 1); - } else -#endif - { - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); - } - break; - case 0x27e: /* movq xmm, ea */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - } - gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); - break; - case 0x7f: /* movq ea, mm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); - } else { - rm = (modrm & 7); - gen_op_movq(s, offsetof(CPUX86State, fpregs[rm].mmx), - offsetof(CPUX86State,fpregs[reg].mmx)); - } - break; - case 0x011: /* movups */ - case 0x111: /* movupd */ - case 0x029: /* movaps */ - case 0x129: /* movapd */ - case 0x17f: /* movdqa ea, xmm */ - case 0x27f: /* movdqu ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movo(s, offsetof(CPUX86State, xmm_regs[rm]), - offsetof(CPUX86State,xmm_regs[reg])); - } - break; - case 0x211: /* movss ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); - gen_op_st_v(s, MO_32, s->T0, s->A0); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movl(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_L(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); - } - break; - case 0x311: /* movsd ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - } - break; - case 0x013: /* movlps */ - case 0x113: /* movlpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - goto illegal_op; - } - break; - case 0x017: /* movhps */ - case 0x117: /* movhpd */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(1))); - } else { - goto illegal_op; - } - break; - case 0x71: /* shift mm, im */ - case 0x72: - case 0x73: - case 0x171: /* shift xmm, im */ - case 0x172: - case 0x173: - if (b1 >= 2) { - goto unknown_op; - } - val = x86_ldub_code(env, s); - if (is_xmm) { - tcg_gen_movi_tl(s->T0, val); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(1))); - op1_offset = offsetof(CPUX86State,xmm_t0); - } else { - tcg_gen_movi_tl(s->T0, val); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, mmx_t0.MMX_L(0))); - tcg_gen_movi_tl(s->T0, 0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, mmx_t0.MMX_L(1))); - op1_offset = offsetof(CPUX86State,mmx_t0); - } - sse_fn_epp = sse_op_table2[((b - 1) & 3) * 8 + - (((modrm >> 3)) & 7)][b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (is_xmm) { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op1_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0x050: /* movmskps */ - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskps(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - case 0x150: /* movmskpd */ - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskpd(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - case 0x02a: /* cvtpi2ps */ - case 0x12a: /* cvtpi2pd */ - gen_helper_enter_mmx(cpu_env); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_ldq_env_A0(s, op2_offset); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - switch(b >> 8) { - case 0x0: - gen_helper_cvtpi2ps(cpu_env, s->ptr0, s->ptr1); - break; - default: - case 0x1: - gen_helper_cvtpi2pd(cpu_env, s->ptr0, s->ptr1); - break; - } - break; - case 0x22a: /* cvtsi2ss */ - case 0x32a: /* cvtsi2sd */ - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - if (ot == MO_32) { - SSEFunc_0_epi sse_fn_epi = sse_op_table3ai[(b >> 8) & 1]; - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - sse_fn_epi(cpu_env, s->ptr0, s->tmp2_i32); - } else { -#ifdef TARGET_X86_64 - SSEFunc_0_epl sse_fn_epl = sse_op_table3aq[(b >> 8) & 1]; - sse_fn_epl(cpu_env, s->ptr0, s->T0); -#else - goto illegal_op; -#endif - } - break; - case 0x02c: /* cvttps2pi */ - case 0x12c: /* cvttpd2pi */ - case 0x02d: /* cvtps2pi */ - case 0x12d: /* cvtpd2pi */ - gen_helper_enter_mmx(cpu_env); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_ldo_env_A0(s, op2_offset); - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - switch(b) { - case 0x02c: - gen_helper_cvttps2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x12c: - gen_helper_cvttpd2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x02d: - gen_helper_cvtps2pi(cpu_env, s->ptr0, s->ptr1); - break; - case 0x12d: - gen_helper_cvtpd2pi(cpu_env, s->ptr0, s->ptr1); - break; - } - break; - case 0x22c: /* cvttss2si */ - case 0x32c: /* cvttsd2si */ - case 0x22d: /* cvtss2si */ - case 0x32d: /* cvtsd2si */ - ot = mo_64_32(s->dflag); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - if ((b >> 8) & 1) { - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_Q(0))); - } else { - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State, xmm_t0.ZMM_L(0))); - } - op2_offset = offsetof(CPUX86State,xmm_t0); - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); - if (ot == MO_32) { - SSEFunc_i_ep sse_fn_i_ep = - sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; - sse_fn_i_ep(s->tmp2_i32, cpu_env, s->ptr0); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - } else { -#ifdef TARGET_X86_64 - SSEFunc_l_ep sse_fn_l_ep = - sse_op_table3bq[((b >> 7) & 2) | (b & 1)]; - sse_fn_l_ep(s->T0, cpu_env, s->ptr0); -#else - goto illegal_op; -#endif - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0xc4: /* pinsrw */ - case 0x1c4: - s->rip_offset = 1; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - val = x86_ldub_code(env, s); - if (b1) { - val &= 7; - tcg_gen_st16_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[reg].ZMM_W(val))); - } else { - val &= 3; - tcg_gen_st16_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); - } - break; - case 0xc5: /* pextrw */ - case 0x1c5: - if (mod != 3) - goto illegal_op; - ot = mo_64_32(s->dflag); - val = x86_ldub_code(env, s); - if (b1) { - val &= 7; - rm = (modrm & 7) | REX_B(s); - tcg_gen_ld16u_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_regs[rm].ZMM_W(val))); - } else { - val &= 3; - rm = (modrm & 7); - tcg_gen_ld16u_tl(s->T0, cpu_env, - offsetof(CPUX86State,fpregs[rm].mmx.MMX_W(val))); - } - reg = ((modrm >> 3) & 7) | rex_r; - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0x1d6: /* movq ea, xmm */ - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_stq_env_A0(s, offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(0))); - } else { - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), - offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); - gen_op_movq_env_0(s, - offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(1))); - } - break; - case 0x2d6: /* movq2dq */ - gen_helper_enter_mmx(cpu_env); - rm = (modrm & 7); - gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), - offsetof(CPUX86State,fpregs[rm].mmx)); - gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); - break; - case 0x3d6: /* movdq2q */ - gen_helper_enter_mmx(cpu_env); - rm = (modrm & 7) | REX_B(s); - gen_op_movq(s, offsetof(CPUX86State, fpregs[reg & 7].mmx), - offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); - break; - case 0xd7: /* pmovmskb */ - case 0x1d7: - if (mod != 3) - goto illegal_op; - if (b1) { - rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State, xmm_regs[rm])); - gen_helper_pmovmskb_xmm(s->tmp2_i32, cpu_env, s->ptr0); - } else { - rm = (modrm & 7); - tcg_gen_addi_ptr(s->ptr0, cpu_env, - offsetof(CPUX86State, fpregs[rm].mmx)); - gen_helper_pmovmskb_mmx(s->tmp2_i32, cpu_env, s->ptr0); - } - reg = ((modrm >> 3) & 7) | rex_r; - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - break; - - case 0x138: - case 0x038: - b = modrm; - if ((b & 0xf0) == 0xf0) { - goto do_0f_38_fx; - } - modrm = x86_ldub_code(env, s); - rm = modrm & 7; - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - if (b1 >= 2) { - goto unknown_op; - } - - sse_fn_epp = sse_op_table6[b].op[b1]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (!(s->cpuid_ext_features & sse_op_table6[b].ext_mask)) - goto illegal_op; - - if (b1) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); - } else { - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_lea_modrm(env, s, modrm); - switch (b) { - case 0x20: case 0x30: /* pmovsxbw, pmovzxbw */ - case 0x23: case 0x33: /* pmovsxwd, pmovzxwd */ - case 0x25: case 0x35: /* pmovsxdq, pmovzxdq */ - gen_ldq_env_A0(s, op2_offset + - offsetof(ZMMReg, ZMM_Q(0))); - break; - case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ - case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, op2_offset + - offsetof(ZMMReg, ZMM_L(0))); - break; - case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ - tcg_gen_qemu_ld_tl(s->tmp0, s->A0, - s->mem_index, MO_LEUW); - tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset + - offsetof(ZMMReg, ZMM_W(0))); - break; - case 0x2a: /* movntqda */ - gen_ldo_env_A0(s, op1_offset); - return; - default: - gen_ldo_env_A0(s, op2_offset); - } - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } else { - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, op2_offset); - } - } - if (sse_fn_epp == SSE_SPECIAL) { - goto unknown_op; - } - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - - if (b == 0x17) { - set_cc_op(s, CC_OP_EFLAGS); - } - break; - - case 0x238: - case 0x338: - do_0f_38_fx: - /* Various integer extensions at 0f 38 f[0-f]. */ - b = modrm | (b1 << 8); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - switch (b) { - case 0x3f0: /* crc32 Gd,Eb */ - case 0x3f1: /* crc32 Gd,Ey */ - do_crc32: - if (!(s->cpuid_ext_features & CPUID_EXT_SSE42)) { - goto illegal_op; - } - if ((b & 0xff) == 0xf0) { - ot = MO_8; - } else if (s->dflag != MO_64) { - ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); - } else { - ot = MO_64; - } - - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[reg]); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_crc32(s->T0, s->tmp2_i32, - s->T0, tcg_const_i32(8 << ot)); - - ot = mo_64_32(s->dflag); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - case 0x1f0: /* crc32 or movbe */ - case 0x1f1: - /* For these insns, the f3 prefix is supposed to have priority - over the 66 prefix, but that's not what we implement above - setting b1. */ - if (s->prefix & PREFIX_REPNZ) { - goto do_crc32; - } - /* FALLTHRU */ - case 0x0f0: /* movbe Gy,My */ - case 0x0f1: /* movbe My,Gy */ - if (!(s->cpuid_ext_features & CPUID_EXT_MOVBE)) { - goto illegal_op; - } - if (s->dflag != MO_64) { - ot = (s->prefix & PREFIX_DATA ? MO_16 : MO_32); - } else { - ot = MO_64; - } - - gen_lea_modrm(env, s, modrm); - if ((b & 1) == 0) { - tcg_gen_qemu_ld_tl(s->T0, s->A0, - s->mem_index, ot | MO_BE); - gen_op_mov_reg_v(s, ot, reg, s->T0); - } else { - tcg_gen_qemu_st_tl(cpu_regs[reg], s->A0, - s->mem_index, ot | MO_BE); - } - break; - - case 0x0f2: /* andn Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_andc_tl(s->T0, s->T0, cpu_regs[s->vex_v]); - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0x0f7: /* bextr Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - { - TCGv bound, zero; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Extract START, and shift the operand. - Shifts larger than operand size get zeros. */ - tcg_gen_ext8u_tl(s->A0, cpu_regs[s->vex_v]); - tcg_gen_shr_tl(s->T0, s->T0, s->A0); - - bound = tcg_const_tl(ot == MO_64 ? 63 : 31); - zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_LEU, s->T0, s->A0, bound, - s->T0, zero); - tcg_temp_free(zero); - - /* Extract the LEN into a mask. Lengths larger than - operand size get all ones. */ - tcg_gen_extract_tl(s->A0, cpu_regs[s->vex_v], 8, 8); - tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->A0, bound, - s->A0, bound); - tcg_temp_free(bound); - tcg_gen_movi_tl(s->T1, 1); - tcg_gen_shl_tl(s->T1, s->T1, s->A0); - tcg_gen_subi_tl(s->T1, s->T1, 1); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - } - break; - - case 0x0f5: /* bzhi Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - tcg_gen_ext8u_tl(s->T1, cpu_regs[s->vex_v]); - { - TCGv bound = tcg_const_tl(ot == MO_64 ? 63 : 31); - /* Note that since we're using BMILG (in order to get O - cleared) we need to store the inverse into C. */ - tcg_gen_setcond_tl(TCG_COND_LT, cpu_cc_src, - s->T1, bound); - tcg_gen_movcond_tl(TCG_COND_GT, s->T1, s->T1, - bound, bound, s->T1); - tcg_temp_free(bound); - } - tcg_gen_movi_tl(s->A0, -1); - tcg_gen_shl_tl(s->A0, s->A0, s->T1); - tcg_gen_andc_tl(s->T0, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - case 0x3f6: /* mulx By, Gy, rdx, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - switch (ot) { - default: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EDX]); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp3_i32); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(s->T0, s->T1, - s->T0, cpu_regs[R_EDX]); - tcg_gen_mov_i64(cpu_regs[s->vex_v], s->T0); - tcg_gen_mov_i64(cpu_regs[reg], s->T1); - break; -#endif - } - break; - - case 0x3f5: /* pdep Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Note that by zero-extending the source operand, we - automatically handle zero-extending the result. */ - if (ot == MO_64) { - tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); - } else { - tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); - } - gen_helper_pdep(cpu_regs[reg], s->T1, s->T0); - break; - - case 0x2f5: /* pext Gy, By, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - /* Note that by zero-extending the source operand, we - automatically handle zero-extending the result. */ - if (ot == MO_64) { - tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); - } else { - tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); - } - gen_helper_pext(cpu_regs[reg], s->T1, s->T0); - break; - - case 0x1f6: /* adcx Gy, Ey */ - case 0x2f6: /* adox Gy, Ey */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_ADX)) { - goto illegal_op; - } else { - TCGv carry_in, carry_out, zero; - int end_op; - - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - /* Re-use the carry-out from a previous round. */ - carry_in = NULL; - carry_out = (b == 0x1f6 ? cpu_cc_dst : cpu_cc_src2); - switch (s->cc_op) { - case CC_OP_ADCX: - if (b == 0x1f6) { - carry_in = cpu_cc_dst; - end_op = CC_OP_ADCX; - } else { - end_op = CC_OP_ADCOX; - } - break; - case CC_OP_ADOX: - if (b == 0x1f6) { - end_op = CC_OP_ADCOX; - } else { - carry_in = cpu_cc_src2; - end_op = CC_OP_ADOX; - } - break; - case CC_OP_ADCOX: - end_op = CC_OP_ADCOX; - carry_in = carry_out; - break; - default: - end_op = (b == 0x1f6 ? CC_OP_ADCX : CC_OP_ADOX); - break; - } - /* If we can't reuse carry-out, get it out of EFLAGS. */ - if (!carry_in) { - if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { - gen_compute_eflags(s); - } - carry_in = s->tmp0; - tcg_gen_extract_tl(carry_in, cpu_cc_src, - ctz32(b == 0x1f6 ? CC_C : CC_O), 1); - } - - switch (ot) { -#ifdef TARGET_X86_64 - case MO_32: - /* If we know TL is 64-bit, and we want a 32-bit - result, just do everything in 64-bit arithmetic. */ - tcg_gen_ext32u_i64(cpu_regs[reg], cpu_regs[reg]); - tcg_gen_ext32u_i64(s->T0, s->T0); - tcg_gen_add_i64(s->T0, s->T0, cpu_regs[reg]); - tcg_gen_add_i64(s->T0, s->T0, carry_in); - tcg_gen_ext32u_i64(cpu_regs[reg], s->T0); - tcg_gen_shri_i64(carry_out, s->T0, 32); - break; -#endif - default: - /* Otherwise compute the carry-out in two steps. */ - zero = tcg_const_tl(0); - tcg_gen_add2_tl(s->T0, carry_out, - s->T0, zero, - carry_in, zero); - tcg_gen_add2_tl(cpu_regs[reg], carry_out, - cpu_regs[reg], carry_out, - s->T0, zero); - tcg_temp_free(zero); - break; - } - set_cc_op(s, end_op); - } - break; - - case 0x1f7: /* shlx Gy, Ey, By */ - case 0x2f7: /* sarx Gy, Ey, By */ - case 0x3f7: /* shrx Gy, Ey, By */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - if (ot == MO_64) { - tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 63); - } else { - tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 31); - } - if (b == 0x1f7) { - tcg_gen_shl_tl(s->T0, s->T0, s->T1); - } else if (b == 0x2f7) { - if (ot != MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } - tcg_gen_sar_tl(s->T0, s->T0, s->T1); - } else { - if (ot != MO_64) { - tcg_gen_ext32u_tl(s->T0, s->T0); - } - tcg_gen_shr_tl(s->T0, s->T0, s->T1); - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - case 0x0f3: - case 0x1f3: - case 0x2f3: - case 0x3f3: /* Group 17 */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - - tcg_gen_mov_tl(cpu_cc_src, s->T0); - switch (reg & 7) { - case 1: /* blsr By,Ey */ - tcg_gen_subi_tl(s->T1, s->T0, 1); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - break; - case 2: /* blsmsk By,Ey */ - tcg_gen_subi_tl(s->T1, s->T0, 1); - tcg_gen_xor_tl(s->T0, s->T0, s->T1); - break; - case 3: /* blsi By, Ey */ - tcg_gen_neg_tl(s->T1, s->T0); - tcg_gen_and_tl(s->T0, s->T0, s->T1); - break; - default: - goto unknown_op; - } - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - gen_op_mov_reg_v(s, ot, s->vex_v, s->T0); - set_cc_op(s, CC_OP_BMILGB + ot); - break; - - default: - goto unknown_op; - } - break; - - case 0x03a: - case 0x13a: - b = modrm; - modrm = x86_ldub_code(env, s); - rm = modrm & 7; - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - if (b1 >= 2) { - goto unknown_op; - } - - sse_fn_eppi = sse_op_table7[b].op[b1]; - if (!sse_fn_eppi) { - goto unknown_op; - } - if (!(s->cpuid_ext_features & sse_op_table7[b].ext_mask)) - goto illegal_op; - - s->rip_offset = 1; - - if (sse_fn_eppi == SSE_SPECIAL) { - ot = mo_64_32(s->dflag); - rm = (modrm & 7) | REX_B(s); - if (mod != 3) - gen_lea_modrm(env, s, modrm); - reg = ((modrm >> 3) & 7) | rex_r; - val = x86_ldub_code(env, s); - switch (b) { - case 0x14: /* pextrb */ - tcg_gen_ld8u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_B(val & 15))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_UB); - } - break; - case 0x15: /* pextrw */ - tcg_gen_ld16u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_W(val & 7))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_LEUW); - } - break; - case 0x16: - if (ot == MO_32) { /* pextrd */ - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - if (mod == 3) { - tcg_gen_extu_i32_tl(cpu_regs[rm], s->tmp2_i32); - } else { - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - } else { /* pextrq */ -#ifdef TARGET_X86_64 - tcg_gen_ld_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(val & 1))); - if (mod == 3) { - tcg_gen_mov_i64(cpu_regs[rm], s->tmp1_i64); - } else { - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - } -#else - goto illegal_op; -#endif - } - break; - case 0x17: /* extractps */ - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - tcg_gen_qemu_st_tl(s->T0, s->A0, - s->mem_index, MO_LEUL); - } - break; - case 0x20: /* pinsrb */ - if (mod == 3) { - gen_op_mov_v_reg(s, MO_32, s->T0, rm); - } else { - tcg_gen_qemu_ld_tl(s->T0, s->A0, - s->mem_index, MO_UB); - } - tcg_gen_st8_tl(s->T0, cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_B(val & 15))); - break; - case 0x21: /* insertps */ - if (mod == 3) { - tcg_gen_ld_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State,xmm_regs[rm] - .ZMM_L((val >> 6) & 3))); - } else { - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - tcg_gen_st_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State,xmm_regs[reg] - .ZMM_L((val >> 4) & 3))); - if ((val >> 0) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(0))); - if ((val >> 1) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(1))); - if ((val >> 2) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(2))); - if ((val >> 3) & 1) - tcg_gen_st_i32(tcg_const_i32(0 /*float32_zero*/), - cpu_env, offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(3))); - break; - case 0x22: - if (ot == MO_32) { /* pinsrd */ - if (mod == 3) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[rm]); - } else { - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - } - tcg_gen_st_i32(s->tmp2_i32, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_L(val & 3))); - } else { /* pinsrq */ -#ifdef TARGET_X86_64 - if (mod == 3) { - gen_op_mov_v_reg(s, ot, s->tmp1_i64, rm); - } else { - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - } - tcg_gen_st_i64(s->tmp1_i64, cpu_env, - offsetof(CPUX86State, - xmm_regs[reg].ZMM_Q(val & 1))); -#else - goto illegal_op; -#endif - } - break; - } - return; - } - - if (b1) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,xmm_regs[rm | REX_B(s)]); - } else { - op2_offset = offsetof(CPUX86State,xmm_t0); - gen_lea_modrm(env, s, modrm); - gen_ldo_env_A0(s, op2_offset); - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod == 3) { - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } else { - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_lea_modrm(env, s, modrm); - gen_ldq_env_A0(s, op2_offset); - } - } - val = x86_ldub_code(env, s); - - if ((b & 0xfc) == 0x60) { /* pcmpXstrX */ - set_cc_op(s, CC_OP_EFLAGS); - - if (s->dflag == MO_64) { - /* The helper must use entire 64-bit gp registers */ - val |= 1 << 8; - } - } - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_eppi(cpu_env, s->ptr0, s->ptr1, tcg_const_i32(val)); - break; - - case 0x33a: - /* Various integer extensions at 0f 3a f[0-f]. */ - b = modrm | (b1 << 8); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - switch (b) { - case 0x3f0: /* rorx Gy,Ey, Ib */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI2) - || !(s->prefix & PREFIX_VEX) - || s->vex_l != 0) { - goto illegal_op; - } - ot = mo_64_32(s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - b = x86_ldub_code(env, s); - if (ot == MO_64) { - tcg_gen_rotri_tl(s->T0, s->T0, b & 63); - } else { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b & 31); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - - default: - goto unknown_op; - } - break; - - default: - unknown_op: - gen_unknown_opcode(env, s); - return; - } - } else { - /* generic MMX or SSE operation */ - switch(b) { - case 0x70: /* pshufx insn */ - case 0xc6: /* pshufx insn */ - case 0xc2: /* compare insns */ - s->rip_offset = 1; - break; - default: - break; - } - if (is_xmm) { - op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - if (mod != 3) { - int sz = 4; - - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,xmm_t0); - - switch (b) { - case 0x50 ... 0x5a: - case 0x5c ... 0x5f: - case 0xc2: - /* Most sse scalar operations. */ - if (b1 == 2) { - sz = 2; - } else if (b1 == 3) { - sz = 3; - } - break; - - case 0x2e: /* ucomis[sd] */ - case 0x2f: /* comis[sd] */ - if (b1 == 0) { - sz = 2; - } else { - sz = 3; - } - break; - } - - switch (sz) { - case 2: - /* 32 bit access */ - gen_op_ld_v(s, MO_32, s->T0, s->A0); - tcg_gen_st32_tl(s->T0, cpu_env, - offsetof(CPUX86State,xmm_t0.ZMM_L(0))); - break; - case 3: - /* 64 bit access */ - gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_D(0))); - break; - default: - /* 128 bit access */ - gen_ldo_env_A0(s, op2_offset); - break; - } - } else { - rm = (modrm & 7) | REX_B(s); - op2_offset = offsetof(CPUX86State,xmm_regs[rm]); - } - } else { - op1_offset = offsetof(CPUX86State,fpregs[reg].mmx); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - op2_offset = offsetof(CPUX86State,mmx_t0); - gen_ldq_env_A0(s, op2_offset); - } else { - rm = (modrm & 7); - op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); - } - } - switch(b) { - case 0x0f: /* 3DNow! data insns */ - val = x86_ldub_code(env, s); - sse_fn_epp = sse_op_table5[val]; - if (!sse_fn_epp) { - goto unknown_op; - } - if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { - goto illegal_op; - } - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0x70: /* pshufx insn */ - case 0xc6: /* pshufx insn */ - val = x86_ldub_code(env, s); - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - /* XXX: introduce a new table? */ - sse_fn_ppi = (SSEFunc_0_ppi)sse_fn_epp; - sse_fn_ppi(s->ptr0, s->ptr1, tcg_const_i32(val)); - break; - case 0xc2: - /* compare insns */ - val = x86_ldub_code(env, s); - if (val >= 8) - goto unknown_op; - sse_fn_epp = sse_op_table4[val][b1]; - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - case 0xf7: - /* maskmov : we must prepare A0 */ - if (mod != 3) - goto illegal_op; - tcg_gen_mov_tl(s->A0, cpu_regs[R_EDI]); - gen_extu(s->aflag, s->A0); - gen_add_A0_ds_seg(s); - - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - /* XXX: introduce a new table? */ - sse_fn_eppt = (SSEFunc_0_eppt)sse_fn_epp; - sse_fn_eppt(cpu_env, s->ptr0, s->ptr1, s->A0); - break; - default: - tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, s->ptr0, s->ptr1); - break; - } - if (b == 0x2e || b == 0x2f) { - set_cc_op(s, CC_OP_EFLAGS); - } - } -} - -/* convert one instruction. s->base.is_jmp is set if the translation must - be stopped. Return the next pc value */ -static target_ulong disas_insn(DisasContext *s, CPUState *cpu) -{ - CPUX86State *env = cpu->env_ptr; - int b, prefixes; - int shift; - MemOp ot, aflag, dflag; - int modrm, reg, rm, mod, op, opreg, val; - target_ulong next_eip, tval; - int rex_w, rex_r; - target_ulong pc_start = s->base.pc_next; - - s->pc_start = s->pc = pc_start; - s->override = -1; -#ifdef TARGET_X86_64 - s->rex_x = 0; - s->rex_b = 0; - s->x86_64_hregs = false; -#endif - s->rip_offset = 0; /* for relative ip address */ - s->vex_l = 0; - s->vex_v = 0; - if (sigsetjmp(s->jmpbuf, 0) != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - return s->pc; - } - - prefixes = 0; - rex_w = -1; - rex_r = 0; - - next_byte: - b = x86_ldub_code(env, s); - /* Collect prefixes. */ - switch (b) { - case 0xf3: - prefixes |= PREFIX_REPZ; - goto next_byte; - case 0xf2: - prefixes |= PREFIX_REPNZ; - goto next_byte; - case 0xf0: - prefixes |= PREFIX_LOCK; - goto next_byte; - case 0x2e: - s->override = R_CS; - goto next_byte; - case 0x36: - s->override = R_SS; - goto next_byte; - case 0x3e: - s->override = R_DS; - goto next_byte; - case 0x26: - s->override = R_ES; - goto next_byte; - case 0x64: - s->override = R_FS; - goto next_byte; - case 0x65: - s->override = R_GS; - goto next_byte; - case 0x66: - prefixes |= PREFIX_DATA; - goto next_byte; - case 0x67: - prefixes |= PREFIX_ADR; - goto next_byte; -#ifdef TARGET_X86_64 - case 0x40 ... 0x4f: - if (CODE64(s)) { - /* REX prefix */ - rex_w = (b >> 3) & 1; - rex_r = (b & 0x4) << 1; - s->rex_x = (b & 0x2) << 2; - REX_B(s) = (b & 0x1) << 3; - /* select uniform byte register addressing */ - s->x86_64_hregs = true; - goto next_byte; - } - break; -#endif - case 0xc5: /* 2-byte VEX */ - case 0xc4: /* 3-byte VEX */ - /* VEX prefixes cannot be used except in 32-bit mode. - Otherwise the instruction is LES or LDS. */ - if (s->code32 && !s->vm86) { - static const int pp_prefix[4] = { - 0, PREFIX_DATA, PREFIX_REPZ, PREFIX_REPNZ - }; - int vex3, vex2 = x86_ldub_code(env, s); - - if (!CODE64(s) && (vex2 & 0xc0) != 0xc0) { - /* 4.1.4.6: In 32-bit mode, bits [7:6] must be 11b, - otherwise the instruction is LES or LDS. */ - s->pc--; /* rewind the advance_pc() x86_ldub_code() did */ - break; - } - - /* 4.1.1-4.1.3: No preceding lock, 66, f2, f3, or rex prefixes. */ - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ - | PREFIX_LOCK | PREFIX_DATA)) { - goto illegal_op; - } -#ifdef TARGET_X86_64 - if (s->x86_64_hregs) { - goto illegal_op; - } -#endif - rex_r = (~vex2 >> 4) & 8; - if (b == 0xc5) { - /* 2-byte VEX prefix: RVVVVlpp, implied 0f leading opcode byte */ - vex3 = vex2; - b = x86_ldub_code(env, s) | 0x100; - } else { - /* 3-byte VEX prefix: RXBmmmmm wVVVVlpp */ -#ifdef TARGET_X86_64 - s->rex_x = (~vex2 >> 3) & 8; - s->rex_b = (~vex2 >> 2) & 8; -#endif - vex3 = x86_ldub_code(env, s); - rex_w = (vex3 >> 7) & 1; - switch (vex2 & 0x1f) { - case 0x01: /* Implied 0f leading opcode bytes. */ - b = x86_ldub_code(env, s) | 0x100; - break; - case 0x02: /* Implied 0f 38 leading opcode bytes. */ - b = 0x138; - break; - case 0x03: /* Implied 0f 3a leading opcode bytes. */ - b = 0x13a; - break; - default: /* Reserved for future use. */ - goto unknown_op; - } - } - s->vex_v = (~vex3 >> 3) & 0xf; - s->vex_l = (vex3 >> 2) & 1; - prefixes |= pp_prefix[vex3 & 3] | PREFIX_VEX; - } - break; - } - - /* Post-process prefixes. */ - if (CODE64(s)) { - /* In 64-bit mode, the default data size is 32-bit. Select 64-bit - data with rex_w, and 16-bit data with 0x66; rex_w takes precedence - over 0x66 if both are present. */ - dflag = (rex_w > 0 ? MO_64 : prefixes & PREFIX_DATA ? MO_16 : MO_32); - /* In 64-bit mode, 0x67 selects 32-bit addressing. */ - aflag = (prefixes & PREFIX_ADR ? MO_32 : MO_64); - } else { - /* In 16/32-bit mode, 0x66 selects the opposite data size. */ - if (s->code32 ^ ((prefixes & PREFIX_DATA) != 0)) { - dflag = MO_32; - } else { - dflag = MO_16; - } - /* In 16/32-bit mode, 0x67 selects the opposite addressing. */ - if (s->code32 ^ ((prefixes & PREFIX_ADR) != 0)) { - aflag = MO_32; - } else { - aflag = MO_16; - } - } - - s->prefix = prefixes; - s->aflag = aflag; - s->dflag = dflag; - - /* now check op code */ - reswitch: - switch(b) { - case 0x0f: - /**************************/ - /* extended op code */ - b = x86_ldub_code(env, s) | 0x100; - goto reswitch; - - /**************************/ - /* arith & logic */ - case 0x00 ... 0x05: - case 0x08 ... 0x0d: - case 0x10 ... 0x15: - case 0x18 ... 0x1d: - case 0x20 ... 0x25: - case 0x28 ... 0x2d: - case 0x30 ... 0x35: - case 0x38 ... 0x3d: - { - int op, f, val; - op = (b >> 3) & 7; - f = (b >> 1) & 3; - - ot = mo_b_d(b, dflag); - - switch(f) { - case 0: /* OP Ev, Gv */ - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else if (op == OP_XORL && rm == reg) { - xor_zero: - /* xor reg, reg optimisation */ - set_cc_op(s, CC_OP_CLR); - tcg_gen_movi_tl(s->T0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } else { - opreg = rm; - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op(s, op, ot, opreg); - break; - case 1: /* OP Gv, Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - reg = ((modrm >> 3) & 7) | rex_r; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - } else if (op == OP_XORL && rm == reg) { - goto xor_zero; - } else { - gen_op_mov_v_reg(s, ot, s->T1, rm); - } - gen_op(s, op, ot, reg); - break; - case 2: /* OP A, Iv */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, OR_EAX); - break; - } - } - break; - - case 0x82: - if (CODE64(s)) - goto illegal_op; - /* fall through */ - case 0x80: /* GRP1 */ - case 0x81: - case 0x83: - { - int val; - - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (b == 0x83) - s->rip_offset = 1; - else - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; - } - - switch(b) { - default: - case 0x80: - case 0x81: - case 0x82: - val = insn_get(env, s, ot); - break; - case 0x83: - val = (int8_t)insn_get(env, s, MO_8); - break; - } - tcg_gen_movi_tl(s->T1, val); - gen_op(s, op, ot, opreg); - } - break; - - /**************************/ - /* inc, dec, and other misc arith */ - case 0x40 ... 0x47: /* inc Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), 1); - break; - case 0x48 ... 0x4f: /* dec Gv */ - ot = dflag; - gen_inc(s, ot, OR_EAX + (b & 7), -1); - break; - case 0xf6: /* GRP3 */ - case 0xf7: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (mod != 3) { - if (op == 0) { - s->rip_offset = insn_const_size(ot); - } - gen_lea_modrm(env, s, modrm); - /* For those below that handle locked memory, don't load here. */ - if (!(s->prefix & PREFIX_LOCK) - || op != 2) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* test */ - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - case 2: /* not */ - if (s->prefix & PREFIX_LOCK) { - if (mod == 3) { - goto illegal_op; - } - tcg_gen_movi_tl(s->T0, ~0); - tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, - s->mem_index, ot | MO_LE); - } else { - tcg_gen_not_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; - case 3: /* neg */ - if (s->prefix & PREFIX_LOCK) { - TCGLabel *label1; - TCGv a0, t0, t1, t2; - - if (mod == 3) { - goto illegal_op; - } - a0 = tcg_temp_local_new(); - t0 = tcg_temp_local_new(); - label1 = gen_new_label(); - - tcg_gen_mov_tl(a0, s->A0); - tcg_gen_mov_tl(t0, s->T0); - - gen_set_label(label1); - t1 = tcg_temp_new(); - t2 = tcg_temp_new(); - tcg_gen_mov_tl(t2, t0); - tcg_gen_neg_tl(t1, t0); - tcg_gen_atomic_cmpxchg_tl(t0, a0, t0, t1, - s->mem_index, ot | MO_LE); - tcg_temp_free(t1); - tcg_gen_brcond_tl(TCG_COND_NE, t0, t2, label1); - - tcg_temp_free(t2); - tcg_temp_free(a0); - tcg_gen_mov_tl(s->T0, t0); - tcg_temp_free(t0); - } else { - tcg_gen_neg_tl(s->T0, s->T0); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - gen_op_update_neg_cc(s); - set_cc_op(s, CC_OP_SUBB + ot); - break; - case 4: /* mul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8u_tl(s->T0, s->T0); - tcg_gen_ext8u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16u_tl(s->T0, s->T0); - tcg_gen_ext16u_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 5: /* imul */ - switch(ot) { - case MO_8: - gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - tcg_gen_ext8s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext8s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - set_cc_op(s, CC_OP_MULB); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - tcg_gen_shri_tl(s->T0, s->T0, 16); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - set_cc_op(s, CC_OP_MULW); - break; - default: - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - set_cc_op(s, CC_OP_MULL); - break; -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - s->T0, cpu_regs[R_EAX]); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); - set_cc_op(s, CC_OP_MULQ); - break; -#endif - } - break; - case 6: /* div */ - switch(ot) { - case MO_8: - gen_helper_divb_AL(cpu_env, s->T0); - break; - case MO_16: - gen_helper_divw_AX(cpu_env, s->T0); - break; - default: - case MO_32: - gen_helper_divl_EAX(cpu_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_divq_EAX(cpu_env, s->T0); - break; -#endif - } - break; - case 7: /* idiv */ - switch(ot) { - case MO_8: - gen_helper_idivb_AL(cpu_env, s->T0); - break; - case MO_16: - gen_helper_idivw_AX(cpu_env, s->T0); - break; - default: - case MO_32: - gen_helper_idivl_EAX(cpu_env, s->T0); - break; -#ifdef TARGET_X86_64 - case MO_64: - gen_helper_idivq_EAX(cpu_env, s->T0); - break; -#endif - } - break; - default: - goto unknown_op; - } - break; - - case 0xfe: /* GRP4 */ - case 0xff: /* GRP5 */ - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - op = (modrm >> 3) & 7; - if (op >= 2 && b == 0xfe) { - goto unknown_op; - } - if (CODE64(s)) { - if (op == 2 || op == 4) { - /* operand size for jumps is 64 bit */ - ot = MO_64; - } else if (op == 3 || op == 5) { - ot = dflag != MO_16 ? MO_32 + (rex_w == 1) : MO_16; - } else if (op == 6) { - /* default push size is 64 bit */ - ot = mo_pushpop(s, dflag); - } - } - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - if (op >= 2 && op != 3 && op != 5) - gen_op_ld_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - - switch(op) { - case 0: /* inc Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, 1); - break; - case 1: /* dec Ev */ - if (mod != 3) - opreg = OR_TMP0; - else - opreg = rm; - gen_inc(s, ot, opreg, -1); - break; - case 2: /* call Ev */ - /* XXX: optimize if memory (no 'and' is necessary) */ - if (dflag == MO_16) { - tcg_gen_ext16u_tl(s->T0, s->T0); - } - next_eip = s->pc - s->cs_base; - tcg_gen_movi_tl(s->T1, next_eip); - gen_push_v(s, s->T1); - gen_op_jmp_v(s->T0); - gen_bnd_jmp(s); - gen_jr(s, s->T0); - break; - case 3: /* lcall Ev */ - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - do_lcall: - if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lcall_protected(cpu_env, s->tmp2_i32, s->T1, - tcg_const_i32(dflag - 1), - tcg_const_tl(s->pc - s->cs_base)); - } else { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lcall_real(cpu_env, s->tmp2_i32, s->T1, - tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - s->cs_base)); - } - tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, s->tmp4); - break; - case 4: /* jmp Ev */ - if (dflag == MO_16) { - tcg_gen_ext16u_tl(s->T0, s->T0); - } - gen_op_jmp_v(s->T0); - gen_bnd_jmp(s); - gen_jr(s, s->T0); - break; - case 5: /* ljmp Ev */ - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, s->T0, s->A0); - do_ljmp: - if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ljmp_protected(cpu_env, s->tmp2_i32, s->T1, - tcg_const_tl(s->pc - s->cs_base)); - } else { - gen_op_movl_seg_T0_vm(s, R_CS); - gen_op_jmp_v(s->T1); - } - tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, s->tmp4); - break; - case 6: /* push Ev */ - gen_push_v(s, s->T0); - break; - default: - goto unknown_op; - } - break; - - case 0x84: /* test Ev, Gv */ - case 0x85: - ot = mo_b_d(b, dflag); - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_v_reg(s, ot, s->T1, reg); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0xa8: /* test eAX, Iv */ - case 0xa9: - ot = mo_b_d(b, dflag); - val = insn_get(env, s, ot); - - gen_op_mov_v_reg(s, ot, s->T0, OR_EAX); - tcg_gen_movi_tl(s->T1, val); - gen_op_testl_T0_T1_cc(s); - set_cc_op(s, CC_OP_LOGICB + ot); - break; - - case 0x98: /* CWDE/CBW */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX); - tcg_gen_ext8s_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - tcg_abort(); - } - break; - case 0x99: /* CDQ/CWD */ - switch (dflag) { -#ifdef TARGET_X86_64 - case MO_64: - gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX); - tcg_gen_sari_tl(s->T0, s->T0, 63); - gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0); - break; -#endif - case MO_32: - gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); - tcg_gen_ext32s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 31); - gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0); - break; - case MO_16: - gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_sari_tl(s->T0, s->T0, 15); - gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); - break; - default: - tcg_abort(); - } - break; - case 0x1af: /* imul Gv, Ev */ - case 0x69: /* imul Gv, Ev, I */ - case 0x6b: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - if (b == 0x69) - s->rip_offset = insn_const_size(ot); - else if (b == 0x6b) - s->rip_offset = 1; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - if (b == 0x69) { - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T1, val); - } else if (b == 0x6b) { - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T1, val); - } else { - gen_op_mov_v_reg(s, ot, s->T1, reg); - } - switch (ot) { -#ifdef TARGET_X86_64 - case MO_64: - tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, s->T1); - break; -#endif - case MO_32: - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); - tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); - tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); - break; - default: - tcg_gen_ext16s_tl(s->T0, s->T0); - tcg_gen_ext16s_tl(s->T1, s->T1); - /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(s->T0, s->T0, s->T1); - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - tcg_gen_ext16s_tl(s->tmp0, s->T0); - tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - } - set_cc_op(s, CC_OP_MULB + ot); - break; - case 0x1c0: - case 0x1c1: /* xadd Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - gen_op_mov_v_reg(s, ot, s->T0, reg); - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, ot, s->T1, rm); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(s, ot, reg, s->T1); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_fetch_add_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - } else { - gen_op_ld_v(s, ot, s->T1, s->A0); - tcg_gen_add_tl(s->T0, s->T0, s->T1); - gen_op_st_v(s, ot, s->T0, s->A0); - } - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - gen_op_update2_cc(s); - set_cc_op(s, CC_OP_ADDB + ot); - break; - case 0x1b0: - case 0x1b1: /* cmpxchg Ev, Gv */ - { - TCGv oldv, newv, cmpv; - - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - oldv = tcg_temp_new(); - newv = tcg_temp_new(); - cmpv = tcg_temp_new(); - gen_op_mov_v_reg(s, ot, newv, reg); - tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); - - if (s->prefix & PREFIX_LOCK) { - if (mod == 3) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, - s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(s, ot, R_EAX, oldv); - } else { - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, ot, oldv, rm); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, oldv, s->A0); - rm = 0; /* avoid warning */ - } - gen_extu(ot, oldv); - gen_extu(ot, cmpv); - /* store value = (old == cmp ? new : old); */ - tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); - if (mod == 3) { - gen_op_mov_reg_v(s, ot, R_EAX, oldv); - gen_op_mov_reg_v(s, ot, rm, newv); - } else { - /* Perform an unconditional store cycle like physical cpu; - must be before changing accumulator to ensure - idempotency if the store faults and the instruction - is restarted */ - gen_op_st_v(s, ot, newv, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, oldv); - } - } - tcg_gen_mov_tl(cpu_cc_src, oldv); - tcg_gen_mov_tl(s->cc_srcT, cmpv); - tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); - set_cc_op(s, CC_OP_SUBB + ot); - tcg_temp_free(oldv); - tcg_temp_free(newv); - tcg_temp_free(cmpv); - } - break; - case 0x1c7: /* cmpxchg8b */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - switch ((modrm >> 3) & 7) { - case 1: /* CMPXCHG8, CMPXCHG16 */ - if (mod == 3) { - goto illegal_op; - } -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - if (!(s->cpuid_ext_features & CPUID_EXT_CX16)) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - if ((s->prefix & PREFIX_LOCK) && - (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg16b(cpu_env, s->A0); - } else { - gen_helper_cmpxchg16b_unlocked(cpu_env, s->A0); - } - set_cc_op(s, CC_OP_EFLAGS); - break; - } -#endif - if (!(s->cpuid_features & CPUID_CX8)) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - if ((s->prefix & PREFIX_LOCK) && - (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg8b(cpu_env, s->A0); - } else { - gen_helper_cmpxchg8b_unlocked(cpu_env, s->A0); - } - set_cc_op(s, CC_OP_EFLAGS); - break; - - case 7: /* RDSEED */ - case 6: /* RDRAND */ - if (mod != 3 || - (s->prefix & (PREFIX_LOCK | PREFIX_REPZ | PREFIX_REPNZ)) || - !(s->cpuid_ext_features & CPUID_EXT_RDRAND)) { - goto illegal_op; - } - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdrand(s->T0, cpu_env); - rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(s, dflag, rm, s->T0); - set_cc_op(s, CC_OP_EFLAGS); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - - default: - goto illegal_op; - } - break; - - /**************************/ - /* push/pop */ - case 0x50 ... 0x57: /* push */ - gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s)); - gen_push_v(s, s->T0); - break; - case 0x58 ... 0x5f: /* pop */ - ot = gen_pop_T0(s); - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0); - break; - case 0x60: /* pusha */ - if (CODE64(s)) - goto illegal_op; - gen_pusha(s); - break; - case 0x61: /* popa */ - if (CODE64(s)) - goto illegal_op; - gen_popa(s); - break; - case 0x68: /* push Iv */ - case 0x6a: - ot = mo_pushpop(s, dflag); - if (b == 0x68) - val = insn_get(env, s, ot); - else - val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_push_v(s, s->T0); - break; - case 0x8f: /* pop Ev */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - ot = gen_pop_T0(s); - if (mod == 3) { - /* NOTE: order is important for pop %sp */ - gen_pop_update(s, ot); - rm = (modrm & 7) | REX_B(s); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } else { - /* NOTE: order is important too for MMU exceptions */ - s->popl_esp_hack = 1 << ot; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - s->popl_esp_hack = 0; - gen_pop_update(s, ot); - } - break; - case 0xc8: /* enter */ - { - int level; - val = x86_lduw_code(env, s); - level = x86_ldub_code(env, s); - gen_enter(s, val, level); - } - break; - case 0xc9: /* leave */ - gen_leave(s); - break; - case 0x06: /* push es */ - case 0x0e: /* push cs */ - case 0x16: /* push ss */ - case 0x1e: /* push ds */ - if (CODE64(s)) - goto illegal_op; - gen_op_movl_T0_seg(s, b >> 3); - gen_push_v(s, s->T0); - break; - case 0x1a0: /* push fs */ - case 0x1a8: /* push gs */ - gen_op_movl_T0_seg(s, (b >> 3) & 7); - gen_push_v(s, s->T0); - break; - case 0x07: /* pop es */ - case 0x17: /* pop ss */ - case 0x1f: /* pop ds */ - if (CODE64(s)) - goto illegal_op; - reg = b >> 3; - ot = gen_pop_T0(s); - gen_movl_seg_T0(s, reg); - gen_pop_update(s, ot); - /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - if (reg == R_SS) { - s->tf = 0; - gen_eob_inhibit_irq(s, true); - } else { - gen_eob(s); - } - } - break; - case 0x1a1: /* pop fs */ - case 0x1a9: /* pop gs */ - ot = gen_pop_T0(s); - gen_movl_seg_T0(s, (b >> 3) & 7); - gen_pop_update(s, ot); - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } - break; - - /**************************/ - /* mov */ - case 0x88: - case 0x89: /* mov Gv, Ev */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; - case 0xc6: - case 0xc7: /* mov Ev, Iv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod != 3) { - s->rip_offset = insn_const_size(ot); - gen_lea_modrm(env, s, modrm); - } - val = insn_get(env, s, ot); - tcg_gen_movi_tl(s->T0, val); - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0); - } - break; - case 0x8a: - case 0x8b: /* mov Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - case 0x8e: /* mov seg, Gv */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - if (reg >= 6 || reg == R_CS) - goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_movl_seg_T0(s, reg); - /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - if (reg == R_SS) { - s->tf = 0; - gen_eob_inhibit_irq(s, true); - } else { - gen_eob(s); - } - } - break; - case 0x8c: /* mov Gv, seg */ - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (reg >= 6) - goto illegal_op; - gen_op_movl_T0_seg(s, reg); - ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - - case 0x1b6: /* movzbS Gv, Eb */ - case 0x1b7: /* movzwS Gv, Eb */ - case 0x1be: /* movsbS Gv, Eb */ - case 0x1bf: /* movswS Gv, Eb */ - { - MemOp d_ot; - MemOp s_ot; - - /* d_ot is the size of destination */ - d_ot = dflag; - /* ot is the size of source */ - ot = (b & 1) + MO_8; - /* s_ot is the sign+size of source */ - s_ot = b & 8 ? MO_SIGN | ot : ot; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) { - tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8); - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - switch (s_ot) { - case MO_UB: - tcg_gen_ext8u_tl(s->T0, s->T0); - break; - case MO_SB: - tcg_gen_ext8s_tl(s->T0, s->T0); - break; - case MO_UW: - tcg_gen_ext16u_tl(s->T0, s->T0); - break; - default: - case MO_SW: - tcg_gen_ext16s_tl(s->T0, s->T0); - break; - } - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, s_ot, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } - break; - - case 0x8d: /* lea */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | rex_r; - { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(s, a); - gen_lea_v_seg(s, s->aflag, ea, -1, -1); - gen_op_mov_reg_v(s, dflag, reg, s->A0); - } - break; - - case 0xa0: /* mov EAX, Ov */ - case 0xa1: - case 0xa2: /* mov Ov, EAX */ - case 0xa3: - { - target_ulong offset_addr; - - ot = mo_b_d(b, dflag); - switch (s->aflag) { -#ifdef TARGET_X86_64 - case MO_64: - offset_addr = x86_ldq_code(env, s); - break; -#endif - default: - offset_addr = insn_get(env, s, s->aflag); - break; - } - tcg_gen_movi_tl(s->A0, offset_addr); - gen_add_A0_ds_seg(s); - if ((b & 2) == 0) { - gen_op_ld_v(s, ot, s->T0, s->A0); - gen_op_mov_reg_v(s, ot, R_EAX, s->T0); - } else { - gen_op_mov_v_reg(s, ot, s->T0, R_EAX); - gen_op_st_v(s, ot, s->T0, s->A0); - } - } - break; - case 0xd7: /* xlat */ - tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]); - tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]); - tcg_gen_add_tl(s->A0, s->A0, s->T0); - gen_extu(s->aflag, s->A0); - gen_add_A0_ds_seg(s); - gen_op_ld_v(s, MO_8, s->T0, s->A0); - gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); - break; - case 0xb0 ... 0xb7: /* mov R, Ib */ - val = insn_get(env, s, MO_8); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0); - break; - case 0xb8 ... 0xbf: /* mov R, Iv */ -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - uint64_t tmp; - /* 64 bit case */ - tmp = x86_ldq_code(env, s); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, tmp); - gen_op_mov_reg_v(s, MO_64, reg, s->T0); - } else -#endif - { - ot = dflag; - val = insn_get(env, s, ot); - reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(s->T0, val); - gen_op_mov_reg_v(s, ot, reg, s->T0); - } - break; - - case 0x91 ... 0x97: /* xchg R, EAX */ - do_xchg_reg_eax: - ot = dflag; - reg = (b & 7) | REX_B(s); - rm = R_EAX; - goto do_xchg_reg; - case 0x86: - case 0x87: /* xchg Ev, Gv */ - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - if (mod == 3) { - rm = (modrm & 7) | REX_B(s); - do_xchg_reg: - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_op_mov_v_reg(s, ot, s->T1, rm); - gen_op_mov_reg_v(s, ot, rm, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(s, ot, s->T0, reg); - /* for xchg, lock is implicit */ - tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0, - s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(s, ot, reg, s->T1); - } - break; - case 0xc4: /* les Gv */ - /* In CODE64 this is VEX3; see above. */ - op = R_ES; - goto do_lxx; - case 0xc5: /* lds Gv */ - /* In CODE64 this is VEX2; see above. */ - op = R_DS; - goto do_lxx; - case 0x1b2: /* lss Gv */ - op = R_SS; - goto do_lxx; - case 0x1b4: /* lfs Gv */ - op = R_FS; - goto do_lxx; - case 0x1b5: /* lgs Gv */ - op = R_GS; - do_lxx: - ot = dflag != MO_16 ? MO_32 : MO_16; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, s->T1, s->A0); - gen_add_A0_im(s, 1 << ot); - /* load the segment first to handle exceptions properly */ - gen_op_ld_v(s, MO_16, s->T0, s->A0); - gen_movl_seg_T0(s, op); - /* then put the data */ - gen_op_mov_reg_v(s, ot, reg, s->T1); - if (s->base.is_jmp) { - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } - break; - - /************************/ - /* shifts */ - case 0xc0: - case 0xc1: - /* shift Ev,Ib */ - shift = 2; - grp2: - { - ot = mo_b_d(b, dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - - if (mod != 3) { - if (shift == 2) { - s->rip_offset = 1; - } - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = (modrm & 7) | REX_B(s); - } - - /* simpler op */ - if (shift == 0) { - gen_shift(s, op, ot, opreg, OR_ECX); - } else { - if (shift == 2) { - shift = x86_ldub_code(env, s); - } - gen_shifti(s, op, ot, opreg, shift); - } - } - break; - case 0xd0: - case 0xd1: - /* shift Ev,1 */ - shift = 1; - goto grp2; - case 0xd2: - case 0xd3: - /* shift Ev,cl */ - shift = 0; - goto grp2; - - case 0x1a4: /* shld imm */ - op = 0; - shift = 1; - goto do_shiftd; - case 0x1a5: /* shld cl */ - op = 0; - shift = 0; - goto do_shiftd; - case 0x1ac: /* shrd imm */ - op = 1; - shift = 1; - goto do_shiftd; - case 0x1ad: /* shrd cl */ - op = 1; - shift = 0; - do_shiftd: - ot = dflag; - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | rex_r; - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - opreg = OR_TMP0; - } else { - opreg = rm; - } - gen_op_mov_v_reg(s, ot, s->T1, reg); - - if (shift) { - TCGv imm = tcg_const_tl(x86_ldub_code(env, s)); - gen_shiftd_rm_T1(s, ot, opreg, op, imm); - tcg_temp_free(imm); - } else { - gen_shiftd_rm_T1(s, ot, opreg, op, cpu_regs[R_ECX]); - } - break; - - /************************/ - /* floats */ - case 0xd8 ... 0xdf: - if (s->flags & (HF_EM_MASK | HF_TS_MASK)) { - /* if CR0.EM or CR0.TS are set, generate an FPU exception */ - /* XXX: what to do if illegal op ? */ - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - break; - } - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - rm = modrm & 7; - op = ((b & 7) << 3) | ((modrm >> 3) & 7); - if (mod != 3) { - /* memory op */ - gen_lea_modrm(env, s, modrm); - switch(op) { - case 0x00 ... 0x07: /* fxxxs */ - case 0x10 ... 0x17: /* fixxxl */ - case 0x20 ... 0x27: /* fxxxl */ - case 0x30 ... 0x37: /* fixxx */ - { - int op1; - op1 = op & 7; - - switch(op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_flds_FT0(cpu_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - gen_helper_fldl_FT0(cpu_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); - break; - } - - gen_helper_fp_arith_ST0_FT0(op1); - if (op1 == 3) { - /* fcomp needs pop */ - gen_helper_fpop(cpu_env); - } - } - break; - case 0x08: /* flds */ - case 0x0a: /* fsts */ - case 0x0b: /* fstps */ - case 0x18 ... 0x1b: /* fildl, fisttpl, fistl, fistpl */ - case 0x28 ... 0x2b: /* fldl, fisttpll, fstl, fstpl */ - case 0x38 ... 0x3b: /* filds, fisttps, fists, fistps */ - switch(op & 7) { - case 0: - switch(op >> 4) { - case 0: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_flds_ST0(cpu_env, s->tmp2_i32); - break; - case 1: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); - break; - case 2: - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - gen_helper_fldl_ST0(cpu_env, s->tmp1_i64); - break; - case 3: - default: - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LESW); - gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); - break; - } - break; - case 1: - /* XXX: the corresponding CPUID bit must be tested ! */ - switch(op >> 4) { - case 1: - gen_helper_fisttl_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_helper_fisttll_ST0(s->tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - break; - case 3: - default: - gen_helper_fistt_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - gen_helper_fpop(cpu_env); - break; - default: - switch(op >> 4) { - case 0: - gen_helper_fsts_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 1: - gen_helper_fistl_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUL); - break; - case 2: - gen_helper_fstl_ST0(s->tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, - s->mem_index, MO_LEQ); - break; - case 3: - default: - gen_helper_fist_ST0(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - } - if ((op & 7) == 3) - gen_helper_fpop(cpu_env); - break; - } - break; - case 0x0c: /* fldenv mem */ - gen_helper_fldenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); - break; - case 0x0d: /* fldcw mem */ - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - gen_helper_fldcw(cpu_env, s->tmp2_i32); - break; - case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); - break; - case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - case 0x1d: /* fldt mem */ - gen_helper_fldt_ST0(cpu_env, s->A0); - break; - case 0x1f: /* fstpt mem */ - gen_helper_fstt_ST0(cpu_env, s->A0); - gen_helper_fpop(cpu_env); - break; - case 0x2c: /* frstor mem */ - gen_helper_frstor(cpu_env, s->A0, tcg_const_i32(dflag - 1)); - break; - case 0x2e: /* fnsave mem */ - gen_helper_fsave(cpu_env, s->A0, tcg_const_i32(dflag - 1)); - break; - case 0x2f: /* fnstsw mem */ - gen_helper_fnstsw(s->tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, - s->mem_index, MO_LEUW); - break; - case 0x3c: /* fbld */ - gen_helper_fbld_ST0(cpu_env, s->A0); - break; - case 0x3e: /* fbstp */ - gen_helper_fbst_ST0(cpu_env, s->A0); - gen_helper_fpop(cpu_env); - break; - case 0x3d: /* fildll */ - tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); - gen_helper_fildll_ST0(cpu_env, s->tmp1_i64); - break; - case 0x3f: /* fistpll */ - gen_helper_fistll_ST0(s->tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); - gen_helper_fpop(cpu_env); - break; - default: - goto unknown_op; - } - } else { - /* register float ops */ - opreg = rm; - - switch(op) { - case 0x08: /* fld sti */ - gen_helper_fpush(cpu_env); - gen_helper_fmov_ST0_STN(cpu_env, - tcg_const_i32((opreg + 1) & 7)); - break; - case 0x09: /* fxchg sti */ - case 0x29: /* fxchg4 sti, undocumented op */ - case 0x39: /* fxchg7 sti, undocumented op */ - gen_helper_fxchg_ST0_STN(cpu_env, tcg_const_i32(opreg)); - break; - case 0x0a: /* grp d9/2 */ - switch(rm) { - case 0: /* fnop */ - /* check exceptions (FreeBSD FPU probe) */ - gen_helper_fwait(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x0c: /* grp d9/4 */ - switch(rm) { - case 0: /* fchs */ - gen_helper_fchs_ST0(cpu_env); - break; - case 1: /* fabs */ - gen_helper_fabs_ST0(cpu_env); - break; - case 4: /* ftst */ - gen_helper_fldz_FT0(cpu_env); - gen_helper_fcom_ST0_FT0(cpu_env); - break; - case 5: /* fxam */ - gen_helper_fxam_ST0(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x0d: /* grp d9/5 */ - { - switch(rm) { - case 0: - gen_helper_fpush(cpu_env); - gen_helper_fld1_ST0(cpu_env); - break; - case 1: - gen_helper_fpush(cpu_env); - gen_helper_fldl2t_ST0(cpu_env); - break; - case 2: - gen_helper_fpush(cpu_env); - gen_helper_fldl2e_ST0(cpu_env); - break; - case 3: - gen_helper_fpush(cpu_env); - gen_helper_fldpi_ST0(cpu_env); - break; - case 4: - gen_helper_fpush(cpu_env); - gen_helper_fldlg2_ST0(cpu_env); - break; - case 5: - gen_helper_fpush(cpu_env); - gen_helper_fldln2_ST0(cpu_env); - break; - case 6: - gen_helper_fpush(cpu_env); - gen_helper_fldz_ST0(cpu_env); - break; - default: - goto unknown_op; - } - } - break; - case 0x0e: /* grp d9/6 */ - switch(rm) { - case 0: /* f2xm1 */ - gen_helper_f2xm1(cpu_env); - break; - case 1: /* fyl2x */ - gen_helper_fyl2x(cpu_env); - break; - case 2: /* fptan */ - gen_helper_fptan(cpu_env); - break; - case 3: /* fpatan */ - gen_helper_fpatan(cpu_env); - break; - case 4: /* fxtract */ - gen_helper_fxtract(cpu_env); - break; - case 5: /* fprem1 */ - gen_helper_fprem1(cpu_env); - break; - case 6: /* fdecstp */ - gen_helper_fdecstp(cpu_env); - break; - default: - case 7: /* fincstp */ - gen_helper_fincstp(cpu_env); - break; - } - break; - case 0x0f: /* grp d9/7 */ - switch(rm) { - case 0: /* fprem */ - gen_helper_fprem(cpu_env); - break; - case 1: /* fyl2xp1 */ - gen_helper_fyl2xp1(cpu_env); - break; - case 2: /* fsqrt */ - gen_helper_fsqrt(cpu_env); - break; - case 3: /* fsincos */ - gen_helper_fsincos(cpu_env); - break; - case 5: /* fscale */ - gen_helper_fscale(cpu_env); - break; - case 4: /* frndint */ - gen_helper_frndint(cpu_env); - break; - case 6: /* fsin */ - gen_helper_fsin(cpu_env); - break; - default: - case 7: /* fcos */ - gen_helper_fcos(cpu_env); - break; - } - break; - case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ - case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ - case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ - { - int op1; - - op1 = op & 7; - if (op >= 0x20) { - gen_helper_fp_arith_STN_ST0(op1, opreg); - if (op >= 0x30) - gen_helper_fpop(cpu_env); - } else { - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fp_arith_ST0_FT0(op1); - } - } - break; - case 0x02: /* fcom */ - case 0x22: /* fcom2, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); - break; - case 0x03: /* fcomp */ - case 0x23: /* fcomp3, undocumented op */ - case 0x32: /* fcomp5, undocumented op */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - break; - case 0x15: /* da/5 */ - switch(rm) { - case 1: /* fucompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x1c: - switch(rm) { - case 0: /* feni (287 only, just do nop here) */ - break; - case 1: /* fdisi (287 only, just do nop here) */ - break; - case 2: /* fclex */ - gen_helper_fclex(cpu_env); - break; - case 3: /* fninit */ - gen_helper_fninit(cpu_env); - break; - case 4: /* fsetpm (287 only, just do nop here) */ - break; - default: - goto unknown_op; - } - break; - case 0x1d: /* fucomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucomi_ST0_FT0(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x1e: /* fcomi */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcomi_ST0_FT0(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x28: /* ffree sti */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); - break; - case 0x2a: /* fst sti */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); - break; - case 0x2b: /* fstp sti */ - case 0x0b: /* fstp1 sti, undocumented op */ - case 0x3a: /* fstp8 sti, undocumented op */ - case 0x3b: /* fstp9 sti, undocumented op */ - gen_helper_fmov_STN_ST0(cpu_env, tcg_const_i32(opreg)); - gen_helper_fpop(cpu_env); - break; - case 0x2c: /* fucom st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); - break; - case 0x2d: /* fucomp st(i) */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - break; - case 0x33: /* de/3 */ - switch(rm) { - case 1: /* fcompp */ - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(1)); - gen_helper_fcom_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - gen_helper_fpop(cpu_env); - break; - default: - goto unknown_op; - } - break; - case 0x38: /* ffreep sti, undocumented op */ - gen_helper_ffree_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fpop(cpu_env); - break; - case 0x3c: /* df/4 */ - switch(rm) { - case 0: - gen_helper_fnstsw(s->tmp2_i32, cpu_env); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); - break; - default: - goto unknown_op; - } - break; - case 0x3d: /* fucomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fucomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x3e: /* fcomip */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_helper_fmov_FT0_STN(cpu_env, tcg_const_i32(opreg)); - gen_helper_fcomi_ST0_FT0(cpu_env); - gen_helper_fpop(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x10 ... 0x13: /* fcmovxx */ - case 0x18 ... 0x1b: - { - int op1; - TCGLabel *l1; - static const uint8_t fcmov_cc[8] = { - (JCC_B << 1), - (JCC_Z << 1), - (JCC_BE << 1), - (JCC_P << 1), - }; - - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - op1 = fcmov_cc[op & 3] | (((op >> 3) & 1) ^ 1); - l1 = gen_new_label(); - gen_jcc1_noeob(s, op1, l1); - gen_helper_fmov_ST0_STN(cpu_env, tcg_const_i32(opreg)); - gen_set_label(l1); - } - break; - default: - goto unknown_op; - } - } - break; - /************************/ - /* string ops */ - - case 0xa4: /* movsS */ - case 0xa5: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_movs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - } else { - gen_movs(s, ot); - } - break; - - case 0xaa: /* stosS */ - case 0xab: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_stos(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - } else { - gen_stos(s, ot); - } - break; - case 0xac: /* lodsS */ - case 0xad: - ot = mo_b_d(b, dflag); - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_lods(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - } else { - gen_lods(s, ot); - } - break; - case 0xae: /* scasS */ - case 0xaf: - ot = mo_b_d(b, dflag); - if (prefixes & PREFIX_REPNZ) { - gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_scas(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); - } else { - gen_scas(s, ot); - } - break; - - case 0xa6: /* cmpsS */ - case 0xa7: - ot = mo_b_d(b, dflag); - if (prefixes & PREFIX_REPNZ) { - gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 1); - } else if (prefixes & PREFIX_REPZ) { - gen_repz_cmps(s, ot, pc_start - s->cs_base, s->pc - s->cs_base, 0); - } else { - gen_cmps(s, ot); - } - break; - case 0x6c: /* insS */ - case 0x6d: - ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); - gen_check_io(s, ot, pc_start - s->cs_base, - SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes) | 4); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_ins(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - /* jump generated by gen_repz_ins */ - } else { - gen_ins(s, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - } - break; - case 0x6e: /* outsS */ - case 0x6f: - ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); - gen_check_io(s, ot, pc_start - s->cs_base, - svm_is_rep(prefixes) | 4); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) { - gen_repz_outs(s, ot, pc_start - s->cs_base, s->pc - s->cs_base); - /* jump generated by gen_repz_outs */ - } else { - gen_outs(s, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - } - break; - - /************************/ - /* port I/O */ - - case 0xe4: - case 0xe5: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_tl(s->T0, val); - gen_check_io(s, ot, pc_start - s->cs_base, - SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - tcg_gen_movi_i32(s->tmp2_i32, val); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - case 0xe6: - case 0xe7: - ot = mo_b_d32(b, dflag); - val = x86_ldub_code(env, s); - tcg_gen_movi_tl(s->T0, val); - gen_check_io(s, ot, pc_start - s->cs_base, - svm_is_rep(prefixes)); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - tcg_gen_movi_i32(s->tmp2_i32, val); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - case 0xec: - case 0xed: - ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); - gen_check_io(s, ot, pc_start - s->cs_base, - SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_in_func(ot, s->T1, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, R_EAX, s->T1); - gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - case 0xee: - case 0xef: - ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); - gen_check_io(s, ot, pc_start - s->cs_base, - svm_is_rep(prefixes)); - gen_op_mov_v_reg(s, ot, s->T1, R_EAX); - - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); - gen_bpt_io(s, s->tmp2_i32, ot); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - - /************************/ - /* control */ - case 0xc2: /* ret im */ - val = x86_ldsw_code(env, s); - ot = gen_pop_T0(s); - gen_stack_update(s, val + (1 << ot)); - /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s->T0); - gen_bnd_jmp(s); - gen_jr(s, s->T0); - break; - case 0xc3: /* ret */ - ot = gen_pop_T0(s); - gen_pop_update(s, ot); - /* Note that gen_pop_T0 uses a zero-extending load. */ - gen_op_jmp_v(s->T0); - gen_bnd_jmp(s); - gen_jr(s, s->T0); - break; - case 0xca: /* lret im */ - val = x86_ldsw_code(env, s); - do_lret: - if (s->pe && !s->vm86) { - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), - tcg_const_i32(val)); - } else { - gen_stack_A0(s); - /* pop offset */ - gen_op_ld_v(s, dflag, s->T0, s->A0); - /* NOTE: keeping EIP updated is not a problem in case of - exception */ - gen_op_jmp_v(s->T0); - /* pop selector */ - gen_add_A0_im(s, 1 << dflag); - gen_op_ld_v(s, dflag, s->T0, s->A0); - gen_op_movl_seg_T0_vm(s, R_CS); - /* add stack offset */ - gen_stack_update(s, val + (2 << dflag)); - } - gen_eob(s); - break; - case 0xcb: /* lret */ - val = 0; - goto do_lret; - case 0xcf: /* iret */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_IRET); - if (!s->pe) { - /* real mode */ - gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); - set_cc_op(s, CC_OP_EFLAGS); - } else if (s->vm86) { - if (s->iopl != 3) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_helper_iret_real(cpu_env, tcg_const_i32(dflag - 1)); - set_cc_op(s, CC_OP_EFLAGS); - } - } else { - gen_helper_iret_protected(cpu_env, tcg_const_i32(dflag - 1), - tcg_const_i32(s->pc - s->cs_base)); - set_cc_op(s, CC_OP_EFLAGS); - } - gen_eob(s); - break; - case 0xe8: /* call im */ - { - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); - } - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } else if (!CODE64(s)) { - tval &= 0xffffffff; - } - tcg_gen_movi_tl(s->T0, next_eip); - gen_push_v(s, s->T0); - gen_bnd_jmp(s); - gen_jmp(s, tval); - } - break; - case 0x9a: /* lcall im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); - } - goto do_lcall; - case 0xe9: /* jmp im */ - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); - } - tval += s->pc - s->cs_base; - if (dflag == MO_16) { - tval &= 0xffff; - } else if (!CODE64(s)) { - tval &= 0xffffffff; - } - gen_bnd_jmp(s); - gen_jmp(s, tval); - break; - case 0xea: /* ljmp im */ - { - unsigned int selector, offset; - - if (CODE64(s)) - goto illegal_op; - ot = dflag; - offset = insn_get(env, s, ot); - selector = insn_get(env, s, MO_16); - - tcg_gen_movi_tl(s->T0, selector); - tcg_gen_movi_tl(s->T1, offset); - } - goto do_ljmp; - case 0xeb: /* jmp Jb */ - tval = (int8_t)insn_get(env, s, MO_8); - tval += s->pc - s->cs_base; - if (dflag == MO_16) { - tval &= 0xffff; - } - gen_jmp(s, tval); - break; - case 0x70 ... 0x7f: /* jcc Jb */ - tval = (int8_t)insn_get(env, s, MO_8); - goto do_jcc; - case 0x180 ... 0x18f: /* jcc Jv */ - if (dflag != MO_16) { - tval = (int32_t)insn_get(env, s, MO_32); - } else { - tval = (int16_t)insn_get(env, s, MO_16); - } - do_jcc: - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } - gen_bnd_jmp(s); - gen_jcc(s, b, tval, next_eip); - break; - - case 0x190 ... 0x19f: /* setcc Gv */ - modrm = x86_ldub_code(env, s); - gen_setcc1(s, b, s->T0); - gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); - break; - case 0x140 ... 0x14f: /* cmov Gv, Ev */ - if (!(s->cpuid_features & CPUID_CMOV)) { - goto illegal_op; - } - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - gen_cmovcc1(env, s, ot, b, modrm, reg); - break; - - /************************/ - /* flags */ - case 0x9c: /* pushf */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_PUSHF); - if (s->vm86 && s->iopl != 3) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_update_cc_op(s); - gen_helper_read_eflags(s->T0, cpu_env); - gen_push_v(s, s->T0); - } - break; - case 0x9d: /* popf */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_POPF); - if (s->vm86 && s->iopl != 3) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - ot = gen_pop_T0(s); - if (s->cpl == 0) { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK | - IF_MASK | - IOPL_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK | - IF_MASK | IOPL_MASK) - & 0xffff)); - } - } else { - if (s->cpl <= s->iopl) { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | - AC_MASK | - ID_MASK | - NT_MASK | - IF_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | - AC_MASK | - ID_MASK | - NT_MASK | - IF_MASK) - & 0xffff)); - } - } else { - if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK))); - } else { - gen_helper_write_eflags(cpu_env, s->T0, - tcg_const_i32((TF_MASK | AC_MASK | - ID_MASK | NT_MASK) - & 0xffff)); - } - } - } - gen_pop_update(s, ot); - set_cc_op(s, CC_OP_EFLAGS); - /* abort translation because TF/AC flag may change */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } - break; - case 0x9e: /* sahf */ - if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) - goto illegal_op; - gen_op_mov_v_reg(s, MO_8, s->T0, R_AH); - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); - tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); - break; - case 0x9f: /* lahf */ - if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) - goto illegal_op; - gen_compute_eflags(s); - /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); - gen_op_mov_reg_v(s, MO_8, R_AH, s->T0); - break; - case 0xf5: /* cmc */ - gen_compute_eflags(s); - tcg_gen_xori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xf8: /* clc */ - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_C); - break; - case 0xf9: /* stc */ - gen_compute_eflags(s); - tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); - break; - case 0xfc: /* cld */ - tcg_gen_movi_i32(s->tmp2_i32, 1); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); - break; - case 0xfd: /* std */ - tcg_gen_movi_i32(s->tmp2_i32, -1); - tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); - break; - - /************************/ - /* bit operations */ - case 0x1ba: /* bt/bts/btr/btc Gv, im */ - ot = dflag; - modrm = x86_ldub_code(env, s); - op = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - if (mod != 3) { - s->rip_offset = 1; - gen_lea_modrm(env, s, modrm); - if (!(s->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - /* load shift */ - val = x86_ldub_code(env, s); - tcg_gen_movi_tl(s->T1, val); - if (op < 4) - goto unknown_op; - op -= 4; - goto bt_op; - case 0x1a3: /* bt Gv, Ev */ - op = 0; - goto do_btx; - case 0x1ab: /* bts */ - op = 1; - goto do_btx; - case 0x1b3: /* btr */ - op = 2; - goto do_btx; - case 0x1bb: /* btc */ - op = 3; - do_btx: - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(s, MO_32, s->T1, reg); - if (mod != 3) { - AddressParts a = gen_lea_modrm_0(env, s, modrm); - /* specific case: we need to add a displacement */ - gen_exts(ot, s->T1); - tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); - tcg_gen_shli_tl(s->tmp0, s->tmp0, ot); - tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a), s->tmp0); - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); - if (!(s->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s, ot, s->T0, s->A0); - } - } else { - gen_op_mov_v_reg(s, ot, s->T0, rm); - } - bt_op: - tcg_gen_andi_tl(s->T1, s->T1, (1 << (3 + ot)) - 1); - tcg_gen_movi_tl(s->tmp0, 1); - tcg_gen_shl_tl(s->tmp0, s->tmp0, s->T1); - if (s->prefix & PREFIX_LOCK) { - switch (op) { - case 0: /* bt */ - /* Needs no atomic ops; we surpressed the normal - memory load for LOCK above so do it now. */ - gen_op_ld_v(s, ot, s->T0, s->A0); - break; - case 1: /* bts */ - tcg_gen_atomic_fetch_or_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - case 2: /* btr */ - tcg_gen_not_tl(s->tmp0, s->tmp0); - tcg_gen_atomic_fetch_and_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - default: - case 3: /* btc */ - tcg_gen_atomic_fetch_xor_tl(s->T0, s->A0, s->tmp0, - s->mem_index, ot | MO_LE); - break; - } - tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); - } else { - tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); - switch (op) { - case 0: /* bt */ - /* Data already loaded; nothing to do. */ - break; - case 1: /* bts */ - tcg_gen_or_tl(s->T0, s->T0, s->tmp0); - break; - case 2: /* btr */ - tcg_gen_andc_tl(s->T0, s->T0, s->tmp0); - break; - default: - case 3: /* btc */ - tcg_gen_xor_tl(s->T0, s->T0, s->tmp0); - break; - } - if (op != 0) { - if (mod != 3) { - gen_op_st_v(s, ot, s->T0, s->A0); - } else { - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - } - - /* Delay all CC updates until after the store above. Note that - C is the result of the test, Z is unchanged, and the others - are all undefined. */ - switch (s->cc_op) { - case CC_OP_MULB ... CC_OP_MULQ: - case CC_OP_ADDB ... CC_OP_ADDQ: - case CC_OP_ADCB ... CC_OP_ADCQ: - case CC_OP_SUBB ... CC_OP_SUBQ: - case CC_OP_SBBB ... CC_OP_SBBQ: - case CC_OP_LOGICB ... CC_OP_LOGICQ: - case CC_OP_INCB ... CC_OP_INCQ: - case CC_OP_DECB ... CC_OP_DECQ: - case CC_OP_SHLB ... CC_OP_SHLQ: - case CC_OP_SARB ... CC_OP_SARQ: - case CC_OP_BMILGB ... CC_OP_BMILGQ: - /* Z was going to be computed from the non-zero status of CC_DST. - We can get that same Z value (and the new C value) by leaving - CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the - same width. */ - tcg_gen_mov_tl(cpu_cc_src, s->tmp4); - set_cc_op(s, ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB); - break; - default: - /* Otherwise, generate EFLAGS and replace the C bit. */ - gen_compute_eflags(s); - tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, s->tmp4, - ctz32(CC_C), 1); - break; - } - break; - case 0x1bc: /* bsf / tzcnt */ - case 0x1bd: /* bsr / lzcnt */ - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, s->T0); - - /* Note that lzcnt and tzcnt are in different extensions. */ - if ((prefixes & PREFIX_REPZ) - && (b & 1 - ? s->cpuid_ext3_features & CPUID_EXT3_ABM - : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { - int size = 8 << ot; - /* For lzcnt/tzcnt, C bit is defined related to the input. */ - tcg_gen_mov_tl(cpu_cc_src, s->T0); - if (b & 1) { - /* For lzcnt, reduce the target_ulong result by the - number of zeros that we expect to find at the top. */ - tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); - tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - size); - } else { - /* For tzcnt, a zero input must return the operand size. */ - tcg_gen_ctzi_tl(s->T0, s->T0, size); - } - /* For lzcnt/tzcnt, Z bit is defined related to the result. */ - gen_op_update1_cc(s); - set_cc_op(s, CC_OP_BMILGB + ot); - } else { - /* For bsr/bsf, only the Z bit is defined and it is related - to the input and not the result. */ - tcg_gen_mov_tl(cpu_cc_dst, s->T0); - set_cc_op(s, CC_OP_LOGICB + ot); - - /* ??? The manual says that the output is undefined when the - input is zero, but real hardware leaves it unchanged, and - real programs appear to depend on that. Accomplish this - by passing the output as the value to return upon zero. */ - if (b & 1) { - /* For bsr, return the bit index of the first 1 bit, - not the count of leading zeros. */ - tcg_gen_xori_tl(s->T1, cpu_regs[reg], TARGET_LONG_BITS - 1); - tcg_gen_clz_tl(s->T0, s->T0, s->T1); - tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); - } else { - tcg_gen_ctz_tl(s->T0, s->T0, cpu_regs[reg]); - } - } - gen_op_mov_reg_v(s, ot, reg, s->T0); - break; - /************************/ - /* bcd */ - case 0x27: /* daa */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_daa(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x2f: /* das */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_das(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x37: /* aaa */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_aaa(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0x3f: /* aas */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_helper_aas(cpu_env); - set_cc_op(s, CC_OP_EFLAGS); - break; - case 0xd4: /* aam */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - if (val == 0) { - gen_exception(s, EXCP00_DIVZ, pc_start - s->cs_base); - } else { - gen_helper_aam(cpu_env, tcg_const_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - } - break; - case 0xd5: /* aad */ - if (CODE64(s)) - goto illegal_op; - val = x86_ldub_code(env, s); - gen_helper_aad(cpu_env, tcg_const_i32(val)); - set_cc_op(s, CC_OP_LOGICB); - break; - /************************/ - /* misc */ - case 0x90: /* nop */ - /* XXX: correct lock test for all insn */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - /* If REX_B is set, then this is xchg eax, r8d, not a nop. */ - if (REX_B(s)) { - goto do_xchg_reg_eax; - } - if (prefixes & PREFIX_REPZ) { - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start)); - s->base.is_jmp = DISAS_NORETURN; - } - break; - case 0x9b: /* fwait */ - if ((s->flags & (HF_MP_MASK | HF_TS_MASK)) == - (HF_MP_MASK | HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - } else { - gen_helper_fwait(cpu_env); - } - break; - case 0xcc: /* int3 */ - gen_interrupt(s, EXCP03_INT3, pc_start - s->cs_base, s->pc - s->cs_base); - break; - case 0xcd: /* int N */ - val = x86_ldub_code(env, s); - if (s->vm86 && s->iopl != 3) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_interrupt(s, val, pc_start - s->cs_base, s->pc - s->cs_base); - } - break; - case 0xce: /* into */ - if (CODE64(s)) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); - break; -#ifdef WANT_ICEBP - case 0xf1: /* icebp (undocumented, exits to external debugger) */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_ICEBP); - gen_debug(s, pc_start - s->cs_base); - break; -#endif - case 0xfa: /* cli */ - if (!s->vm86) { - if (s->cpl <= s->iopl) { - gen_helper_cli(cpu_env); - } else { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } - } else { - if (s->iopl == 3) { - gen_helper_cli(cpu_env); - } else { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } - } - break; - case 0xfb: /* sti */ - if (s->vm86 ? s->iopl == 3 : s->cpl <= s->iopl) { - gen_helper_sti(cpu_env); - /* interruptions are enabled only the first insn after sti */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob_inhibit_irq(s, true); - } else { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } - break; - case 0x62: /* bound */ - if (CODE64(s)) - goto illegal_op; - ot = dflag; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_op_mov_v_reg(s, ot, s->T0, reg); - gen_lea_modrm(env, s, modrm); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - if (ot == MO_16) { - gen_helper_boundw(cpu_env, s->A0, s->tmp2_i32); - } else { - gen_helper_boundl(cpu_env, s->A0, s->tmp2_i32); - } - break; - case 0x1c8 ... 0x1cf: /* bswap reg */ - reg = (b & 7) | REX_B(s); -#ifdef TARGET_X86_64 - if (dflag == MO_64) { - gen_op_mov_v_reg(s, MO_64, s->T0, reg); - tcg_gen_bswap64_i64(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_64, reg, s->T0); - } else -#endif - { - gen_op_mov_v_reg(s, MO_32, s->T0, reg); - tcg_gen_ext32u_tl(s->T0, s->T0); - tcg_gen_bswap32_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_32, reg, s->T0); - } - break; - case 0xd6: /* salc */ - if (CODE64(s)) - goto illegal_op; - gen_compute_eflags_c(s, s->T0); - tcg_gen_neg_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); - break; - case 0xe0: /* loopnz */ - case 0xe1: /* loopz */ - case 0xe2: /* loop */ - case 0xe3: /* jecxz */ - { - TCGLabel *l1, *l2, *l3; - - tval = (int8_t)insn_get(env, s, MO_8); - next_eip = s->pc - s->cs_base; - tval += next_eip; - if (dflag == MO_16) { - tval &= 0xffff; - } - - l1 = gen_new_label(); - l2 = gen_new_label(); - l3 = gen_new_label(); - gen_update_cc_op(s); - b &= 3; - switch(b) { - case 0: /* loopnz */ - case 1: /* loopz */ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jz_ecx(s, s->aflag, l3); - gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); - break; - case 2: /* loop */ - gen_op_add_reg_im(s, s->aflag, R_ECX, -1); - gen_op_jnz_ecx(s, s->aflag, l1); - break; - default: - case 3: /* jcxz */ - gen_op_jz_ecx(s, s->aflag, l1); - break; - } - - gen_set_label(l3); - gen_jmp_im(s, next_eip); - tcg_gen_br(l2); - - gen_set_label(l1); - gen_jmp_im(s, tval); - gen_set_label(l2); - gen_eob(s); - } - break; - case 0x130: /* wrmsr */ - case 0x132: /* rdmsr */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (b & 2) { - gen_helper_rdmsr(cpu_env); - } else { - gen_helper_wrmsr(cpu_env); - } - } - break; - case 0x131: /* rdtsc */ - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtsc(cpu_env); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - case 0x133: /* rdpmc */ - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_rdpmc(cpu_env); - break; - case 0x134: /* sysenter */ - /* For Intel SYSENTER is valid on 64-bit */ - if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) - goto illegal_op; - if (!s->pe) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_helper_sysenter(cpu_env); - gen_eob(s); - } - break; - case 0x135: /* sysexit */ - /* For Intel SYSEXIT is valid on 64-bit */ - if (CODE64(s) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) - goto illegal_op; - if (!s->pe) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_helper_sysexit(cpu_env, tcg_const_i32(dflag - 1)); - gen_eob(s); - } - break; -#ifdef TARGET_X86_64 - case 0x105: /* syscall */ - /* XXX: is it usable in real mode ? */ - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_syscall(cpu_env, tcg_const_i32(s->pc - pc_start)); - /* TF handling for the syscall insn is different. The TF bit is checked - after the syscall insn completes. This allows #DB to not be - generated after one has entered CPL0 if TF is set in FMASK. */ - gen_eob_worker(s, false, true); - break; - case 0x107: /* sysret */ - if (!s->pe) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_helper_sysret(cpu_env, tcg_const_i32(dflag - 1)); - /* condition codes are modified only in long mode */ - if (s->lma) { - set_cc_op(s, CC_OP_EFLAGS); - } - /* TF handling for the sysret insn is different. The TF bit is - checked after the sysret insn completes. This allows #DB to be - generated "as if" the syscall insn in userspace has just - completed. */ - gen_eob_worker(s, false, true); - } - break; -#endif - case 0x1a2: /* cpuid */ - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_cpuid(cpu_env); - break; - case 0xf4: /* hlt */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); - s->base.is_jmp = DISAS_NORETURN; - } - break; - case 0x100: - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* sldt */ - if (!s->pe || s->vm86) - goto illegal_op; - gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ); - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State, ldt.selector)); - ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - case 2: /* lldt */ - if (!s->pe || s->vm86) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_lldt(cpu_env, s->tmp2_i32); - } - break; - case 1: /* str */ - if (!s->pe || s->vm86) - goto illegal_op; - gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ); - tcg_gen_ld32u_tl(s->T0, cpu_env, - offsetof(CPUX86State, tr.selector)); - ot = mod == 3 ? dflag : MO_16; - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - case 3: /* ltr */ - if (!s->pe || s->vm86) - goto illegal_op; - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ltr(cpu_env, s->tmp2_i32); - } - break; - case 4: /* verr */ - case 5: /* verw */ - if (!s->pe || s->vm86) - goto illegal_op; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_update_cc_op(s); - if (op == 4) { - gen_helper_verr(cpu_env, s->T0); - } else { - gen_helper_verw(cpu_env, s->T0); - } - set_cc_op(s, CC_OP_EFLAGS); - break; - default: - goto unknown_op; - } - break; - - case 0x101: - modrm = x86_ldub_code(env, s); - switch (modrm) { - CASE_MODRM_MEM_OP(0): /* sgdt */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_READ); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, - cpu_env, offsetof(CPUX86State, gdt.limit)); - gen_op_st_v(s, MO_16, s->T0, s->A0); - gen_add_A0_im(s, 2); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } - gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); - break; - - case 0xc8: /* monitor */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - tcg_gen_mov_tl(s->A0, cpu_regs[R_EAX]); - gen_extu(s->aflag, s->A0); - gen_add_A0_ds_seg(s); - gen_helper_monitor(cpu_env, s->A0); - break; - - case 0xc9: /* mwait */ - if (!(s->cpuid_ext_features & CPUID_EXT_MONITOR) || s->cpl != 0) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); - gen_eob(s); - break; - - case 0xca: /* clac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) - || s->cpl != 0) { - goto illegal_op; - } - gen_helper_clac(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - case 0xcb: /* stac */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_SMAP) - || s->cpl != 0) { - goto illegal_op; - } - gen_helper_stac(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - CASE_MODRM_MEM_OP(1): /* sidt */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_READ); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.limit)); - gen_op_st_v(s, MO_16, s->T0, s->A0); - gen_add_A0_im(s, 2); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } - gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); - break; - - case 0xd0: /* xgetbv */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->prefix & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_xgetbv(s->tmp1_i64, cpu_env, s->tmp2_i32); - tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); - break; - - case 0xd1: /* xsetbv */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->prefix & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_xsetbv(cpu_env, s->tmp2_i32, s->tmp1_i64); - /* End TB because translation flags may change. */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - case 0xd8: /* VMRUN */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmrun(cpu_env, tcg_const_i32(s->aflag - 1), - tcg_const_i32(s->pc - pc_start)); - tcg_gen_exit_tb(NULL, 0); - s->base.is_jmp = DISAS_NORETURN; - break; - - case 0xd9: /* VMMCALL */ - if (!(s->flags & HF_SVME_MASK)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmmcall(cpu_env); - break; - - case 0xda: /* VMLOAD */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); - break; - - case 0xdb: /* VMSAVE */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); - break; - - case 0xdc: /* STGI */ - if ((!(s->flags & HF_SVME_MASK) - && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) - || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_helper_stgi(cpu_env); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - case 0xdd: /* CLGI */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_clgi(cpu_env); - break; - - case 0xde: /* SKINIT */ - if ((!(s->flags & HF_SVME_MASK) - && !(s->cpuid_ext3_features & CPUID_EXT3_SKINIT)) - || !s->pe) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_skinit(cpu_env); - break; - - case 0xdf: /* INVLPGA */ - if (!(s->flags & HF_SVME_MASK) || !s->pe) { - goto illegal_op; - } - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag - 1)); - break; - - CASE_MODRM_MEM_OP(2): /* lgdt */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_svm_check_intercept(s, pc_start, SVM_EXIT_GDTR_WRITE); - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_16, s->T1, s->A0); - gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } - tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); - tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, gdt.limit)); - break; - - CASE_MODRM_MEM_OP(3): /* lidt */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_svm_check_intercept(s, pc_start, SVM_EXIT_IDTR_WRITE); - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_16, s->T1, s->A0); - gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); - if (dflag == MO_16) { - tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); - } - tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); - tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, idt.limit)); - break; - - CASE_MODRM_OP(4): /* smsw */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_CR0); - tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0])); - /* - * In 32-bit mode, the higher 16 bits of the destination - * register are undefined. In practice CR0[31:0] is stored - * just like in 64-bit mode. - */ - mod = (modrm >> 6) & 3; - ot = (mod != 3 ? MO_16 : s->dflag); - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); - break; - case 0xee: /* rdpkru */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_rdpkru(s->tmp1_i64, cpu_env, s->tmp2_i32); - tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); - break; - case 0xef: /* wrpkru */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); - gen_helper_wrpkru(cpu_env, s->tmp2_i32, s->tmp1_i64); - break; - CASE_MODRM_OP(6): /* lmsw */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - gen_helper_lmsw(cpu_env, s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - CASE_MODRM_MEM_OP(7): /* invlpg */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - break; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - gen_lea_modrm(env, s, modrm); - gen_helper_invlpg(cpu_env, s->A0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - case 0xf8: /* swapgs */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - tcg_gen_mov_tl(s->T0, cpu_seg_base[R_GS]); - tcg_gen_ld_tl(cpu_seg_base[R_GS], cpu_env, - offsetof(CPUX86State, kernelgsbase)); - tcg_gen_st_tl(s->T0, cpu_env, - offsetof(CPUX86State, kernelgsbase)); - } - break; - } -#endif - goto illegal_op; - - case 0xf9: /* rdtscp */ - if (!(s->cpuid_ext2_features & CPUID_EXT2_RDTSCP)) { - goto illegal_op; - } - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_rdtscp(cpu_env); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - break; - - default: - goto unknown_op; - } - break; - - case 0x108: /* invd */ - case 0x109: /* wbinvd */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, (b & 2) ? SVM_EXIT_INVD : SVM_EXIT_WBINVD); - /* nothing to do */ - } - break; - case 0x63: /* arpl or movslS (x86_64) */ -#ifdef TARGET_X86_64 - if (CODE64(s)) { - int d_ot; - /* d_ot is the size of destination */ - d_ot = dflag; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - mod = (modrm >> 6) & 3; - rm = (modrm & 7) | REX_B(s); - - if (mod == 3) { - gen_op_mov_v_reg(s, MO_32, s->T0, rm); - /* sign extend */ - if (d_ot == MO_64) { - tcg_gen_ext32s_tl(s->T0, s->T0); - } - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } else { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0); - gen_op_mov_reg_v(s, d_ot, reg, s->T0); - } - } else -#endif - { - TCGLabel *label1; - TCGv t0, t1, t2, a0; - - if (!s->pe || s->vm86) - goto illegal_op; - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); - t2 = tcg_temp_local_new(); - ot = MO_16; - modrm = x86_ldub_code(env, s); - reg = (modrm >> 3) & 7; - mod = (modrm >> 6) & 3; - rm = modrm & 7; - if (mod != 3) { - gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, t0, s->A0); - a0 = tcg_temp_local_new(); - tcg_gen_mov_tl(a0, s->A0); - } else { - gen_op_mov_v_reg(s, ot, t0, rm); - a0 = NULL; - } - gen_op_mov_v_reg(s, ot, t1, reg); - tcg_gen_andi_tl(s->tmp0, t0, 3); - tcg_gen_andi_tl(t1, t1, 3); - tcg_gen_movi_tl(t2, 0); - label1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1); - tcg_gen_andi_tl(t0, t0, ~3); - tcg_gen_or_tl(t0, t0, t1); - tcg_gen_movi_tl(t2, CC_Z); - gen_set_label(label1); - if (mod != 3) { - gen_op_st_v(s, ot, t0, a0); - tcg_temp_free(a0); - } else { - gen_op_mov_reg_v(s, ot, rm, t0); - } - gen_compute_eflags(s); - tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); - tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, t2); - tcg_temp_free(t0); - tcg_temp_free(t1); - tcg_temp_free(t2); - } - break; - case 0x102: /* lar */ - case 0x103: /* lsl */ - { - TCGLabel *label1; - TCGv t0; - if (!s->pe || s->vm86) - goto illegal_op; - ot = dflag != MO_16 ? MO_32 : MO_16; - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - t0 = tcg_temp_local_new(); - gen_update_cc_op(s); - if (b == 0x102) { - gen_helper_lar(t0, cpu_env, s->T0); - } else { - gen_helper_lsl(t0, cpu_env, s->T0); - } - tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); - label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); - gen_op_mov_reg_v(s, ot, reg, t0); - gen_set_label(label1); - set_cc_op(s, CC_OP_EFLAGS); - tcg_temp_free(t0); - } - break; - case 0x118: - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - op = (modrm >> 3) & 7; - switch(op) { - case 0: /* prefetchnta */ - case 1: /* prefetchnt0 */ - case 2: /* prefetchnt0 */ - case 3: /* prefetchnt0 */ - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - /* nothing more to do */ - break; - default: /* nop (multi byte) */ - gen_nop_modrm(env, s, modrm); - break; - } - break; - case 0x11a: - modrm = x86_ldub_code(env, s); - if (s->flags & HF_MPX_EN_MASK) { - mod = (modrm >> 6) & 3; - reg = ((modrm >> 3) & 7) | rex_r; - if (prefixes & PREFIX_REPZ) { - /* bndcl */ - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16) { - goto illegal_op; - } - gen_bndck(env, s, modrm, TCG_COND_LTU, cpu_bndl[reg]); - } else if (prefixes & PREFIX_REPNZ) { - /* bndcu */ - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16) { - goto illegal_op; - } - TCGv_i64 notu = tcg_temp_new_i64(); - tcg_gen_not_i64(notu, cpu_bndu[reg]); - gen_bndck(env, s, modrm, TCG_COND_GTU, notu); - tcg_temp_free_i64(notu); - } else if (prefixes & PREFIX_DATA) { - /* bndmov -- from reg/mem */ - if (reg >= 4 || s->aflag == MO_16) { - goto illegal_op; - } - if (mod == 3) { - int reg2 = (modrm & 7) | REX_B(s); - if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if (s->flags & HF_MPX_IU_MASK) { - tcg_gen_mov_i64(cpu_bndl[reg], cpu_bndl[reg2]); - tcg_gen_mov_i64(cpu_bndu[reg], cpu_bndu[reg2]); - } - } else { - gen_lea_modrm(env, s, modrm); - if (CODE64(s)) { - tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->A0, - s->mem_index, MO_LEQ); - tcg_gen_addi_tl(s->A0, s->A0, 8); - tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, - s->mem_index, MO_LEQ); - } else { - tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->A0, - s->mem_index, MO_LEUL); - tcg_gen_addi_tl(s->A0, s->A0, 4); - tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, - s->mem_index, MO_LEUL); - } - /* bnd registers are now in-use */ - gen_set_hflag(s, HF_MPX_IU_MASK); - } - } else if (mod != 3) { - /* bndldx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16 - || a.base < -1) { - goto illegal_op; - } - if (a.base >= 0) { - tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); - } else { - tcg_gen_movi_tl(s->A0, 0); - } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); - if (a.index >= 0) { - tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); - } else { - tcg_gen_movi_tl(s->T0, 0); - } - if (CODE64(s)) { - gen_helper_bndldx64(cpu_bndl[reg], cpu_env, s->A0, s->T0); - tcg_gen_ld_i64(cpu_bndu[reg], cpu_env, - offsetof(CPUX86State, mmx_t0.MMX_Q(0))); - } else { - gen_helper_bndldx32(cpu_bndu[reg], cpu_env, s->A0, s->T0); - tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); - tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); - } - gen_set_hflag(s, HF_MPX_IU_MASK); - } - } - gen_nop_modrm(env, s, modrm); - break; - case 0x11b: - modrm = x86_ldub_code(env, s); - if (s->flags & HF_MPX_EN_MASK) { - mod = (modrm >> 6) & 3; - reg = ((modrm >> 3) & 7) | rex_r; - if (mod != 3 && (prefixes & PREFIX_REPZ)) { - /* bndmk */ - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16) { - goto illegal_op; - } - AddressParts a = gen_lea_modrm_0(env, s, modrm); - if (a.base >= 0) { - tcg_gen_extu_tl_i64(cpu_bndl[reg], cpu_regs[a.base]); - if (!CODE64(s)) { - tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndl[reg]); - } - } else if (a.base == -1) { - /* no base register has lower bound of 0 */ - tcg_gen_movi_i64(cpu_bndl[reg], 0); - } else { - /* rip-relative generates #ud */ - goto illegal_op; - } - tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, a)); - if (!CODE64(s)) { - tcg_gen_ext32u_tl(s->A0, s->A0); - } - tcg_gen_extu_tl_i64(cpu_bndu[reg], s->A0); - /* bnd registers are now in-use */ - gen_set_hflag(s, HF_MPX_IU_MASK); - break; - } else if (prefixes & PREFIX_REPNZ) { - /* bndcn */ - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16) { - goto illegal_op; - } - gen_bndck(env, s, modrm, TCG_COND_GTU, cpu_bndu[reg]); - } else if (prefixes & PREFIX_DATA) { - /* bndmov -- to reg/mem */ - if (reg >= 4 || s->aflag == MO_16) { - goto illegal_op; - } - if (mod == 3) { - int reg2 = (modrm & 7) | REX_B(s); - if (reg2 >= 4 || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if (s->flags & HF_MPX_IU_MASK) { - tcg_gen_mov_i64(cpu_bndl[reg2], cpu_bndl[reg]); - tcg_gen_mov_i64(cpu_bndu[reg2], cpu_bndu[reg]); - } - } else { - gen_lea_modrm(env, s, modrm); - if (CODE64(s)) { - tcg_gen_qemu_st_i64(cpu_bndl[reg], s->A0, - s->mem_index, MO_LEQ); - tcg_gen_addi_tl(s->A0, s->A0, 8); - tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, - s->mem_index, MO_LEQ); - } else { - tcg_gen_qemu_st_i64(cpu_bndl[reg], s->A0, - s->mem_index, MO_LEUL); - tcg_gen_addi_tl(s->A0, s->A0, 4); - tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, - s->mem_index, MO_LEUL); - } - } - } else if (mod != 3) { - /* bndstx */ - AddressParts a = gen_lea_modrm_0(env, s, modrm); - if (reg >= 4 - || (prefixes & PREFIX_LOCK) - || s->aflag == MO_16 - || a.base < -1) { - goto illegal_op; - } - if (a.base >= 0) { - tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); - } else { - tcg_gen_movi_tl(s->A0, 0); - } - gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); - if (a.index >= 0) { - tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); - } else { - tcg_gen_movi_tl(s->T0, 0); - } - if (CODE64(s)) { - gen_helper_bndstx64(cpu_env, s->A0, s->T0, - cpu_bndl[reg], cpu_bndu[reg]); - } else { - gen_helper_bndstx32(cpu_env, s->A0, s->T0, - cpu_bndl[reg], cpu_bndu[reg]); - } - } - } - gen_nop_modrm(env, s, modrm); - break; - case 0x119: case 0x11c ... 0x11f: /* nop (multi byte) */ - modrm = x86_ldub_code(env, s); - gen_nop_modrm(env, s, modrm); - break; - case 0x120: /* mov reg, crN */ - case 0x122: /* mov crN, reg */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - modrm = x86_ldub_code(env, s); - /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of - * intel 386 and 486 processors all show that the mod bits - * are assumed to be 1's, regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | rex_r; - if (CODE64(s)) - ot = MO_64; - else - ot = MO_32; - if ((prefixes & PREFIX_LOCK) && (reg == 0) && - (s->cpuid_ext3_features & CPUID_EXT3_CR8LEG)) { - reg = 8; - } - switch(reg) { - case 0: - case 2: - case 3: - case 4: - case 8: - gen_update_cc_op(s); - gen_jmp_im(s, pc_start - s->cs_base); - if (b & 2) { - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_op_mov_v_reg(s, ot, s->T0, rm); - gen_helper_write_crN(cpu_env, tcg_const_i32(reg), - s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } else { - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_read_crN(s->T0, cpu_env, tcg_const_i32(reg)); - gen_op_mov_reg_v(s, ot, rm, s->T0); - if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { - gen_jmp(s, s->pc - s->cs_base); - } - } - break; - default: - goto unknown_op; - } - } - break; - case 0x121: /* mov reg, drN */ - case 0x123: /* mov drN, reg */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - modrm = x86_ldub_code(env, s); - /* Ignore the mod bits (assume (modrm&0xc0)==0xc0). - * AMD documentation (24594.pdf) and testing of - * intel 386 and 486 processors all show that the mod bits - * are assumed to be 1's, regardless of actual values. - */ - rm = (modrm & 7) | REX_B(s); - reg = ((modrm >> 3) & 7) | rex_r; - if (CODE64(s)) - ot = MO_64; - else - ot = MO_32; - if (reg >= 8) { - goto illegal_op; - } - if (b & 2) { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); - gen_op_mov_v_reg(s, ot, s->T0, rm); - tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_set_dr(cpu_env, s->tmp2_i32, s->T0); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); - tcg_gen_movi_i32(s->tmp2_i32, reg); - gen_helper_get_dr(s->T0, cpu_env, s->tmp2_i32); - gen_op_mov_reg_v(s, ot, rm, s->T0); - } - } - break; - case 0x106: /* clts */ - if (s->cpl != 0) { - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); - } else { - gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); - gen_helper_clts(cpu_env); - /* abort block because static cpu state changed */ - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - } - break; - /* MMX/3DNow!/SSE/SSE2/SSE3/SSSE3/SSE4 support */ - case 0x1c3: /* MOVNTI reg, mem */ - if (!(s->cpuid_features & CPUID_SSE2)) - goto illegal_op; - ot = mo_64_32(dflag); - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - reg = ((modrm >> 3) & 7) | rex_r; - /* generate a generic store */ - gen_ldst_modrm(env, s, modrm, ot, reg, 1); - break; - case 0x1ae: - modrm = x86_ldub_code(env, s); - switch (modrm) { - CASE_MODRM_MEM_OP(0): /* fxsave */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(cpu_env, s->A0); - break; - - CASE_MODRM_MEM_OP(1): /* fxrstor */ - if (!(s->cpuid_features & CPUID_FXSR) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - if ((s->flags & HF_EM_MASK) || (s->flags & HF_TS_MASK)) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - break; - } - gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(cpu_env, s->A0); - break; - - CASE_MODRM_MEM_OP(2): /* ldmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - break; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); - break; - - CASE_MODRM_MEM_OP(3): /* stmxcsr */ - if ((s->flags & HF_EM_MASK) || !(s->flags & HF_OSFXSR_MASK)) { - goto illegal_op; - } - if (s->flags & HF_TS_MASK) { - gen_exception(s, EXCP07_PREX, pc_start - s->cs_base); - break; - } - gen_helper_update_mxcsr(cpu_env); - gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, s->T0, s->A0); - break; - - CASE_MODRM_MEM_OP(4): /* xsave */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xsave(cpu_env, s->A0, s->tmp1_i64); - break; - - CASE_MODRM_MEM_OP(5): /* xrstor */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (prefixes & (PREFIX_LOCK | PREFIX_DATA - | PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xrstor(cpu_env, s->A0, s->tmp1_i64); - /* XRSTOR is how MPX is enabled, which changes how - we translate. Thus we need to end the TB. */ - gen_update_cc_op(s); - gen_jmp_im(s, s->pc - s->cs_base); - gen_eob(s); - break; - - CASE_MODRM_MEM_OP(6): /* xsaveopt / clwb */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clwb */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLWB)) { - goto illegal_op; - } - gen_nop_modrm(env, s, modrm); - } else { - /* xsaveopt */ - if ((s->cpuid_ext_features & CPUID_EXT_XSAVE) == 0 - || (s->cpuid_xsave_features & CPUID_XSAVE_XSAVEOPT) == 0 - || (prefixes & (PREFIX_REPZ | PREFIX_REPNZ))) { - goto illegal_op; - } - gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], - cpu_regs[R_EDX]); - gen_helper_xsaveopt(cpu_env, s->A0, s->tmp1_i64); - } - break; - - CASE_MODRM_MEM_OP(7): /* clflush / clflushopt */ - if (prefixes & PREFIX_LOCK) { - goto illegal_op; - } - if (prefixes & PREFIX_DATA) { - /* clflushopt */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_CLFLUSHOPT)) { - goto illegal_op; - } - } else { - /* clflush */ - if ((s->prefix & (PREFIX_REPZ | PREFIX_REPNZ)) - || !(s->cpuid_features & CPUID_CLFLUSH)) { - goto illegal_op; - } - } - gen_nop_modrm(env, s, modrm); - break; - - case 0xc0 ... 0xc7: /* rdfsbase (f3 0f ae /0) */ - case 0xc8 ... 0xcf: /* rdgsbase (f3 0f ae /1) */ - case 0xd0 ... 0xd7: /* wrfsbase (f3 0f ae /2) */ - case 0xd8 ... 0xdf: /* wrgsbase (f3 0f ae /3) */ - if (CODE64(s) - && (prefixes & PREFIX_REPZ) - && !(prefixes & PREFIX_LOCK) - && (s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_FSGSBASE)) { - TCGv base, treg, src, dst; - - /* Preserve hflags bits by testing CR4 at runtime. */ - tcg_gen_movi_i32(s->tmp2_i32, CR4_FSGSBASE_MASK); - gen_helper_cr4_testbit(cpu_env, s->tmp2_i32); - - base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; - treg = cpu_regs[(modrm & 7) | REX_B(s)]; - - if (modrm & 0x10) { - /* wr*base */ - dst = base, src = treg; - } else { - /* rd*base */ - dst = treg, src = base; - } - - if (s->dflag == MO_32) { - tcg_gen_ext32u_tl(dst, src); - } else { - tcg_gen_mov_tl(dst, src); - } - break; - } - goto unknown_op; - - case 0xf8: /* sfence / pcommit */ - if (prefixes & PREFIX_DATA) { - /* pcommit */ - if (!(s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_PCOMMIT) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - break; - } - /* fallthru */ - case 0xf9 ... 0xff: /* sfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ST_ST | TCG_BAR_SC); - break; - case 0xe8 ... 0xef: /* lfence */ - if (!(s->cpuid_features & CPUID_SSE) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_LD_LD | TCG_BAR_SC); - break; - case 0xf0 ... 0xf7: /* mfence */ - if (!(s->cpuid_features & CPUID_SSE2) - || (prefixes & PREFIX_LOCK)) { - goto illegal_op; - } - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - break; - - default: - goto unknown_op; - } - break; - - case 0x10d: /* 3DNow! prefetch(w) */ - modrm = x86_ldub_code(env, s); - mod = (modrm >> 6) & 3; - if (mod == 3) - goto illegal_op; - gen_nop_modrm(env, s, modrm); - break; - case 0x1aa: /* rsm */ - gen_svm_check_intercept(s, pc_start, SVM_EXIT_RSM); - if (!(s->flags & HF_SMM_MASK)) - goto illegal_op; - gen_update_cc_op(s); - gen_jmp_im(s, s->pc - s->cs_base); - gen_helper_rsm(cpu_env); - gen_eob(s); - break; - case 0x1b8: /* SSE4.2 popcnt */ - if ((prefixes & (PREFIX_REPZ | PREFIX_LOCK | PREFIX_REPNZ)) != - PREFIX_REPZ) - goto illegal_op; - if (!(s->cpuid_ext_features & CPUID_EXT_POPCNT)) - goto illegal_op; - - modrm = x86_ldub_code(env, s); - reg = ((modrm >> 3) & 7) | rex_r; - - if (s->prefix & PREFIX_DATA) { - ot = MO_16; - } else { - ot = mo_64_32(dflag); - } - - gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, s->T0); - tcg_gen_mov_tl(cpu_cc_src, s->T0); - tcg_gen_ctpop_tl(s->T0, s->T0); - gen_op_mov_reg_v(s, ot, reg, s->T0); - - set_cc_op(s, CC_OP_POPCNT); - break; - case 0x10e ... 0x10f: - /* 3DNow! instructions, ignore prefixes */ - s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); - /* fall through */ - case 0x110 ... 0x117: - case 0x128 ... 0x12f: - case 0x138 ... 0x13a: - case 0x150 ... 0x179: - case 0x17c ... 0x17f: - case 0x1c2: - case 0x1c4 ... 0x1c6: - case 0x1d0 ... 0x1fe: - gen_sse(env, s, b, pc_start, rex_r); - break; - default: - goto unknown_op; - } - return s->pc; - illegal_op: - gen_illegal_opcode(s); - return s->pc; - unknown_op: - gen_unknown_opcode(env, s); - return s->pc; -} - -void tcg_x86_init(void) -{ - static const char reg_names[CPU_NB_REGS][4] = { -#ifdef TARGET_X86_64 - [R_EAX] = "rax", - [R_EBX] = "rbx", - [R_ECX] = "rcx", - [R_EDX] = "rdx", - [R_ESI] = "rsi", - [R_EDI] = "rdi", - [R_EBP] = "rbp", - [R_ESP] = "rsp", - [8] = "r8", - [9] = "r9", - [10] = "r10", - [11] = "r11", - [12] = "r12", - [13] = "r13", - [14] = "r14", - [15] = "r15", -#else - [R_EAX] = "eax", - [R_EBX] = "ebx", - [R_ECX] = "ecx", - [R_EDX] = "edx", - [R_ESI] = "esi", - [R_EDI] = "edi", - [R_EBP] = "ebp", - [R_ESP] = "esp", -#endif - }; - static const char seg_base_names[6][8] = { - [R_CS] = "cs_base", - [R_DS] = "ds_base", - [R_ES] = "es_base", - [R_FS] = "fs_base", - [R_GS] = "gs_base", - [R_SS] = "ss_base", - }; - static const char bnd_regl_names[4][8] = { - "bnd0_lb", "bnd1_lb", "bnd2_lb", "bnd3_lb" - }; - static const char bnd_regu_names[4][8] = { - "bnd0_ub", "bnd1_ub", "bnd2_ub", "bnd3_ub" - }; - int i; - - cpu_cc_op = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUX86State, cc_op), "cc_op"); - cpu_cc_dst = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_dst), - "cc_dst"); - cpu_cc_src = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src), - "cc_src"); - cpu_cc_src2 = tcg_global_mem_new(cpu_env, offsetof(CPUX86State, cc_src2), - "cc_src2"); - - for (i = 0; i < CPU_NB_REGS; ++i) { - cpu_regs[i] = tcg_global_mem_new(cpu_env, - offsetof(CPUX86State, regs[i]), - reg_names[i]); - } - - for (i = 0; i < 6; ++i) { - cpu_seg_base[i] - = tcg_global_mem_new(cpu_env, - offsetof(CPUX86State, segs[i].base), - seg_base_names[i]); - } - - for (i = 0; i < 4; ++i) { - cpu_bndl[i] - = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUX86State, bnd_regs[i].lb), - bnd_regl_names[i]); - cpu_bndu[i] - = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUX86State, bnd_regs[i].ub), - bnd_regu_names[i]); - } -} - -static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - CPUX86State *env = cpu->env_ptr; - uint32_t flags = dc->base.tb->flags; - target_ulong cs_base = dc->base.tb->cs_base; - - dc->pe = (flags >> HF_PE_SHIFT) & 1; - dc->code32 = (flags >> HF_CS32_SHIFT) & 1; - dc->ss32 = (flags >> HF_SS32_SHIFT) & 1; - dc->addseg = (flags >> HF_ADDSEG_SHIFT) & 1; - dc->f_st = 0; - dc->vm86 = (flags >> VM_SHIFT) & 1; - dc->cpl = (flags >> HF_CPL_SHIFT) & 3; - dc->iopl = (flags >> IOPL_SHIFT) & 3; - dc->tf = (flags >> TF_SHIFT) & 1; - dc->cc_op = CC_OP_DYNAMIC; - dc->cc_op_dirty = false; - dc->cs_base = cs_base; - dc->popl_esp_hack = 0; - /* select memory access functions */ - dc->mem_index = 0; -#ifdef CONFIG_SOFTMMU - dc->mem_index = cpu_mmu_index(env, false); -#endif - dc->cpuid_features = env->features[FEAT_1_EDX]; - dc->cpuid_ext_features = env->features[FEAT_1_ECX]; - dc->cpuid_ext2_features = env->features[FEAT_8000_0001_EDX]; - dc->cpuid_ext3_features = env->features[FEAT_8000_0001_ECX]; - dc->cpuid_7_0_ebx_features = env->features[FEAT_7_0_EBX]; - dc->cpuid_xsave_features = env->features[FEAT_XSAVE]; -#ifdef TARGET_X86_64 - dc->lma = (flags >> HF_LMA_SHIFT) & 1; - dc->code64 = (flags >> HF_CS64_SHIFT) & 1; -#endif - dc->flags = flags; - dc->jmp_opt = !(dc->tf || dc->base.singlestep_enabled || - (flags & HF_INHIBIT_IRQ_MASK)); - /* Do not optimize repz jumps at all in icount mode, because - rep movsS instructions are execured with different paths - in !repz_opt and repz_opt modes. The first one was used - always except single step mode. And this setting - disables jumps optimization and control paths become - equivalent in run and single step modes. - Now there will be no jump optimization for repz in - record/replay modes and there will always be an - additional step for ecx=0 when icount is enabled. - */ - dc->repz_opt = !dc->jmp_opt && !(tb_cflags(dc->base.tb) & CF_USE_ICOUNT); -#if 0 - /* check addseg logic */ - if (!dc->addseg && (dc->vm86 || !dc->pe || !dc->code32)) - printf("ERROR addseg\n"); -#endif - - dc->T0 = tcg_temp_new(); - dc->T1 = tcg_temp_new(); - dc->A0 = tcg_temp_new(); - - dc->tmp0 = tcg_temp_new(); - dc->tmp1_i64 = tcg_temp_new_i64(); - dc->tmp2_i32 = tcg_temp_new_i32(); - dc->tmp3_i32 = tcg_temp_new_i32(); - dc->tmp4 = tcg_temp_new(); - dc->ptr0 = tcg_temp_new_ptr(); - dc->ptr1 = tcg_temp_new_ptr(); - dc->cc_srcT = tcg_temp_local_new(); -} - -static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) -{ -} - -static void i386_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - tcg_gen_insn_start(dc->base.pc_next, dc->cc_op); -} - -static bool i386_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, - const CPUBreakpoint *bp) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - /* If RF is set, suppress an internally generated breakpoint. */ - int flags = dc->base.tb->flags & HF_RF_MASK ? BP_GDB : BP_ANY; - if (bp->flags & flags) { - gen_debug(dc, dc->base.pc_next - dc->cs_base); - dc->base.is_jmp = DISAS_NORETURN; - /* The address covered by the breakpoint must be included in - [tb->pc, tb->pc + tb->size) in order to for it to be - properly cleared -- thus we increment the PC here so that - the generic logic setting tb->size later does the right thing. */ - dc->base.pc_next += 1; - return true; - } else { - return false; - } -} - -static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - target_ulong pc_next; - -#ifdef TARGET_VSYSCALL_PAGE - /* - * Detect entry into the vsyscall page and invoke the syscall. - */ - if ((dc->base.pc_next & TARGET_PAGE_MASK) == TARGET_VSYSCALL_PAGE) { - gen_exception(dc, EXCP_VSYSCALL, dc->base.pc_next); - return; - } -#endif - - pc_next = disas_insn(dc, cpu); - - if (dc->tf || (dc->base.tb->flags & HF_INHIBIT_IRQ_MASK)) { - /* if single step mode, we generate only one instruction and - generate an exception */ - /* if irq were inhibited with HF_INHIBIT_IRQ_MASK, we clear - the flag and abort the translation to give the irqs a - chance to happen */ - dc->base.is_jmp = DISAS_TOO_MANY; - } else if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) - && ((pc_next & TARGET_PAGE_MASK) - != ((pc_next + TARGET_MAX_INSN_SIZE - 1) - & TARGET_PAGE_MASK) - || (pc_next & ~TARGET_PAGE_MASK) == 0)) { - /* Do not cross the boundary of the pages in icount mode, - it can cause an exception. Do it only when boundary is - crossed by the first instruction in the block. - If current instruction already crossed the bound - it's ok, - because an exception hasn't stopped this code. - */ - dc->base.is_jmp = DISAS_TOO_MANY; - } else if ((pc_next - dc->base.pc_first) >= (TARGET_PAGE_SIZE - 32)) { - dc->base.is_jmp = DISAS_TOO_MANY; - } - - dc->base.pc_next = pc_next; -} - -static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - if (dc->base.is_jmp == DISAS_TOO_MANY) { - gen_jmp_im(dc, dc->base.pc_next - dc->cs_base); - gen_eob(dc); - } -} - -static void i386_tr_disas_log(const DisasContextBase *dcbase, - CPUState *cpu) -{ - DisasContext *dc = container_of(dcbase, DisasContext, base); - - qemu_log("IN: %s\n", lookup_symbol(dc->base.pc_first)); - log_target_disas(cpu, dc->base.pc_first, dc->base.tb->size); -} - -static const TranslatorOps i386_tr_ops = { - .init_disas_context = i386_tr_init_disas_context, - .tb_start = i386_tr_tb_start, - .insn_start = i386_tr_insn_start, - .breakpoint_check = i386_tr_breakpoint_check, - .translate_insn = i386_tr_translate_insn, - .tb_stop = i386_tr_tb_stop, - .disas_log = i386_tr_disas_log, -}; - -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) -{ - DisasContext dc; - - translator_loop(&i386_tr_ops, &dc.base, cpu, tb, max_insns); -} - -void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, - target_ulong *data) -{ - int cc_op = data[1]; - env->eip = data[0] - tb->cs_base; - if (cc_op != CC_OP_DYNAMIC) { - env->cc_op = cc_op; - } -} -- cgit v1.2.3-55-g7522