Blame view
lib/bug.c
5.86 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> |
58f6e3844 ftrace,bug: Impro... |
49 |
#include <linux/ftrace.h> |
7664c5a1d [PATCH] Generic B... |
50 |
|
19d436268 debug: Add _ONCE(... |
51 |
extern struct bug_entry __start___bug_table[], __stop___bug_table[]; |
7664c5a1d [PATCH] Generic B... |
52 |
|
b93a531e3 allow bug table e... |
53 54 55 56 57 58 59 60 |
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... |
61 |
#ifdef CONFIG_MODULES |
1fb9341ac module: put modul... |
62 |
/* Updates are protected by module mutex */ |
7664c5a1d [PATCH] Generic B... |
63 |
static LIST_HEAD(module_bug_list); |
19d436268 debug: Add _ONCE(... |
64 |
static struct bug_entry *module_find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
65 66 |
{ struct module *mod; |
19d436268 debug: Add _ONCE(... |
67 |
struct bug_entry *bug = NULL; |
7664c5a1d [PATCH] Generic B... |
68 |
|
0be964be0 module: Sanitize ... |
69 |
rcu_read_lock_sched(); |
0286b5ea1 lib/bug: Use RCU ... |
70 |
list_for_each_entry_rcu(mod, &module_bug_list, bug_list) { |
7664c5a1d [PATCH] Generic B... |
71 |
unsigned i; |
0286b5ea1 lib/bug: Use RCU ... |
72 |
bug = mod->bug_table; |
7664c5a1d [PATCH] Generic B... |
73 |
for (i = 0; i < mod->num_bugs; ++i, ++bug) |
b93a531e3 allow bug table e... |
74 |
if (bugaddr == bug_addr(bug)) |
0286b5ea1 lib/bug: Use RCU ... |
75 |
goto out; |
7664c5a1d [PATCH] Generic B... |
76 |
} |
0286b5ea1 lib/bug: Use RCU ... |
77 78 |
bug = NULL; out: |
0be964be0 module: Sanitize ... |
79 |
rcu_read_unlock_sched(); |
0286b5ea1 lib/bug: Use RCU ... |
80 81 |
return bug; |
7664c5a1d [PATCH] Generic B... |
82 |
} |
5336377d6 modules: Fix modu... |
83 84 |
void module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, struct module *mod) |
7664c5a1d [PATCH] Generic B... |
85 86 87 |
{ char *secstrings; unsigned int i; |
0be964be0 module: Sanitize ... |
88 |
lockdep_assert_held(&module_mutex); |
7664c5a1d [PATCH] Generic B... |
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
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 ... |
106 107 |
* Thus, this uses RCU to safely manipulate the bug list, since BUG * must run in non-interruptive state. |
7664c5a1d [PATCH] Generic B... |
108 |
*/ |
0286b5ea1 lib/bug: Use RCU ... |
109 |
list_add_rcu(&mod->bug_list, &module_bug_list); |
7664c5a1d [PATCH] Generic B... |
110 111 112 113 |
} void module_bug_cleanup(struct module *mod) { |
0be964be0 module: Sanitize ... |
114 |
lockdep_assert_held(&module_mutex); |
0286b5ea1 lib/bug: Use RCU ... |
115 |
list_del_rcu(&mod->bug_list); |
7664c5a1d [PATCH] Generic B... |
116 117 118 |
} #else |
19d436268 debug: Add _ONCE(... |
119 |
static inline struct bug_entry *module_find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
120 121 122 123 |
{ return NULL; } #endif |
19d436268 debug: Add _ONCE(... |
124 |
struct bug_entry *find_bug(unsigned long bugaddr) |
7664c5a1d [PATCH] Generic B... |
125 |
{ |
19d436268 debug: Add _ONCE(... |
126 |
struct bug_entry *bug; |
7664c5a1d [PATCH] Generic B... |
127 128 |
for (bug = __start___bug_table; bug < __stop___bug_table; ++bug) |
b93a531e3 allow bug table e... |
129 |
if (bugaddr == bug_addr(bug)) |
7664c5a1d [PATCH] Generic B... |
130 131 132 133 |
return bug; return module_find_bug(bugaddr); } |
608e26196 generic bug: use ... |
134 |
enum bug_trap_type report_bug(unsigned long bugaddr, struct pt_regs *regs) |
7664c5a1d [PATCH] Generic B... |
135 |
{ |
19d436268 debug: Add _ONCE(... |
136 |
struct bug_entry *bug; |
7664c5a1d [PATCH] Generic B... |
137 |
const char *file; |
19d436268 debug: Add _ONCE(... |
138 |
unsigned line, warning, once, done; |
7664c5a1d [PATCH] Generic B... |
139 140 141 142 143 |
if (!is_valid_bugaddr(bugaddr)) return BUG_TRAP_TYPE_NONE; bug = find_bug(bugaddr); |
1b4cfe3c0 lib/bug.c: exclud... |
144 145 |
if (!bug) return BUG_TRAP_TYPE_NONE; |
7664c5a1d [PATCH] Generic B... |
146 |
|
58f6e3844 ftrace,bug: Impro... |
147 |
disable_trace_on_warning(); |
7664c5a1d [PATCH] Generic B... |
148 149 150 151 152 153 |
file = NULL; line = 0; warning = 0; if (bug) { #ifdef CONFIG_DEBUG_BUGVERBOSE |
b93a531e3 allow bug table e... |
154 |
#ifndef CONFIG_GENERIC_BUG_RELATIVE_POINTERS |
7664c5a1d [PATCH] Generic B... |
155 |
file = bug->file; |
b93a531e3 allow bug table e... |
156 157 158 |
#else file = (const char *)bug + bug->file_disp; #endif |
7664c5a1d [PATCH] Generic B... |
159 160 161 |
line = bug->line; #endif warning = (bug->flags & BUGFLAG_WARNING) != 0; |
19d436268 debug: Add _ONCE(... |
162 163 164 165 166 167 168 169 170 171 172 173 |
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... |
174 |
} |
a44f71a9a bug: move WARN_ON... |
175 176 177 178 179 180 181 182 |
/* * BUG() and WARN_ON() families don't print a custom debug message * before triggering the exception handler, so we must add the * "cut here" line now. WARN() issues its own "cut here" before the * extra debugging message it writes before triggering the handler. */ if ((bug->flags & BUGFLAG_NO_CUT_HERE) == 0) printk(KERN_DEFAULT CUT_HERE); |
7664c5a1d [PATCH] Generic B... |
183 184 |
if (warning) { /* this is a WARN_ON rather than BUG/BUG_ON */ |
2553b67a1 lib/bug.c: use co... |
185 186 |
__warn(file, line, (void *)bugaddr, BUG_GET_TAINT(bug), regs, NULL); |
7664c5a1d [PATCH] Generic B... |
187 188 189 190 |
return BUG_TRAP_TYPE_WARN; } if (file) |
c56ba7033 lib/bug.c: conver... |
191 192 |
pr_crit("kernel BUG at %s:%u! ", file, line); |
7664c5a1d [PATCH] Generic B... |
193 |
else |
0862ca422 bug: use %pB in B... |
194 195 |
pr_crit("Kernel BUG at %pB [verbose debug info unavailable] ", |
c56ba7033 lib/bug.c: conver... |
196 |
(void *)bugaddr); |
7664c5a1d [PATCH] Generic B... |
197 198 199 |
return BUG_TRAP_TYPE_BUG; } |
aaf5dcfb2 kernel debug: sup... |
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
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); } |