diff options
Diffstat (limited to 'lib/bug.c')
-rw-r--r-- | lib/bug.c | 163 |
1 files changed, 163 insertions, 0 deletions
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 | } | ||