diff options
author | Richard Henderson <richard.henderson@linaro.org> | 2023-12-12 09:01:38 -0800 |
---|---|---|
committer | Michael Tokarev <mjt@tls.msk.ru> | 2023-12-14 11:25:31 +0300 |
commit | 5b2fd6cf37352d671db09b54e4bc9d2ce707ca6d (patch) | |
tree | c5956b6645e430a069f6fc7d3e8ad0bdd8a1c6a7 | |
parent | b8311827286a08743a0257372b75fe98c0b8a67b (diff) |
target/i386: Fix 32-bit wrapping of pc/eip computation
In 32-bit mode, pc = eip + cs_base is also 32-bit, and must wrap.
Failure to do so results in incorrect memory exceptions to the guest.
Before 732d548732ed, this was implicitly done via truncation to
target_ulong but only in qemu-system-i386, not qemu-system-x86_64.
To fix this, we must add conditional zero-extensions.
Since we have to test for 32 vs 64-bit anyway, note that cs_base
is always zero in 64-bit mode.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2022
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <20231212172510.103305-1-richard.henderson@linaro.org>
(cherry picked from commit b5e0d5d22fbffc3d8f7d3e86d7a2d05a1a974e27)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
(Mjt: context fix in target/i386/tcg/tcg-cpu.c for v8.1.0-1190-gb77af26e97
"accel/tcg: Replace CPUState.env_ptr with cpu_env()")
-rw-r--r-- | target/i386/cpu.h | 9 | ||||
-rw-r--r-- | target/i386/tcg/tcg-cpu.c | 11 | ||||
-rw-r--r-- | target/i386/tcg/translate.c | 23 |
3 files changed, 33 insertions, 10 deletions
diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e0771a1043..0893b794e9 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2286,10 +2286,15 @@ static inline int cpu_mmu_index_kernel(CPUX86State *env) static inline void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { - *cs_base = env->segs[R_CS].base; - *pc = *cs_base + env->eip; *flags = env->hflags | (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); + if (env->hflags & HF_CS64_MASK) { + *cs_base = 0; + *pc = env->eip; + } else { + *cs_base = env->segs[R_CS].base; + *pc = (uint32_t)(*cs_base + env->eip); + } } void do_cpu_init(X86CPU *cpu); diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index b942c306d6..f252e336d7 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -52,7 +52,12 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs, /* The instruction pointer is always up to date with CF_PCREL. */ if (!(tb_cflags(tb) & CF_PCREL)) { CPUX86State *env = cs->env_ptr; - env->eip = tb->pc - tb->cs_base; + + if (tb->flags & HF_CS64_MASK) { + env->eip = tb->pc; + } else { + env->eip = (uint32_t)(tb->pc - tb->cs_base); + } } } @@ -66,8 +71,10 @@ static void x86_restore_state_to_opc(CPUState *cs, if (tb_cflags(tb) & CF_PCREL) { env->eip = (env->eip & TARGET_PAGE_MASK) | data[0]; + } else if (tb->flags & HF_CS64_MASK) { + env->eip = data[0]; } else { - env->eip = data[0] - tb->cs_base; + env->eip = (uint32_t)(data[0] - tb->cs_base); } if (cc_op != CC_OP_DYNAMIC) { env->cc_op = cc_op; diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index e0a622941c..93001fde7e 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -552,8 +552,10 @@ static void gen_update_eip_cur(DisasContext *s) assert(s->pc_save != -1); if (tb_cflags(s->base.tb) & CF_PCREL) { tcg_gen_addi_tl(cpu_eip, cpu_eip, s->base.pc_next - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->base.pc_next); } else { - tcg_gen_movi_tl(cpu_eip, s->base.pc_next - s->cs_base); + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); } s->pc_save = s->base.pc_next; } @@ -563,8 +565,10 @@ static void gen_update_eip_next(DisasContext *s) assert(s->pc_save != -1); if (tb_cflags(s->base.tb) & CF_PCREL) { tcg_gen_addi_tl(cpu_eip, cpu_eip, s->pc - s->pc_save); + } else if (CODE64(s)) { + tcg_gen_movi_tl(cpu_eip, s->base.pc_next); } else { - tcg_gen_movi_tl(cpu_eip, s->pc - s->cs_base); + tcg_gen_movi_tl(cpu_eip, (uint32_t)(s->base.pc_next - s->cs_base)); } s->pc_save = s->pc; } @@ -610,8 +614,10 @@ static TCGv eip_next_tl(DisasContext *s) TCGv ret = tcg_temp_new(); tcg_gen_addi_tl(ret, cpu_eip, s->pc - s->pc_save); return ret; + } else if (CODE64(s)) { + return tcg_constant_tl(s->pc); } else { - return tcg_constant_tl(s->pc - s->cs_base); + return tcg_constant_tl((uint32_t)(s->pc - s->cs_base)); } } @@ -622,8 +628,10 @@ static TCGv eip_cur_tl(DisasContext *s) TCGv ret = tcg_temp_new(); tcg_gen_addi_tl(ret, cpu_eip, s->base.pc_next - s->pc_save); return ret; + } else if (CODE64(s)) { + return tcg_constant_tl(s->base.pc_next); } else { - return tcg_constant_tl(s->base.pc_next - s->cs_base); + return tcg_constant_tl((uint32_t)(s->base.pc_next - s->cs_base)); } } @@ -2859,6 +2867,10 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) } } new_eip &= mask; + new_pc = new_eip + s->cs_base; + if (!CODE64(s)) { + new_pc = (uint32_t)new_pc; + } gen_update_cc_op(s); set_cc_op(s, CC_OP_DYNAMIC); @@ -2876,8 +2888,7 @@ static void gen_jmp_rel(DisasContext *s, MemOp ot, int diff, int tb_num) } } - if (use_goto_tb && - translator_use_goto_tb(&s->base, new_eip + s->cs_base)) { + if (use_goto_tb && translator_use_goto_tb(&s->base, new_pc)) { /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); if (!(tb_cflags(s->base.tb) & CF_PCREL)) { |