/* * arch/alpha/kernel/traps.c * * (C) Copyright 1994 Linus Torvalds */ /* * This file initializes the trap entry points */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "proto.h" /* Work-around for some SRMs which mishandle opDEC faults. */ static int opDEC_fix; static void __cpuinit opDEC_check(void) { __asm__ __volatile__ ( /* Load the address of... */ " br $16, 1f\n" /* A stub instruction fault handler. Just add 4 to the pc and continue. */ " ldq $16, 8($sp)\n" " addq $16, 4, $16\n" " stq $16, 8($sp)\n" " call_pal %[rti]\n" /* Install the instruction fault handler. */ "1: lda $17, 3\n" " call_pal %[wrent]\n" /* With that in place, the fault from the round-to-minf fp insn will arrive either at the "lda 4" insn (bad) or one past that (good). This places the correct fixup in %0. */ " lda %[fix], 0\n" " cvttq/svm $f31,$f31\n" " lda %[fix], 4" : [fix] "=r" (opDEC_fix) : [rti] "n" (PAL_rti), [wrent] "n" (PAL_wrent) : "$0", "$1", "$16", "$17", "$22", "$23", "$24", "$25"); if (opDEC_fix) printk("opDEC fixup enabled.\n"); } void dik_show_regs(struct pt_regs *regs, unsigned long *r9_15) { printk("pc = [<%016lx>] ra = [<%016lx>] ps = %04lx %s\n", regs->pc, regs->r26, regs->ps, print_tainted()); print_symbol("pc is at %s\n", regs->pc); print_symbol("ra is at %s\n", regs->r26 ); printk("v0 = %016lx t0 = %016lx t1 = %016lx\n", regs->r0, regs->r1, regs->r2); printk("t2 = %016lx t3 = %016lx t4 = %016lx\n", regs->r3, regs->r4, regs->r5); printk("t5 = %016lx t6 = %016lx t7 = %016lx\n", regs->r6, regs->r7, regs->r8); if (r9_15) { printk("s0 = %016lx s1 = %016lx s2 = %016lx\n", r9_15[9], r9_15[10], r9_15[11]); printk("s3 = %016lx s4 = %016lx s5 = %016lx\n", r9_15[12], r9_15[13], r9_15[14]); printk("s6 = %016lx\n", r9_15[15]); } printk("a0 = %016lx a1 = %016lx a2 = %016lx\n", regs->r16, regs->r17, regs->r18); printk("a3 = %016lx a4 = %016lx a5 = %016lx\n", regs->r19, regs->r20, regs->r21); printk("t8 = %016lx t9 = %016lx t10= %016lx\n", regs->r22, regs->r23, regs->r24); printk("t11= %016lx pv = %016lx at = %016lx\n", regs->r25, regs->r27, regs->r28); printk("gp = %016lx sp = %p\n", regs->gp, regs+1); #if 0 __halt(); #endif } #if 0 static char * ireg_name[] = {"v0", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "a0", "a1", "a2", "a3", "a4", "a5", "t8", "t9", "t10", "t11", "ra", "pv", "at", "gp", "sp", "zero"}; #endif static void dik_show_code(unsigned int *pc) { long i; printk("Code:"); for (i = -6; i < 2; i++) { unsigned int insn; if (__get_user(insn, (unsigned int __user *)pc + i)) break; printk("%c%08x%c", i ? ' ' : '<', insn, i ? ' ' : '>'); } printk("\n"); } static void dik_show_trace(unsigned long *sp) { long i = 0; printk("Trace:\n"); while (0x1ff8 & (unsigned long) sp) { extern char _stext[], _etext[]; unsigned long tmp = *sp; sp++; if (tmp < (unsigned long) &_stext) continue; if (tmp >= (unsigned long) &_etext) continue; printk("[<%lx>]", tmp); print_symbol(" %s", tmp); printk("\n"); if (i > 40) { printk(" ..."); break; } } printk("\n"); } static int kstack_depth_to_print = 24; void show_stack(struct task_struct *task, unsigned long *sp) { unsigned long *stack; int i; /* * debugging aid: "show_stack(NULL);" prints the * back trace for this cpu. */ if(sp==NULL) sp=(unsigned long*)&sp; stack = sp; for(i=0; i < kstack_depth_to_print; i++) { if (((long) stack & (THREAD_SIZE-1)) == 0) break; if (i && ((i % 4) == 0)) printk("\n "); printk("%016lx ", *stack++); } printk("\n"); dik_show_trace(sp); } void dump_stack(void) { show_stack(NULL, NULL); } EXPORT_SYMBOL(dump_stack); void die_if_kernel(char * str, struct pt_regs *regs, long err, unsigned long *r9_15) { if (regs->ps & 8) return; #ifdef CONFIG_SMP printk("CPU %d ", hard_smp_processor_id()); #endif printk("%s(%d): %s %ld\n", current->comm, task_pid_nr(current), str, err); dik_show_regs(regs, r9_15); add_taint(TAINT_DIE); dik_show_trace((unsigned long *)(regs+1)); dik_show_code((unsigned int *)regs->pc); if (test_and_set_thread_flag (TIF_DIE_IF_KERNEL)) { printk("die_if_kernel recursion detected.\n"); local_irq_enable(); while (1); } do_exit(SIGSEGV); } #ifndef CONFIG_MATHEMU static long dummy_emul(void) { return 0; } long (*alpha_fp_emul_imprecise)(struct pt_regs *regs, unsigned long writemask) = (void *)dummy_emul; long (*alpha_fp_emul) (unsigned long pc) = (void *)dummy_emul; #else long alpha_fp_emul_imprecise(struct pt_regs *regs, unsigned long writemask); long alpha_fp_emul (unsigned long pc); #endif asmlinkage void do_entArith(unsigned long summary, unsigned long write_mask, struct pt_regs *regs) { long si_code = FPE_FLTINV; siginfo_t info; if (summary & 1) { /* Software-completion summary bit is set, so try to emulate the instruction. If the processor supports precise exceptions, we don't have to search. */ if (!amask(AMASK_PRECISE_TRAP)) si_code = alpha_fp_emul(regs->pc - 4); else si_code = alpha_fp_emul_imprecise(regs, write_mask); if (si_code == 0) return; } die_if_kernel("Arithmetic fault", regs, 0, NULL); info.si_signo = SIGFPE; info.si_errno = 0; info.si_code = si_code; info.si_addr = (void __user *) regs->pc; send_sig_info(SIGFPE, &info, current); } asmlinkage void do_entIF(unsigned long type, struct pt_regs *regs) { siginfo_t info; int signo, code; if ((regs->ps & ~IPL_MAX) == 0) { if (type == 1) { const unsigned int *data = (const unsigned int *) regs->pc; printk("Kernel bug at %s:%d\n", (const char *)(data[1] | (long)data[2] << 32), data[0]); } die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"), regs, type, NULL); } switch (type) { case 0: /* breakpoint */ info.si_signo = SIGTRAP; info.si_errno = 0; info.si_code = TRAP_BRKPT; info.si_trapno = 0; info.si_addr = (void __user *) regs->pc; if (ptrace_cancel_bpt(current)) { regs->pc -= 4; /* make pc point to former bpt */ } send_sig_info(SIGTRAP, &info, current); return;