The patch titled Generic implementatation of BUG has been added to the -mm tree. Its filename is generic-implementatation-of-bug.patch See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this ------------------------------------------------------ Subject: Generic implementatation of BUG From: Jeremy Fitzhardinge <jeremy@xxxxxxxx> 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 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/function 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/function information. Some architectures (powerpc) implement WARN using the same mechanism; if the illegal instruction was the result of a WARN, then report_bug() returns 1; otherwise it returns 0. 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. This patch also converts i386, x86-64 and powerpc to use this infrastructure. I have only tested i386; x86-64 and powerpc are not even compile-tested in this patch. Because powerpc also records the function name, I added this to i386 and x86-64 for consistency. Strictly speaking the function name is redundant with kallsyms, so perhaps it can be dropped from powerpc. i386 implements CONFIG_DEBUG_BUGVERBOSE, but x86-64 and powerpc do not. This should probably be made more consistent. [akpm@xxxxxxxx: Fix CONFIG_MODULES=n] Signed-off-by: Jeremy Fitzhardinge <jeremy@xxxxxxxx> Cc: Andi Kleen <ak@xxxxxx> Cc: Hugh Dickens <hugh@xxxxxxxxxxx> Cc: Michael Ellerman <michael@xxxxxxxxxxxxxx> Cc: Paul Mackerras <paulus@xxxxxxxxx> Cc: Benjamin Herrenschmidt <benh@xxxxxxxxxxxxxxxxxxx> Cc: Rusty Russell <rusty@xxxxxxxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxx> --- include/asm-generic/bug.h | 14 ++ include/asm-generic/vmlinux.lds.h | 8 + include/linux/bug.h | 33 +++++ include/linux/module.h | 7 + lib/Kconfig.debug | 2 lib/Makefile | 2 lib/bug.c | 163 ++++++++++++++++++++++++++++ 7 files changed, 228 insertions(+), 1 deletion(-) diff -puN include/asm-generic/bug.h~generic-implementatation-of-bug include/asm-generic/bug.h --- a/include/asm-generic/bug.h~generic-implementatation-of-bug +++ a/include/asm-generic/bug.h @@ -4,6 +4,20 @@ #include <linux/compiler.h> #ifdef CONFIG_BUG + +#ifdef CONFIG_GENERIC_BUG +struct bug_entry { + unsigned long bug_addr; +#ifdef CONFIG_DEBUG_BUGVERBOSE + const char *file; + unsigned short line; +#endif + unsigned short flags; +}; + +#define BUGFLAG_WARNING (1<<0) +#endif /* CONFIG_GENERIC_BUG */ + #ifndef HAVE_ARCH_BUG #define BUG() do { \ printk("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \ diff -puN include/asm-generic/vmlinux.lds.h~generic-implementatation-of-bug include/asm-generic/vmlinux.lds.h --- a/include/asm-generic/vmlinux.lds.h~generic-implementatation-of-bug +++ a/include/asm-generic/vmlinux.lds.h @@ -195,5 +195,13 @@ .stab.indexstr 0 : { *(.stab.indexstr) } \ .comment 0 : { *(.comment) } +#define BUG_TABLE \ + . = ALIGN(8); \ + __bug_table : AT(ADDR(__bug_table) - LOAD_OFFSET) { \ + __start___bug_table = .; \ + *(__bug_table) \ + __stop___bug_table = .; \ + } + #define NOTES \ .notes : { *(.note.*) } :note diff -puN /dev/null include/linux/bug.h --- /dev/null +++ a/include/linux/bug.h @@ -0,0 +1,33 @@ +#ifndef _LINUX_BUG_H +#define _LINUX_BUG_H + +#include <asm/bug.h> + +#ifdef CONFIG_GENERIC_BUG +#include <linux/module.h> +#include <asm-generic/bug.h> + +static inline int is_warning_bug(const struct bug_entry *bug) +{ + return bug->flags & BUGFLAG_WARNING; +} + +enum bug_trap_type { + BUG_TRAP_TYPE_NONE = 0, + BUG_TRAP_TYPE_WARN = 1, + BUG_TRAP_TYPE_BUG = 2, +}; + +const struct bug_entry *find_bug(unsigned long bugaddr); + +enum bug_trap_type report_bug(unsigned long bug_addr); + +int module_bug_finalize(const Elf_Ehdr *, const Elf_Shdr *, + struct module *); +void module_bug_cleanup(struct module *); + +/* These are defined by the architecture */ +int is_valid_bugaddr(unsigned long addr); + +#endif /* CONFIG_GENERIC_BUG */ +#endif /* _LINUX_BUG_H */ diff -puN include/linux/module.h~generic-implementatation-of-bug include/linux/module.h --- a/include/linux/module.h~generic-implementatation-of-bug +++ a/include/linux/module.h @@ -322,6 +322,13 @@ struct module unsigned int taints; /* same bits as kernel:tainted */ +#ifdef CONFIG_GENERIC_BUG + /* Support for BUG */ + struct list_head bug_list; + struct bug_entry *bug_table; + unsigned num_bugs; +#endif + #ifdef CONFIG_MODULE_UNLOAD /* Reference counts */ struct module_ref ref[NR_CPUS]; diff -puN lib/Kconfig.debug~generic-implementatation-of-bug lib/Kconfig.debug --- a/lib/Kconfig.debug~generic-implementatation-of-bug +++ a/lib/Kconfig.debug @@ -284,7 +284,7 @@ config DEBUG_HIGHMEM config DEBUG_BUGVERBOSE bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EMBEDDED depends on BUG - depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH + depends on ARM || ARM26 || AVR32 || M32R || M68K || SPARC32 || SPARC64 || X86_32 || FRV || SUPERH || GENERIC_BUG default !EMBEDDED help Say Y here to make BUG() panics output the file name and line number diff -puN lib/Makefile~generic-implementatation-of-bug lib/Makefile --- a/lib/Makefile~generic-implementatation-of-bug +++ a/lib/Makefile @@ -55,6 +55,8 @@ obj-$(CONFIG_AUDIT_GENERIC) += audit.o obj-$(CONFIG_SWIOTLB) += swiotlb.o +lib-$(CONFIG_GENERIC_BUG) += bug.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff -puN /dev/null lib/bug.c --- /dev/null +++ a/lib/bug.c @@ -0,0 +1,163 @@ +/* + 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. + 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@xxxxxxxx> 2006 + */ +#include <linux/list.h> +#include <linux/module.h> +#include <linux/bug.h> + +extern const struct bug_entry __start___bug_table[], __stop___bug_table[]; + +#ifdef CONFIG_MODULES +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) + if (bugaddr == bug->bug_addr) + return bug; + } + return NULL; +} + +int module_bug_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs, + struct module *mod) +{ + 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); + + return 0; +} + +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) + if (bugaddr == bug->bug_addr) + return bug; + + return module_find_bug(bugaddr); +} + +enum bug_trap_type report_bug(unsigned long bugaddr) +{ + 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); + + printk(KERN_EMERG "------------[ cut here ]------------\n"); + + file = NULL; + line = 0; + warning = 0; + + if (bug) { +#ifdef CONFIG_DEBUG_BUGVERBOSE + file = bug->file; + line = bug->line; +#endif + warning = (bug->flags & BUGFLAG_WARNING) != 0; + } + + if (warning) { + /* this is a WARN_ON rather than BUG/BUG_ON */ + if (file) + printk(KERN_ERR "Badness at %s:%u\n", + file, line); + else + printk(KERN_ERR "Badness at %p " + "[verbose debug info unavailable]\n", + (void *)bugaddr); + + dump_stack(); + return BUG_TRAP_TYPE_WARN; + } + + if (file) + printk(KERN_CRIT "kernel BUG at %s:%u!\n", + file, line); + else + printk(KERN_CRIT "Kernel BUG at %p " + "[verbose debug info unavailable]\n", + (void *)bugaddr); + + return BUG_TRAP_TYPE_BUG; +} _ Patches currently in -mm which might be from jeremy@xxxxxxxx are origin.patch x86-remove-default_ldt-and-simplify-ldt-setting.patch generic-implementatation-of-bug.patch generic-bug-for-i386.patch generic-bug-for-x86-64.patch generic-bug-for-powerpc.patch bug-test-1.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html