Commit d0c0cf74 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: Update BUG handling based on ppc32

parent d8b4bc5c
...@@ -376,15 +376,57 @@ int apply_relocate_add(Elf64_Shdr *sechdrs, ...@@ -376,15 +376,57 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
return 0; return 0;
} }
LIST_HEAD(module_bug_list);
int module_finalize(const Elf_Ehdr *hdr, int module_finalize(const Elf_Ehdr *hdr,
const Elf_Shdr *sechdrs, struct module *me) const Elf_Shdr *sechdrs, struct module *me)
{ {
char *secstrings;
unsigned int i;
me->arch.bug_table = NULL;
me->arch.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;
me->arch.bug_table = (void *) sechdrs[i].sh_addr;
me->arch.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(&me->arch.bug_list, &module_bug_list);
sort_ex_table((struct exception_table_entry *)me->extable, sort_ex_table((struct exception_table_entry *)me->extable,
(struct exception_table_entry *)me->extable + (struct exception_table_entry *)me->extable +
me->num_exentries); me->num_exentries);
return 0; return 0;
} }
void module_arch_cleanup(struct module *mod) void module_arch_cleanup(struct module *mod)
{ {
list_del(&mod->arch.bug_list);
}
struct bug_entry *module_find_bug(unsigned long bugaddr)
{
struct mod_arch_specific *mod;
unsigned int i;
struct bug_entry *bug;
list_for_each_entry(mod, &module_bug_list, bug_list) {
bug = mod->bug_table;
for (i = 0; i < mod->num_bugs; ++i, ++bug)
if (bugaddr == bug->bug_addr)
return bug;
}
return NULL;
} }
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/config.h> #include <linux/config.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -306,6 +307,56 @@ static void parse_fpe(struct pt_regs *regs) ...@@ -306,6 +307,56 @@ static void parse_fpe(struct pt_regs *regs)
_exception(SIGFPE, &info, regs); _exception(SIGFPE, &info, regs);
} }
/*
* Look through the list of trap instructions that are used for BUG(),
* BUG_ON() and WARN_ON() and see if we hit one. At this point we know
* that the exception was caused by a trap instruction of some kind.
* Returns 1 if we should continue (i.e. it was a WARN_ON) or 0
* otherwise.
*/
extern struct bug_entry __start___bug_table[], __stop___bug_table[];
#ifndef CONFIG_MODULES
#define module_find_bug(x) NULL
#endif
static struct bug_entry *find_bug(unsigned long bugaddr)
{
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);
}
int
check_bug_trap(struct pt_regs *regs)
{
struct bug_entry *bug;
unsigned long addr;
if (regs->msr & MSR_PR)
return 0; /* not in kernel */
addr = regs->nip; /* address of trap instruction */
if (addr < PAGE_OFFSET)
return 0;
bug = find_bug(regs->nip);
if (bug == NULL)
return 0;
if (bug->line & BUG_WARNING_TRAP) {
/* this is a WARN_ON rather than BUG/BUG_ON */
printk(KERN_ERR "Badness in %s at %s:%d\n",
bug->function, bug->file,
bug->line & ~BUG_WARNING_TRAP);
dump_stack();
return 1;
}
printk(KERN_CRIT "kernel BUG in %s at %s:%d!\n",
bug->function, bug->file, bug->line);
return 0;
}
void void
ProgramCheckException(struct pt_regs *regs) ProgramCheckException(struct pt_regs *regs)
{ {
...@@ -330,6 +381,10 @@ ProgramCheckException(struct pt_regs *regs) ...@@ -330,6 +381,10 @@ ProgramCheckException(struct pt_regs *regs)
if (debugger_bpt && debugger_bpt(regs)) if (debugger_bpt && debugger_bpt(regs))
return; return;
#endif #endif
if (check_bug_trap(regs)) {
regs->nip += 4;
return;
}
info.si_signo = SIGTRAP; info.si_signo = SIGTRAP;
info.si_errno = 0; info.si_errno = 0;
info.si_code = TRAP_BRKPT; info.si_code = TRAP_BRKPT;
......
...@@ -65,6 +65,10 @@ SECTIONS ...@@ -65,6 +65,10 @@ SECTIONS
__ex_table : { *(__ex_table) } __ex_table : { *(__ex_table) }
__stop___ex_table = .; __stop___ex_table = .;
__start___bug_table = .;
__bug_table : { *(__bug_table) }
__stop___bug_table = .;
__start___ftr_fixup = .; __start___ftr_fixup = .;
__ftr_fixup : { *(__ftr_fixup) } __ftr_fixup : { *(__ftr_fixup) }
__stop___ftr_fixup = .; __stop___ftr_fixup = .;
......
#ifndef _PPC64_BUG_H #ifndef _PPC64_BUG_H
#define _PPC64_BUG_H #define _PPC64_BUG_H
#include <linux/config.h>
/* /*
* Define an illegal instr to trap on the bug. * Define an illegal instr to trap on the bug.
* We don't use 0 because that marks the end of a function * We don't use 0 because that marks the end of a function
...@@ -13,29 +11,48 @@ ...@@ -13,29 +11,48 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#ifdef CONFIG_XMON struct bug_entry {
struct pt_regs; unsigned long bug_addr;
extern void xmon(struct pt_regs *excp); long line;
#define BUG() do { \ const char *file;
printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ const char *function;
xmon(0); \ };
} while (0)
#else /*
#define BUG() do { \ * If this bit is set in the line number it means that the trap
printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ * is for WARN_ON rather than BUG or BUG_ON.
__asm__ __volatile__(".long " BUG_ILLEGAL_INSTR); \ */
#define BUG_WARNING_TRAP 0x1000000
#define BUG() do { \
__asm__ __volatile__( \
"1: twi 31,0,0\n" \
".section __bug_table,\"a\"\n\t" \
" .llong 1b,%0,%1,%2\n" \
".previous" \
: : "i" (__LINE__), "i" (__FILE__), "i" (__FUNCTION__)); \
} while (0) } while (0)
#endif
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) #define BUG_ON(x) do { \
__asm__ __volatile__( \
"1: tdnei %0,0\n" \
".section __bug_table,\"a\"\n\t" \
" .llong 1b,%1,%2,%3\n" \
".previous" \
: : "r" (x), "i" (__LINE__), "i" (__FILE__), \
"i" (__FUNCTION__)); \
} while (0)
#define PAGE_BUG(page) do { BUG(); } while (0) #define PAGE_BUG(page) do { BUG(); } while (0)
#define WARN_ON(condition) do { \ #define WARN_ON(x) do { \
if (unlikely((condition)!=0)) { \ __asm__ __volatile__( \
printk("Badness in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ "1: tdnei %0,0\n" \
dump_stack(); \ ".section __bug_table,\"a\"\n\t" \
} \ " .llong 1b,%1,%2,%3\n" \
".previous" \
: : "r" (x), "i" (__LINE__ + BUG_WARNING_TRAP), \
"i" (__FILE__), "i" (__FUNCTION__)); \
} while (0) } while (0)
#endif #endif
......
#ifndef _ASM_PPC64_MODULE_H #ifndef _ASM_PPC64_MODULE_H
#define _ASM_PPC64_MODULE_H #define _ASM_PPC64_MODULE_H
#include <linux/list.h>
#include <asm/bug.h>
struct mod_arch_specific struct mod_arch_specific
{ {
/* Index of stubs section within module. */ /* Index of stubs section within module. */
...@@ -8,8 +11,15 @@ struct mod_arch_specific ...@@ -8,8 +11,15 @@ struct mod_arch_specific
/* What section is the TOC? */ /* What section is the TOC? */
unsigned int toc_section; unsigned int toc_section;
/* List of BUG addresses, source line numbers and filenames */
struct list_head bug_list;
struct bug_entry *bug_table;
unsigned int num_bugs;
}; };
extern struct bug_entry *module_find_bug(unsigned long bugaddr);
#define Elf_Shdr Elf64_Shdr #define Elf_Shdr Elf64_Shdr
#define Elf_Sym Elf64_Sym #define Elf_Sym Elf64_Sym
#define Elf_Ehdr Elf64_Ehdr #define Elf_Ehdr Elf64_Ehdr
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment