Pull asus into release branch
[pandora-kernel.git] / arch / mips / kernel / signal.c
index 54398af..07d6730 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
 #include <linux/compiler.h>
+#include <linux/uaccess.h>
 
 #include <asm/abi.h>
 #include <asm/asm.h>
 #include <asm/cacheflush.h>
 #include <asm/fpu.h>
 #include <asm/sim.h>
-#include <asm/uaccess.h>
 #include <asm/ucontext.h>
 #include <asm/cpu-features.h>
 #include <asm/war.h>
 
 #include "signal-common.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
-
+/*
+ * Horribly complicated - with the bloody RM9000 workarounds enabled
+ * the signal trampolines is moving to the end of the structure so we can
+ * increase the alignment without breaking software compatibility.
+ */
 #if ICACHE_REFILLS_WORKAROUND_WAR == 0
 
+struct sigframe {
+       u32 sf_ass[4];          /* argument save space for o32 */
+       u32 sf_code[2];         /* signal trampoline */
+       struct sigcontext sf_sc;
+       sigset_t sf_mask;
+};
+
 struct rt_sigframe {
        u32 rs_ass[4];          /* argument save space for o32 */
        u32 rs_code[2];         /* signal trampoline */
@@ -47,6 +57,14 @@ struct rt_sigframe {
 
 #else
 
+struct sigframe {
+       u32 sf_ass[4];                  /* argument save space for o32 */
+       u32 sf_pad[2];
+       struct sigcontext sf_sc;        /* hw context */
+       sigset_t sf_mask;
+       u32 sf_code[8] ____cacheline_aligned;   /* signal trampoline */
+};
+
 struct rt_sigframe {
        u32 rs_ass[4];                  /* argument save space for o32 */
        u32 rs_pad[2];
@@ -60,10 +78,51 @@ struct rt_sigframe {
 /*
  * Helper routines
  */
+static int protected_save_fp_context(struct sigcontext __user *sc)
+{
+       int err;
+       while (1) {
+               lock_fpu_owner();
+               own_fpu_inatomic(1);
+               err = save_fp_context(sc); /* this might fail */
+               unlock_fpu_owner();
+               if (likely(!err))
+                       break;
+               /* touch the sigcontext and try again */
+               err = __put_user(0, &sc->sc_fpregs[0]) |
+                       __put_user(0, &sc->sc_fpregs[31]) |
+                       __put_user(0, &sc->sc_fpc_csr);
+               if (err)
+                       break;  /* really bad sigcontext */
+       }
+       return err;
+}
+
+static int protected_restore_fp_context(struct sigcontext __user *sc)
+{
+       int err, tmp;
+       while (1) {
+               lock_fpu_owner();
+               own_fpu_inatomic(0);
+               err = restore_fp_context(sc); /* this might fail */
+               unlock_fpu_owner();
+               if (likely(!err))
+                       break;
+               /* touch the sigcontext and try again */
+               err = __get_user(tmp, &sc->sc_fpregs[0]) |
+                       __get_user(tmp, &sc->sc_fpregs[31]) |
+                       __get_user(tmp, &sc->sc_fpc_csr);
+               if (err)
+                       break;  /* really bad sigcontext */
+       }
+       return err;
+}
+
 int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 {
        int err = 0;
        int i;
+       unsigned int used_math;
 
        err |= __put_user(regs->cp0_epc, &sc->sc_pc);
 
@@ -71,6 +130,9 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        for (i = 1; i < 32; i++)
                err |= __put_user(regs->regs[i], &sc->sc_regs[i]);
 
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+       err |= __put_user(regs->acx, &sc->sc_acx);
+#endif
        err |= __put_user(regs->hi, &sc->sc_mdhi);
        err |= __put_user(regs->lo, &sc->sc_mdlo);
        if (cpu_has_dsp) {
@@ -83,24 +145,48 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
        }
 
-       err |= __put_user(!!used_math(), &sc->sc_used_math);
+       used_math = !!used_math();
+       err |= __put_user(used_math, &sc->sc_used_math);
 
-       if (used_math()) {
+       if (used_math) {
                /*
                 * Save FPU state to signal context. Signal handler
                 * will "inherit" current FPU state.
                 */
-               preempt_disable();
+               err |= protected_save_fp_context(sc);
+       }
+       return err;
+}
 
-               if (!is_fpu_owner()) {
-                       own_fpu();
-                       restore_fp(current);
-               }
-               err |= save_fp_context(sc);
+int fpcsr_pending(unsigned int __user *fpcsr)
+{
+       int err, sig = 0;
+       unsigned int csr, enabled;
 
-               preempt_enable();
+       err = __get_user(csr, fpcsr);
+       enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5);
+       /*
+        * If the signal handler set some FPU exceptions, clear it and
+        * send SIGFPE.
+        */
+       if (csr & enabled) {
+               csr &= ~enabled;
+               err |= __put_user(csr, fpcsr);
+               sig = SIGFPE;
        }
-       return err;
+       return err ?: sig;
+}
+
+static int
+check_and_restore_fp_context(struct sigcontext __user *sc)
+{
+       int err, sig;
+
+       err = sig = fpcsr_pending(&sc->sc_fpc_csr);
+       if (err > 0)
+               err = 0;
+       err |= protected_restore_fp_context(sc);
+       return err ?: sig;
 }
 
 int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
@@ -114,6 +200,10 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
 
        err |= __get_user(regs->cp0_epc, &sc->sc_pc);
+
+#ifdef CONFIG_CPU_HAS_SMARTMIPS
+       err |= __get_user(regs->acx, &sc->sc_acx);
+#endif
        err |= __get_user(regs->hi, &sc->sc_mdhi);
        err |= __get_user(regs->lo, &sc->sc_mdlo);
        if (cpu_has_dsp) {
@@ -132,19 +222,15 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        err |= __get_user(used_math, &sc->sc_used_math);
        conditional_used_math(used_math);
 
-       preempt_disable();
-
-       if (used_math()) {
+       if (used_math) {
                /* restore fpu context if we have used it before */
-               own_fpu();
-               err |= restore_fp_context(sc);
+               if (!err)
+                       err = check_and_restore_fp_context(sc);
        } else {
                /* signal handler may have used FPU.  Give it up. */
-               lose_fpu();
+               lose_fpu(0);
        }
 
-       preempt_enable();
-
        return err;
 }
 
@@ -307,6 +393,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
 {
        struct sigframe __user *frame;
        sigset_t blocked;
+       int sig;
 
        frame = (struct sigframe __user *) regs.regs[29];
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -320,8 +407,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(&regs, &frame->sf_sc))
+       sig = restore_sigcontext(&regs, &frame->sf_sc);
+       if (sig < 0)
                goto badframe;
+       else if (sig)
+               force_sig(sig, current);
 
        /*
         * Don't let your children do this ...
@@ -343,6 +433,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        struct rt_sigframe __user *frame;
        sigset_t set;
        stack_t st;
+       int sig;
 
        frame = (struct rt_sigframe __user *) regs.regs[29];
        if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -356,8 +447,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
+       sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
+       if (sig < 0)
                goto badframe;
+       else if (sig)
+               force_sig(sig, current);
 
        if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st)))
                goto badframe;
@@ -380,7 +474,7 @@ badframe:
 }
 
 #ifdef CONFIG_TRAD_SIGNALS
-int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
+static int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
        int signr, sigset_t *set)
 {
        struct sigframe __user *frame;
@@ -425,7 +519,7 @@ give_sigsegv:
 }
 #endif
 
-int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
+static int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
        int signr, sigset_t *set, siginfo_t *info)
 {
        struct rt_sigframe __user *frame;
@@ -483,6 +577,14 @@ give_sigsegv:
        return -EFAULT;
 }
 
+struct mips_abi mips_abi = {
+#ifdef CONFIG_TRAD_SIGNALS
+       .setup_frame    = setup_frame,
+#endif
+       .setup_rt_frame = setup_rt_frame,
+       .restart        = __NR_restart_syscall
+};
+
 static int handle_signal(unsigned long sig, siginfo_t *info,
        struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
 {
@@ -521,7 +623,7 @@ static int handle_signal(unsigned long sig, siginfo_t *info,
        return ret;
 }
 
-void do_signal(struct pt_regs *regs)
+static void do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka;
        sigset_t *oldset;
@@ -571,7 +673,7 @@ void do_signal(struct pt_regs *regs)
                        regs->cp0_epc -= 8;
                }
                if (regs->regs[2] == ERESTART_RESTARTBLOCK) {
-                       regs->regs[2] = __NR_restart_syscall;
+                       regs->regs[2] = current->thread.abi->restart;
                        regs->regs[7] = regs->regs[26];
                        regs->cp0_epc -= 4;
                }
@@ -597,5 +699,5 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
 {
        /* deal with pending signal delivery */
        if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
-               current->thread.abi->do_signal(regs);
+               do_signal(regs);
 }