[AVR32] Clean up exception handling code

* Use generic BUG() handling
  * Remove some useless debug statements
  * Use a common function _exception() to send signals or oops when
    an exception can't be handled. This makes sure init doesn't
    enter an infinite exception loop as well. Borrowed from powerpc.
  * Add some basic exception tracing support to the page fault code.
  * Rework dump_stack(), show_regs() and friends and move everything
    into process.c
  * Print information about configuration options and chip type when
    oopsing

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
This commit is contained in:
Haavard Skinnemoen 2007-03-13 17:59:11 +01:00
parent 3b328c9809
commit 623b0355d5
9 changed files with 403 additions and 418 deletions

View file

@ -68,6 +68,11 @@ config GENERIC_CALIBRATE_DELAY
bool bool
default y default y
config GENERIC_BUG
bool
default y
depends on BUG
source "init/Kconfig" source "init/Kconfig"
menu "System Type and features" menu "System Type and features"

View file

@ -12,10 +12,11 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/moduleloader.h> #include <linux/bug.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleloader.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
void *module_alloc(unsigned long size) void *module_alloc(unsigned long size)
@ -315,10 +316,10 @@ int module_finalize(const Elf_Ehdr *hdr, const Elf_Shdr *sechdrs,
vfree(module->arch.syminfo); vfree(module->arch.syminfo);
module->arch.syminfo = NULL; module->arch.syminfo = NULL;
return 0; return module_bug_finalize(hdr, sechdrs, module);
} }
void module_arch_cleanup(struct module *module) void module_arch_cleanup(struct module *module)
{ {
module_bug_cleanup(module);
} }

View file

