2 * Originally written by Glenn Engel, Lake Stevens Instrument Division
4 * Contributed by HP Systems
6 * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse
7 * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de>
9 * Copyright (C) 1995 Andreas Busse
11 * Copyright (C) 2003 MontaVista Software Inc.
12 * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
14 * Copyright (C) 2004-2005 MontaVista Software Inc.
15 * Author: Manish Lachwani, mlachwani@mvista.com or manish@koffee-break.com
17 * Copyright (C) 2007-2008 Wind River Systems, Inc.
18 * Author/Maintainer: Jason Wessel, jason.wessel@windriver.com
20 * This file is licensed under the terms of the GNU General Public License
21 * version 2. This program is licensed "as is" without any warranty of any
22 * kind, whether express or implied.
25 #include <linux/ptrace.h> /* for linux pt_regs struct */
26 #include <linux/kgdb.h>
27 #include <linux/kdebug.h>
28 #include <linux/sched.h>
31 #include <asm/cacheflush.h>
32 #include <asm/processor.h>
33 #include <asm/sigcontext.h>
35 static struct hard_trap_info
{
36 unsigned char tt
; /* Trap type code for MIPS R3xxx and R4xxx */
37 unsigned char signo
; /* Signal that we map this trap into */
38 } hard_trap_info
[] = {
39 { 6, SIGBUS
}, /* instruction bus error */
40 { 7, SIGBUS
}, /* data bus error */
41 { 9, SIGTRAP
}, /* break */
42 /* { 11, SIGILL }, */ /* CPU unusable */
43 { 12, SIGFPE
}, /* overflow */
44 { 13, SIGTRAP
}, /* trap */
45 { 14, SIGSEGV
}, /* virtual instruction cache coherency */
46 { 15, SIGFPE
}, /* floating point exception */
47 { 23, SIGSEGV
}, /* watch */
48 { 31, SIGSEGV
}, /* virtual data cache coherency */
49 { 0, 0} /* Must be last */
52 void arch_kgdb_breakpoint(void)
55 ".globl breakinst\n\t"
58 "breakinst:\tbreak\n\t"
63 static void kgdb_call_nmi_hook(void *ignored
)
65 kgdb_nmicallback(raw_smp_processor_id(), NULL
);
68 void kgdb_roundup_cpus(unsigned long flags
)
71 smp_call_function(kgdb_call_nmi_hook
, NULL
, 0);
75 static int compute_signal(int tt
)
77 struct hard_trap_info
*ht
;
79 for (ht
= hard_trap_info
; ht
->tt
&& ht
->signo
; ht
++)
83 return SIGHUP
; /* default for things we don't know about */
86 void pt_regs_to_gdb_regs(unsigned long *gdb_regs
, struct pt_regs
*regs
)
90 #if (KGDB_GDB_REG_SIZE == 32)
91 u32
*ptr
= (u32
*)gdb_regs
;
93 u64
*ptr
= (u64
*)gdb_regs
;
96 for (reg
= 0; reg
< 32; reg
++)
97 *(ptr
++) = regs
->regs
[reg
];
99 *(ptr
++) = regs
->cp0_status
;
102 *(ptr
++) = regs
->cp0_badvaddr
;
103 *(ptr
++) = regs
->cp0_cause
;
104 *(ptr
++) = regs
->cp0_epc
;
107 if (!(current
&& (regs
->cp0_status
& ST0_CU1
)))
111 for (reg
= 0; reg
< 32; reg
++)
112 *(ptr
++) = current
->thread
.fpu
.fpr
[reg
];
115 void gdb_regs_to_pt_regs(unsigned long *gdb_regs
, struct pt_regs
*regs
)
119 #if (KGDB_GDB_REG_SIZE == 32)
120 const u32
*ptr
= (u32
*)gdb_regs
;
122 const u64
*ptr
= (u64
*)gdb_regs
;
125 for (reg
= 0; reg
< 32; reg
++)
126 regs
->regs
[reg
] = *(ptr
++);
128 regs
->cp0_status
= *(ptr
++);
131 regs
->cp0_badvaddr
= *(ptr
++);
132 regs
->cp0_cause
= *(ptr
++);
133 regs
->cp0_epc
= *(ptr
++);
135 /* FP REGS from current */
136 if (!(current
&& (regs
->cp0_status
& ST0_CU1
)))
139 for (reg
= 0; reg
< 32; reg
++)
140 current
->thread
.fpu
.fpr
[reg
] = *(ptr
++);
145 * Similar to regs_to_gdb_regs() except that process is sleeping and so
146 * we may not be able to get all the info.
148 void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs
, struct task_struct
*p
)
151 struct thread_info
*ti
= task_thread_info(p
);
152 unsigned long ksp
= (unsigned long)ti
+ THREAD_SIZE
- 32;
153 struct pt_regs
*regs
= (struct pt_regs
*)ksp
- 1;
154 #if (KGDB_GDB_REG_SIZE == 32)
155 u32
*ptr
= (u32
*)gdb_regs
;
157 u64
*ptr
= (u64
*)gdb_regs
;
160 for (reg
= 0; reg
< 16; reg
++)
161 *(ptr
++) = regs
->regs
[reg
];
164 for (reg
= 16; reg
< 24; reg
++)
165 *(ptr
++) = regs
->regs
[reg
];
167 for (reg
= 24; reg
< 28; reg
++)
171 for (reg
= 28; reg
< 32; reg
++)
172 *(ptr
++) = regs
->regs
[reg
];
174 *(ptr
++) = regs
->cp0_status
;
177 *(ptr
++) = regs
->cp0_badvaddr
;
178 *(ptr
++) = regs
->cp0_cause
;
179 *(ptr
++) = regs
->cp0_epc
;
183 * Calls linux_debug_hook before the kernel dies. If KGDB is enabled,
184 * then try to fall into the debugger
186 static int kgdb_mips_notify(struct notifier_block
*self
, unsigned long cmd
,
189 struct die_args
*args
= (struct die_args
*)ptr
;
190 struct pt_regs
*regs
= args
->regs
;
191 int trap
= (regs
->cp0_cause
& 0x7c) >> 2;
193 /* Userpace events, ignore. */
197 if (atomic_read(&kgdb_active
) != -1)
198 kgdb_nmicallback(smp_processor_id(), regs
);
200 if (kgdb_handle_exception(trap
, compute_signal(trap
), 0, regs
))
203 if (atomic_read(&kgdb_setting_breakpoint
))
204 if ((trap
== 9) && (regs
->cp0_epc
== (unsigned long)breakinst
))
207 /* In SMP mode, __flush_cache_all does IPI */
214 static struct notifier_block kgdb_notifier
= {
215 .notifier_call
= kgdb_mips_notify
,
219 * Handle the 's' and 'c' commands
221 int kgdb_arch_handle_exception(int vector
, int signo
, int err_code
,
222 char *remcom_in_buffer
, char *remcom_out_buffer
,
223 struct pt_regs
*regs
)
226 unsigned long address
;
227 int cpu
= smp_processor_id();
229 switch (remcom_in_buffer
[0]) {
232 /* handle the optional parameter */
233 ptr
= &remcom_in_buffer
[1];
234 if (kgdb_hex2long(&ptr
, &address
))
235 regs
->cp0_epc
= address
;
237 atomic_set(&kgdb_cpu_doing_single_step
, -1);
238 if (remcom_in_buffer
[0] == 's')
239 atomic_set(&kgdb_cpu_doing_single_step
, cpu
);
247 struct kgdb_arch arch_kgdb_ops
;
250 * We use kgdb_early_setup so that functions we need to call now don't
251 * cause trouble when called again later.
253 int kgdb_arch_init(void)
255 union mips_instruction insn
= {
261 memcpy(arch_kgdb_ops
.gdb_bpt_instr
, insn
.byte
, BREAK_INSTR_SIZE
);
263 register_die_notifier(&kgdb_notifier
);
269 * kgdb_arch_exit - Perform any architecture specific uninitalization.
271 * This function will handle the uninitalization of any architecture
272 * specific callbacks, for dynamic registration and unregistration.
274 void kgdb_arch_exit(void)
276 unregister_die_notifier(&kgdb_notifier
);