diff options
author | aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-11-18 20:07:32 +0000 |
---|---|---|
committer | aliguori <aliguori@c046a42c-6fe2-441c-8c8c-71466251a162> | 2008-11-18 20:07:32 +0000 |
commit | a1d1bb3101db1fea4ff47b74de15208971f8d64e (patch) | |
tree | d8062f3891754bd62a26bfd58613b5c74fb029c3 /exec.c | |
parent | d6fc1b397bca686d2ac92f4e8f470c98472a3c4d (diff) |
Refactor and enhance break/watchpoint API (Jan Kiszka)
This patch prepares the QEMU cpu_watchpoint/breakpoint API to allow the
succeeding enhancements this series comes with.
First of all, it overcomes MAX_BREAKPOINTS/MAX_WATCHPOINTS by switching
to dynamically allocated data structures that are kept in linked lists.
This also allows to return a stable reference to the related objects,
required for later introduced x86 debug register support.
Breakpoints and watchpoints are stored with their full information set
and an additional flag field that makes them easily extensible for use
beyond pure guest debugging.
Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5738 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'exec.c')
-rw-r--r-- | exec.c | 195 |
1 files changed, 118 insertions, 77 deletions
@@ -537,7 +537,6 @@ void cpu_exec_init(CPUState *env) cpu_index++; } env->cpu_index = cpu_index; - env->nb_watchpoints = 0; *penv = env; #if defined(CPU_SAVE_VERSION) && !defined(CONFIG_USER_ONLY) register_savevm("cpu_common", cpu_index, CPU_COMMON_SAVE_VERSION, @@ -1299,107 +1298,150 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) #endif /* Add a watchpoint. */ -int cpu_watchpoint_insert(CPUState *env, target_ulong addr, int type) +int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len, + int flags, CPUWatchpoint **watchpoint) { - int i; + CPUWatchpoint *wp; - for (i = 0; i < env->nb_watchpoints; i++) { - if (addr == env->watchpoint[i].vaddr) - return 0; - } - if (env->nb_watchpoints >= MAX_WATCHPOINTS) - return -1; + wp = qemu_malloc(sizeof(*wp)); + if (!wp) + return -ENOBUFS; + + wp->vaddr = addr; + wp->len_mask = 0; + wp->flags = flags; + + wp->next = env->watchpoints; + wp->prev = NULL; + if (wp->next) + wp->next->prev = wp; + env->watchpoints = wp; - i = env->nb_watchpoints++; - env->watchpoint[i].vaddr = addr; - env->watchpoint[i].type = type; 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; + + if (watchpoint) + *watchpoint = wp; + return 0; } -/* Remove a watchpoint. */ -int cpu_watchpoint_remove(CPUState *env, target_ulong addr) +/* Remove a specific watchpoint. */ +int cpu_watchpoint_remove(CPUState *env, target_ulong addr, target_ulong len, + int flags) { - int i; + CPUWatchpoint *wp; - 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); + for (wp = env->watchpoints; wp != NULL; wp = wp->next) { + if (addr == wp->vaddr && flags == wp->flags) { + cpu_watchpoint_remove_by_ref(env, wp); return 0; } } - return -1; + return -ENOENT; } -/* Remove all watchpoints. */ -void cpu_watchpoint_remove_all(CPUState *env) { - int i; +/* Remove a specific watchpoint by reference. */ +void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint) +{ + if (watchpoint->next) + watchpoint->next->prev = watchpoint->prev; + if (watchpoint->prev) + watchpoint->prev->next = watchpoint->next; + else + env->watchpoints = watchpoint->next; - for (i = 0; i < env->nb_watchpoints; i++) { - tlb_flush_page(env, env->watchpoint[i].vaddr); - } - env->nb_watchpoints = 0; + tlb_flush_page(env, watchpoint->vaddr); + + qemu_free(watchpoint); +} + +/* Remove all matching watchpoints. */ +void cpu_watchpoint_remove_all(CPUState *env, int mask) +{ + CPUWatchpoint *wp; + + for (wp = env->watchpoints; wp != NULL; wp = wp->next) + if (wp->flags & mask) + cpu_watchpoint_remove_by_ref(env, wp); } -/* 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) +/* Add a breakpoint. */ +int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags, + CPUBreakpoint **breakpoint) { #if defined(TARGET_HAS_ICE) - int i; + CPUBreakpoint *bp; - for(i = 0; i < env->nb_breakpoints; i++) { - if (env->breakpoints[i] == pc) - return 0; - } + bp = qemu_malloc(sizeof(*bp)); + if (!bp) + return -ENOBUFS; - if (env->nb_breakpoints >= MAX_BREAKPOINTS) - return -1; - env->breakpoints[env->nb_breakpoints++] = pc; + bp->pc = pc; + bp->flags = flags; + + bp->next = env->breakpoints; + bp->prev = NULL; + if (bp->next) + bp->next->prev = bp; + env->breakpoints = bp; breakpoint_invalidate(env, pc); + + if (breakpoint) + *breakpoint = bp; return 0; #else - return -1; + return -ENOSYS; #endif } -/* remove all breakpoints */ -void cpu_breakpoint_remove_all(CPUState *env) { +/* Remove a specific breakpoint. */ +int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags) +{ #if defined(TARGET_HAS_ICE) - int i; - for(i = 0; i < env->nb_breakpoints; i++) { - breakpoint_invalidate(env, env->breakpoints[i]); + CPUBreakpoint *bp; + + for (bp = env->breakpoints; bp != NULL; bp = bp->next) { + if (bp->pc == pc && bp->flags == flags) { + cpu_breakpoint_remove_by_ref(env, bp); + return 0; + } } - env->nb_breakpoints = 0; + return -ENOENT; +#else + return -ENOSYS; #endif } -/* remove a breakpoint */ -int cpu_breakpoint_remove(CPUState *env, target_ulong pc) +/* Remove a specific breakpoint by reference. */ +void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint) { #if defined(TARGET_HAS_ICE) - int i; - for(i = 0; i < env->nb_breakpoints; i++) { - if (env->breakpoints[i] == pc) - goto found; - } - return -1; - found: - env->nb_breakpoints--; - if (i < env->nb_breakpoints) - env->breakpoints[i] = env->breakpoints[env->nb_breakpoints]; + if (breakpoint->next) + breakpoint->next->prev = breakpoint->prev; + if (breakpoint->prev) + breakpoint->prev->next = breakpoint->next; + else + env->breakpoints = breakpoint->next; - breakpoint_invalidate(env, pc); - return 0; -#else - return -1; + breakpoint_invalidate(env, breakpoint->pc); + + qemu_free(breakpoint); +#endif +} + +/* Remove all matching breakpoints. */ +void cpu_breakpoint_remove_all(CPUState *env, int mask) +{ +#if defined(TARGET_HAS_ICE) + CPUBreakpoint *bp; + + for (bp = env->breakpoints; bp != NULL; bp = bp->next) + if (bp->flags & mask) + cpu_breakpoint_remove_by_ref(env, bp); #endif } @@ -1881,7 +1923,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, target_phys_addr_t addend; int ret; CPUTLBEntry *te; - int i; + CPUWatchpoint *wp; target_phys_addr_t iotlb; p = phys_page_find(paddr >> TARGET_PAGE_BITS); @@ -1922,8 +1964,8 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, code_address = address; /* 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)) { + for (wp = env->watchpoints; wp != NULL; wp = wp->next) { + if (vaddr == (wp->vaddr & TARGET_PAGE_MASK)) { iotlb = io_mem_watch + paddr; /* TODO: The memory case can be optimized by not trapping reads of pages with a write breakpoint. */ @@ -2456,13 +2498,12 @@ static void check_watchpoint(int offset, int flags) { CPUState *env = cpu_single_env; target_ulong vaddr; - int i; + CPUWatchpoint *wp; vaddr = (env->mem_io_vaddr & TARGET_PAGE_MASK) + offset; - for (i = 0; i < env->nb_watchpoints; i++) { - if (vaddr == env->watchpoint[i].vaddr - && (env->watchpoint[i].type & flags)) { - env->watchpoint_hit = i + 1; + for (wp = env->watchpoints; wp != NULL; wp = wp->next) { + if (vaddr == wp->vaddr && (wp->flags & flags)) { + env->watchpoint_hit = wp; cpu_interrupt(env, CPU_INTERRUPT_DEBUG); break; } @@ -2474,40 +2515,40 @@ static void check_watchpoint(int offset, int flags) phys routines. */ static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_READ); return ldub_phys(addr); } static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_READ); return lduw_phys(addr); } static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_READ); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_READ); return ldl_phys(addr); } static void watch_mem_writeb(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_WRITE); stb_phys(addr, val); } static void watch_mem_writew(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_WRITE); stw_phys(addr, val); } static void watch_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val) { - check_watchpoint(addr & ~TARGET_PAGE_MASK, PAGE_WRITE); + check_watchpoint(addr & ~TARGET_PAGE_MASK, BP_MEM_WRITE); stl_phys(addr, val); } |