diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-01-20 12:02:54 -0500 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2010-02-15 16:39:14 -0500 |
commit | a9221de66d2d94e6e34c3f56bbdd744935020737 (patch) | |
tree | d28a14e4a536a282aff11ae135c19c3a8b237b40 | |
parent | 2b0d8c251b8876d530a6bf671eb5425838fa698a (diff) |
ARM: add notify_die() support
Kernel debuggers want to be informed of die() events, so that they
can take some action to allow the problem to be inspected. Provide
the hook in a similar manner to x86.
Note that we currently don't implement the individual trap hooks.
Acked-by: Jason Wessel <jason.wessel@windriver.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r-- | arch/arm/include/asm/system.h | 3 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 35 |
2 files changed, 26 insertions, 12 deletions
diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 058e7e90881d..ca88e6a84707 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h | |||
@@ -73,8 +73,7 @@ extern unsigned int mem_fclk_21285; | |||
73 | 73 | ||
74 | struct pt_regs; | 74 | struct pt_regs; |
75 | 75 | ||
76 | void die(const char *msg, struct pt_regs *regs, int err) | 76 | void die(const char *msg, struct pt_regs *regs, int err); |
77 | __attribute__((noreturn)); | ||
78 | 77 | ||
79 | struct siginfo; | 78 | struct siginfo; |
80 | void arm_notify_die(const char *str, struct pt_regs *regs, struct siginfo *info, | 79 | void arm_notify_die(const char *str, struct pt_regs *regs, struct siginfo *info, |
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 3f361a783f43..1621e5327b2a 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -12,15 +12,17 @@ | |||
12 | * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably | 12 | * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably |
13 | * kill the offending process. | 13 | * kill the offending process. |
14 | */ | 14 | */ |
15 | #include <linux/module.h> | ||
16 | #include <linux/signal.h> | 15 | #include <linux/signal.h> |
17 | #include <linux/spinlock.h> | ||
18 | #include <linux/personality.h> | 16 | #include <linux/personality.h> |
19 | #include <linux/kallsyms.h> | 17 | #include <linux/kallsyms.h> |
20 | #include <linux/delay.h> | 18 | #include <linux/spinlock.h> |
19 | #include <linux/uaccess.h> | ||
21 | #include <linux/hardirq.h> | 20 | #include <linux/hardirq.h> |
21 | #include <linux/kdebug.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/kexec.h> | ||
24 | #include <linux/delay.h> | ||
22 | #include <linux/init.h> | 25 | #include <linux/init.h> |
23 | #include <linux/uaccess.h> | ||
24 | 26 | ||
25 | #include <asm/atomic.h> | 27 | #include <asm/atomic.h> |
26 | #include <asm/cacheflush.h> | 28 | #include <asm/cacheflush.h> |
@@ -224,14 +226,21 @@ void show_stack(struct task_struct *tsk, unsigned long *sp) | |||
224 | #define S_SMP "" | 226 | #define S_SMP "" |
225 | #endif | 227 | #endif |
226 | 228 | ||
227 | static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) | 229 | static int __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs) |
228 | { | 230 | { |
229 | struct task_struct *tsk = thread->task; | 231 | struct task_struct *tsk = thread->task; |
230 | static int die_counter; | 232 | static int die_counter; |
233 | int ret; | ||
231 | 234 | ||
232 | printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", | 235 | printk(KERN_EMERG "Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n", |
233 | str, err, ++die_counter); | 236 | str, err, ++die_counter); |
234 | sysfs_printk_last_file(); | 237 | sysfs_printk_last_file(); |
238 | |||
239 | /* trap and error numbers are mostly meaningless on ARM */ | ||
240 | ret = notify_die(DIE_OOPS, str, regs, err, tsk->thread.trap_no, SIGSEGV); | ||
241 | if (ret == NOTIFY_STOP) | ||
242 | return ret; | ||
243 | |||
235 | print_modules(); | 244 | print_modules(); |
236 | __show_regs(regs); | 245 | __show_regs(regs); |
237 | printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", | 246 | printk(KERN_EMERG "Process %.*s (pid: %d, stack limit = 0x%p)\n", |
@@ -243,6 +252,8 @@ static void __die(const char *str, int err, struct thread_info *thread, struct p | |||
243 | dump_backtrace(regs, tsk); | 252 | dump_backtrace(regs, tsk); |
244 | dump_instr(KERN_EMERG, regs); | 253 | dump_instr(KERN_EMERG, regs); |
245 | } | 254 | } |
255 | |||
256 | return ret; | ||
246 | } | 257 | } |
247 | 258 | ||
248 | DEFINE_SPINLOCK(die_lock); | 259 | DEFINE_SPINLOCK(die_lock); |
@@ -250,16 +261,21 @@ DEFINE_SPINLOCK(die_lock); | |||
250 | /* | 261 | /* |
251 | * This function is protected against re-entrancy. | 262 | * This function is protected against re-entrancy. |
252 | */ | 263 | */ |
253 | NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | 264 | void die(const char *str, struct pt_regs *regs, int err) |
254 | { | 265 | { |
255 | struct thread_info *thread = current_thread_info(); | 266 | struct thread_info *thread = current_thread_info(); |
267 | int ret; | ||
256 | 268 | ||
257 | oops_enter(); | 269 | oops_enter(); |
258 | 270 | ||
259 | spin_lock_irq(&die_lock); | 271 | spin_lock_irq(&die_lock); |
260 | console_verbose(); | 272 | console_verbose(); |
261 | bust_spinlocks(1); | 273 | bust_spinlocks(1); |
262 | __die(str, err, thread, regs); | 274 | ret = __die(str, err, thread, regs); |
275 | |||
276 | if (regs && kexec_should_crash(thread->task)) | ||
277 | crash_kexec(regs); | ||
278 | |||
263 | bust_spinlocks(0); | 279 | bust_spinlocks(0); |
264 | add_taint(TAINT_DIE); | 280 | add_taint(TAINT_DIE); |
265 | spin_unlock_irq(&die_lock); | 281 | spin_unlock_irq(&die_lock); |
@@ -267,11 +283,10 @@ NORET_TYPE void die(const char *str, struct pt_regs *regs, int err) | |||
267 | 283 | ||
268 | if (in_interrupt()) | 284 | if (in_interrupt()) |
269 | panic("Fatal exception in interrupt"); | 285 | panic("Fatal exception in interrupt"); |
270 | |||
271 | if (panic_on_oops) | 286 | if (panic_on_oops) |
272 | panic("Fatal exception"); | 287 | panic("Fatal exception"); |
273 | 288 | if (ret != NOTIFY_STOP) | |
274 | do_exit(SIGSEGV); | 289 | do_exit(SIGSEGV); |
275 | } | 290 | } |
276 | 291 | ||
277 | void arm_notify_die(const char *str, struct pt_regs *regs, | 292 | void arm_notify_die(const char *str, struct pt_regs *regs, |