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