Commit 6658ffb81ee56a510d7d77025872a508a9adce3a

Authored by pbrook
1 parent b35d7448

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
cpu-all.h
@@ -775,10 +775,13 @@ extern int code_copy_enabled; @@ -775,10 +775,13 @@ extern int code_copy_enabled;
775 #define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ 775 #define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */
776 #define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ 776 #define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */
777 #define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */ 777 #define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */
  778 +#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */
778 779
779 void cpu_interrupt(CPUState *s, int mask); 780 void cpu_interrupt(CPUState *s, int mask);
780 void cpu_reset_interrupt(CPUState *env, int mask); 781 void cpu_reset_interrupt(CPUState *env, int mask);
781 782
  783 +int cpu_watchpoint_insert(CPUState *env, target_ulong addr);
  784 +int cpu_watchpoint_remove(CPUState *env, target_ulong addr);
782 int cpu_breakpoint_insert(CPUState *env, target_ulong pc); 785 int cpu_breakpoint_insert(CPUState *env, target_ulong pc);
783 int cpu_breakpoint_remove(CPUState *env, target_ulong pc); 786 int cpu_breakpoint_remove(CPUState *env, target_ulong pc);
784 void cpu_single_step(CPUState *env, int enabled); 787 void cpu_single_step(CPUState *env, int enabled);
cpu-defs.h
@@ -76,6 +76,7 @@ typedef unsigned long ram_addr_t; @@ -76,6 +76,7 @@ typedef unsigned long ram_addr_t;
76 #define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ 76 #define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */
77 #define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ 77 #define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */
78 #define MAX_BREAKPOINTS 32 78 #define MAX_BREAKPOINTS 32
  79 +#define MAX_WATCHPOINTS 32
79 80
80 #define TB_JMP_CACHE_BITS 12 81 #define TB_JMP_CACHE_BITS 12
81 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) 82 #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS)
@@ -125,6 +126,13 @@ typedef struct CPUTLBEntry { @@ -125,6 +126,13 @@ typedef struct CPUTLBEntry {
125 int nb_breakpoints; \ 126 int nb_breakpoints; \
126 int singlestep_enabled; \ 127 int singlestep_enabled; \
127 \ 128 \
  129 + struct { \
  130 + target_ulong vaddr; \
  131 + int is_ram; \
  132 + } watchpoint[MAX_WATCHPOINTS]; \
  133 + int nb_watchpoints; \
  134 + int watchpoint_hit; \
  135 + \
128 void *next_cpu; /* next CPU sharing TB cache */ \ 136 void *next_cpu; /* next CPU sharing TB cache */ \
129 int cpu_index; /* CPU index (informative) */ \ 137 int cpu_index; /* CPU index (informative) */ \
130 /* user data */ \ 138 /* user data */ \
cpu-exec.c
@@ -409,6 +409,11 @@ int cpu_exec(CPUState *env1) @@ -409,6 +409,11 @@ int cpu_exec(CPUState *env1)
409 #endif 409 #endif
410 interrupt_request = env->interrupt_request; 410 interrupt_request = env->interrupt_request;
411 if (__builtin_expect(interrupt_request, 0)) { 411 if (__builtin_expect(interrupt_request, 0)) {
  412 + if (interrupt_request & CPU_INTERRUPT_DEBUG) {
  413 + env->interrupt_request &= ~CPU_INTERRUPT_DEBUG;
  414 + env->exception_index = EXCP_DEBUG;
  415 + cpu_loop_exit();
  416 + }
412 #if defined(TARGET_I386) 417 #if defined(TARGET_I386)
413 if ((interrupt_request & CPU_INTERRUPT_SMI) && 418 if ((interrupt_request & CPU_INTERRUPT_SMI) &&
414 !(env->hflags & HF_SMM_MASK)) { 419 !(env->hflags & HF_SMM_MASK)) {
@@ -128,6 +128,9 @@ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; @@ -128,6 +128,9 @@ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
128 CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; 128 CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
129 void *io_mem_opaque[IO_MEM_NB_ENTRIES]; 129 void *io_mem_opaque[IO_MEM_NB_ENTRIES];
130 static int io_mem_nb; 130 static int io_mem_nb;
  131 +#if defined(CONFIG_SOFTMMU)
  132 +static int io_mem_watch;
  133 +#endif
131 134
132 /* log support */ 135 /* log support */
133 char *logfilename = "/tmp/qemu.log"; 136 char *logfilename = "/tmp/qemu.log";
@@ -274,6 +277,7 @@ void cpu_exec_init(CPUState *env) @@ -274,6 +277,7 @@ void cpu_exec_init(CPUState *env)
274 cpu_index++; 277 cpu_index++;
275 } 278 }
276 env->cpu_index = cpu_index; 279 env->cpu_index = cpu_index;
  280 + env->nb_watchpoints = 0;
277 *penv = env; 281 *penv = env;
278 } 282 }
279 283
@@ -1029,6 +1033,44 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) @@ -1029,6 +1033,44 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc)
1029 } 1033 }
1030 #endif 1034 #endif
1031 1035
  1036 +/* Add a watchpoint. */
  1037 +int cpu_watchpoint_insert(CPUState *env, target_ulong addr)
  1038 +{
  1039 + int i;
  1040 +
  1041 + for (i = 0; i < env->nb_watchpoints; i++) {
  1042 + if (addr == env->watchpoint[i].vaddr)
  1043 + return 0;
  1044 + }
  1045 + if (env->nb_watchpoints >= MAX_WATCHPOINTS)
  1046 + return -1;
  1047 +
  1048 + i = env->nb_watchpoints++;
  1049 + env->watchpoint[i].vaddr = addr;
  1050 + tlb_flush_page(env, addr);
  1051 + /* FIXME: This flush is needed because of the hack to make memory ops
  1052 + terminate the TB. It can be removed once the proper IO trap and
  1053 + re-execute bits are in. */
  1054 + tb_flush(env);
  1055 + return i;
  1056 +}
  1057 +
  1058 +/* Remove a watchpoint. */
  1059 +int cpu_watchpoint_remove(CPUState *env, target_ulong addr)
  1060 +{
  1061 + int i;
  1062 +
  1063 + for (i = 0; i < env->nb_watchpoints; i++) {
  1064 + if (addr == env->watchpoint[i].vaddr) {
  1065 + env->nb_watchpoints--;
  1066 + env->watchpoint[i] = env->watchpoint[env->nb_watchpoints];
  1067 + tlb_flush_page(env, addr);
  1068 + return 0;
  1069 + }
  1070 + }
  1071 + return -1;
  1072 +}
  1073 +
