Blame view
lib/bug.c
4.69 KB
7664c5a1d
|
1 2 3 4 5 6 7 |
/* Generic support for BUG() This respects the following config options: CONFIG_BUG - emit BUG traps. Nothing happens without this. CONFIG_GENERIC_BUG - enable this code. |
b93a531e3
|
8 9 |
CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit pointers relative to the containing struct bug_entry for bug_addr and file. |
7664c5a1d
|
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
CONFIG_DEBUG_BUGVERBOSE - emit full file+line information for each BUG CONFIG_BUG and CONFIG_DEBUG_BUGVERBOSE are potentially user-settable (though they're generally always on). CONFIG_GENERIC_BUG is set by each architecture using this code. To use this, your architecture must: 1. Set up the config options: - Enable CONFIG_GENERIC_BUG if CONFIG_BUG 2. Implement BUG (and optionally BUG_ON, WARN, WARN_ON) - Define HAVE_ARCH_BUG - Implement BUG() to generate a faulting instruction - NOTE: struct bug_entry does not have "file" or "line" entries when CONFIG_DEBUG_BUGVERBOSE is not enabled, so you must generate the values accordingly. 3. Implement the trap - In the illegal instruction trap handler (typically), verify that the fault was in kernel mode, and call report_bug() - report_bug() will return whether it was a false alarm, a warning, or an actual bug. - You must implement the is_valid_bugaddr(bugaddr) callback which returns true if the eip is a real kernel address, and it points to the expected BUG trap instruction. Jeremy Fitzhardinge <jeremy@goop.org> 2006 */ |
c56ba7033
|
40 41 |
#define pr_fmt(fmt) fmt |
7664c5a1d
|
42 43 |
#include <linux/list.h> #include <linux/module.h> |
da9eac899
|
44 |
#include <linux/kernel.h> |
7664c5a1d
|
45 |
#include <linux/bug.h> |
608e26196
|
46 |
#include <linux/sched.h> |
7664c5a1d
|
47 48 |
extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; |
b93a531e3
|
49 50 51 52 53 54 55 56 |
static inline unsigned long bug_addr(const struct bug_entry *bug) { #ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS return bug->bug_addr; #else return (unsigned long)bug + bug->bug_addr_disp; #endif } |
7664c5a1d
|
57 |
#ifdef CONFIG_MODULES |
1fb9341ac
|
58 |
/* Updates are protected by module mutex */ |
7664c5a1d
|
59 60 61 62 63 64 65 66 67 68 69 |
static LIST_HEAD(module_bug_list); static const struct bug_entry *module_find_bug(unsigned long bugaddr) { struct module *mod; list_for_each_entry(mod, &module_bug_list, bug_list) { const struct bug_entry *bug = mod->bug_table; unsigned i; for (i = 0; i < mod->num_bugs; ++i, ++bug) |
b93a531e3
|
70 |
if (bugaddr == bug_addr(bug)) |
7664c5a1d
|
71 72 73 74 |
return bug; } return NULL; } |
5336377d6
|
75 76 |
void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) |
7664c5a1d
|
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
{ char *secstrings; unsigned int i; mod->bug_table = NULL; mod->num_bugs = 0; /* Find the __bug_table section, if present */ secstrings = (char *)hdr + sechdrs[hdr->e_shstrndx].sh_offset; for (i = 1; i < hdr->e_shnum; i++) { if (strcmp(secstrings+sechdrs[i].sh_name, "__bug_table")) continue; mod->bug_table = (void *) sechdrs[i].sh_addr; mod->num_bugs = sechdrs[i].sh_size / sizeof(struct bug_entry); break; } /* * Strictly speaking this should have a spinlock to protect against * traversals, but since we only traverse on BUG()s, a spinlock * could potentially lead to deadlock and thus be counter-productive. */ list_add(&mod->bug_list, &module_bug_list); |
7664c5a1d
|
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
} void module_bug_cleanup(struct module *mod) { list_del(&mod->bug_list); } #else static inline const struct bug_entry *module_find_bug(unsigned long bugaddr) { return NULL; } #endif const struct bug_entry *find_bug(unsigned long bugaddr) { const struct bug_entry *bug; for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) |
b93a531e3
|
120 |
if (bugaddr == bug_addr(bug)) |
7664c5a1d
|
121 122 123 124 |
return bug; return module_find_bug(bugaddr); } |
608e26196
|
125 |
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) |
7664c5a1d
|
126 127 128 129 130 131 132 133 134 |
{ const struct bug_entry *bug; const char *file; unsigned line, warning; if (!is_valid_bugaddr(bugaddr)) return BUG_TRAP_TYPE_NONE; bug = find_bug(bugaddr); |
7664c5a1d
|
135 136 137 138 139 140 |
file = NULL; line = 0; warning = 0; if (bug) { #ifdef CONFIG_DEBUG_BUGVERBOSE |
b93a531e3
|
141 |
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS |
7664c5a1d
|
142 |
file = bug->file; |
b93a531e3
|
143 144 145 |
#else file = (const char *)bug + bug->file_disp; #endif |
7664c5a1d
|
146 147 148 149 150 151 152 |
line = bug->line; #endif warning = (bug->flags & BUGFLAG_WARNING) != 0; } if (warning) { /* this is a WARN_ON rather than BUG/BUG_ON */ |
c56ba7033
|
153 154 |
pr_warn("------------[ cut here ]------------ "); |
e2e7e0932
|
155 |
|
7664c5a1d
|
156 |
if (file) |
c56ba7033
|
157 158 |
pr_warn("WARNING: at %s:%u ", file, line); |
7664c5a1d
|
159 |
else |
c56ba7033
|
160 161 162 |
pr_warn("WARNING: at %p [verbose debug info unavailable] ", (void *)bugaddr); |
7664c5a1d
|
163 |
|
e2e7e0932
|
164 |
print_modules(); |
608e26196
|
165 |
show_regs(regs); |
863a60492
|
166 |
print_oops_end_marker(); |
373d4d099
|
167 168 |
/* Just a warning, don't kill lockdep. */ add_taint(BUG_GET_TAINT(bug), LOCKDEP_STILL_OK); |
7664c5a1d
|
169 170 |
return BUG_TRAP_TYPE_WARN; } |
b0f4c4b32
|
171 172 |
printk(KERN_DEFAULT "------------[ cut here ]------------ "); |
e2e7e0932
|
173 |
|
7664c5a1d
|
174 |
if (file) |
c56ba7033
|
175 176 |
pr_crit("kernel BUG at %s:%u! ", file, line); |
7664c5a1d
|
177 |
else |
c56ba7033
|
178 179 180 |
pr_crit("Kernel BUG at %p [verbose debug info unavailable] ", (void *)bugaddr); |
7664c5a1d
|
181 182 183 |
return BUG_TRAP_TYPE_BUG; } |