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