xref: /qemu/accel/tcg/watchpoint.c (revision fc524567087c2537b5103cdfc1d41e4f442892b6)
11c3d42c4SPhilippe Mathieu-Daudé /*
21c3d42c4SPhilippe Mathieu-Daudé  * CPU watchpoints
31c3d42c4SPhilippe Mathieu-Daudé  *
41c3d42c4SPhilippe Mathieu-Daudé  *  Copyright (c) 2003 Fabrice Bellard
51c3d42c4SPhilippe Mathieu-Daudé  *
61c3d42c4SPhilippe Mathieu-Daudé  * This library is free software; you can redistribute it and/or
71c3d42c4SPhilippe Mathieu-Daudé  * modify it under the terms of the GNU Lesser General Public
81c3d42c4SPhilippe Mathieu-Daudé  * License as published by the Free Software Foundation; either
91c3d42c4SPhilippe Mathieu-Daudé  * version 2.1 of the License, or (at your option) any later version.
101c3d42c4SPhilippe Mathieu-Daudé  *
111c3d42c4SPhilippe Mathieu-Daudé  * This library is distributed in the hope that it will be useful,
121c3d42c4SPhilippe Mathieu-Daudé  * but WITHOUT ANY WARRANTY; without even the implied warranty of
131c3d42c4SPhilippe Mathieu-Daudé  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
141c3d42c4SPhilippe Mathieu-Daudé  * Lesser General Public License for more details.
151c3d42c4SPhilippe Mathieu-Daudé  *
161c3d42c4SPhilippe Mathieu-Daudé  * You should have received a copy of the GNU Lesser General Public
171c3d42c4SPhilippe Mathieu-Daudé  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
181c3d42c4SPhilippe Mathieu-Daudé  */
191c3d42c4SPhilippe Mathieu-Daudé 
201c3d42c4SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
211c3d42c4SPhilippe Mathieu-Daudé #include "qemu/main-loop.h"
2254699338SRichard Henderson #include "exec/breakpoint.h"
2354699338SRichard Henderson #include "exec/cpu-interrupt.h"
24487a31e0SPhilippe Mathieu-Daudé #include "exec/page-protection.h"
258865049bSPhilippe Mathieu-Daudé #include "exec/translation-block.h"
2632cad1ffSPhilippe Mathieu-Daudé #include "system/tcg.h"
2732cad1ffSPhilippe Mathieu-Daudé #include "system/replay.h"
2815017436SPhilippe Mathieu-Daudé #include "accel/tcg/cpu-ops.h"
291c3d42c4SPhilippe Mathieu-Daudé #include "hw/core/cpu.h"
301760c5ccSPhilippe Mathieu-Daudé #include "internal-common.h"
311c3d42c4SPhilippe Mathieu-Daudé 
321c3d42c4SPhilippe Mathieu-Daudé /*
331c3d42c4SPhilippe Mathieu-Daudé  * Return true if this watchpoint address matches the specified
341c3d42c4SPhilippe Mathieu-Daudé  * access (ie the address range covered by the watchpoint overlaps
351c3d42c4SPhilippe Mathieu-Daudé  * partially or completely with the address range covered by the
361c3d42c4SPhilippe Mathieu-Daudé  * access).
371c3d42c4SPhilippe Mathieu-Daudé  */
watchpoint_address_matches(CPUWatchpoint * wp,vaddr addr,vaddr len)381c3d42c4SPhilippe Mathieu-Daudé static inline bool watchpoint_address_matches(CPUWatchpoint *wp,
391c3d42c4SPhilippe Mathieu-Daudé                                               vaddr addr, vaddr len)
401c3d42c4SPhilippe Mathieu-Daudé {
411c3d42c4SPhilippe Mathieu-Daudé     /*
421c3d42c4SPhilippe Mathieu-Daudé      * We know the lengths are non-zero, but a little caution is
431c3d42c4SPhilippe Mathieu-Daudé      * required to avoid errors in the case where the range ends
441c3d42c4SPhilippe Mathieu-Daudé      * exactly at the top of the address space and so addr + len
451c3d42c4SPhilippe Mathieu-Daudé      * wraps round to zero.
461c3d42c4SPhilippe Mathieu-Daudé      */
471c3d42c4SPhilippe Mathieu-Daudé     vaddr wpend = wp->vaddr + wp->len - 1;
481c3d42c4SPhilippe Mathieu-Daudé     vaddr addrend = addr + len - 1;
491c3d42c4SPhilippe Mathieu-Daudé 
501c3d42c4SPhilippe Mathieu-Daudé     return !(addr > wpend || wp->vaddr > addrend);
511c3d42c4SPhilippe Mathieu-Daudé }
521c3d42c4SPhilippe Mathieu-Daudé 
531c3d42c4SPhilippe Mathieu-Daudé /* Return flags for watchpoints that match addr + prot.  */
cpu_watchpoint_address_matches(CPUState * cpu,vaddr addr,vaddr len)541c3d42c4SPhilippe Mathieu-Daudé int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len)
551c3d42c4SPhilippe Mathieu-Daudé {
561c3d42c4SPhilippe Mathieu-Daudé     CPUWatchpoint *wp;
571c3d42c4SPhilippe Mathieu-Daudé     int ret = 0;
581c3d42c4SPhilippe Mathieu-Daudé 
591c3d42c4SPhilippe Mathieu-Daudé     QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
601c3d42c4SPhilippe Mathieu-Daudé         if (watchpoint_address_matches(wp, addr, len)) {
611c3d42c4SPhilippe Mathieu-Daudé             ret |= wp->flags;
621c3d42c4SPhilippe Mathieu-Daudé         }
631c3d42c4SPhilippe Mathieu-Daudé     }
641c3d42c4SPhilippe Mathieu-Daudé     return ret;
651c3d42c4SPhilippe Mathieu-Daudé }
661c3d42c4SPhilippe Mathieu-Daudé 
671c3d42c4SPhilippe Mathieu-Daudé /* Generate a debug exception if a watchpoint has been hit.  */
cpu_check_watchpoint(CPUState * cpu,vaddr addr,vaddr len,MemTxAttrs attrs,int flags,uintptr_t ra)681c3d42c4SPhilippe Mathieu-Daudé void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len,
691c3d42c4SPhilippe Mathieu-Daudé                           MemTxAttrs attrs, int flags, uintptr_t ra)
701c3d42c4SPhilippe Mathieu-Daudé {
711c3d42c4SPhilippe Mathieu-Daudé     CPUWatchpoint *wp;
721c3d42c4SPhilippe Mathieu-Daudé 
731c3d42c4SPhilippe Mathieu-Daudé     assert(tcg_enabled());
741c3d42c4SPhilippe Mathieu-Daudé     if (cpu->watchpoint_hit) {
751c3d42c4SPhilippe Mathieu-Daudé         /*
761c3d42c4SPhilippe Mathieu-Daudé          * We re-entered the check after replacing the TB.
771c3d42c4SPhilippe Mathieu-Daudé          * Now raise the debug interrupt so that it will
781c3d42c4SPhilippe Mathieu-Daudé          * trigger after the current instruction.
791c3d42c4SPhilippe Mathieu-Daudé          */
801c3d42c4SPhilippe Mathieu-Daudé         bql_lock();
811c3d42c4SPhilippe Mathieu-Daudé         cpu_interrupt(cpu, CPU_INTERRUPT_DEBUG);
821c3d42c4SPhilippe Mathieu-Daudé         bql_unlock();
831c3d42c4SPhilippe Mathieu-Daudé         return;
841c3d42c4SPhilippe Mathieu-Daudé     }
851c3d42c4SPhilippe Mathieu-Daudé 
86*e27fa95fSPhilippe Mathieu-Daudé     if (cpu->cc->tcg_ops->adjust_watchpoint_address) {
871c3d42c4SPhilippe Mathieu-Daudé         /* this is currently used only by ARM BE32 */
88*e27fa95fSPhilippe Mathieu-Daudé         addr = cpu->cc->tcg_ops->adjust_watchpoint_address(cpu, addr, len);
891c3d42c4SPhilippe Mathieu-Daudé     }
901c3d42c4SPhilippe Mathieu-Daudé 
911c3d42c4SPhilippe Mathieu-Daudé     assert((flags & ~BP_MEM_ACCESS) == 0);
921c3d42c4SPhilippe Mathieu-Daudé     QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
931c3d42c4SPhilippe Mathieu-Daudé         int hit_flags = wp->flags & flags;
941c3d42c4SPhilippe Mathieu-Daudé 
951c3d42c4SPhilippe Mathieu-Daudé         if (hit_flags && watchpoint_address_matches(wp, addr, len)) {
961c3d42c4SPhilippe Mathieu-Daudé             if (replay_running_debug()) {
971c3d42c4SPhilippe Mathieu-Daudé                 /*
981c3d42c4SPhilippe Mathieu-Daudé                  * replay_breakpoint reads icount.
991c3d42c4SPhilippe Mathieu-Daudé                  * Force recompile to succeed, because icount may
1001c3d42c4SPhilippe Mathieu-Daudé                  * be read only at the end of the block.
1011c3d42c4SPhilippe Mathieu-Daudé                  */
1021c3d42c4SPhilippe Mathieu-Daudé                 if (!cpu->neg.can_do_io) {
1031c3d42c4SPhilippe Mathieu-Daudé                     /* Force execution of one insn next time.  */
1041c3d42c4SPhilippe Mathieu-Daudé                     cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
1051c3d42c4SPhilippe Mathieu-Daudé                     cpu_loop_exit_restore(cpu, ra);
1061c3d42c4SPhilippe Mathieu-Daudé                 }
1071c3d42c4SPhilippe Mathieu-Daudé                 /*
1081c3d42c4SPhilippe Mathieu-Daudé                  * Don't process the watchpoints when we are
1091c3d42c4SPhilippe Mathieu-Daudé                  * in a reverse debugging operation.
1101c3d42c4SPhilippe Mathieu-Daudé                  */
1111c3d42c4SPhilippe Mathieu-Daudé                 replay_breakpoint();
1121c3d42c4SPhilippe Mathieu-Daudé                 return;
1131c3d42c4SPhilippe Mathieu-Daudé             }
1141c3d42c4SPhilippe Mathieu-Daudé 
1151c3d42c4SPhilippe Mathieu-Daudé             wp->flags |= hit_flags << BP_HIT_SHIFT;
1161c3d42c4SPhilippe Mathieu-Daudé             wp->hitaddr = MAX(addr, wp->vaddr);
1171c3d42c4SPhilippe Mathieu-Daudé             wp->hitattrs = attrs;
1181c3d42c4SPhilippe Mathieu-Daudé 
1191c3d42c4SPhilippe Mathieu-Daudé             if (wp->flags & BP_CPU
120*e27fa95fSPhilippe Mathieu-Daudé                 && cpu->cc->tcg_ops->debug_check_watchpoint
121*e27fa95fSPhilippe Mathieu-Daudé                 && !cpu->cc->tcg_ops->debug_check_watchpoint(cpu, wp)) {
1221c3d42c4SPhilippe Mathieu-Daudé                 wp->flags &= ~BP_WATCHPOINT_HIT;
1231c3d42c4SPhilippe Mathieu-Daudé                 continue;
1241c3d42c4SPhilippe Mathieu-Daudé             }
1251c3d42c4SPhilippe Mathieu-Daudé             cpu->watchpoint_hit = wp;
1261c3d42c4SPhilippe Mathieu-Daudé 
1271c3d42c4SPhilippe Mathieu-Daudé             /* This call also restores vCPU state */
1281c3d42c4SPhilippe Mathieu-Daudé             tb_check_watchpoint(cpu, ra);
1291c3d42c4SPhilippe Mathieu-Daudé             if (wp->flags & BP_STOP_BEFORE_ACCESS) {
1301c3d42c4SPhilippe Mathieu-Daudé                 cpu->exception_index = EXCP_DEBUG;
1311c3d42c4SPhilippe Mathieu-Daudé                 cpu_loop_exit(cpu);
1321c3d42c4SPhilippe Mathieu-Daudé             } else {
1331c3d42c4SPhilippe Mathieu-Daudé                 /* Force execution of one insn next time.  */
1341c3d42c4SPhilippe Mathieu-Daudé                 cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu);
1351c3d42c4SPhilippe Mathieu-Daudé                 cpu_loop_exit_noexc(cpu);
1361c3d42c4SPhilippe Mathieu-Daudé             }
1371c3d42c4SPhilippe Mathieu-Daudé         } else {
1381c3d42c4SPhilippe Mathieu-Daudé             wp->flags &= ~BP_WATCHPOINT_HIT;
1391c3d42c4SPhilippe Mathieu-Daudé         }
1401c3d42c4SPhilippe Mathieu-Daudé     }
1411c3d42c4SPhilippe Mathieu-Daudé }
142