1 | /* |
2 | * Hardware exception handling |
3 | * |
4 | * Copyright (C) 2010 Tobias Klauser <tklauser@distanz.ch> |
5 | * Copyright (C) 2004 Microtronix Datacom Ltd. |
6 | * Copyright (C) 2001 Vic Phillips |
7 | * |
8 | * This file is subject to the terms and conditions of the GNU General |
9 | * Public License. See the file COPYING in the main directory of this |
10 | * archive for more details. |
11 | */ |
12 | |
13 | #include <linux/sched.h> |
14 | #include <linux/sched/debug.h> |
15 | #include <linux/kernel.h> |
16 | #include <linux/signal.h> |
17 | #include <linux/export.h> |
18 | #include <linux/mm.h> |
19 | #include <linux/ptrace.h> |
20 | |
21 | #include <asm/traps.h> |
22 | #include <asm/sections.h> |
23 | #include <linux/uaccess.h> |
24 | |
25 | static DEFINE_SPINLOCK(die_lock); |
26 | |
27 | static void _send_sig(int signo, int code, unsigned long addr) |
28 | { |
29 | force_sig_fault(sig: signo, code, addr: (void __user *) addr); |
30 | } |
31 | |
32 | void die(const char *str, struct pt_regs *regs, long err) |
33 | { |
34 | console_verbose(); |
35 | spin_lock_irq(lock: &die_lock); |
36 | pr_warn("Oops: %s, sig: %ld\n" , str, err); |
37 | show_regs(regs); |
38 | spin_unlock_irq(lock: &die_lock); |
39 | /* |
40 | * make_task_dead() should take care of panic'ing from an interrupt |
41 | * context so we don't handle it here |
42 | */ |
43 | make_task_dead(signr: err); |
44 | } |
45 | |
46 | void _exception(int signo, struct pt_regs *regs, int code, unsigned long addr) |
47 | { |
48 | if (!user_mode(regs)) |
49 | die(str: "Exception in kernel mode" , regs, err: signo); |
50 | |
51 | _send_sig(signo, code, addr); |
52 | } |
53 | |
54 | /* |
55 | * The show_stack() is external API which we do not use ourselves. |
56 | */ |
57 | |
58 | int kstack_depth_to_print = 48; |
59 | |
60 | void show_stack(struct task_struct *task, unsigned long *stack, |
61 | const char *loglvl) |
62 | { |
63 | unsigned long *endstack, addr; |
64 | int i; |
65 | |
66 | if (!stack) { |
67 | if (task) |
68 | stack = (unsigned long *)task->thread.ksp; |
69 | else |
70 | stack = (unsigned long *)&stack; |
71 | } |
72 | |
73 | addr = (unsigned long) stack; |
74 | endstack = (unsigned long *) PAGE_ALIGN(addr); |
75 | |
76 | printk("%sStack from %08lx:" , loglvl, (unsigned long)stack); |
77 | for (i = 0; i < kstack_depth_to_print; i++) { |
78 | if (stack + 1 > endstack) |
79 | break; |
80 | if (i % 8 == 0) |
81 | printk("%s\n " , loglvl); |
82 | printk("%s %08lx" , loglvl, *stack++); |
83 | } |
84 | |
85 | printk("%s\nCall Trace:" , loglvl); |
86 | i = 0; |
87 | while (stack + 1 <= endstack) { |
88 | addr = *stack++; |
89 | /* |
90 | * If the address is either in the text segment of the |
91 | * kernel, or in the region which contains vmalloc'ed |
92 | * memory, it *may* be the address of a calling |
93 | * routine; if so, print it so that someone tracing |
94 | * down the cause of the crash will be able to figure |
95 | * out the call path that was taken. |
96 | */ |
97 | if (((addr >= (unsigned long) _stext) && |
98 | (addr <= (unsigned long) _etext))) { |
99 | if (i % 4 == 0) |
100 | pr_emerg("\n " ); |
101 | printk("%s [<%08lx>]" , loglvl, addr); |
102 | i++; |
103 | } |
104 | } |
105 | printk("%s\n" , loglvl); |
106 | } |
107 | |
108 | /* Breakpoint handler */ |
109 | asmlinkage void breakpoint_c(struct pt_regs *fp) |
110 | { |
111 | /* |
112 | * The breakpoint entry code has moved the PC on by 4 bytes, so we must |
113 | * move it back. This could be done on the host but we do it here |
114 | * because monitor.S of JTAG gdbserver does it too. |
115 | */ |
116 | fp->ea -= 4; |
117 | _exception(SIGTRAP, regs: fp, TRAP_BRKPT, addr: fp->ea); |
118 | } |
119 | |
120 | #ifndef CONFIG_NIOS2_ALIGNMENT_TRAP |
121 | /* Alignment exception handler */ |
122 | asmlinkage void handle_unaligned_c(struct pt_regs *fp, int cause) |
123 | { |
124 | unsigned long addr = RDCTL(CTL_BADADDR); |
125 | |
126 | cause >>= 2; |
127 | fp->ea -= 4; |
128 | |
129 | if (fixup_exception(fp)) |
130 | return; |
131 | |
132 | if (!user_mode(regs: fp)) { |
133 | pr_alert("Unaligned access from kernel mode, this might be a hardware\n" ); |
134 | pr_alert("problem, dump registers and restart the instruction\n" ); |
135 | pr_alert(" BADADDR 0x%08lx\n" , addr); |
136 | pr_alert(" cause %d\n" , cause); |
137 | pr_alert(" op-code 0x%08lx\n" , *(unsigned long *)(fp->ea)); |
138 | show_regs(fp); |
139 | return; |
140 | } |
141 | |
142 | _exception(SIGBUS, regs: fp, BUS_ADRALN, addr); |
143 | } |
144 | #endif /* CONFIG_NIOS2_ALIGNMENT_TRAP */ |
145 | |
146 | /* Illegal instruction handler */ |
147 | asmlinkage void handle_illegal_c(struct pt_regs *fp) |
148 | { |
149 | fp->ea -= 4; |
150 | _exception(SIGILL, regs: fp, ILL_ILLOPC, addr: fp->ea); |
151 | } |
152 | |
153 | /* Supervisor instruction handler */ |
154 | asmlinkage void handle_supervisor_instr(struct pt_regs *fp) |
155 | { |
156 | fp->ea -= 4; |
157 | _exception(SIGILL, regs: fp, ILL_PRVOPC, addr: fp->ea); |
158 | } |
159 | |
160 | /* Division error handler */ |
161 | asmlinkage void handle_diverror_c(struct pt_regs *fp) |
162 | { |
163 | fp->ea -= 4; |
164 | _exception(SIGFPE, regs: fp, FPE_INTDIV, addr: fp->ea); |
165 | } |
166 | |
167 | /* Unhandled exception handler */ |
168 | asmlinkage void unhandled_exception(struct pt_regs *regs, int cause) |
169 | { |
170 | unsigned long addr = RDCTL(CTL_BADADDR); |
171 | |
172 | cause /= 4; |
173 | |
174 | pr_emerg("Unhandled exception #%d in %s mode (badaddr=0x%08lx)\n" , |
175 | cause, user_mode(regs) ? "user" : "kernel" , addr); |
176 | |
177 | regs->ea -= 4; |
178 | show_regs(regs); |
179 | |
180 | pr_emerg("opcode: 0x%08lx\n" , *(unsigned long *)(regs->ea)); |
181 | } |
182 | |
183 | asmlinkage void handle_trap_1_c(struct pt_regs *fp) |
184 | { |
185 | _send_sig(SIGUSR1, code: 0, addr: fp->ea); |
186 | } |
187 | |
188 | asmlinkage void handle_trap_2_c(struct pt_regs *fp) |
189 | { |
190 | _send_sig(SIGUSR2, code: 0, addr: fp->ea); |
191 | } |
192 | |
193 | asmlinkage void handle_trap_3_c(struct pt_regs *fp) |
194 | { |
195 | _send_sig(SIGILL, ILL_ILLTRP, addr: fp->ea); |
196 | } |
197 | |