]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ddb/db_watch.c
xnu-344.23.tar.gz
[apple/xnu.git] / osfmk / ddb / db_watch.c
1 /*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 * @OSF_COPYRIGHT@
24 */
25 /*
26 * HISTORY
27 *
28 * Revision 1.1.1.1 1998/09/22 21:05:48 wsanchez
29 * Import of Mac OS X kernel (~semeria)
30 *
31 * Revision 1.1.1.1 1998/03/07 02:26:09 wsanchez
32 * Import of OSF Mach kernel (~mburg)
33 *
34 * Revision 1.1.12.2 1995/01/06 19:11:06 devrcs
35 * mk6 CR668 - 1.3b26 merge
36 * * Revision 1.1.3.5 1994/05/06 18:40:29 tmt
37 * Merged osc1.3dec/shared with osc1.3b19
38 * Merge Alpha changes into osc1.312b source code.
39 * 64bit cleanup.
40 * * End1.3merge
41 * [1994/11/04 08:50:16 dwm]
42 *
43 * Revision 1.1.12.1 1994/09/23 01:22:53 ezf
44 * change marker to not FREE
45 * [1994/09/22 21:11:33 ezf]
46 *
47 * Revision 1.1.10.1 1994/01/05 19:28:22 bolinger
48 * Be sure to count kernel-loaded tasks as part of kernel address space
49 * in locating watchpoints.
50 * [1994/01/04 17:43:33 bolinger]
51 *
52 * Revision 1.1.3.3 1993/07/27 18:28:31 elliston
53 * Add ANSI prototypes. CR #9523.
54 * [1993/07/27 18:13:30 elliston]
55 *
56 * Revision 1.1.3.2 1993/06/02 23:13:14 jeffc
57 * Added to OSF/1 R1.3 from NMK15.0.
58 * [1993/06/02 20:57:54 jeffc]
59 *
60 * Revision 1.1 1992/09/30 02:01:33 robert
61 * Initial revision
62 *
63 * $EndLog$
64 */
65 /* CMU_HIST */
66 /*
67 * Revision 2.7 91/10/09 16:04:32 af
68 * Revision 2.6.3.1 91/10/05 13:08:50 jeffreyh
69 * Added user space watch point support including non current task.
70 * Changed "map" field of db_watchpoint structure to "task"
71 * for a user to easily understand the target space.
72 * [91/08/29 tak]
73 *
74 * Revision 2.6.3.1 91/10/05 13:08:50 jeffreyh
75 * Added user space watch point support including non current task.
76 * Changed "map" field of db_watchpoint structure to "task"
77 * for a user to easily understand the target space.
78 * [91/08/29 tak]
79 *
80 * Revision 2.6 91/05/14 15:37:30 mrt
81 * Correcting copyright
82 *
83 * Revision 2.5 91/02/05 17:07:27 mrt
84 * Changed to new Mach copyright
85 * [91/01/31 16:20:02 mrt]
86 *
87 * Revision 2.4 91/01/08 15:09:24 rpd
88 * Use db_map_equal, db_map_current, db_map_addr.
89 * [90/11/10 rpd]
90 *
91 * Revision 2.3 90/11/05 14:26:39 rpd
92 * Initialize db_watchpoints_inserted to TRUE.
93 * [90/11/04 rpd]
94 *
95 * Revision 2.2 90/10/25 14:44:16 rwd
96 * Made db_watchpoint_cmd parse a size argument.
97 * [90/10/17 rpd]
98 * Generalized the watchpoint support.
99 * [90/10/16 rwd]
100 * Created.
101 * [90/10/16 rpd]
102 *
103 */
104 /* CMU_ENDHIST */
105 /*
106 * Mach Operating System
107 * Copyright (c) 1991,1990 Carnegie Mellon University
108 * All Rights Reserved.
109 *
110 * Permission to use, copy, modify and distribute this software and its
111 * documentation is hereby granted, provided that both the copyright
112 * notice and this permission notice appear in all copies of the
113 * software, derivative works or modified versions, and any portions
114 * thereof, and that both notices appear in supporting documentation.
115 *
116 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
117 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
118 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
119 *
120 * Carnegie Mellon requests users of this software to return to
121 *
122 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
123 * School of Computer Science
124 * Carnegie Mellon University
125 * Pittsburgh PA 15213-3890
126 *
127 * any improvements or extensions that they make and grant Carnegie Mellon
128 * the rights to redistribute these changes.
129 */
130 /*
131 */
132 /*
133 * Author: Richard P. Draves, Carnegie Mellon University
134 * Date: 10/90
135 */
136
137 #include <mach/boolean.h>
138 #include <mach/vm_param.h>
139 #include <mach/machine/vm_types.h>
140 #include <mach/machine/vm_param.h>
141 #include <vm/vm_map.h>
142
143 #include <machine/db_machdep.h>
144 #include <ddb/db_lex.h>
145 #include <ddb/db_watch.h>
146 #include <ddb/db_access.h>
147 #include <ddb/db_sym.h>
148 #include <ddb/db_task_thread.h>
149 #include <ddb/db_command.h>
150 #include <ddb/db_expr.h>
151 #include <ddb/db_output.h> /* For db_printf() */
152 #include <ddb/db_run.h> /* For db_single_step() */
153
154 /*
155 * Watchpoints.
156 */
157
158 boolean_t db_watchpoints_inserted = TRUE;
159
160 #define NWATCHPOINTS 100
161 struct db_watchpoint db_watch_table[NWATCHPOINTS];
162 db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0];
163 db_watchpoint_t db_free_watchpoints = 0;
164 db_watchpoint_t db_watchpoint_list = 0;
165
166 extern vm_map_t kernel_map;
167
168
169
170 /* Prototypes for functions local to this file. XXX -- should be static.
171 */
172
173 db_watchpoint_t db_watchpoint_alloc(void);
174
175 void db_watchpoint_free(register db_watchpoint_t watch);
176
177 void db_set_watchpoint(
178 task_t task,
179 db_addr_t addr,
180 vm_size_t size);
181
182 void db_delete_watchpoint(
183 task_t task,
184 db_addr_t addr);
185
186 static int db_get_task(
187 char *modif,
188 task_t *taskp,
189 db_addr_t addr);
190
191 void db_list_watchpoints(void);
192
193
194
195 db_watchpoint_t
196 db_watchpoint_alloc(void)
197 {
198 register db_watchpoint_t watch;
199
200 if ((watch = db_free_watchpoints) != 0) {
201 db_free_watchpoints = watch->link;
202 return (watch);
203 }
204 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) {
205 db_printf("All watchpoints used.\n");
206 return (0);
207 }
208 watch = db_next_free_watchpoint;
209 db_next_free_watchpoint++;
210
211 return (watch);
212 }
213
214 void
215 db_watchpoint_free(register db_watchpoint_t watch)
216 {
217 watch->link = db_free_watchpoints;
218 db_free_watchpoints = watch;
219 }
220
221 void
222 db_set_watchpoint(
223 task_t task,
224 db_addr_t addr,
225 vm_size_t size)
226 {
227 register db_watchpoint_t watch;
228
229 /*
230 * Should we do anything fancy with overlapping regions?
231 */
232
233 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
234 if (watch->task == task &&
235 (watch->loaddr == addr) &&
236 (watch->hiaddr == addr+size)) {
237 db_printf("Already set.\n");
238 return;
239 }
240 }
241
242 watch = db_watchpoint_alloc();
243 if (watch == 0) {
244 db_printf("Too many watchpoints.\n");
245 return;
246 }
247
248 watch->task = task;
249 watch->loaddr = addr;
250 watch->hiaddr = addr+size;
251
252 watch->link = db_watchpoint_list;
253 db_watchpoint_list = watch;
254
255 db_watchpoints_inserted = FALSE;
256 }
257
258 void
259 db_delete_watchpoint(
260 task_t task,
261 db_addr_t addr)
262 {
263 register db_watchpoint_t watch;
264 register db_watchpoint_t *prev;
265
266 for (prev = &db_watchpoint_list; (watch = *prev) != 0;
267 prev = &watch->link) {
268 if (watch->task == task &&
269 (watch->loaddr <= addr) &&
270 (addr < watch->hiaddr)) {
271 *prev = watch->link;
272 db_watchpoint_free(watch);
273 return;
274 }
275 }
276
277 db_printf("Not set.\n");
278 }
279
280 void
281 db_list_watchpoints(void)
282 {
283 register db_watchpoint_t watch;
284 int task_id;
285
286 if (db_watchpoint_list == 0) {
287 db_printf("No watchpoints set\n");
288 return;
289 }
290
291 db_printf("Space Address Size\n");
292 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
293 if (watch->task == TASK_NULL)
294 db_printf("kernel ");
295 else {
296 task_id = db_lookup_task(watch->task);
297 if (task_id < 0)
298 db_printf("%*X", 2*sizeof(vm_offset_t), watch->task);
299 else
300 db_printf("task%-3d ", task_id);
301 }
302 db_printf(" %*X %X\n", 2*sizeof(vm_offset_t), watch->loaddr,
303 watch->hiaddr - watch->loaddr);
304 }
305 }
306
307 static int
308 db_get_task(
309 char *modif,
310 task_t *taskp,
311 db_addr_t addr)
312 {
313 task_t task = TASK_NULL;
314 db_expr_t value;
315 boolean_t user_space;
316
317 user_space = db_option(modif, 'T');
318 if (user_space) {
319 if (db_expression(&value)) {
320 task = (task_t)value;
321 if (db_lookup_task(task) < 0) {
322 db_printf("bad task address %X\n", task);
323 return(-1);
324 }
325 } else {
326 task = db_default_task;
327 if (task == TASK_NULL) {
328 if ((task = db_current_task()) == TASK_NULL) {
329 db_printf("no task\n");
330 return(-1);
331 }
332 }
333 }
334 }
335 if (!DB_VALID_ADDRESS(addr, user_space)) {
336 db_printf("Address %#X is not in %s space\n", addr,
337 (user_space)? "user": "kernel");
338 return(-1);
339 }
340 *taskp = task;
341 return(0);
342 }
343
344 /* Delete watchpoint */
345 void
346 db_deletewatch_cmd(
347 db_expr_t addr,
348 int have_addr,
349 db_expr_t count,
350 char * modif)
351 {
352 task_t task;
353
354 if (db_get_task(modif, &task, addr) < 0)
355 return;
356 db_delete_watchpoint(task, addr);
357 }
358
359 /* Set watchpoint */
360 void
361 db_watchpoint_cmd(
362 db_expr_t addr,
363 int have_addr,
364 db_expr_t count,
365 char * modif)
366 {
367 vm_size_t size;
368 db_expr_t value;
369 task_t task;
370
371 if (db_get_task(modif, &task, addr) < 0)
372 return;
373 if (db_expression(&value))
374 size = (vm_size_t) value;
375 else
376 size = sizeof(int);
377 db_set_watchpoint(task, addr, size);
378 }
379
380 /* list watchpoints */
381 void
382 db_listwatch_cmd(void)
383 {
384 db_list_watchpoints();
385 }
386
387 void
388 db_set_watchpoints(void)
389 {
390 register db_watchpoint_t watch;
391 vm_map_t map;
392
393 if (!db_watchpoints_inserted) {
394 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
395 map = (watch->task)? watch->task->map: kernel_map;
396 pmap_protect(map->pmap,
397 trunc_page(watch->loaddr),
398 round_page(watch->hiaddr),
399 VM_PROT_READ);
400 }
401 db_watchpoints_inserted = TRUE;
402 }
403 }
404
405 void
406 db_clear_watchpoints(void)
407 {
408 db_watchpoints_inserted = FALSE;
409 }
410
411 boolean_t
412 db_find_watchpoint(
413 vm_map_t map,
414 db_addr_t addr,
415 db_regs_t *regs)
416 {
417 register db_watchpoint_t watch;
418 db_watchpoint_t found = 0;
419 register task_t task_space;
420
421 task_space = (vm_map_pmap(map) == kernel_pmap)?
422 TASK_NULL: db_current_space();
423 for (watch = db_watchpoint_list; watch != 0; watch = watch->link) {
424 if (watch->task == task_space) {
425 if ((watch->loaddr <= addr) && (addr < watch->hiaddr))
426 return (TRUE);
427 else if ((trunc_page(watch->loaddr) <= addr) &&
428 (addr < round_page(watch->hiaddr)))
429 found = watch;
430 }
431 }
432
433 /*
434 * We didn't hit exactly on a watchpoint, but we are
435 * in a protected region. We want to single-step
436 * and then re-protect.
437 */
438
439 if (found) {
440 db_watchpoints_inserted = FALSE;
441 db_single_step(regs, task_space);
442 }
443
444 return (FALSE);
445 }