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