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,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)) { |
exec.c
@@ -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 |