CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
void *io_mem_opaque[IO_MEM_NB_ENTRIES];
static int io_mem_nb;
+#if defined(CONFIG_SOFTMMU)
+static int io_mem_watch;
+#endif
/* log support */
char *logfilename = "/tmp/qemu.log";
cpu_index++;
}
env->cpu_index = cpu_index;
+ env->nb_watchpoints = 0;
*penv = env;
}
}
#endif
+/* Add a watchpoint. */
+int cpu_watchpoint_insert(CPUState *env, target_ulong addr)
+{
+ int i;
+
+ for (i = 0; i < env->nb_watchpoints; i++) {
+ if (addr == env->watchpoint[i].vaddr)
+ return 0;
+ }
+ if (env->nb_watchpoints >= MAX_WATCHPOINTS)
+ return -1;
+
+ i = env->nb_watchpoints++;
+ env->watchpoint[i].vaddr = addr;
+ tlb_flush_page(env, addr);
+ /* FIXME: This flush is needed because of the hack to make memory ops
+ terminate the TB. It can be removed once the proper IO trap and
+ re-execute bits are in. */
+ tb_flush(env);
+ return i;
+}
+
+/* Remove a watchpoint. */
+int cpu_watchpoint_remove(CPUState *env, target_ulong addr)
+{
+ int i;
+
+ for (i = 0; i < env->nb_watchpoints; i++) {
+ if (addr == env->watchpoint[i].vaddr) {
+ env->nb_watchpoints--;
+ env->watchpoint[i] = env->watchpoint[env->nb_watchpoints];
+ tlb_flush_page(env, addr);
+ return 0;
+ }
+ }
+ return -1;
+}
+
/* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
breakpoint is reached */
int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
target_phys_addr_t addend;
int ret;
CPUTLBEntry *te;
+ int i;
p = phys_page_find(paddr >> TARGET_PAGE_BITS);
if (!p) {
address = vaddr;
addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK);
}
+
+ /* Make accesses to pages with watchpoints go via the
+ watchpoint trap routines. */
+ for (i = 0; i < env->nb_watchpoints; i++) {
+ if (vaddr == (env->watchpoint[i].vaddr & TARGET_PAGE_MASK)) {
+ if (address & ~TARGET_PAGE_MASK) {
+ env->watchpoint[i].is_ram = 0;
+ address = vaddr | io_mem_watch;
+ } else {
+ env->watchpoint[i].is_ram = 1;
+ /* TODO: Figure out how to make read watchpoints coexist
+ with code. */
+ pd = (pd & TARGET_PAGE_MASK) | io_mem_watch | IO_MEM_ROMD;
+ }
+ }
+ }
index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
addend -= vaddr;
notdirty_mem_writel,
};
+#if defined(CONFIG_SOFTMMU)
+/* Watchpoint access routines. Watchpoints are inserted using TLB tricks,
+ so these check for a hit then pass through to the normal out-of-line
+ phys routines. */
+static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr)
+{
+ return ldub_phys(addr);
+}
+
+static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr)
+{
+ return lduw_phys(addr);
+}
+
+static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr)
+{
+ return ldl_phys(addr);
+}
+
+/* Generate a debug exception if a watchpoint has been hit.
+ Returns the real physical address of the access. addr will be a host
+ address in the is_ram case. */
+static target_ulong check_watchpoint(target_phys_addr_t addr)
+{
+ CPUState *env = cpu_single_env;
+ target_ulong watch;
+ target_ulong retaddr;
+ int i;
+
+ retaddr = addr;
+ for (i = 0; i < env->nb_watchpoints; i++) {
+ watch = env->watchpoint[i].vaddr;
+ if (((env->mem_write_vaddr ^ watch) & TARGET_PAGE_MASK) == 0) {
+ if (env->watchpoint[i].is_ram)
+ retaddr = addr - (unsigned long)phys_ram_base;
+ if (((addr ^ watch) & ~TARGET_PAGE_MASK) == 0) {
+ cpu_single_env->watchpoint_hit = i + 1;
+ cpu_interrupt(cpu_single_env, CPU_INTERRUPT_DEBUG);
+ break;
+ }
+ }
+ }
+ return retaddr;
+}
+
+static void watch_mem_writeb(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ addr = check_watchpoint(addr);
+ stb_phys(addr, val);
+}
+
+static void watch_mem_writew(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ addr = check_watchpoint(addr);
+ stw_phys(addr, val);
+}
+
+static void watch_mem_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t val)
+{
+ addr = check_watchpoint(addr);
+ stl_phys(addr, val);
+}
+
+static CPUReadMemoryFunc *watch_mem_read[3] = {
+ watch_mem_readb,
+ watch_mem_readw,
+ watch_mem_readl,
+};
+
+static CPUWriteMemoryFunc *watch_mem_write[3] = {
+ watch_mem_writeb,
+ watch_mem_writew,
+ watch_mem_writel,
+};
+#endif
+
static void io_mem_init(void)
{
cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL);
io_mem_nb = 5;
+#if defined(CONFIG_SOFTMMU)
+ io_mem_watch = cpu_register_io_memory(-1, watch_mem_read,
+ watch_mem_write, NULL);
+#endif
/* alloc dirty bits array */
phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS);
memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS);
if (cpu_breakpoint_insert(env, addr) < 0)
goto breakpoint_error;
put_packet(s, "OK");
+#ifndef CONFIG_USER_ONLY
+ } else if (type == 2) {
+ if (cpu_watchpoint_insert(env, addr) < 0)
+ goto breakpoint_error;
+ put_packet(s, "OK");
+#endif
} else {
breakpoint_error:
put_packet(s, "E22");
if (type == 0 || type == 1) {
cpu_breakpoint_remove(env, addr);
put_packet(s, "OK");
+#ifndef CONFIG_USER_ONLY
+ } else if (type == 2) {
+ cpu_watchpoint_remove(env, addr);
+ put_packet(s, "OK");
+#endif
} else {
goto breakpoint_error;
}
cpu_single_step(s->env, 0);
if (reason == EXCP_DEBUG) {
+ if (s->env->watchpoint_hit) {
+ snprintf(buf, sizeof(buf), "T%02xwatch:%x;", SIGTRAP,
+ s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
+ put_packet(s, buf);
+ s->env->watchpoint_hit = 0;
+ return;
+ }
tb_flush(s->env);
ret = SIGTRAP;
} else if (reason == EXCP_INTERRUPT) {
struct TranslationBlock *tb;
int singlestep_enabled;
int thumb;
+ int is_mem;
#if !defined(CONFIG_USER_ONLY)
int user;
#endif
#define gen_ldst(name, s) gen_op_##name##_raw()
#else
#define gen_ldst(name, s) do { \
+ s->is_mem = 1; \
if (IS_USER(s)) \
gen_op_##name##_user(); \
else \
gen_add_data_offset(s, insn);
if (insn & (1 << 20)) {
/* load */
+ s->is_mem = 1;
#if defined(CONFIG_USER_ONLY)
if (insn & (1 << 22))
gen_op_ldub_raw();
dc->singlestep_enabled = env->singlestep_enabled;
dc->condjmp = 0;
dc->thumb = env->thumb;
+ dc->is_mem = 0;
#if !defined(CONFIG_USER_ONLY)
dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR;
#endif
gen_set_label(dc->condlabel);
dc->condjmp = 0;
}
+ /* Terminate the TB on memory ops if watchpoints are present. */
+ /* FIXME: This should be replacd by the deterministic execution
+ * IRQ raising bits. */
+ if (dc->is_mem && env->nb_watchpoints)
+ break;
+
/* Translation stops when a conditional branch is enoutered.
* Otherwise the subsequent code could get translated several times.
* Also stop translation when a page boundary is reached. This