xref: /netbsd-src/sys/ddb/db_watch.c (revision cda4f8f6ee55684e8d311b86c99ea59191e6b74f)
1 /*
2  * Mach Operating System
3  * Copyright (c) 1991,1990 Carnegie Mellon University
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify and distribute this software and its
7  * documentation is hereby granted, provided that both the copyright
8  * notice and this permission notice appear in all copies of the
9  * software, derivative works or modified versions, and any portions
10  * thereof, and that both notices appear in supporting documentation.
11  *
12  * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13  * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14  * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15  *
16  * Carnegie Mellon requests users of this software to return to
17  *
18  *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19  *  School of Computer Science
20  *  Carnegie Mellon University
21  *  Pittsburgh PA 15213-3890
22  *
23  * any improvements or extensions that they make and grant Carnegie the
24  * rights to redistribute these changes.
25  */
26 /*
27  * $Id: db_watch.c,v 1.2 1993/05/20 03:39:38 cgd Exp $
28  *
29  * HISTORY
30  * $Log: db_watch.c,v $
31  * Revision 1.2  1993/05/20 03:39:38  cgd
32  * add explicit rcs id
33  *
34  * Revision 1.1.1.1  1993/03/21  09:46:27  cgd
35  * initial import of 386bsd-0.1 sources
36  *
37  * Revision 1.1  1992/03/25  21:45:37  pace
38  * Initial revision
39  *
40  * Revision 2.5  91/02/05  17:07:27  mrt
41  * 	Changed to new Mach copyright
42  * 	[91/01/31  16:20:02  mrt]
43  *
44  * Revision 2.4  91/01/08  15:09:24  rpd
45  * 	Use db_map_equal, db_map_current, db_map_addr.
46  * 	[90/11/10            rpd]
47  *
48  * Revision 2.3  90/11/05  14:26:39  rpd
49  * 	Initialize db_watchpoints_inserted to TRUE.
50  * 	[90/11/04            rpd]
51  *
52  * Revision 2.2  90/10/25  14:44:16  rwd
53  * 	Made db_watchpoint_cmd parse a size argument.
54  * 	[90/10/17            rpd]
55  * 	Generalized the watchpoint support.
56  * 	[90/10/16            rwd]
57  * 	Created.
58  * 	[90/10/16            rpd]
59  *
60  */
61 /*
62  * 	Author: Richard P. Draves, Carnegie Mellon University
63  *	Date:	10/90
64  */
65 
66 #include "param.h"
67 #include "proc.h"
68 #include <machine/db_machdep.h>
69 
70 #include <vm/vm_map.h>
71 #include <ddb/db_lex.h>
72 #include <ddb/db_watch.h>
73 #include <ddb/db_access.h>
74 #include <ddb/db_sym.h>
75 #include <machine/db_machdep.h>
76 
77 /*
78  * Watchpoints.
79  */
80 
81 extern boolean_t db_map_equal();
82 extern boolean_t db_map_current();
83 extern vm_map_t db_map_addr();
84 
85 boolean_t	db_watchpoints_inserted = TRUE;
86 
87 #define	NWATCHPOINTS	100
88 struct db_watchpoint	db_watch_table[NWATCHPOINTS];
89 db_watchpoint_t		db_next_free_watchpoint = &db_watch_table[0];
90 db_watchpoint_t		db_free_watchpoints = 0;
91 db_watchpoint_t		db_watchpoint_list = 0;
92 
93 db_watchpoint_t
94 db_watchpoint_alloc()
95 {
96 	register db_watchpoint_t	watch;
97 
98 	if ((watch = db_free_watchpoints) != 0) {
99 	    db_free_watchpoints = watch->link;
100 	    return (watch);
101 	}
102 	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
103 	    db_printf("All watchpoints used.\n");
104 	    return (0);
105 	}
106 	watch = db_next_free_watchpoint;
107 	db_next_free_watchpoint++;
108 
109 	return (watch);
110 }
111 
112 void
113 db_watchpoint_free(watch)
114 	register db_watchpoint_t	watch;
115 {
116 	watch->link = db_free_watchpoints;
117 	db_free_watchpoints = watch;
118 }
119 
120 void
121 db_set_watchpoint(map, addr, size)
122 	vm_map_t	map;
123 	db_addr_t	addr;
124 	vm_size_t	size;
125 {
126 	register db_watchpoint_t	watch;
127 
128 	if (map == NULL) {
129 	    db_printf("No map.\n");
130 	    return;
131 	}
132 
133 	/*
134 	 *	Should we do anything fancy with overlapping regions?
135 	 */
136 
137 	for (watch = db_watchpoint_list;
138 	     watch != 0;
139 	     watch = watch->link)
140 	    if (db_map_equal(watch->map, map) &&
141 		(watch->loaddr == addr) &&
142 		(watch->hiaddr == addr+size)) {
143 		db_printf("Already set.\n");
144 		return;
145 	    }
146 
147 	watch = db_watchpoint_alloc();
148 	if (watch == 0) {
149 	    db_printf("Too many watchpoints.\n");
150 	    return;
151 	}
152 
153 	watch->map = map;
154 	watch->loaddr = addr;
155 	watch->hiaddr = addr+size;
156 
157 	watch->link = db_watchpoint_list;
158 	db_watchpoint_list = watch;
159 
160 	db_watchpoints_inserted = FALSE;
161 }
162 
163 void
164 db_delete_watchpoint(map, addr)
165 	vm_map_t	map;
166 	db_addr_t	addr;
167 {
168 	register db_watchpoint_t	watch;
169 	register db_watchpoint_t	*prev;
170 
171 	for (prev = &db_watchpoint_list;
172 	     (watch = *prev) != 0;
173 	     prev = &watch->link)
174 	    if (db_map_equal(watch->map, map) &&
175 		(watch->loaddr <= addr) &&
176 		(addr < watch->hiaddr)) {
177 		*prev = watch->link;
178 		db_watchpoint_free(watch);
179 		return;
180 	    }
181 
182 	db_printf("Not set.\n");
183 }
184 
185 void
186 db_list_watchpoints()
187 {
188 	register db_watchpoint_t	watch;
189 
190 	if (db_watchpoint_list == 0) {
191 	    db_printf("No watchpoints set\n");
192 	    return;
193 	}
194 
195 	db_printf(" Map        Address  Size\n");
196 	for (watch = db_watchpoint_list;
197 	     watch != 0;
198 	     watch = watch->link)
199 	    db_printf("%s%8x  %8x  %x\n",
200 		      db_map_current(watch->map) ? "*" : " ",
201 		      watch->map, watch->loaddr,
202 		      watch->hiaddr - watch->loaddr);
203 }
204 
205 /* Delete watchpoint */
206 /*ARGSUSED*/
207 void
208 db_deletewatch_cmd(addr, have_addr, count, modif)
209 	db_expr_t	addr;
210 	int		have_addr;
211 	db_expr_t	count;
212 	char *		modif;
213 {
214 	db_delete_watchpoint(db_map_addr(addr), addr);
215 }
216 
217 /* Set watchpoint */
218 /*ARGSUSED*/
219 void
220 db_watchpoint_cmd(addr, have_addr, count, modif)
221 	db_expr_t	addr;
222 	int		have_addr;
223 	db_expr_t	count;
224 	char *		modif;
225 {
226 	vm_size_t	size;
227 	db_expr_t	value;
228 
229 	if (db_expression(&value))
230 	    size = (vm_size_t) value;
231 	else
232 	    size = 4;
233 	db_skip_to_eol();
234 
235 	db_set_watchpoint(db_map_addr(addr), addr, size);
236 }
237 
238 /* list watchpoints */
239 void
240 db_listwatch_cmd()
241 {
242 	db_list_watchpoints();
243 }
244 
245 void
246 db_set_watchpoints()
247 {
248 	register db_watchpoint_t	watch;
249 
250 	if (!db_watchpoints_inserted) {
251 	    for (watch = db_watchpoint_list;
252 	         watch != 0;
253 	         watch = watch->link)
254 		pmap_protect(watch->map->pmap,
255 			     trunc_page(watch->loaddr),
256 			     round_page(watch->hiaddr),
257 			     VM_PROT_READ);
258 
259 	    db_watchpoints_inserted = TRUE;
260 	}
261 }
262 
263 void
264 db_clear_watchpoints()
265 {
266 	db_watchpoints_inserted = FALSE;
267 }
268 
269 boolean_t
270 db_find_watchpoint(map, addr, regs)
271 	vm_map_t	map;
272 	db_addr_t	addr;
273 	db_regs_t	*regs;
274 {
275 	register db_watchpoint_t watch;
276 	db_watchpoint_t found = 0;
277 
278 	for (watch = db_watchpoint_list;
279 	     watch != 0;
280 	     watch = watch->link)
281 	    if (db_map_equal(watch->map, map)) {
282 		if ((watch->loaddr <= addr) &&
283 		    (addr < watch->hiaddr))
284 		    return (TRUE);
285 		else if ((trunc_page(watch->loaddr) <= addr) &&
286 			 (addr < round_page(watch->hiaddr)))
287 		    found = watch;
288 	    }
289 
290 	/*
291 	 *	We didn't hit exactly on a watchpoint, but we are
292 	 *	in a protected region.  We want to single-step
293 	 *	and then re-protect.
294 	 */
295 
296 	if (found) {
297 	    db_watchpoints_inserted = FALSE;
298 	    db_single_step(regs);
299 	}
300 
301 	return (FALSE);
302 }
303