Merge branch 'linus' into cont_syslog
[safe/jmp/linux-2.6] / arch / mips / kernel / signal.c
index 9a44053..2099d5a 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/mm.h>
 #include <linux/personality.h>
 #include <linux/smp.h>
-#include <linux/smp_lock.h>
 #include <linux/kernel.h>
 #include <linux/signal.h>
 #include <linux/errno.h>
@@ -20,6 +19,9 @@
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
 #include <linux/compiler.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/tracehook.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 <asm/vdso.h>
 
 #include "signal-common.h"
 
-#define DEBUG_SIG 0
+static int (*save_fp_context)(struct sigcontext __user *sc);
+static int (*restore_fp_context)(struct sigcontext __user *sc);
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
+extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
+
+extern asmlinkage int fpu_emulator_save_context(struct sigcontext __user *sc);
+extern asmlinkage int fpu_emulator_restore_context(struct sigcontext __user *sc);
+
+struct sigframe {
+       u32 sf_ass[4];          /* argument save space for o32 */
+       u32 sf_pad[2];          /* Was: signal trampoline */
+       struct sigcontext sf_sc;
+       sigset_t sf_mask;
+};
+
+struct rt_sigframe {
+       u32 rs_ass[4];          /* argument save space for o32 */
+       u32 rs_pad[2];          /* Was: signal trampoline */
+       struct siginfo rs_info;
+       struct ucontext rs_uc;
+};
+
+/*
+ * 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);
+
+       err |= __put_user(0, &sc->sc_regs[0]);
+       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) {
+               err |= __put_user(mfhi1(), &sc->sc_hi1);
+               err |= __put_user(mflo1(), &sc->sc_lo1);
+               err |= __put_user(mfhi2(), &sc->sc_hi2);
+               err |= __put_user(mflo2(), &sc->sc_lo2);
+               err |= __put_user(mfhi3(), &sc->sc_hi3);
+               err |= __put_user(mflo3(), &sc->sc_lo3);
+               err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
+       }
+
+       used_math = !!used_math();
+       err |= __put_user(used_math, &sc->sc_used_math);
+
+       if (used_math) {
+               /*
+                * Save FPU state to signal context. Signal handler
+                * will "inherit" current FPU state.
+                */
+               err |= protected_save_fp_context(sc);
+       }
+       return err;
+}
+
+int fpcsr_pending(unsigned int __user *fpcsr)
+{
+       int err, sig = 0;
+       unsigned int csr, enabled;
+
+       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 ?: 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)
+{
+       unsigned int used_math;
+       unsigned long treg;
+       int err = 0;
+       int i;
+
+       /* Always make any pending restarted system calls return -EINTR */
+       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) {
+               err |= __get_user(treg, &sc->sc_hi1); mthi1(treg);
+               err |= __get_user(treg, &sc->sc_lo1); mtlo1(treg);
+               err |= __get_user(treg, &sc->sc_hi2); mthi2(treg);
+               err |= __get_user(treg, &sc->sc_lo2); mtlo2(treg);
+               err |= __get_user(treg, &sc->sc_hi3); mthi3(treg);
+               err |= __get_user(treg, &sc->sc_lo3); mtlo3(treg);
+               err |= __get_user(treg, &sc->sc_dsp); wrdsp(treg, DSP_MASK);
+       }
+
+       for (i = 1; i < 32; i++)
+               err |= __get_user(regs->regs[i], &sc->sc_regs[i]);
+
+       err |= __get_user(used_math, &sc->sc_used_math);
+       conditional_used_math(used_math);
+
+       if (used_math) {
+               /* restore fpu context if we have used it before */
+               if (!err)
+                       err = check_and_restore_fp_context(sc);
+       } else {
+               /* signal handler may have used FPU.  Give it up. */
+               lose_fpu(0);
+       }
+
+       return err;
+}
+
+void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+                         size_t frame_size)
+{
+       unsigned long sp;
+
+       /* Default to using normal stack */
+       sp = regs->regs[29];
+
+       /*
+        * FPU emulator may have it's own trampoline active just
+        * above the user stack, 16-bytes before the next lowest
+        * 16 byte boundary.  Try to avoid trashing it.
+        */
+       sp -= 32;
+
+       /* This is the X/Open sanctioned signal stack switching.  */
+       if ((ka->sa.sa_flags & SA_ONSTACK) && (sas_ss_flags (sp) == 0))
+               sp = current->sas_ss_sp + current->sas_ss_size;
+
+       return (void __user *)((sp - frame_size) & (ICACHE_REFILLS_WORKAROUND_WAR ? ~(cpu_icache_line_size()-1) : ALMASK));
+}
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
 
 #ifdef CONFIG_TRAD_SIGNALS
