Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / arch / ia64 / kernel / traps.c
index e021594..7c1ddc8 100644 (file)
 #include <linux/tty.h>
 #include <linux/vt_kern.h>             /* For unblank_screen() */
 #include <linux/module.h>       /* for EXPORT_SYMBOL */
+#include <linux/hardirq.h>
+#include <linux/kprobes.h>
+#include <linux/delay.h>               /* for ssleep() */
 
 #include <asm/fpswa.h>
-#include <asm/hardirq.h>
 #include <asm/ia32.h>
 #include <asm/intrinsics.h>
 #include <asm/processor.h>
 #include <asm/uaccess.h>
-
-/*
- * fp_emulate() needs to be able to access and update all floating point registers.  Those
- * saved in pt_regs can be accessed through that structure, but those not saved, will be
- * accessed directly.  To make this work, we need to ensure that the compiler does not end
- * up using a preserved floating point register on its own.  The following achieves this
- * by declaring preserved registers that are not marked as "fixed" as global register
- * variables.
- */
-#ifdef ASM_SUPPORTED
-register double f2 asm ("f2"); register double f3 asm ("f3");
-register double f4 asm ("f4"); register double f5 asm ("f5");
-
-register long f16 asm ("f16"); register long f17 asm ("f17");
-register long f18 asm ("f18"); register long f19 asm ("f19");
-register long f20 asm ("f20"); register long f21 asm ("f21");
-register long f22 asm ("f22"); register long f23 asm ("f23");
-
-register double f24 asm ("f24"); register double f25 asm ("f25");
-register double f26 asm ("f26"); register double f27 asm ("f27");
-register double f28 asm ("f28"); register double f29 asm ("f29");
-register double f30 asm ("f30"); register double f31 asm ("f31");
-#endif
+#include <asm/kdebug.h>
 
 extern spinlock_t timerlist_lock;
 
 fpswa_interface_t *fpswa_interface;
 EXPORT_SYMBOL(fpswa_interface);
 
+ATOMIC_NOTIFIER_HEAD(ia64die_chain);
+
+int
+register_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&ia64die_chain, nb);
+}
+EXPORT_SYMBOL_GPL(register_die_notifier);
+
+int
+unregister_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&ia64die_chain, nb);
+}
+EXPORT_SYMBOL_GPL(unregister_die_notifier);
+
 void __init
 trap_init (void)
 {
@@ -99,18 +95,21 @@ die (const char *str, struct pt_regs *regs, long err)
                .lock_owner_depth =     0
        };
        static int die_counter;
+       int cpu = get_cpu();
 
-       if (die.lock_owner != smp_processor_id()) {
+       if (die.lock_owner != cpu) {
                console_verbose();
                spin_lock_irq(&die.lock);
-               die.lock_owner = smp_processor_id();
+               die.lock_owner = cpu;
                die.lock_owner_depth = 0;
                bust_spinlocks(1);
        }
+       put_cpu();
 
        if (++die.lock_owner_depth < 3) {
                printk("%s[%d]: %s %ld [%d]\n",
                        current->comm, current->pid, str, err, ++die_counter);
+               (void) notify_die(DIE_OOPS, (char *)str, regs, err, 255, SIGSEGV);
                show_regs(regs);
        } else
                printk(KERN_ERR "Recursive die() failure, output suppressed\n");
@@ -118,6 +117,13 @@ die (const char *str, struct pt_regs *regs, long err)
        bust_spinlocks(0);
        die.lock_owner = -1;
        spin_unlock_irq(&die.lock);
+
+       if (panic_on_oops) {
+               printk(KERN_EMERG "Fatal exception: panic in 5 seconds\n");
+               ssleep(5);
+               panic("Fatal exception");
+       }
+
        do_exit(SIGSEGV);
 }
 
@@ -129,19 +135,22 @@ die_if_kernel (char *str, struct pt_regs *regs, long err)
 }
 
 void
-ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
+__kprobes ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
 {
        siginfo_t siginfo;
        int sig, code;
 
        /* SIGILL, SIGFPE, SIGSEGV, and SIGBUS want these field initialized: */
-       siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+       siginfo.si_addr = (void __user *) (regs->cr_iip + ia64_psr(regs)->ri);
        siginfo.si_imm = break_num;
        siginfo.si_flags = 0;           /* clear __ISR_VALID */
        siginfo.si_isr = 0;
 
        switch (break_num) {
              case 0: /* unknown error (used by GCC for __builtin_abort()) */
+               if (notify_die(DIE_BREAK, "break 0", regs, break_num, TRAP_BRKPT, SIGTRAP)
+                               == NOTIFY_STOP)
+                       return;
                die_if_kernel("bugcheck!", regs, break_num);
                sig = SIGILL; code = ILL_ILLOPC;
                break;
@@ -201,6 +210,9 @@ ia64_bad_break (unsigned long break_num, struct pt_regs *regs)
                if (break_num < 0x80000) {
                        sig = SIGILL; code = __ILL_BREAK;
                } else {
+                       if (notify_die(DIE_BREAK, "bad break", regs, break_num, TRAP_BRKPT, SIGTRAP)
+                                       == NOTIFY_STOP)
+                               return;
                        sig = SIGTRAP; code = TRAP_BRKPT;
                }
        }
@@ -225,13 +237,21 @@ disabled_fph_fault (struct pt_regs *regs)
 
        /* first, grant user-level access to fph partition: */
        psr->dfh = 0;
+
+       /*
+        * Make sure that no other task gets in on this processor
+        * while we're claiming the FPU
+        */
+       preempt_disable();
 #ifndef CONFIG_SMP
        {
                struct task_struct *fpu_owner
                        = (struct task_struct *)ia64_get_kr(IA64_KR_FPU_OWNER);
 
-               if (ia64_is_local_fpu_owner(current))
+               if (ia64_is_local_fpu_owner(current)) {
+                       preempt_enable_no_resched();
                        return;
+               }
 
                if (fpu_owner)
                        ia64_flush_fph(fpu_owner);
@@ -249,6 +269,7 @@ disabled_fph_fault (struct pt_regs *regs)
                 */
                psr->mfh = 1;
        }
+       preempt_enable_no_resched();
 }
 
 static inline int
@@ -305,7 +326,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
        fault_ip = regs->cr_iip;
        if (!fp_fault && (ia64_psr(regs)->ri == 0))
                fault_ip -= 16;
-       if (copy_from_user(bundle, (void *) fault_ip, sizeof(bundle)))
+       if (copy_from_user(bundle, (void __user *) fault_ip, sizeof(bundle)))
                return -1;
 
        if (jiffies - last_time > 5*HZ)
@@ -335,7 +356,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
                        siginfo.si_signo = SIGFPE;
                        siginfo.si_errno = 0;
                        siginfo.si_code = __SI_FAULT;   /* default code */
