--- latest/arch/ppc/mm/fault.c Tue Feb 29 21:40:29 2000 +++ /usr/src/linux/arch/ppc/mm/fault.c Sat Aug 19 20:12:28 2000 @@ -185,10 +185,94 @@ do_sigbus: bad_page_fault(regs, address, error_code); } +/* + * The code below attempts to do kernel symbol lookups on backtraces. + * It also implements a not-so-close equivalent of Linux x86's Oops + * by executing a do_exit(SIGKILL) on behalf of the controlling task. + * This may or maynot leave your system in a fragile state. You should + * reboot soon after this pseudo-oops attempts to fix things for you. + * --Steven Bytnar, sbytnar at users sourceforge net + */ +#include +/* This function is defined with print_backtrace in arch/ppc/kernel/process.c */ +struct module *I_dbg_get_next_module(struct module *this_mod); + +struct module_symbol *I_dbg_get_symbol(struct module *this_mod, unsigned int symlistoffset); + +int I_find_symbol_for_address(char *address, + struct module **found_mod, + struct module_symbol **found_sym, + unsigned int *offset); + +int +I_find_symbol_for_address(char *address, + struct module **found_mod, + struct module_symbol **found_sym, + unsigned int *offset) +{ +/* This is brute force. This makes no attempt to optimize + * looking up addresses. It would have been smart to sort + * all the addresses into a hash table, but chances are we + * are low on resources and shouldn't do that during a panic. + */ + int wasfound = 0; + struct module *mod = NULL; + struct module_symbol *sym = NULL; + /* code symbols shouldn't be more than this much apart */ + const signed int symsearchrange = 128*1024; + signed int symoffset = symsearchrange; + while ((mod = I_dbg_get_next_module(mod))) + { + int symcount = 0; + while ((sym = I_dbg_get_symbol(mod, symcount))) + { + char *symaddr; + signed int addroffset; + symaddr = (char*)sym->value; + addroffset = address - symaddr; + if ((addroffset >= 0) && (addroffset < symoffset)) + { + *found_mod = mod; + *found_sym = sym; + *offset = symoffset = addroffset; + wasfound++; + if (addroffset == 0) + return wasfound; + } + symcount++; + } + } + return wasfound; +} +/* + * Called by debuggers for stack backtrace symbol dumps. + */ + +struct module * +I_dbg_get_next_module(struct module *this_mod) +{ + if (this_mod == NULL) + return module_list; + return this_mod->next; +} + +struct module_symbol * +I_dbg_get_symbol(struct module *this_mod, unsigned int symlistoffset) +{ + if (symlistoffset >= this_mod->nsyms) + return NULL; + return this_mod->syms+symlistoffset; +} + void bad_page_fault(struct pt_regs *regs, unsigned long address, unsigned long err) { unsigned long fixup; + extern struct task_struct *last_task_used_math; + extern struct task_struct *last_task_used_altivec; + struct task_struct *last_math = last_task_used_math; + struct task_struct *last_altivec = last_task_used_altivec; + if (user_mode(regs)) { force_sig(SIGSEGV, current); return; @@ -211,9 +295,49 @@ bad_page_fault(struct pt_regs *regs, uns if (debugger_kernel_faults) debugger(regs); #endif +#if 0 print_backtrace((unsigned long *)regs->gpr[1]); - panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d", + panic("kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d\n", regs->nip, regs->link, address, current->comm, current->pid); +#else + printk(KERN_ALERT + "kernel access of bad area pc %lx lr %lx address %lX tsk %s/%d\n", + regs->nip, regs->link, address, current->comm, current->pid); +{ +struct module *mod; +struct module_symbol *sym; +unsigned int offset; + +#define WAIT_TIME 0x40000000 + +#define PRINT_POINTER_SYMBOL(string, symbol) \ + if ((symbol) && I_find_symbol_for_address((void*)(symbol), &mod, &sym, &offset)) { \ + printk(KERN_ALERT "%s: %p @ %s.%s+0x%x\n", string, (void*)(symbol), mod->name, sym->name, offset); \ + } else { \ + printk(KERN_ALERT "%s: %p\n", string, (void*)(symbol)); \ + } + + PRINT_POINTER_SYMBOL("last FP task", last_math); + PRINT_POINTER_SYMBOL("last VEC task", last_altivec); + PRINT_POINTER_SYMBOL("DAR: ", address); + PRINT_POINTER_SYMBOL("PC: ", regs->nip); + PRINT_POINTER_SYMBOL("LR: ", regs->link); + + print_backtrace((unsigned long *)regs->gpr[1]); + +} +/* SIGSEGV works pretty well */ + printk(KERN_ALERT "bad_page_fault waiting to do_exit(SIGKILL)\n\n"); +{ volatile unsigned int i; for (i = 0; i < WAIT_TIME; i++) {} } + printk(KERN_ALERT "bad_page_fault calling do_exit(SIGKILL)\n"); + do_exit(SIGKILL); /* attempt a seg fault -- sorta works. */ + /* leaves the system somewhat unstable.. good processes may die randomly. */ + printk(KERN_ALERT "bad_page_fault FAILED TO doexit(SIGKILL)\n\n"); + +{ volatile unsigned int i; for (i = 0; i < WAIT_TIME; i++) {} } + force_sig(SIGKILL, current); /* use current registers ? */ + printk(KERN_ALERT "bad_page_fault FAILED TO force_sig(SIGKILL)\n\n"); +#endif } #ifdef CONFIG_8xx