blob: 5d5d56bcd0efd5121ff2965f83a6e200b4726866 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#include <linux/user.h>
26#include <linux/string.h>
27#include <linux/linkage.h>
28#include <linux/init.h>
29#include <linux/ptrace.h>
Sebastian Siewiorf6054e22008-05-01 12:16:38 +100030#include <linux/kallsyms.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#include <asm/setup.h>
33#include <asm/fpu.h>
34#include <asm/system.h>
35#include <asm/uaccess.h>
36#include <asm/traps.h>
37#include <asm/pgtable.h>
38#include <asm/machdep.h>
39#include <asm/siginfo.h>
40
Greg Ungererdb81fb82005-09-02 10:42:52 +100041static char const * const vec_names[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -070042 "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
43 "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
44 "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
45 "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
46 "FORMAT ERROR", "UNINITIALIZED INTERRUPT",
47 "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
48 "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
49 "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
50 "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
51 "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
52 "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
53 "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
54 "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
55 "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
56 "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15",
57 "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW",
58 "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN",
59 "FPCP UNSUPPORTED OPERATION",
60 "MMU CONFIGURATION ERROR"
61};
62
63void __init trap_init(void)
64{
Linus Torvalds1da177e2005-04-16 15:20:36 -070065}
66
67void die_if_kernel(char *str, struct pt_regs *fp, int nr)
68{
69 if (!(fp->sr & PS_S))
70 return;
71
72 console_verbose();
73 printk(KERN_EMERG "%s: %08x\n",str,nr);
74 printk(KERN_EMERG "PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n",
75 fp->pc, fp->sr, fp, fp->a2);
76 printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
77 fp->d0, fp->d1, fp->d2, fp->d3);
78 printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
79 fp->d4, fp->d5, fp->a0, fp->a1);
80
81 printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n",
82 current->comm, current->pid, PAGE_SIZE+(unsigned long)current);
Greg Ungerera4c8b912007-07-19 01:49:14 -070083 show_stack(NULL, (unsigned long *)(fp + 1));
Pavel Emelianovbcdcd8e2007-07-17 04:03:42 -070084 add_taint(TAINT_DIE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 do_exit(SIGSEGV);
86}
87
88asmlinkage void buserr_c(struct frame *fp)
89{
90 /* Only set esp0 if coming from user mode */
91 if (user_mode(&fp->ptregs))
92 current->thread.esp0 = (unsigned long) fp;
93
Greg Ungererbb286322006-06-26 10:33:10 +100094#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -070095 printk (KERN_DEBUG "*** Bus Error *** Format is %x\n", fp->ptregs.format);
96#endif
97
98 die_if_kernel("bad frame format",&fp->ptregs,0);
Greg Ungererbb286322006-06-26 10:33:10 +100099#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 printk(KERN_DEBUG "Unknown SIGSEGV - 4\n");
101#endif
102 force_sig(SIGSEGV, current);
103}
104
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200105static void print_this_address(unsigned long addr, int i)
106{
107#ifdef CONFIG_KALLSYMS
108 printk(KERN_EMERG " [%08lx] ", addr);
109 print_symbol(KERN_CONT "%s\n", addr);
110#else
111 if (i % 5)
112 printk(KERN_CONT " [%08lx] ", addr);
113 else
114 printk(KERN_CONT "\n" KERN_EMERG " [%08lx] ", addr);
115 i++;
116#endif
117}
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119int kstack_depth_to_print = 48;
120
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000121static void __show_stack(struct task_struct *task, unsigned long *stack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122{
Greg Ungererdb81fb82005-09-02 10:42:52 +1000123 unsigned long *endstack, addr;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200124#ifdef CONFIG_FRAME_POINTER
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000125 unsigned long *last_stack;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200126#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 int i;
128
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000129 if (!stack)
130 stack = (unsigned long *)task->thread.ksp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131
Greg Ungererdb81fb82005-09-02 10:42:52 +1000132 addr = (unsigned long) stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 endstack = (unsigned long *) PAGE_ALIGN(addr);
134
135 printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack);
136 for (i = 0; i < kstack_depth_to_print; i++) {
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000137 if (stack + 1 + i > endstack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 break;
139 if (i % 8 == 0)
Greg Ungerer329237c2006-12-04 17:27:09 +1000140 printk("\n" KERN_EMERG " ");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000141 printk(" %08lx", *(stack + i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000143 printk("\n");
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200144 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000146#ifdef CONFIG_FRAME_POINTER
147 printk(KERN_EMERG "Call Trace:\n");
148
149 last_stack = stack - 1;
150 while (stack <= endstack && stack > last_stack) {
151
152 addr = *(stack + 1);
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200153 print_this_address(addr, i);
154 i++;
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000155
156 last_stack = stack;
157 stack = (unsigned long *)*stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000159 printk("\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000160#else
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200161 printk(KERN_EMERG "Call Trace with CONFIG_FRAME_POINTER disabled:\n");
162 while (stack <= endstack) {
163 addr = *stack++;
164 /*
165 * If the address is either in the text segment of the kernel,
166 * or in a region which is occupied by a module then it *may*
167 * be the address of a calling routine; if so, print it so that
168 * someone tracing down the cause of the crash will be able to
169 * figure out the call path that was taken.
170 */
171 if (__kernel_text_address(addr)) {
172 print_this_address(addr, i);
173 i++;
174 }
175 }
176 printk(KERN_CONT "\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000177#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}
179
180void bad_super_trap(struct frame *fp)
181{
182 console_verbose();
Ahmed S. Darwishbf0059b2007-02-10 01:43:46 -0800183 if (fp->ptregs.vector < 4 * ARRAY_SIZE(vec_names))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 printk (KERN_WARNING "*** %s *** FORMAT=%X\n",
185 vec_names[(fp->ptregs.vector) >> 2],
186 fp->ptregs.format);
187 else
188 printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n",
189 (fp->ptregs.vector) >> 2,
190 fp->ptregs.format);
191 printk (KERN_WARNING "Current process id is %d\n", current->pid);
192 die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0);
193}
194
195asmlinkage void trap_c(struct frame *fp)
196{
197 int sig;
198 siginfo_t info;
199
200 if (fp->ptregs.sr & PS_S) {
201 if ((fp->ptregs.vector >> 2) == VEC_TRACE) {
202 /* traced a trapping instruction */
203 current->ptrace |= PT_DTRACE;
204 } else
205 bad_super_trap(fp);
206 return;
207 }
208
209 /* send the appropriate signal to the user program */
210 switch ((fp->ptregs.vector) >> 2) {
211 case VEC_ADDRERR:
212 info.si_code = BUS_ADRALN;
213 sig = SIGBUS;
214 break;
215 case VEC_ILLEGAL:
216 case VEC_LINE10:
217 case VEC_LINE11:
218 info.si_code = ILL_ILLOPC;
219 sig = SIGILL;
220 break;
221 case VEC_PRIV:
222 info.si_code = ILL_PRVOPC;
223 sig = SIGILL;
224 break;
225 case VEC_COPROC:
226 info.si_code = ILL_COPROC;
227 sig = SIGILL;
228 break;
229 case VEC_TRAP1: /* gdbserver breakpoint */
230 fp->ptregs.pc -= 2;
231 info.si_code = TRAP_TRACE;
232 sig = SIGTRAP;
233 break;
234 case VEC_TRAP2:
235 case VEC_TRAP3:
236 case VEC_TRAP4:
237 case VEC_TRAP5:
238 case VEC_TRAP6:
239 case VEC_TRAP7:
240 case VEC_TRAP8:
241 case VEC_TRAP9:
242 case VEC_TRAP10:
243 case VEC_TRAP11:
244 case VEC_TRAP12:
245 case VEC_TRAP13:
246 case VEC_TRAP14:
247 info.si_code = ILL_ILLTRP;
248 sig = SIGILL;
249 break;
250 case VEC_FPBRUC:
251 case VEC_FPOE:
252 case VEC_FPNAN:
253 info.si_code = FPE_FLTINV;
254 sig = SIGFPE;
255 break;
256 case VEC_FPIR:
257 info.si_code = FPE_FLTRES;
258 sig = SIGFPE;
259 break;
260 case VEC_FPDIVZ:
261 info.si_code = FPE_FLTDIV;
262 sig = SIGFPE;
263 break;
264 case VEC_FPUNDER:
265 info.si_code = FPE_FLTUND;
266 sig = SIGFPE;
267 break;
268 case VEC_FPOVER:
269 info.si_code = FPE_FLTOVF;
270 sig = SIGFPE;
271 break;
272 case VEC_ZERODIV:
273 info.si_code = FPE_INTDIV;
274 sig = SIGFPE;
275 break;
276 case VEC_CHK:
277 case VEC_TRAP:
278 info.si_code = FPE_INTOVF;
279 sig = SIGFPE;
280 break;
281 case VEC_TRACE: /* ptrace single step */
282 info.si_code = TRAP_TRACE;
283 sig = SIGTRAP;
284 break;
285 case VEC_TRAP15: /* breakpoint */
286 info.si_code = TRAP_BRKPT;
287 sig = SIGTRAP;
288 break;
289 default:
290 info.si_code = ILL_ILLOPC;
291 sig = SIGILL;
292 break;
293 }
294 info.si_signo = sig;
295 info.si_errno = 0;
296 switch (fp->ptregs.format) {
297 default:
298 info.si_addr = (void *) fp->ptregs.pc;
299 break;
300 case 2:
301 info.si_addr = (void *) fp->un.fmt2.iaddr;
302 break;
303 case 7:
304 info.si_addr = (void *) fp->un.fmt7.effaddr;
305 break;
306 case 9:
307 info.si_addr = (void *) fp->un.fmt9.iaddr;
308 break;
309 case 10:
310 info.si_addr = (void *) fp->un.fmta.daddr;
311 break;
312 case 11:
313 info.si_addr = (void *) fp->un.fmtb.daddr;
314 break;
315 }
316 force_sig_info (sig, &info, current);
317}
318
319asmlinkage void set_esp0(unsigned long ssp)
320{
321 current->thread.esp0 = ssp;
322}
323
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324/*
325 * The architecture-independent backtrace generator
326 */
327void dump_stack(void)
328{
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000329 /*
330 * We need frame pointers for this little trick, which works as follows:
331 *
332 * +------------+ 0x00
333 * | Next SP | -> 0x0c
334 * +------------+ 0x04
335 * | Caller |
336 * +------------+ 0x08
337 * | Local vars | -> our stack var
338 * +------------+ 0x0c
339 * | Next SP | -> 0x18, that is what we pass to show_stack()
340 * +------------+ 0x10
341 * | Caller |
342 * +------------+ 0x14
343 * | Local vars |
344 * +------------+ 0x18
345 * | ... |
346 * +------------+
347 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000349 unsigned long *stack;
350
351 stack = (unsigned long *)&stack;
352 stack++;
353 __show_stack(current, stack);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354}
Greg Ungererdb81fb82005-09-02 10:42:52 +1000355EXPORT_SYMBOL(dump_stack);
356
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000357void show_stack(struct task_struct *task, unsigned long *stack)
358{
359 if (!stack && !task)
360 dump_stack();
361 else
362 __show_stack(task, stack);
363}
364
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365#ifdef CONFIG_M68KFPU_EMU
366asmlinkage void fpemu_signal(int signal, int code, void *addr)
367{
368 siginfo_t info;
369
370 info.si_signo = signal;
371 info.si_errno = 0;
372 info.si_code = code;
373 info.si_addr = addr;
374 force_sig_info(signal, &info, current);
375}
376#endif