[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:
parent
3b328c9809
commit
623b0355d5
9 changed files with 403 additions and 418 deletions
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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(¤t->sighand->siglock);
|
||||||
|
handler = current->sighand->action[signr-1].sa.sa_handler;
|
||||||
|
spin_unlock_irq(¤t->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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||||
* 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 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||||
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 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||||
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 @@ asmlinkage void do_page_fault(unsigned long ecr, struct pt_regs *regs)
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue