vserver 1.9.5.x5
[linux-2.6.git] / arch / mips / kernel / signal.c
index 273d10c..508026a 100644 (file)
 #include <linux/wait.h>
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
+#include <linux/compiler.h>
 
 #include <asm/asm.h>
-#include <asm/bitops.h>
+#include <linux/bitops.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 "signal-common.h"
 
 #define DEBUG_SIG 0
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-extern asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs);
-
-extern asmlinkage void do_syscall_trace(void);
+static int do_signal(sigset_t *oldset, struct pt_regs *regs);
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
@@ -42,7 +44,8 @@ extern asmlinkage void do_syscall_trace(void);
 
 #ifdef CONFIG_TRAD_SIGNALS
 save_static_function(sys_sigsuspend);
-static_unused int _sys_sigsuspend(struct pt_regs regs)
+__attribute_used__ noinline static int
+_sys_sigsuspend(nabi_no_regargs struct pt_regs regs)
 {
        sigset_t *uset, saveset, newset;
 
@@ -69,7 +72,8 @@ static_unused int _sys_sigsuspend(struct pt_regs regs)
 #endif
 
 save_static_function(sys_rt_sigsuspend);
-static_unused int _sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
+__attribute_used__ noinline static int
+_sys_rt_sigsuspend(nabi_no_regargs struct pt_regs regs)
 {
        sigset_t *unewset, saveset, newset;
        size_t sigsetsize;
@@ -150,65 +154,32 @@ asmlinkage int sys_sigaltstack(nabi_no_regargs struct pt_regs regs)
        return do_sigaltstack(uss, uoss, usp);
 }
 
-asmlinkage int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
-{
-       int err = 0;
-
-       /* 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);
-
-#define restore_gp_reg(i) do {                                         \
-       err |= __get_user(regs->regs[i], &sc->sc_regs[i]);              \
-} while(0)
-       restore_gp_reg( 1); restore_gp_reg( 2); restore_gp_reg( 3);
-       restore_gp_reg( 4); restore_gp_reg( 5); restore_gp_reg( 6);
-       restore_gp_reg( 7); restore_gp_reg( 8); restore_gp_reg( 9);
-       restore_gp_reg(10); restore_gp_reg(11); restore_gp_reg(12);
-       restore_gp_reg(13); restore_gp_reg(14); restore_gp_reg(15);
-       restore_gp_reg(16); restore_gp_reg(17); restore_gp_reg(18);
-       restore_gp_reg(19); restore_gp_reg(20); restore_gp_reg(21);
-       restore_gp_reg(22); restore_gp_reg(23); restore_gp_reg(24);
-       restore_gp_reg(25); restore_gp_reg(26); restore_gp_reg(27);
-       restore_gp_reg(28); restore_gp_reg(29); restore_gp_reg(30);
-       restore_gp_reg(31);
-#undef restore_gp_reg
-
-       err |= __get_user(current->used_math, &sc->sc_used_math);
-
-       if (current->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();
-       }
-
-       return err;
-}
+#if PLAT_TRAMPOLINE_STUFF_LINE
+#define __tramp __attribute__((aligned(PLAT_TRAMPOLINE_STUFF_LINE)))
+#else
+#define __tramp
+#endif
 
 #ifdef CONFIG_TRAD_SIGNALS
 struct sigframe {
        u32 sf_ass[4];                  /* argument save space for o32 */
-       u32 sf_code[2];                 /* signal trampoline */
-       struct sigcontext sf_sc;
+       u32 sf_code[2] __tramp;         /* signal trampoline */
+       struct sigcontext sf_sc __tramp;
        sigset_t sf_mask;
 };
 #endif
 
 struct rt_sigframe {
        u32 rs_ass[4];                  /* argument save space for o32 */
-       u32 rs_code[2];                 /* signal trampoline */
-       struct siginfo rs_info;
+       u32 rs_code[2] __tramp;         /* signal trampoline */
+       struct siginfo rs_info __tramp;
        struct ucontext rs_uc;
 };
 
 #ifdef CONFIG_TRAD_SIGNALS
-asmlinkage void sys_sigreturn(struct pt_regs regs)
+save_static_function(sys_sigreturn);
+__attribute_used__ noinline static void
+_sys_sigreturn(nabi_no_regargs struct pt_regs regs)
 {
        struct sigframe *frame;
        sigset_t blocked;
@@ -232,7 +203,7 @@ asmlinkage void sys_sigreturn(struct pt_regs regs)
         * Don't let your children do this ...
         */
        if (current_thread_info()->flags & TIF_SYSCALL_TRACE)
-               do_syscall_trace();
+               do_syscall_trace(&regs, 1);
        __asm__ __volatile__(
                "move\t$29, %0\n\t"
                "j\tsyscall_exit"
@@ -245,7 +216,9 @@ badframe:
 }
 #endif
 
-asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
+save_static_function(sys_rt_sigreturn);
+__attribute_used__ noinline static void
+_sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
 {
        struct rt_sigframe *frame;
        sigset_t set;
@@ -286,76 +259,6 @@ badframe:
        force_sig(SIGSEGV, current);
 }
 
-inline int setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
-{
-       int err = 0;
-
-       err |= __put_user(regs->cp0_epc, &sc->sc_pc);
-       err |= __put_user(regs->cp0_status, &sc->sc_status);
-
-#define save_gp_reg(i) do {                                            \
-       err |= __put_user(regs->regs[i], &sc->sc_regs[i]);              \
-} while(0)
-       __put_user(0, &sc->sc_regs[0]); save_gp_reg(1); save_gp_reg(2);
-       save_gp_reg(3); save_gp_reg(4); save_gp_reg(5); save_gp_reg(6);
-       save_gp_reg(7); save_gp_reg(8); save_gp_reg(9); save_gp_reg(10);
-       save_gp_reg(11); save_gp_reg(12); save_gp_reg(13); save_gp_reg(14);
-       save_gp_reg(15); save_gp_reg(16); save_gp_reg(17); save_gp_reg(18);
-       save_gp_reg(19); save_gp_reg(20); save_gp_reg(21); save_gp_reg(22);
-       save_gp_reg(23); save_gp_reg(24); save_gp_reg(25); save_gp_reg(26);
-       save_gp_reg(27); save_gp_reg(28); save_gp_reg(29); save_gp_reg(30);
-       save_gp_reg(31);
-#undef save_gp_reg
-
-       err |= __put_user(regs->hi, &sc->sc_mdhi);
-       err |= __put_user(regs->lo, &sc->sc_mdlo);
-       err |= __put_user(regs->cp0_cause, &sc->sc_cause);
-       err |= __put_user(regs->cp0_badvaddr, &sc->sc_badvaddr);
-
-       err |= __put_user(current->used_math, &sc->sc_used_math);
-
-       if (!current->used_math)
-               goto out;
-
-       /*
-        * Save FPU state to signal context.  Signal handler will "inherit"
-        * current FPU state.
-        */
-       if (!is_fpu_owner()) {
-               own_fpu();
-               restore_fp(current);
-       }
-       err |= save_fp_context(sc);
-
-out:
-       return err;
-}
-
-/*
- * Determine which stack to use..
- */
-static inline void *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 *)((sp - frame_size) & ALMASK);
-}
-
 #ifdef CONFIG_TRAD_SIGNALS
 static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
        int signr, sigset_t *set)
