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 /* 30 * Author: David B. Golub, Carnegie Mellon University 31 * Date: 7/90 32 */ 33 /* 34 * Breakpoints. 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kdb.h> 40 41 #include <vm/vm.h> 42 #include <vm/vm_kern.h> 43 44 #include <machine/kdb.h> 45 46 #include <ddb/ddb.h> 47 #include <ddb/db_break.h> 48 #include <ddb/db_access.h> 49 #include <ddb/db_sym.h> 50 51 struct db_breakpoint_type { 52 db_breakpoint_t db_next_free_breakpoint; 53 db_breakpoint_t db_breakpoint_limit; 54 db_breakpoint_t db_free_breakpoints; 55 db_breakpoint_t db_breakpoint_list; 56 }; 57 58 #define NBREAKPOINTS 100 59 static struct db_breakpoint db_break_table[NBREAKPOINTS]; 60 61 static struct db_breakpoint_type db_breakpoint = { 62 .db_next_free_breakpoint = &db_break_table[0], 63 .db_breakpoint_limit = &db_break_table[NBREAKPOINTS], 64 .db_free_breakpoints = NULL, 65 .db_breakpoint_list = NULL, 66 }; 67 68 #ifdef HAS_HW_BREAKPOINT 69 static struct db_breakpoint db_hbreak_table[NHBREAKPOINTS]; 70 71 static struct db_breakpoint_type db_hbreakpoint = { 72 .db_next_free_breakpoint = &db_hbreak_table[0], 73 .db_breakpoint_limit = &db_hbreak_table[NHBREAKPOINTS], 74 .db_free_breakpoints = NULL, 75 .db_breakpoint_list = NULL, 76 }; 77 #endif 78 79 static db_breakpoint_t db_breakpoint_alloc( 80 struct db_breakpoint_type *bkpt_type); 81 static void db_breakpoint_free(struct db_breakpoint_type *bkpt_typ, 82 db_breakpoint_t bkpt); 83 static void db_delete_breakpoint(struct db_breakpoint_type *bkpt_type, 84 vm_map_t map, db_addr_t addr); 85 static db_breakpoint_t db_find_breakpoint(struct db_breakpoint_type *bkpt_type, 86 vm_map_t map, db_addr_t addr); 87 static void db_list_breakpoints(void); 88 static bool db_set_breakpoint(struct db_breakpoint_type *bkpt_type, 89 vm_map_t map, db_addr_t addr, int count); 90 91 static db_breakpoint_t 92 db_breakpoint_alloc(struct db_breakpoint_type *bkpt_type) 93 { 94 register db_breakpoint_t bkpt; 95 96 if ((bkpt = bkpt_type->db_free_breakpoints) != 0) { 97 bkpt_type->db_free_breakpoints = bkpt->link; 98 return (bkpt); 99 } 100 if (bkpt_type->db_next_free_breakpoint == 101 bkpt_type->db_breakpoint_limit) { 102 db_printf("All breakpoints used.\n"); 103 return (0); 104 } 105 bkpt = bkpt_type->db_next_free_breakpoint; 106 bkpt_type->db_next_free_breakpoint++; 107 108 return (bkpt); 109 } 110 111 static void 112 db_breakpoint_free(struct db_breakpoint_type *bkpt_type, db_breakpoint_t bkpt) 113 { 114 bkpt->link = bkpt_type->db_free_breakpoints; 115 bkpt_type->db_free_breakpoints = bkpt; 116 } 117 118 static bool 119 db_set_breakpoint(struct db_breakpoint_type *bkpt_type, vm_map_t map, 120 db_addr_t addr, int count) 121 { 122 register db_breakpoint_t bkpt; 123 124 if (db_find_breakpoint(bkpt_type, map, addr)) { 125 db_printf("Already set.\n"); 126 return (false); 127 } 128 129 bkpt = db_breakpoint_alloc(bkpt_type); 130 if (bkpt == 0) { 131 db_printf("Too many breakpoints.\n"); 132 return (false); 133 } 134 135 bkpt->map = map; 136 bkpt->address = addr; 137 bkpt->flags = 0; 138 bkpt->init_count = count; 139 bkpt->count = count; 140 141 bkpt->link = bkpt_type->db_breakpoint_list; 142 bkpt_type->db_breakpoint_list = bkpt; 143 144 return (true); 145 } 146 147 static void 148 db_delete_breakpoint(struct db_breakpoint_type *bkpt_type, vm_map_t map, 149 db_addr_t addr) 150 { 151 register db_breakpoint_t bkpt; 152 register db_breakpoint_t *prev; 153 154 for (prev = &bkpt_type->db_breakpoint_list; 155 (bkpt = *prev) != 0; 156 prev = &bkpt->link) { 157 if (db_map_equal(bkpt->map, map) && 158 (bkpt->address == addr)) { 159 *prev = bkpt->link; 160 break; 161 } 162 } 163 if (bkpt == 0) { 164 db_printf("Not set.\n"); 165 return; 166 } 167 168 db_breakpoint_free(bkpt_type, bkpt); 169 } 170 171 static db_breakpoint_t 172 db_find_breakpoint(struct db_breakpoint_type *bkpt_type, vm_map_t map, 173 db_addr_t addr) 174 { 175 register db_breakpoint_t bkpt; 176 177 for (bkpt = bkpt_type->db_breakpoint_list; 178 bkpt != 0; 179 bkpt = bkpt->link) 180 { 181 if (db_map_equal(bkpt->map, map) && 182 (bkpt->address == addr)) 183 return (bkpt); 184 } 185 return (0); 186 } 187 188 db_breakpoint_t 189 db_find_breakpoint_here(db_addr_t addr) 190 { 191 db_breakpoint_t bkpt; 192 193 bkpt = db_find_breakpoint(&db_breakpoint, db_map_addr(addr), addr); 194 #ifdef HAS_HW_BREAKPOINT 195 if (bkpt == NULL) 196 bkpt = db_find_breakpoint(&db_hbreakpoint, db_map_addr(addr), 197 addr); 198 #endif 199 200 return (bkpt); 201 } 202 203 static bool db_breakpoints_inserted = true; 204 205 #ifndef BKPT_WRITE 206 #define BKPT_WRITE(addr, storage) \ 207 do { \ 208 *storage = db_get_value(addr, BKPT_SIZE, false); \ 209 db_put_value(addr, BKPT_SIZE, BKPT_SET(*storage)); \ 210 } while (0) 211 #endif 212 213 #ifndef BKPT_CLEAR 214 #define BKPT_CLEAR(addr, storage) \ 215 db_put_value(addr, BKPT_SIZE, *storage) 216 #endif 217 218 /* 219 * Set software breakpoints. 220 */ 221 void 222 db_set_breakpoints(void) 223 { 224 register db_breakpoint_t bkpt; 225 226 if (!db_breakpoints_inserted) { 227 for (bkpt = db_breakpoint.db_breakpoint_list; 228 bkpt != 0; 229 bkpt = bkpt->link) 230 if (db_map_current(bkpt->map)) { 231 BKPT_WRITE(bkpt->address, &bkpt->bkpt_inst); 232 } 233 db_breakpoints_inserted = true; 234 } 235 } 236 237 /* 238 * Clean software breakpoints. 239 */ 240 void 241 db_clear_breakpoints(void) 242 { 243 register db_breakpoint_t bkpt; 244 245 if (db_breakpoints_inserted) { 246 for (bkpt = db_breakpoint.db_breakpoint_list; 247 bkpt != 0; 248 bkpt = bkpt->link) 249 if (db_map_current(bkpt->map)) { 250 BKPT_CLEAR(bkpt->address, &bkpt->bkpt_inst); 251 } 252 db_breakpoints_inserted = false; 253 } 254 } 255 256 /* 257 * List software breakpoints. 258 */ 259 static void 260 db_list_breakpoints(void) 261 { 262 register db_breakpoint_t bkpt; 263 264 if (db_breakpoint.db_breakpoint_list == 0) { 265 db_printf("No breakpoints set\n"); 266 return; 267 } 268 269 db_printf(" Map Count Address\n"); 270 for (bkpt = db_breakpoint.db_breakpoint_list; 271 bkpt != 0; 272 bkpt = bkpt->link) { 273 db_printf("%s%8p %5d ", 274 db_map_current(bkpt->map) ? "*" : " ", 275 (void *)bkpt->map, bkpt->init_count); 276 db_printsym(bkpt->address, DB_STGY_PROC); 277 db_printf("\n"); 278 } 279 } 280 281 /* 282 * Delete software breakpoint 283 */ 284 /*ARGSUSED*/ 285 void 286 db_delete_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 287 { 288 db_delete_breakpoint(&db_breakpoint, db_map_addr(addr), 289 (db_addr_t)addr); 290 } 291 292 /* 293 * Set software breakpoint with skip count 294 */ 295 /*ARGSUSED*/ 296 void 297 db_breakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 298 { 299 if (count == -1) 300 count = 1; 301 302 db_set_breakpoint(&db_breakpoint, db_map_addr(addr), (db_addr_t)addr, 303 count); 304 } 305 306 #ifdef HAS_HW_BREAKPOINT 307 /* 308 * Delete hardware breakpoint 309 */ 310 void 311 db_deletehbreak_cmd(db_expr_t addr, bool have_addr, db_expr_t count, 312 char *modif) 313 { 314 if (count == -1) 315 count = 1; 316 317 if (kdb_cpu_clr_breakpoint(addr) != 0) { 318 db_printf("hardware breakpoint could not be delete\n"); 319 return; 320 } 321 322 db_delete_breakpoint(&db_hbreakpoint, db_map_addr(addr), 323 (db_addr_t)addr); 324 } 325 326 /* 327 * Set hardware breakpoint 328 */ 329 void 330 db_hbreakpoint_cmd(db_expr_t addr, bool have_addr, db_expr_t count, char *modif) 331 { 332 if (count == -1) 333 count = 1; 334 335 if (!db_set_breakpoint(&db_hbreakpoint, db_map_addr(addr), 336 (db_addr_t)addr, count)) 337 return; 338 339 if (kdb_cpu_set_breakpoint(addr) != 0) { 340 db_printf("hardware breakpoint could not be set\n"); 341 db_delete_breakpoint(&db_hbreakpoint, db_map_addr(addr), 342 (db_addr_t)addr); 343 } 344 } 345 #endif 346 347 /* list breakpoints */ 348 void 349 db_listbreak_cmd(db_expr_t dummy1, bool dummy2, db_expr_t dummy3, char *dummy4) 350 { 351 db_list_breakpoints(); 352 #ifdef HAS_HW_BREAKPOINT 353 db_md_list_breakpoints(); 354 #endif 355 } 356 357 /* 358 * We want ddb to be usable before most of the kernel has been 359 * initialized. In particular, current_thread() or kernel_map 360 * (or both) may be null. 361 */ 362 363 bool 364 db_map_equal(vm_map_t map1, vm_map_t map2) 365 { 366 return ((map1 == map2) || 367 ((map1 == NULL) && (map2 == kernel_map)) || 368 ((map1 == kernel_map) && (map2 == NULL))); 369 } 370 371 bool 372 db_map_current(vm_map_t map) 373 { 374 #if 0 375 thread_t thread; 376 377 return ((map == NULL) || 378 (map == kernel_map) || 379 (((thread = current_thread()) != NULL) && 380 (map == thread->task->map))); 381 #else 382 return (true); 383 #endif 384 } 385 386 vm_map_t 387 db_map_addr(vm_offset_t addr) 388 { 389 #if 0 390 thread_t thread; 391 392 /* 393 * We want to return kernel_map for all 394 * non-user addresses, even when debugging 395 * kernel tasks with their own maps. 396 */ 397 398 if ((VM_MIN_ADDRESS <= addr) && 399 (addr < VM_MAX_ADDRESS) && 400 ((thread = current_thread()) != NULL)) 401 return thread->task->map; 402 else 403 #endif 404 return kernel_map; 405 } 406