1 /*- 2 * SPDX-License-Identifier: MIT-CMU 3 * 4 * Mach Operating System 5 * Copyright (c) 1991,1990 Carnegie Mellon University 6 * All Rights Reserved. 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR 16 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 */ 28 /* 29 * Author: Richard P. Draves, Carnegie Mellon University 30 * Date: 10/90 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/kdb.h> 38 #include <sys/kernel.h> 39 #include <sys/lock.h> 40 #include <sys/proc.h> 41 42 #include <vm/vm.h> 43 #include <vm/pmap.h> 44 #include <vm/vm_map.h> 45 46 #include <machine/kdb.h> 47 48 #include <ddb/ddb.h> 49 #include <ddb/db_watch.h> 50 51 /* 52 * Watchpoints. 53 */ 54 55 static bool db_watchpoints_inserted = true; 56 57 #define NWATCHPOINTS 100 58 static struct db_watchpoint db_watch_table[NWATCHPOINTS]; 59 static db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; 60 static db_watchpoint_t db_free_watchpoints = 0; 61 static db_watchpoint_t db_watchpoint_list = 0; 62 63 static db_watchpoint_t db_watchpoint_alloc(void); 64 static void db_watchpoint_free(db_watchpoint_t watch); 65 static void db_delete_watchpoint(vm_map_t map, db_addr_t addr); 66 #ifdef notused 67 static bool db_find_watchpoint(vm_map_t map, db_addr_t addr, 68 db_regs_t *regs); 69 #endif 70 static void db_list_watchpoints(void); 71 static void db_set_watchpoint(vm_map_t map, db_addr_t addr, 72 vm_size_t size); 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 #ifdef __LP64__ 171 db_printf(" Map Address Size\n"); 172 #else 173 db_printf(" Map Address Size\n"); 174 #endif 175 for (watch = db_watchpoint_list; 176 watch != 0; 177 watch = watch->link) 178 #ifdef __LP64__ 179 db_printf("%s%16p %16lx %lx\n", 180 #else 181 db_printf("%s%8p %8lx %lx\n", 182 #endif 183 db_map_current(watch->map) ? "*" : " ", 184 (void *)watch->map, (long)watch->loaddr, 185 (long)watch->hiaddr - (long)watch->loaddr); 186 } 187 188 /* Delete watchpoint */ 189 /*ARGSUSED*/ 190 void 191 db_deletewatch_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 192 char *modif) 193 { 194 db_delete_watchpoint(db_map_addr(addr), addr); 195 } 196 197 /* Set watchpoint */ 198 /*ARGSUSED*/ 199 void 200 db_watchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 201 char *modif) 202 { 203 vm_size_t size; 204 db_expr_t value; 205 206 if (db_expression(&value)) 207 size = (vm_size_t) value; 208 else 209 size = 4; 210 db_skip_to_eol(); 211 212 db_set_watchpoint(db_map_addr(addr), addr, size); 213 } 214 215 /* 216 * At least one non-optional show-command must be implemented using 217 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 218 */ 219 DB_SHOW_COMMAND_FLAGS(watches, db_listwatch_cmd, DB_CMD_MEMSAFE) 220 { 221 db_list_watchpoints(); 222 db_md_list_watchpoints(); 223 } 224 225 void 226 db_set_watchpoints(void) 227 { 228 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(void) 245 { 246 db_watchpoints_inserted = false; 247 } 248 249 #ifdef notused 250 static bool 251 db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs) 252 { 253 db_watchpoint_t watch; 254 db_watchpoint_t found = 0; 255 256 for (watch = db_watchpoint_list; 257 watch != 0; 258 watch = watch->link) 259 if (db_map_equal(watch->map, map)) { 260 if ((watch->loaddr <= addr) && 261 (addr < watch->hiaddr)) 262 return (true); 263 else if ((trunc_page(watch->loaddr) <= addr) && 264 (addr < round_page(watch->hiaddr))) 265 found = watch; 266 } 267 268 /* 269 * We didn't hit exactly on a watchpoint, but we are 270 * in a protected region. We want to single-step 271 * and then re-protect. 272 */ 273 274 if (found) { 275 db_watchpoints_inserted = false; 276 db_single_step(regs); 277 } 278 279 return (false); 280 } 281 #endif 282 283 /* Delete hardware watchpoint */ 284 void 285 db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t size, 286 char *modif) 287 { 288 int rc; 289 290 if (size < 0) 291 size = 4; 292 293 rc = kdb_cpu_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size); 294 switch (rc) { 295 case ENXIO: 296 /* Not supported, ignored. */ 297 break; 298 case EINVAL: 299 db_printf("Invalid watchpoint address or size.\n"); 300 break; 301 default: 302 if (rc != 0) 303 db_printf("Hardware watchpoint could not be deleted, " 304 "status=%d\n", rc); 305 break; 306 } 307 } 308 309 /* Set hardware watchpoint */ 310 void 311 db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t size, 312 char *modif) 313 { 314 int rc; 315 316 if (size < 0) 317 size = 4; 318 319 rc = kdb_cpu_set_watchpoint((vm_offset_t)addr, (vm_size_t)size, 320 KDB_DBG_ACCESS_W); 321 322 switch (rc) { 323 case EINVAL: 324 db_printf("Invalid watchpoint size or address.\n"); 325 break; 326 case EBUSY: 327 db_printf("No hardware watchpoints available.\n"); 328 break; 329 case ENXIO: 330 db_printf("Hardware watchpoints are not supported on this platform.\n"); 331 break; 332 default: 333 if (rc != 0) 334 db_printf("Could not set hardware watchpoint, " 335 "status=%d\n", rc); 336 } 337 } 338