1/*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
49 *  School of Computer Science
50 *  Carnegie Mellon University
51 *  Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56/*
57 */
58/*
59 * 	Author: Richard P. Draves, Carnegie Mellon University
60 *	Date:	10/90
61 */
62
63#include <mach/boolean.h>
64#include <mach/vm_param.h>
65#include <mach/machine/vm_types.h>
66#include <mach/machine/vm_param.h>
67#include <vm/vm_map.h>
68
69#include <machine/db_machdep.h>
70#include <ddb/db_lex.h>
71#include <ddb/db_watch.h>
72#include <ddb/db_access.h>
73#include <ddb/db_sym.h>
74#include <ddb/db_task_thread.h>
75#include <ddb/db_command.h>
76#include <ddb/db_expr.h>
77#include <ddb/db_output.h>		/* For db_printf() */
78#include <ddb/db_run.h>			/* For db_single_step() */
79
80/*
81 * Watchpoints.
82 */
83
84boolean_t	db_watchpoints_inserted = TRUE;
85
86#define	NWATCHPOINTS	100
87struct db_watchpoint	db_watch_table[NWATCHPOINTS];
88db_watchpoint_t		db_next_free_watchpoint = &db_watch_table[0];
89db_watchpoint_t		db_free_watchpoints = 0;
90db_watchpoint_t		db_watchpoint_list = 0;
91
92extern vm_map_t		kernel_map;
93
94
95
96/* Prototypes for functions local to this file.  XXX -- should be static.
97 */
98
99db_watchpoint_t db_watchpoint_alloc(void);
100
101void db_watchpoint_free(register db_watchpoint_t watch);
102
103void db_set_watchpoint(
104	task_t		task,
105	db_addr_t	addr,
106	vm_size_t	size);
107
108void db_delete_watchpoint(
109	task_t		task,
110	db_addr_t	addr);
111
112static int db_get_task(
113	char		*modif,
114	task_t		*taskp,
115	db_addr_t	addr);
116
117void db_list_watchpoints(void);
118
119
120
121db_watchpoint_t
122db_watchpoint_alloc(void)
123{
124	register db_watchpoint_t	watch;
125
126	if ((watch = db_free_watchpoints) != 0) {
127	    db_free_watchpoints = watch->link;
128	    return (watch);
129	}
130	if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
131	    db_printf("All watchpoints used.\n");
132	    return (0);
133	}
134	watch = db_next_free_watchpoint;
135	db_next_free_watchpoint++;
136
137	return (watch);
138}
139
140void
141db_watchpoint_free(register db_watchpoint_t watch)
142{
143	watch->link = db_free_watchpoints;
144	db_free_watchpoints = watch;
145}
146
147void
148db_set_watchpoint(
149	task_t		task,
150	db_addr_t	addr,
151	vm_size_t	size)
152{
153	register db_watchpoint_t	watch;
154
155	/*
156	 *	Should we do anything fancy with overlapping regions?
157	 */
158
159	for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
160	    if (watch->task == task &&
161		(watch->loaddr == addr) &&
162		(watch->hiaddr == addr+size)) {
163		db_printf("Already set.\n");
164		return;
165	    }
166	}
167
168	watch = db_watchpoint_alloc();
169	if (watch == 0) {
170	    db_printf("Too many watchpoints.\n");
171	    return;
172	}
173
174	watch->task = task;
175	watch->loaddr = addr;
176	watch->hiaddr = addr+size;
177
178	watch->link = db_watchpoint_list;
179	db_watchpoint_list = watch;
180
181	db_watchpoints_inserted = FALSE;
182}
183
184void
185db_delete_watchpoint(
186	task_t		task,
187	db_addr_t	addr)
188{
189	register db_watchpoint_t	watch;
190	register db_watchpoint_t	*prev;
191
192	for (prev = &db_watchpoint_list; (watch = *prev) != 0;
193	     prev = &watch->link) {
194	    if (watch->task == task &&
195		(watch->loaddr <= addr) &&
196		(addr < watch->hiaddr)) {
197		*prev = watch->link;
198		db_watchpoint_free(watch);
199		return;
200	    }
201	}
202
203	db_printf("Not set.\n");
204}
205
206void
207db_list_watchpoints(void)
208{
209	register db_watchpoint_t watch;
210	int	 task_id;
211
212	if (db_watchpoint_list == 0) {
213	    db_printf("No watchpoints set\n");
214	    return;
215	}
216
217	db_printf("Space      Address  Size\n");
218	for (watch = db_watchpoint_list; watch != 0; watch = watch->link)  {
219	    if (watch->task == TASK_NULL)
220		db_printf("kernel  ");
221	    else {
222		task_id = db_lookup_task(watch->task);
223		if (task_id < 0)
224		    db_printf("%*X", 2*sizeof(vm_offset_t), watch->task);
225		else
226		    db_printf("task%-3d ", task_id);
227	    }
228	    db_printf("  %*X  %X\n", 2*sizeof(vm_offset_t), watch->loaddr,
229		      watch->hiaddr - watch->loaddr);
230	}
231}
232
233static int
234db_get_task(
235	char		*modif,
236	task_t		*taskp,
237	db_addr_t	addr)
238{
239	task_t		task = TASK_NULL;
240	db_expr_t	value;
241	boolean_t	user_space;
242
243	user_space = db_option(modif, 'T');
244	if (user_space) {
245	    if (db_expression(&value)) {
246		task = (task_t)(unsigned long)value;
247		if (db_lookup_task(task) < 0) {
248		    db_printf("bad task address %X\n", task);
249		    return(-1);
250		}
251	    } else {
252		task = db_default_task;
253		if (task == TASK_NULL) {
254		    if ((task = db_current_task()) == TASK_NULL) {
255			db_printf("no task\n");
256			return(-1);
257		    }
258		}
259	    }
260	}
261	if (!DB_VALID_ADDRESS(addr, user_space)) {
262	    db_printf("Address %#X is not in %s space\n", addr,
263			(user_space)? "user": "kernel");
264	    return(-1);
265	}
266	*taskp = task;
267	return(0);
268}
269
270/* Delete watchpoint */
271void
272db_deletewatch_cmd(db_expr_t addr, __unused boolean_t have_addr,
273		   __unused db_expr_t count, char *modif)
274{
275	task_t		task;
276
277	if (db_get_task(modif, &task, addr) < 0)
278	    return;
279	db_delete_watchpoint(task, addr);
280}
281
282/* Set watchpoint */
283void
284db_watchpoint_cmd(db_expr_t addr, __unused boolean_t have_addr,
285		  __unused db_expr_t count, char *modif)
286{
287	vm_size_t	size;
288	db_expr_t	value;
289	task_t		task;
290
291	if (db_get_task(modif, &task, addr) < 0)
292	    return;
293	if (db_expression(&value))
294	    size = (vm_size_t) value;
295	else
296	    size = sizeof(int);
297	db_set_watchpoint(task, addr, size);
298}
299
300/* list watchpoints */
301void
302db_listwatch_cmd(__unused db_expr_t addr, __unused boolean_t have_addr,
303		 __unused db_expr_t count, __unused char *modif)
304{
305	db_list_watchpoints();
306}
307
308void
309db_set_watchpoints(void)
310{
311	register db_watchpoint_t	watch;
312	vm_map_t			map;
313
314	if (!db_watchpoints_inserted) {
315	    for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
316		map = (watch->task)? watch->task->map: kernel_map;
317		pmap_protect(map->pmap,
318			     vm_map_trunc_page(watch->loaddr),
319			     vm_map_round_page(watch->hiaddr),
320			     VM_PROT_READ);
321	    }
322	    db_watchpoints_inserted = TRUE;
323	}
324}
325
326void
327db_clear_watchpoints(void)
328{
329	db_watchpoints_inserted = FALSE;
330}
331
332boolean_t
333db_find_watchpoint(
334	vm_map_t	map,
335	db_addr_t	addr,
336	db_regs_t	*regs)
337{
338	register db_watchpoint_t watch;
339	db_watchpoint_t found = 0;
340	register task_t	task_space;
341
342	task_space = (vm_map_pmap(map) == kernel_pmap)?
343		TASK_NULL: db_current_space();
344	for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
345	    if (watch->task == task_space) {
346		if ((watch->loaddr <= addr) && (addr < watch->hiaddr))
347		    return (TRUE);
348		else if ((trunc_page(watch->loaddr) <= addr) &&
349			 (addr < round_page(watch->hiaddr)))
350		    found = watch;
351	    }
352	}
353
354	/*
355	 *	We didn't hit exactly on a watchpoint, but we are
356	 *	in a protected region.  We want to single-step
357	 *	and then re-protect.
358	 */
359
360	if (found) {
361	    db_watchpoints_inserted = FALSE;
362	    db_single_step(regs, task_space);
363	}
364
365	return (FALSE);
366}
367