]> git.saurik.com Git - apple/xnu.git/blame - osfmk/ddb/db_watch.c
xnu-1228.5.18.tar.gz
[apple/xnu.git] / osfmk / ddb / db_watch.c
CommitLineData
1c79356b 1/*
2d21ac55 2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
1c79356b 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
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.
8f6c56a5 14 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
1c79356b
A
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)) {
2d21ac55 246 task = (task_t)(unsigned long)value;
1c79356b
A
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
2d21ac55
A
272db_deletewatch_cmd(db_expr_t addr, __unused boolean_t have_addr,
273 __unused db_expr_t count, char *modif)
1c79356b
A
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
2d21ac55
A
284db_watchpoint_cmd(db_expr_t addr, __unused boolean_t have_addr,
285 __unused db_expr_t count, char *modif)
1c79356b
A
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
2d21ac55
A
302db_listwatch_cmd(__unused db_expr_t addr, __unused boolean_t have_addr,
303 __unused db_expr_t count, __unused char *modif)
1c79356b
A
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,
91447636
A
318 vm_map_trunc_page(watch->loaddr),
319 vm_map_round_page(watch->hiaddr),
1c79356b
A
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);
91447636
A
348 else if ((trunc_page(watch->loaddr) <= addr) &&
349 (addr < round_page(watch->hiaddr)))
1c79356b
A
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}