/*
* 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 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 <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <signal.h>
#include "qemu/host-utils.h"
#include "cpu.h"
#include "disas/disas.h"
#include "tcg-op.h"
#include "helper.h"
#define GEN_HELPER 1
#include "helper.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
//#define MACRO_TEST 1
/* global register indexes */
static TCGv_ptr cpu_env;
static TCGv cpu_A0;
static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT;
static TCGv_i32 cpu_cc_op;
static TCGv cpu_regs[CPU_NB_REGS];
/* local temps */
static TCGv cpu_T[2];
/* local register indexes (only used inside old micro ops) */
static TCGv cpu_tmp0, cpu_tmp4;
static TCGv_ptr cpu_ptr0, cpu_ptr1;
static TCGv_i32 cpu_tmp2_i32, cpu_tmp3_i32;
static TCGv_i64 cpu_tmp1_i64;
static uint8_t gen_opc_cc_op[OPC_BUF_SIZE];
#include "exec/gen-icount.h"
#ifdef TARGET_X86_64
static int x86_64_hregs;
#endif
typedef struct DisasContext {
/* current insn context */
int override; /* -1 if no override */
int prefix;
TCGMemOp aflag;
TCGMemOp dflag;
target_ulong pc; /* pc = eip + cs_base */
int is_jmp; /* 1 = means jump (stop translation), 2 means CPU
static state change (stop translation) */
/* 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 compliment. */
int ss32; /* 32 bit stack segment */
CCOp cc_op; /* current CC operation */
bool cc_op_dirty;
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 singlestep_enabled; /* "hardware" single step enabled */
int jmp_opt; /* use direct block chaining for direct jumps */
int mem_index; /* select memory access functions */
uint64_t flags; /* all execution flags */
struct TranslationBlock *tb;
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;
} DisasContext;
static void gen_eob(DisasContext *s);
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, TCGMemOp 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,
};
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(cpu_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(int reg)
{
if (reg < 4) {
return false;
}
#ifdef TARGET_X86_64
if (reg >= 8 || x86_64_hregs) {
return false;
}
#endif
return true;
}
/* Select the size of a push/pop operation. */
static inline TCGMemOp mo_pushpop(DisasContext *s, TCGMemOp ot)
{
if (CODE64(s)) {
return ot == MO_16 ? MO_16 : MO_64;
} else {
return ot;
}
}
/* Select only size 64 else 32. Used for SSE operand sizes. */
static inline TCGMemOp mo_64_32(TCGMemOp 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 TCGMemOp mo_b_d(int b, TCGMemOp 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 TCGMemOp mo_b_d32(int b, TCGMemOp ot)
{
return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8;
}
static void gen_op_mov_reg_v(TCGMemOp ot, int reg, TCGv t0)
{
switch(ot) {
case MO_8:
if (!byte_reg_is_xH(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(TCGMemOp ot, TCGv t0, int reg)
{
if (ot == MO_8 && byte_reg_is_xH(reg)) {
tcg_gen_shri_tl(t0, cpu_regs[reg - 4], 8);
tcg_gen_ext8u_tl(t0, t0);
} else {
tcg_gen_mov_tl(t0, cpu_regs[reg]);
}
}
static inline void gen_op_movl_A0_reg(int reg)
{
tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]);
}
static inline void gen_op_addl_A0_im(int32_t val)
{
tcg_gen_addi_tl(cpu_A0, cpu_A0, val);
#ifdef TARGET_X86_64
tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
#endif
}
#ifdef TARGET_X86_64
static inline void gen_op_addq_A0_im(int64_t val)
{
tcg_gen_addi_tl(cpu_A0, cpu_A0, val);
}
#endif
static void gen_add_A0_im(DisasContext *s, int val)
{
#ifdef TARGET_X86_64
if (CODE64(s))
gen_op_addq_A0_im(val);
else
#endif
gen_op_addl_A0_im(val);
}
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(TCGMemOp size, int reg, int32_t val)
{
tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val);
gen_op_mov_reg_v(size, reg, cpu_tmp0);
}
static inline void gen_op_add_reg_T0(TCGMemOp size, int reg)
{
tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T[0]);
gen_op_mov_reg_v(size, reg, cpu_tmp0);
}
static inline void gen_op_addl_A0_reg_sN(int shift, int reg)
{
tcg_gen_mov_tl(cpu_tmp0, cpu_regs[reg]);
if (shift != 0)
tcg_gen_shli_tl(cpu_tmp0, cpu_tmp0, shift);
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
/* For x86_64, this sets the higher half of register to zero.
For i386, this is equivalent to a nop. */
tcg_gen_ext32u_tl(cpu_A0, cpu_A0);
}
static inline void gen_op_movl_A0_seg(int reg)
{
tcg_gen_ld32u_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base) + REG_L_OFFSET);
}
static inline void gen_op_addl_A0_seg(DisasContext *s, int reg)
{
tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base));
#ifdef TARGET_X86_64
if (CODE64(s)) {
tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
} else {
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
tcg_gen_andi_tl(cpu_A0, cpu_A0, 0xffffffff);
}
#else
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
#endif
}
#ifdef TARGET_X86_64
static inline void gen_op_movq_A0_seg(int reg)
{
tcg_gen_ld_tl(cpu_A0, cpu_env, offsetof(CPUX86State, segs[reg].base));
}
static inline void gen_op_addq_A0_seg(int reg)
{
tcg_gen_ld_tl(cpu_tmp0, cpu_env, offsetof(CPUX86State, segs[reg].base));
tcg_gen_add_tl(cpu_A0, cpu_A0, cpu_tmp0);
}
static inline void gen_op_movq_A0_reg(int reg)
{
tcg_gen_mov_tl(cpu_A0, cpu_regs[reg]);
|