]> rtime.felk.cvut.cz Git - mcf548x/linux.git/blobdiff - arch/m68k/coldfire/traps.c
Current (FEC from 2.6.31 port, no CAN, no I2C, no PCI)
[mcf548x/linux.git] / arch / m68k / coldfire / traps.c
diff --git a/arch/m68k/coldfire/traps.c b/arch/m68k/coldfire/traps.c
new file mode 100644 (file)
index 0000000..38784a5
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ *  linux/arch/m68knommu/kernel/traps.c
+ *
+ *  Copyright (C) 1993, 1994 by Hamish Macdonald
+ *
+ *  68040 fixes by Michael Rausch
+ *  68040 fixes by Martin Apel
+ *  68060 fixes by Roman Hodek
+ *  68060 fixes by Jesper Skov
+ *  Coldfire fixes by Kurt Mahan
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+/*
+ * Sets up all exception vectors
+ */
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/a.out.h>
+#include <linux/user.h>
+#include <linux/string.h>
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/kallsyms.h>
+
+#include <asm/setup.h>
+#include <asm/fpu.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <asm/traps.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+#include <asm/siginfo.h>
+
+static char const * const vec_names[] = {
+       "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR",
+       "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc",
+       "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111",
+       "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION",
+       "FORMAT ERROR", "UNINITIALIZED INTERRUPT",
+       "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17",
+       "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19",
+       "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21",
+       "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23",
+       "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT",
+       "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT",
+       "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3",
+       "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7",
+       "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11",
+       "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15",
+       "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW",
+       "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN",
+       "FPCP UNSUPPORTED OPERATION",
+       "MMU CONFIGURATION ERROR"
+};
+
+asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address,
+                            unsigned long error_code);
+asmlinkage void trap_c(struct frame *fp);
+extern void __init coldfire_trap_init(void);
+
+void __init trap_init(void)
+{
+       coldfire_trap_init();
+}
+
+/* The following table converts the FS encoding of a ColdFire
+   exception stack frame into the error_code value needed by
+   do_fault. */
+
+static const unsigned char fs_err_code[] = {
+       0,  /* 0000 */
+       0,  /* 0001 */
+       0,  /* 0010 */
+       0,  /* 0011 */
+       1,  /* 0100 */
+       0,  /* 0101 */
+       0,  /* 0110 */
+       0,  /* 0111 */
+       2,  /* 1000 */
+       3,  /* 1001 */
+       2,  /* 1010 */
+       0,  /* 1011 */
+       1,  /* 1100 */
+       1,  /* 1101 */
+       0,  /* 1110 */
+       0   /* 1111 */
+};
+
+#ifdef DEBUG
+static const char *fs_err_msg[16] = {
+       "Normal",
+       "Reserved",
+       "Interrupt during debug service routine",
+       "Reserved",
+       "X Protection",
+       "TLB X miss (opword)",
+       "TLB X miss (ext. word)",
+       "IFP in emulator mode",
+       "W Protection",
+       "Write error",
+       "TLB W miss",
+       "Reserved",
+       "R Protection",
+       "R/RMW Protection",
+       "TLB R miss",
+       "OEP in emulator mode",
+};
+#endif
+
+static inline void access_errorCF(struct frame *fp)
+{
+       unsigned long int mmusr, complainingAddress;
+       unsigned int err_code, fs;
+       int need_page_fault;
+
+       mmusr = fp->ptregs.mmusr;
+       complainingAddress = fp->ptregs.mmuar;
+#ifdef DEBUG
+       printk(KERN_DEBUG "pc %#lx, mmusr %#lx, complainingAddress %#lx\n", \
+               fp->ptregs.pc, mmusr, complainingAddress);
+#endif
+
+       /*
+        * error_code:
+        *      bit 0 == 0 means no page found, 1 means protection fault
+        *      bit 1 == 0 means read, 1 means write
+        */
+
+       fs = (fp->ptregs.fs2 << 2) | fp->ptregs.fs1;
+       switch (fs) {
+       case  5:  /* 0101 TLB opword X miss */
+               need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 0, 0);
+               complainingAddress = fp->ptregs.pc;
+               break;
+       case  6:  /* 0110 TLB extension word X miss */
+               need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 0, 1);
+               complainingAddress = fp->ptregs.pc + sizeof(long);
+               break;
+       case 10:  /* 1010 TLB W miss */
+               need_page_fault = cf_tlb_miss(&fp->ptregs, 1, 1, 0);
+               break;
+       case 14: /* 1110 TLB R miss */
+               need_page_fault = cf_tlb_miss(&fp->ptregs, 0, 1, 0);
+               break;
+       default:
+               /* 0000 Normal  */
+               /* 0001 Reserved */
+               /* 0010 Interrupt during debug service routine */
+               /* 0011 Reserved */
+               /* 0100 X Protection */
+               /* 0111 IFP in emulator mode */
+               /* 1000 W Protection*/
+               /* 1001 Write error*/
+               /* 1011 Reserved*/
+               /* 1100 R Protection*/
+               /* 1101 R Protection*/
+               /* 1111 OEP in emulator mode*/
+               need_page_fault = 1;
+               break;
+       }
+
+       if (need_page_fault) {
+               err_code = fs_err_code[fs];
+               if ((fs == 13) && (mmusr & MMUSR_WF)) /* rd-mod-wr access */
+                       err_code |= 2; /* bit1 - write, bit0 - protection */
+               do_page_fault(&fp->ptregs, complainingAddress, err_code);
+       }
+}
+
+void die_if_kernel(char *str, struct pt_regs *fp, int nr)
+{
+       if (!(fp->sr & PS_S))
+               return;
+
+       console_verbose();
+       printk(KERN_EMERG "%s: %08x\n", str, nr);
+       printk(KERN_EMERG "PC: [<%08lx>]", fp->pc);
+       print_symbol(" %s", fp->pc);
+       printk(KERN_EMERG "\nSR: %04x  SP: %p  a2: %08lx\n",
+              fp->sr, fp, fp->a2);
+       printk(KERN_EMERG "d0: %08lx    d1: %08lx    d2: %08lx    d3: %08lx\n",
+              fp->d0, fp->d1, fp->d2, fp->d3);
+       printk(KERN_EMERG "d4: %08lx    d5: %08lx    a0: %08lx    a1: %08lx\n",
+              fp->d4, fp->d5, fp->a0, fp->a1);
+
+       printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n",
+               current->comm, current->pid, PAGE_SIZE+(unsigned long)current);
+       show_stack(NULL, (unsigned long *)fp);
+       do_exit(SIGSEGV);
+}
+
+asmlinkage void buserr_c(struct frame *fp)
+{
+       unsigned int fs;
+
+       /* Only set esp0 if coming from user mode */
+       if (user_mode(&fp->ptregs))
+               current->thread.esp0 = (unsigned long) fp;
+
+       fs = (fp->ptregs.fs2 << 2) | fp->ptregs.fs1;
+#if defined(DEBUG)
+       printk(KERN_DEBUG "*** Bus Error *** (%x)%s\n", fs,
+               fs_err_msg[fs & 0xf]);
+#endif
+       switch (fs) {
+       case 0x5:
+       case 0x6:
+       case 0x7:
+       case 0x9:
+       case 0xa:
+       case 0xd:
+       case 0xe:
+       case 0xf:
+               access_errorCF(fp);
+               break;
+       default:
+               die_if_kernel("bad frame format", &fp->ptregs, 0);
+#if defined(DEBUG)
+               printk(KERN_DEBUG "Unknown SIGSEGV - 4\n");
+#endif
+               force_sig(SIGSEGV, current);
+       }
+}
+
+
+int kstack_depth_to_print = 48;
+
+void show_stack(struct task_struct *task, unsigned long *stack)
+{
+       unsigned long *endstack, addr, symaddr;
+       extern char _start, _etext;
+       int i;
+
+       if (!stack) {
+               if (task)
+                       stack = (unsigned long *)task->thread.ksp;
+               else
+                       stack = (unsigned long *)&stack;
+       }
+
+       addr = (unsigned long) stack;
+       endstack = (unsigned long *) PAGE_ALIGN(addr);
+
+       printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack);
+       for (i = 0; i < kstack_depth_to_print; i++) {
+               if (stack + 1 > endstack)
+                       break;
+               if (i % 8 == 0)
+                       printk("\n" KERN_EMERG "       ");
+               symaddr = *stack;
+               printk(KERN_EMERG " %08lx", *stack++);
+               if ((symaddr >= 0xc0000000) && (symaddr < 0xc1000000))
+                       print_symbol("(%s)", symaddr);
+       }
+       printk("\n");
+
+       printk(KERN_EMERG "Call Trace:");
+       i = 0;
+       while (stack + 1 <= endstack) {
+               addr = *stack++;
+               /*
+                * If the address is either in the text segment of the
+                * kernel, or in the region which contains vmalloc'ed
+                * memory, it *may* be the address of a calling
+                * routine; if so, print it so that someone tracing
+                * down the cause of the crash will be able to figure
+                * out the call path that was taken.
+                */
+               if (((addr >= (unsigned long) &_start) &&
+                    (addr <= (unsigned long) &_etext))) {
+                       if (i % 4 == 0)
+                               printk("\n" KERN_EMERG "       ");
+                       printk(KERN_EMERG " [<%08lx>]", addr);
+                       i++;
+               }
+       }
+       printk("\n");
+}
+
+void bad_super_trap(struct frame *fp)
+{
+       console_verbose();
+       if (fp->ptregs.vector < sizeof(vec_names)/sizeof(vec_names[0]))
+               printk(KERN_WARNING "*** %s ***   FORMAT=%X\n",
+                       vec_names[fp->ptregs.vector],
+                       fp->ptregs.format);
+       else
+               printk(KERN_WARNING "*** Exception %d ***   FORMAT=%X\n",
+                       fp->ptregs.vector,
+                       fp->ptregs.format);
+       printk(KERN_WARNING "Current process id is %d\n", current->pid);
+       die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0);
+}
+
+asmlinkage void trap_c(struct frame *fp)
+{
+       int sig;
+       siginfo_t info;
+
+       if (fp->ptregs.sr & PS_S) {
+               if (fp->ptregs.vector == VEC_TRACE) {
+                       /* traced a trapping instruction */
+                       current->ptrace |= PT_DTRACE;
+               } else
+                       bad_super_trap(fp);
+               return;
+       }
+
+       /* send the appropriate signal to the user program */
+       switch (fp->ptregs.vector) {
+       case VEC_ADDRERR:
+               info.si_code = BUS_ADRALN;
+               sig = SIGBUS;
+               break;
+       case VEC_ILLEGAL:
+       case VEC_LINE10:
+       case VEC_LINE11:
+               info.si_code = ILL_ILLOPC;
+               sig = SIGILL;
+               break;
+       case VEC_PRIV:
+               info.si_code = ILL_PRVOPC;
+               sig = SIGILL;
+               break;
+       case VEC_COPROC:
+               info.si_code = ILL_COPROC;
+               sig = SIGILL;
+               break;
+       case VEC_TRAP1: /* gdbserver breakpoint */
+               fp->ptregs.pc -= 2;
+               info.si_code = TRAP_TRACE;
+               sig = SIGTRAP;
+               break;
+       case VEC_TRAP2:
+       case VEC_TRAP3:
+       case VEC_TRAP4:
+       case VEC_TRAP5:
+       case VEC_TRAP6:
+       case VEC_TRAP7:
+       case VEC_TRAP8:
+       case VEC_TRAP9:
+       case VEC_TRAP10:
+       case VEC_TRAP11:
+       case VEC_TRAP12:
+       case VEC_TRAP13:
+       case VEC_TRAP14:
+               info.si_code = ILL_ILLTRP;
+               sig = SIGILL;
+               break;
+       case VEC_FPBRUC:
+       case VEC_FPOE:
+       case VEC_FPNAN:
+               info.si_code = FPE_FLTINV;
+               sig = SIGFPE;
+               break;
+       case VEC_FPIR:
+               info.si_code = FPE_FLTRES;
+               sig = SIGFPE;
+               break;
+       case VEC_FPDIVZ:
+               info.si_code = FPE_FLTDIV;
+               sig = SIGFPE;
+               break;
+       case VEC_FPUNDER:
+               info.si_code = FPE_FLTUND;
+               sig = SIGFPE;
+               break;
+       case VEC_FPOVER:
+               info.si_code = FPE_FLTOVF;
+               sig = SIGFPE;
+               break;
+       case VEC_ZERODIV:
+               info.si_code = FPE_INTDIV;
+               sig = SIGFPE;
+               break;
+       case VEC_CHK:
+       case VEC_TRAP:
+               info.si_code = FPE_INTOVF;
+               sig = SIGFPE;
+               break;
+       case VEC_TRACE:         /* ptrace single step */
+               info.si_code = TRAP_TRACE;
+               sig = SIGTRAP;
+               break;
+       case VEC_TRAP15:                /* breakpoint */
+               info.si_code = TRAP_BRKPT;
+               sig = SIGTRAP;
+               break;
+       default:
+               info.si_code = ILL_ILLOPC;
+               sig = SIGILL;
+               break;
+       }
+       info.si_signo = sig;
+       info.si_errno = 0;
+       switch (fp->ptregs.format) {
+       default:
+               info.si_addr = (void *) fp->ptregs.pc;
+               break;
+       case 2:
+               info.si_addr = (void *) fp->un.fmt2.iaddr;
+               break;
+       case 7:
+               info.si_addr = (void *) fp->un.fmt7.effaddr;
+               break;
+       case 9:
+               info.si_addr = (void *) fp->un.fmt9.iaddr;
+               break;
+       case 10:
+               info.si_addr = (void *) fp->un.fmta.daddr;
+               break;
+       case 11:
+               info.si_addr = (void *) fp->un.fmtb.daddr;
+               break;
+       }
+       force_sig_info(sig, &info, current);
+}
+
+asmlinkage void set_esp0(unsigned long ssp)
+{
+       current->thread.esp0 = ssp;
+}
+
+/*
+ * The architecture-independent backtrace generator
+ */
+void dump_stack(void)
+{
+       unsigned long stack;
+
+       show_stack(current, &stack);
+}
+EXPORT_SYMBOL(dump_stack);
+
+#ifdef CONFIG_M68KFPU_EMU
+asmlinkage void fpemu_signal(int signal, int code, void *addr)
+{
+       siginfo_t info;
+
+       info.si_signo = signal;
+       info.si_errno = 0;
+       info.si_code = code;
+       info.si_addr = addr;
+       force_sig_info(signal, &info, current);
+}
+#endif