1/* 2 * Copyright (C) 2004-2006 Atmel Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/bug.h> 10#include <linux/hardirq.h> 11#include <linux/init.h> 12#include <linux/kallsyms.h> 13#include <linux/kdebug.h> 14#include <linux/module.h> 15#include <linux/notifier.h> 16#include <linux/sched.h> 17#include <linux/uaccess.h> 18 19#include <asm/addrspace.h> 20#include <asm/mmu_context.h> 21#include <asm/ocd.h> 22#include <asm/sysreg.h> 23#include <asm/traps.h> 24 25static DEFINE_SPINLOCK(die_lock); 26 27void die(const char *str, struct pt_regs *regs, long err) 28{ 29 static int die_counter; 30 31 console_verbose(); 32 spin_lock_irq(&die_lock); 33 bust_spinlocks(1); 34 35 printk(KERN_ALERT "Oops: %s, sig: %ld [#%d]\n", 36 str, err, ++die_counter); 37 38 printk(KERN_EMERG); 39 40#ifdef CONFIG_PREEMPT 41 printk(KERN_CONT "PREEMPT "); 42#endif 43#ifdef CONFIG_FRAME_POINTER 44 printk(KERN_CONT "FRAME_POINTER "); 45#endif 46 if (current_cpu_data.features & AVR32_FEATURE_OCD) { 47 unsigned long did = ocd_read(DID); 48 printk(KERN_CONT "chip: 0x%03lx:0x%04lx rev %lu\n", 49 (did >> 1) & 0x7ff, 50 (did >> 12) & 0x7fff, 51 (did >> 28) & 0xf); 52 } else { 53 printk(KERN_CONT "cpu: arch %u r%u / core %u r%u\n", 54 current_cpu_data.arch_type, 55 current_cpu_data.arch_revision, 56 current_cpu_data.cpu_type, 57 current_cpu_data.cpu_revision); 58 } 59 60 print_modules(); 61 show_regs_log_lvl(regs, KERN_EMERG); 62 show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG); 63 bust_spinlocks(0); 64 add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE); 65 spin_unlock_irq(&die_lock); 66 67 if (in_interrupt()) 68 panic("Fatal exception in interrupt"); 69 70 if (panic_on_oops) 71 panic("Fatal exception"); 72 73 do_exit(err); 74} 75 76void _exception(long signr, struct pt_regs *regs, int code, 77 unsigned long addr) 78{ 79 siginfo_t info; 80 81 if (!user_mode(regs)) { 82 const struct exception_table_entry *fixup; 83 84 /* Are we prepared to handle this kernel fault? */ 85 fixup = search_exception_tables(regs->pc); 86 if (fixup) { 87 regs->pc = fixup->fixup; 88 return; 89 } 90 die("Unhandled exception in kernel mode", regs, signr); 91 } 92 93 memset(&info, 0, sizeof(info)); 94 info.si_signo = signr; 95 info.si_code = code; 96 info.si_addr = (void __user *)addr; 97 force_sig_info(signr, &info, current); 98} 99 100asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) 101{ 102 int ret; 103 104 nmi_enter(); 105 106 ret = notify_die(DIE_NMI, "NMI", regs, 0, ecr, SIGINT); 107 switch (ret) { 108 case NOTIFY_OK: 109 case NOTIFY_STOP: 110 break; 111 case NOTIFY_BAD: 112 die("Fatal Non-Maskable Interrupt", regs, SIGINT); 113 default: 114 printk(KERN_ALERT "Got NMI, but nobody cared. Disabling...\n"); 115 nmi_disable(); 116 break; 117 } 118 nmi_exit(); 119} 120 121asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) 122{ 123 die("Critical exception", regs, SIGKILL); 124} 125 126asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) 127{ 128 _exception(SIGBUS, regs, BUS_ADRALN, regs->pc); 129} 130 131/* This way of handling undefined instructions is stolen from ARM */ 132static LIST_HEAD(undef_hook); 133static DEFINE_SPINLOCK(undef_lock); 134 135void register_undef_hook(struct undef_hook *hook) 136{ 137 spin_lock_irq(&undef_lock); 138 list_add(&hook->node, &undef_hook); 139 spin_unlock_irq(&undef_lock); 140} 141 142void unregister_undef_hook(struct undef_hook *hook) 143{ 144 spin_lock_irq(&undef_lock); 145 list_del(&hook->node); 146 spin_unlock_irq(&undef_lock); 147} 148 149static int do_cop_absent(u32 insn) 150{ 151 int cop_nr; 152 u32 cpucr; 153 154 if ((insn & 0xfdf00000) == 0xf1900000) 155 /* LDC0 */ 156 cop_nr = 0; 157 else 158 cop_nr = (insn >> 13) & 0x7; 159 160 /* Try enabling the coprocessor */ 161 cpucr = sysreg_read(CPUCR); 162 cpucr |= (1 << (24 + cop_nr)); 163 sysreg_write(CPUCR, cpucr); 164 165 cpucr = sysreg_read(CPUCR); 166 if (!(cpucr & (1 << (24 + cop_nr)))) 167 return -ENODEV; 168 169 return 0; 170} 171 172#ifdef CONFIG_BUG 173int is_valid_bugaddr(unsigned long pc) 174{ 175 unsigned short opcode; 176 177 if (pc < PAGE_OFFSET) 178 return 0; 179 if (probe_kernel_address((u16 *)pc, opcode)) 180 return 0; 181 182 return opcode == AVR32_BUG_OPCODE; 183} 184#endif 185 186asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs) 187{ 188 u32 insn; 189 struct undef_hook *hook; 190 void __user *pc; 191 long code; 192 193#ifdef CONFIG_BUG 194 if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) { 195 enum bug_trap_type type; 196 197 type = report_bug(regs->pc, regs); 198 switch (type) { 199 case BUG_TRAP_TYPE_NONE: 200 break; 201 case BUG_TRAP_TYPE_WARN: 202 regs->pc += 2; 203 return; 204 case BUG_TRAP_TYPE_BUG: 205 die("Kernel BUG", regs, SIGKILL); 206 } 207 } 208#endif 209 210 local_irq_enable(); 211 212 if (user_mode(regs)) { 213 pc = (void __user *)instruction_pointer(regs); 214 if (get_user(insn, (u32 __user *)pc)) 215 goto invalid_area; 216 217 if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn)) 218 return; 219 220 spin_lock_irq(&undef_lock); 221 list_for_each_entry(hook, &undef_hook, node) { 222 if ((insn & hook->insn_mask) == hook->insn_val) { 223 if (hook->fn(regs, insn) == 0) { 224 spin_unlock_irq(&undef_lock); 225 return; 226 } 227 } 228 } 229 spin_unlock_irq(&undef_lock); 230 } 231 232 switch (ecr) { 233 case ECR_PRIVILEGE_VIOLATION: 234 code = ILL_PRVOPC; 235 break; 236 case ECR_COPROC_ABSENT: 237 code = ILL_COPROC; 238 break; 239 default: 240 code = ILL_ILLOPC; 241 break; 242 } 243 244 _exception(SIGILL, regs, code, regs->pc); 245 return; 246 247invalid_area: 248 _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc); 249} 250 251asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs) 252{ 253 /* We have no FPU yet */ 254 _exception(SIGILL, regs, ILL_COPROC, regs->pc); 255} 256 257 258void __init trap_init(void) 259{ 260 261} 262