1032 /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a 1074 /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a
1033 breakpoint is reached */ 1075 breakpoint is reached */
1034 int cpu_breakpoint_insert(CPUState *env, target_ulong pc) 1076 int cpu_breakpoint_insert(CPUState *env, target_ulong pc)
@@ -1484,6 +1526,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, @@ -1484,6 +1526,7 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
1484 target_phys_addr_t addend; 1526 target_phys_addr_t addend;
1485 int ret; 1527 int ret;
1486 CPUTLBEntry *te; 1528 CPUTLBEntry *te;
  1529 + int i;
1487 1530
1488 p = phys_page_find(paddr >> TARGET_PAGE_BITS); 1531 p = phys_page_find(paddr >> TARGET_PAGE_BITS);
1489 if (!p) { 1532 if (!p) {
@@ -1510,6 +1553,22 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, @@ -1510,6 +1553,22 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr,
1510 address = vaddr; 1553 address = vaddr;
1511 addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK); 1554 addend = (unsigned long)phys_ram_base + (pd & TARGET_PAGE_MASK);
1512 } 1555 }
  1556 +
  1557 + /* Make accesses to pages with watchpoints go via the
  1558 + watchpoint trap routines. */
  1559 + for (i = 0; i < env->nb_watchpoints; i++) {
  1560 + if (vaddr == (env->watchpoint[i].vaddr & TARGET_PAGE_MASK)) {
  1561 + if (address & ~TARGET_PAGE_MASK) {
  1562 + env->watchpoint[i].is_ram = 0;
  1563 + address = vaddr | io_mem_watch;
  1564 + } else {
  1565 + env->watchpoint[i].is_ram = 1;
  1566 + /* TODO: Figure out how to make read watchpoints coexist
  1567 + with code. */
  1568 + pd = (pd & TARGET_PAGE_MASK) | io_mem_watch | IO_MEM_ROMD;
  1569 + }
  1570 + }
  1571 + }
