diff options
author | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-03-16 23:58:11 +0000 |
---|---|---|
committer | pbrook <pbrook@c046a42c-6fe2-441c-8c8c-71466251a162> | 2007-03-16 23:58:11 +0000 |
commit | 6658ffb81ee56a510d7d77025872a508a9adce3a (patch) | |
tree | 6cd595ce75c8cf82c7d38e997fdfd09de4fafefb /exec.c | |
parent | b35d7448b1d27a77bc6f59acc697710d5bd3823c (diff) |
Watchpoint support (previous commit got eaten by Savannah server crash).
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2479 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'exec.c')
-rw-r--r-- | exec.c | 142 |
1 files changed, 142 insertions, 0 deletions
@@ -128,6 +128,9 @@ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; 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"; @@ -274,6 +277,7 @@ void cpu_exec_init(CPUState *env) cpu_index++; } env->cpu_index = cpu_index; + env->nb_watchpoints = 0; *penv = env; } @@ -1029,6 +1033,44 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) } #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) @@ -1484,6 +1526,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, target_phys_addr_t addend; int ret; CPUTLBEntry *te; + int i; p = phys_page_find(paddr >> TARGET_PAGE_BITS); if (!p) { @@ -1510,6 +1553,22 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, 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; @@ -1960,6 +2019,85 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { 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); @@ -1967,6 +2105,10 @@ static void io_mem_init(void) 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); |