Blame view
lib/bug.c
5.44 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
7664c5a1d [PATCH] Generic B... |
2 3 4 5 6 7 8 |
/* 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 allow bug table e... |
9 10 |
CONFIG_GENERIC_BUG_RELATIVE_POINTERS - use 32-bit pointers relative to the containing struct bug_entry for bug_addr and file. |
7664c5a1d [PATCH] Generic B... |
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 40 |
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 lib/bug.c: conver... |
41 42 |
#define pr_fmt(fmt) fmt |
7664c5a1d [PATCH] Generic B... |
43 44 |
#include <linux/list.h> #include <linux/module.h> |
da9eac899 lib: taint kernel... |
45 |
#include <linux/kernel.h> |
7664c5a1d [PATCH] Generic B... |
46 |
#include <linux/bug.h> |
608e26196 generic bug: use ... |
47 |
#include <linux/sched.h> |
b2d091031 sched/headers: Pr... |
48 |
#include <linux/rculist.h> |
7664c5a1d [PATCH] Generic B... |
49 |
|
19d436268 debug: Add _ONCE(... |
50 |
extern struct bug_entry __start___bug_table[], __stop___bug_table[]; |
7664c5a1d [PATCH] Generic B... |
51 |
|
b93a531e3 allow bug table e... |
52 53 54 55 56 57 58 59 |
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 [PATCH] Generic B... |
60 |
#ifdef CONFIG_MODULES |
1fb9341ac module: put modul... |
61 |
/* Updates are protected by module mutex */ |
7664c5a1d [PATCH] Generic B... |
62 |
static LIST_HEAD(module_bug_list); |
19d436268 debug: Add _ONCE(... |
63 |
static struct bug_entry *module_find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
64 65 |
{ struct module *mod; |
19d436268 debug: Add _ONCE(... |
66 |
struct bug_entry *bug = NULL; |
7664c5a1d [PATCH] Generic B... |
67 |
|
0be964be0 module: Sanitize ... |
68 |
rcu_read_lock_sched(); |
0286b5ea1 lib/bug: Use RCU ... |
69 |
list_for_each_entry_rcu(mod, &module_bug_list, bug_list) { |
7664c5a1d [PATCH] Generic B... |
70 |
unsigned i; |
0286b5ea1 lib/bug: Use RCU ... |
71 |
bug = mod->bug_table; |
7664c5a1d [PATCH] Generic B... |
72 |
for (i = 0; i < mod->num_bugs; ++i, ++bug) |
b93a531e3 allow bug table e... |
73 |
if (bugaddr == bug_addr(bug)) |
0286b5ea1 lib/bug: Use RCU ... |
74 |
goto out; |
7664c5a1d [PATCH] Generic B... |
75 |
} |
0286b5ea1 lib/bug: Use RCU ... |
76 77 |
bug = NULL; out: |
0be964be0 module: Sanitize ... |
78 |
rcu_read_unlock_sched(); |
0286b5ea1 lib/bug: Use RCU ... |
79 80 |
return bug; |
7664c5a1d [PATCH] Generic B... |
81 |
} |
5336377d6 modules: Fix modu... |
82 83 |
void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) |
7664c5a1d [PATCH] Generic B... |
84 85 86 |
{ char *secstrings; unsigned int i; |
0be964be0 module: Sanitize ... |
87 |
lockdep_assert_held(&module_mutex); |
7664c5a1d [PATCH] Generic B... |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
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. |
0286b5ea1 lib/bug: Use RCU ... |
105 106 |
* Thus, this uses RCU to safely manipulate the bug list, since BUG * must run in non-interruptive state. |
7664c5a1d [PATCH] Generic B... |
107 |
*/ |
0286b5ea1 lib/bug: Use RCU ... |
108 |
list_add_rcu(&mod->bug_list, &module_bug_list); |
7664c5a1d [PATCH] Generic B... |
109 110 111 112 |
} void module_bug_cleanup(struct module *mod) { |
0be964be0 module: Sanitize ... |
113 |
lockdep_assert_held(&module_mutex); |
0286b5ea1 lib/bug: Use RCU ... |
114 |
list_del_rcu(&mod->bug_list); |
7664c5a1d [PATCH] Generic B... |
115 116 117 |
} #else |
19d436268 debug: Add _ONCE(... |
118 |
static inline struct bug_entry *module_find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
119 120 121 122 |
{ return NULL; } #endif |
19d436268 debug: Add _ONCE(... |
123 |
struct bug_entry *find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
124 |
{ |
19d436268 debug: Add _ONCE(... |
125 |
struct bug_entry *bug; |
7664c5a1d [PATCH] Generic B... |
126 127 |
for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) |
b93a531e3 allow bug table e... |
128 |
if (bugaddr == bug_addr(bug)) |
7664c5a1d [PATCH] Generic B... |
129 130 131 132 |
return bug; return module_find_bug(bugaddr); } |
608e26196 generic bug: use ... |
133 |
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) |
7664c5a1d [PATCH] Generic B... |
134 |
{ |
19d436268 debug: Add _ONCE(... |
135 |
struct bug_entry *bug; |
7664c5a1d [PATCH] Generic B... |
136 |
const char *file; |
19d436268 debug: Add _ONCE(... |
137 |
unsigned line, warning, once, done; |
7664c5a1d [PATCH] Generic B... |
138 139 140 141 142 |
if (!is_valid_bugaddr(bugaddr)) return BUG_TRAP_TYPE_NONE; bug = find_bug(bugaddr); |
7664c5a1d [PATCH] Generic B... |
143 144 145 146 147 148 |
file = NULL; line = 0; warning = 0; if (bug) { #ifdef CONFIG_DEBUG_BUGVERBOSE |
b93a531e3 allow bug table e... |
149 |
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS |
7664c5a1d [PATCH] Generic B... |
150 |
file = bug->file; |
b93a531e3 allow bug table e... |
151 152 153 |
#else file = (const char *)bug + bug->file_disp; #endif |
7664c5a1d [PATCH] Generic B... |
154 155 156 |
line = bug->line; #endif warning = (bug->flags & BUGFLAG_WARNING) != 0; |
19d436268 debug: Add _ONCE(... |
157 158 159 160 161 162 163 164 165 166 167 168 |
once = (bug->flags & BUGFLAG_ONCE) != 0; done = (bug->flags & BUGFLAG_DONE) != 0; if (warning && once) { if (done) return BUG_TRAP_TYPE_WARN; /* * Since this is the only store, concurrency is not an issue. */ bug->flags |= BUGFLAG_DONE; } |
7664c5a1d [PATCH] Generic B... |
169 170 171 172 |
} if (warning) { /* this is a WARN_ON rather than BUG/BUG_ON */ |
2553b67a1 lib/bug.c: use co... |
173 174 |
__warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs, NULL); |
7664c5a1d [PATCH] Generic B... |
175 176 |
return BUG_TRAP_TYPE_WARN; } |
2a8358d8a bug: define the "... |
177 |
printk(KERN_DEFAULT CUT_HERE); |
e2e7e0932 lib/bug.c: make W... |
178 |
|
7664c5a1d [PATCH] Generic B... |
179 |
if (file) |
c56ba7033 lib/bug.c: conver... |
180 181 |
pr_crit("kernel BUG at %s:%u! ", file, line); |
7664c5a1d [PATCH] Generic B... |
182 |
else |
c56ba7033 lib/bug.c: conver... |
183 184 185 |
pr_crit("Kernel BUG at %p [verbose debug info unavailable] ", (void *)bugaddr); |
7664c5a1d [PATCH] Generic B... |
186 187 188 |
return BUG_TRAP_TYPE_BUG; } |
aaf5dcfb2 kernel debug: sup... |
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
static void clear_once_table(struct bug_entry *start, struct bug_entry *end) { struct bug_entry *bug; for (bug = start; bug < end; bug++) bug->flags &= ~BUGFLAG_DONE; } void generic_bug_clear_once(void) { #ifdef CONFIG_MODULES struct module *mod; rcu_read_lock_sched(); list_for_each_entry_rcu(mod, &module_bug_list, bug_list) clear_once_table(mod->bug_table, mod->bug_table + mod->num_bugs); rcu_read_unlock_sched(); #endif clear_once_table(__start___bug_table, __stop___bug_table); } |