traps.c revision ad361c9884e809340f6daca80d56a9e9c871690a
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 NORET_TYPE 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); 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 /* 100 * Init gets no signals that it doesn't have a handler for. 101 * That's all very well, but if it has caused a synchronous 102 * exception and we ignore the resulting signal, it will just 103 * generate the same exception over and over again and we get 104 * nowhere. Better to kill it and let the kernel panic. 105 */ 106 if (is_global_init(current)) { 107 __sighandler_t handler; 108 109 spin_lock_irq(¤t->sighand->siglock); 110 handler = current->sighand->action[signr-1].sa.sa_handler; 111 spin_unlock_irq(¤t->sighand->siglock); 112 if (handler == SIG_DFL) { 113 /* init has generated a synchronous exception 114 and it doesn't have a handler for the signal */ 115 printk(KERN_CRIT "init has generated signal %ld " 116 "but has no handler for it\n", signr); 117 do_exit(signr); 118 } 119 } 120} 121 122asmlinkage void do_nmi(unsigned long ecr, struct pt_regs *regs) 123{ 124 int ret; 125 126 nmi_enter(); 127 128 ret = notify_die(DIE_NMI, "NMI", regs, 0, ecr, SIGINT); 129 switch (ret) { 130 case NOTIFY_OK: 131 case NOTIFY_STOP: 132 break; 133 case NOTIFY_BAD: 134 die("Fatal Non-Maskable Interrupt", regs, SIGINT); 135 default: 136 printk(KERN_ALERT "Got NMI, but nobody cared. Disabling...\n"); 137 nmi_disable(); 138 break; 139 } 140 nmi_exit(); 141} 142 143asmlinkage void do_critical_exception(unsigned long ecr, struct pt_regs *regs) 144{ 145 die("Critical exception", regs, SIGKILL); 146} 147 148asmlinkage void do_address_exception(unsigned long ecr, struct pt_regs *regs) 149{ 150 _exception(SIGBUS, regs, BUS_ADRALN, regs->pc); 151} 152 153/* This way of handling undefined instructions is stolen from ARM */ 154static LIST_HEAD(undef_hook); 155static DEFINE_SPINLOCK(undef_lock); 156 157void register_undef_hook(struct undef_hook *hook) 158{ 159 spin_lock_irq(&undef_lock); 160 list_add(&hook->node, &undef_hook); 161 spin_unlock_irq(&undef_lock); 162} 163 164void unregister_undef_hook(struct undef_hook *hook) 165{ 166 spin_lock_irq(&undef_lock); 167 list_del(&hook->node); 168 spin_unlock_irq(&undef_lock); 169} 170 171static int do_cop_absent(u32 insn) 172{ 173 int cop_nr; 174 u32 cpucr; 175 176 if ((insn & 0xfdf00000) == 0xf1900000) 177 /* LDC0 */ 178 cop_nr = 0; 179 else 180 cop_nr = (insn >> 13) & 0x7; 181 182 /* Try enabling the coprocessor */ 183 cpucr = sysreg_read(CPUCR); 184 cpucr |= (1 << (24 + cop_nr)); 185 sysreg_write(CPUCR, cpucr); 186 187 cpucr = sysreg_read(CPUCR); 188 if (!(cpucr & (1 << (24 + cop_nr)))) 189 return -ENODEV; 190 191 return 0; 192} 193 194#ifdef CONFIG_BUG 195int is_valid_bugaddr(unsigned long pc) 196{ 197 unsigned short opcode; 198 199 if (pc < PAGE_OFFSET) 200 return 0; 201 if (probe_kernel_address((u16 *)pc, opcode)) 202 return 0; 203 204 return opcode == AVR32_BUG_OPCODE; 205} 206#endif 207 208asmlinkage void do_illegal_opcode(unsigned long ecr, struct pt_regs *regs) 209{ 210 u32 insn; 211 struct undef_hook *hook; 212 void __user *pc; 213 long code; 214 215#ifdef CONFIG_BUG 216 if (!user_mode(regs) && (ecr == ECR_ILLEGAL_OPCODE)) { 217 enum bug_trap_type type; 218 219 type = report_bug(regs->pc, regs); 220 switch (type) { 221 case BUG_TRAP_TYPE_NONE: 222 break; 223 case BUG_TRAP_TYPE_WARN: 224 regs->pc += 2; 225 return; 226 case BUG_TRAP_TYPE_BUG: 227 die("Kernel BUG", regs, SIGKILL); 228 } 229 } 230#endif 231 232 local_irq_enable(); 233 234 if (user_mode(regs)) { 235 pc = (void __user *)instruction_pointer(regs); 236 if (get_user(insn, (u32 __user *)pc)) 237 goto invalid_area; 238 239 if (ecr == ECR_COPROC_ABSENT && !do_cop_absent(insn)) 240 return; 241 242 spin_lock_irq(&undef_lock); 243 list_for_each_entry(hook, &undef_hook, node) { 244 if ((insn & hook->insn_mask) == hook->insn_val) { 245 if (hook->fn(regs, insn) == 0) { 246 spin_unlock_irq(&undef_lock); 247 return; 248 } 249 } 250 } 251 spin_unlock_irq(&undef_lock); 252 } 253 254 switch (ecr) { 255 case ECR_PRIVILEGE_VIOLATION: 256 code = ILL_PRVOPC; 257 break; 258 case ECR_COPROC_ABSENT: 259 code = ILL_COPROC; 260 break; 261 default: 262 code = ILL_ILLOPC; 263 break; 264 } 265 266 _exception(SIGILL, regs, code, regs->pc); 267 return; 268 269invalid_area: 270 _exception(SIGSEGV, regs, SEGV_MAPERR, regs->pc); 271} 272 273asmlinkage void do_fpe(unsigned long ecr, struct pt_regs *regs) 274{ 275 /* We have no FPU yet */ 276 _exception(SIGILL, regs, ILL_COPROC, regs->pc); 277} 278 279 280void __init trap_init(void) 281{ 282 283} 284