db_watch.c revision 149925
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 * 	Author: Richard P. Draves, Carnegie Mellon University
28 *	Date:	10/90
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: head/sys/ddb/db_watch.c 149925 2005-09-10 03:01:25Z marcel $");
33
34#include <sys/param.h>
35#include <sys/kernel.h>
36#include <sys/lock.h>
37#include <sys/proc.h>
38
39#include <vm/vm.h>
40#include <vm/pmap.h>
41#include <vm/vm_map.h>
42
43#include <ddb/ddb.h>
44#include <ddb/db_watch.h>
45
46/*
47 * Watchpoints.
48 */
49
50static boolean_t	db_watchpoints_inserted = TRUE;
51
52#define	NWATCHPOINTS	100
53static struct db_watchpoint	db_watch_table[NWATCHPOINTS];
54static db_watchpoint_t	db_next_free_watchpoint = &db_watch_table[0];
55static db_watchpoint_t	db_free_watchpoints = 0;
56static db_watchpoint_t	db_watchpoint_list = 0;
57
58static db_watchpoint_t	db_watchpoint_alloc(void);
59static void		db_watchpoint_free(db_watchpoint_t watch);
60static void		db_delete_watchpoint(vm_map_t map, db_addr_t addr);
61#ifdef notused
62static boolean_t	db_find_watchpoint(vm_map_t map, db_addr_t addr,
63					db_regs_t *regs);
64#endif
65static void		db_list_watchpoints(void);
66static void		db_set_watchpoint(vm_map_t map, db_addr_t addr,
67				       vm_size_t size);
68
69static db_watchpoint_t
70db_watchpoint_alloc()
71{
72	register db_watchpoint_t	watch;
73
74	if ((watch = db_free_watchpoints) != 0) {
75	    db_free_watchpoints = watch->link;
76	    return (watch);
77	}
78	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
79	    db_printf("All watchpoints used.\n");
80	    return (0);
81	}
82	watch = db_next_free_watchpoint;
83	db_next_free_watchpoint++;
84
85	return (watch);
86}
87
88static void
89db_watchpoint_free(watch)
90	register db_watchpoint_t	watch;
91{
92	watch->link = db_free_watchpoints;
93	db_free_watchpoints = watch;
94}
95
96static void
97db_set_watchpoint(map, addr, size)
98	vm_map_t	map;
99	db_addr_t	addr;
100	vm_size_t	size;
101{
102	register db_watchpoint_t	watch;
103
104	if (map == NULL) {
105	    db_printf("No map.\n");
106	    return;
107	}
108
109	/*
110	 *	Should we do anything fancy with overlapping regions?
111	 */
112
113	for (watch = db_watchpoint_list;
114	     watch != 0;
115	     watch = watch->link)
116	    if (db_map_equal(watch->map, map) &&
117		(watch->loaddr == addr) &&
118		(watch->hiaddr == addr+size)) {
119		db_printf("Already set.\n");
120		return;
121	    }
122
123	watch = db_watchpoint_alloc();
124	if (watch == 0) {
125	    db_printf("Too many watchpoints.\n");
126	    return;
127	}
128
129	watch->map = map;
130	watch->loaddr = addr;
131	watch->hiaddr = addr+size;
132
133	watch->link = db_watchpoint_list;
134	db_watchpoint_list = watch;
135
136	db_watchpoints_inserted = FALSE;
137}
138
139static void
140db_delete_watchpoint(map, addr)
141	vm_map_t	map;
142	db_addr_t	addr;
143{
144	register db_watchpoint_t	watch;
145	register db_watchpoint_t	*prev;
146
147	for (prev = &db_watchpoint_list;
148	     (watch = *prev) != 0;
149	     prev = &watch->link)
150	    if (db_map_equal(watch->map, map) &&
151		(watch->loaddr <= addr) &&
152		(addr < watch->hiaddr)) {
153		*prev = watch->link;
154		db_watchpoint_free(watch);
155		return;
156	    }
157
158	db_printf("Not set.\n");
159}
160
161static void
162db_list_watchpoints()
163{
164	register db_watchpoint_t	watch;
165
166	if (db_watchpoint_list == 0) {
167	    db_printf("No watchpoints set\n");
168	    return;
169	}
170
171	db_printf(" Map        Address  Size\n");
172	for (watch = db_watchpoint_list;
173	     watch != 0;
174	     watch = watch->link)
175	    db_printf("%s%8p  %8lx  %lx\n",
176		      db_map_current(watch->map) ? "*" : " ",
177		      (void *)watch->map, (long)watch->loaddr,
178		      (long)watch->hiaddr - (long)watch->loaddr);
179}
180
181/* Delete watchpoint */
182/*ARGSUSED*/
183void
184db_deletewatch_cmd(addr, have_addr, count, modif)
185	db_expr_t	addr;
186	boolean_t	have_addr;
187	db_expr_t	count;
188	char *		modif;
189{
190	db_delete_watchpoint(db_map_addr(addr), addr);
191}
192
193/* Set watchpoint */
194/*ARGSUSED*/
195void
196db_watchpoint_cmd(addr, have_addr, count, modif)
197	db_expr_t	addr;
198	boolean_t	have_addr;
199	db_expr_t	count;
200	char *		modif;
201{
202	vm_size_t	size;
203	db_expr_t	value;
204
205	if (db_expression(&value))
206	    size = (vm_size_t) value;
207	else
208	    size = 4;
209	db_skip_to_eol();
210
211	db_set_watchpoint(db_map_addr(addr), addr, size);
212}
213
214/*
215 * At least one non-optional show-command must be implemented using
216 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
217 */
218DB_SHOW_COMMAND(watches, db_listwatch_cmd)
219{
220	db_list_watchpoints();
221	db_md_list_watchpoints();
222}
223
224void
225db_set_watchpoints()
226{
227	register db_watchpoint_t	watch;
228
229	if (!db_watchpoints_inserted) {
230	    for (watch = db_watchpoint_list;
231	         watch != 0;
232	         watch = watch->link)
233		pmap_protect(watch->map->pmap,
234			     trunc_page(watch->loaddr),
235			     round_page(watch->hiaddr),
236			     VM_PROT_READ);
237
238	    db_watchpoints_inserted = TRUE;
239	}
240}
241
242void
243db_clear_watchpoints()
244{
245	db_watchpoints_inserted = FALSE;
246}
247
248#ifdef notused
249static boolean_t
250db_find_watchpoint(map, addr, regs)
251	vm_map_t	map;
252	db_addr_t	addr;
253	db_regs_t	*regs;
254{
255	register db_watchpoint_t watch;
256	db_watchpoint_t found = 0;
257
258	for (watch = db_watchpoint_list;
259	     watch != 0;
260	     watch = watch->link)
261	    if (db_map_equal(watch->map, map)) {
262		if ((watch->loaddr <= addr) &&
263		    (addr < watch->hiaddr))
264		    return (TRUE);
265		else if ((trunc_page(watch->loaddr) <= addr) &&
266			 (addr < round_page(watch->hiaddr)))
267		    found = watch;
268	    }
269
270	/*
271	 *	We didn't hit exactly on a watchpoint, but we are
272	 *	in a protected region.  We want to single-step
273	 *	and then re-protect.
274	 */
275
276	if (found) {
277	    db_watchpoints_inserted = FALSE;
278	    db_single_step(regs);
279	}
280
281	return (FALSE);
282}
283#endif
284
285
286
287/* Delete hardware watchpoint */
288/*ARGSUSED*/
289void
290db_deletehwatch_cmd(addr, have_addr, count, modif)
291	db_expr_t	addr;
292	boolean_t	have_addr;
293	db_expr_t	count;
294	char *		modif;
295{
296	int rc;
297
298        if (count < 0)
299                count = 4;
300
301	rc = db_md_clr_watchpoint(addr, count);
302	if (rc < 0)
303		db_printf("hardware watchpoint could not be deleted\n");
304}
305
306/* Set hardware watchpoint */
307/*ARGSUSED*/
308void
309db_hwatchpoint_cmd(addr, have_addr, count, modif)
310	db_expr_t	addr;
311	boolean_t	have_addr;
312	db_expr_t	count;
313	char *		modif;
314{
315	int rc;
316
317        if (count < 0)
318                count = 4;
319
320	rc = db_md_set_watchpoint(addr, count);
321	if (rc < 0)
322		db_printf("hardware watchpoint could not be set\n");
323}
324