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 #include <sys/param.h> 35 #include <sys/kdb.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/proc.h> 39 40 #include <vm/vm.h> 41 #include <vm/pmap.h> 42 #include <vm/vm_map.h> 43 44 #include <machine/kdb.h> 45 46 #include <ddb/ddb.h> 47 #include <ddb/db_watch.h> 48 49 /* 50 * Watchpoints. 51 */ 52 53 static bool db_watchpoints_inserted = true; 54 55 #define NWATCHPOINTS 100 56 static struct db_watchpoint db_watch_table[NWATCHPOINTS]; 57 static db_watchpoint_t db_next_free_watchpoint = &db_watch_table[0]; 58 static db_watchpoint_t db_free_watchpoints = 0; 59 static db_watchpoint_t db_watchpoint_list = 0; 60 61 static db_watchpoint_t db_watchpoint_alloc(void); 62 static void db_watchpoint_free(db_watchpoint_t watch); 63 static void db_delete_watchpoint(vm_map_t map, db_addr_t addr); 64 #ifdef notused 65 static bool db_find_watchpoint(vm_map_t map, db_addr_t addr, 66 db_regs_t *regs); 67 #endif 68 static void db_list_watchpoints(void); 69 static void db_set_watchpoint(vm_map_t map, db_addr_t addr, 70 vm_size_t size); 71 72 static db_watchpoint_t 73 db_watchpoint_alloc(void) 74 { 75 db_watchpoint_t watch; 76 77 if ((watch = db_free_watchpoints) != 0) { 78 db_free_watchpoints = watch->link; 79 return (watch); 80 } 81 if (db_next_free_watchpoint == &db_watch_table[NWATCHPOINTS]) { 82 db_printf("All watchpoints used.\n"); 83 return (0); 84 } 85 watch = db_next_free_watchpoint; 86 db_next_free_watchpoint++; 87 88 return (watch); 89 } 90 91 static void 92 db_watchpoint_free(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(vm_map_t map, db_addr_t addr, vm_size_t size) 100 { 101 db_watchpoint_t watch; 102 103 if (map == NULL) { 104 db_printf("No map.\n"); 105 return; 106 } 107 108 /* 109 * Should we do anything fancy with overlapping regions? 110 */ 111 112 for (watch = db_watchpoint_list; 113 watch != 0; 114 watch = watch->link) 115 if (db_map_equal(watch->map, map) && 116 (watch->loaddr == addr) && 117 (watch->hiaddr == addr+size)) { 118 db_printf("Already set.\n"); 119 return; 120 } 121 122 watch = db_watchpoint_alloc(); 123 if (watch == 0) { 124 db_printf("Too many watchpoints.\n"); 125 return; 126 } 127 128 watch->map = map; 129 watch->loaddr = addr; 130 watch->hiaddr = addr+size; 131 132 watch->link = db_watchpoint_list; 133 db_watchpoint_list = watch; 134 135 db_watchpoints_inserted = false; 136 } 137 138 static void 139 db_delete_watchpoint(vm_map_t map, db_addr_t addr) 140 { 141 db_watchpoint_t watch; 142 db_watchpoint_t *prev; 143 144 for (prev = &db_watchpoint_list; 145 (watch = *prev) != 0; 146 prev = &watch->link) 147 if (db_map_equal(watch->map, map) && 148 (watch->loaddr <= addr) && 149 (addr < watch->hiaddr)) { 150 *prev = watch->link; 151 db_watchpoint_free(watch); 152 return; 153 } 154 155 db_printf("Not set.\n"); 156 } 157 158 static void 159 db_list_watchpoints(void) 160 { 161 db_watchpoint_t watch; 162 163 if (db_watchpoint_list == 0) { 164 db_printf("No watchpoints set\n"); 165 return; 166 } 167 168 #ifdef __LP64__ 169 db_printf(" Map Address Size\n"); 170 #else 171 db_printf(" Map Address Size\n"); 172 #endif 173 for (watch = db_watchpoint_list; 174 watch != 0; 175 watch = watch->link) 176 #ifdef __LP64__ 177 db_printf("%s%16p %16lx %lx\n", 178 #else 179 db_printf("%s%8p %8lx %lx\n", 180 #endif 181 db_map_current(watch->map) ? "*" : " ", 182 (void *)watch->map, (long)watch->loaddr, 183 (long)watch->hiaddr - (long)watch->loaddr); 184 } 185 186 /* Delete watchpoint */ 187 /*ARGSUSED*/ 188 void 189 db_deletewatch_cmd(db_expr_t addr, bool have_addr, 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(db_expr_t addr, bool have_addr, db_expr_t count, 199 char *modif) 200 { 201 vm_size_t size; 202 db_expr_t value; 203 204 if (db_expression(&value)) 205 size = (vm_size_t) value; 206 else 207 size = 4; 208 db_skip_to_eol(); 209 210 db_set_watchpoint(db_map_addr(addr), addr, size); 211 } 212 213 /* 214 * At least one non-optional show-command must be implemented using 215 * DB_SHOW_COMMAND() so that db_show_cmd_set gets created. Here is one. 216 */ 217 DB_SHOW_COMMAND_FLAGS(watches, db_listwatch_cmd, DB_CMD_MEMSAFE) 218 { 219 db_list_watchpoints(); 220 db_md_list_watchpoints(); 221 } 222 223 void 224 db_set_watchpoints(void) 225 { 226 db_watchpoint_t watch; 227 228 if (!db_watchpoints_inserted) { 229 for (watch = db_watchpoint_list; 230 watch != 0; 231 watch = watch->link) 232 pmap_protect(watch->map->pmap, 233 trunc_page(watch->loaddr), 234 round_page(watch->hiaddr), 235 VM_PROT_READ); 236 237 db_watchpoints_inserted = true; 238 } 239 } 240 241 void 242 db_clear_watchpoints(void) 243 { 244 db_watchpoints_inserted = false; 245 } 246 247 #ifdef notused 248 static bool 249 db_find_watchpoint(vm_map_t map, db_addr_t addr, db_regs_t regs) 250 { 251 db_watchpoint_t watch; 252 db_watchpoint_t found = 0; 253 254 for (watch = db_watchpoint_list; 255 watch != 0; 256 watch = watch->link) 257 if (db_map_equal(watch->map, map)) { 258 if ((watch->loaddr <= addr) && 259 (addr < watch->hiaddr)) 260 return (true); 261 else if ((trunc_page(watch->loaddr) <= addr) && 262 (addr < round_page(watch->hiaddr))) 263 found = watch; 264 } 265 266 /* 267 * We didn't hit exactly on a watchpoint, but we are 268 * in a protected region. We want to single-step 269 * and then re-protect. 270 */ 271 272 if (found) { 273 db_watchpoints_inserted = false; 274 db_single_step(regs); 275 } 276 277 return (false); 278 } 279 #endif 280 281 /* Delete hardware watchpoint */ 282 void 283 db_deletehwatch_cmd(db_expr_t addr, bool have_addr, db_expr_t size, 284 char *modif) 285 { 286 int rc; 287 288 if (size < 0) 289 size = 4; 290 291 rc = kdb_cpu_clr_watchpoint((vm_offset_t)addr, (vm_size_t)size); 292 switch (rc) { 293 case ENXIO: 294 /* Not supported, ignored. */ 295 break; 296 case EINVAL: 297 db_printf("Invalid watchpoint address or size.\n"); 298 break; 299 default: 300 if (rc != 0) 301 db_printf("Hardware watchpoint could not be deleted, " 302 "status=%d\n", rc); 303 break; 304 } 305 } 306 307 /* Set hardware watchpoint */ 308 void 309 db_hwatchpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t size, 310 char *modif) 311 { 312 int rc; 313 314 if (size < 0) 315 size = 4; 316 317 rc = kdb_cpu_set_watchpoint((vm_offset_t)addr, (vm_size_t)size, 318 KDB_DBG_ACCESS_W); 319 320 switch (rc) { 321 case EINVAL: 322 db_printf("Invalid watchpoint size or address.\n"); 323 break; 324 case EBUSY: 325 db_printf("No hardware watchpoints available.\n"); 326 break; 327 case ENXIO: 328 db_printf("Hardware watchpoints are not supported on this platform.\n"); 329 break; 330 default: 331 if (rc != 0) 332 db_printf("Could not set hardware watchpoint, " 333 "status=%d\n", rc); 334 } 335 } 336