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, |
