diff --git a/cpu-all.h b/cpu-all.h index 1f4f1e76e82c09cf870fff948851681aef5bce23..0322955ea144292d172e5e7937a8fc30dee57ff4 100644 --- a/cpu-all.h +++ b/cpu-all.h @@ -765,6 +765,7 @@ void cpu_reset_interrupt(CPUState *env, int mask); #define BP_MEM_READ 0x01 #define BP_MEM_WRITE 0x02 #define BP_MEM_ACCESS (BP_MEM_READ | BP_MEM_WRITE) +#define BP_STOP_BEFORE_ACCESS 0x04 #define BP_GDB 0x10 int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags, diff --git a/exec.c b/exec.c index a9ac1ad433c551c10c1da4df3d217169b0cb9234..0dd4aa3d8d7b2a1abc6d9a751c886a6f452d79d4 100644 --- a/exec.c +++ b/exec.c @@ -2506,16 +2506,38 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { static void check_watchpoint(int offset, int len_mask, int flags) { CPUState *env = cpu_single_env; + target_ulong pc, cs_base; + TranslationBlock *tb; target_ulong vaddr; CPUWatchpoint *wp; + int cpu_flags; + if (env->watchpoint_hit) { + /* We re-entered the check after replacing the TB. Now raise + * the debug interrupt so that is will trigger after the + * current instruction. */ + cpu_interrupt(env, CPU_INTERRUPT_DEBUG); + return; + } vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; for (wp = env->watchpoints; wp != NULL; wp = wp->next) { if ((vaddr == (wp->vaddr & len_mask) || (vaddr & wp->len_mask) == wp->vaddr) && (wp->flags & flags)) { env->watchpoint_hit = wp; - cpu_interrupt(env, CPU_INTERRUPT_DEBUG); - break; + tb = tb_find_pc(env->mem_io_pc); + if (!tb) { + cpu_abort(env, "check_watchpoint: could not find TB for pc=%p", + (void *)env->mem_io_pc); + } + cpu_restore_state(tb, env, env->mem_io_pc, NULL); + tb_phys_invalidate(tb, -1); + if (wp->flags & BP_STOP_BEFORE_ACCESS) { + env->exception_index = EXCP_DEBUG; + } else { + cpu_get_tb_cpu_state(env, &pc, &cs_base, &cpu_flags); + tb_gen_code(env, pc, cs_base, cpu_flags, 1); + } + cpu_resume_from_signal(env, NULL); } } }