diff --git a/qemu/aarch64.h b/qemu/aarch64.h index ad096574a4..6340ab3015 100644 --- a/qemu/aarch64.h +++ b/qemu/aarch64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_aarch64 #define helper_gvec_bitsel helper_gvec_bitsel_aarch64 #define cpu_restore_state cpu_restore_state_aarch64 +#define cpu_restore_pc_only cpu_restore_pc_only_aarch64 #define page_collection_lock page_collection_lock_aarch64 #define page_collection_unlock page_collection_unlock_aarch64 #define free_code_gen_buffer free_code_gen_buffer_aarch64 @@ -2770,6 +2771,7 @@ #define gen_sshl_i64 gen_sshl_i64_aarch64 #define gen_intermediate_code gen_intermediate_code_aarch64 #define restore_state_to_opc restore_state_to_opc_aarch64 +#define restore_pc_to_opc restore_pc_to_opc_aarch64 #define disas_sve disas_sve_aarch64 #define helper_neon_qrdmlah_s16 helper_neon_qrdmlah_s16_aarch64 #define helper_gvec_qrdmlah_s16 helper_gvec_qrdmlah_s16_aarch64 diff --git a/qemu/accel/tcg/cputlb.c b/qemu/accel/tcg/cputlb.c index 3d0bebd33e..47bcd08dc0 100644 --- a/qemu/accel/tcg/cputlb.c +++ b/qemu/accel/tcg/cputlb.c @@ -1033,6 +1033,9 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, cpu_io_recompile(cpu, retaddr); } + /* Restore the guest CPU state to ensure the PC is synced */ + cpu_restore_pc_only(uc->cpu, retaddr, false); + r = memory_region_dispatch_read(uc, mr, mr_offset, &val, op, iotlbentry->attrs); if (r != MEMTX_OK) { #if 0 @@ -1067,6 +1070,9 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, } cpu->mem_io_pc = retaddr; + /* Restore the guest CPU state to ensure the PC is synced */ + cpu_restore_pc_only(uc->cpu, retaddr, false); + r = memory_region_dispatch_write(uc, mr, mr_offset, val, op, iotlbentry->attrs); if (r != MEMTX_OK) { #if 0 @@ -1569,7 +1575,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, @@ -1590,7 +1596,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced &&!uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, @@ -1658,7 +1664,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ, paddr, size, 0, hook->user_data)); @@ -1690,7 +1696,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, @@ -1740,7 +1746,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, @@ -1854,7 +1860,7 @@ load_helper(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_hookmem_t)hook->callback)(env->uc, UC_MEM_READ_AFTER, paddr, size, res, hook->user_data)); @@ -2202,7 +2208,7 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_hookmem_t)hook->callback)(uc, UC_MEM_WRITE, paddr, size, val, hook->user_data)); @@ -2221,7 +2227,7 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, @@ -2274,7 +2280,7 @@ store_helper(CPUArchState *env, target_ulong addr, uint64_t val, if (!HOOK_BOUND_CHECK(hook, paddr)) continue; if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD_VAR(handled, diff --git a/qemu/accel/tcg/translate-all.c b/qemu/accel/tcg/translate-all.c index 71b6b2b193..af4e628257 100644 --- a/qemu/accel/tcg/translate-all.c +++ b/qemu/accel/tcg/translate-all.c @@ -317,6 +317,84 @@ static int cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, return 0; } +static int cpu_restore_pc_from_tb(CPUState *cpu, TranslationBlock *tb, + uintptr_t searched_pc, bool reset_icount) +{ + target_ulong data[TARGET_INSN_START_WORDS] = { tb->pc }; + uintptr_t host_pc = (uintptr_t)tb->tc.ptr; + CPUArchState *env = cpu->env_ptr; + uint8_t *p = (uint8_t *)tb->tc.ptr + tb->tc.size; + int i, j, num_insns = tb->icount; + + searched_pc -= GETPC_ADJ; + + if (searched_pc < host_pc) { + return -1; + } + + /* Reconstruct the stored insn data while looking for the point at + which the end of the insn exceeds the searched_pc. */ + for (i = 0; i < num_insns; ++i) { + for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + data[j] += decode_sleb128(&p); + } + host_pc += decode_sleb128(&p); + if (host_pc > searched_pc) { + goto found; + } + } + return -1; + + found: + if (reset_icount && (tb_cflags(tb) & CF_USE_ICOUNT)) { + /* Reset the cycle counter to the start of the block + and shift if to the number of actually executed instructions */ + cpu_neg(cpu)->icount_decr.u16.low += num_insns - i; + } + restore_pc_to_opc(env, tb, data); + + return 0; +} + +bool cpu_restore_pc_only(CPUState *cpu, uintptr_t host_pc, bool will_exit) +{ + TCGContext *tcg_ctx = cpu->uc->tcg_ctx; + TranslationBlock *tb; + bool r = false; + uintptr_t check_offset; + struct uc_struct *uc = cpu->uc; + + /* The host_pc has to be in the region of current code buffer. If + * it is not we will not be able to resolve it here. The two cases + * where host_pc will not be correct are: + * + * - fault during translation (instruction fetch) + * - fault from helper (not using GETPC() macro) + * + * Either way we need return early as we can't resolve it here. + * + * We are using unsigned arithmetic so if host_pc < + * tcg_init_ctx.code_gen_buffer check_offset will wrap to way + * above the code_gen_buffer_size + */ + check_offset = host_pc - (uintptr_t) uc->tcg_ctx->code_gen_buffer; + + if (check_offset < uc->tcg_ctx->code_gen_buffer_size) { + tb = tcg_tb_lookup(tcg_ctx, host_pc); + if (tb) { + cpu_restore_pc_from_tb(cpu, tb, host_pc, will_exit); + if (tb_cflags(tb) & CF_NOCACHE) { + /* one-shot translation, invalidate it immediately */ + tb_phys_invalidate(tcg_ctx, tb, -1); + tcg_tb_remove(tcg_ctx, tb); + } + r = true; + } + } + + return r; +} + bool cpu_restore_state(CPUState *cpu, uintptr_t host_pc, bool will_exit) { TCGContext *tcg_ctx = cpu->uc->tcg_ctx; diff --git a/qemu/arm.h b/qemu/arm.h index cd81f540f0..92cd5eaf72 100644 --- a/qemu/arm.h +++ b/qemu/arm.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_arm #define helper_gvec_bitsel helper_gvec_bitsel_arm #define cpu_restore_state cpu_restore_state_arm +#define cpu_restore_pc_only cpu_restore_pc_only_arm #define page_collection_lock page_collection_lock_arm #define page_collection_unlock page_collection_unlock_arm #define free_code_gen_buffer free_code_gen_buffer_arm @@ -1789,6 +1790,7 @@ #define gen_sshl_i64 gen_sshl_i64_arm #define gen_intermediate_code gen_intermediate_code_arm #define restore_state_to_opc restore_state_to_opc_arm +#define restore_pc_to_opc restore_pc_to_opc_arm #define helper_neon_qrdmlah_s16 helper_neon_qrdmlah_s16_arm #define helper_gvec_qrdmlah_s16 helper_gvec_qrdmlah_s16_arm #define helper_neon_qrdmlsh_s16 helper_neon_qrdmlsh_s16_arm diff --git a/qemu/include/exec/exec-all.h b/qemu/include/exec/exec-all.h index 68c656787f..83e1e9dfd4 100644 --- a/qemu/include/exec/exec-all.h +++ b/qemu/include/exec/exec-all.h @@ -40,6 +40,10 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns); void restore_state_to_opc(CPUArchState *env, TranslationBlock *tb, target_ulong *data); +void restore_pc_to_opc(CPUArchState *env, TranslationBlock *tb, + target_ulong *data); +bool cpu_restore_pc_only(CPUState *cpu, uintptr_t host_pc, bool will_exit); + /** * cpu_restore_state: * @cpu: the vCPU state is to be restore to diff --git a/qemu/m68k.h b/qemu/m68k.h index 67f94364d2..aafdb5ba0a 100644 --- a/qemu/m68k.h +++ b/qemu/m68k.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_m68k #define helper_gvec_bitsel helper_gvec_bitsel_m68k #define cpu_restore_state cpu_restore_state_m68k +#define cpu_restore_pc_only cpu_restore_pc_only_m68k #define page_collection_lock page_collection_lock_m68k #define page_collection_unlock page_collection_unlock_m68k #define free_code_gen_buffer free_code_gen_buffer_m68k @@ -1450,4 +1451,5 @@ #define register_m68k_insns register_m68k_insns_m68k #define gen_intermediate_code gen_intermediate_code_m68k #define restore_state_to_opc restore_state_to_opc_m68k +#define restore_pc_to_opc restore_pc_to_opc_m68k #endif diff --git a/qemu/mips.h b/qemu/mips.h index 9b0fa37cd0..08b2b189dc 100644 --- a/qemu/mips.h +++ b/qemu/mips.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_mips #define helper_gvec_bitsel helper_gvec_bitsel_mips #define cpu_restore_state cpu_restore_state_mips +#define cpu_restore_pc_only cpu_restore_pc_only_mips #define page_collection_lock page_collection_lock_mips #define page_collection_unlock page_collection_unlock_mips #define free_code_gen_buffer free_code_gen_buffer_mips @@ -2401,6 +2402,7 @@ #define cpu_mips_realize_env cpu_mips_realize_env_mips #define cpu_state_reset cpu_state_reset_mips #define restore_state_to_opc restore_state_to_opc_mips +#define restore_pc_to_opc restore_pc_to_opc_mips #define ieee_rm ieee_rm_mips #define mips_defs mips_defs_mips #define mips_defs_number mips_defs_number_mips diff --git a/qemu/mips64.h b/qemu/mips64.h index ec030ff261..a38dd12daa 100644 --- a/qemu/mips64.h +++ b/qemu/mips64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_mips64 #define helper_gvec_bitsel helper_gvec_bitsel_mips64 #define cpu_restore_state cpu_restore_state_mips64 +#define cpu_restore_pc_only cpu_restore_pc_only_mips64 #define page_collection_lock page_collection_lock_mips64 #define page_collection_unlock page_collection_unlock_mips64 #define free_code_gen_buffer free_code_gen_buffer_mips64 @@ -2401,6 +2402,7 @@ #define cpu_mips_realize_env cpu_mips_realize_env_mips64 #define cpu_state_reset cpu_state_reset_mips64 #define restore_state_to_opc restore_state_to_opc_mips64 +#define restore_pc_to_opc restore_pc_to_opc_mips64 #define ieee_rm ieee_rm_mips64 #define mips_defs mips_defs_mips64 #define mips_defs_number mips_defs_number_mips64 diff --git a/qemu/mips64el.h b/qemu/mips64el.h index ea8c92739c..47ee8f1226 100644 --- a/qemu/mips64el.h +++ b/qemu/mips64el.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_mips64el #define helper_gvec_bitsel helper_gvec_bitsel_mips64el #define cpu_restore_state cpu_restore_state_mips64el +#define cpu_restore_pc_only cpu_restore_pc_only_mips64el #define page_collection_lock page_collection_lock_mips64el #define page_collection_unlock page_collection_unlock_mips64el #define free_code_gen_buffer free_code_gen_buffer_mips64el @@ -2401,6 +2402,7 @@ #define cpu_mips_realize_env cpu_mips_realize_env_mips64el #define cpu_state_reset cpu_state_reset_mips64el #define restore_state_to_opc restore_state_to_opc_mips64el +#define restore_pc_to_opc restore_pc_to_opc_mips64el #define ieee_rm ieee_rm_mips64el #define mips_defs mips_defs_mips64el #define mips_defs_number mips_defs_number_mips64el diff --git a/qemu/mipsel.h b/qemu/mipsel.h index 1b30cbc229..41f472649b 100644 --- a/qemu/mipsel.h +++ b/qemu/mipsel.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_mipsel #define helper_gvec_bitsel helper_gvec_bitsel_mipsel #define cpu_restore_state cpu_restore_state_mipsel +#define cpu_restore_pc_only cpu_restore_pc_only_mipsel #define page_collection_lock page_collection_lock_mipsel #define page_collection_unlock page_collection_unlock_mipsel #define free_code_gen_buffer free_code_gen_buffer_mipsel @@ -2401,6 +2402,7 @@ #define cpu_mips_realize_env cpu_mips_realize_env_mipsel #define cpu_state_reset cpu_state_reset_mipsel #define restore_state_to_opc restore_state_to_opc_mipsel +#define restore_pc_to_opc restore_pc_to_opc_mipsel #define ieee_rm ieee_rm_mipsel #define mips_defs mips_defs_mipsel #define mips_defs_number mips_defs_number_mipsel diff --git a/qemu/ppc.h b/qemu/ppc.h index 9648fd2fe5..57c42983a0 100644 --- a/qemu/ppc.h +++ b/qemu/ppc.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_ppc #define helper_gvec_bitsel helper_gvec_bitsel_ppc #define cpu_restore_state cpu_restore_state_ppc +#define cpu_restore_pc_only cpu_restore_pc_only_ppc #define page_collection_lock page_collection_lock_ppc #define page_collection_unlock page_collection_unlock_ppc #define free_code_gen_buffer free_code_gen_buffer_ppc @@ -1680,6 +1681,7 @@ #define cpu_ppc_init cpu_ppc_init_ppc #define gen_intermediate_code gen_intermediate_code_ppc #define restore_state_to_opc restore_state_to_opc_ppc +#define restore_pc_to_opc restore_pc_to_opc_ppc #define ppc_set_irq ppc_set_irq_ppc #define ppc6xx_irq_init ppc6xx_irq_init_ppc #define ppc40x_core_reset ppc40x_core_reset_ppc diff --git a/qemu/ppc64.h b/qemu/ppc64.h index 2d995d5ece..3accf0a61a 100644 --- a/qemu/ppc64.h +++ b/qemu/ppc64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_ppc64 #define helper_gvec_bitsel helper_gvec_bitsel_ppc64 #define cpu_restore_state cpu_restore_state_ppc64 +#define cpu_restore_pc_only cpu_restore_pc_only_ppc64 #define page_collection_lock page_collection_lock_ppc64 #define page_collection_unlock page_collection_unlock_ppc64 #define free_code_gen_buffer free_code_gen_buffer_ppc64 @@ -1680,6 +1681,7 @@ #define cpu_ppc_init cpu_ppc_init_ppc64 #define gen_intermediate_code gen_intermediate_code_ppc64 #define restore_state_to_opc restore_state_to_opc_ppc64 +#define restore_pc_to_opc restore_pc_to_opc_ppc64 #define ppc_set_irq ppc_set_irq_ppc64 #define ppc6xx_irq_init ppc6xx_irq_init_ppc64 #define ppc40x_core_reset ppc40x_core_reset_ppc64 diff --git a/qemu/riscv32.h b/qemu/riscv32.h index 26c798b413..5c09b44f91 100644 --- a/qemu/riscv32.h +++ b/qemu/riscv32.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_riscv32 #define helper_gvec_bitsel helper_gvec_bitsel_riscv32 #define cpu_restore_state cpu_restore_state_riscv32 +#define cpu_restore_pc_only cpu_restore_pc_only_riscv32 #define page_collection_lock page_collection_lock_riscv32 #define page_collection_unlock page_collection_unlock_riscv32 #define free_code_gen_buffer free_code_gen_buffer_riscv32 @@ -1376,6 +1377,7 @@ #define gen_intermediate_code gen_intermediate_code_riscv32 #define riscv_translate_init riscv_translate_init_riscv32 #define restore_state_to_opc restore_state_to_opc_riscv32 +#define restore_pc_to_opc restore_pc_to_opc_riscv32 #define cpu_riscv_init cpu_riscv_init_riscv32 #define helper_fcvt_l_s helper_fcvt_l_s_riscv32 #define helper_fcvt_lu_s helper_fcvt_lu_s_riscv32 diff --git a/qemu/riscv64.h b/qemu/riscv64.h index 8332ce6dab..1d9a69f562 100644 --- a/qemu/riscv64.h +++ b/qemu/riscv64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_riscv64 #define helper_gvec_bitsel helper_gvec_bitsel_riscv64 #define cpu_restore_state cpu_restore_state_riscv64 +#define cpu_restore_pc_only cpu_restore_pc_only_riscv64 #define page_collection_lock page_collection_lock_riscv64 #define page_collection_unlock page_collection_unlock_riscv64 #define free_code_gen_buffer free_code_gen_buffer_riscv64 @@ -1376,6 +1377,7 @@ #define gen_intermediate_code gen_intermediate_code_riscv64 #define riscv_translate_init riscv_translate_init_riscv64 #define restore_state_to_opc restore_state_to_opc_riscv64 +#define restore_pc_to_opc restore_pc_to_opc_riscv64 #define cpu_riscv_init cpu_riscv_init_riscv64 #define helper_fcvt_l_s helper_fcvt_l_s_riscv64 #define helper_fcvt_lu_s helper_fcvt_lu_s_riscv64 diff --git a/qemu/s390x.h b/qemu/s390x.h index 5daa819afa..38d1d1c3aa 100644 --- a/qemu/s390x.h +++ b/qemu/s390x.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_s390x #define helper_gvec_bitsel helper_gvec_bitsel_s390x #define cpu_restore_state cpu_restore_state_s390x +#define cpu_restore_pc_only cpu_restore_pc_only_s390x #define page_collection_lock page_collection_lock_s390x #define page_collection_unlock page_collection_unlock_s390x #define free_code_gen_buffer free_code_gen_buffer_s390x diff --git a/qemu/softmmu/ioport.c b/qemu/softmmu/ioport.c index fdf8d0f457..eed505c209 100644 --- a/qemu/softmmu/ioport.c +++ b/qemu/softmmu/ioport.c @@ -47,7 +47,7 @@ void cpu_outb(struct uc_struct *uc, uint32_t addr, uint8_t val, uintptr_t retadd continue; if (hook->insn == UC_X86_INS_OUT) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_insn_out_t)hook->callback)(uc, addr, 1, val, hook->user_data)); @@ -73,7 +73,7 @@ void cpu_outw(struct uc_struct *uc, uint32_t addr, uint16_t val, uintptr_t retad continue; if (hook->insn == UC_X86_INS_OUT) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_insn_out_t)hook->callback)(uc, addr, 2, val, hook->user_data)); @@ -99,7 +99,7 @@ void cpu_outl(struct uc_struct *uc, uint32_t addr, uint32_t val, uintptr_t retad continue; if (hook->insn == UC_X86_INS_OUT) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_insn_out_t)hook->callback)(uc, addr, 4, val, hook->user_data)); @@ -124,7 +124,7 @@ uint8_t cpu_inb(struct uc_struct *uc, uint32_t addr, uintptr_t retaddr) continue; if (hook->insn == UC_X86_INS_IN) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } uint8_t ret; @@ -154,7 +154,7 @@ uint16_t cpu_inw(struct uc_struct *uc, uint32_t addr, uintptr_t retaddr) continue; if (hook->insn == UC_X86_INS_IN) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } uint16_t ret; @@ -186,7 +186,7 @@ uint32_t cpu_inl(struct uc_struct *uc, uint32_t addr, uintptr_t retaddr) continue; if (hook->insn == UC_X86_INS_IN) { if (!synced && !uc->skip_sync_pc_on_exit && retaddr) { - cpu_restore_state(uc->cpu, retaddr, false); + cpu_restore_pc_only(uc->cpu, retaddr, false); synced = true; } uint32_t ret; diff --git a/qemu/softmmu/unicorn_vtlb.c b/qemu/softmmu/unicorn_vtlb.c index 25c684064f..d8c77b7e21 100644 --- a/qemu/softmmu/unicorn_vtlb.c +++ b/qemu/softmmu/unicorn_vtlb.c @@ -55,7 +55,7 @@ bool unicorn_fill_tlb(CPUState *cs, vaddr address, int size, struct hook *hook; HOOK_FOREACH_VAR_DECLARE; - cpu_restore_state(cs, retaddr, false); + cpu_restore_pc_only(cs, retaddr, false); HOOK_FOREACH(uc, hook, UC_HOOK_TLB_FILL) { if (hook->to_delete) { diff --git a/qemu/sparc.h b/qemu/sparc.h index 5f7c4689b7..5947420026 100644 --- a/qemu/sparc.h +++ b/qemu/sparc.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_sparc #define helper_gvec_bitsel helper_gvec_bitsel_sparc #define cpu_restore_state cpu_restore_state_sparc +#define cpu_restore_pc_only cpu_restore_pc_only_sparc #define page_collection_lock page_collection_lock_sparc #define page_collection_unlock page_collection_unlock_sparc #define free_code_gen_buffer free_code_gen_buffer_sparc @@ -1402,6 +1403,7 @@ #define gen_intermediate_code gen_intermediate_code_sparc #define sparc_tcg_init sparc_tcg_init_sparc #define restore_state_to_opc restore_state_to_opc_sparc +#define restore_pc_to_opc restore_pc_to_opc_sparc #define cpu_set_cwp cpu_set_cwp_sparc #define cpu_get_psr cpu_get_psr_sparc #define cpu_put_psr_raw cpu_put_psr_raw_sparc diff --git a/qemu/sparc64.h b/qemu/sparc64.h index 33003493db..058f21b0d5 100644 --- a/qemu/sparc64.h +++ b/qemu/sparc64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_sparc64 #define helper_gvec_bitsel helper_gvec_bitsel_sparc64 #define cpu_restore_state cpu_restore_state_sparc64 +#define cpu_restore_pc_only cpu_restore_pc_only_sparc64 #define page_collection_lock page_collection_lock_sparc64 #define page_collection_unlock page_collection_unlock_sparc64 #define free_code_gen_buffer free_code_gen_buffer_sparc64 @@ -1402,6 +1403,7 @@ #define gen_intermediate_code gen_intermediate_code_sparc64 #define sparc_tcg_init sparc_tcg_init_sparc64 #define restore_state_to_opc restore_state_to_opc_sparc64 +#define restore_pc_to_opc restore_pc_to_opc_sparc64 #define cpu_set_cwp cpu_set_cwp_sparc64 #define cpu_get_psr cpu_get_psr_sparc64 #define cpu_put_psr_raw cpu_put_psr_raw_sparc64 diff --git a/qemu/target/arm/op_helper.c b/qemu/target/arm/op_helper.c index af4e1089c4..6eb2b97e5f 100644 --- a/qemu/target/arm/op_helper.c +++ b/qemu/target/arm/op_helper.c @@ -298,7 +298,7 @@ void HELPER(wfi)(CPUARMState *env, uint32_t insn_len) if (hook->insn == (env->aarch64 ? UC_ARM64_INS_WFI : UC_ARM_INS_WFI)) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD_VAR(skip_wfi, ((uc_cb_insn_wfi_t)hook->callback)(env->uc, hook->user_data)); diff --git a/qemu/target/arm/translate.c b/qemu/target/arm/translate.c index 744d8ff709..6c64703bd6 100644 --- a/qemu/target/arm/translate.c +++ b/qemu/target/arm/translate.c @@ -11826,3 +11826,13 @@ void restore_state_to_opc(CPUARMState *env, TranslationBlock *tb, env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; } } + +void restore_pc_to_opc(CPUARMState *env, TranslationBlock *tb, + target_ulong *data) +{ + if (is_a64(env)) { + env->pc = data[0]; + } else { + env->regs[15] = data[0]; + } +} diff --git a/qemu/target/i386/misc_helper.c b/qemu/target/i386/misc_helper.c index ccc12c8192..0a8cfb04e7 100644 --- a/qemu/target/i386/misc_helper.c +++ b/qemu/target/i386/misc_helper.c @@ -125,7 +125,7 @@ void helper_cpuid(CPUX86State *env) if (hook->insn == UC_X86_INS_CPUID) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD_VAR(skip_cpuid, ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); @@ -237,7 +237,7 @@ void helper_rdtsc(CPUX86State *env) if (hook->insn == UC_X86_INS_RDTSC) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD_VAR(skip_rdtsc, ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); @@ -281,7 +281,7 @@ void helper_rdtscp(CPUX86State *env) if (hook->insn == UC_X86_INS_RDTSCP) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD_VAR(skip_rdtscp, ((uc_cb_insn_cpuid_t)hook->callback)(env->uc, hook->user_data)); diff --git a/qemu/target/i386/seg_helper.c b/qemu/target/i386/seg_helper.c index 97447fee38..118a9d5584 100644 --- a/qemu/target/i386/seg_helper.c +++ b/qemu/target/i386/seg_helper.c @@ -985,7 +985,7 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) if (hook->insn == UC_X86_INS_SYSCALL) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data)); @@ -2369,7 +2369,7 @@ void helper_sysenter(CPUX86State *env, int next_eip_addend) if (hook->insn == UC_X86_INS_SYSENTER) { uintptr_t pc = GETPC(); if (!synced && !uc->skip_sync_pc_on_exit && pc) { - cpu_restore_state(uc->cpu, pc, false); + cpu_restore_pc_only(uc->cpu, pc, false); synced = true; } JIT_CALLBACK_GUARD(((uc_cb_insn_syscall_t)hook->callback)(env->uc, hook->user_data)); diff --git a/qemu/target/i386/translate.c b/qemu/target/i386/translate.c index 8278774a58..1f513f87b2 100644 --- a/qemu/target/i386/translate.c +++ b/qemu/target/i386/translate.c @@ -9458,3 +9458,9 @@ void restore_state_to_opc(CPUX86State *env, TranslationBlock *tb, env->cc_op = cc_op; } } + +void restore_pc_to_opc(CPUX86State *env, TranslationBlock *tb, + target_ulong *data) +{ + env->eip = data[0] - tb->cs_base; +} diff --git a/qemu/target/m68k/translate.c b/qemu/target/m68k/translate.c index 5b9a74ce2b..50f0d1fe84 100644 --- a/qemu/target/m68k/translate.c +++ b/qemu/target/m68k/translate.c @@ -6460,3 +6460,9 @@ void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, env->cc_op = cc_op; } } + +void restore_pc_to_opc(CPUM68KState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} diff --git a/qemu/target/mips/translate.c b/qemu/target/mips/translate.c index 3fab57b251..7590ed4b90 100644 --- a/qemu/target/mips/translate.c +++ b/qemu/target/mips/translate.c @@ -31438,3 +31438,9 @@ void restore_state_to_opc(CPUMIPSState *env, TranslationBlock *tb, break; } } + +void restore_pc_to_opc(CPUMIPSState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->active_tc.PC = data[0]; +} diff --git a/qemu/target/ppc/translate.c b/qemu/target/ppc/translate.c index 0b3e5c8c08..206d4eb4fb 100644 --- a/qemu/target/ppc/translate.c +++ b/qemu/target/ppc/translate.c @@ -7761,3 +7761,9 @@ void restore_state_to_opc(CPUPPCState *env, TranslationBlock *tb, { env->nip = data[0]; } + +void restore_pc_to_opc(CPUPPCState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->nip = data[0]; +} diff --git a/qemu/target/riscv/cpu.c b/qemu/target/riscv/cpu.c index 2313cfc6cc..258d7b125f 100644 --- a/qemu/target/riscv/cpu.c +++ b/qemu/target/riscv/cpu.c @@ -167,6 +167,12 @@ void restore_state_to_opc(CPURISCVState *env, TranslationBlock *tb, env->pc = data[0]; } +void restore_pc_to_opc(CPURISCVState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} + static void riscv_cpu_reset(CPUState *dev) { CPUState *cs = CPU(dev); diff --git a/qemu/target/s390x/translate.c b/qemu/target/s390x/translate.c index e41a3b73b0..690cf7e2cc 100644 --- a/qemu/target/s390x/translate.c +++ b/qemu/target/s390x/translate.c @@ -6976,3 +6976,9 @@ void restore_state_to_opc(CPUS390XState *env, TranslationBlock *tb, /* Record ILEN. */ env->int_pgm_ilen = data[2]; } + +void restore_pc_to_opc(CPUS390XState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->psw.addr = data[0]; +} diff --git a/qemu/target/sparc/translate.c b/qemu/target/sparc/translate.c index 95b24ab649..209cf0c2fb 100644 --- a/qemu/target/sparc/translate.c +++ b/qemu/target/sparc/translate.c @@ -6178,3 +6178,9 @@ void restore_state_to_opc(CPUSPARCState *env, TranslationBlock *tb, env->npc = npc; } } + +void restore_pc_to_opc(CPUSPARCState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->pc = data[0]; +} diff --git a/qemu/target/tricore/translate.c b/qemu/target/tricore/translate.c index 75188b8be6..d51455d7fb 100644 --- a/qemu/target/tricore/translate.c +++ b/qemu/target/tricore/translate.c @@ -9328,6 +9328,12 @@ restore_state_to_opc(CPUTriCoreState *env, TranslationBlock *tb, env->PC = data[0]; } +void restore_pc_to_opc(CPUTriCoreState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->PC = data[0]; +} + /* * * Initialization diff --git a/qemu/tricore.h b/qemu/tricore.h index 937eb5d257..edafed3fe5 100644 --- a/qemu/tricore.h +++ b/qemu/tricore.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_tricore #define helper_gvec_bitsel helper_gvec_bitsel_tricore #define cpu_restore_state cpu_restore_state_tricore +#define cpu_restore_pc_only cpu_restore_pc_only_tricore #define page_collection_lock page_collection_lock_tricore #define page_collection_unlock page_collection_unlock_tricore #define free_code_gen_buffer free_code_gen_buffer_tricore @@ -1303,5 +1304,6 @@ #define helper_pack helper_pack_tricore #define gen_intermediate_code gen_intermediate_code_tricore #define restore_state_to_opc restore_state_to_opc_tricore +#define restore_pc_to_opc restore_pc_to_opc_tricore #define helper_uc_tricore_exit helper_uc_tricore_exit_tricore #endif diff --git a/qemu/x86_64.h b/qemu/x86_64.h index 637b88524a..6e2d4880c3 100644 --- a/qemu/x86_64.h +++ b/qemu/x86_64.h @@ -1257,6 +1257,7 @@ #define helper_gvec_umax64 helper_gvec_umax64_x86_64 #define helper_gvec_bitsel helper_gvec_bitsel_x86_64 #define cpu_restore_state cpu_restore_state_x86_64 +#define cpu_restore_pc_only cpu_restore_pc_only_x86_64 #define page_collection_lock page_collection_lock_x86_64 #define page_collection_unlock page_collection_unlock_x86_64 #define free_code_gen_buffer free_code_gen_buffer_x86_64 @@ -1891,6 +1892,7 @@ #define tcg_x86_init tcg_x86_init_x86_64 #define gen_intermediate_code gen_intermediate_code_x86_64 #define restore_state_to_opc restore_state_to_opc_x86_64 +#define restore_pc_to_opc restore_pc_to_opc_x86_64 #define x86_cpu_xsave_all_areas x86_cpu_xsave_all_areas_x86_64 #define x86_cpu_xrstor_all_areas x86_cpu_xrstor_all_areas_x86_64 #define cpu_get_fp80 cpu_get_fp80_x86_64 diff --git a/symbols.sh b/symbols.sh index bcedaea983..e2f5ec3380 100755 --- a/symbols.sh +++ b/symbols.sh @@ -1257,6 +1257,7 @@ helper_gvec_umax32 \ helper_gvec_umax64 \ helper_gvec_bitsel \ cpu_restore_state \ +cpu_restore_pc_only \ page_collection_lock \ page_collection_unlock \ free_code_gen_buffer \ @@ -1894,6 +1895,7 @@ do_vmexit \ tcg_x86_init \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ x86_cpu_xsave_all_areas \ x86_cpu_xrstor_all_areas \ cpu_get_fp80 \ @@ -2396,6 +2398,7 @@ gen_sshl_i32 \ gen_sshl_i64 \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ helper_neon_qrdmlah_s16 \ helper_gvec_qrdmlah_s16 \ helper_neon_qrdmlsh_s16 \ @@ -4085,6 +4088,7 @@ gen_sshl_i32 \ gen_sshl_i64 \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ disas_sve \ helper_neon_qrdmlah_s16 \ helper_gvec_qrdmlah_s16 \ @@ -4391,6 +4395,7 @@ pmpaddr_csr_read \ gen_intermediate_code \ riscv_translate_init \ restore_state_to_opc \ +restore_pc_to_opc \ cpu_riscv_init \ helper_fcvt_l_s \ helper_fcvt_lu_s \ @@ -5515,6 +5520,7 @@ mips_tcg_init \ cpu_mips_realize_env \ cpu_state_reset \ restore_state_to_opc \ +restore_pc_to_opc \ ieee_rm \ mips_defs \ mips_defs_number \ @@ -5637,6 +5643,7 @@ sparc_cpu_get_phys_page_debug \ gen_intermediate_code \ sparc_tcg_init \ restore_state_to_opc \ +restore_pc_to_opc \ cpu_set_cwp \ cpu_get_psr \ cpu_put_psr_raw \ @@ -5827,6 +5834,7 @@ m68k_tcg_init \ register_m68k_insns \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ " ppc_SYMBOLS=" @@ -6216,6 +6224,7 @@ ppc_translate_init \ cpu_ppc_init \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ ppc_set_irq \ ppc6xx_irq_init \ ppc40x_core_reset \ @@ -6549,6 +6558,7 @@ helper_fmsub \ helper_pack \ gen_intermediate_code \ restore_state_to_opc \ +restore_pc_to_opc \ helper_uc_tricore_exit \ " diff --git a/tests/unit/test_arm.c b/tests/unit/test_arm.c index 3b0c7915cb..972b9462e0 100644 --- a/tests/unit/test_arm.c +++ b/tests/unit/test_arm.c @@ -16,6 +16,10 @@ typedef struct _WFI_HOOK_INSN_RESULT { bool called; } WFI_HOOK_INSN_RESULT; +typedef struct _MMIO_MAP_WRITE_PC_SYNC_RESULT { + uint32_t pc_val; +} MMIO_MAP_WRITE_PC_SYNC_RESULT; + static void test_arm_nop(void) { uc_engine *uc; @@ -1052,8 +1056,8 @@ static void test_arm_hook_insn_wfi(void) uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1, UC_CPU_ARM_CORTEX_A15); - OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_arm_hook_insn_wfi_callback, &result, 1, 0, - UC_ARM_INS_WFI)); + OK(uc_hook_add(uc, &hook, UC_HOOK_INSN, test_arm_hook_insn_wfi_callback, + &result, 1, 0, UC_ARM_INS_WFI)); OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0)); TEST_CHECK(result.called == true); @@ -1062,6 +1066,100 @@ static void test_arm_hook_insn_wfi(void) OK(uc_close(uc)); } +static uint64_t test_arm_mmio_map_pc_sync_read_callback(struct uc_struct *uc, uint64_t addr, + unsigned size, void *user_data) +{ + return 0; +} + +static void test_arm_mmio_map_pc_sync_write_callback(struct uc_struct *uc, uint64_t addr, + unsigned size, uint64_t data, void *user_data) +{ + MMIO_MAP_WRITE_PC_SYNC_RESULT *result = (MMIO_MAP_WRITE_PC_SYNC_RESULT *)user_data; + uc_reg_read(uc, UC_ARM_REG_PC, &result->pc_val); +} + +static void test_arm_mmio_map_pc_sync(void) +{ + uc_engine *uc; + char code[] = "\xce\xf2\x00\x01\xcd\xf6\xad\x62\x0a\x60\x00\xbf"; + /* + * 00001000 0 cef20001 movt r1, #0xe000 + * 00001004 0 cdf6ad62 movt r2, #0xdead + * 00001008 0 0a60 str r2, [r1] + * 0000100a 0 00bf nop + */ + + MMIO_MAP_WRITE_PC_SYNC_RESULT result = {0xFFFFFFFF}; + + uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code, sizeof(code) - 1, + UC_CPU_ARM_CORTEX_A15); + OK(uc_mmio_map(uc, 0xe0000000, 0x1000, + test_arm_mmio_map_pc_sync_read_callback, NULL, + test_arm_mmio_map_pc_sync_write_callback, &result)); + + OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code) - 1, 0, 0)); + /* + * The PC value should be at the str inst, because that is when the MMIO_MAP + * write callback is called. If the PC has not been synced yet, + * our PC value would be 0x1000. + */ + TEST_CHECK(result.pc_val == 0x1008); + + OK(uc_close(uc)); +}; + +static bool test_arm_hook_condexec_corruption_cb(uc_engine *uc, int type, + uint64_t address, int size, + int64_t value, void *user_data) +{ + return 0; +} + +static void test_arm_hook_condexec_corruption(void) +{ + /* + * Test to ensure that ARM condexec bits are not + * corrupted on a hook, and that only PC is synced. + */ + uc_engine *uc; + uc_hook hook; + uint32_t reg; + char code_main[] = "\x00\xf0\x7e\xf8\x00\xbf\x96\x21"; + /* + * 00010000 00f07ef8 bl 0x1100 + * 00010004 00bf nop + * 00010006 9621 movs r1, #0x96 + */ + char code_itte_fn[] = "\x9a\xbf\x00\xbf\x00\x48\x00\xbf\x70\x47"; + /* + * 00010100 9abf itte ls + * 00010102 00bf nop + * 00010104 0048 ldr r0, [pc] @ trigger the mem hook! + * 00010106 00bf nop + * 00010108 7047 bx lr + */ + + uc_common_setup(&uc, UC_ARCH_ARM, UC_MODE_THUMB, code_main, + sizeof(code_main) - 1, UC_CPU_ARM_CORTEX_A15); + OK(uc_mem_write(uc, 0x1100, code_itte_fn, sizeof(code_itte_fn) - 1)); + OK(uc_hook_add(uc, &hook, UC_HOOK_MEM_READ, + test_arm_hook_condexec_corruption_cb, NULL, 1, 0)); + + OK(uc_emu_start(uc, code_start | 1, code_start + sizeof(code_main) - 1, + 0, 0)); + + /* + Ensure the MOVS instruction executed. If condexec has been + corrupted, this MOVS instruction becomes conditional and + will not execute. + */ + OK(uc_reg_read(uc, UC_ARM_REG_R1, ®)); + TEST_CHECK(reg == 0x96); + + OK(uc_close(uc)); +}; + TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_thumb_sub", test_arm_thumb_sub}, {"test_armeb_sub", test_armeb_sub}, @@ -1094,4 +1192,6 @@ TEST_LIST = {{"test_arm_nop", test_arm_nop}, {"test_arm_v7_lpae", test_arm_v7_lpae}, {"test_arm_svc_hvc_syndrome", test_arm_svc_hvc_syndrome}, {"test_arm_hook_insn_wfi", test_arm_hook_insn_wfi}, + {"test_arm_mmio_map_pc_sync", test_arm_mmio_map_pc_sync}, + {"test_arm_hook_condexec_corruption", test_arm_hook_condexec_corruption}, {NULL, NULL}};