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 static db_watchpoint_t 70 db_watchpoint_alloc() 71 { 72 register db_watchpoint_t watch; 73 74 if ((watch = db_free_watchpoints) != 0) { 75 db_free_watchpoints = watch->link; 76 return (watch); 77 } 78 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 79 db_printf("All watchpoints used.\n"); 80 return (0); 81 } 82 watch = db_next_free_watchpoint; 83 db_next_free_watchpoint++; 84 85 return (watch); 86 } 87 88 static void 89 db_watchpoint_free(watch) 90 register db_watchpoint_t watch; 91 { 92 watch->link = db_free_watchpoints; 93 db_free_watchpoints = watch; 94 } 95 96 static void 97 db_set_watchpoint(map, addr, size) 98 vm_map_t map; 99 db_addr_t addr; 100 vm_size_t size; 101 { 102 register db_watchpoint_t watch; 103 104 if (map == NULL) { 105 db_printf("No map.\n"); 106 return; 107 } 108 109 /* 110 * Should we do anything fancy with overlapping regions? 111 */ 112 113 for (watch = db_watchpoint_list; 114 watch != 0; 115 watch = watch->link) 116 if (db_map_equal(watch->map, map) && 117 (watch->loaddr == addr) && 118 (watch->hiaddr == addr+size)) { 119 db_printf("Already set.\n"); 120 return; 121 } 122 123 watch = db_watchpoint_alloc(); 124 if (watch == 0) { 125 db_printf("Too many watchpoints.\n"); 126 return; 127 } 128 129 watch->map = map; 130 watch->loaddr = addr; 131 watch->hiaddr = addr+size; 132 133 watch->link = db_watchpoint_list; 134 db_watchpoint_list = watch; 135 136 db_watchpoints_inserted = FALSE; 137 } 138 139 static void 140 db_delete_watchpoint(map, addr) 141 vm_map_t map; 142 db_addr_t addr; 143 { 144 register db_watchpoint_t watch; 145 register db_watchpoint_t *prev; 146 147 for (prev = &db_watchpoint_list; 148 (watch = *prev) != 0; 149 prev = &watch->link) 150 if (db_map_equal(watch->map, map) && 151 (watch->loaddr <= addr) && 152 (addr < watch->hiaddr)) { 153 *prev = watch->link; 154 db_watchpoint_free(watch); 155 return; 156 } 157 158 db_printf("Not set.\n"); 159 } 160 161 static void 162 db_list_watchpoints() 163 { 164 register db_watchpoint_t watch; 165 166 if (db_watchpoint_list == 0) { 167 db_printf("No watchpoints set\n"); 168 return; 169 } 170 171 db_printf(" Map Address Size\n"); 172 for (watch = db_watchpoint_list; 173 watch != 0; 174 watch = watch->link) 175 db_printf("%s%8p %8lx %lx\n", 176 db_map_current(watch->map) ? "*" : " ", 177 (void *)watch->map, (long)watch->loaddr, 178 (long)watch->hiaddr - (long)watch->loaddr); 179 } 180 181 /* Delete watchpoint */ 182 /*ARGSUSED*/ 183 void 184 db_deletewatch_cmd(addr, have_addr, count, modif) 185 db_expr_t addr; 186 boolean_t have_addr; 187 db_expr_t count; 188 char * modif; 189 { 190 db_delete_watchpoint(db_map_addr(addr), addr); 191 } 192 193 /* Set watchpoint */ 194 /*ARGSUSED*/ 195 void 196 db_watchpoint_cmd(addr, have_addr, count, modif) 197 db_expr_t addr; 198 boolean_t have_addr; 199 db_expr_t count; 200 char * modif; 201 { 202 vm_size_t size; 203 db_expr_t value; 204 205 if (db_expression(&value)) 206 size = (vm_size_t) value; 207 else 208 size = 4; 209 db_skip_to_eol(); 210 211 db_set_watchpoint(db_map_addr(addr), addr, size); 212 } 213 214 /* 215 * At least one non-optional show-command must be implemented using 216 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 217 */ 218 DB_SHOW_COMMAND(watches, db_listwatch_cmd) 219 { 220 db_list_watchpoints(); 221 db_md_list_watchpoints(); 222 } 223 224 void 225 db_set_watchpoints() 226 { 227 register db_watchpoint_t watch; 228 229 if (!db_watchpoints_inserted) { 230 for (watch = db_watchpoint_list; 231 watch != 0; 232 watch = watch->link) 233 pmap_protect(watch->map->pmap, 234 trunc_page(watch->loaddr), 235 round_page(watch->hiaddr), 236 VM_PROT_READ); 237 238 db_watchpoints_inserted = TRUE; 239 } 240 } 241 242 void 243 db_clear_watchpoints() 244 { 245 db_watchpoints_inserted = FALSE; 246 } 247 248 #ifdef notused 249 static boolean_t 250 db_find_watchpoint(map, addr, regs) 251 vm_map_t map; 252 db_addr_t addr; 253 db_regs_t *regs; 254 { 255 register db_watchpoint_t watch; 256 db_watchpoint_t found = 0; 257 258 for (watch = db_watchpoint_list; 259 watch != 0; 260 watch = watch->link) 261 if (db_map_equal(watch->map, map)) { 262 if ((watch->loaddr <= addr) && 263 (addr < watch->hiaddr)) 264 return (TRUE); 265 else if ((trunc_page(watch->loaddr) <= addr) && 266 (addr < round_page(watch->hiaddr))) 267 found = watch; 268 } 269 270 /* 271 * We didn't hit exactly on a watchpoint, but we are 272 * in a protected region. We want to single-step 273 * and then re-protect. 274 */ 275 276 if (found) { 277 db_watchpoints_inserted = FALSE; 278 db_single_step(regs); 279 } 280 281 return (FALSE); 282 } 283 #endif 284 285 286 287 /* Delete hardware watchpoint */ 288 /*ARGSUSED*/ 289 void 290 db_deletehwatch_cmd(addr, have_addr, count, modif) 291 db_expr_t addr; 292 boolean_t have_addr; 293 db_expr_t count; 294 char * modif; 295 { 296 int rc; 297 298 if (count < 0) 299 count = 4; 300 301 rc = db_md_clr_watchpoint(addr, count); 302 if (rc < 0) 303 db_printf("hardware watchpoint could not be deleted\n"); 304 } 305 306 /* Set hardware watchpoint */ 307 /*ARGSUSED*/ 308 void 309 db_hwatchpoint_cmd(addr, have_addr, count, modif) 310 db_expr_t addr; 311 boolean_t have_addr; 312 db_expr_t count; 313 char * modif; 314 { 315 int rc; 316 317 if (count < 0) 318 count = 4; 319 320 rc = db_md_set_watchpoint(addr, count); 321 if (rc < 0) 322 db_printf("hardware watchpoint could not be set\n"); 323 } 324