diff options
-rw-r--r-- | include/asm-generic/bug.h | 16 | ||||
-rw-r--r-- | include/asm-generic/vmlinux.lds.h | 8 | ||||
-rw-r--r-- | include/linux/bug.h | 47 | ||||
-rw-r--r-- | include/linux/module.h | 7 | ||||
-rw-r--r-- | lib/Kconfig.debug | 2 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/bug.c | 163 |
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__ | ||
10 | struct 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 | |||
7 | enum 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 | |||
16 | static inline int is_warning_bug(const struct bug_entry *bug) | ||
17 | { | ||
18 | return bug->flags & BUGFLAG_WARNING; | ||
19 | } | ||
20 | |||
21 | const struct bug_entry *find_bug(unsigned long bugaddr); | ||
22 | |||
23 | enum bug_trap_type report_bug(unsigned long bug_addr); | ||
24 | |||
25 | int module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *, | ||
26 | struct module *); | ||
27 | void module_bug_cleanup(struct module *); | ||
28 | |||
29 | /* These are defined by the architecture */ | ||
30 | int is_valid_bugaddr(unsigned long addr); | ||
31 | |||
32 | #else /* !CONFIG_GENERIC_BUG */ | ||
33 | |||
34 | static inline enum bug_trap_type report_bug(unsigned long bug_addr) | ||
35 | { | ||
36 | return BUG_TRAP_TYPE_BUG; | ||
37 | } | ||
38 | static inline int module_bug_finalize(const Elf_Ehdr *hdr, | ||
39 | const Elf_Shdr *sechdrs, | ||
40 | struct module *mod) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | static 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 | |||
285 | config DEBUG_BUGVERBOSE | 285 | config 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 | ||
56 | obj-$(CONFIG_SWIOTLB) += swiotlb.o | 56 | obj-$(CONFIG_SWIOTLB) += swiotlb.o |
57 | 57 | ||
58 | lib-$(CONFIG_GENERIC_BUG) += bug.o | ||
59 | |||
58 | hostprogs-y := gen_crc32table | 60 | hostprogs-y := gen_crc32table |
59 | clean-files := crc32table.h | 61 | clean-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 | |||
42 | extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; | ||
43 | |||
44 | #ifdef CONFIG_MODULES | ||
45 | static LIST_HEAD(module_bug_list); | ||
46 | |||
47 | static 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 | |||
62 | int 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 | |||
91 | void module_bug_cleanup(struct module *mod) | ||
92 | { | ||
93 | list_del(&mod->bug_list); | ||
94 | } | ||
95 | |||
96 | #else | ||
97 | |||
98 | static inline const struct bug_entry *module_find_bug(unsigned long bugaddr) | ||
99 | { | ||
100 | return NULL; | ||
101 | } | ||
102 | #endif | ||
103 | |||
104 | const 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 | |||
115 | enum 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 | } | ||