diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 14:51:57 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 14:51:57 -0400 |
commit | 81f56e5375e84689b891e0e6c5a02ec12a1f18d9 (patch) | |
tree | a1e128a71ff24fc705428df86a858076cfe4bc13 /arch/arm64/kernel/traps.c | |
parent | 6c09931b3f987898f5c581d267ef269f5e2e9575 (diff) | |
parent | 27aa55c5e5123fa8b8ad0156559d34d7edff58ca (diff) |
Merge tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64
Pull arm64 support from Catalin Marinas:
"Linux support for the 64-bit ARM architecture (AArch64)
Features currently supported:
- 39-bit address space for user and kernel (each)
- 4KB and 64KB page configurations
- Compat (32-bit) user applications (ARMv7, EABI only)
- Flattened Device Tree (mandated for all AArch64 platforms)
- ARM generic timers"
* tag 'arm64-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/cmarinas/linux-aarch64: (35 commits)
arm64: ptrace: remove obsolete ptrace request numbers from user headers
arm64: Do not set the SMP/nAMP processor bit
arm64: MAINTAINERS update
arm64: Build infrastructure
arm64: Miscellaneous header files
arm64: Generic timers support
arm64: Loadable modules
arm64: Miscellaneous library functions
arm64: Performance counters support
arm64: Add support for /proc/sys/debug/exception-trace
arm64: Debugging support
arm64: Floating point and SIMD
arm64: 32-bit (compat) applications support
arm64: User access library functions
arm64: Signal handling support
arm64: VDSO support
arm64: System calls handling
arm64: ELF definitions
arm64: SMP support
arm64: DMA mapping API
...
Diffstat (limited to 'arch/arm64/kernel/traps.c')
-rw-r--r-- | arch/arm64/kernel/traps.c | 348 |
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 | |||
40 | static const char *handler[]= { | ||
41 | "Synchronous Abort", | ||
42 | "IRQ", | ||
43 | "FIQ", | ||
44 | "Error" | ||
45 | }; | ||
46 | |||
47 | int show_unhandled_signals = 1; | ||
48 | |||
49 | /* | ||
50 | * Dump out the contents of some memory nicely... | ||
51 | */ | ||
52 | static 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 | |||
91 | static 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 | |||
99 | static 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 | |||
131 | static 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 | |||
170 | void dump_stack(void) | ||
171 | { | ||
172 | dump_backtrace(NULL, NULL); | ||
173 | } | ||
174 | |||
175 | EXPORT_SYMBOL(dump_stack); | ||
176 | |||
177 | void 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 | |||
194 | static 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 | |||
224 | static DEFINE_RAW_SPINLOCK(die_lock); | ||
225 | |||
226 | /* | ||
227 | * This function is protected against re-entrancy. | ||
228 | */ | ||
229 | void 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 | |||
257 | void 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 | |||
266 | asmlinkage 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 | |||
291 | long compat_arm_syscall(struct pt_regs *regs); | ||
292 | |||
293 | asmlinkage 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 | */ | ||
318 | asmlinkage 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 | |||
330 | void __pte_error(const char *file, int line, unsigned long val) | ||
331 | { | ||
332 | printk("%s:%d: bad pte %016lx.\n", file, line, val); | ||
333 | } | ||
334 | |||
335 | void __pmd_error(const char *file, int line, unsigned long val) | ||
336 | { | ||
337 | printk("%s:%d: bad pmd %016lx.\n", file, line, val); | ||
338 | } | ||
339 | |||
340 | void __pgd_error(const char *file, int line, unsigned long val) | ||
341 | { | ||
342 | printk("%s:%d: bad pgd %016lx.\n", file, line, val); | ||
343 | } | ||
344 | |||
345 | void __init trap_init(void) | ||
346 | { | ||
347 | return; | ||
348 | } | ||