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 * $FreeBSD: src/sys/ddb/db_watch.c,v 1.20.2.1 2001/07/25 01:00:08 bsd Exp $ 27 * $DragonFly: src/sys/ddb/db_watch.c,v 1.5 2005/12/23 21:35:44 swildner Exp $ 28 */ 29 30 /* 31 * Author: Richard P. Draves, Carnegie Mellon University 32 * Date: 10/90 33 */ 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 38 #include <vm/vm.h> 39 #include <sys/lock.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, 61 db_addr_t addr); 62 #ifdef notused 63 static boolean_t db_find_watchpoint (vm_map_t map, db_addr_t addr, 64 db_regs_t *regs); 65 #endif 66 static void db_list_watchpoints (void); 67 static void db_set_watchpoint (vm_map_t map, db_addr_t addr, 68 vm_size_t size); 69 70 int db_md_set_watchpoint (db_expr_t addr, db_expr_t size); 71 int db_md_clr_watchpoint (db_expr_t addr, db_expr_t size); 72 void db_md_list_watchpoints (void); 73 74 75 db_watchpoint_t 76 db_watchpoint_alloc(void) 77 { 78 db_watchpoint_t watch; 79 80 if ((watch = db_free_watchpoints) != 0) { 81 db_free_watchpoints = watch->link; 82 return (watch); 83 } 84 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 85 db_printf("All watchpoints used.\n"); 86 return (0); 87 } 88 watch = db_next_free_watchpoint; 89 db_next_free_watchpoint++; 90 91 return (watch); 92 } 93 94 void 95 db_watchpoint_free(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(vm_map_t map, db_addr_t addr, vm_size_t size) 103 { 104 db_watchpoint_t watch; 105 106 if (map == NULL) { 107 db_printf("No map.\n"); 108 return; 109 } 110 111 /* 112 * Should we do anything fancy with overlapping regions? 113 */ 114 115 for (watch = db_watchpoint_list; 116 watch != 0; 117 watch = watch->link) 118 if (db_map_equal(watch->map, map) && 119 (watch->loaddr == addr) && 120 (watch->hiaddr == addr+size)) { 121 db_printf("Already set.\n"); 122 return; 123 } 124 125 watch = db_watchpoint_alloc(); 126 if (watch == 0) { 127 db_printf("Too many watchpoints.\n"); 128 return; 129 } 130 131 watch->map = map; 132 watch->loaddr = addr; 133 watch->hiaddr = addr+size; 134 135 watch->link = db_watchpoint_list; 136 db_watchpoint_list = watch; 137 138 db_watchpoints_inserted = FALSE; 139 } 140 141 static void 142 db_delete_watchpoint(vm_map_t map, db_addr_t addr) 143 { 144 db_watchpoint_t watch; 145 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(void) 163 { 164 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(db_expr_t addr, boolean_t have_addr, db_expr_t count, 185 char *modif) 186 { 187 db_delete_watchpoint(db_map_addr(addr), addr); 188 } 189 190 /* Set watchpoint */ 191 /*ARGSUSED*/ 192 void 193 db_watchpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 194 char *modif) 195 { 196 vm_size_t size; 197 db_expr_t value; 198 199 if (db_expression(&value)) 200 size = (vm_size_t) value; 201 else 202 size = 4; 203 db_skip_to_eol(); 204 205 db_set_watchpoint(db_map_addr(addr), addr, size); 206 } 207 208 /* 209 * At least one non-optional show-command must be implemented using 210 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 211 */ 212 DB_SHOW_COMMAND(watches, db_listwatch_cmd) 213 { 214 db_list_watchpoints(); 215 db_md_list_watchpoints(); 216 } 217 218 void 219 db_set_watchpoints(void) 220 { 221 db_watchpoint_t watch; 222 223 if (!db_watchpoints_inserted) { 224 for (watch = db_watchpoint_list; 225 watch != 0; 226 watch = watch->link) 227 pmap_protect(watch->map->pmap, 228 trunc_page(watch->loaddr), 229 round_page(watch->hiaddr), 230 VM_PROT_READ); 231 232 db_watchpoints_inserted = TRUE; 233 } 234 } 235 236 void 237 db_clear_watchpoints(void) 238 { 239 db_watchpoints_inserted = FALSE; 240 } 241 242 #ifdef notused 243 static boolean_t 244 db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t *regs) 245 { 246 db_watchpoint_t watch; 247 db_watchpoint_t found = 0; 248 249 for (watch = db_watchpoint_list; 250 watch != 0; 251 watch = watch->link) 252 if (db_map_equal(watch->map, map)) { 253 if ((watch->loaddr <= addr) && 254 (addr < watch->hiaddr)) 255 return (TRUE); 256 else if ((trunc_page(watch->loaddr) <= addr) && 257 (addr < round_page(watch->hiaddr))) 258 found = watch; 259 } 260 261 /* 262 * We didn't hit exactly on a watchpoint, but we are 263 * in a protected region. We want to single-step 264 * and then re-protect. 265 */ 266 267 if (found) { 268 db_watchpoints_inserted = FALSE; 269 db_single_step(regs); 270 } 271 272 return (FALSE); 273 } 274 #endif 275 276 277 278 /* Delete hardware watchpoint */ 279 /*ARGSUSED*/ 280 void 281 db_deletehwatch_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 282 char *modif) 283 { 284 int rc; 285 286 if (count < 0) 287 count = 4; 288 289 rc = db_md_clr_watchpoint(addr, count); 290 if (rc < 0) 291 db_printf("hardware watchpoint could not be deleted\n"); 292 } 293 294 /* Set hardware watchpoint */ 295 /*ARGSUSED*/ 296 void 297 db_hwatchpoint_cmd(db_expr_t addr, boolean_t have_addr, db_expr_t count, 298 char *modif) 299 { 300 int rc; 301 302 if (count < 0) 303 count = 4; 304 305 rc = db_md_set_watchpoint(addr, count); 306 if (rc < 0) 307 db_printf("hardware watchpoint could not be set\n"); 308 } 309