1513 1572
1514 index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); 1573 index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
1515 addend -= vaddr; 1574 addend -= vaddr;
@@ -1960,6 +2019,85 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { @@ -1960,6 +2019,85 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = {
1960 notdirty_mem_writel, 2019 notdirty_mem_writel,
1961 }; 2020 };
1962 2021
  2022 +#if defined(CONFIG_SOFTMMU)
  2023 +/* Watchpoint access routines. Watchpoints are inserted using TLB tricks,
  2024 + so these check for a hit then pass through to the normal out-of-line
  2025 + phys routines. */
  2026 +static uint32_t watch_mem_readb(void *opaque, target_phys_addr_t addr)
  2027 +{
  2028 + return ldub_phys(addr);
  2029 +}
  2030 +
  2031 +static uint32_t watch_mem_readw(void *opaque, target_phys_addr_t addr)
  2032 +{
  2033 + return lduw_phys(addr);
  2034 +}
  2035 +
  2036 +static uint32_t watch_mem_readl(void *opaque, target_phys_addr_t addr)
  2037 +{
  2038 + return ldl_phys(addr);
  2039 +}
  2040 +
  2041 +/* Generate a debug exception if a watchpoint has been hit.
  2042 + Returns the real physical address of the access. addr will be a host
  2043 + address in the is_ram case. */
  2044 +static target_ulong check_watchpoint(target_phys_addr_t addr)
  2045 +{
  2046 + CPUState *env = cpu_single_env;
  2047 + target_ulong watch;
  2048 + target_ulong retaddr;
  2049 + int i;
  2050 +
  2051 + retaddr = addr;
  2052 + for (i = 0; i < env->nb_watchpoints; i++) {
  2053 + watch = env->watchpoint[i].vaddr;
  2054 + if (((env->mem_write_vaddr ^ watch) & TARGET_PAGE_MASK) == 0) {
  2055 + if (env->watchpoint[i].is_ram)
  2056 + retaddr = addr - (unsigned long)phys_ram_base;
  2057 + if (((addr ^ watch) & ~TARGET_PAGE_MASK) == 0) {
  2058 + cpu_single_env->watchpoint_hit = i + 1;
  2059 + cpu_interrupt(cpu_single_env, CPU_INTERRUPT_DEBUG);
  2060 + break;
  2061 + }
  2062 + }
  2063 + }
  2064 + return retaddr;
  2065 +}
  2066 +
  2067 +static void watch_mem_writeb(void *opaque, target_phys_addr_t addr,
  2068 + uint32_t val)
  2069 +{
  2070 + addr = check_watchpoint(addr);
  2071 + stb_phys(addr, val);
  2072 +}
  2073 +
  2074 +static void watch_mem_writew(void *opaque, target_phys_addr_t addr,
  2075 + uint32_t val)
  2076 +{
  2077 + addr = check_watchpoint(addr);
  2078 + stw_phys(addr, val);
  2079 +}
  2080 +
  2081 +static void watch_mem_writel(void *opaque, target_phys_addr_t addr,
  2082 + uint32_t val)
  2083 +{
  2084 + addr = check_watchpoint(addr);
  2085 + stl_phys(addr, val);
  2086 +}
  2087 +
  2088 +static CPUReadMemoryFunc *watch_mem_read[3] = {
  2089 + watch_mem_readb,
  2090 + watch_mem_readw,
  2091 + watch_mem_readl,
  2092 +};
  2093 +
  2094 +static CPUWriteMemoryFunc *watch_mem_write[3] = {
  2095 + watch_mem_writeb,
  2096 + watch_mem_writew,
  2097 + watch_mem_writel,
  2098 +};
  2099 +#endif
  2100 +
1963 static void io_mem_init(void) 2101 static void io_mem_init(void)
1964 { 2102 {
1965 cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL); 2103 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) @@ -1967,6 +2105,10 @@ static void io_mem_init(void)
1967 cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL); 2105 cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL);
1968 io_mem_nb = 5; 2106 io_mem_nb = 5;
1969 2107
  2108 +#if defined(CONFIG_SOFTMMU)
  2109 + io_mem_watch = cpu_register_io_memory(-1, watch_mem_read,
  2110 + watch_mem_write, NULL);
  2111 +#endif
