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