1 /* 2 * Mach Operating System 3 * Copyright (c) 1991,1990 Carnegie Mellon University 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify and distribute this software and its 7 * documentation is hereby granted, provided that both the copyright 8 * notice and this permission notice appear in all copies of the 9 * software, derivative works or modified versions, and any portions 10 * thereof, and that both notices appear in supporting documentation. 11 * 12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 15 * 16 * Carnegie Mellon requests users of this software to return to 17 * 18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 19 * School of Computer Science 20 * Carnegie Mellon University 21 * Pittsburgh PA 15213-3890 22 * 23 * any improvements or extensions that they make and grant Carnegie the 24 * rights to redistribute these changes. 25 */ 26 /* 27 * Author: Richard P. Draves, Carnegie Mellon University 28 * Date: 10/90 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/proc.h> 38 39 #include <vm/vm.h> 40 #include <vm/pmap.h> 41 #include <vm/vm_map.h> 42 43 #include <ddb/ddb.h> 44 #include <ddb/db_watch.h> 45 46 /* 47 * Watchpoints. 48 */ 49 50 static boolean_t db_watchpoints_inserted = TRUE; 51 52 #define NWATCHPOINTS 100 53 static struct db_watchpoint db_watch_table[NWATCHPOINTS]; 54 static db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; 55 static db_watchpoint_t db_free_watchpoints = 0; 56 static db_watchpoint_t db_watchpoint_list = 0; 57 58 static db_watchpoint_t db_watchpoint_alloc(void); 59 static void db_watchpoint_free(db_watchpoint_t watch); 60 static void db_delete_watchpoint(vm_map_t map, db_addr_t addr); 61 #ifdef notused 62 static boolean_t db_find_watchpoint(vm_map_t map, db_addr_t addr, 63 db_regs_t *regs); 64 #endif 65 static void db_list_watchpoints(void); 66 static void db_set_watchpoint(vm_map_t map, db_addr_t addr, 67 vm_size_t size); 68 69 int db_md_set_watchpoint(db_expr_t addr, db_expr_t size); 70 int db_md_clr_watchpoint(db_expr_t addr, db_expr_t size); 71 void db_md_list_watchpoints(void); 72 73 74 static db_watchpoint_t 75 db_watchpoint_alloc() 76 { 77 register db_watchpoint_t watch; 78 79 if ((watch = db_free_watchpoints) != 0) { 80 db_free_watchpoints = watch->link; 81 return (watch); 82 } 83 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 84 db_printf("All watchpoints used.\n"); 85 return (0); 86 } 87 watch = db_next_free_watchpoint; 88 db_next_free_watchpoint++; 89 90 return (watch); 91 } 92 93 static void 94 db_watchpoint_free(watch) 95 register db_watchpoint_t watch; 96 { 97 watch->link = db_free_watchpoints; 98 db_free_watchpoints = watch; 99 } 100 101 static void 102 db_set_watchpoint(map, addr, size) 103 vm_map_t map; 104 db_addr_t addr; 105 vm_size_t size; 106 { 107 register db_watchpoint_t watch; 108 109 if (map == NULL) { 110 db_printf("No map.\n"); 111 return; 112 } 113 114 /* 115 * Should we do anything fancy with overlapping regions? 116 */ 117 118 for (watch = db_watchpoint_list; 119 watch != 0; 120 watch = watch->link) 121 if (db_map_equal(watch->map, map) && 122 (watch->loaddr == addr) && 123 (watch->hiaddr == addr+size)) { 124 db_printf("Already set.\n"); 125 return; 126 } 127 128 watch = db_watchpoint_alloc(); 129 if (watch == 0) { 130 db_printf("Too many watchpoints.\n"); 131 return; 132 } 133 134 watch->map = map; 135 watch->loaddr = addr; 136 watch->hiaddr = addr+size; 137 138 watch->link = db_watchpoint_list; 139 db_watchpoint_list = watch; 140 141 db_watchpoints_inserted = FALSE; 142 } 143 144 static void 145 db_delete_watchpoint(map, addr) 146 vm_map_t map; 147 db_addr_t addr; 148 { 149 register db_watchpoint_t watch; 150 register db_watchpoint_t *prev; 151 152 for (prev = &db_watchpoint_list; 153 (watch = *prev) != 0; 154 prev = &watch->link) 155 if (db_map_equal(watch->map, map) && 156 (watch->loaddr <= addr) && 157 (addr < watch->hiaddr)) { 158 *prev = watch->link; 159 db_watchpoint_free(watch); 160 return; 161 } 162 163 db_printf("Not set.\n"); 164 } 165 166 static void 167 db_list_watchpoints() 168 { 169 register db_watchpoint_t watch; 170 171 if (db_watchpoint_list == 0) { 172 db_printf("No watchpoints set\n"); 173 return; 174 } 175 176 db_printf(" Map Address Size\n"); 177 for (watch = db_watchpoint_list; 178 watch != 0; 179 watch = watch->link) 180 db_printf("%s%8p %8lx %lx\n", 181 db_map_current(watch->map) ? "*" : " ", 182 (void *)watch->map, (long)watch->loaddr, 183 (long)watch->hiaddr - (long)watch->loaddr); 184 } 185 186 /* Delete watchpoint */ 187 /*ARGSUSED*/ 188 void 189 db_deletewatch_cmd(addr, have_addr, count, modif) 190 db_expr_t addr; 191 boolean_t have_addr; 192 db_expr_t count; 193 char * modif; 194 { 195 db_delete_watchpoint(db_map_addr(addr), addr); 196 } 197 198 /* Set watchpoint */ 199 /*ARGSUSED*/ 200 void 201 db_watchpoint_cmd(addr, have_addr, count, modif) 202 db_expr_t addr; 203 boolean_t have_addr; 204 db_expr_t count; 205 char * modif; 206 { 207 vm_size_t size; 208 db_expr_t value; 209 210 if (db_expression(&value)) 211 size = (vm_size_t) value; 212 else 213 size = 4; 214 db_skip_to_eol(); 215 216 db_set_watchpoint(db_map_addr(addr), addr, size); 217 } 218 219 /* 220 * At least one non-optional show-command must be implemented using 221 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 222 */ 223 DB_SHOW_COMMAND(watches, db_listwatch_cmd) 224 { 225 db_list_watchpoints(); 226 db_md_list_watchpoints(); 227 } 228 229 void 230 db_set_watchpoints() 231 { 232 register db_watchpoint_t watch; 233 234 if (!db_watchpoints_inserted) { 235 for (watch = db_watchpoint_list; 236 watch != 0; 237 watch = watch->link) 238 pmap_protect(watch->map->pmap, 239 trunc_page(watch->loaddr), 240 round_page(watch->hiaddr), 241 VM_PROT_READ); 242 243 db_watchpoints_inserted = TRUE; 244 } 245 } 246 247 void 248 db_clear_watchpoints() 249 { 250 db_watchpoints_inserted = FALSE; 251 } 252 253 #ifdef notused 254 static boolean_t 255 db_find_watchpoint(map, addr, regs) 256 vm_map_t map; 257 db_addr_t addr; 258 db_regs_t *regs; 259 { 260 register db_watchpoint_t watch; 261 db_watchpoint_t found = 0; 262 263 for (watch = db_watchpoint_list; 264 watch != 0; 265 watch = watch->link) 266 if (db_map_equal(watch->map, map)) { 267 if ((watch->loaddr <= addr) && 268 (addr < watch->hiaddr)) 269 return (TRUE); 270 else if ((trunc_page(watch->loaddr) <= addr) && 271 (addr < round_page(watch->hiaddr))) 272 found = watch; 273 } 274 275 /* 276 * We didn't hit exactly on a watchpoint, but we are 277 * in a protected region. We want to single-step 278 * and then re-protect. 279 */ 280 281 if (found) { 282 db_watchpoints_inserted = FALSE; 283 db_single_step(regs); 284 } 285 286 return (FALSE); 287 } 288 #endif 289 290 291 292 /* Delete hardware watchpoint */ 293 /*ARGSUSED*/ 294 void 295 db_deletehwatch_cmd(addr, have_addr, count, modif) 296 db_expr_t addr; 297 boolean_t have_addr; 298 db_expr_t count; 299 char * modif; 300 { 301 int rc; 302 303 if (count < 0) 304 count = 4; 305 306 rc = db_md_clr_watchpoint(addr, count); 307 if (rc < 0) 308 db_printf("hardware watchpoint could not be deleted\n"); 309 } 310 311 /* Set hardware watchpoint */ 312 /*ARGSUSED*/ 313 void 314 db_hwatchpoint_cmd(addr, have_addr, count, modif) 315 db_expr_t addr; 316 boolean_t have_addr; 317 db_expr_t count; 318 char * modif; 319 { 320 int rc; 321 322 if (count < 0) 323 count = 4; 324 325 rc = db_md_set_watchpoint(addr, count); 326 if (rc < 0) 327 db_printf("hardware watchpoint could not be set\n"); 328 } 329