| Commit | Line | Data |
|---|---|---|
| d7f50089 | 1 | /* |
| c8fe38ae MD |
2 | * Copyright (c) 2008 The DragonFly Project. All rights reserved. |
| 3 | * | |
| 4 | * Redistribution and use in source and binary forms, with or without | |
| 5 | * modification, are permitted provided that the following conditions | |
| 6 | * are met: | |
| 7 | * | |
| 8 | * 1. Redistributions of source code must retain the above copyright | |
| 9 | * notice, this list of conditions and the following disclaimer. | |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 11 | * notice, this list of conditions and the following disclaimer in | |
| 12 | * the documentation and/or other materials provided with the | |
| 13 | * distribution. | |
| 14 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 15 | * contributors may be used to endorse or promote products derived | |
| 16 | * from this software without specific, prior written permission. | |
| 17 | * | |
| 18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 22 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 23 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 26 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 27 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 28 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 29 | * SUCH DAMAGE. | |
| 30 | * | |
| 31 | * -- | |
| 32 | * | |
| d7f50089 YY |
33 | * Mach Operating System |
| 34 | * Copyright (c) 1991,1990 Carnegie Mellon University | |
| 35 | * All Rights Reserved. | |
| 36 | * | |
| 37 | * Permission to use, copy, modify and distribute this software and its | |
| 38 | * documentation is hereby granted, provided that both the copyright | |
| 39 | * notice and this permission notice appear in all copies of the | |
| 40 | * software, derivative works or modified versions, and any portions | |
| 41 | * thereof, and that both notices appear in supporting documentation. | |
| 42 | * | |
| 43 | * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS | |
| 44 | * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR | |
| 45 | * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. | |
| 46 | * | |
| 47 | * Carnegie Mellon requests users of this software to return to | |
| 48 | * | |
| 49 | * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU | |
| 50 | * School of Computer Science | |
| 51 | * Carnegie Mellon University | |
| 52 | * Pittsburgh PA 15213-3890 | |
| 53 | * | |
| 54 | * any improvements or extensions that they make and grant Carnegie the | |
| 55 | * rights to redistribute these changes. | |
| 56 | * | |
| 57 | * $FreeBSD: src/sys/i386/i386/db_interface.c,v 1.48.2.1 2000/07/07 00:38:46 obrien Exp $ | |
| d7f50089 YY |
58 | */ |
| 59 | ||
| 60 | /* | |
| 61 | * Interface to new debugger. | |
| 62 | */ | |
| 63 | #include <sys/param.h> | |
| 64 | #include <sys/systm.h> | |
| 65 | #include <sys/reboot.h> | |
| 66 | #include <sys/cons.h> | |
| 33bb509e | 67 | #include <sys/thread.h> |
| d7f50089 YY |
68 | |
| 69 | #include <machine/cpu.h> | |
| 70 | #include <machine/smp.h> | |
| 71 | #include <machine/globaldata.h> | |
| 72 | #include <machine/md_var.h> | |
| 73 | ||
| 74 | #include <vm/vm.h> | |
| 75 | #include <vm/pmap.h> | |
| 76 | ||
| 77 | #include <ddb/ddb.h> | |
| 78 | ||
| 33bb509e MD |
79 | #include <sys/thread2.h> |
| 80 | ||
| d7f50089 YY |
81 | #include <setjmp.h> |
| 82 | ||
| 83 | static jmp_buf *db_nofault = 0; | |
| 84 | extern jmp_buf db_jmpbuf; | |
| 85 | ||
| c8fe38ae MD |
86 | extern void gdb_handle_exception (db_regs_t *, int, int); |
| 87 | ||
| d7f50089 YY |
88 | int db_active; |
| 89 | db_regs_t ddb_regs; | |
| 90 | ||
| 91 | static jmp_buf db_global_jmpbuf; | |
| 92 | static int db_global_jmpbuf_valid; | |
| 93 | ||
| 94 | #ifdef __GNUC__ | |
| 95 | #define rss() ({u_short ss; __asm __volatile("mov %%ss,%0" : "=r" (ss)); ss;}) | |
| 96 | #endif | |
| 97 | ||
| 98 | /* | |
| 99 | * kdb_trap - field a TRACE or BPT trap | |
| 100 | */ | |
| 101 | int | |
| c1543a89 | 102 | kdb_trap(int type, int code, struct x86_64_saved_state *regs) |
| d7f50089 YY |
103 | { |
| 104 | volatile int ddb_mode = !(boothowto & RB_GDB); | |
| 105 | ||
| 106 | /* | |
| 107 | * XXX try to do nothing if the console is in graphics mode. | |
| 108 | * Handle trace traps (and hardware breakpoints...) by ignoring | |
| 109 | * them except for forgetting about them. Return 0 for other | |
| 110 | * traps to say that we haven't done anything. The trap handler | |
| 111 | * will usually panic. We should handle breakpoint traps for | |
| 112 | * our breakpoints by disarming our breakpoints and fixing up | |
| 113 | * %eip. | |
| 114 | */ | |
| 115 | if (cons_unavail && ddb_mode) { | |
| 116 | if (type == T_TRCTRAP) { | |
| 117 | regs->tf_rflags &= ~PSL_T; | |
| 118 | return (1); | |
| 119 | } | |
| 120 | return (0); | |
| 121 | } | |
| 122 | ||
| 123 | switch (type) { | |
| 124 | case T_BPTFLT: /* breakpoint */ | |
| 125 | case T_TRCTRAP: /* debug exception */ | |
| 126 | break; | |
| 127 | ||
| 128 | default: | |
| 129 | /* | |
| 130 | * XXX this is almost useless now. In most cases, | |
| 131 | * trap_fatal() has already printed a much more verbose | |
| 132 | * message. However, it is dangerous to print things in | |
| 133 | * trap_fatal() - kprintf() might be reentered and trap. | |
| 134 | * The debugger should be given control first. | |
| 135 | */ | |
| 136 | if (ddb_mode) | |
| 137 | db_printf("kernel: type %d trap, code=%x\n", type, code); | |
| 138 | ||
| 139 | if (db_nofault) { | |
| 140 | jmp_buf *no_fault = db_nofault; | |
| 141 | db_nofault = 0; | |
| 142 | longjmp(*no_fault, 1); | |
| 143 | } | |
| 144 | } | |
| 145 | ||
| 146 | /* | |
| 147 | * This handles unexpected traps in ddb commands, including calls to | |
| 148 | * non-ddb functions. db_nofault only applies to memory accesses by | |
| 149 | * internal ddb commands. | |
| 150 | */ | |
| 151 | if (db_global_jmpbuf_valid) | |
| 152 | longjmp(db_global_jmpbuf, 1); | |
| 153 | ||
| 154 | /* | |
| 155 | * XXX We really should switch to a local stack here. | |
| 156 | */ | |
| 157 | ddb_regs = *regs; | |
| 158 | ||
| 33bb509e | 159 | crit_enter(); |
| d7f50089 YY |
160 | #ifdef SMP |
| 161 | db_printf("\nCPU%d stopping CPUs: 0x%08x\n", | |
| 162 | mycpu->gd_cpuid, mycpu->gd_other_cpus); | |
| 163 | ||
| 164 | /* We stop all CPUs except ourselves (obviously) */ | |
| 165 | stop_cpus(mycpu->gd_other_cpus); | |
| 166 | ||
| 167 | db_printf(" stopped\n"); | |
| 168 | #endif /* SMP */ | |
| 169 | ||
| 170 | setjmp(db_global_jmpbuf); | |
| 171 | db_global_jmpbuf_valid = TRUE; | |
| 172 | db_active++; | |
| 173 | /* vcons_set_mode(1); */ | |
| 174 | if (ddb_mode) { | |
| 175 | cndbctl(TRUE); | |
| 176 | db_trap(type, code); | |
| 177 | cndbctl(FALSE); | |
| 178 | } else | |
| c8fe38ae | 179 | gdb_handle_exception(&ddb_regs, type, code); |
| d7f50089 YY |
180 | db_active--; |
| 181 | /* vcons_set_mode(0); */ | |
| 182 | db_global_jmpbuf_valid = FALSE; | |
| 183 | ||
| 184 | #ifdef SMP | |
| 185 | db_printf("\nCPU%d restarting CPUs: 0x%08x\n", | |
| 186 | mycpu->gd_cpuid, stopped_cpus); | |
| 187 | ||
| 188 | /* Restart all the CPUs we previously stopped */ | |
| 189 | if (stopped_cpus != mycpu->gd_other_cpus) { | |
| 190 | db_printf("whoa, other_cpus: 0x%08x, stopped_cpus: 0x%08x\n", | |
| 191 | mycpu->gd_other_cpus, stopped_cpus); | |
| 192 | panic("stop_cpus() failed"); | |
| 193 | } | |
| 194 | restart_cpus(stopped_cpus); | |
| 195 | ||
| 196 | db_printf(" restarted\n"); | |
| 197 | #endif /* SMP */ | |
| 33bb509e | 198 | crit_exit(); |
| d7f50089 YY |
199 | |
| 200 | regs->tf_rip = ddb_regs.tf_rip; | |
| 201 | regs->tf_rflags = ddb_regs.tf_rflags; | |
| 202 | regs->tf_rax = ddb_regs.tf_rax; | |
| 203 | regs->tf_rcx = ddb_regs.tf_rcx; | |
| 204 | regs->tf_rdx = ddb_regs.tf_rdx; | |
| 205 | regs->tf_rbx = ddb_regs.tf_rbx; | |
| 206 | ||
| c8fe38ae MD |
207 | regs->tf_rsp = ddb_regs.tf_rsp; |
| 208 | regs->tf_ss = ddb_regs.tf_ss & 0xffff; | |
| d7f50089 YY |
209 | |
| 210 | regs->tf_rbp = ddb_regs.tf_rbp; | |
| 211 | regs->tf_rsi = ddb_regs.tf_rsi; | |
| 212 | regs->tf_rdi = ddb_regs.tf_rdi; | |
| c8fe38ae MD |
213 | |
| 214 | regs->tf_r8 = ddb_regs.tf_r8; | |
| 215 | regs->tf_r9 = ddb_regs.tf_r9; | |
| 216 | regs->tf_r10 = ddb_regs.tf_r10; | |
| 217 | regs->tf_r11 = ddb_regs.tf_r11; | |
| 218 | regs->tf_r12 = ddb_regs.tf_r12; | |
| 219 | regs->tf_r13 = ddb_regs.tf_r13; | |
| 220 | regs->tf_r14 = ddb_regs.tf_r14; | |
| 221 | regs->tf_r15 = ddb_regs.tf_r15; | |
| 222 | ||
| d7f50089 YY |
223 | /* regs->tf_es = ddb_regs.tf_es & 0xffff; */ |
| 224 | /* regs->tf_fs = ddb_regs.tf_fs & 0xffff; */ | |
| 225 | /* regs->tf_gs = ddb_regs.tf_gs & 0xffff; */ | |
| 226 | regs->tf_cs = ddb_regs.tf_cs & 0xffff; | |
| 227 | /* regs->tf_ds = ddb_regs.tf_ds & 0xffff; */ | |
| 228 | return (1); | |
| 229 | } | |
| 230 | ||
| 231 | /* | |
| 232 | * Read bytes from kernel address space for debugger. | |
| 233 | */ | |
| 234 | void | |
| 235 | db_read_bytes(vm_offset_t addr, size_t size, char *data) | |
| 236 | { | |
| 237 | char *src; | |
| 238 | ||
| 239 | db_nofault = &db_jmpbuf; | |
| 240 | ||
| 241 | src = (char *)addr; | |
| 242 | while (size-- > 0) | |
| 243 | *data++ = *src++; | |
| 244 | ||
| 245 | db_nofault = 0; | |
| 246 | } | |
| 247 | ||
| 248 | /* | |
| 249 | * Write bytes to kernel address space for debugger. | |
| 250 | */ | |
| 251 | void | |
| 252 | db_write_bytes(vm_offset_t addr, size_t size, char *data) | |
| 253 | { | |
| 254 | char *dst; | |
| 255 | #if 0 | |
| 256 | vpte_t *ptep0 = NULL; | |
| 257 | vpte_t oldmap0 = 0; | |
| 258 | vm_offset_t addr1; | |
| 259 | vpte_t *ptep1 = NULL; | |
| 260 | vpte_t oldmap1 = 0; | |
| 261 | #endif | |
| 262 | ||
| 263 | db_nofault = &db_jmpbuf; | |
| 264 | #if 0 | |
| 265 | if (addr > trunc_page((vm_offset_t)btext) - size && | |
| 266 | addr < round_page((vm_offset_t)etext)) { | |
| 267 | ||
| 268 | ptep0 = pmap_kpte(addr); | |
| 269 | oldmap0 = *ptep0; | |
| 270 | *ptep0 |= VPTE_W; | |
| 271 | ||
| 272 | /* Map another page if the data crosses a page boundary. */ | |
| 273 | if ((*ptep0 & PG_PS) == 0) { | |
| 274 | addr1 = trunc_page(addr + size - 1); | |
| 275 | if (trunc_page(addr) != addr1) { | |
| 276 | ptep1 = pmap_kpte(addr1); | |
| 277 | oldmap1 = *ptep1; | |
| 278 | *ptep1 |= VPTE_W; | |
| 279 | } | |
| 280 | } else { | |
| 281 | addr1 = trunc_4mpage(addr + size - 1); | |
| 282 | if (trunc_4mpage(addr) != addr1) { | |
| 283 | ptep1 = pmap_kpte(addr1); | |
| 284 | oldmap1 = *ptep1; | |
| 285 | *ptep1 |= VPTE_W; | |
| 286 | } | |
| 287 | } | |
| 288 | ||
| 289 | cpu_invltlb(); | |
| 290 | } | |
| 291 | #endif | |
| 292 | ||
| 293 | dst = (char *)addr; | |
| 294 | ||
| 295 | while (size-- > 0) | |
| 296 | *dst++ = *data++; | |
| 297 | ||
| 298 | db_nofault = 0; | |
| 299 | ||
| 300 | #if 0 | |
| 301 | if (ptep0) { | |
| 302 | *ptep0 = oldmap0; | |
| 303 | ||
| 304 | if (ptep1) | |
| 305 | *ptep1 = oldmap1; | |
| 306 | ||
| 307 | cpu_invltlb(); | |
| 308 | } | |
| 309 | #endif | |
| 310 | } | |
| 311 | ||
| 312 | /* | |
| 313 | * The debugger sometimes needs to know the actual KVM address represented | |
| 314 | * by the instruction pointer, stack pointer, or base pointer. Normally | |
| 315 | * the actual KVM address is simply the contents of the register. However, | |
| 316 | * if the debugger is entered from the BIOS or VM86 we need to figure out | |
| 317 | * the offset from the segment register. | |
| 318 | */ | |
| 319 | db_addr_t | |
| 320 | PC_REGS(db_regs_t *regs) | |
| 321 | { | |
| 322 | return(regs->tf_rip); | |
| 323 | } | |
| 324 | ||
| 325 | db_addr_t | |
| 326 | SP_REGS(db_regs_t *regs) | |
| 327 | { | |
| 328 | return(regs->tf_rsp); | |
| 329 | } | |
| 330 | ||
| 331 | db_addr_t | |
| 332 | BP_REGS(db_regs_t *regs) | |
| 333 | { | |
| 334 | return(regs->tf_rbp); | |
| 335 | } | |
| 336 | ||
| 337 | /* | |
| 338 | * XXX | |
| 339 | * Move this to machdep.c and allow it to be called if any debugger is | |
| 340 | * installed. | |
| 341 | */ | |
| 342 | void | |
| 343 | Debugger(const char *msg) | |
| 344 | { | |
| 345 | static volatile u_char in_Debugger; | |
| 346 | ||
| 347 | /* | |
| 348 | * XXX | |
| 349 | * Do nothing if the console is in graphics mode. This is | |
| 350 | * OK if the call is for the debugger hotkey but not if the call | |
| 351 | * is a weak form of panicing. | |
| 352 | */ | |
| 353 | if (cons_unavail && !(boothowto & RB_GDB)) | |
| 354 | return; | |
| 355 | ||
| 356 | if (!in_Debugger) { | |
| 357 | in_Debugger = 1; | |
| 358 | db_printf("Debugger(\"%s\")\n", msg); | |
| 359 | breakpoint(); | |
| 360 | in_Debugger = 0; | |
| 361 | } | |
| 362 | } |