@ -11,6 +11,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
@ -115,39 +116,178 @@ void release_thread(struct task_struct *dead_task)
/* do nothing */ /* do nothing */
} }
static void dump_mem(const char *str, const char *log_lvl,
unsigned long bottom, unsigned long top)
{
unsigned long p;
int i;
printk("%s%s(0x%08lx to 0x%08lx)\n", log_lvl, str, bottom, top);
for (p = bottom & ~31; p < top; ) {
printk("%s%04lx: ", log_lvl, p & 0xffff);
for (i = 0; i < 8; i++, p += 4) {
unsigned int val;
if (p < bottom || p >= top)
printk(" ");
else {
if (__get_user(val, (unsigned int __user *)p)) {
printk("\n");
goto out;
}
printk("%08x ", val);
}
}
printk("\n");
}
out:
return;
}
static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
{
return (p > (unsigned long)tinfo)
&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
}
#ifdef CONFIG_FRAME_POINTER
static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs, const char *log_lvl)
{
unsigned long lr, fp;
struct thread_info *tinfo;
if (regs)
fp = regs->r7;
else if (tsk == current)
asm("mov %0, r7" : "=r"(fp));
else
fp = tsk->thread.cpu_context.r7;
/*
* Walk the stack as long as the frame pointer (a) is within
* the kernel stack of the task, and (b) it doesn't move
* downwards.
*/
tinfo = task_thread_info(tsk);
printk("%sCall trace:\n", log_lvl);
while (valid_stack_ptr(tinfo, fp)) {
unsigned long new_fp;
lr = *(unsigned long *)fp;
#ifdef CONFIG_KALLSYMS
printk("%s [<%08lx>] ", log_lvl, lr);
#else
printk(" [<%08lx>] ", lr);
#endif
print_symbol("%s\n", lr);
new_fp = *(unsigned long *)(fp + 4);
if (new_fp <= fp)
break;
fp = new_fp;
}
printk("\n");
}
#else
static void show_trace_log_lvl(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs, const char *log_lvl)
{
unsigned long addr;
printk("%sCall trace:\n", log_lvl);
while (!kstack_end(sp)) {
addr = *sp++;
if (kernel_text_address(addr)) {
#ifdef CONFIG_KALLSYMS
printk("%s [<%08lx>] ", log_lvl, addr);
#else
printk(" [<%08lx>] ", addr);
#endif
print_symbol("%s\n", addr);
}
}
printk("\n");
}
#endif
void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
struct pt_regs *regs, const char *log_lvl)
{
struct thread_info *tinfo;
if (sp == 0) {
if (tsk)
sp = tsk->thread.cpu_context.ksp;
else
sp = (unsigned long)&tinfo;
}
if (!tsk)
tsk = current;
tinfo = task_thread_info(tsk);
if (valid_stack_ptr(tinfo, sp)) {
dump_mem("Stack: ", log_lvl, sp,
THREAD_SIZE + (unsigned long)tinfo);
show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);
}
}
void show_stack(struct task_struct *tsk, unsigned long *stack)
{
show_stack_log_lvl(tsk, (unsigned long)stack, NULL, "");
}
void dump_stack(void)
{
unsigned long stack;
show_trace_log_lvl(current, &stack, NULL, "");
}
EXPORT_SYMBOL(dump_stack);
static const char *cpu_modes[] = { static const char *cpu_modes[] = {
"Application", "Supervisor", "Interrupt level 0", "Interrupt level 1", "Application", "Supervisor", "Interrupt level 0", "Interrupt level 1",
"Interrupt level 2", "Interrupt level 3", "Exception", "NMI" "Interrupt level 2", "Interrupt level 3", "Exception", "NMI"
}; };
void show_regs(struct pt_regs *regs) void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl)
{ {
unsigned long sp = regs->sp; unsigned long sp = regs->sp;
unsigned long lr = regs->lr; unsigned long lr = regs->lr;
unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT; unsigned long mode = (regs->sr & MODE_MASK) >> MODE_SHIFT;
if (!user_mode(regs)) if (!user_mode(regs)) {
sp = (unsigned long)regs + FRAME_SIZE_FULL; sp = (unsigned long)regs + FRAME_SIZE_FULL;
print_symbol("PC is at %s\n", instruction_pointer(regs)); printk("%s", log_lvl);
print_symbol("LR is at %s\n", lr); print_symbol("PC is at %s\n", instruction_pointer(regs));
printk("pc : [<%08lx>] lr : [<%08lx>] %s\n" printk("%s", log_lvl);
"sp : %08lx r12: %08lx r11: %08lx\n", print_symbol("LR is at %s\n", lr);
instruction_pointer(regs), }
lr, print_tainted(), sp, regs->r12, regs->r11);
printk("r10: %08lx r9 : %08lx r8 : %08lx\n", printk("%spc : [<%08lx>] lr : [<%08lx>] %s\n"
regs->r10, regs->r9, regs->r8); "%ssp : %08lx r12: %08lx r11: %08lx\n",
printk("r7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n", log_lvl, instruction_pointer(regs), lr, print_tainted(),
regs->r7, regs->r6, regs->r5, regs->r4); log_lvl, sp, regs->r12, regs->r11);
printk("r3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n", printk("%sr10: %08lx r9 : %08lx r8 : %08lx\n",
regs->r3, regs->r2, regs->r1, regs->r0); log_lvl, regs->r10, regs->r9, regs->r8);
printk("Flags: %c%c%c%c%c\n", printk("%sr7 : %08lx r6 : %08lx r5 : %08lx r4 : %08lx\n",
log_lvl, regs->r7, regs->r6, regs->r5, regs->r4);
printk("%sr3 : %08lx r2 : %08lx r1 : %08lx r0 : %08lx\n",
log_lvl, regs->r3, regs->r2, regs->r1, regs->r0);
printk("%sFlags: %c%c%c%c%c\n", log_lvl,
regs->sr & SR_Q ? 'Q' : 'q', regs->sr & SR_Q ? 'Q' : 'q',
regs->sr & SR_V ? 'V' : 'v', regs->sr & SR_V ? 'V' : 'v',
regs->sr & SR_N ? 'N' : 'n', regs->sr & SR_N ? 'N' : 'n',
regs->sr & SR_Z ? 'Z' : 'z', regs->sr & SR_Z ? 'Z' : 'z',
regs->sr & SR_C ? 'C' : 'c'); regs->sr & SR_C ? 'C' : 'c');
printk("Mode bits: %c%c%c%c%c%c%c%c%c\n", printk("%sMode bits: %c%c%c%c%c%c%c%c%c\n", log_lvl,
regs->sr & SR_H ? 'H' : 'h', regs->sr & SR_H ? 'H' : 'h',
regs->sr & SR_R ? 'R' : 'r', regs->sr & SR_R ? 'R' : 'r',
regs->sr & SR_J ? 'J' : 'j', regs->sr & SR_J ? 'J' : 'j',
@ -157,9 +297,21 @@ void show_regs(struct pt_regs *regs)
regs->sr & SR_I1M ? '1' : '.', regs->sr & SR_I1M ? '1' : '.',
regs->sr & SR_I0M ? '0' : '.', regs->sr & SR_I0M ? '0' : '.',
regs->sr & SR_GM ? 'G' : 'g'); regs->sr & SR_GM ? 'G' : 'g');
printk("CPU Mode: %s\n", cpu_modes[mode]); printk("%sCPU Mode: %s\n", log_lvl, cpu_modes[mode]);
printk("%sProcess: %s [%d] (task: %p thread: %p)\n",
log_lvl, current->comm, current->pid, current,
task_thread_info(current));
}
show_trace(NULL, (unsigned long *)sp, regs); void show_regs(struct pt_regs *regs)
{
unsigned long sp = regs->sp;
if (!user_mode(regs))
sp = (unsigned long)regs + FRAME_SIZE_FULL;
show_regs_log_lvl(regs, "");
show_trace_log_lvl(current, (unsigned long *)sp, regs, "");
} }
EXPORT_SYMBOL(show_regs); EXPORT_SYMBOL(show_regs);

