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