-                       siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                       siginfo.si_addr = (void __user *) (regs->cr_iip + ia64_psr(regs)->ri);
                        if (isr & 0x11) {
                                siginfo.si_code = FPE_FLTINV;
                        } else if (isr & 0x22) {
@@ -359,7 +380,7 @@ handle_fpu_swa (int fp_fault, struct pt_regs *regs, unsigned long isr)
                        siginfo.si_signo = SIGFPE;
                        siginfo.si_errno = 0;
                        siginfo.si_code = __SI_FAULT;   /* default code */
-                       siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                       siginfo.si_addr = (void __user *) (regs->cr_iip + ia64_psr(regs)->ri);
                        if (isr & 0x880) {
                                siginfo.si_code = FPE_FLTOVF;
                        } else if (isr & 0x1100) {
@@ -381,11 +402,10 @@ struct illegal_op_return {
 };
 
 struct illegal_op_return
-ia64_illegal_op_fault (unsigned long ec, unsigned long arg1, unsigned long arg2,
-                      unsigned long arg3, unsigned long arg4, unsigned long arg5,
-                      unsigned long arg6, unsigned long arg7, unsigned long stack)
+ia64_illegal_op_fault (unsigned long ec, long arg1, long arg2, long arg3,
+                      long arg4, long arg5, long arg6, long arg7,
+                      struct pt_regs regs)
 {
-       struct pt_regs *regs = (struct pt_regs *) &stack;
        struct illegal_op_return rv;
        struct siginfo si;
        char buf[128];
@@ -394,31 +414,30 @@ ia64_illegal_op_fault (unsigned long ec, unsigned long arg1, unsigned long arg2,
        {
                extern struct illegal_op_return ia64_emulate_brl (struct pt_regs *, unsigned long);
 
-               rv = ia64_emulate_brl(regs, ec);
+               rv = ia64_emulate_brl(&regs, ec);
                if (rv.fkt != (unsigned long) -1)
                        return rv;
        }
 #endif
 
        sprintf(buf, "IA-64 Illegal operation fault");
-       die_if_kernel(buf, regs, 0);
+       die_if_kernel(buf, &regs, 0);
 
        memset(&si, 0, sizeof(si));
        si.si_signo = SIGILL;
        si.si_code = ILL_ILLOPC;
-       si.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+       si.si_addr = (void __user *) (regs.cr_iip + ia64_psr(&regs)->ri);
        force_sig_info(SIGILL, &si, current);
        rv.fkt = 0;
        return rv;
 }
 
-void
+void __kprobes
 ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
-           unsigned long iim, unsigned long itir, unsigned long arg5,
-           unsigned long arg6, unsigned long arg7, unsigned long stack)
+           unsigned long iim, unsigned long itir, long arg5, long arg6,
+           long arg7, struct pt_regs regs)
 {
-       struct pt_regs *regs = (struct pt_regs *) &stack;
-       unsigned long code, error = isr;
+       unsigned long code, error = isr, iip;
        struct siginfo siginfo;
        char buf[128];
        int result, sig;
@@ -438,10 +457,12 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                 * This fault was due to lfetch.fault, set "ed" bit in the psr to cancel
                 * the lfetch.
                 */
-               ia64_psr(regs)->ed = 1;
+               ia64_psr(&regs)->ed = 1;
                return;
        }
 
+       iip = regs.cr_iip + ia64_psr(&regs)->ri;
+
        switch (vector) {
              case 24: /* General Exception */
                code = (isr >> 4) & 0xf;
@@ -451,8 +472,8 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                if (code == 8) {
 # ifdef CONFIG_IA64_PRINT_HAZARDS
                        printk("%s[%d]: possible hazard @ ip=%016lx (pr = %016lx)\n",
-                              current->comm, current->pid, regs->cr_iip + ia64_psr(regs)->ri,
-                              regs->pr);
+                              current->comm, current->pid,
+                              regs.cr_iip + ia64_psr(&regs)->ri, regs.pr);
 # endif
                        return;
                }
@@ -460,26 +481,27 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
 
              case 25: /* Disabled FP-Register */
                if (isr & 2) {
-                       disabled_fph_fault(regs);
+                       disabled_fph_fault(&regs);
                        return;
                }
                sprintf(buf, "Disabled FPL fault---not supposed to happen!");
                break;
 
              case 26: /* NaT Consumption */
-               if (user_mode(regs)) {
-                       void *addr;
+               if (user_mode(&regs)) {
+                       void __user *addr;
 
                        if (((isr >> 4) & 0xf) == 2) {
                                /* NaT page consumption */
                                sig = SIGSEGV;
                                code = SEGV_ACCERR;
-                               addr = (void *) ifa;
+                               addr = (void __user *) ifa;
                        } else {
                                /* register NaT consumption */
                                sig = SIGILL;
                                code = ILL_ILLOPN;
-                               addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                               addr = (void __user *) (regs.cr_iip
+                                                       + ia64_psr(&regs)->ri);
                        }
                        siginfo.si_signo = sig;
                        siginfo.si_code = code;
@@ -490,17 +512,17 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                        siginfo.si_isr = isr;
                        force_sig_info(sig, &siginfo, current);
                        return;
-               } else if (ia64_done_with_exception(regs))
+               } else if (ia64_done_with_exception(&regs))
                        return;
                sprintf(buf, "NaT consumption");
                break;
 
              case 31: /* Unsupported Data Reference */
-               if (user_mode(regs)) {
+               if (user_mode(&regs)) {
                        siginfo.si_signo = SIGILL;
                        siginfo.si_code = ILL_ILLOPN;
                        siginfo.si_errno = 0;
-                       siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                       siginfo.si_addr = (void __user *) iip;
                        siginfo.si_imm = vector;
                        siginfo.si_flags = __ISR_VALID;
                        siginfo.si_isr = isr;
@@ -513,21 +535,24 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
              case 29: /* Debug */
              case 35: /* Taken Branch Trap */
              case 36: /* Single Step Trap */
-               if (fsys_mode(current, regs)) {
+               if (fsys_mode(current, &regs)) {
                        extern char __kernel_syscall_via_break[];
                        /*
-                        * Got a trap in fsys-mode: Taken Branch Trap and Single Step trap
-                        * need special handling; Debug trap is not supposed to happen.
+                        * Got a trap in fsys-mode: Taken Branch Trap
+                        * and Single Step trap need special handling;
+                        * Debug trap is ignored (we disable it here
+                        * and re-enable it in the lower-privilege trap).
                         */
                        if (unlikely(vector == 29)) {
-                               die("Got debug trap in fsys-mode---not supposed to happen!",
-                                   regs, 0);
+                               set_thread_flag(TIF_DB_DISABLED);
+                               ia64_psr(&regs)->db = 0;
+                               ia64_psr(&regs)->lp = 1;
                                return;
                        }
                        /* re-do the system call via break 0x100000: */
-                       regs->cr_iip = (unsigned long) __kernel_syscall_via_break;
-                       ia64_psr(regs)->ri = 0;
-                       ia64_psr(regs)->cpl = 3;
+                       regs.cr_iip = (unsigned long) __kernel_syscall_via_break;
+                       ia64_psr(&regs)->ri = 0;
+                       ia64_psr(&regs)->cpl = 3;
                        return;
                }
                switch (vector) {
@@ -538,16 +563,19 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                         * Erratum 10 (IFA may contain incorrect address) now has
                         * "NoFix" status.  There are no plans for fixing this.
                         */
-                       if (ia64_psr(regs)->is == 0)
-                         ifa = regs->cr_iip;
+                       if (ia64_psr(&regs)->is == 0)
+                         ifa = regs.cr_iip;
 #endif
                        break;
                      case 35: siginfo.si_code = TRAP_BRANCH; ifa = 0; break;
                      case 36: siginfo.si_code = TRAP_TRACE; ifa = 0; break;
                }
+               if (notify_die(DIE_FAULT, "ia64_fault", &regs, vector, siginfo.si_code, SIGTRAP)
+                               == NOTIFY_STOP)
+                       return;
                siginfo.si_signo = SIGTRAP;
                siginfo.si_errno = 0;
-               siginfo.si_addr  = (void *) ifa;
+               siginfo.si_addr  = (void __user *) ifa;
                siginfo.si_imm   = 0;
                siginfo.si_flags = __ISR_VALID;
                siginfo.si_isr   = isr;
@@ -556,12 +584,12 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
 
              case 32: /* fp fault */
              case 33: /* fp trap */
-               result = handle_fpu_swa((vector == 32) ? 1 : 0, regs, isr);
+               result = handle_fpu_swa((vector == 32) ? 1 : 0, &regs, isr);
                if ((result < 0) || (current->thread.flags & IA64_THREAD_FPEMU_SIGFPE)) {
                        siginfo.si_signo = SIGFPE;
                        siginfo.si_errno = 0;
                        siginfo.si_code = FPE_FLTINV;
-                       siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                       siginfo.si_addr = (void __user *) iip;
                        siginfo.si_flags = __ISR_VALID;
                        siginfo.si_isr = isr;
                        siginfo.si_imm = 0;
@@ -572,23 +600,32 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
              case 34:
                if (isr & 0x2) {
                        /* Lower-Privilege Transfer Trap */
+
+                       /* If we disabled debug traps during an fsyscall,
+                        * re-enable them here.
+                        */
+                       if (test_thread_flag(TIF_DB_DISABLED)) {
+                               clear_thread_flag(TIF_DB_DISABLED);
+                               ia64_psr(&regs)->db = 1;
+                       }
+
                        /*
-                        * Just clear PSR.lp and then return immediately: all the
-                        * interesting work (e.g., signal delivery is done in the kernel
-                        * exit path).
+                        * Just clear PSR.lp and then return immediately:
+                        * all the interesting work (e.g., signal delivery)
+                        * is done in the kernel exit path.
                         */
-                       ia64_psr(regs)->lp = 0;
+                       ia64_psr(&regs)->lp = 0;
                        return;
                } else {
                        /* Unimplemented Instr. Address Trap */
-                       if (user_mode(regs)) {
+                       if (user_mode(&regs)) {
                                siginfo.si_signo = SIGILL;
                                siginfo.si_code = ILL_BADIADDR;
                                siginfo.si_errno = 0;
                                siginfo.si_flags = 0;
                                siginfo.si_isr = 0;
                                siginfo.si_imm = 0;
-                               siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri);
+                               siginfo.si_addr = (void __user *) iip;
                                force_sig_info(SIGILL, &siginfo, current);
                                return;
                        }
@@ -598,23 +635,23 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
 
              case 45:
 #ifdef CONFIG_IA32_SUPPORT
-               if (ia32_exception(regs, isr) == 0)
+               if (ia32_exception(&regs, isr) == 0)
                        return;
 #endif
                printk(KERN_ERR "Unexpected IA-32 exception (Trap 45)\n");
                printk(KERN_ERR "  iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx\n",
-                      regs->cr_iip, ifa, isr);
+                      iip, ifa, isr);
                force_sig(SIGSEGV, current);
                break;
 
              case 46:
 #ifdef CONFIG_IA32_SUPPORT
-               if (ia32_intercept(regs, isr) == 0)
+               if (ia32_intercept(&regs, isr) == 0)
                        return;
 #endif
                printk(KERN_ERR "Unexpected IA-32 intercept trap (Trap 46)\n");
                printk(KERN_ERR "  iip - 0x%lx, ifa - 0x%lx, isr - 0x%lx, iim - 0x%lx\n",
-                      regs->cr_iip, ifa, isr, iim);
+                      iip, ifa, isr, iim);
                force_sig(SIGSEGV, current);
                return;
 
@@ -626,6 +663,6 @@ ia64_fault (unsigned long vector, unsigned long isr, unsigned long ifa,
                sprintf(buf, "Fault %lu", vector);
                break;
        }
-       die_if_kernel(buf, regs, error);
+       die_if_kernel(buf, &regs, error);
        force_sig(SIGILL, current);
 }