@@ -373,6 +276,8 @@ static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
         *         li      v0, __NR_sigreturn
         *         syscall
         */
+       if (PLAT_TRAMPOLINE_STUFF_LINE)
+               __clear_user(frame->sf_code, PLAT_TRAMPOLINE_STUFF_LINE);
        err |= __put_user(0x24020000 + __NR_sigreturn, frame->sf_code + 0);
        err |= __put_user(0x0000000c                 , frame->sf_code + 1);
        flush_cache_sigtramp((unsigned long) frame->sf_code);
@@ -407,9 +312,7 @@ static void inline setup_frame(struct k_sigaction * ka, struct pt_regs *regs,
         return;
 
 give_sigsegv:
-       if (signr == SIGSEGV)
-               ka->sa.sa_handler = SIG_DFL;
-       force_sig(SIGSEGV, current);
+       force_sigsegv(signr, current);
 }
 #endif
 
@@ -429,6 +332,8 @@ static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
         *         li      v0, __NR_rt_sigreturn
         *         syscall
         */
+       if (PLAT_TRAMPOLINE_STUFF_LINE)
+               __clear_user(frame->rs_code, PLAT_TRAMPOLINE_STUFF_LINE);
        err |= __put_user(0x24020000 + __NR_rt_sigreturn, frame->rs_code + 0);
        err |= __put_user(0x0000000c                    , frame->rs_code + 1);
        flush_cache_sigtramp((unsigned long) frame->rs_code);
