Initial import from FreeBSD RELENG_4:
[games.git] / sys / platform / pc32 / i386 / db_interface.c
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
50 static jmp_buf *db_nofault = 0;
51 extern jmp_buf  db_jmpbuf;
52
53 extern void     gdb_handle_exception __P((db_regs_t *, int, int));
54
55 int     db_active;
56 db_regs_t ddb_regs;
57
58 static jmp_buf  db_global_jmpbuf;
59 static 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  */
68 int
69 kdb_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  */
215 void
216 db_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  */
235 void
236 db_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  */
300 void
301 Debugger(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 }