Skip to content

Commit

Permalink
Refactor and enhance break/watchpoint API (Jan Kiszka)
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Anthony Liguori <[email protected]>



git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5738 c046a42c-6fe2-441c-8c8c-71466251a162
  • Loading branch information
aliguori committed Nov 18, 2008
1 parent d6fc1b3 commit a1d1bb3
Show file tree
Hide file tree
Showing 13 changed files with 267 additions and 183 deletions.
23 changes: 17 additions & 6 deletions cpu-all.h
Original file line number Diff line number Diff line change
Expand Up @@ -761,12 +761,23 @@ extern int use_icount;
void cpu_interrupt(CPUState *s, int mask);
void cpu_reset_interrupt(CPUState *env, int mask);

int cpu_watchpoint_insert(CPUState *env, target_ulong addr, int type);
int cpu_watchpoint_remove(CPUState *env, target_ulong addr);
void cpu_watchpoint_remove_all(CPUState *env);
int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
void cpu_breakpoint_remove_all(CPUState *env);
/* Breakpoint/watchpoint flags */
#define BP_MEM_READ 0x01
#define BP_MEM_WRITE 0x02
#define BP_MEM_ACCESS (BP_MEM_READ | BP_MEM_WRITE)
#define BP_GDB 0x10

int cpu_breakpoint_insert(CPUState *env, target_ulong pc, int flags,
CPUBreakpoint **breakpoint);
int cpu_breakpoint_remove(CPUState *env, target_ulong pc, int flags);
void cpu_breakpoint_remove_by_ref(CPUState *env, CPUBreakpoint *breakpoint);
void cpu_breakpoint_remove_all(CPUState *env, int mask);
int cpu_watchpoint_insert(CPUState *env, target_ulong addr, target_ulong len,
int flags, CPUWatchpoint **watchpoint);
int cpu_watchpoint_remove(CPUState *env, target_ulong addr,
target_ulong len, int flags);
void cpu_watchpoint_remove_by_ref(CPUState *env, CPUWatchpoint *watchpoint);
void cpu_watchpoint_remove_all(CPUState *env, int mask);

#define SSTEP_ENABLE 0x1 /* Enable simulated HW single stepping */
#define SSTEP_NOIRQ 0x2 /* Do not use IRQ while single stepping */
Expand Down
26 changes: 16 additions & 10 deletions cpu-defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,6 @@ typedef uint64_t target_phys_addr_t;
#define EXCP_HLT 0x10001 /* hlt instruction reached */
#define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
#define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
#define MAX_BREAKPOINTS 32
#define MAX_WATCHPOINTS 32

#define TB_JMP_CACHE_BITS 12
#define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
Expand Down Expand Up @@ -145,6 +143,19 @@ typedef struct icount_decr_u16 {
struct kvm_run;
struct KVMState;

typedef struct CPUBreakpoint {
target_ulong pc;
int flags; /* BP_* */
struct CPUBreakpoint *prev, *next;
} CPUBreakpoint;

typedef struct CPUWatchpoint {
target_ulong vaddr;
target_ulong len_mask;
int flags; /* BP_* */
struct CPUWatchpoint *prev, *next;
} CPUWatchpoint;

#define CPU_TEMP_BUF_NLONGS 128
#define CPU_COMMON \
struct TranslationBlock *current_tb; /* currently executing TB */ \
Expand Down Expand Up @@ -177,16 +188,11 @@ struct KVMState;
\
/* from this point: preserved by CPU reset */ \
/* ice debug support */ \
target_ulong breakpoints[MAX_BREAKPOINTS]; \
int nb_breakpoints; \
CPUBreakpoint *breakpoints; \
int singlestep_enabled; \
\
struct { \
target_ulong vaddr; \
int type; /* PAGE_READ/PAGE_WRITE */ \
} watchpoint[MAX_WATCHPOINTS]; \
int nb_watchpoints; \
int watchpoint_hit; \
CPUWatchpoint *watchpoints; \
CPUWatchpoint *watchpoint_hit; \
\
struct GDBRegisterState *gdb_regs; \
\
Expand Down
195 changes: 118 additions & 77 deletions exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}

Expand Down
Loading

0 comments on commit a1d1bb3

Please sign in to comment.