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 bool		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 bool		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(void)
71{
72	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(db_watchpoint_t watch)
90{
91	watch->link = db_free_watchpoints;
92	db_free_watchpoints = watch;
93}
94
95static void
96db_set_watchpoint(vm_map_t map, db_addr_t addr, vm_size_t size)
97{
98	db_watchpoint_t	watch;
99
100	if (map == NULL) {
101	    db_printf("No map.\n");
102	    return;
103	}
104
105	/*
106	 *	Should we do anything fancy with overlapping regions?
107	 */
108
109	for (watch = db_watchpoint_list;
110	     watch != 0;
111	     watch = watch->link)
112	    if (db_map_equal(watch->map, map) &&
113		(watch->loaddr == addr) &&
114		(watch->hiaddr == addr+size)) {
115		db_printf("Already set.\n");
116		return;
117	    }
118
119	watch = db_watchpoint_alloc();
120	if (watch == 0) {
121	    db_printf("Too many watchpoints.\n");
122	    return;
123	}
124
125	watch->map = map;
126	watch->loaddr = addr;
127	watch->hiaddr = addr+size;
128
129	watch->link = db_watchpoint_list;
130	db_watchpoint_list = watch;
131
132	db_watchpoints_inserted = false;
133}
134
135static void
136db_delete_watchpoint(vm_map_t map, db_addr_t addr)
137{
138	db_watchpoint_t	watch;
139	db_watchpoint_t	*prev;
140
141	for (prev = &db_watchpoint_list;
142	     (watch = *prev) != 0;
143	     prev = &watch->link)
144	    if (db_map_equal(watch->map, map) &&
145		(watch->loaddr <= addr) &&
146		(addr < watch->hiaddr)) {
147		*prev = watch->link;
148		db_watchpoint_free(watch);
149		return;
150	    }
151
152	db_printf("Not set.\n");
153}
154
155static void
156db_list_watchpoints(void)
157{
158	db_watchpoint_t	watch;
159
160	if (db_watchpoint_list == 0) {
161	    db_printf("No watchpoints set\n");
162	    return;
163	}
164
165#ifdef __LP64__
166	db_printf(" Map                Address          Size\n");
167#else
168	db_printf(" Map        Address  Size\n");
169#endif
170	for (watch = db_watchpoint_list;
171	     watch != 0;
172	     watch = watch->link)
173#ifdef __LP64__
174	    db_printf("%s%16p  %16lx  %lx\n",
175#else
176	    db_printf("%s%8p  %8lx  %lx\n",
177#endif
178		      db_map_current(watch->map) ? "*" : " ",
179		      (void *)watch->map, (long)watch->loaddr,
180		      (long)watch->hiaddr - (long)watch->loaddr);
181}
182
183/* Delete watchpoint */
184/*ARGSUSED*/
185void
186db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
187   char *modif)
188{
189	db_delete_watchpoint(db_map_addr(addr), addr);
190}
191
192/* Set watchpoint */
193/*ARGSUSED*/
194void
195db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
196   char *modif)
197{
198	vm_size_t	size;
199	db_expr_t	value;
200
201	if (db_expression(&value))
202	    size = (vm_size_t) value;
203	else
204	    size = 4;
205	db_skip_to_eol();
206
207	db_set_watchpoint(db_map_addr(addr), addr, size);
208}
209
210/*
211 * At least one non-optional show-command must be implemented using
212 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created.  Here is one.
213 */
214DB_SHOW_COMMAND(watches, db_listwatch_cmd)
215{
216	db_list_watchpoints();
217	db_md_list_watchpoints();
218}
219
220void
221db_set_watchpoints(void)
222{
223	db_watchpoint_t	watch;
224
225	if (!db_watchpoints_inserted) {
226	    for (watch = db_watchpoint_list;
227	         watch != 0;
228	         watch = watch->link)
229		pmap_protect(watch->map->pmap,
230			     trunc_page(watch->loaddr),
231			     round_page(watch->hiaddr),
232			     VM_PROT_READ);
233
234	    db_watchpoints_inserted = true;
235	}
236}
237
238void
239db_clear_watchpoints(void)
240{
241	db_watchpoints_inserted = false;
242}
243
244#ifdef notused
245static bool
246db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs)
247{
248	db_watchpoint_t watch;
249	db_watchpoint_t found = 0;
250
251	for (watch = db_watchpoint_list;
252	     watch != 0;
253	     watch = watch->link)
254	    if (db_map_equal(watch->map, map)) {
255		if ((watch->loaddr <= addr) &&
256		    (addr < watch->hiaddr))
257		    return (true);
258		else if ((trunc_page(watch->loaddr) <= addr) &&
259			 (addr < round_page(watch->hiaddr)))
260		    found = watch;
261	    }
262
263	/*
264	 *	We didn't hit exactly on a watchpoint, but we are
265	 *	in a protected region.  We want to single-step
266	 *	and then re-protect.
267	 */
268
269	if (found) {
270	    db_watchpoints_inserted = false;
271	    db_single_step(regs);
272	}
273
274	return (false);
275}
276#endif
277
278
279
280/* Delete hardware watchpoint */
281/*ARGSUSED*/
282void
283db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
284   char *modif)
285{
286	int rc;
287
288	if (count < 0)
289		count = 4;
290
291	rc = db_md_clr_watchpoint(addr, count);
292	if (rc < 0)
293		db_printf("hardware watchpoint could not be deleted\n");
294}
295
296/* Set hardware watchpoint */
297/*ARGSUSED*/
298void
299db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count,
300   char *modif)
301{
302	int rc;
303
304	if (count < 0)
305		count = 4;
306
307	rc = db_md_set_watchpoint(addr, count);
308	if (rc < 0)
309		db_printf("hardware watchpoint could not be set\n");
310}
311