@@ -476,19 +381,15 @@ static void inline setup_rt_frame(struct k_sigaction * ka, struct pt_regs *regs,
        return;
 
 give_sigsegv:
-       if (signr == SIGSEGV)
-               ka->sa.sa_handler = SIG_DFL;
-       force_sig(SIGSEGV, current);
+       force_sigsegv(signr, current);
 }
 
 extern void setup_rt_frame_n32(struct k_sigaction * ka,
        struct pt_regs *regs, int signr, sigset_t *set, siginfo_t *info);
 
 static inline void handle_signal(unsigned long sig, siginfo_t *info,
-       sigset_t *oldset, struct pt_regs *regs)
+       struct k_sigaction *ka, sigset_t *oldset, struct pt_regs *regs)
 {
-       struct k_sigaction *ka = &current->sighand->action[sig-1];
-
        switch(regs->regs[0]) {
        case ERESTART_RESTARTBLOCK:
        case ERESTARTNOHAND:
@@ -524,8 +425,6 @@ static inline void handle_signal(unsigned long sig, siginfo_t *info,
                setup_frame(ka, regs, sig, oldset);
 #endif
 
-       if (ka->sa.sa_flags & SA_ONESHOT)
-               ka->sa.sa_handler = SIG_DFL;
        if (!(ka->sa.sa_flags & SA_NODEFER)) {
                spin_lock_irq(&current->sighand->siglock);
                sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
@@ -538,8 +437,9 @@ static inline void handle_signal(unsigned long sig, siginfo_t *info,
 extern int do_signal32(sigset_t *oldset, struct pt_regs *regs);
 extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs);
 
-asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
+static int do_signal(sigset_t *oldset, struct pt_regs *regs)
 {
+       struct k_sigaction ka;
        siginfo_t info;
        int signr;
 
@@ -549,15 +449,27 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
        }
 #endif
 
+       /*
+        * We want the common case to go fast, which is why we may in certain
+        * cases get here from kernel mode. Just return without doing anything
+        * if so.
+        */
+       if (!user_mode(regs))
+               return 1;
+
+       if (try_to_freeze(0))
+               goto no_signal;
+
        if (!oldset)
                oldset = &current->blocked;
 
-       signr = get_signal_to_deliver(&info, regs, NULL);
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
        if (signr > 0) {
-               handle_signal(signr, &info, oldset, regs);
+               handle_signal(signr, &info, &ka, oldset, regs);
                return 1;
        }
 
+no_signal:
        /*
         * Who's code doesn't conform to the restartable syscall convention
         * dies here!!!  The li instruction, a single machine instruction,
@@ -579,8 +491,6 @@ asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs)
        return 0;
 }
 
-extern int do_irix_signal(sigset_t *oldset, struct pt_regs *regs);
-
 /*
  * notification of userspace execution resumption
  * - triggered by current->work.notify_resume