diff options
-rw-r--r-- | arch/arm/Kconfig | 4 | ||||
-rw-r--r-- | arch/arm/include/asm/bug.h | 55 | ||||
-rw-r--r-- | arch/arm/kernel/traps.c | 31 | ||||
-rw-r--r-- | arch/arm/kernel/vmlinux.lds.S | 3 |
4 files changed, 73 insertions, 20 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3269576dbfa8..1412da8ccf05 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig | |||
@@ -215,6 +215,10 @@ config ARM_PATCH_PHYS_VIRT_16BIT | |||
215 | to allow physical memory down to a theoretical minimum of 64K | 215 | to allow physical memory down to a theoretical minimum of 64K |
216 | boundaries. | 216 | boundaries. |
217 | 217 | ||
218 | config GENERIC_BUG | ||
219 | def_bool y | ||
220 | depends on BUG | ||
221 | |||
218 | source "init/Kconfig" | 222 | source "init/Kconfig" |
219 | 223 | ||
220 | source "kernel/Kconfig.freezer" | 224 | source "kernel/Kconfig.freezer" |
diff --git a/arch/arm/include/asm/bug.h b/arch/arm/include/asm/bug.h index 4d88425a4169..9abe7a07d5ac 100644 --- a/arch/arm/include/asm/bug.h +++ b/arch/arm/include/asm/bug.h | |||
@@ -3,21 +3,58 @@ | |||
3 | 3 | ||
4 | 4 | ||
5 | #ifdef CONFIG_BUG | 5 | #ifdef CONFIG_BUG |
6 | #ifdef CONFIG_DEBUG_BUGVERBOSE | ||
7 | extern void __bug(const char *file, int line) __attribute__((noreturn)); | ||
8 | |||
9 | /* give file/line information */ | ||
10 | #define BUG() __bug(__FILE__, __LINE__) | ||
11 | 6 | ||
7 | /* | ||
8 | * Use a suitable undefined instruction to use for ARM/Thumb2 bug handling. | ||
9 | * We need to be careful not to conflict with those used by other modules and | ||
10 | * the register_undef_hook() system. | ||
11 | */ | ||
12 | #ifdef CONFIG_THUMB2_KERNEL | ||
13 | #define BUG_INSTR_VALUE 0xde02 | ||
14 | #define BUG_INSTR_TYPE ".hword " | ||
12 | #else | 15 | #else |
16 | #define BUG_INSTR_VALUE 0xe7f001f2 | ||
17 | #define BUG_INSTR_TYPE ".word " | ||
18 | #endif | ||
13 | 19 | ||
14 | /* this just causes an oops */ | ||
15 | #define BUG() do { *(int *)0 = 0; } while (1) | ||
16 | 20 | ||
17 | #endif | 21 | #define BUG() _BUG(__FILE__, __LINE__, BUG_INSTR_VALUE) |
22 | #define _BUG(file, line, value) __BUG(file, line, value) | ||
23 | |||
24 | #ifdef CONFIG_DEBUG_BUGVERBOSE | ||
25 | |||
26 | /* | ||
27 | * The extra indirection is to ensure that the __FILE__ string comes through | ||
28 | * OK. Many version of gcc do not support the asm %c parameter which would be | ||
29 | * preferable to this unpleasantness. We use mergeable string sections to | ||
30 | * avoid multiple copies of the string appearing in the kernel image. | ||
31 | */ | ||
32 | |||
33 | #define __BUG(__file, __line, __value) \ | ||
34 | do { \ | ||
35 | BUILD_BUG_ON(sizeof(struct bug_entry) != 12); \ | ||
36 | asm volatile("1:\t" BUG_INSTR_TYPE #__value "\n" \ | ||
37 | ".pushsection .rodata.str, \"aMS\", %progbits, 1\n" \ | ||
38 | "2:\t.asciz " #__file "\n" \ | ||
39 | ".popsection\n" \ | ||
40 | ".pushsection __bug_table,\"a\"\n" \ | ||
41 | "3:\t.word 1b, 2b\n" \ | ||
42 | "\t.hword " #__line ", 0\n" \ | ||
43 | ".popsection"); \ | ||
44 | unreachable(); \ | ||
45 | } while (0) | ||
46 | |||
47 | #else /* not CONFIG_DEBUG_BUGVERBOSE */ | ||
48 | |||
49 | #define __BUG(__file, __line, __value) \ | ||
50 | do { \ | ||
51 | asm volatile(BUG_INSTR_TYPE #__value); \ | ||
52 | unreachable(); \ | ||
53 | } while (0) | ||
54 | #endif /* CONFIG_DEBUG_BUGVERBOSE */ | ||
18 | 55 | ||
19 | #define HAVE_ARCH_BUG | 56 | #define HAVE_ARCH_BUG |
20 | #endif | 57 | #endif /* CONFIG_BUG */ |
21 | 58 | ||
22 | #include <asm-generic/bug.h> | 59 | #include <asm-generic/bug.h> |
23 | 60 | ||
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index bc9f9da782cb..74969248c375 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c | |||
@@ -21,6 +21,7 @@ | |||
21 | #include <linux/kdebug.h> | 21 | #include <linux/kdebug.h> |
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/kexec.h> | 23 | #include <linux/kexec.h> |
24 | #include <linux/bug.h> | ||
24 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
25 | #include <linux/init.h> | 26 | #include <linux/init.h> |
26 | #include <linux/sched.h> | 27 | #include <linux/sched.h> |
@@ -270,6 +271,8 @@ void die(const char *str, struct pt_regs *regs, int err) | |||
270 | spin_lock_irq(&die_lock); | 271 | spin_lock_irq(&die_lock); |
271 | console_verbose(); | 272 | console_verbose(); |
272 | bust_spinlocks(1); | 273 | bust_spinlocks(1); |
274 | if (!user_mode(regs)) | ||
275 | report_bug(regs->ARM_pc, regs); | ||
273 | ret = __die(str, err, thread, regs); | 276 | ret = __die(str, err, thread, regs); |
274 | 277 | ||
275 | if (regs && kexec_should_crash(thread->task)) | 278 | if (regs && kexec_should_crash(thread->task)) |
@@ -301,6 +304,24 @@ void arm_notify_die(const char *str, struct pt_regs *regs, | |||
301 | } | 304 | } |
302 | } | 305 | } |
303 | 306 | ||
307 | #ifdef CONFIG_GENERIC_BUG | ||
308 | |||
309 | int is_valid_bugaddr(unsigned long pc) | ||
310 | { | ||
311 | #ifdef CONFIG_THUMB2_KERNEL | ||
312 | unsigned short bkpt; | ||
313 | #else | ||
314 | unsigned long bkpt; | ||
315 | #endif | ||
316 | |||
317 | if (probe_kernel_address((unsigned *)pc, bkpt)) | ||
318 | return 0; | ||
319 | |||
320 | return bkpt == BUG_INSTR_VALUE; | ||
321 | } | ||
322 | |||
323 | #endif | ||
324 | |||
304 | static LIST_HEAD(undef_hook); | 325 | static LIST_HEAD(undef_hook); |
305 | static DEFINE_SPINLOCK(undef_lock); | 326 | static DEFINE_SPINLOCK(undef_lock); |
306 | 327 | ||
@@ -706,16 +727,6 @@ baddataabort(int code, unsigned long instr, struct pt_regs *regs) | |||
706 | arm_notify_die("unknown data abort code", regs, &info, instr, 0); | 727 | arm_notify_die("unknown data abort code", regs, &info, instr, 0); |
707 | } | 728 | } |
708 | 729 | ||
709 | void __attribute__((noreturn)) __bug(const char *file, int line) | ||
710 | { | ||
711 | printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line); | ||
712 | *(int *)0 = 0; | ||
713 | |||
714 | /* Avoid "noreturn function does return" */ | ||
715 | for (;;); | ||
716 | } | ||
717 | EXPORT_SYMBOL(__bug); | ||
718 | |||
719 | void __readwrite_bug(const char *fn) | 730 | void __readwrite_bug(const char *fn) |
720 | { | 731 | { |
721 | printk("%s called, but not implemented\n", fn); | 732 | printk("%s called, but not implemented\n", fn); |
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index bf977f8514f6..7b2541efd9f8 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S | |||
@@ -21,7 +21,8 @@ | |||
21 | #define ARM_CPU_KEEP(x) | 21 | #define ARM_CPU_KEEP(x) |
22 | #endif | 22 | #endif |
23 | 23 | ||
24 | #if defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK) | 24 | #if (defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK)) || \ |
25 | defined(CONFIG_GENERIC_BUG) | ||
25 | #define ARM_EXIT_KEEP(x) x | 26 | #define ARM_EXIT_KEEP(x) x |
26 | #else | 27 | #else |
27 | #define ARM_EXIT_KEEP(x) | 28 | #define ARM_EXIT_KEEP(x) |