View file

@ -5,158 +5,25 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#undef DEBUG
#include <linux/sched.h> #include <linux/bug.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <asm/traps.h>
#include <asm/sysreg.h>
#include <asm/addrspace.h> #include <asm/addrspace.h>
#include <asm/ocd.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/uaccess.h> #include <asm/ocd.h>
#include <asm/sysreg.h>
static void dump_mem(const char *str, unsigned long bottom, unsigned long top) #include <asm/traps.h>
{
unsigned long p;
int i;
printk("%s(0x%08lx to 0x%08lx)\n", str, bottom, top);
for (p = bottom & ~31; p < top; ) {
printk("%04lx: ", p & 0xffff);
for (i = 0; i < 8; i++, p += 4) {
unsigned int val;
if (p < bottom || p >= top)
printk(" ");
else {
if (__get_user(val, (unsigned int __user *)p)) {
printk("\n");
goto out;
}
printk("%08x ", val);
}
}
printk("\n");
}
out:
return;
}
static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p)
{
return (p > (unsigned long)tinfo)
&& (p < (unsigned long)tinfo + THREAD_SIZE - 3);
}
#ifdef CONFIG_FRAME_POINTER
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs)
{
unsigned long lr, fp;
struct thread_info *tinfo;
tinfo = (struct thread_info *)
((unsigned long)sp & ~(THREAD_SIZE - 1));
if (regs)
fp = regs->r7;
else if (tsk == current)
asm("mov %0, r7" : "=r"(fp));
else
fp = tsk->thread.cpu_context.r7;
/*
* Walk the stack as long as the frame pointer (a) is within
* the kernel stack of the task, and (b) it doesn't move
* downwards.
*/
while (valid_stack_ptr(tinfo, fp)) {
unsigned long new_fp;
lr = *(unsigned long *)fp;
printk(" [<%08lx>] ", lr);
print_symbol("%s\n", lr);
new_fp = *(unsigned long *)(fp + 4);
if (new_fp <= fp)
break;
fp = new_fp;
}
printk("\n");
}
#else
static inline void __show_trace(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs)
{
unsigned long addr;
while (!kstack_end(sp)) {
addr = *sp++;
if (kernel_text_address(addr)) {
printk(" [<%08lx>] ", addr);
print_symbol("%s\n", addr);
}
}
}
#endif
void show_trace(struct task_struct *tsk, unsigned long *sp,
struct pt_regs *regs)
{
if (regs &&
(((regs->sr & MODE_MASK) == MODE_EXCEPTION) ||
((regs->sr & MODE_MASK) == MODE_USER)))
return;
printk ("Call trace:");
#ifdef CONFIG_KALLSYMS
printk("\n");
#endif
__show_trace(tsk, sp, regs);
printk("\n");
}
void show_stack(struct task_struct *tsk, unsigned long *sp)
{
unsigned long stack;
if (!tsk)
tsk = current;
if (sp == 0) {
if (tsk == current) {
register unsigned long *real_sp __asm__("sp");
sp = real_sp;
} else {
sp = (unsigned long *)tsk->thread.cpu_context.ksp;
}
}
stack = (unsigned long)sp;
dump_mem("Stack: ", stack,
THREAD_SIZE + (unsigned long)tsk->thread_info);
show_trace(tsk, sp, NULL);
}
void dump_stack(void)
{
show_stack(NULL, NULL);
}
EXPORT_SYMBOL(dump_stack);
ATOMIC_NOTIFIER_HEAD(avr32_die_chain); ATOMIC_NOTIFIER_HEAD(avr32_die_chain);
int register_die_notifier(struct notifier_block *nb) int register_die_notifier(struct notifier_block *nb)
{ {
pr_debug("register_die_notifier: %p\n", nb);
return atomic_notifier_chain_register(&avr32_die_chain, nb); return atomic_notifier_chain_register(&avr32_die_chain, nb);
} }
EXPORT_SYMBOL(register_die_notifier); EXPORT_SYMBOL(register_die_notifier);
@ -169,93 +36,103 @@ EXPORT_SYMBOL(unregister_die_notifier);
static DEFINE_SPINLOCK(die_lock); static DEFINE_SPINLOCK(die_lock);
void __die(const char *str, struct pt_regs *regs, unsigned long err, void NORET_TYPE die(const char *str, struct pt_regs *regs, long err)
const char *file, const char *func, unsigned long line)
{ {
struct task_struct *tsk = current;
static int die_counter; static int die_counter;
console_verbose(); console_verbose();
spin_lock_irq(&die_lock); spin_lock_irq(&die_lock);
bust_spinlocks(1); bust_spinlocks(1);
printk(KERN_ALERT "%s", str); printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n" KERN_EMERG,
if (file && func) str, err, ++die_counter);
printk(" in %s:%s, line %ld", file, func, line); #ifdef CONFIG_PREEMPT
printk("[#%d]:\n", ++die_counter); printk("PREEMPT ");
print_modules(); #endif
show_regs(regs); #ifdef CONFIG_FRAME_POINTER
printk("Process %s (pid: %d, stack limit = 0x%p)\n", printk("FRAME_POINTER ");
tsk->comm, tsk->pid, tsk->thread_info + 1); #endif
if (current_cpu_data.features & AVR32_FEATURE_OCD) {
if (!user_mode(regs) || in_interrupt()) { unsigned long did = __mfdr(DBGREG_DID);
dump_mem("Stack: ", regs->sp, printk("chip: 0x%03lx:0x%04lx rev %lu\n",
THREAD_SIZE + (unsigned long)tsk->thread_info); (did >> 1) & 0x7ff,
(did >> 12) & 0x7fff,
(did >> 28) & 0xf);
} else {
printk("cpu: arch %u r%u / core %u r%u\n",
current_cpu_data.arch_type,
current_cpu_data.arch_revision,
current_cpu_data.cpu_type,
current_cpu_data.cpu_revision);
} }
print_modules();
show_regs_log_lvl(regs, KERN_EMERG);
show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG);
bust_spinlocks(0); bust_spinlocks(0);
spin_unlock_irq(&die_lock); spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
if (in_interrupt())
panic("Fatal exception in interrupt");
if (panic_on_oops)
panic("Fatal exception");
do_exit(err);
} }
void __die_if_kernel(const char *str, struct pt_regs *regs, unsigned long err, void _exception(long signr, struct pt_regs *regs, int code,
const char *file, const char *func, unsigned long line) unsigned long addr)
{ {
siginfo_t info;
if (!user_mode(regs)) if (!user_mode(regs))
__die(str, regs, err, file, func, line); die("Unhandled exception in kernel mode", regs, signr);
memset(&info, 0, sizeof(info));
info.si_signo = signr;
info.si_code = code;
info.si_addr = (void __user *)addr;
force_sig_info(signr, &info, current);
/*
* Init gets no signals that it doesn't have a handler for.
* That's all very well, but if it has caused a synchronous
* exception and we ignore the resulting signal, it will just
* generate the same exception over and over again and we get
* nowhere. Better to kill it and let the kernel panic.
*/
if (is_init(current)) {
__sighandler_t handler;
spin_lock_irq(&current->sighand->siglock);
handler = current->sighand->action[signr-1].sa.sa_handler;
spin_unlock_irq(&current->sighand->siglock);
if (handler == SIG_DFL) {
/* init has generated a synchronous exception
and it doesn't have a handler for the signal */
printk(KERN_CRIT "init has generated signal %ld "
"but has no handler for it\n", signr);
do_exit(signr);
}
}
} }
asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs)
{ {
#ifdef CONFIG_SUBARCH_AVR32B printk(KERN_ALERT "Got Non-Maskable Interrupt, dumping regs\n");
/* show_regs_log_lvl(regs, KERN_ALERT);
* The exception entry always saves RSR_EX. For NMI, this is show_stack_log_lvl(current, regs->sp, regs, KERN_ALERT);
* wrong; it should be RSR_NMI
*/
regs->sr = sysreg_read(RSR_NMI);
#endif
printk("NMI taken!!!!\n");
die("NMI", regs, ecr);
BUG();
} }
asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs)
{ {
printk("Unable to handle critical exception %lu at pc = %08lx!\n", die("Critical exception", regs, SIGKILL);
ecr, regs->pc);
die("Oops", regs, ecr);
BUG();
} }
asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs)
{ {
siginfo_t info; _exception(SIGBUS, regs, BUS_ADRALN, regs->pc);
die_if_kernel("Oops: Address exception in kernel mode", regs, ecr);
#ifdef DEBUG
if (ecr == ECR_ADDR_ALIGN_X)
pr_debug("Instruction Address Exception at pc = %08lx\n",
regs->pc);
else if (ecr == ECR_ADDR_ALIGN_R)
pr_debug("Data Address Exception (Read) at pc = %08lx\n",
regs->pc);
else if (ecr == ECR_ADDR_ALIGN_W)
pr_debug("Data Address Exception (Write) at pc = %08lx\n",
regs->pc);
else
BUG();
show_regs(regs);
#endif
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRALN;
info.si_addr = (void __user *)regs->pc;
force_sig_info(SIGBUS, &info, current);
} }
/* This way of handling undefined instructions is stolen from ARM */ /* This way of handling undefined instructions is stolen from ARM */
@ -280,7 +157,8 @@ static int do_cop_absent(u32 insn)
{ {
int cop_nr; int cop_nr;
u32 cpucr; u32 cpucr;
if ( (insn & 0xfdf00000) == 0xf1900000 )
if ((insn & 0xfdf00000) == 0xf1900000)
/* LDC0 */ /* LDC0 */
cop_nr = 0; cop_nr = 0;
else else
@ -292,136 +170,91 @@ static int do_cop_absent(u32 insn)
sysreg_write(CPUCR, cpucr); sysreg_write(CPUCR, cpucr);
cpucr = sysreg_read(CPUCR); cpucr = sysreg_read(CPUCR);
if ( !(cpucr & (1 << (24 + cop_nr))) ){ if (!(cpucr & (1 << (24 + cop_nr))))
printk("Coprocessor #%i not found!\n", cop_nr); return -ENODEV;
return -1;
}
return 0; return 0;
} }
#ifdef CONFIG_BUG int is_valid_bugaddr(unsigned long pc)
#ifdef CONFIG_DEBUG_BUGVERBOSE
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
{ {
char *file; unsigned short opcode;
u16 line;
char c;
if (__get_user(line, (u16 __user *)(regs->pc + 2))) if (pc < PAGE_OFFSET)
return; return 0;
if (__get_user(file, (char * __user *)(regs->pc + 4)) if (probe_kernel_address((u16 *)pc, opcode))
|| (unsigned long)file < PAGE_OFFSET return 0;
|| __get_user(c, file))
file = "<bad filename>";
printk(KERN_ALERT "kernel BUG at %s:%d!\n", file, line); return opcode == AVR32_BUG_OPCODE;
} }
#else
static inline void do_bug_verbose(struct pt_regs *regs, u32 insn)
{
}
#endif
#endif
asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs)
{ {
u32 insn; u32 insn;
struct undef_hook *hook; struct undef_hook *hook;
siginfo_t info;
void __user *pc; void __user *pc;
long code;
if (!user_mode(regs)) if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) {
goto kernel_trap; enum bug_trap_type type;
type = report_bug(regs->pc);
switch (type) {
case BUG_TRAP_TYPE_NONE:
break;
case BUG_TRAP_TYPE_WARN:
regs->pc += 2;
return;
case BUG_TRAP_TYPE_BUG:
die("Kernel BUG", regs, SIGKILL);
}
}
local_irq_enable(); local_irq_enable();
pc = (void __user *)instruction_pointer(regs); if (user_mode(regs)) {
if (__get_user(insn, (u32 __user *)pc)) pc = (void __user *)instruction_pointer(regs);
goto invalid_area; if (get_user(insn, (u32 __user *)pc))
goto invalid_area;
if (ecr == ECR_COPROC_ABSENT) { if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn))
if (do_cop_absent(insn) == 0)
return; return;
}
spin_lock_irq(&undef_lock); spin_lock_irq(&undef_lock);
list_for_each_entry(hook, &undef_hook, node) { list_for_each_entry(hook, &undef_hook, node) {
if ((insn & hook->insn_mask) == hook->insn_val) { if ((insn & hook->insn_mask) == hook->insn_val) {
if (hook->fn(regs, insn) == 0) { if (hook->fn(regs, insn) == 0) {
spin_unlock_irq(&undef_lock); spin_unlock_irq(&undef_lock);
return; return;
}
} }
} }
spin_unlock_irq(&undef_lock);
} }
spin_unlock_irq(&undef_lock);
invalid_area:
#ifdef DEBUG
printk("Illegal instruction at pc = %08lx\n", regs->pc);
if (regs->pc < TASK_SIZE) {
unsigned long ptbr, pgd, pte, *p;
ptbr = sysreg_read(PTBR);
p = (unsigned long *)ptbr;
pgd = p[regs->pc >> 22];
p = (unsigned long *)((pgd & 0x1ffff000) | 0x80000000);
pte = p[(regs->pc >> 12) & 0x3ff];
printk("page table: 0x%08lx -> 0x%08lx -> 0x%08lx\n", ptbr, pgd, pte);
}
#endif
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_addr = (void __user *)regs->pc;
switch (ecr) { switch (ecr) {
case ECR_ILLEGAL_OPCODE:
case ECR_UNIMPL_INSTRUCTION:
info.si_code = ILL_ILLOPC;
break;
case ECR_PRIVILEGE_VIOLATION: case ECR_PRIVILEGE_VIOLATION:
info.si_code = ILL_PRVOPC; code = ILL_PRVOPC;
break; break;
case ECR_COPROC_ABSENT: case ECR_COPROC_ABSENT:
info.si_code = ILL_COPROC; code = ILL_COPROC;
break; break;
default: default:
BUG(); code = ILL_ILLOPC;
break;
} }
force_sig_info(SIGILL, &info, current); _exception(SIGILL, regs, code, regs->pc);
return; return;
kernel_trap: invalid_area:
#ifdef CONFIG_BUG _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc);
if (__kernel_text_address(instruction_pointer(regs))) {
insn = *(u16 *)instruction_pointer(regs);
if (insn == AVR32_BUG_OPCODE) {
do_bug_verbose(regs, insn);
die("Kernel BUG", regs, 0);
return;
}
}
#endif
die("Oops: Illegal instruction in kernel code", regs, ecr);
} }
asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs)
{ {
siginfo_t info; /* We have no FPU yet */
_exception(SIGILL, regs, ILL_COPROC, regs->pc);
printk("Floating-point exception at pc = %08lx\n", regs->pc);
/* We have no FPU... */
info.si_signo = SIGILL;
info.si_errno = 0;
info.si_addr = (void __user *)regs->pc;
info.si_code = ILL_COPROC;
force_sig_info(SIGILL, &info, current);
} }

