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$ 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 #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 __P((void)); 59 static void db_watchpoint_free __P((db_watchpoint_t watch)); 60 static void db_delete_watchpoint __P((vm_map_t map, 61 db_addr_t addr)); 62 #ifdef notused 63 static boolean_t db_find_watchpoint __P((vm_map_t map, db_addr_t addr, 64 db_regs_t *regs)); 65 #endif 66 static void db_list_watchpoints __P((void)); 67 static void db_set_watchpoint __P((vm_map_t map, db_addr_t addr, 68 vm_size_t size)); 69 70 71 db_watchpoint_t 72 db_watchpoint_alloc() 73 { 74 register db_watchpoint_t watch; 75 76 if ((watch = db_free_watchpoints) != 0) { 77 db_free_watchpoints = watch->link; 78 return (watch); 79 } 80 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 81 db_printf("All watchpoints used.\n"); 82 return (0); 83 } 84 watch = db_next_free_watchpoint; 85 db_next_free_watchpoint++; 86 87 return (watch); 88 } 89 90 void 91 db_watchpoint_free(watch) 92 register db_watchpoint_t watch; 93 { 94 watch->link = db_free_watchpoints; 95 db_free_watchpoints = watch; 96 } 97 98 static void 99 db_set_watchpoint(map, addr, size) 100 vm_map_t map; 101 db_addr_t addr; 102 vm_size_t size; 103 { 104 register 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(map, addr) 143 vm_map_t map; 144 db_addr_t addr; 145 { 146 register db_watchpoint_t watch; 147 register db_watchpoint_t *prev; 148 149 for (prev = &db_watchpoint_list; 150 (watch = *prev) != 0; 151 prev = &watch->link) 152 if (db_map_equal(watch->map, map) && 153 (watch->loaddr <= addr) && 154 (addr < watch->hiaddr)) { 155 *prev = watch->link; 156 db_watchpoint_free(watch); 157 return; 158 } 159 160 db_printf("Not set.\n"); 161 } 162 163 static void 164 db_list_watchpoints() 165 { 166 register db_watchpoint_t watch; 167 168 if (db_watchpoint_list == 0) { 169 db_printf("No watchpoints set\n"); 170 return; 171 } 172 173 db_printf(" Map Address Size\n"); 174 for (watch = db_watchpoint_list; 175 watch != 0; 176 watch = watch->link) 177 db_printf("%s%8p %8lx %lx\n", 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(addr, have_addr, count, modif) 187 db_expr_t addr; 188 boolean_t have_addr; 189 db_expr_t count; 190 char * modif; 191 { 192 db_delete_watchpoint(db_map_addr(addr), addr); 193 } 194 195 /* Set watchpoint */ 196 /*ARGSUSED*/ 197 void 198 db_watchpoint_cmd(addr, have_addr, count, modif) 199 db_expr_t addr; 200 boolean_t have_addr; 201 db_expr_t count; 202 char * modif; 203 { 204 vm_size_t size; 205 db_expr_t value; 206 207 if (db_expression(&value)) 208 size = (vm_size_t) value; 209 else 210 size = 4; 211 db_skip_to_eol(); 212 213 db_set_watchpoint(db_map_addr(addr), addr, size); 214 } 215 216 /* 217 * At least one non-optional show-command must be implemented using 218 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 219 */ 220 DB_SHOW_COMMAND(watches, db_listwatch_cmd) 221 { 222 db_list_watchpoints(); 223 } 224 225 void 226 db_set_watchpoints() 227 { 228 register db_watchpoint_t watch; 229 230 if (!db_watchpoints_inserted) { 231 for (watch = db_watchpoint_list; 232 watch != 0; 233 watch = watch->link) 234 pmap_protect(watch->map->pmap, 235 trunc_page(watch->loaddr), 236 round_page(watch->hiaddr), 237 VM_PROT_READ); 238 239 db_watchpoints_inserted = TRUE; 240 } 241 } 242 243 void 244 db_clear_watchpoints() 245 { 246 db_watchpoints_inserted = FALSE; 247 } 248 249 #ifdef notused 250 static boolean_t 251 db_find_watchpoint(map, addr, regs) 252 vm_map_t map; 253 db_addr_t addr; 254 db_regs_t *regs; 255 { 256 register db_watchpoint_t watch; 257 db_watchpoint_t found = 0; 258 259 for (watch = db_watchpoint_list; 260 watch != 0; 261 watch = watch->link) 262 if (db_map_equal(watch->map, map)) { 263 if ((watch->loaddr <= addr) && 264 (addr < watch->hiaddr)) 265 return (TRUE); 266 else if ((trunc_page(watch->loaddr) <= addr) && 267 (addr < round_page(watch->hiaddr))) 268 found = watch; 269 } 270 271 /* 272 * We didn't hit exactly on a watchpoint, but we are 273 * in a protected region. We want to single-step 274 * and then re-protect. 275 */ 276 277 if (found) { 278 db_watchpoints_inserted = FALSE; 279 db_single_step(regs); 280 } 281 282 return (FALSE); 283 } 284 #endif 285