Commit 6658ffb81ee56a510d7d77025872a508a9adce3a
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
Showing
6 changed files
with
186 additions
and
0 deletions
cpu-all.h
| ... | ... | @@ -775,10 +775,13 @@ extern int code_copy_enabled; |
| 775 | 775 | #define CPU_INTERRUPT_FIQ 0x10 /* Fast interrupt pending. */ |
| 776 | 776 | #define CPU_INTERRUPT_HALT 0x20 /* CPU halt wanted */ |
| 777 | 777 | #define CPU_INTERRUPT_SMI 0x40 /* (x86 only) SMI interrupt pending */ |
| 778 | +#define CPU_INTERRUPT_DEBUG 0x80 /* Debug event occured. */ | |
| 778 | 779 | |
| 779 | 780 | void cpu_interrupt(CPUState *s, int mask); |
| 780 | 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 | 785 | int cpu_breakpoint_insert(CPUState *env, target_ulong pc); |
| 783 | 786 | int cpu_breakpoint_remove(CPUState *env, target_ulong pc); |
| 784 | 787 | void cpu_single_step(CPUState *env, int enabled); | ... | ... |
cpu-defs.h
| ... | ... | @@ -76,6 +76,7 @@ typedef unsigned long ram_addr_t; |
| 76 | 76 | #define EXCP_DEBUG 0x10002 /* cpu stopped after a breakpoint or singlestep */ |
| 77 | 77 | #define EXCP_HALTED 0x10003 /* cpu is halted (waiting for external event) */ |
| 78 | 78 | #define MAX_BREAKPOINTS 32 |
| 79 | +#define MAX_WATCHPOINTS 32 | |
| 79 | 80 | |
| 80 | 81 | #define TB_JMP_CACHE_BITS 12 |
| 81 | 82 | #define TB_JMP_CACHE_SIZE (1 << TB_JMP_CACHE_BITS) |
| ... | ... | @@ -125,6 +126,13 @@ typedef struct CPUTLBEntry { |
| 125 | 126 | int nb_breakpoints; \ |
| 126 | 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 | 136 | void *next_cpu; /* next CPU sharing TB cache */ \ |
| 129 | 137 | int cpu_index; /* CPU index (informative) */ \ |
| 130 | 138 | /* user data */ \ | ... | ... |
cpu-exec.c
| ... | ... | @@ -409,6 +409,11 @@ int cpu_exec(CPUState *env1) |
| 409 | 409 | #endif |
| 410 | 410 | interrupt_request = env->interrupt_request; |
| 411 | 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 | 417 | #if defined(TARGET_I386) |
| 413 | 418 | if ((interrupt_request & CPU_INTERRUPT_SMI) && |
| 414 | 419 | !(env->hflags & HF_SMM_MASK)) { | ... | ... |
exec.c
| ... | ... | @@ -128,6 +128,9 @@ CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4]; |
| 128 | 128 | CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4]; |
| 129 | 129 | void *io_mem_opaque[IO_MEM_NB_ENTRIES]; |
| 130 | 130 | static int io_mem_nb; |
| 131 | +#if defined(CONFIG_SOFTMMU) | |
| 132 | +static int io_mem_watch; | |
| 133 | +#endif | |
| 131 | 134 | |
| 132 | 135 | /* log support */ |
| 133 | 136 | char *logfilename = "/tmp/qemu.log"; |
| ... | ... | @@ -274,6 +277,7 @@ void cpu_exec_init(CPUState *env) |
| 274 | 277 | cpu_index++; |
| 275 | 278 | } |
| 276 | 279 | env->cpu_index = cpu_index; |
| 280 | + env->nb_watchpoints = 0; | |
| 277 | 281 | *penv = env; |
| 278 | 282 | } |
| 279 | 283 | |
| ... | ... | @@ -1029,6 +1033,44 @@ static void breakpoint_invalidate(CPUState *env, target_ulong pc) |
| 1029 | 1033 | } |
| 1030 | 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 | 1074 | /* add a breakpoint. EXCP_DEBUG is returned by the CPU loop if a |
| 1033 | 1075 | breakpoint is reached */ |
| 1034 | 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 | 1526 | target_phys_addr_t addend; |
| 1485 | 1527 | int ret; |
| 1486 | 1528 | CPUTLBEntry *te; |
| 1529 | + int i; | |
| 1487 | 1530 | |
| 1488 | 1531 | p = phys_page_find(paddr >> TARGET_PAGE_BITS); |
| 1489 | 1532 | if (!p) { |
| ... | ... | @@ -1510,6 +1553,22 @@ int tlb_set_page_exec(CPUState *env, target_ulong vaddr, |
| 1510 | 1553 | address = vaddr; |
| 1511 | 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 | 1573 | index = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); |
| 1515 | 1574 | addend -= vaddr; |
| ... | ... | @@ -1960,6 +2019,85 @@ static CPUWriteMemoryFunc *notdirty_mem_write[3] = { |
| 1960 | 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 | 2101 | static void io_mem_init(void) |
| 1964 | 2102 | { |
| 1965 | 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 | 2105 | cpu_register_io_memory(IO_MEM_NOTDIRTY >> IO_MEM_SHIFT, error_mem_read, notdirty_mem_write, NULL); |
| 1968 | 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 | 2112 | /* alloc dirty bits array */ |
| 1971 | 2113 | phys_ram_dirty = qemu_vmalloc(phys_ram_size >> TARGET_PAGE_BITS); |
| 1972 | 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 | 856 | if (cpu_breakpoint_insert(env, addr) < 0) |
| 857 | 857 | goto breakpoint_error; |
| 858 | 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 | 865 | } else { |
| 860 | 866 | breakpoint_error: |
| 861 | 867 | put_packet(s, "E22"); |
| ... | ... | @@ -872,6 +878,11 @@ static int gdb_handle_packet(GDBState *s, CPUState *env, const char *line_buf) |
| 872 | 878 | if (type == 0 || type == 1) { |
| 873 | 879 | cpu_breakpoint_remove(env, addr); |
| 874 | 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 | 886 | } else { |
| 876 | 887 | goto breakpoint_error; |
| 877 | 888 | } |
| ... | ... | @@ -914,6 +925,13 @@ static void gdb_vm_stopped(void *opaque, int reason) |
| 914 | 925 | cpu_single_step(s->env, 0); |
| 915 | 926 | |
| 916 | 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 | 935 | tb_flush(s->env); |
| 918 | 936 | ret = SIGTRAP; |
| 919 | 937 | } else if (reason == EXCP_INTERRUPT) { | ... | ... |
target-arm/translate.c
| ... | ... | @@ -45,6 +45,7 @@ typedef struct DisasContext { |
| 45 | 45 | struct TranslationBlock *tb; |
| 46 | 46 | int singlestep_enabled; |
| 47 | 47 | int thumb; |
| 48 | + int is_mem; | |
| 48 | 49 | #if !defined(CONFIG_USER_ONLY) |
| 49 | 50 | int user; |
| 50 | 51 | #endif |
| ... | ... | @@ -290,6 +291,7 @@ static inline void gen_bx(DisasContext *s) |
| 290 | 291 | #define gen_ldst(name, s) gen_op_##name##_raw() |
| 291 | 292 | #else |
| 292 | 293 | #define gen_ldst(name, s) do { \ |
| 294 | + s->is_mem = 1; \ | |
| 293 | 295 | if (IS_USER(s)) \ |
| 294 | 296 | gen_op_##name##_user(); \ |
| 295 | 297 | else \ |
| ... | ... | @@ -1612,6 +1614,7 @@ static void disas_arm_insn(CPUState * env, DisasContext *s) |
| 1612 | 1614 | gen_add_data_offset(s, insn); |
| 1613 | 1615 | if (insn & (1 << 20)) { |
| 1614 | 1616 | /* load */ |
| 1617 | + s->is_mem = 1; | |
| 1615 | 1618 | #if defined(CONFIG_USER_ONLY) |
| 1616 | 1619 | if (insn & (1 << 22)) |
| 1617 | 1620 | gen_op_ldub_raw(); |
| ... | ... | @@ -2409,6 +2412,7 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2409 | 2412 | dc->singlestep_enabled = env->singlestep_enabled; |
| 2410 | 2413 | dc->condjmp = 0; |
| 2411 | 2414 | dc->thumb = env->thumb; |
| 2415 | + dc->is_mem = 0; | |
| 2412 | 2416 | #if !defined(CONFIG_USER_ONLY) |
| 2413 | 2417 | dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR; |
| 2414 | 2418 | #endif |
| ... | ... | @@ -2447,6 +2451,12 @@ static inline int gen_intermediate_code_internal(CPUState *env, |
| 2447 | 2451 | gen_set_label(dc->condlabel); |
| 2448 | 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 | 2460 | /* Translation stops when a conditional branch is enoutered. |
| 2451 | 2461 | * Otherwise the subsequent code could get translated several times. |
| 2452 | 2462 | * Also stop translation when a page boundary is reached. This | ... | ... |