View file

@ -26,6 +26,12 @@ SECTIONS
_sinittext = .; _sinittext = .;
*(.text.reset) *(.text.reset)
*(.init.text) *(.init.text)
/*
* .exit.text is discarded at runtime, not
* link time, to deal with references from
* __bug_table
*/
*(.exit.text)
_einittext = .; _einittext = .;
. = ALIGN(4); . = ALIGN(4);
__tagtable_begin = .; __tagtable_begin = .;
@ -86,6 +92,8 @@ SECTIONS
__stop___ex_table = .; __stop___ex_table = .;
} }
BUG_TABLE
RODATA RODATA
. = ALIGN(8192); . = ALIGN(8192);
@ -126,7 +134,6 @@ SECTIONS
* thrown away, as cleanup code is never called unless it's a module. * thrown away, as cleanup code is never called unless it's a module.
*/ */
/DISCARD/ : { /DISCARD/ : {
*(.exit.text)
*(.exit.data) *(.exit.data)
*(.exitcall.exit) *(.exitcall.exit)
} }

View file

@ -16,26 +16,8 @@
#include <asm/kdebug.h> #include <asm/kdebug.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/sysreg.h> #include <asm/sysreg.h>
#include <asm/uaccess.h>
#include <asm/tlb.h> #include <asm/tlb.h>
#include <asm/uaccess.h>
#ifdef DEBUG
static void dump_code(unsigned long pc)
{
char *p = (char *)pc;
char val;
int i;
printk(KERN_DEBUG "Code:");
for (i = 0; i < 16; i++) {
if (__get_user(val, p + i))
break;
printk(" %02x", val);
}
printk("\n");
}
#endif
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain); ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
@ -68,17 +50,19 @@ static inline int notify_page_fault(enum die_val val, struct pt_regs *regs,
} }
#endif #endif
int exception_trace = 1;
/* /*
* This routine handles page faults. It determines the address and the * This routine handles page faults. It determines the address and the
* problem, and then passes it off to one of the appropriate routines. * problem, and then passes it off to one of the appropriate routines.
* *
* ecr is the Exception Cause Register. Possible values are: * ecr is the Exception Cause Register. Possible values are:
* 5: Page not found (instruction access)
* 6: Protection fault (instruction access) * 6: Protection fault (instruction access)
* 12: Page not found (read access) * 15: Protection fault (read access)
* 13: Page not found (write access) * 16: Protection fault (write access)
* 14: Protection fault (read access) * 20: Page not found (instruction access)
* 15: Protection fault (write access) * 24: Page not found (read access)
* 28: Page not found (write access)
*/ */
asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs) asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
{ {
@ -88,7 +72,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
const struct exception_table_entry *fixup; const struct exception_table_entry *fixup;
unsigned long address; unsigned long address;
unsigned long page; unsigned long page;
int writeaccess = 0; int writeaccess;
long signr;
int code;
if (notify_page_fault(DIE_PAGE_FAULT, regs, if (notify_page_fault(DIE_PAGE_FAULT, regs,
ecr, SIGSEGV) == NOTIFY_STOP) ecr, SIGSEGV) == NOTIFY_STOP)
@ -99,6 +85,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
tsk = current; tsk = current;
mm = tsk->mm; mm = tsk->mm;
signr = SIGSEGV;
code = SEGV_MAPERR;
/* /*
* If we're in an interrupt or have no user context, we must * If we're in an interrupt or have no user context, we must
* not take the fault... * not take the fault...
@ -125,7 +114,9 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
* can handle it... * can handle it...
*/ */
good_area: good_area:
//pr_debug("good area: vm_flags = 0x%lx\n", vma->vm_flags); code = SEGV_ACCERR;
writeaccess = 0;
switch (ecr) { switch (ecr) {
case ECR_PROTECTION_X: case ECR_PROTECTION_X:
case ECR_TLB_MISS_X: case ECR_TLB_MISS_X:
@ -176,46 +167,24 @@ survive:
* map. Fix it, but check if it's kernel or user first... * map. Fix it, but check if it's kernel or user first...
*/ */
bad_area: bad_area:
pr_debug("Bad area [%s:%u]: addr %08lx, ecr %lu\n",
tsk->comm, tsk->pid, address, ecr);
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
if (user_mode(regs)) { if (user_mode(regs)) {
/* Hmm...we have to pass address and ecr somehow... */ if (exception_trace)
/* tsk->thread.address = address; printk("%s%s[%d]: segfault at %08lx pc %08lx "
tsk->thread.error_code = ecr; */ "sp %08lx ecr %lu\n",
#ifdef DEBUG is_init(tsk) ? KERN_EMERG : KERN_INFO,
show_regs(regs); tsk->comm, tsk->pid, address, regs->pc,
dump_code(regs->pc); regs->sp, ecr);
_exception(SIGSEGV, regs, code, address);
page = sysreg_read(PTBR);
printk("ptbr = %08lx", page);
if (page) {
page = ((unsigned long *)page)[address >> 22];
printk(" pgd = %08lx", page);
if (page & _PAGE_PRESENT) {
page &= PAGE_MASK;
address &= 0x003ff000;
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
printk(" pte = %08lx\n", page);
}
}
#endif
pr_debug("Sending SIGSEGV to PID %d...\n",
tsk->pid);
force_sig(SIGSEGV, tsk);
return; return;
} }
no_context: no_context:
pr_debug("No context\n");
/* Are we prepared to handle this kernel fault? */ /* Are we prepared to handle this kernel fault? */
fixup = search_exception_tables(regs->pc); fixup = search_exception_tables(regs->pc);
if (fixup) { if (fixup) {
regs->pc = fixup->fixup; regs->pc = fixup->fixup;
pr_debug("Found fixup at %08lx\n", fixup->fixup);
return; return;
} }
@ -230,7 +199,6 @@ no_context:
printk(KERN_ALERT printk(KERN_ALERT
"Unable to handle kernel paging request"); "Unable to handle kernel paging request");
printk(" at virtual address %08lx\n", address); printk(" at virtual address %08lx\n", address);
printk(KERN_ALERT "pc = %08lx\n", regs->pc);
page = sysreg_read(PTBR); page = sysreg_read(PTBR);
printk(KERN_ALERT "ptbr = %08lx", page); printk(KERN_ALERT "ptbr = %08lx", page);
@ -241,20 +209,20 @@ no_context:
page &= PAGE_MASK; page &= PAGE_MASK;
address &= 0x003ff000; address &= 0x003ff000;
page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT]; page = ((unsigned long *)__va(page))[address >> PAGE_SHIFT];
printk(" pte = %08lx\n", page); printk(" pte = %08lx", page);
} }
} }
die("\nOops", regs, ecr); printk("\n");
do_exit(SIGKILL); die("Kernel access of bad area", regs, signr);
return;
/* /*
* We ran out of memory, or some other thing happened to us * We ran out of memory, or some other thing happened to us
* that made us unable to handle the page fault gracefully. * that made us unable to handle the page fault gracefully.
*/ */
out_of_memory: out_of_memory:
printk("Out of memory\n");
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
if (current->pid == 1) { if (is_init(current)) {
yield(); yield();
down_read(&mm->mmap_sem); down_read(&mm->mmap_sem);
goto survive; goto survive;
@ -267,21 +235,20 @@ out_of_memory:
do_sigbus: do_sigbus:
up_read(&mm->mmap_sem); up_read(&mm->mmap_sem);
/*
* Send a sigbus, regardless of whether we were in kernel or
* user mode.
*/
/* address, error_code, trap_no, ... */
#ifdef DEBUG
show_regs(regs);
dump_code(regs->pc);
#endif
pr_debug("Sending SIGBUS to PID %d...\n", tsk->pid);
force_sig(SIGBUS, tsk);
/* Kernel mode? Handle exceptions or die */ /* Kernel mode? Handle exceptions or die */
signr = SIGBUS;
code = BUS_ADRERR;
if (!user_mode(regs)) if (!user_mode(regs))
goto no_context; goto no_context;
if (exception_trace)
printk("%s%s[%d]: bus error at %08lx pc %08lx "
"sp %08lx ecr %lu\n",
is_init(tsk) ? KERN_EMERG : KERN_INFO,
tsk->comm, tsk->pid, address, regs->pc,
regs->sp, ecr);
_exception(SIGBUS, regs, BUS_ADRERR, address);
} }
asmlinkage void do_bus_error(unsigned long addr, int write_access, asmlinkage void do_bus_error(unsigned long addr, int write_access,
@ -292,8 +259,7 @@ asmlinkage void do_bus_error(unsigned long addr, int write_access,
addr, write_access ? "write" : "read"); addr, write_access ? "write" : "read");
printk(KERN_INFO "DTLB dump:\n"); printk(KERN_INFO "DTLB dump:\n");
dump_dtlb(); dump_dtlb();
die("Bus Error", regs, write_access); die("Bus Error", regs, SIGKILL);
do_exit(SIGKILL);
} }
/* /*

View file

@ -18,27 +18,53 @@
#ifdef CONFIG_DEBUG_BUGVERBOSE #ifdef CONFIG_DEBUG_BUGVERBOSE
#define BUG() \ #define _BUG_OR_WARN(flags) \
do { \ asm volatile( \
asm volatile(".hword %0\n\t" \ "1: .hword %0\n" \
".hword %1\n\t" \ " .section __bug_table,\"a\",@progbits\n" \
".long %2" \ "2: .long 1b\n" \
: \ " .long %1\n" \
: "n"(AVR32_BUG_OPCODE), \ " .short %2\n" \
"i"(__LINE__), "X"(__FILE__)); \ " .short %3\n" \
} while (0) " .org 2b + %4\n" \
" .previous" \
: \
: "i"(AVR32_BUG_OPCODE), "i"(__FILE__), \
"i"(__LINE__), "i"(flags), \
"i"(sizeof(struct bug_entry)))
#else #else
#define BUG() \ #define _BUG_OR_WARN(flags) \
do { \ asm volatile( \
asm volatile(".hword %0\n\t" \ "1: .hword %0\n" \
: : "n"(AVR32_BUG_OPCODE)); \ " .section __bug_table,\"a\",@progbits\n" \
} while (0) "2: .long 1b\n" \
" .short %1\n" \
" .org 2b + %2\n" \
" .previous" \
: \
: "i"(AVR32_BUG_OPCODE), "i"(flags), \
"i"(sizeof(struct bug_entry)))
#endif /* CONFIG_DEBUG_BUGVERBOSE */ #endif /* CONFIG_DEBUG_BUGVERBOSE */
#define BUG() \
do { \
_BUG_OR_WARN(0); \
for (;;); \
} while (0)
#define WARN_ON(condition) \
({ \
typeof(condition) __ret_warn_on = (condition); \
if (unlikely(__ret_warn_on)) \
_BUG_OR_WARN(BUGFLAG_WARNING); \
unlikely(__ret_warn_on); \
})
#define HAVE_ARCH_BUG #define HAVE_ARCH_BUG
#define HAVE_ARCH_WARN_ON
#endif /* CONFIG_BUG */ #endif /* CONFIG_BUG */

