blob: 46f8f9d0c4089085c2c3888e0c63afa2cbbb0e09 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/arch/m68knommu/kernel/traps.c
3 *
4 * Copyright (C) 1993, 1994 by Hamish Macdonald
5 *
6 * 68040 fixes by Michael Rausch
7 * 68040 fixes by Martin Apel
8 * 68060 fixes by Roman Hodek
9 * 68060 fixes by Jesper Skov
10 *
11 * This file is subject to the terms and conditions of the GNU General Public
12 * License. See the file COPYING in the main directory of this archive
13 * for more details.
14 */
15
16/*
17 * Sets up all exception vectors
18 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/sched.h>
20#include <linux/signal.h>
21#include <linux/kernel.h>
22#include <linux/mm.h>
Greg Ungererdb81fb82005-09-02 10:42:52 +100023#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024#include <linux/types.h>
25#include <linux/a.out.h>
26#include <linux/user.h>
27#include <linux/string.h>
28#include <linux/linkage.h>
29#include <linux/init.h>
30#include <linux/ptrace.h>
Sebastian Siewiorf6054e22008-05-01 12:16:38 +100031#include <linux/kallsyms.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
33#include <asm/setup.h>
34#include <asm/fpu.h>
35#include <asm/system.h>
36#include <asm/uaccess.h>
37#include <asm/traps.h>
38#include <asm/pgtable.h>
39#include <asm/machdep.h>
40#include <asm/siginfo.h>
41
Greg Ungererdb81fb82005-09-02 10:42:52 +100042static char const * const vec_names[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070043 "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
44 "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
45 "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
46 "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
47 "FORMAT ERROR", "UNINITIALIZED INTERRUPT",
48 "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
49 "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
50 "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
51 "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
52 "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
53 "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
54 "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
55 "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
56 "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
57 "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15",
58 "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW",
59 "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN",
60 "FPCP UNSUPPORTED OPERATION",
61 "MMU CONFIGURATION ERROR"
62};
63
64void __init trap_init(void)
65{
Linus Torvalds1da177e2005-04-16 15:20:36 -070066}
67
68void die_if_kernel(char *str, struct pt_regs *fp, int nr)
69{
70 if (!(fp->sr & PS_S))
71 return;
72
73 console_verbose();
74 printk(KERN_EMERG "%s: %08x\n",str,nr);
75 printk(KERN_EMERG "PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n",
76 fp->pc, fp->sr, fp, fp->a2);
77 printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
78 fp->d0, fp->d1, fp->d2, fp->d3);
79 printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
80 fp->d4, fp->d5, fp->a0, fp->a1);
81
82 printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n",
83 current->comm, current->pid, PAGE_SIZE+(unsigned long)current);
Greg Ungerera4c8b912007-07-19 01:49:14 -070084 show_stack(NULL, (unsigned long *)(fp + 1));
Pavel Emelianovbcdcd8e2007-07-17 04:03:42 -070085 add_taint(TAINT_DIE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 do_exit(SIGSEGV);
87}
88
89asmlinkage void buserr_c(struct frame *fp)
90{
91 /* Only set esp0 if coming from user mode */
92 if (user_mode(&fp->ptregs))
93 current->thread.esp0 = (unsigned long) fp;
94
Greg Ungererbb286322006-06-26 10:33:10 +100095#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 printk (KERN_DEBUG "*** Bus Error *** Format is %x\n", fp->ptregs.format);
97#endif
98
99 die_if_kernel("bad frame format",&fp->ptregs,0);
Greg Ungererbb286322006-06-26 10:33:10 +1000100#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 printk(KERN_DEBUG "Unknown SIGSEGV - 4\n");
102#endif
103 force_sig(SIGSEGV, current);
104}
105
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200106static void print_this_address(unsigned long addr, int i)
107{
108#ifdef CONFIG_KALLSYMS
109 printk(KERN_EMERG " [%08lx] ", addr);
110 print_symbol(KERN_CONT "%s\n", addr);
111#else
112 if (i % 5)
113 printk(KERN_CONT " [%08lx] ", addr);
114 else
115 printk(KERN_CONT "\n" KERN_EMERG " [%08lx] ", addr);
116 i++;
117#endif
118}
119
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120int kstack_depth_to_print = 48;
121
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000122static void __show_stack(struct task_struct *task, unsigned long *stack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
Greg Ungererdb81fb82005-09-02 10:42:52 +1000124 unsigned long *endstack, addr;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200125#ifdef CONFIG_FRAME_POINTER
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000126 unsigned long *last_stack;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200127#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128 int i;
129
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000130 if (!stack)
131 stack = (unsigned long *)task->thread.ksp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
Greg Ungererdb81fb82005-09-02 10:42:52 +1000133 addr = (unsigned long) stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 endstack = (unsigned long *) PAGE_ALIGN(addr);
135
136 printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack);
137 for (i = 0; i < kstack_depth_to_print; i++) {
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000138 if (stack + 1 + i > endstack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139 break;
140 if (i % 8 == 0)
Greg Ungerer329237c2006-12-04 17:27:09 +1000141 printk("\n" KERN_EMERG " ");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000142 printk(" %08lx", *(stack + i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000144 printk("\n");
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200145 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000147#ifdef CONFIG_FRAME_POINTER
148 printk(KERN_EMERG "Call Trace:\n");
149
150 last_stack = stack - 1;
151 while (stack <= endstack && stack > last_stack) {
152
153 addr = *(stack + 1);
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200154 print_this_address(addr, i);
155 i++;
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000156
157 last_stack = stack;
158 stack = (unsigned long *)*stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000160 printk("\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000161#else
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200162 printk(KERN_EMERG "Call Trace with CONFIG_FRAME_POINTER disabled:\n");
163 while (stack <= endstack) {
164 addr = *stack++;
165 /*
166 * If the address is either in the text segment of the kernel,
167 * or in a region which is occupied by a module then it *may*
168 * be the address of a calling routine; if so, print it so that
169 * someone tracing down the cause of the crash will be able to
170 * figure out the call path that was taken.
171 */
172 if (__kernel_text_address(addr)) {
173 print_this_address(addr, i);
174 i++;
175 }
176 }
177 printk(KERN_CONT "\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000178#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179}
180
181void bad_super_trap(struct frame *fp)
182{
183 console_verbose();
Ahmed S. Darwishbf0059b2007-02-10 01:43:46 -0800184 if (fp->ptregs.vector < 4 * ARRAY_SIZE(vec_names))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 printk (KERN_WARNING "*** %s *** FORMAT=%X\n",
186 vec_names[(fp->ptregs.vector) >> 2],
187 fp->ptregs.format);
188 else
189 printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n",
190 (fp->ptregs.vector) >> 2,
191 fp->ptregs.format);
192 printk (KERN_WARNING "Current process id is %d\n", current->pid);
193 die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0);
194}
195
196asmlinkage void trap_c(struct frame *fp)
197{
198 int sig;
199 siginfo_t info;
200
201 if (fp->ptregs.sr & PS_S) {
202 if ((fp->ptregs.vector >> 2) == VEC_TRACE) {
203 /* traced a trapping instruction */
204 current->ptrace |= PT_DTRACE;
205 } else
206 bad_super_trap(fp);
207 return;
208 }
209
210 /* send the appropriate signal to the user program */
211 switch ((fp->ptregs.vector) >> 2) {
212 case VEC_ADDRERR:
213 info.si_code = BUS_ADRALN;
214 sig = SIGBUS;
215 break;
216 case VEC_ILLEGAL:
217 case VEC_LINE10:
218 case VEC_LINE11:
219 info.si_code = ILL_ILLOPC;
220 sig = SIGILL;
221 break;
222 case VEC_PRIV:
223 info.si_code = ILL_PRVOPC;
224 sig = SIGILL;
225 break;
226 case VEC_COPROC:
227 info.si_code = ILL_COPROC;
228 sig = SIGILL;
229 break;
230 case VEC_TRAP1: /* gdbserver breakpoint */
231 fp->ptregs.pc -= 2;
232 info.si_code = TRAP_TRACE;
233 sig = SIGTRAP;
234 break;
235 case VEC_TRAP2:
236 case VEC_TRAP3:
237 case VEC_TRAP4:
238 case VEC_TRAP5:
239 case VEC_TRAP6:
240 case VEC_TRAP7:
241 case VEC_TRAP8:
242 case VEC_TRAP9:
243 case VEC_TRAP10:
244 case VEC_TRAP11:
245 case VEC_TRAP12:
246 case VEC_TRAP13:
247 case VEC_TRAP14:
248 info.si_code = ILL_ILLTRP;
249 sig = SIGILL;
250 break;
251 case VEC_FPBRUC:
252 case VEC_FPOE:
253 case VEC_FPNAN:
254 info.si_code = FPE_FLTINV;
255 sig = SIGFPE;
256 break;
257 case VEC_FPIR:
258 info.si_code = FPE_FLTRES;
259 sig = SIGFPE;
260 break;
261 case VEC_FPDIVZ:
262 info.si_code = FPE_FLTDIV;
263 sig = SIGFPE;
264 break;
265 case VEC_FPUNDER:
266 info.si_code = FPE_FLTUND;
267 sig = SIGFPE;
268 break;
269 case VEC_FPOVER:
270 info.si_code = FPE_FLTOVF;
271 sig = SIGFPE;
272 break;
273 case VEC_ZERODIV:
274 info.si_code = FPE_INTDIV;
275 sig = SIGFPE;
276 break;
277 case VEC_CHK:
278 case VEC_TRAP:
279 info.si_code = FPE_INTOVF;
280 sig = SIGFPE;
281 break;
282 case VEC_TRACE: /* ptrace single step */
283 info.si_code = TRAP_TRACE;
284 sig = SIGTRAP;
285 break;
286 case VEC_TRAP15: /* breakpoint */
287 info.si_code = TRAP_BRKPT;
288 sig = SIGTRAP;
289 break;
290 default:
291 info.si_code = ILL_ILLOPC;
292 sig = SIGILL;
293 break;
294 }
295 info.si_signo = sig;
296 info.si_errno = 0;
297 switch (fp->ptregs.format) {
298 default:
299 info.si_addr = (void *) fp->ptregs.pc;
300 break;
301 case 2:
302 info.si_addr = (void *) fp->un.fmt2.iaddr;
303 break;
304 case 7:
305 info.si_addr = (void *) fp->un.fmt7.effaddr;
306 break;
307 case 9:
308 info.si_addr = (void *) fp->un.fmt9.iaddr;
309 break;
310 case 10:
311 info.si_addr = (void *) fp->un.fmta.daddr;
312 break;
313 case 11:
314 info.si_addr = (void *) fp->un.fmtb.daddr;
315 break;
316 }
317 force_sig_info (sig, &info, current);
318}
319
320asmlinkage void set_esp0(unsigned long ssp)
321{
322 current->thread.esp0 = ssp;
323}
324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325/*
326 * The architecture-independent backtrace generator
327 */
328void dump_stack(void)
329{
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000330 /*
331 * We need frame pointers for this little trick, which works as follows:
332 *
333 * +------------+ 0x00
334 * | Next SP | -> 0x0c
335 * +------------+ 0x04
336 * | Caller |
337 * +------------+ 0x08
338 * | Local vars | -> our stack var
339 * +------------+ 0x0c
340 * | Next SP | -> 0x18, that is what we pass to show_stack()
341 * +------------+ 0x10
342 * | Caller |
343 * +------------+ 0x14
344 * | Local vars |
345 * +------------+ 0x18
346 * | ... |
347 * +------------+
348 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000350 unsigned long *stack;
351
352 stack = (unsigned long *)&stack;
353 stack++;
354 __show_stack(current, stack);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355}
Greg Ungererdb81fb82005-09-02 10:42:52 +1000356EXPORT_SYMBOL(dump_stack);
357
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000358void show_stack(struct task_struct *task, unsigned long *stack)
359{
360 if (!stack && !task)
361 dump_stack();
362 else
363 __show_stack(task, stack);
364}
365
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366#ifdef CONFIG_M68KFPU_EMU
367asmlinkage void fpemu_signal(int signal, int code, void *addr)
368{
369 siginfo_t info;
370
371 info.si_signo = signal;
372 info.si_errno = 0;
373 info.si_code = code;
374 info.si_addr = addr;
375 force_sig_info(signal, &info, current);
376}
377#endif