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$");
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#ifdef __LP64__
172	db_printf(" Map                Address          Size\n");
173#else
174	db_printf(" Map        Address  Size\n");
175#endif
176	for (watch = db_watchpoint_list;
177	     watch != 0;
178	     watch = watch->link)
179#ifdef __LP64__
180	    db_printf("%s%16p  %16lx  %lx\n",
181#else
182	    db_printf("%s%8p  %8lx  %lx\n",
183#endif
184		      db_map_current(watch->map) ? "*" : " ",
185		      (void *)watch->map, (long)watch->loaddr,
186		      (long)watch->hiaddr - (long)watch->loaddr);
187}
188
189/* Delete watchpoint */
190/*ARGSUSED*/
191void
192db_deletewatch_cmd(addr, have_addr, count, modif)
193	db_expr_t	addr;
194	boolean_t	have_addr;
195	db_expr_t	count;
196	char *		modif;
197{
198	db_delete_watchpoint(db_map_addr(addr), addr);
199}
200
201/* Set watchpoint */
202/*ARGSUSED*/
203void
204db_watchpoint_cmd(addr, have_addr, count, modif)
205	db_expr_t	addr;
206	boolean_t	have_addr;
207	db_expr_t	count;
208	char *		modif;
209{
210	vm_size_t	size;
211	db_expr_t	value;
212
213	if (db_expression(&value))
214	    size = (vm_size_t) value;
215	else
216	    size = 4;
217	db_skip_to_eol();
218
219	db_set_watchpoint(db_map_addr(addr), addr, size);
220}
221
222/*
223 * At least one non-optional show-command must be implemented using
224 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
225 */
226DB_SHOW_COMMAND(watches, db_listwatch_cmd)
227{
228	db_list_watchpoints();
229	db_md_list_watchpoints();
230}
231
232void
233db_set_watchpoints()
234{
235	register db_watchpoint_t	watch;
236
237	if (!db_watchpoints_inserted) {
238	    for (watch = db_watchpoint_list;
239	         watch != 0;
240	         watch = watch->link)
241		pmap_protect(watch->map->pmap,
242			     trunc_page(watch->loaddr),
243			     round_page(watch->hiaddr),
244			     VM_PROT_READ);
245
246	    db_watchpoints_inserted = TRUE;
247	}
248}
249
250void
251db_clear_watchpoints()
252{
253	db_watchpoints_inserted = FALSE;
254}
255
256#ifdef notused
257static boolean_t
258db_find_watchpoint(map, addr, regs)
259	vm_map_t	map;
260	db_addr_t	addr;
261	db_regs_t	*regs;
262{
263	register db_watchpoint_t watch;
264	db_watchpoint_t found = 0;
265
266	for (watch = db_watchpoint_list;
267	     watch != 0;
268	     watch = watch->link)
269	    if (db_map_equal(watch->map, map)) {
270		if ((watch->loaddr <= addr) &&
271		    (addr < watch->hiaddr))
272		    return (TRUE);
273		else if ((trunc_page(watch->loaddr) <= addr) &&
274			 (addr < round_page(watch->hiaddr)))
275		    found = watch;
276	    }
277
278	/*
279	 *	We didn't hit exactly on a watchpoint, but we are
280	 *	in a protected region.  We want to single-step
281	 *	and then re-protect.
282	 */
283
284	if (found) {
285	    db_watchpoints_inserted = FALSE;
286	    db_single_step(regs);
287	}
288
289	return (FALSE);
290}
291#endif
292
293
294
295/* Delete hardware watchpoint */
296/*ARGSUSED*/
297void
298db_deletehwatch_cmd(addr, have_addr, count, modif)
299	db_expr_t	addr;
300	boolean_t	have_addr;
301	db_expr_t	count;
302	char *		modif;
303{
304	int rc;
305
306        if (count < 0)
307                count = 4;
308
309	rc = db_md_clr_watchpoint(addr, count);
310	if (rc < 0)
311		db_printf("hardware watchpoint could not be deleted\n");
312}
313
314/* Set hardware watchpoint */
315/*ARGSUSED*/
316void
317db_hwatchpoint_cmd(addr, have_addr, count, modif)
318	db_expr_t	addr;
319	boolean_t	have_addr;
320	db_expr_t	count;
321	char *		modif;
322{
323	int rc;
324
325        if (count < 0)
326                count = 4;
327
328	rc = db_md_set_watchpoint(addr, count);
329	if (rc < 0)
330		db_printf("hardware watchpoint could not be set\n");
331}
332