View file

@ -134,10 +134,10 @@ extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
#define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc) #define thread_saved_pc(tsk) ((tsk)->thread.cpu_context.pc)
struct pt_regs; struct pt_regs;
void show_trace(struct task_struct *task, unsigned long *stack,
struct pt_regs *regs);
extern unsigned long get_wchan(struct task_struct *p); extern unsigned long get_wchan(struct task_struct *p);
extern void show_regs_log_lvl(struct pt_regs *regs, const char *log_lvl);
extern void show_stack_log_lvl(struct task_struct *tsk, unsigned long sp,
struct pt_regs *regs, const char *log_lvl);
#define KSTK_EIP(tsk) ((tsk)->thread.cpu_context.pc) #define KSTK_EIP(tsk) ((tsk)->thread.cpu_context.pc)
#define KSTK_ESP(tsk) ((tsk)->thread.cpu_context.ksp) #define KSTK_ESP(tsk) ((tsk)->thread.cpu_context.ksp)

View file

@ -9,6 +9,7 @@
#define __ASM_AVR32_SYSTEM_H #define __ASM_AVR32_SYSTEM_H
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/linkage.h>
#include <linux/types.h> #include <linux/types.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
@ -140,15 +141,9 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
sizeof(*(ptr)))) sizeof(*(ptr))))
struct pt_regs; struct pt_regs;
extern void __die(const char *, struct pt_regs *, unsigned long, void NORET_TYPE die(const char *str, struct pt_regs *regs, long err);
const char *, const char *, unsigned long); void _exception(long signr, struct pt_regs *regs, int code,
extern void __die_if_kernel(const char *, struct pt_regs *, unsigned long, unsigned long addr);
const char *, const char *, unsigned long);
#define die(msg, regs, err) \
__die(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
#define die_if_kernel(msg, regs, err) \
__die_if_kernel(msg, regs, err, __FILE__ ":", __FUNCTION__, __LINE__)
#define arch_align_stack(x) (x) #define arch_align_stack(x) (x)