db_watch.c revision 79573
1166551Smarcel/*
2166551Smarcel * Mach Operating System
3166551Smarcel * Copyright (c) 1991,1990 Carnegie Mellon University
4166551Smarcel * All Rights Reserved.
5166551Smarcel *
6166551Smarcel * Permission to use, copy, modify and distribute this software and its
7166551Smarcel * documentation is hereby granted, provided that both the copyright
8166551Smarcel * notice and this permission notice appear in all copies of the
9166551Smarcel * software, derivative works or modified versions, and any portions
10166551Smarcel * thereof, and that both notices appear in supporting documentation.
11166551Smarcel *
12166551Smarcel * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
13166551Smarcel * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14166551Smarcel * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15166551Smarcel *
16166551Smarcel * Carnegie Mellon requests users of this software to return to
17166551Smarcel *
18166551Smarcel *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
19166551Smarcel *  School of Computer Science
20166551Smarcel *  Carnegie Mellon University
21166551Smarcel *  Pittsburgh PA 15213-3890
22166551Smarcel *
23166551Smarcel * any improvements or extensions that they make and grant Carnegie the
24166551Smarcel * rights to redistribute these changes.
25166551Smarcel *
26166551Smarcel * $FreeBSD: head/sys/ddb/db_watch.c 79573 2001-07-11 03:15:25Z bsd $
27166551Smarcel */
28166551Smarcel
29166551Smarcel/*
30166551Smarcel * 	Author: Richard P. Draves, Carnegie Mellon University
31166551Smarcel *	Date:	10/90
32166551Smarcel */
33166551Smarcel
34166551Smarcel#include <sys/param.h>
35166551Smarcel#include <sys/kernel.h>
36166551Smarcel#include <sys/lock.h>
37166551Smarcel#include <sys/proc.h>
38166551Smarcel
39166551Smarcel#include <vm/vm.h>
40179747Smarcel#include <vm/pmap.h>
41179747Smarcel#include <vm/vm_map.h>
42179747Smarcel
43166551Smarcel#include <ddb/ddb.h>
44166551Smarcel#include <ddb/db_watch.h>
45166551Smarcel
46166551Smarcel/*
47166551Smarcel * Watchpoints.
48166551Smarcel */
49166551Smarcel
50166551Smarcelstatic boolean_t	db_watchpoints_inserted = TRUE;
51179747Smarcel
52179747Smarcel#define	NWATCHPOINTS	100
53166551Smarcelstatic struct db_watchpoint	db_watch_table[NWATCHPOINTS];
54166551Smarcelstatic db_watchpoint_t	db_next_free_watchpoint = &db_watch_table[0];
55166551Smarcelstatic db_watchpoint_t	db_free_watchpoints = 0;
56166551Smarcelstatic db_watchpoint_t	db_watchpoint_list = 0;
57166551Smarcel
58166551Smarcelstatic db_watchpoint_t	db_watchpoint_alloc __P((void));
59166551Smarcelstatic void		db_watchpoint_free __P((db_watchpoint_t watch));
60166551Smarcelstatic void		db_delete_watchpoint __P((vm_map_t map,
61166551Smarcel					db_addr_t addr));
62172855Smarcel#ifdef notused
63166551Smarcelstatic boolean_t	db_find_watchpoint __P((vm_map_t map, db_addr_t addr,
64166551Smarcel					db_regs_t *regs));
65166551Smarcel#endif
66166551Smarcelstatic void		db_list_watchpoints __P((void));
67static void		db_set_watchpoint __P((vm_map_t map, db_addr_t addr,
68				       vm_size_t size));
69
70int  db_md_set_watchpoint   __P((db_expr_t addr, db_expr_t size));
71int  db_md_clr_watchpoint   __P((db_expr_t addr, db_expr_t size));
72void db_md_list_watchpoints __P((void));
73
74
75db_watchpoint_t
76db_watchpoint_alloc()
77{
78	register db_watchpoint_t	watch;
79
80	if ((watch = db_free_watchpoints) != 0) {
81	    db_free_watchpoints = watch->link;
82	    return (watch);
83	}
84	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
85	    db_printf("All watchpoints used.\n");
86	    return (0);
87	}
88	watch = db_next_free_watchpoint;
89	db_next_free_watchpoint++;
90
91	return (watch);
92}
93
94void
95db_watchpoint_free(watch)
96	register db_watchpoint_t	watch;
97{
98	watch->link = db_free_watchpoints;
99	db_free_watchpoints = watch;
100}
101
102static void
103db_set_watchpoint(map, addr, size)
104	vm_map_t	map;
105	db_addr_t	addr;
106	vm_size_t	size;
107{
108	register db_watchpoint_t	watch;
109
110	if (map == NULL) {
111	    db_printf("No map.\n");
112	    return;
113	}
114
115	/*
116	 *	Should we do anything fancy with overlapping regions?
117	 */
118
119	for (watch = db_watchpoint_list;
120	     watch != 0;
121	     watch = watch->link)
122	    if (db_map_equal(watch->map, map) &&
123		(watch->loaddr == addr) &&
124		(watch->hiaddr == addr+size)) {
125		db_printf("Already set.\n");
126		return;
127	    }
128
129	watch = db_watchpoint_alloc();
130	if (watch == 0) {
131	    db_printf("Too many watchpoints.\n");
132	    return;
133	}
134
135	watch->map = map;
136	watch->loaddr = addr;
137	watch->hiaddr = addr+size;
138
139	watch->link = db_watchpoint_list;
140	db_watchpoint_list = watch;
141
142	db_watchpoints_inserted = FALSE;
143}
144
145static void
146db_delete_watchpoint(map, addr)
147	vm_map_t	map;
148	db_addr_t	addr;
149{
150	register db_watchpoint_t	watch;
151	register db_watchpoint_t	*prev;
152
153	for (prev = &db_watchpoint_list;
154	     (watch = *prev) != 0;
155	     prev = &watch->link)
156	    if (db_map_equal(watch->map, map) &&
157		(watch->loaddr <= addr) &&
158		(addr < watch->hiaddr)) {
159		*prev = watch->link;
160		db_watchpoint_free(watch);
161		return;
162	    }
163
164	db_printf("Not set.\n");
165}
166
167static void
168db_list_watchpoints()
169{
170	register db_watchpoint_t	watch;
171
172	if (db_watchpoint_list == 0) {
173	    db_printf("No watchpoints set\n");
174	    return;
175	}
176
177	db_printf(" Map        Address  Size\n");
178	for (watch = db_watchpoint_list;
179	     watch != 0;
180	     watch = watch->link)
181	    db_printf("%s%8p  %8lx  %lx\n",
182		      db_map_current(watch->map) ? "*" : " ",
183		      (void *)watch->map, (long)watch->loaddr,
184		      (long)watch->hiaddr - (long)watch->loaddr);
185}
186
187/* Delete watchpoint */
188/*ARGSUSED*/
189void
190db_deletewatch_cmd(addr, have_addr, count, modif)
191	db_expr_t	addr;
192	boolean_t	have_addr;
193	db_expr_t	count;
194	char *		modif;
195{
196	db_delete_watchpoint(db_map_addr(addr), addr);
197}
198
199/* Set watchpoint */
200/*ARGSUSED*/
201void
202db_watchpoint_cmd(addr, have_addr, count, modif)
203	db_expr_t	addr;
204	boolean_t	have_addr;
205	db_expr_t	count;
206	char *		modif;
207{
208	vm_size_t	size;
209	db_expr_t	value;
210
211	if (db_expression(&value))
212	    size = (vm_size_t) value;
213	else
214	    size = 4;
215	db_skip_to_eol();
216
217	db_set_watchpoint(db_map_addr(addr), addr, size);
218}
219
220/*
221 * At least one non-optional show-command must be implemented using
222 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
223 */
224DB_SHOW_COMMAND(watches, db_listwatch_cmd)
225{
226	db_list_watchpoints();
227	db_md_list_watchpoints();
228}
229
230void
231db_set_watchpoints()
232{
233	register db_watchpoint_t	watch;
234
235	if (!db_watchpoints_inserted) {
236	    for (watch = db_watchpoint_list;
237	         watch != 0;
238	         watch = watch->link)
239		pmap_protect(watch->map->pmap,
240			     trunc_page(watch->loaddr),
241			     round_page(watch->hiaddr),
242			     VM_PROT_READ);
243
244	    db_watchpoints_inserted = TRUE;
245	}
246}
247
248void
249db_clear_watchpoints()
250{
251	db_watchpoints_inserted = FALSE;
252}
253
254#ifdef notused
255static boolean_t
256db_find_watchpoint(map, addr, regs)
257	vm_map_t	map;
258	db_addr_t	addr;
259	db_regs_t	*regs;
260{
261	register db_watchpoint_t watch;
262	db_watchpoint_t found = 0;
263
264	for (watch = db_watchpoint_list;
265	     watch != 0;
266	     watch = watch->link)
267	    if (db_map_equal(watch->map, map)) {
268		if ((watch->loaddr <= addr) &&
269		    (addr < watch->hiaddr))
270		    return (TRUE);
271		else if ((trunc_page(watch->loaddr) <= addr) &&
272			 (addr < round_page(watch->hiaddr)))
273		    found = watch;
274	    }
275
276	/*
277	 *	We didn't hit exactly on a watchpoint, but we are
278	 *	in a protected region.  We want to single-step
279	 *	and then re-protect.
280	 */
281
282	if (found) {
283	    db_watchpoints_inserted = FALSE;
284	    db_single_step(regs);
285	}
286
287	return (FALSE);
288}
289#endif
290
291
292
293/* Delete hardware watchpoint */
294/*ARGSUSED*/
295void
296db_deletehwatch_cmd(addr, have_addr, count, modif)
297	db_expr_t	addr;
298	boolean_t	have_addr;
299	db_expr_t	count;
300	char *		modif;
301{
302	int rc;
303
304        if (count < 0)
305                count = 4;
306
307	rc = db_md_clr_watchpoint(addr, count);
308	if (rc < 0)
309		db_printf("hardware watchpoint could not be deleted\n");
310}
311
312/* Set hardware watchpoint */
313/*ARGSUSED*/
314void
315db_hwatchpoint_cmd(addr, have_addr, count, modif)
316	db_expr_t	addr;
317	boolean_t	have_addr;
318	db_expr_t	count;
319	char *		modif;
320{
321	int rc;
322
323        if (count < 0)
324                count = 4;
325
326	rc = db_md_set_watchpoint(addr, count);
327	if (rc < 0)
328		db_printf("hardware watchpoint could not be set\n");
329}
330