| 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: src/sys/i386/i386/db_interface.c,v 1.48.2.1 2000/07/07 00:38:46 obrien Exp $ |
| 27 | * $DragonFly: src/sys/i386/i386/Attic/db_interface.c,v 1.3 2003/07/04 00:32:24 dillon Exp $ |
| 28 | */ |
| 29 | |
| 30 | /* |
| 31 | * Interface to new debugger. |
| 32 | */ |
| 33 | #include <sys/param.h> |
| 34 | #include <sys/systm.h> |
| 35 | #include <sys/reboot.h> |
| 36 | #include <sys/cons.h> |
| 37 | |
| 38 | #include <machine/cpu.h> |
| 39 | #ifdef SMP |
| 40 | #include <machine/smp.h> |
| 41 | #include <machine/smptests.h> /** CPUSTOP_ON_DDBBREAK */ |
| 42 | #endif |
| 43 | #include <machine/globaldata.h> |
| 44 | |
| 45 | #include <vm/vm.h> |
| 46 | #include <vm/pmap.h> |
| 47 | |
| 48 | #include <ddb/ddb.h> |
| 49 | |
| 50 | #include <setjmp.h> |
| 51 | |
| 52 | static jmp_buf *db_nofault = 0; |
| 53 | extern jmp_buf db_jmpbuf; |
| 54 | |
| 55 | extern void gdb_handle_exception __P((db_regs_t *, int, int)); |
| 56 | |
| 57 | int db_active; |
| 58 | db_regs_t ddb_regs; |
| 59 | |
| 60 | static jmp_buf db_global_jmpbuf; |
| 61 | static int db_global_jmpbuf_valid; |
| 62 | |
| 63 | #ifdef __GNUC__ |
| 64 | #define rss() ({u_short ss; __asm __volatile("mov %%ss,%0" : "=r" (ss)); ss;}) |
| 65 | #endif |
| 66 | |
| 67 | /* |
| 68 | * kdb_trap - field a TRACE or BPT trap |
| 69 | */ |
| 70 | int |
| 71 | kdb_trap(type, code, regs) |
| 72 | int type, code; |
| 73 | register struct i386_saved_state *regs; |
| 74 | { |
| 75 | volatile int ddb_mode = !(boothowto & RB_GDB); |
| 76 | |
| 77 | /* |
| 78 | * XXX try to do nothing if the console is in graphics mode. |
| 79 | * Handle trace traps (and hardware breakpoints...) by ignoring |
| 80 | * them except for forgetting about them. Return 0 for other |
| 81 | * traps to say that we haven't done anything. The trap handler |
| 82 | * will usually panic. We should handle breakpoint traps for |
| 83 | * our breakpoints by disarming our breakpoints and fixing up |
| 84 | * %eip. |
| 85 | */ |
| 86 | if (cons_unavail && ddb_mode) { |
| 87 | if (type == T_TRCTRAP) { |
| 88 | regs->tf_eflags &= ~PSL_T; |
| 89 | return (1); |
| 90 | } |
| 91 | return (0); |
| 92 | } |
| 93 | |
| 94 | switch (type) { |
| 95 | case T_BPTFLT: /* breakpoint */ |
| 96 | case T_TRCTRAP: /* debug exception */ |
| 97 | break; |
| 98 | |
| 99 | default: |
| 100 | /* |
| 101 | * XXX this is almost useless now. In most cases, |
| 102 | * trap_fatal() has already printed a much more verbose |
| 103 | * message. However, it is dangerous to print things in |
| 104 | * trap_fatal() - printf() might be reentered and trap. |
| 105 | * The debugger should be given control first. |
| 106 | */ |
| 107 | if (ddb_mode) |
| 108 | db_printf("kernel: type %d trap, code=%x\n", type, code); |
| 109 | |
| 110 | if (db_nofault) { |
| 111 | jmp_buf *no_fault = db_nofault; |
| 112 | db_nofault = 0; |
| 113 | longjmp(*no_fault, 1); |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | /* |
| 118 | * This handles unexpected traps in ddb commands, including calls to |
| 119 | * non-ddb functions. db_nofault only applies to memory accesses by |
| 120 | * internal ddb commands. |
| 121 | */ |
| 122 | if (db_global_jmpbuf_valid) |
| 123 | longjmp(db_global_jmpbuf, 1); |
| 124 | |
| 125 | /* |
| 126 | * XXX We really should switch to a local stack here. |
| 127 | */ |
| 128 | ddb_regs = *regs; |
| 129 | |
| 130 | /* |
| 131 | * If in kernel mode, esp and ss are not saved, so dummy them up. |
| 132 | */ |
| 133 | if (ISPL(regs->tf_cs) == 0) { |
| 134 | ddb_regs.tf_esp = (int)®s->tf_esp; |
| 135 | ddb_regs.tf_ss = rss(); |
| 136 | } |
| 137 | |
| 138 | #ifdef SMP |
| 139 | #ifdef CPUSTOP_ON_DDBBREAK |
| 140 | |
| 141 | #if defined(VERBOSE_CPUSTOP_ON_DDBBREAK) |
| 142 | db_printf("\nCPU%d stopping CPUs: 0x%08x\n", cpuid, other_cpus); |
| 143 | #endif /* VERBOSE_CPUSTOP_ON_DDBBREAK */ |
| 144 | |
| 145 | /* We stop all CPUs except ourselves (obviously) */ |
| 146 | stop_cpus(mycpu->gd_other_cpus); |
| 147 | |
| 148 | #if defined(VERBOSE_CPUSTOP_ON_DDBBREAK) |
| 149 | db_printf(" stopped\n"); |
| 150 | #endif /* VERBOSE_CPUSTOP_ON_DDBBREAK */ |
| 151 | |
| 152 | #endif /* CPUSTOP_ON_DDBBREAK */ |
| 153 | #endif /* SMP */ |
| 154 | |
| 155 | (void) setjmp(db_global_jmpbuf); |
| 156 | db_global_jmpbuf_valid = TRUE; |
| 157 | db_active++; |
| 158 | if (ddb_mode) { |
| 159 | cndbctl(TRUE); |
| 160 | db_trap(type, code); |
| 161 | cndbctl(FALSE); |
| 162 | } else |
| 163 | gdb_handle_exception(&ddb_regs, type, code); |
| 164 | db_active--; |
| 165 | db_global_jmpbuf_valid = FALSE; |
| 166 | |
| 167 | #ifdef SMP |
| 168 | #ifdef CPUSTOP_ON_DDBBREAK |
| 169 | |
| 170 | #if defined(VERBOSE_CPUSTOP_ON_DDBBREAK) |
| 171 | db_printf("\nCPU%d restarting CPUs: 0x%08x\n", cpuid, stopped_cpus); |
| 172 | #endif /* VERBOSE_CPUSTOP_ON_DDBBREAK */ |
| 173 | |
| 174 | /* Restart all the CPUs we previously stopped */ |
| 175 | if (stopped_cpus != mycpu->gd_other_cpus && smp_started != 0) { |
| 176 | db_printf("whoa, other_cpus: 0x%08x, stopped_cpus: 0x%08x\n", |
| 177 | mycpu->gd_other_cpus, stopped_cpus); |
| 178 | panic("stop_cpus() failed"); |
| 179 | } |
| 180 | restart_cpus(stopped_cpus); |
| 181 | |
| 182 | #if defined(VERBOSE_CPUSTOP_ON_DDBBREAK) |
| 183 | db_printf(" restarted\n"); |
| 184 | #endif /* VERBOSE_CPUSTOP_ON_DDBBREAK */ |
| 185 | |
| 186 | #endif /* CPUSTOP_ON_DDBBREAK */ |
| 187 | #endif /* SMP */ |
| 188 | |
| 189 | regs->tf_eip = ddb_regs.tf_eip; |
| 190 | regs->tf_eflags = ddb_regs.tf_eflags; |
| 191 | regs->tf_eax = ddb_regs.tf_eax; |
| 192 | regs->tf_ecx = ddb_regs.tf_ecx; |
| 193 | regs->tf_edx = ddb_regs.tf_edx; |
| 194 | regs->tf_ebx = ddb_regs.tf_ebx; |
| 195 | |
| 196 | /* |
| 197 | * If in user mode, the saved ESP and SS were valid, restore them. |
| 198 | */ |
| 199 | if (ISPL(regs->tf_cs)) { |
| 200 | regs->tf_esp = ddb_regs.tf_esp; |
| 201 | regs->tf_ss = ddb_regs.tf_ss & 0xffff; |
| 202 | } |
| 203 | |
| 204 | regs->tf_ebp = ddb_regs.tf_ebp; |
| 205 | regs->tf_esi = ddb_regs.tf_esi; |
| 206 | regs->tf_edi = ddb_regs.tf_edi; |
| 207 | regs->tf_es = ddb_regs.tf_es & 0xffff; |
| 208 | regs->tf_fs = ddb_regs.tf_fs & 0xffff; |
| 209 | regs->tf_cs = ddb_regs.tf_cs & 0xffff; |
| 210 | regs->tf_ds = ddb_regs.tf_ds & 0xffff; |
| 211 | return (1); |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | * Read bytes from kernel address space for debugger. |
| 216 | */ |
| 217 | void |
| 218 | db_read_bytes(addr, size, data) |
| 219 | vm_offset_t addr; |
| 220 | register size_t size; |
| 221 | register char *data; |
| 222 | { |
| 223 | register char *src; |
| 224 | |
| 225 | db_nofault = &db_jmpbuf; |
| 226 | |
| 227 | src = (char *)addr; |
| 228 | while (size-- > 0) |
| 229 | *data++ = *src++; |
| 230 | |
| 231 | db_nofault = 0; |
| 232 | } |
| 233 | |
| 234 | /* |
| 235 | * Write bytes to kernel address space for debugger. |
| 236 | */ |
| 237 | void |
| 238 | db_write_bytes(addr, size, data) |
| 239 | vm_offset_t addr; |
| 240 | register size_t size; |
| 241 | register char *data; |
| 242 | { |
| 243 | register char *dst; |
| 244 | |
| 245 | unsigned *ptep0 = NULL; |
| 246 | unsigned oldmap0 = 0; |
| 247 | vm_offset_t addr1; |
| 248 | unsigned *ptep1 = NULL; |
| 249 | unsigned oldmap1 = 0; |
| 250 | |
| 251 | db_nofault = &db_jmpbuf; |
| 252 | |
| 253 | if (addr > trunc_page((vm_offset_t)btext) - size && |
| 254 | addr < round_page((vm_offset_t)etext)) { |
| 255 | |
| 256 | ptep0 = pmap_pte(kernel_pmap, addr); |
| 257 | oldmap0 = *ptep0; |
| 258 | *ptep0 |= PG_RW; |
| 259 | |
| 260 | /* Map another page if the data crosses a page boundary. */ |
| 261 | if ((*ptep0 & PG_PS) == 0) { |
| 262 | addr1 = trunc_page(addr + size - 1); |
| 263 | if (trunc_page(addr) != addr1) { |
| 264 | ptep1 = pmap_pte(kernel_pmap, addr1); |
| 265 | oldmap1 = *ptep1; |
| 266 | *ptep1 |= PG_RW; |
| 267 | } |
| 268 | } else { |
| 269 | addr1 = trunc_4mpage(addr + size - 1); |
| 270 | if (trunc_4mpage(addr) != addr1) { |
| 271 | ptep1 = pmap_pte(kernel_pmap, addr1); |
| 272 | oldmap1 = *ptep1; |
| 273 | *ptep1 |= PG_RW; |
| 274 | } |
| 275 | } |
| 276 | |
| 277 | invltlb(); |
| 278 | } |
| 279 | |
| 280 | dst = (char *)addr; |
| 281 | |
| 282 | while (size-- > 0) |
| 283 | *dst++ = *data++; |
| 284 | |
| 285 | db_nofault = 0; |
| 286 | |
| 287 | if (ptep0) { |
| 288 | *ptep0 = oldmap0; |
| 289 | |
| 290 | if (ptep1) |
| 291 | *ptep1 = oldmap1; |
| 292 | |
| 293 | invltlb(); |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /* |
| 298 | * XXX |
| 299 | * Move this to machdep.c and allow it to be called if any debugger is |
| 300 | * installed. |
| 301 | */ |
| 302 | void |
| 303 | Debugger(msg) |
| 304 | const char *msg; |
| 305 | { |
| 306 | static volatile u_char in_Debugger; |
| 307 | |
| 308 | /* |
| 309 | * XXX |
| 310 | * Do nothing if the console is in graphics mode. This is |
| 311 | * OK if the call is for the debugger hotkey but not if the call |
| 312 | * is a weak form of panicing. |
| 313 | */ |
| 314 | if (cons_unavail && !(boothowto & RB_GDB)) |
| 315 | return; |
| 316 | |
| 317 | if (!in_Debugger) { |
| 318 | in_Debugger = 1; |
| 319 | db_printf("Debugger(\"%s\")\n", msg); |
| 320 | breakpoint(); |
| 321 | in_Debugger = 0; |
| 322 | } |
| 323 | } |