blob: e67b8c806959a9796b7b2840be064ae9c4a3e775 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070063void die_if_kernel(char *str, struct pt_regs *fp, int nr)
64{
65 if (!(fp->sr & PS_S))
66 return;
67
68 console_verbose();
69 printk(KERN_EMERG "%s: %08x\n",str,nr);
70 printk(KERN_EMERG "PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n",
71 fp->pc, fp->sr, fp, fp->a2);
72 printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n",
73 fp->d0, fp->d1, fp->d2, fp->d3);
74 printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n",
75 fp->d4, fp->d5, fp->a0, fp->a1);
76
77 printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n",
78 current->comm, current->pid, PAGE_SIZE+(unsigned long)current);
Greg Ungerera4c8b912007-07-19 01:49:14 -070079 show_stack(NULL, (unsigned long *)(fp + 1));
Pavel Emelianovbcdcd8e2007-07-17 04:03:42 -070080 add_taint(TAINT_DIE);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 do_exit(SIGSEGV);
82}
83
84asmlinkage void buserr_c(struct frame *fp)
85{
86 /* Only set esp0 if coming from user mode */
87 if (user_mode(&fp->ptregs))
88 current->thread.esp0 = (unsigned long) fp;
89
Greg Ungererbb286322006-06-26 10:33:10 +100090#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 printk (KERN_DEBUG "*** Bus Error *** Format is %x\n", fp->ptregs.format);
92#endif
93
94 die_if_kernel("bad frame format",&fp->ptregs,0);
Greg Ungererbb286322006-06-26 10:33:10 +100095#if defined(DEBUG)
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 printk(KERN_DEBUG "Unknown SIGSEGV - 4\n");
97#endif
98 force_sig(SIGSEGV, current);
99}
100
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200101static void print_this_address(unsigned long addr, int i)
102{
103#ifdef CONFIG_KALLSYMS
104 printk(KERN_EMERG " [%08lx] ", addr);
105 print_symbol(KERN_CONT "%s\n", addr);
106#else
107 if (i % 5)
108 printk(KERN_CONT " [%08lx] ", addr);
109 else
Joe Perchesad361c92009-07-06 13:05:40 -0700110 printk(KERN_EMERG " [%08lx] ", addr);
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200111 i++;
112#endif
113}
114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115int kstack_depth_to_print = 48;
116
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000117static void __show_stack(struct task_struct *task, unsigned long *stack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118{
Greg Ungererdb81fb82005-09-02 10:42:52 +1000119 unsigned long *endstack, addr;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200120#ifdef CONFIG_FRAME_POINTER
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000121 unsigned long *last_stack;
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200122#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123 int i;
124
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000125 if (!stack)
126 stack = (unsigned long *)task->thread.ksp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127
Greg Ungererdb81fb82005-09-02 10:42:52 +1000128 addr = (unsigned long) stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 endstack = (unsigned long *) PAGE_ALIGN(addr);
130
131 printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack);
132 for (i = 0; i < kstack_depth_to_print; i++) {
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000133 if (stack + 1 + i > endstack)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 break;
135 if (i % 8 == 0)
Joe Perchesad361c92009-07-06 13:05:40 -0700136 printk(KERN_EMERG " ");
137 printk(KERN_CONT " %08lx", *(stack + i));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000139 printk("\n");
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200140 i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000142#ifdef CONFIG_FRAME_POINTER
143 printk(KERN_EMERG "Call Trace:\n");
144
145 last_stack = stack - 1;
146 while (stack <= endstack && stack > last_stack) {
147
148 addr = *(stack + 1);
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200149 print_this_address(addr, i);
150 i++;
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000151
152 last_stack = stack;
153 stack = (unsigned long *)*stack;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 }
Greg Ungerer329237c2006-12-04 17:27:09 +1000155 printk("\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000156#else
Sebastian Siewior1fda83d2008-05-09 16:13:36 +0200157 printk(KERN_EMERG "Call Trace with CONFIG_FRAME_POINTER disabled:\n");
158 while (stack <= endstack) {
159 addr = *stack++;
160 /*
161 * If the address is either in the text segment of the kernel,
162 * or in a region which is occupied by a module then it *may*
163 * be the address of a calling routine; if so, print it so that
164 * someone tracing down the cause of the crash will be able to
165 * figure out the call path that was taken.
166 */
167 if (__kernel_text_address(addr)) {
168 print_this_address(addr, i);
169 i++;
170 }
171 }
172 printk(KERN_CONT "\n");
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000173#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174}
175
176void bad_super_trap(struct frame *fp)
177{
Greg Ungerer730251f2010-10-07 17:16:56 +1000178 int vector = (fp->ptregs.vector >> 2) & 0xff;
179
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 console_verbose();
Greg Ungerer730251f2010-10-07 17:16:56 +1000181 if (vector < ARRAY_SIZE(vec_names))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 printk (KERN_WARNING "*** %s *** FORMAT=%X\n",
Greg Ungerer730251f2010-10-07 17:16:56 +1000183 vec_names[vector],
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 fp->ptregs.format);
185 else
186 printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n",
Greg Ungerer730251f2010-10-07 17:16:56 +1000187 vector,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 fp->ptregs.format);
189 printk (KERN_WARNING "Current process id is %d\n", current->pid);
190 die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0);
191}
192
193asmlinkage void trap_c(struct frame *fp)
194{
195 int sig;
Greg Ungerer730251f2010-10-07 17:16:56 +1000196 int vector = (fp->ptregs.vector >> 2) & 0xff;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 siginfo_t info;
198
199 if (fp->ptregs.sr & PS_S) {
Greg Ungerer730251f2010-10-07 17:16:56 +1000200 if (vector == VEC_TRACE) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 /* traced a trapping instruction */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 } else
203 bad_super_trap(fp);
204 return;
205 }
206
207 /* send the appropriate signal to the user program */
Greg Ungerer730251f2010-10-07 17:16:56 +1000208 switch (vector) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 case VEC_ADDRERR:
210 info.si_code = BUS_ADRALN;
211 sig = SIGBUS;
212 break;
213 case VEC_ILLEGAL:
214 case VEC_LINE10:
215 case VEC_LINE11:
216 info.si_code = ILL_ILLOPC;
217 sig = SIGILL;
218 break;
219 case VEC_PRIV:
220 info.si_code = ILL_PRVOPC;
221 sig = SIGILL;
222 break;
223 case VEC_COPROC:
224 info.si_code = ILL_COPROC;
225 sig = SIGILL;
226 break;
227 case VEC_TRAP1: /* gdbserver breakpoint */
228 fp->ptregs.pc -= 2;
229 info.si_code = TRAP_TRACE;
230 sig = SIGTRAP;
231 break;
232 case VEC_TRAP2:
233 case VEC_TRAP3:
234 case VEC_TRAP4:
235 case VEC_TRAP5:
236 case VEC_TRAP6:
237 case VEC_TRAP7:
238 case VEC_TRAP8:
239 case VEC_TRAP9:
240 case VEC_TRAP10:
241 case VEC_TRAP11:
242 case VEC_TRAP12:
243 case VEC_TRAP13:
244 case VEC_TRAP14:
245 info.si_code = ILL_ILLTRP;
246 sig = SIGILL;
247 break;
248 case VEC_FPBRUC:
249 case VEC_FPOE:
250 case VEC_FPNAN:
251 info.si_code = FPE_FLTINV;
252 sig = SIGFPE;
253 break;
254 case VEC_FPIR:
255 info.si_code = FPE_FLTRES;
256 sig = SIGFPE;
257 break;
258 case VEC_FPDIVZ:
259 info.si_code = FPE_FLTDIV;
260 sig = SIGFPE;
261 break;
262 case VEC_FPUNDER:
263 info.si_code = FPE_FLTUND;
264 sig = SIGFPE;
265 break;
266 case VEC_FPOVER:
267 info.si_code = FPE_FLTOVF;
268 sig = SIGFPE;
269 break;
270 case VEC_ZERODIV:
271 info.si_code = FPE_INTDIV;
272 sig = SIGFPE;
273 break;
274 case VEC_CHK:
275 case VEC_TRAP:
276 info.si_code = FPE_INTOVF;
277 sig = SIGFPE;
278 break;
279 case VEC_TRACE: /* ptrace single step */
280 info.si_code = TRAP_TRACE;
281 sig = SIGTRAP;
282 break;
283 case VEC_TRAP15: /* breakpoint */
284 info.si_code = TRAP_BRKPT;
285 sig = SIGTRAP;
286 break;
287 default:
288 info.si_code = ILL_ILLOPC;
289 sig = SIGILL;
290 break;
291 }
292 info.si_signo = sig;
293 info.si_errno = 0;
294 switch (fp->ptregs.format) {
295 default:
296 info.si_addr = (void *) fp->ptregs.pc;
297 break;
298 case 2:
299 info.si_addr = (void *) fp->un.fmt2.iaddr;
300 break;
301 case 7:
302 info.si_addr = (void *) fp->un.fmt7.effaddr;
303 break;
304 case 9:
305 info.si_addr = (void *) fp->un.fmt9.iaddr;
306 break;
307 case 10:
308 info.si_addr = (void *) fp->un.fmta.daddr;
309 break;
310 case 11:
311 info.si_addr = (void *) fp->un.fmtb.daddr;
312 break;
313 }
314 force_sig_info (sig, &info, current);
315}
316
317asmlinkage void set_esp0(unsigned long ssp)
318{
319 current->thread.esp0 = ssp;
320}
321
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322/*
323 * The architecture-independent backtrace generator
324 */
325void dump_stack(void)
326{
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000327 /*
328 * We need frame pointers for this little trick, which works as follows:
329 *
330 * +------------+ 0x00
331 * | Next SP | -> 0x0c
332 * +------------+ 0x04
333 * | Caller |
334 * +------------+ 0x08
335 * | Local vars | -> our stack var
336 * +------------+ 0x0c
337 * | Next SP | -> 0x18, that is what we pass to show_stack()
338 * +------------+ 0x10
339 * | Caller |
340 * +------------+ 0x14
341 * | Local vars |
342 * +------------+ 0x18
343 * | ... |
344 * +------------+
345 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000347 unsigned long *stack;
348
349 stack = (unsigned long *)&stack;
350 stack++;
351 __show_stack(current, stack);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352}
Greg Ungererdb81fb82005-09-02 10:42:52 +1000353EXPORT_SYMBOL(dump_stack);
354
Sebastian Siewiorf6054e22008-05-01 12:16:38 +1000355void show_stack(struct task_struct *task, unsigned long *stack)
356{
357 if (!stack && !task)
358 dump_stack();
359 else
360 __show_stack(task, stack);
361}