mirror of
https://gitlab.com/qemu-project/qemu
synced 2024-09-06 01:43:39 +00:00
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
This commit is contained in:
parent
b35d7448b1
commit
6658ffb81e
|
@ -775,10 +775,13 @@ extern int code_copy_enabled;
|
|||
#define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
|
||||
#define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
|
||||
#define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */
|
||||
#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */
|
||||
|
||||
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 cpu_watchpoint_remove(CPUState *env, target_ulong addr);
|
||||
int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
|
||||
int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
|
||||
void cpu_single_step(CPUState *env, int enabled);
|
||||
|
|
|
@ -76,6 +76,7 @@ typedef unsigned long ram_addr_t;
|
|||
#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)
|
||||
|
@ -125,6 +126,13 @@ typedef struct CPUTLBEntry {
|
|||
int nb_breakpoints; \
|
||||
int singlestep_enabled; \
|
||||
\
|
||||
struct { \
|
||||
target_ulong vaddr; \
|
||||
int is_ram; \
|
||||
} watchpoint[MAX_WATCHPOINTS]; \
|
||||
int nb_watchpoints; \
|
||||
int watchpoint_hit; \
|
||||
\
|
||||
void *next_cpu; /* next CPU sharing TB cache */ \
|
||||
int cpu_index; /* CPU index (informative) */ \
|
||||
/* user data */ \
|
||||
|
|
|
@ -409,6 +409,11 @@ int cpu_exec(CPUState *env1)
|
|||
#endif
|
||||
interrupt_request = env->interrupt_request;
|
||||
if (__builtin_expect(interrupt_request, 0)) {
|
||||
if (interrupt_request & CPU_INTERRUPT_DEBUG) {
|
||||
env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
|
||||
env->exception_index = EXCP_DEBUG;
|
||||
cpu_loop_exit();
|
||||
}
|
||||
#if defined(TARGET_I386)
|
||||
if ((interrupt_request & CPU_INTERRUPT_SMI) &&
|
||||
!(env->hflags & HF_SMM_MASK)) {
|
||||
|
|
142
exec.c
142
exec.c
|
@ -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);
|
||||
|
|
18
gdbstub.c
18
gdbstub.c
|
@ -856,6 +856,12 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
|||
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");
|
||||
|
@ -872,6 +878,11 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
|
|||
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;
|
||||
}
|
||||
|
@ -914,6 +925,13 @@ static void gdb_vm_stopped(void *opaque, int reason)
|
|||
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) {
|
||||
|
|
|
@ -45,6 +45,7 @@ typedef struct DisasContext {
|
|||
struct TranslationBlock *tb;
|
||||
int singlestep_enabled;
|
||||
int thumb;
|
||||
int is_mem;
|
||||
#if !defined(CONFIG_USER_ONLY)
|
||||
int user;
|
||||
#endif
|
||||
|
@ -290,6 +291,7 @@ static inline void gen_bx(DisasContext *s)
|
|||
#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 \
|
||||
|
@ -1612,6 +1614,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
|
|||
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();
|
||||
|
@ -2409,6 +2412,7 @@ static inline int gen_intermediate_code_internal(CPUState *env,
|
|||
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
|
||||
|
@ -2447,6 +2451,12 @@ static inline int gen_intermediate_code_internal(CPUState *env,
|
|||
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
|
||||
|
|
Loading…
Reference in a new issue