[MIPS] signal: do not inline handle_signal()
[sfrench/cifs-2.6.git] / arch / mips / kernel / signal.c
index 402efd27c79e87bfc7ed2c0e8a1cbc284313be7e..54398af2371f9b33d834cfc70b9916fc4337d0d6 100644 (file)
@@ -7,7 +7,6 @@
  * Copyright (C) 1994 - 2000  Ralf Baechle
  * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
  */
-#include <linux/config.h>
 #include <linux/cache.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
 
 #include "signal-common.h"
 
-#define DEBUG_SIG 0
-
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
+#if ICACHE_REFILLS_WORKAROUND_WAR == 0
+
+struct rt_sigframe {
+       u32 rs_ass[4];          /* argument save space for o32 */
+       u32 rs_code[2];         /* signal trampoline */
+       struct siginfo rs_info;
+       struct ucontext rs_uc;
+};
+
+#else
+
+struct rt_sigframe {
+       u32 rs_ass[4];                  /* argument save space for o32 */
+       u32 rs_pad[2];
+       struct siginfo rs_info;
+       struct ucontext rs_uc;
+       u32 rs_code[8] ____cacheline_aligned;   /* signal trampoline */
+};
+
+#endif
+
+/*
+ * Helper routines
+ */
+int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+       int err = 0;
+       int i;
+
+       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]);
+
+       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);
+       }
+
+       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.
+                */
+               preempt_disable();
+
+               if (!is_fpu_owner()) {
+                       own_fpu();
+                       restore_fp(current);
+               }
+               err |= save_fp_context(sc);
+
+               preempt_enable();
+       }
+       return err;
+}
+
+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);
+       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);
+
+       preempt_disable();
+
+       if (used_math()) {
+               /* restore fpu context if we have used it before */
+               own_fpu();
+               err |= restore_fp_context(sc);
+       } else {
+               /* signal handler may have used FPU.  Give it up. */
+               lose_fpu();
+       }
+
+       preempt_enable();
+
+       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));
+}
+
+int install_sigtramp(unsigned int __user *tramp, unsigned int syscall)
+{
+       int err;
+
+       /*
+        * Set up the return code ...
+        *
+        *         li      v0, __NR__foo_sigreturn
+        *         syscall
+        */
+
+       err = __put_user(0x24020000 + syscall, tramp + 0);
+       err |= __put_user(0x0000000c         , tramp + 1);
+       if (ICACHE_REFILLS_WORKAROUND_WAR) {
+               err |= __put_user(0, tramp + 2);
+               err |= __put_user(0, tramp + 3);
+               err |= __put_user(0, tramp + 4);
+               err |= __put_user(0, tramp + 5);
+               err |= __put_user(0, tramp + 6);
+               err |= __put_user(0, tramp + 7);
+       }
+       flush_cache_sigtramp((unsigned long) tramp);
+
+       return err;
+}
+
 /*
  * 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;
@@ -69,9 +224,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;
@@ -90,7 +243,7 @@ _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
        spin_lock_irq(&current->sighand->siglock);
        current->saved_sigmask = current->blocked;
        current->blocked = newset;
-        recalc_sigpending();
+       recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
        current->state = TASK_INTERRUPTIBLE;
@@ -125,7 +278,7 @@ asmlinkage int sys_sigaction(int sig, const struct sigaction __user *act,
 
        if (!ret && oact) {
                if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)))
-                        return -EFAULT;
+                       return -EFAULT;
                err |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
                err |= __put_user(old_ka.sa.sa_handler, &oact->sa_handler);
                err |= __put_user(old_ka.sa.sa_mask.sig[0], oact->sa_mask.sig);
@@ -149,45 +302,8 @@ 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;
@@ -222,9 +338,7 @@ 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;
@@ -276,7 +390,7 @@ 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 |= install_sigtramp(frame->sf_code, __NR_sigreturn);
 
        err |= setup_sigcontext(regs, &frame->sf_sc);
        err |= __copy_to_user(&frame->sf_mask, set, sizeof(*set));
@@ -300,12 +414,10 @@ int setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
        regs->regs[31] = (unsigned long) frame->sf_code;
        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
-        return 0;
+              frame, regs->cp0_epc, regs->regs[31]);
+       return 0;
 
 give_sigsegv:
        force_sigsegv(signr, current);
@@ -323,7 +435,7 @@ 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);
+       err |= install_sigtramp(frame->rs_code, __NR_rt_sigreturn);
 
        /* Create siginfo.  */
        err |= copy_siginfo_to_user(&frame->rs_info, info);
@@ -360,11 +472,10 @@ int setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
        regs->regs[31] = (unsigned long) frame->rs_code;
        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:
@@ -372,7 +483,7 @@ give_sigsegv:
        return -EFAULT;
 }
 
-static inline int handle_signal(unsigned long sig, siginfo_t *info,
+static int handle_signal(unsigned long sig, siginfo_t *info,
        struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
 {
        int ret;
@@ -425,15 +536,11 @@ void do_signal(struct pt_regs *regs)
        if (!user_mode(regs))
                return;
 
-       if (try_to_freeze())
-               goto no_signal;
-
        if (test_thread_flag(TIF_RESTORE_SIGMASK))
                oldset = &current->saved_sigmask;
        else
                oldset = &current->blocked;
 
-
        signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
                /* Whee!  Actually deliver the signal.  */
@@ -447,9 +554,10 @@ void do_signal(struct pt_regs *regs)
                        if (test_thread_flag(TIF_RESTORE_SIGMASK))
                                clear_thread_flag(TIF_RESTORE_SIGMASK);
                }
+
+               return;
        }
 
-no_signal:
        /*
         * Who's code doesn't conform to the restartable syscall convention
         * dies here!!!  The li instruction, a single machine instruction,
@@ -467,6 +575,7 @@ no_signal:
                        regs->regs[7] = regs->regs[26];
                        regs->cp0_epc -= 4;
                }
+               regs->regs[0] = 0;      /* Don't deal with this again.  */
        }
 
        /*