aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Fitzhardinge <jeremy@goop.org>2006-12-08 05:36:19 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-12-08 11:28:39 -0500
commit7664c5a1da4711bb6383117f51b94c8dc8f3f1cd (patch)
tree79a2e2a4626c66a411488b5ceb554b011d862a7d
parentc48f70c3d046f021b1c22438604ef2a583380eca (diff)
[PATCH] Generic BUG implementation
This patch adds common handling for kernel BUGs, for use by architectures as they wish. The code is derived from arch/powerpc. The advantages of having common BUG handling are: - consistent BUG reporting across architectures - shared implementation of out-of-line file/line data - implement CONFIG_DEBUG_BUGVERBOSE consistently This means that in inline impact of BUG is just the illegal instruction itself, which is an improvement for i386 and x86-64. A BUG is represented in the instruction stream as an illegal instruction, which has file/line information associated with it. This extra information is stored in the __bug_table section in the ELF file. When the kernel gets an illegal instruction, it first confirms it might possibly be from a BUG (ie, in kernel mode, the right illegal instruction). It then calls report_bug(). This searches __bug_table for a matching instruction pointer, and if found, prints the corresponding file/line information. If report_bug() determines that it wasn't a BUG which caused the trap, it returns BUG_TRAP_TYPE_NONE. Some architectures (powerpc) implement WARN using the same mechanism; if the illegal instruction was the result of a WARN, then report_bug(Q) returns CONFIG_DEBUG_BUGVERBOSE; otherwise it returns BUG_TRAP_TYPE_BUG. lib/bug.c keeps a list of loaded modules which can be searched for __bug_table entries. The architecture must call module_bug_finalize()/module_bug_cleanup() from its corresponding module_finalize/cleanup functions. Unsetting CONFIG_DEBUG_BUGVERBOSE will reduce the kernel size by some amount. At the very least, filename and line information will not be recorded for each but, but architectures may decide to store no extra information per BUG at all. Unfortunately, gcc doesn't have a general way to mark an asm() as noreturn, so architectures will generally have to include an infinite loop (or similar) in the BUG code, so that gcc knows execution won't continue beyond that point. gcc does have a __builtin_trap() operator which may be useful to achieve the same effect, unfortunately it cannot be used to actually implement the BUG itself, because there's no way to get the instruction's address for use in generating the __bug_table entry. [randy.dunlap@oracle.com: Handle BUG=n, GENERIC_BUG=n to prevent build errors] [bunk@stusta.de: include/linux/bug.h must always #include <linux/module.h] Signed-off-by: Jeremy Fitzhardinge <jeremy@goop.org> Cc: Andi Kleen <ak@muc.de> Cc: Hugh Dickens <hugh@veritas.com> Cc: Michael Ellerman <michael@ellerman.id.au> Cc: Paul Mackerras <paulus@samba.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--include/asm-generic/bug.h16
-rw-r--r--include/asm-generic/vmlinux.lds.h8
-rw-r--r--include/linux/bug.h47
-rw-r--r--include/linux/module.h7
-rw-r--r--lib/Kconfig.debug2
-rw-r--r--lib/Makefile2
-rw-r--r--lib/bug.c163
7 files changed, 244 insertions, 1 deletions
diff --git a/include/asm-generic/bug.h b/include/asm-generic/bug.h
index c92ae0f166ff..47e3561638b1 100644
--- a/include/asm-generic/bug.h
+++ b/include/asm-generic/bug.h
@@ -4,6 +4,22 @@
4#include <linux/compiler.h> 4#include <linux/compiler.h>
5 5
6#ifdef CONFIG_BUG 6#ifdef CONFIG_BUG
7
8#ifdef CONFIG_GENERIC_BUG
9#ifndef __ASSEMBLY__
10struct bug_entry {
11 unsigned long bug_addr;
12#ifdef CONFIG_DEBUG_BUGVERBOSE
13 const char *file;
14 unsigned short line;
15#endif
16 unsigned short flags;
17};
18#endif /* __ASSEMBLY__ */
19
20#define BUGFLAG_WARNING (1<<0)
21#endif /* CONFIG_GENERIC_BUG */
22
7#ifndef HAVE_ARCH_BUG 23#ifndef HAVE_ARCH_BUG
8#define BUG() do { \ 24#define BUG() do { \
9 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \ 25 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 4d4c62d11059..6e9fcebbf89f 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -218,6 +218,14 @@
218 .stab.indexstr 0 : { *(.stab.indexstr) } \ 218 .stab.indexstr 0 : { *(.stab.indexstr) } \
219 .comment 0 : { *(.comment) } 219 .comment 0 : { *(.comment) }
220 220
221#define BUG_TABLE \
222 . = ALIGN(8); \
223 __bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \
224 __start___bug_table = .; \
225 *(__bug_table) \
226 __stop___bug_table = .; \
227 }
228
221#define NOTES \ 229#define NOTES \
222 .notes : { *(.note.*) } :note 230 .notes : { *(.note.*) } :note
223 231
diff --git a/include/linux/bug.h b/include/linux/bug.h
new file mode 100644
index 000000000000..42aa0a54b6f4
--- /dev/null
+++ b/include/linux/bug.h
@@ -0,0 +1,47 @@
1#ifndef _LINUX_BUG_H
2#define _LINUX_BUG_H
3
4#include <linux/module.h>
5#include <asm/bug.h>
6
7enum bug_trap_type {
8 BUG_TRAP_TYPE_NONE = 0,
9 BUG_TRAP_TYPE_WARN = 1,
10 BUG_TRAP_TYPE_BUG = 2,
11};
12
13#ifdef CONFIG_GENERIC_BUG
14#include <asm-generic/bug.h>
15
16static inline int is_warning_bug(const struct bug_entry *bug)
17{
18 return bug->flags & BUGFLAG_WARNING;
19}
20
21const struct bug_entry *find_bug(unsigned long bugaddr);
22
23enum bug_trap_type report_bug(unsigned long bug_addr);
24
25int module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *,
26 struct module *);
27void module_bug_cleanup(struct module *);
28
29/* These are defined by the architecture */
30int is_valid_bugaddr(unsigned long addr);
31
32#else /* !CONFIG_GENERIC_BUG */
33
34static inline enum bug_trap_type report_bug(unsigned long bug_addr)
35{
36 return BUG_TRAP_TYPE_BUG;
37}
38static inline int module_bug_finalize(const Elf_Ehdr *hdr,
39 const Elf_Shdr *sechdrs,
40 struct module *mod)
41{
42 return 0;
43}
44static inline void module_bug_cleanup(struct module *mod) {}
45
46#endif /* CONFIG_GENERIC_BUG */
47#endif /* _LINUX_BUG_H */
diff --git a/include/linux/module.h b/include/linux/module.h
index d33df2408e05..10f771a49997 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -319,6 +319,13 @@ struct module
319 319
320 unsigned int taints; /* same bits as kernel:tainted */ 320 unsigned int taints; /* same bits as kernel:tainted */
321 321
322#ifdef CONFIG_GENERIC_BUG
323 /* Support for BUG */
324 struct list_head bug_list;
325 struct bug_entry *bug_table;
326 unsigned num_bugs;
327#endif
328
322#ifdef CONFIG_MODULE_UNLOAD 329#ifdef CONFIG_MODULE_UNLOAD
323 /* Reference counts */ 330 /* Reference counts */
324 struct module_ref ref[NR_CPUS]; 331 struct module_ref ref[NR_CPUS];
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index b75fed737f25..ee46fb335d24 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -285,7 +285,7 @@ config DEBUG_HIGHMEM
285config DEBUG_BUGVERBOSE 285config DEBUG_BUGVERBOSE
286 bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED 286 bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED
287 depends on BUG 287 depends on BUG
288 depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH 288 depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH || GENERIC_BUG
289 default !EMBEDDED 289 default !EMBEDDED
290 help 290 help
291 Say Y here to make BUG() panics output the file name and line number 291 Say Y here to make BUG() panics output the file name and line number
diff --git a/lib/Makefile b/lib/Makefile
index fea8f9035f07..7ab486b883a8 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -55,6 +55,8 @@ obj-$(CONFIG_AUDIT_GENERIC) += audit.o
55 55
56obj-$(CONFIG_SWIOTLB) += swiotlb.o 56obj-$(CONFIG_SWIOTLB) += swiotlb.o
57 57
58lib-$(CONFIG_GENERIC_BUG) += bug.o
59
58hostprogs-y := gen_crc32table 60hostprogs-y := gen_crc32table
59clean-files := crc32table.h 61clean-files := crc32table.h
60 62
diff --git a/lib/bug.c b/lib/bug.c
new file mode 100644
index 000000000000..014b582c5c4b
--- /dev/null
+++ b/lib/bug.c
@@ -0,0 +1,163 @@
1/*
2 Generic support for BUG()
3
4 This respects the following config options:
5
6 CONFIG_BUG - emit BUG traps. Nothing happens without this.
7 CONFIG_GENERIC_BUG - enable this code.
8 CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG
9
10 CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable
11 (though they're generally always on).
12
13 CONFIG_GENERIC_BUG is set by each architecture using this code.
14
15 To use this, your architecture must:
16
17 1. Set up the config options:
18 - Enable CONFIG_GENERIC_BUG if CONFIG_BUG
19
20 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON)
21 - Define HAVE_ARCH_BUG
22 - Implement BUG() to generate a faulting instruction
23 - NOTE: struct bug_entry does not have "file" or "line" entries
24 when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate
25 the values accordingly.
26
27 3. Implement the trap
28 - In the illegal instruction trap handler (typically), verify
29 that the fault was in kernel mode, and call report_bug()
30 - report_bug() will return whether it was a false alarm, a warning,
31 or an actual bug.
32 - You must implement the is_valid_bugaddr(bugaddr) callback which
33 returns true if the eip is a real kernel address, and it points
34 to the expected BUG trap instruction.
35
36 Jeremy Fitzhardinge <jeremy@goop.org> 2006
37 */
38#include <linux/list.h>
39#include <linux/module.h>
40#include <linux/bug.h>
41
42extern const struct bug_entry __start___bug_table[], __stop___bug_table[];
43
44#ifdef CONFIG_MODULES
45static LIST_HEAD(module_bug_list);
46
47static const struct bug_entry *module_find_bug(unsigned long bugaddr)
48{
49 struct module *mod;
50
51 list_for_each_entry(mod, &module_bug_list, bug_list) {
52 const struct bug_entry *bug = mod->bug_table;
53 unsigned i;
54
55 for (i = 0; i < mod->num_bugs; ++i, ++bug)
56 if (bugaddr == bug->bug_addr)
57 return bug;
58 }
59 return NULL;
60}
61
62int module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
63 struct module *mod)
64{
65 char *secstrings;
66 unsigned int i;
67
68 mod->bug_table = NULL;
69 mod->num_bugs = 0;
70
71 /* Find the __bug_table section, if present */
72 secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
73 for (i = 1; i < hdr->e_shnum; i++) {
74 if (strcmp(secstrings+sechdrs[i].sh_name, "__bug_table"))
75 continue;
76 mod->bug_table = (void *) sechdrs[i].sh_addr;
77 mod->num_bugs = sechdrs[i].sh_size / sizeof(struct bug_entry);
78 break;
79 }
80
81 /*
82 * Strictly speaking this should have a spinlock to protect against
83 * traversals, but since we only traverse on BUG()s, a spinlock
84 * could potentially lead to deadlock and thus be counter-productive.
85 */
86 list_add(&mod->bug_list, &module_bug_list);
87
88 return 0;
89}
90
91void module_bug_cleanup(struct module *mod)
92{
93 list_del(&mod->bug_list);
94}
95
96#else
97
98static inline const struct bug_entry *module_find_bug(unsigned long bugaddr)
99{
100 return NULL;
101}
102#endif
103
104const struct bug_entry *find_bug(unsigned long bugaddr)
105{
106 const struct bug_entry *bug;
107
108 for (bug = __start___bug_table; bug < __stop___bug_table; ++bug)
109 if (bugaddr == bug->bug_addr)
110 return bug;
111
112 return module_find_bug(bugaddr);
113}
114
115enum bug_trap_type report_bug(unsigned long bugaddr)
116{
117 const struct bug_entry *bug;
118 const char *file;
119 unsigned line, warning;
120
121 if (!is_valid_bugaddr(bugaddr))
122 return BUG_TRAP_TYPE_NONE;
123
124 bug = find_bug(bugaddr);
125
126 printk(KERN_EMERG "------------[ cut here ]------------\n");
127
128 file = NULL;
129 line = 0;
130 warning = 0;
131
132 if (bug) {
133#ifdef CONFIG_DEBUG_BUGVERBOSE
134 file = bug->file;
135 line = bug->line;
136#endif
137 warning = (bug->flags & BUGFLAG_WARNING) != 0;
138 }
139
140 if (warning) {
141 /* this is a WARN_ON rather than BUG/BUG_ON */
142 if (file)
143 printk(KERN_ERR "Badness at %s:%u\n",
144 file, line);
145 else
146 printk(KERN_ERR "Badness at %p "
147 "[verbose debug info unavailable]\n",
148 (void *)bugaddr);
149
150 dump_stack();
151 return BUG_TRAP_TYPE_WARN;
152 }
153
154 if (file)
155 printk(KERN_CRIT "kernel BUG at %s:%u!\n",
156 file, line);
157 else
158 printk(KERN_CRIT "Kernel BUG at %p "
159 "[verbose debug info unavailable]\n",
160 (void *)bugaddr);
161
162 return BUG_TRAP_TYPE_BUG;
163}