Commit 7664c5a1da4711bb6383117f51b94c8dc8f3f1cd

Authored by Jeremy Fitzhardinge
Committed by Linus Torvalds
1 parent c48f70c3d0

[PATCH] Generic BUG implementation

This patch adds common handling for kernel BUGs, for use by architectures as
they wish.  The code is derived from arch/powerpc.

The advantages of having common BUG handling are:
 - consistent BUG reporting across architectures
 - shared implementation of out-of-line file/line data
 - implement CONFIG_DEBUG_BUGVERBOSE consistently

This means that in inline impact of BUG is just the illegal instruction
itself, which is an improvement for i386 and x86-64.

A BUG is represented in the instruction stream as an illegal instruction,
which has file/line information associated with it.  This extra information is
stored in the __bug_table section in the ELF file.

When the kernel gets an illegal instruction, it first confirms it might
possibly be from a BUG (ie, in kernel mode, the right illegal instruction).
It then calls report_bug().  This searches __bug_table for a matching
instruction pointer, and if found, prints the corresponding file/line
information.  If report_bug() determines that it wasn't a BUG which caused the
trap, it returns BUG_TRAP_TYPE_NONE.

Some architectures (powerpc) implement WARN using the same mechanism; if the
illegal instruction was the result of a WARN, then report_bug(Q) returns
CONFIG_DEBUG_BUGVERBOSE; otherwise it returns BUG_TRAP_TYPE_BUG.

lib/bug.c keeps a list of loaded modules which can be searched for __bug_table
entries.  The architecture must call
module_bug_finalize()/module_bug_cleanup() from its corresponding
module_finalize/cleanup functions.

Unsetting CONFIG_DEBUG_BUGVERBOSE will reduce the kernel size by some amount.
At the very least, filename and line information will not be recorded for each
but, but architectures may decide to store no extra information per BUG at
all.

Unfortunately, gcc doesn't have a general way to mark an asm() as noreturn, so
architectures will generally have to include an infinite loop (or similar) in
the BUG code, so that gcc knows execution won't continue beyond that point.
gcc does have a __builtin_trap() operator which may be useful to achieve the
same effect, unfortunately it cannot be used to actually implement the BUG
itself, because there's no way to get the instruction's address for use in
generating the __bug_table entry.

[randy.dunlap@oracle.com: Handle BUG=n, GENERIC_BUG=n to prevent build errors]
[bunk@stusta.de: include/linux/bug.h must always #include <linux/module.h]
Signed-off-by: Jeremy Fitzhardinge <jeremy@goop.org>
Cc: Andi Kleen <ak@muc.de>
Cc: Hugh Dickens <hugh@veritas.com>
Cc: Michael Ellerman <michael@ellerman.id.au>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 7 changed files with 244 additions and 1 deletions Side-by-side Diff

include/asm-generic/bug.h
... ... @@ -4,6 +4,22 @@
4 4 #include <linux/compiler.h>
5 5  
6 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 23 #ifndef HAVE_ARCH_BUG
8 24 #define BUG() do { \
9 25 printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
include/asm-generic/vmlinux.lds.h
... ... @@ -218,6 +218,14 @@
218 218 .stab.indexstr 0 : { *(.stab.indexstr) } \
219 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 229 #define NOTES \
222 230 .notes : { *(.note.*) } :note
223 231  
  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 */
include/linux/module.h
... ... @@ -319,6 +319,13 @@
319 319  
320 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 329 #ifdef CONFIG_MODULE_UNLOAD
323 330 /* Reference counts */
324 331 struct module_ref ref[NR_CPUS];
... ... @@ -285,7 +285,7 @@
285 285 config DEBUG_BUGVERBOSE
286 286 bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED
287 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 289 default !EMBEDDED
290 290 help
291 291 Say Y here to make BUG() panics output the file name and line number
... ... @@ -55,6 +55,8 @@
55 55  
56 56 obj-$(CONFIG_SWIOTLB) += swiotlb.o
57 57  
  58 +lib-$(CONFIG_GENERIC_BUG) += bug.o
  59 +
58 60 hostprogs-y := gen_crc32table
59 61 clean-files := crc32table.h
60 62  
  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 +}