linux/arch/sparc/kernel/traps.c
<<
>>
Prefs
   1/*
   2 * arch/sparc/kernel/traps.c
   3 *
   4 * Copyright 1995, 2008 David S. Miller (davem@davemloft.net)
   5 * Copyright 2000 Jakub Jelinek (jakub@redhat.com)
   6 */
   7
   8/*
   9 * I hate traps on the sparc, grrr...
  10 */
  11
  12#include <linux/sched.h>  /* for jiffies */
  13#include <linux/kernel.h>
  14#include <linux/signal.h>
  15#include <linux/smp.h>
  16#include <linux/smp_lock.h>
  17#include <linux/kdebug.h>
  18
  19#include <asm/delay.h>
  20#include <asm/system.h>
  21#include <asm/ptrace.h>
  22#include <asm/oplib.h>
  23#include <asm/page.h>
  24#include <asm/pgtable.h>
  25#include <asm/unistd.h>
  26#include <asm/traps.h>
  27
  28/* #define TRAP_DEBUG */
  29
  30struct trap_trace_entry {
  31        unsigned long pc;
  32        unsigned long type;
  33};
  34
  35void syscall_trace_entry(struct pt_regs *regs)
  36{
  37        printk("%s[%d]: ", current->comm, task_pid_nr(current));
  38        printk("scall<%d> (could be %d)\n", (int) regs->u_regs[UREG_G1],
  39               (int) regs->u_regs[UREG_I0]);
  40}
  41
  42void syscall_trace_exit(struct pt_regs *regs)
  43{
  44}
  45
  46void sun4m_nmi(struct pt_regs *regs)
  47{
  48        unsigned long afsr, afar;
  49
  50        printk("Aieee: sun4m NMI received!\n");
  51        /* XXX HyperSparc hack XXX */
  52        __asm__ __volatile__("mov 0x500, %%g1\n\t"
  53                             "lda [%%g1] 0x4, %0\n\t"
  54                             "mov 0x600, %%g1\n\t"
  55                             "lda [%%g1] 0x4, %1\n\t" :
  56                             "=r" (afsr), "=r" (afar));
  57        printk("afsr=%08lx afar=%08lx\n", afsr, afar);
  58        printk("you lose buddy boy...\n");
  59        show_regs(regs);
  60        prom_halt();
  61}
  62
  63void sun4d_nmi(struct pt_regs *regs)
  64{
  65        printk("Aieee: sun4d NMI received!\n");
  66        printk("you lose buddy boy...\n");
  67        show_regs(regs);
  68        prom_halt();
  69}
  70
  71static void instruction_dump(unsigned long *pc)
  72{
  73        int i;
  74        
  75        if((((unsigned long) pc) & 3))
  76                return;
  77
  78        for(i = -3; i < 6; i++)
  79                printk("%c%08lx%c",i?' ':'<',pc[i],i?' ':'>');
  80        printk("\n");
  81}
  82
  83#define __SAVE __asm__ __volatile__("save %sp, -0x40, %sp\n\t")
  84#define __RESTORE __asm__ __volatile__("restore %g0, %g0, %g0\n\t")
  85
  86void die_if_kernel(char *str, struct pt_regs *regs)
  87{
  88        static int die_counter;
  89        int count = 0;
  90
  91        /* Amuse the user. */
  92        printk(
  93"              \\|/ ____ \\|/\n"
  94"              \"@'/ ,. \\`@\"\n"
  95"              /_| \\__/ |_\\\n"
  96"                 \\__U_/\n");
  97
  98        printk("%s(%d): %s [#%d]\n", current->comm, task_pid_nr(current), str, ++die_counter);
  99        show_regs(regs);
 100        add_taint(TAINT_DIE);
 101
 102        __SAVE; __SAVE; __SAVE; __SAVE;
 103        __SAVE; __SAVE; __SAVE; __SAVE;
 104        __RESTORE; __RESTORE; __RESTORE; __RESTORE;
 105        __RESTORE; __RESTORE; __RESTORE; __RESTORE;
 106
 107        {
 108                struct reg_window *rw = (struct reg_window *)regs->u_regs[UREG_FP];
 109
 110                /* Stop the back trace when we hit userland or we
 111                 * find some badly aligned kernel stack. Set an upper
 112                 * bound in case our stack is trashed and we loop.
 113                 */
 114                while(rw                                        &&
 115                      count++ < 30                              &&
 116                      (((unsigned long) rw) >= PAGE_OFFSET)     &&
 117                      !(((unsigned long) rw) & 0x7)) {
 118                        printk("Caller[%08lx]: %pS\n", rw->ins[7],
 119                               (void *) rw->ins[7]);
 120                        rw = (struct reg_window *)rw->ins[6];
 121                }
 122        }
 123        printk("Instruction DUMP:");
 124        instruction_dump ((unsigned long *) regs->pc);
 125        if(regs->psr & PSR_PS)
 126                do_exit(SIGKILL);
 127        do_exit(SIGSEGV);
 128}
 129
 130void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
 131{
 132        siginfo_t info;
 133
 134        if(type < 0x80) {
 135                /* Sun OS's puke from bad traps, Linux survives! */
 136                printk("Unimplemented Sparc TRAP, type = %02lx\n", type);
 137                die_if_kernel("Whee... Hello Mr. Penguin", regs);
 138        }       
 139
 140        if(regs->psr & PSR_PS)
 141                die_if_kernel("Kernel bad trap", regs);
 142
 143        info.si_signo = SIGILL;
 144        info.si_errno = 0;
 145        info.si_code = ILL_ILLTRP;
 146        info.si_addr = (void __user *)regs->pc;
 147        info.si_trapno = type - 0x80;
 148        force_sig_info(SIGILL, &info, current);
 149}
 150
 151void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 152                            unsigned long psr)
 153{
 154        extern int do_user_muldiv (struct pt_regs *, unsigned long);
 155        siginfo_t info;
 156
 157        if(psr & PSR_PS)
 158                die_if_kernel("Kernel illegal instruction", regs);
 159#ifdef TRAP_DEBUG
 160        printk("Ill instr. at pc=%08lx instruction is %08lx\n",
 161               regs->pc, *(unsigned long *)regs->pc);
 162#endif
 163        if (!do_user_muldiv (regs, pc))
 164                return;
 165
 166        info.si_signo = SIGILL;
 167        info.si_errno = 0;
 168        info.si_code = ILL_ILLOPC;
 169        info.si_addr = (void __user *)pc;
 170        info.si_trapno = 0;
 171        send_sig_info(SIGILL, &info, current);
 172}
 173
 174void do_priv_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 175                         unsigned long psr)
 176{
 177        siginfo_t info;
 178
 179        if(psr & PSR_PS)
 180                die_if_kernel("Penguin instruction from Penguin mode??!?!", regs);
 181        info.si_signo = SIGILL;
 182        info.si_errno = 0;
 183        info.si_code = ILL_PRVOPC;
 184        info.si_addr = (void __user *)pc;
 185        info.si_trapno = 0;
 186        send_sig_info(SIGILL, &info, current);
 187}
 188
 189/* XXX User may want to be allowed to do this. XXX */
 190
 191void do_memaccess_unaligned(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 192                            unsigned long psr)
 193{
 194        siginfo_t info;
 195
 196        if(regs->psr & PSR_PS) {
 197                printk("KERNEL MNA at pc %08lx npc %08lx called by %08lx\n", pc, npc,
 198                       regs->u_regs[UREG_RETPC]);
 199                die_if_kernel("BOGUS", regs);
 200                /* die_if_kernel("Kernel MNA access", regs); */
 201        }
 202#if 0
 203        show_regs (regs);
 204        instruction_dump ((unsigned long *) regs->pc);
 205        printk ("do_MNA!\n");
 206#endif
 207        info.si_signo = SIGBUS;
 208        info.si_errno = 0;
 209        info.si_code = BUS_ADRALN;
 210        info.si_addr = /* FIXME: Should dig out mna address */ (void *)0;
 211        info.si_trapno = 0;
 212        send_sig_info(SIGBUS, &info, current);
 213}
 214
 215extern void fpsave(unsigned long *fpregs, unsigned long *fsr,
 216                   void *fpqueue, unsigned long *fpqdepth);
 217extern void fpload(unsigned long *fpregs, unsigned long *fsr);
 218
 219static unsigned long init_fsr = 0x0UL;
 220static unsigned long init_fregs[32] __attribute__ ((aligned (8))) =
 221                { ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
 222                  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
 223                  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL,
 224                  ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL, ~0UL };
 225
 226void do_fpd_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 227                 unsigned long psr)
 228{
 229        /* Sanity check... */
 230        if(psr & PSR_PS)
 231                die_if_kernel("Kernel gets FloatingPenguinUnit disabled trap", regs);
 232
 233        put_psr(get_psr() | PSR_EF);    /* Allow FPU ops. */
 234        regs->psr |= PSR_EF;
 235#ifndef CONFIG_SMP
 236        if(last_task_used_math == current)
 237                return;
 238        if(last_task_used_math) {
 239                /* Other processes fpu state, save away */
 240                struct task_struct *fptask = last_task_used_math;
 241                fpsave(&fptask->thread.float_regs[0], &fptask->thread.fsr,
 242                       &fptask->thread.fpqueue[0], &fptask->thread.fpqdepth);
 243        }
 244        last_task_used_math = current;
 245        if(used_math()) {
 246                fpload(&current->thread.float_regs[0], &current->thread.fsr);
 247        } else {
 248                /* Set initial sane state. */
 249                fpload(&init_fregs[0], &init_fsr);
 250                set_used_math();
 251        }
 252#else
 253        if(!used_math()) {
 254                fpload(&init_fregs[0], &init_fsr);
 255                set_used_math();
 256        } else {
 257                fpload(&current->thread.float_regs[0], &current->thread.fsr);
 258        }
 259        set_thread_flag(TIF_USEDFPU);
 260#endif
 261}
 262
 263static unsigned long fake_regs[32] __attribute__ ((aligned (8)));
 264static unsigned long fake_fsr;
 265static unsigned long fake_queue[32] __attribute__ ((aligned (8)));
 266static unsigned long fake_depth;
 267
 268extern int do_mathemu(struct pt_regs *, struct task_struct *);
 269
 270void do_fpe_trap(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 271                 unsigned long psr)
 272{
 273        static int calls;
 274        siginfo_t info;
 275        unsigned long fsr;
 276        int ret = 0;
 277#ifndef CONFIG_SMP
 278        struct task_struct *fpt = last_task_used_math;
 279#else
 280        struct task_struct *fpt = current;
 281#endif
 282        put_psr(get_psr() | PSR_EF);
 283        /* If nobody owns the fpu right now, just clear the
 284         * error into our fake static buffer and hope it don't
 285         * happen again.  Thank you crashme...
 286         */
 287#ifndef CONFIG_SMP
 288        if(!fpt) {
 289#else
 290        if (!test_tsk_thread_flag(fpt, TIF_USEDFPU)) {
 291#endif
 292                fpsave(&fake_regs[0], &fake_fsr, &fake_queue[0], &fake_depth);
 293                regs->psr &= ~PSR_EF;
 294                return;
 295        }
 296        fpsave(&fpt->thread.float_regs[0], &fpt->thread.fsr,
 297               &fpt->thread.fpqueue[0], &fpt->thread.fpqdepth);
 298#ifdef DEBUG_FPU
 299        printk("Hmm, FP exception, fsr was %016lx\n", fpt->thread.fsr);
 300#endif
 301
 302        switch ((fpt->thread.fsr & 0x1c000)) {
 303        /* switch on the contents of the ftt [floating point trap type] field */
 304#ifdef DEBUG_FPU
 305        case (1 << 14):
 306                printk("IEEE_754_exception\n");
 307                break;
 308#endif
 309        case (2 << 14):  /* unfinished_FPop (underflow & co) */
 310        case (3 << 14):  /* unimplemented_FPop (quad stuff, maybe sqrt) */
 311                ret = do_mathemu(regs, fpt);
 312                break;
 313#ifdef DEBUG_FPU
 314        case (4 << 14):
 315                printk("sequence_error (OS bug...)\n");
 316                break;
 317        case (5 << 14):
 318                printk("hardware_error (uhoh!)\n");
 319                break;
 320        case (6 << 14):
 321                printk("invalid_fp_register (user error)\n");
 322                break;
 323#endif /* DEBUG_FPU */
 324        }
 325        /* If we successfully emulated the FPop, we pretend the trap never happened :-> */
 326        if (ret) {
 327                fpload(&current->thread.float_regs[0], &current->thread.fsr);
 328                return;
 329        }
 330        /* nope, better SIGFPE the offending process... */
 331               
 332#ifdef CONFIG_SMP
 333        clear_tsk_thread_flag(fpt, TIF_USEDFPU);
 334#endif
 335        if(psr & PSR_PS) {
 336                /* The first fsr store/load we tried trapped,
 337                 * the second one will not (we hope).
 338                 */
 339                printk("WARNING: FPU exception from kernel mode. at pc=%08lx\n",
 340                       regs->pc);
 341                regs->pc = regs->npc;
 342                regs->npc += 4;
 343                calls++;
 344                if(calls > 2)
 345                        die_if_kernel("Too many Penguin-FPU traps from kernel mode",
 346                                      regs);
 347                return;
 348        }
 349
 350        fsr = fpt->thread.fsr;
 351        info.si_signo = SIGFPE;
 352        info.si_errno = 0;
 353        info.si_addr = (void __user *)pc;
 354        info.si_trapno = 0;
 355        info.si_code = __SI_FAULT;
 356        if ((fsr & 0x1c000) == (1 << 14)) {
 357                if (fsr & 0x10)
 358                        info.si_code = FPE_FLTINV;
 359                else if (fsr & 0x08)
 360                        info.si_code = FPE_FLTOVF;
 361                else if (fsr & 0x04)
 362                        info.si_code = FPE_FLTUND;
 363                else if (fsr & 0x02)
 364                        info.si_code = FPE_FLTDIV;
 365                else if (fsr & 0x01)
 366                        info.si_code = FPE_FLTRES;
 367        }
 368        send_sig_info(SIGFPE, &info, fpt);
 369#ifndef CONFIG_SMP
 370        last_task_used_math = NULL;
 371#endif
 372        regs->psr &= ~PSR_EF;
 373        if(calls > 0)
 374                calls=0;
 375}
 376
 377void handle_tag_overflow(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 378                         unsigned long psr)
 379{
 380        siginfo_t info;
 381
 382        if(psr & PSR_PS)
 383                die_if_kernel("Penguin overflow trap from kernel mode", regs);
 384        info.si_signo = SIGEMT;
 385        info.si_errno = 0;
 386        info.si_code = EMT_TAGOVF;
 387        info.si_addr = (void __user *)pc;
 388        info.si_trapno = 0;
 389        send_sig_info(SIGEMT, &info, current);
 390}
 391
 392void handle_watchpoint(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 393                       unsigned long psr)
 394{
 395#ifdef TRAP_DEBUG
 396        printk("Watchpoint detected at PC %08lx NPC %08lx PSR %08lx\n",
 397               pc, npc, psr);
 398#endif
 399        if(psr & PSR_PS)
 400                panic("Tell me what a watchpoint trap is, and I'll then deal "
 401                      "with such a beast...");
 402}
 403
 404void handle_reg_access(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 405                       unsigned long psr)
 406{
 407        siginfo_t info;
 408
 409#ifdef TRAP_DEBUG
 410        printk("Register Access Exception at PC %08lx NPC %08lx PSR %08lx\n",
 411               pc, npc, psr);
 412#endif
 413        info.si_signo = SIGBUS;
 414        info.si_errno = 0;
 415        info.si_code = BUS_OBJERR;
 416        info.si_addr = (void __user *)pc;
 417        info.si_trapno = 0;
 418        force_sig_info(SIGBUS, &info, current);
 419}
 420
 421void handle_cp_disabled(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 422                        unsigned long psr)
 423{
 424        siginfo_t info;
 425
 426        info.si_signo = SIGILL;
 427        info.si_errno = 0;
 428        info.si_code = ILL_COPROC;
 429        info.si_addr = (void __user *)pc;
 430        info.si_trapno = 0;
 431        send_sig_info(SIGILL, &info, current);
 432}
 433
 434void handle_cp_exception(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 435                         unsigned long psr)
 436{
 437        siginfo_t info;
 438
 439#ifdef TRAP_DEBUG
 440        printk("Co-Processor Exception at PC %08lx NPC %08lx PSR %08lx\n",
 441               pc, npc, psr);
 442#endif
 443        info.si_signo = SIGILL;
 444        info.si_errno = 0;
 445        info.si_code = ILL_COPROC;
 446        info.si_addr = (void __user *)pc;
 447        info.si_trapno = 0;
 448        send_sig_info(SIGILL, &info, current);
 449}
 450
 451void handle_hw_divzero(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 452                       unsigned long psr)
 453{
 454        siginfo_t info;
 455
 456        info.si_signo = SIGFPE;
 457        info.si_errno = 0;
 458        info.si_code = FPE_INTDIV;
 459        info.si_addr = (void __user *)pc;
 460        info.si_trapno = 0;
 461        send_sig_info(SIGFPE, &info, current);
 462}
 463
 464#ifdef CONFIG_DEBUG_BUGVERBOSE
 465void do_BUG(const char *file, int line)
 466{
 467        // bust_spinlocks(1);   XXX Not in our original BUG()
 468        printk("kernel BUG at %s:%d!\n", file, line);
 469}
 470#endif
 471
 472/* Since we have our mappings set up, on multiprocessors we can spin them
 473 * up here so that timer interrupts work during initialization.
 474 */
 475
 476extern void sparc_cpu_startup(void);
 477
 478void trap_init(void)
 479{
 480        extern void thread_info_offsets_are_bolixed_pete(void);
 481
 482        /* Force linker to barf if mismatched */
 483        if (TI_UWINMASK    != offsetof(struct thread_info, uwinmask) ||
 484            TI_TASK        != offsetof(struct thread_info, task) ||
 485            TI_EXECDOMAIN  != offsetof(struct thread_info, exec_domain) ||
 486            TI_FLAGS       != offsetof(struct thread_info, flags) ||
 487            TI_CPU         != offsetof(struct thread_info, cpu) ||
 488            TI_PREEMPT     != offsetof(struct thread_info, preempt_count) ||
 489            TI_SOFTIRQ     != offsetof(struct thread_info, softirq_count) ||
 490            TI_HARDIRQ     != offsetof(struct thread_info, hardirq_count) ||
 491            TI_KSP         != offsetof(struct thread_info, ksp) ||
 492            TI_KPC         != offsetof(struct thread_info, kpc) ||
 493            TI_KPSR        != offsetof(struct thread_info, kpsr) ||
 494            TI_KWIM        != offsetof(struct thread_info, kwim) ||
 495            TI_REG_WINDOW  != offsetof(struct thread_info, reg_window) ||
 496            TI_RWIN_SPTRS  != offsetof(struct thread_info, rwbuf_stkptrs) ||
 497            TI_W_SAVED     != offsetof(struct thread_info, w_saved))
 498                thread_info_offsets_are_bolixed_pete();
 499
 500        /* Attach to the address space of init_task. */
 501        atomic_inc(&init_mm.mm_count);
 502        current->active_mm = &init_mm;
 503
 504        /* NOTE: Other cpus have this done as they are started
 505         *       up on SMP.
 506         */
 507}
 508