-save_static_function(sys_sigsuspend);
-__attribute_used__ noinline static int
-_sys_sigsuspend(nabi_no_regargs struct pt_regs regs)
+asmlinkage int sys_sigsuspend(nabi_no_regargs struct pt_regs regs)
 {
        sigset_t newset;
        sigset_t __user *uset;
@@ -68,9 +268,7 @@ _sys_sigsuspend(nabi_no_regargs struct pt_regs regs)
 }
 #endif
 
-save_static_function(sys_rt_sigsuspend);
-__attribute_used__ noinline static int
-_sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
+asmlinkage int sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
 {
        sigset_t newset;
        sigset_t __user *unewset;
@@ -99,8 +297,8 @@ _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
 }
 
 #ifdef CONFIG_TRAD_SIGNALS
-asmlinkage int sys_sigaction(int sig, const struct sigaction __user *act,
-       struct sigaction __user *oact)
+SYSCALL_DEFINE3(sigaction, int, sig, const struct sigaction __user *, act,
+       struct sigaction __user *oact)
 {
        struct k_sigaction new_ka, old_ka;
        int ret;
@@ -148,48 +346,12 @@ asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs)
        return do_sigaltstack(uss, uoss, usp);
 }
 
-/*
- * 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.
- */
 #ifdef CONFIG_TRAD_SIGNALS
-struct sigframe {
-       u32 sf_ass[4];                  /* argument save space for o32 */
-#if ICACHE_REFILLS_WORKAROUND_WAR
-       u32 sf_pad[2];
-#else
-       u32 sf_code[2];                 /* signal trampoline */
-#endif
-       struct sigcontext sf_sc;
-       sigset_t sf_mask;
-#if ICACHE_REFILLS_WORKAROUND_WAR
-       u32 sf_code[8] ____cacheline_aligned;   /* signal trampoline */
-#endif
-};
-#endif
-
-struct rt_sigframe {
-       u32 rs_ass[4];                  /* argument save space for o32 */
-#if ICACHE_REFILLS_WORKAROUND_WAR
-       u32 rs_pad[2];
-#else
-       u32 rs_code[2];                 /* signal trampoline */
-#endif
-       struct siginfo rs_info;
-       struct ucontext rs_uc;
-#if ICACHE_REFILLS_WORKAROUND_WAR
-       u32 rs_code[8] ____cacheline_aligned;   /* signal trampoline */
-#endif
-};
-
-#ifdef CONFIG_TRAD_SIGNALS
-save_static_function(sys_sigreturn);
-__attribute_used__ noinline static void
-_sys_sigreturn(nabi_no_regargs struct pt_regs regs)
+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)))
@@ -203,8 +365,11 @@ _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 ...
@@ -221,13 +386,12 @@ badframe:
 }
 #endif /* CONFIG_TRAD_SIGNALS */
 
-save_static_function(sys_rt_sigreturn);
-__attribute_used__ noinline static void
-_sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
+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)))
@@ -241,8 +405,11 @@ _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;
@@ -265,8 +432,8 @@ badframe:
 }
 
 #ifdef CONFIG_TRAD_SIGNALS
-int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
-       int signr, sigset_t *set)
+static int setup_frame(void *sig_return, struct k_sigaction *ka,
+                      struct pt_regs *regs, int signr, sigset_t *set)
 {
        struct sigframe __user *frame;
        int err = 0;
@@ -275,8 +442,6 @@ int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
        if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
                goto give_sigsegv;
 
-       install_sigtramp(frame->sf_code, __NR_sigreturn);
-
        err |= setup_sigcontext(regs, &frame->sf_sc);
        err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set));
        if (err)
@@ -296,14 +461,12 @@ int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
        regs->regs[ 5] = 0;
        regs->regs[ 6] = (unsigned long) &frame->sf_sc;
        regs->regs[29] = (unsigned long) frame;
-       regs->regs[31] = (unsigned long) frame->sf_code;
+       regs->regs[31] = (unsigned long) sig_return;
        regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler;
 
-#if DEBUG_SIG
-       printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n",
+       DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n",
               current->comm, current->pid,
-              frame, regs->cp0_epc, frame->regs[31]);
-#endif
+              frame, regs->cp0_epc, regs->regs[31]);
        return 0;
 
 give_sigsegv:
@@ -312,8 +475,9 @@ give_sigsegv:
 }
 #endif
 
