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