diff options
-rw-r--r-- | helper-i386.c | 31 | ||||
-rw-r--r-- | op-i386.c | 5 | ||||
-rw-r--r-- | translate-i386.c | 61 |
3 files changed, 78 insertions, 19 deletions
diff --git a/helper-i386.c b/helper-i386.c index 72b75a5c21..3f63704d5a 100644 --- a/helper-i386.c +++ b/helper-i386.c @@ -788,6 +788,37 @@ static inline void load_seg_vm(int seg, int selector) sc->limit = 0xffff; } +/* real mode iret */ +void helper_iret_real(int shift) +{ + uint32_t sp, new_cs, new_eip, new_eflags, new_esp; + uint8_t *ssp; + int eflags_mask; + + sp = env->regs[R_ESP] & 0xffff; + ssp = env->segs[R_SS].base + sp; + if (shift == 1) { + /* 32 bits */ + new_eflags = ldl(ssp + 8); + new_cs = ldl(ssp + 4) & 0xffff; + new_eip = ldl(ssp) & 0xffff; + } else { + /* 16 bits */ + new_eflags = lduw(ssp + 4); + new_cs = lduw(ssp + 2); + new_eip = lduw(ssp); + } + new_esp = sp + (6 << shift); + env->regs[R_ESP] = (env->regs[R_ESP] & 0xffff0000) | + (new_esp & 0xffff); + load_seg_vm(R_CS, new_cs); + env->eip = new_eip; + eflags_mask = FL_UPDATE_CPL0_MASK; + if (shift == 0) + eflags_mask &= 0xffff; + load_eflags(new_eflags, eflags_mask); +} + /* protected mode iret */ void helper_iret_protected(int shift) { @@ -953,6 +953,11 @@ void OPPROTO op_ljmp_T0_T1(void) jmp_seg(T0 & 0xffff, T1); } +void OPPROTO op_iret_real(void) +{ + helper_iret_real(PARAM1); +} + void OPPROTO op_iret_protected(void) { helper_iret_protected(PARAM1); diff --git a/translate-i386.c b/translate-i386.c index 1c051f9956..d5cdee5759 100644 --- a/translate-i386.c +++ b/translate-i386.c @@ -52,6 +52,7 @@ typedef struct DisasContext { static state change (stop translation) */ /* current block context */ uint8_t *cs_base; /* base of CS segment */ + int pe; /* protected mode */ int code32; /* 32 bit code segment */ int ss32; /* 32 bit stack segment */ int cc_op; /* current CC operation */ @@ -989,7 +990,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ if (base >= 0) { /* for correct popl handling with esp */ if (base == 4 && s->popl_esp_hack) - disp += 4; + disp += s->popl_esp_hack; gen_op_movl_A0_reg[base](); if (disp != 0) gen_op_addl_A0_im(disp); @@ -1272,7 +1273,7 @@ static void gen_setcc(DisasContext *s, int b) /* move T0 to seg_reg and compute if the CPU state may change */ static void gen_movl_seg_T0(DisasContext *s, int seg_reg, unsigned int cur_eip) { - if (!s->vm86) + if (s->pe && !s->vm86) gen_op_movl_seg_T0(seg_reg, cur_eip); else gen_op_movl_seg_T0_vm(offsetof(CPUX86State,segs[seg_reg])); @@ -1855,7 +1856,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_ld_T1_A0[ot](); gen_op_addl_A0_im(1 << (ot - OT_WORD + 1)); gen_op_lduw_T0_A0(); - if (!s->vm86) { + if (s->pe && !s->vm86) { /* we compute EIP to handle the exception case */ gen_op_jmp_im(pc_start - s->cs_base); gen_op_ljmp_T0_T1(); @@ -2036,7 +2037,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); gen_pop_T0(s); - s->popl_esp_hack = 1; + s->popl_esp_hack = 2 << dflag; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); s->popl_esp_hack = 0; gen_pop_update(s); @@ -2082,6 +2083,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_pop_T0(s); gen_movl_seg_T0(s, b >> 3, pc_start - s->cs_base); gen_pop_update(s); + /* XXX: if reg == SS, inhibit interrupts/trace */ break; case 0x1a1: /* pop fs */ case 0x1a9: /* pop gs */ @@ -2134,21 +2136,24 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_op_mov_reg_T0[ot][reg](); break; case 0x8e: /* mov seg, Gv */ - ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; - gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); if (reg >= 6 || reg == R_CS) goto illegal_op; + gen_ldst_modrm(s, modrm, OT_WORD, OR_TMP0, 0); gen_movl_seg_T0(s, reg, pc_start - s->cs_base); + /* XXX: if reg == SS, inhibit interrupts/trace */ break; case 0x8c: /* mov Gv, seg */ - ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; + mod = (modrm >> 6) & 3; if (reg >= 6) goto illegal_op; gen_op_movl_T0_seg(reg); + ot = OT_WORD; + if (mod == 3 && dflag) + ot = OT_LONG; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); break; @@ -2938,7 +2943,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0x6c: /* insS */ case 0x6d: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { /* NOTE: even for (E)CX = 0 the exception is raised */ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { @@ -2955,7 +2960,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0x6e: /* outsS */ case 0x6f: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { /* NOTE: even for (E)CX = 0 the exception is raised */ gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { @@ -2975,7 +2980,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) /* port I/O */ case 0xe4: case 0xe5: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { if ((b & 1) == 0) @@ -2990,7 +2995,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0xe6: case 0xe7: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { if ((b & 1) == 0) @@ -3005,7 +3010,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0xec: case 0xed: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { if ((b & 1) == 0) @@ -3019,7 +3024,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0xee: case 0xef: - if (s->cpl > s->iopl || s->vm86) { + if (s->pe && (s->cpl > s->iopl || s->vm86)) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { if ((b & 1) == 0) @@ -3076,7 +3081,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) val = 0; goto do_lret; case 0xcf: /* iret */ - if (s->vm86 && s->iopl != 3) { + if (!s->pe) { + /* real mode */ + gen_op_iret_real(s->dflag); + s->cc_op = CC_OP_EFLAGS; + } else if (s->vm86 && s->iopl != 3) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { if (s->cc_op != CC_OP_DYNAMIC) @@ -3142,7 +3151,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) /* change cs and pc */ gen_op_movl_T0_im(selector); - if (!s->vm86) { + if (s->pe && !s->vm86) { /* we compute EIP to handle the exception case */ gen_op_jmp_im(pc_start - s->cs_base); gen_op_movl_T1_im(offset); @@ -3442,6 +3451,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } } + /* XXX: interruptions are enabled only the first insn after sti */ break; case 0x62: /* bound */ ot = dflag ? OT_LONG : OT_WORD; @@ -3628,7 +3638,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) break; case 0x102: /* lar */ case 0x103: /* lsl */ - if (s->vm86) + if (!s->pe || s->vm86) goto illegal_op; ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); @@ -4106,19 +4116,26 @@ static inline int gen_intermediate_code_internal(CPUState *env, cs_base = (uint8_t *)tb->cs_base; flags = tb->flags; + dc->pe = env->cr[0] & CR0_PE_MASK; dc->code32 = (flags >> GEN_FLAG_CODE32_SHIFT) & 1; dc->ss32 = (flags >> GEN_FLAG_SS32_SHIFT) & 1; dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1; dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7; dc->vm86 = (flags >> GEN_FLAG_VM_SHIFT) & 1; - dc->cpl = (flags >> GEN_FLAG_CPL_SHIFT) & 3; + /* CPL is implicit if real mode or vm86 mode */ + if (!dc->pe) + dc->cpl = 0; + else if (dc->vm86) + dc->cpl = 3; + else + dc->cpl = (flags >> GEN_FLAG_CPL_SHIFT) & 3; dc->iopl = (flags >> GEN_FLAG_IOPL_SHIFT) & 3; dc->tf = (flags >> GEN_FLAG_TF_SHIFT) & 1; dc->cc_op = CC_OP_DYNAMIC; dc->cs_base = cs_base; dc->tb = tb; dc->popl_esp_hack = 0; - + gen_opc_ptr = gen_opc_buf; gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; gen_opparam_ptr = gen_opparam_buf; @@ -4270,13 +4287,14 @@ void cpu_x86_close(CPUX86State *env) /* called when cr3 or PG bit are modified */ static int last_pg_state = -1; +static int last_pe_state = 0; int phys_ram_size; int phys_ram_fd; uint8_t *phys_ram_base; void cpu_x86_update_cr0(CPUX86State *env) { - int pg_state; + int pg_state, pe_state; void *map_addr; #ifdef DEBUG_MMU @@ -4304,6 +4322,11 @@ void cpu_x86_update_cr0(CPUX86State *env) } last_pg_state = pg_state; } + pe_state = env->cr[0] & CR0_PE_MASK; + if (last_pe_state != pe_state) { + tb_flush(); + last_pe_state = pe_state; + } } void cpu_x86_update_cr3(CPUX86State *env) |