xref: /openbmc/qemu/system/watchpoint.c (revision 2e1cacfb)
1 /*
2  * CPU watchpoints
3  *
4  *  Copyright (c) 2003 Fabrice Bellard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "qemu/osdep.h"
21 #include "qemu/error-report.h"
22 #include "exec/exec-all.h"
23 #include "hw/core/cpu.h"
24 
25 /* Add a watchpoint.  */
26 int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len,
27                           int flags, CPUWatchpoint **watchpoint)
28 {
29     CPUWatchpoint *wp;
30     vaddr in_page;
31 
32     /* forbid ranges which are empty or run off the end of the address space */
33     if (len == 0 || (addr + len - 1) < addr) {
34         error_report("tried to set invalid watchpoint at %"
35                      VADDR_PRIx ", len=%" VADDR_PRIu, addr, len);
36         return -EINVAL;
37     }
38     wp = g_malloc(sizeof(*wp));
39 
40     wp->vaddr = addr;
41     wp->len = len;
42     wp->flags = flags;
43 
44     /* keep all GDB-injected watchpoints in front */
45     if (flags & BP_GDB) {
46         QTAILQ_INSERT_HEAD(&cpu->watchpoints, wp, entry);
47     } else {
48         QTAILQ_INSERT_TAIL(&cpu->watchpoints, wp, entry);
49     }
50 
51     in_page = -(addr | TARGET_PAGE_MASK);
52     if (len <= in_page) {
53         tlb_flush_page(cpu, addr);
54     } else {
55         tlb_flush(cpu);
56     }
57 
58     if (watchpoint) {
59         *watchpoint = wp;
60     }
61     return 0;
62 }
63 
64 /* Remove a specific watchpoint.  */
65 int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, vaddr len,
66                           int flags)
67 {
68     CPUWatchpoint *wp;
69 
70     QTAILQ_FOREACH(wp, &cpu->watchpoints, entry) {
71         if (addr == wp->vaddr && len == wp->len
72                 && flags == (wp->flags & ~BP_WATCHPOINT_HIT)) {
73             cpu_watchpoint_remove_by_ref(cpu, wp);
74             return 0;
75         }
76     }
77     return -ENOENT;
78 }
79 
80 /* Remove a specific watchpoint by reference.  */
81 void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint)
82 {
83     QTAILQ_REMOVE(&cpu->watchpoints, watchpoint, entry);
84 
85     tlb_flush_page(cpu, watchpoint->vaddr);
86 
87     g_free(watchpoint);
88 }
89 
90 /* Remove all matching watchpoints.  */
91 void cpu_watchpoint_remove_all(CPUState *cpu, int mask)
92 {
93     CPUWatchpoint *wp, *next;
94 
95     QTAILQ_FOREACH_SAFE(wp, &cpu->watchpoints, entry, next) {
96         if (wp->flags & mask) {
97             cpu_watchpoint_remove_by_ref(cpu, wp);
98         }
99     }
100 }
101