aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel/traps.c
diff options
context:
space:
mode:
authorArnd Bergmann <arnd@arndb.de>2012-10-04 16:57:00 -0400
committerArnd Bergmann <arnd@arndb.de>2012-10-04 16:57:51 -0400
commitc37d6154c0b9163c27e53cc1d0be3867b4abd760 (patch)
tree7a24522c56d1cb284dff1d3c225bbdaba0901bb5 /arch/arm64/kernel/traps.c
parente7a570ff7dff9af6e54ff5e580a61ec7652137a0 (diff)
parent8a1ab3155c2ac7fbe5f2038d6e26efeb607a1498 (diff)
Merge branch 'disintegrate-asm-generic' of git://git.infradead.org/users/dhowells/linux-headers into asm-generic
Patches from David Howells <dhowells@redhat.com>: This is to complete part of the UAPI disintegration for which the preparatory patches were pulled recently. Note that there are some fixup patches which are at the base of the branch aimed at you, plus all arches get the asm-generic branch merged in too. * 'disintegrate-asm-generic' of git://git.infradead.org/users/dhowells/linux-headers: UAPI: (Scripted) Disintegrate include/asm-generic UAPI: Fix conditional header installation handling (notably kvm_para.h on m68k) c6x: remove c6x signal.h UAPI: Split compound conditionals containing __KERNEL__ in Arm64 UAPI: Fix the guards on various asm/unistd.h files Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r--arch/arm64/kernel/traps.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
new file mode 100644
index 000000000000..3883f842434f
--- /dev/null
+++ b/arch/arm64/kernel/traps.c
@@ -0,0 +1,348 @@
1/*
2 * Based on arch/arm/kernel/traps.c
3 *
4 * Copyright (C) 1995-2009 Russell King
5 * Copyright (C) 2012 ARM Ltd.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/signal.h>
21#include <linux/personality.h>
22#include <linux/kallsyms.h>
23#include <linux/spinlock.h>
24#include <linux/uaccess.h>
25#include <linux/hardirq.h>
26#include <linux/kdebug.h>
27#include <linux/module.h>
28#include <linux/kexec.h>
29#include <linux/delay.h>
30#include <linux/init.h>
31#include <linux/sched.h>
32#include <linux/syscalls.h>
33
34#include <asm/atomic.h>
35#include <asm/traps.h>
36#include <asm/stacktrace.h>
37#include <asm/exception.h>
38#include <asm/system_misc.h>
39
40static const char *handler[]= {
41 "Synchronous Abort",
42 "IRQ",
43 "FIQ",
44 "Error"
45};
46
47int show_unhandled_signals = 1;
48
49/*
50 * Dump out the contents of some memory nicely...
51 */
52static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
53 unsigned long top)
54{
55 unsigned long first;
56 mm_segment_t fs;
57 int i;
58
59 /*
60 * We need to switch to kernel mode so that we can use __get_user
61 * to safely read from kernel space. Note that we now dump the
62 * code first, just in case the backtrace kills us.
63 */
64 fs = get_fs();
65 set_fs(KERNEL_DS);
66
67 printk("%s%s(0x%016lx to 0x%016lx)\n", lvl, str, bottom, top);
68
69 for (first = bottom & ~31; first < top; first += 32) {
70 unsigned long p;
71 char str[sizeof(" 12345678") * 8 + 1];
72
73 memset(str, ' ', sizeof(str));
74 str[sizeof(str) - 1] = '\0';
75
76 for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
77 if (p >= bottom && p < top) {
78 unsigned int val;
79 if (__get_user(val, (unsigned int *)p) == 0)
80 sprintf(str + i * 9, " %08x", val);
81 else
82 sprintf(str + i * 9, " ????????");
83 }
84 }
85 printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
86 }
87
88 set_fs(fs);
89}
90
91static void dump_backtrace_entry(unsigned long where, unsigned long stack)
92{
93 print_ip_sym(where);
94 if (in_exception_text(where))
95 dump_mem("", "Exception stack", stack,
96 stack + sizeof(struct pt_regs));
97}
98
99static void dump_instr(const char *lvl, struct pt_regs *regs)
100{
101 unsigned long addr = instruction_pointer(regs);
102 mm_segment_t fs;
103 char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
104 int i;
105
106 /*
107 * We need to switch to kernel mode so that we can use __get_user
108 * to safely read from kernel space. Note that we now dump the
109 * code first, just in case the backtrace kills us.
110 */
111 fs = get_fs();
112 set_fs(KERNEL_DS);
113
114 for (i = -4; i < 1; i++) {
115 unsigned int val, bad;
116
117 bad = __get_user(val, &((u32 *)addr)[i]);
118
119 if (!bad)
120 p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
121 else {
122 p += sprintf(p, "bad PC value");
123 break;
124 }
125 }
126 printk("%sCode: %s\n", lvl, str);
127
128 set_fs(fs);
129}
130
131static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
132{
133 struct stackframe frame;
134 const register unsigned long current_sp asm ("sp");
135
136 pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
137
138 if (!tsk)
139 tsk = current;
140
141 if (regs) {
142 frame.fp = regs->regs[29];
143 frame.sp = regs->sp;
144 frame.pc = regs->pc;
145 } else if (tsk == current) {
146 frame.fp = (unsigned long)__builtin_frame_address(0);
147 frame.sp = current_sp;
148 frame.pc = (unsigned long)dump_backtrace;
149 } else {
150 /*
151 * task blocked in __switch_to
152 */
153 frame.fp = thread_saved_fp(tsk);
154 frame.sp = thread_saved_sp(tsk);
155 frame.pc = thread_saved_pc(tsk);
156 }
157
158 printk("Call trace:\n");
159 while (1) {
160 unsigned long where = frame.pc;
161 int ret;
162
163 ret = unwind_frame(&frame);
164 if (ret < 0)
165 break;
166 dump_backtrace_entry(where, frame.sp);
167 }
168}
169
170void dump_stack(void)
171{
172 dump_backtrace(NULL, NULL);
173}
174
175EXPORT_SYMBOL(dump_stack);
176
177void show_stack(struct task_struct *tsk, unsigned long *sp)
178{
179 dump_backtrace(NULL, tsk);
180 barrier();
181}
182
183#ifdef CONFIG_PREEMPT
184#define S_PREEMPT " PREEMPT"
185#else
186#define S_PREEMPT ""
187#endif
188#ifdef CONFIG_SMP
189#define S_SMP " SMP"
190#else
191#define S_SMP ""
192#endif
193
194static int __die(const char *str, int err, struct thread_info *thread,
195 struct pt_regs *regs)
196{
197 struct task_struct *tsk = thread->task;
198 static int die_counter;
199 int ret;
200
201 pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
202 str, err, ++die_counter);
203
204 /* trap and error numbers are mostly meaningless on ARM */
205 ret = notify_die(DIE_OOPS, str, regs, err, 0, SIGSEGV);
206 if (ret == NOTIFY_STOP)
207 return ret;
208
209 print_modules();
210 __show_regs(regs);
211 pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
212 TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
213
214 if (!user_mode(regs) || in_interrupt()) {
215 dump_mem(KERN_EMERG, "Stack: ", regs->sp,
216 THREAD_SIZE + (unsigned long)task_stack_page(tsk));
217 dump_backtrace(regs, tsk);
218 dump_instr(KERN_EMERG, regs);
219 }
220
221 return ret;
222}
223
224static DEFINE_RAW_SPINLOCK(die_lock);
225
226/*
227 * This function is protected against re-entrancy.
228 */
229void die(const char *str, struct pt_regs *regs, int err)
230{
231 struct thread_info *thread = current_thread_info();
232 int ret;
233
234 oops_enter();
235
236 raw_spin_lock_irq(&die_lock);
237 console_verbose();
238 bust_spinlocks(1);
239 ret = __die(str, err, thread, regs);
240
241 if (regs && kexec_should_crash(thread->task))
242 crash_kexec(regs);
243
244 bust_spinlocks(0);
245 add_taint(TAINT_DIE);
246 raw_spin_unlock_irq(&die_lock);
247 oops_exit();
248
249 if (in_interrupt())
250 panic("Fatal exception in interrupt");
251 if (panic_on_oops)
252 panic("Fatal exception");
253 if (ret != NOTIFY_STOP)
254 do_exit(SIGSEGV);
255}
256
257void arm64_notify_die(const char *str, struct pt_regs *regs,
258 struct siginfo *info, int err)
259{
260 if (user_mode(regs))
261 force_sig_info(info->si_signo, info, current);
262 else
263 die(str, regs, err);
264}
265
266asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
267{
268 siginfo_t info;
269 void __user *pc = (void __user *)instruction_pointer(regs);
270
271#ifdef CONFIG_COMPAT
272 /* check for AArch32 breakpoint instructions */
273 if (compat_user_mode(regs) && aarch32_break_trap(regs) == 0)
274 return;
275#endif
276
277 if (show_unhandled_signals) {
278 pr_info("%s[%d]: undefined instruction: pc=%p\n",
279 current->comm, task_pid_nr(current), pc);
280 dump_instr(KERN_INFO, regs);
281 }
282
283 info.si_signo = SIGILL;
284 info.si_errno = 0;
285 info.si_code = ILL_ILLOPC;
286 info.si_addr = pc;
287
288 arm64_notify_die("Oops - undefined instruction", regs, &info, 0);
289}
290
291long compat_arm_syscall(struct pt_regs *regs);
292
293asmlinkage long do_ni_syscall(struct pt_regs *regs)
294{
295#ifdef CONFIG_COMPAT
296 long ret;
297 if (is_compat_task()) {
298 ret = compat_arm_syscall(regs);
299 if (ret != -ENOSYS)
300 return ret;
301 }
302#endif
303
304 if (show_unhandled_signals) {
305 pr_info("%s[%d]: syscall %d\n", current->comm,
306 task_pid_nr(current), (int)regs->syscallno);
307 dump_instr("", regs);
308 if (user_mode(regs))
309 __show_regs(regs);
310 }
311
312 return sys_ni_syscall();
313}
314
315/*
316 * bad_mode handles the impossible case in the exception vector.
317 */
318asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
319{
320 console_verbose();
321
322 pr_crit("Bad mode in %s handler detected, code 0x%08x\n",
323 handler[reason], esr);
324
325 die("Oops - bad mode", regs, 0);
326 local_irq_disable();
327 panic("bad mode");
328}
329
330void __pte_error(const char *file, int line, unsigned long val)
331{
332 printk("%s:%d: bad pte %016lx.\n", file, line, val);
333}
334
335void __pmd_error(const char *file, int line, unsigned long val)
336{
337 printk("%s:%d: bad pmd %016lx.\n", file, line, val);
338}
339
340void __pgd_error(const char *file, int line, unsigned long val)
341{
342 printk("%s:%d: bad pgd %016lx.\n", file, line, val);
343}
344
345void __init trap_init(void)
346{
347 return;
348}