-int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
-       int signr, sigset_t *set, siginfo_t *info)
+static int setup_rt_frame(void *sig_return, struct k_sigaction *ka,
+                         struct pt_regs *regs, int signr, sigset_t *set,
+                         siginfo_t *info)
 {
        struct rt_sigframe __user *frame;
        int err = 0;
@@ -322,8 +486,6 @@ int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
        if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
                goto give_sigsegv;
 
-       install_sigtramp(frame->rs_code, __NR_rt_sigreturn);
-
        /* Create siginfo.  */
        err |= copy_siginfo_to_user(&frame->rs_info, info);
 
@@ -356,14 +518,13 @@ int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
        regs->regs[ 5] = (unsigned long) &frame->rs_info;
        regs->regs[ 6] = (unsigned long) &frame->rs_uc;
        regs->regs[29] = (unsigned long) frame;
-       regs->regs[31] = (unsigned long) frame->rs_code;
+       regs->regs[31] = (unsigned long) sig_return;
        regs->cp0_epc = regs->regs[25] = (unsigned long) ka->sa.sa_handler;
 
-#if DEBUG_SIG
-       printk("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%p\n",
+       DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n",
               current->comm, current->pid,
               frame, regs->cp0_epc, regs->regs[31]);
-#endif
+
        return 0;
 
 give_sigsegv:
@@ -371,10 +532,23 @@ give_sigsegv:
        return -EFAULT;
 }
 
-static inline int handle_signal(unsigned long sig, siginfo_t *info,
+struct mips_abi mips_abi = {
+#ifdef CONFIG_TRAD_SIGNALS
+       .setup_frame    = setup_frame,
+       .signal_return_offset = offsetof(struct mips_vdso, signal_trampoline),
+#endif
+       .setup_rt_frame = setup_rt_frame,
+       .rt_signal_return_offset =
+               offsetof(struct mips_vdso, rt_signal_trampoline),
+       .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)
 {
        int ret;
+       struct mips_abi *abi = current->thread.abi;
+       void *vdso = current->mm->context.vdso;
 
        switch(regs->regs[0]) {
        case ERESTART_RESTARTBLOCK:
@@ -395,21 +569,23 @@ static inline int handle_signal(unsigned long sig, siginfo_t *info,
        regs->regs[0] = 0;              /* Don't deal with this again.  */
 
        if (sig_uses_siginfo(ka))
-               ret = current->thread.abi->setup_rt_frame(ka, regs, sig, oldset, info);
+               ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset,
+                                         ka, regs, sig, oldset, info);
        else
-               ret = current->thread.abi->setup_frame(ka, regs, sig, oldset);
+               ret = abi->setup_frame(vdso + abi->signal_return_offset,
+                                      ka, regs, sig, oldset);
 
        spin_lock_irq(&current->sighand->siglock);
-       sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+       sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
        if (!(ka->sa.sa_flags & SA_NODEFER))
-               sigaddset(&current->blocked,sig);
+               sigaddset(&current->blocked, sig);
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
        return ret;
 }
 
-void do_signal(struct pt_regs *regs)
+static void do_signal(struct pt_regs *regs)
 {
        struct k_sigaction ka;
        sigset_t *oldset;
@@ -459,7 +635,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;
                }
@@ -485,5 +661,49 @@ 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);
+
+       if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+               clear_thread_flag(TIF_NOTIFY_RESUME);
+               tracehook_notify_resume(regs);
+               if (current->replacement_session_keyring)
+                       key_replace_session_keyring();
+       }
 }
+
+#ifdef CONFIG_SMP
+static int smp_save_fp_context(struct sigcontext __user *sc)
+{
+       return raw_cpu_has_fpu
+              ? _save_fp_context(sc)
+              : fpu_emulator_save_context(sc);
+}
+
+static int smp_restore_fp_context(struct sigcontext __user *sc)
+{
+       return raw_cpu_has_fpu
+              ? _restore_fp_context(sc)
+              : fpu_emulator_restore_context(sc);
+}
+#endif
+
+static int signal_setup(void)
+{
+#ifdef CONFIG_SMP
+       /* For now just do the cpu_has_fpu check when the functions are invoked */
+       save_fp_context = smp_save_fp_context;
+       restore_fp_context = smp_restore_fp_context;
+#else
+       if (cpu_has_fpu) {
+               save_fp_context = _save_fp_context;
+               restore_fp_context = _restore_fp_context;
+       } else {
+               save_fp_context = fpu_emulator_save_context;
+               restore_fp_context = fpu_emulator_restore_context;
+       }
+#endif
+
+       return 0;
+}
+
+arch_initcall(signal_setup);