1970 /* alloc dirty bits array */ 2112 /* alloc dirty bits array */
1971 phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS); 2113 phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS);
1972 memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS); 2114 memset(phys_ram_dirty, 0xff, phys_ram_size >> TARGET_PAGE_BITS);
gdbstub.c
@@ -856,6 +856,12 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) @@ -856,6 +856,12 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
856 if (cpu_breakpoint_insert(env, addr) < 0) 856 if (cpu_breakpoint_insert(env, addr) < 0)
857 goto breakpoint_error; 857 goto breakpoint_error;
858 put_packet(s, "OK"); 858 put_packet(s, "OK");
  859 +#ifndef CONFIG_USER_ONLY
  860 + } else if (type == 2) {
  861 + if (cpu_watchpoint_insert(env, addr) < 0)
  862 + goto breakpoint_error;
  863 + put_packet(s, "OK");
  864 +#endif
859 } else { 865 } else {
860 breakpoint_error: 866 breakpoint_error:
861 put_packet(s, "E22"); 867 put_packet(s, "E22");
@@ -872,6 +878,11 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) @@ -872,6 +878,11 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf)
872 if (type == 0 || type == 1) { 878 if (type == 0 || type == 1) {
873 cpu_breakpoint_remove(env, addr); 879 cpu_breakpoint_remove(env, addr);
874 put_packet(s, "OK"); 880 put_packet(s, "OK");
  881 +#ifndef CONFIG_USER_ONLY
  882 + } else if (type == 2) {
  883 + cpu_watchpoint_remove(env, addr);
  884 + put_packet(s, "OK");
  885 +#endif
875 } else { 886 } else {
876 goto breakpoint_error; 887 goto breakpoint_error;
877 } 888 }
@@ -914,6 +925,13 @@ static void gdb_vm_stopped(void *opaque, int reason) @@ -914,6 +925,13 @@ static void gdb_vm_stopped(void *opaque, int reason)
914 cpu_single_step(s->env, 0); 925 cpu_single_step(s->env, 0);
915 926
916 if (reason == EXCP_DEBUG) { 927 if (reason == EXCP_DEBUG) {
  928 + if (s->env->watchpoint_hit) {
  929 + snprintf(buf, sizeof(buf), "T%02xwatch:%x;", SIGTRAP,
  930 + s->env->watchpoint[s->env->watchpoint_hit - 1].vaddr);
  931 + put_packet(s, buf);
  932 + s->env->watchpoint_hit = 0;
  933 + return;
  934 + }
917 tb_flush(s->env); 935 tb_flush(s->env);
918 ret = SIGTRAP; 936 ret = SIGTRAP;
919 } else if (reason == EXCP_INTERRUPT) { 937 } else if (reason == EXCP_INTERRUPT) {
target-arm/translate.c
@@ -45,6 +45,7 @@ typedef struct DisasContext { @@ -45,6 +45,7 @@ typedef struct DisasContext {
45 struct TranslationBlock *tb; 45 struct TranslationBlock *tb;
46 int singlestep_enabled; 46 int singlestep_enabled;
47 int thumb; 47 int thumb;
  48 + int is_mem;
48 #if !defined(CONFIG_USER_ONLY) 49 #if !defined(CONFIG_USER_ONLY)
49 int user; 50 int user;
50 #endif 51 #endif
@@ -290,6 +291,7 @@ static inline void gen_bx(DisasContext *s) @@ -290,6 +291,7 @@ static inline void gen_bx(DisasContext *s)
290 #define gen_ldst(name, s) gen_op_##name##_raw() 291 #define gen_ldst(name, s) gen_op_##name##_raw()
291 #else 292 #else
292 #define gen_ldst(name, s) do { \ 293 #define gen_ldst(name, s) do { \
  294 + s->is_mem = 1; \
293 if (IS_USER(s)) \ 295 if (IS_USER(s)) \
294 gen_op_##name##_user(); \ 296 gen_op_##name##_user(); \
295 else \ 297 else \
@@ -1612,6 +1614,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) @@ -1612,6 +1614,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s)
1612 gen_add_data_offset(s, insn); 1614 gen_add_data_offset(s, insn);
1613 if (insn & (1 << 20)) { 1615 if (insn & (1 << 20)) {
1614 /* load */ 1616 /* load */
  1617 + s->is_mem = 1;
1615 #if defined(CONFIG_USER_ONLY) 1618 #if defined(CONFIG_USER_ONLY)
1616 if (insn & (1 << 22)) 1619 if (insn & (1 << 22))
1617 gen_op_ldub_raw(); 1620 gen_op_ldub_raw();
@@ -2409,6 +2412,7 @@ static inline int gen_intermediate_code_internal(CPUState *env, @@ -2409,6 +2412,7 @@ static inline int gen_intermediate_code_internal(CPUState *env,
2409 dc->singlestep_enabled = env->singlestep_enabled; 2412 dc->singlestep_enabled = env->singlestep_enabled;
2410 dc->condjmp = 0; 2413 dc->condjmp = 0;
2411 dc->thumb = env->thumb; 2414 dc->thumb = env->thumb;
  2415 + dc->is_mem = 0;
2412 #if !defined(CONFIG_USER_ONLY) 2416 #if !defined(CONFIG_USER_ONLY)
2413 dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; 2417 dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR;
2414 #endif 2418 #endif
@@ -2447,6 +2451,12 @@ static inline int gen_intermediate_code_internal(CPUState *env, @@ -2447,6 +2451,12 @@ static inline int gen_intermediate_code_internal(CPUState *env,
2447 gen_set_label(dc->condlabel); 2451 gen_set_label(dc->condlabel);
2448 dc->condjmp = 0; 2452 dc->condjmp = 0;
2449 } 2453 }
  2454 + /* Terminate the TB on memory ops if watchpoints are present. */
  2455 + /* FIXME: This should be replacd by the deterministic execution
  2456 + * IRQ raising bits. */
  2457 + if (dc->is_mem && env->nb_watchpoints)
  2458 + break;
  2459 +
2450 /* Translation stops when a conditional branch is enoutered. 2460 /* Translation stops when a conditional branch is enoutered.
2451 * Otherwise the subsequent code could get translated several times. 2461 * Otherwise the subsequent code could get translated several times.
2452 * Also stop translation when a page boundary is reached. This 2462 * Also stop translation when a page boundary is reached. This