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 / arm / kernel / signal.c
index c0f84aa..a0cd0a9 100644 (file)
@@ -12,7 +12,6 @@
 #include <linux/signal.h>
 #include <linux/ptrace.h>
 #include <linux/personality.h>
-#include <linux/suspend.h>
 
 #include <asm/cacheflush.h>
 #include <asm/ucontext.h>
@@ -20,6 +19,7 @@
 #include <asm/unistd.h>
 
 #include "ptrace.h"
+#include "signal.h"
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
 #define SWI_SYS_SIGRETURN      (0xef000000|(__NR_sigreturn))
 #define SWI_SYS_RT_SIGRETURN   (0xef000000|(__NR_rt_sigreturn))
 
+/*
+ * With EABI, the syscall number has to be loaded into r7.
+ */
+#define MOV_R7_NR_SIGRETURN    (0xe3a07000 | (__NR_sigreturn - __NR_SYSCALL_BASE))
+#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
+
 /*
  * For Thumb syscalls, we pass the syscall number via r7.  We therefore
  * need two 16-bit instructions.
@@ -36,9 +42,9 @@
 #define SWI_THUMB_SIGRETURN    (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE))
 #define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
 
-static const unsigned long retcodes[4] = {
-       SWI_SYS_SIGRETURN,      SWI_THUMB_SIGRETURN,
-       SWI_SYS_RT_SIGRETURN,   SWI_THUMB_RT_SIGRETURN
+const unsigned long sigreturn_codes[7] = {
+       MOV_R7_NR_SIGRETURN,    SWI_SYS_SIGRETURN,    SWI_THUMB_SIGRETURN,
+       MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
 };
 
 static int do_signal(sigset_t *oldset, struct pt_regs * regs, int syscall);
@@ -103,7 +109,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
 
        if (act) {
                old_sigset_t mask;
-               if (verify_area(VERIFY_READ, act, sizeof(*act)) ||
+               if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
                    __get_user(new_ka.sa.sa_handler, &act->sa_handler) ||
                    __get_user(new_ka.sa.sa_restorer, &act->sa_restorer))
                        return -EFAULT;
@@ -115,7 +121,7 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
        ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
 
        if (!ret && oact) {
-               if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) ||
+               if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
                    __put_user(old_ka.sa.sa_handler, &oact->sa_handler) ||
                    __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer))
                        return -EFAULT;
@@ -126,27 +132,85 @@ sys_sigaction(int sig, const struct old_sigaction __user *act,
        return ret;
 }
 
+#ifdef CONFIG_IWMMXT
+
+/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */
+#define IWMMXT_STORAGE_SIZE    (0x98 + 8)
+#define IWMMXT_MAGIC0          0x12ef842a
+#define IWMMXT_MAGIC1          0x1c07ca71
+
+struct iwmmxt_sigframe {
+       unsigned long   magic0;
+       unsigned long   magic1;
+       unsigned long   storage[0x98/4];
+};
+
+static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
+{
+       char kbuf[sizeof(*frame) + 8];
+       struct iwmmxt_sigframe *kframe;
+
+       /* the iWMMXt context must be 64 bit aligned */
+       kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+       kframe->magic0 = IWMMXT_MAGIC0;
+       kframe->magic1 = IWMMXT_MAGIC1;
+       iwmmxt_task_copy(current_thread_info(), &kframe->storage);
+       return __copy_to_user(frame, kframe, sizeof(*frame));
+}
+
+static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
+{
+       char kbuf[sizeof(*frame) + 8];
+       struct iwmmxt_sigframe *kframe;
+
+       /* the iWMMXt context must be 64 bit aligned */
+       kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
+       if (__copy_from_user(kframe, frame, sizeof(*frame)))
+               return -1;
+       if (kframe->magic0 != IWMMXT_MAGIC0 ||
+           kframe->magic1 != IWMMXT_MAGIC1)
+               return -1;
+       iwmmxt_task_restore(current_thread_info(), &kframe->storage);
+       return 0;
+}
+
+#endif
+
 /*
- * Do a signal return; undo the signal stack.
+ * Auxiliary signal frame.  This saves stuff like FP state.
+ * The layout of this structure is not part of the user ABI.
  */
-struct sigframe
-{
+struct aux_sigframe {
+#ifdef CONFIG_IWMMXT
+       struct iwmmxt_sigframe  iwmmxt;
+#endif
+#ifdef CONFIG_VFP
+       union vfp_state         vfp;
+#endif
+};
+
+/*
+ * Do a signal return; undo the signal stack.  These are aligned to 64-bit.
+ */
+struct sigframe {
        struct sigcontext sc;
        unsigned long extramask[_NSIG_WORDS-1];
-       unsigned long retcode;
+       unsigned long retcode[2];
+       struct aux_sigframe aux __attribute__((aligned(8)));
 };
 
-struct rt_sigframe
-{
+struct rt_sigframe {
        struct siginfo __user *pinfo;
        void __user *puc;
        struct siginfo info;
        struct ucontext uc;
-       unsigned long retcode;
+       unsigned long retcode[2];
+       struct aux_sigframe aux __attribute__((aligned(8)));
 };
 
 static int
-restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
+                  struct aux_sigframe __user *aux)
 {
        int err = 0;
 
@@ -170,6 +234,15 @@ restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 
        err |= !valid_user_regs(regs);
 
+#ifdef CONFIG_IWMMXT
+       if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
+               err |= restore_iwmmxt_context(&aux->iwmmxt);
+#endif
+#ifdef CONFIG_VFP
+//     if (err == 0)
+//             err |= vfp_restore_state(&aux->vfp);
+#endif
+
        return err;
 }
 
@@ -191,7 +264,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
 
        frame = (struct sigframe __user *)regs->ARM_sp;
 
-       if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
+       if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
                goto badframe;
        if (__get_user(set.sig[0], &frame->sc.oldmask)
            || (_NSIG_WORDS > 1
@@ -205,7 +278,7 @@ asmlinkage int sys_sigreturn(struct pt_regs *regs)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(regs, &frame->sc))
+       if (restore_sigcontext(regs, &frame->sc, &frame->aux))
                goto badframe;
 
        /* Send SIGTRAP if we're single-stepping */
@@ -239,7 +312,7 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
 
        frame = (struct rt_sigframe __user *)regs->ARM_sp;
 
-       if (verify_area(VERIFY_READ, frame, sizeof (*frame)))
+       if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
                goto badframe;
        if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
                goto badframe;
@@ -250,7 +323,10 @@ asmlinkage int sys_rt_sigreturn(struct pt_regs *regs)
        recalc_sigpending();
        spin_unlock_irq(&current->sighand->siglock);
 
-       if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &frame->aux))
+               goto badframe;
+
+       if (do_sigaltstack(&frame->uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT)
                goto badframe;
 
        /* Send SIGTRAP if we're single-stepping */
@@ -267,7 +343,7 @@ badframe:
 }
 
 static int
-setup_sigcontext(struct sigcontext __user *sc, /*struct _fpstate *fpstate,*/
+setup_sigcontext(struct sigcontext __user *sc, struct aux_sigframe __user *aux,
                 struct pt_regs *regs, unsigned long mask)
 {
        int err = 0;
@@ -295,6 +371,15 @@ setup_sigcontext(struct sigcontext __user *sc, /*struct _fpstate *fpstate,*/
        __put_user_error(current->thread.address, &sc->fault_address, err);
        __put_user_error(mask, &sc->oldmask, err);
 
+#ifdef CONFIG_IWMMXT
+       if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
+               err |= preserve_iwmmxt_context(&aux->iwmmxt);
+#endif
+#ifdef CONFIG_VFP
+//     if (err == 0)
+//             err |= vfp_save_state(&aux->vfp);
+#endif
+
        return err;
 }
 
@@ -302,6 +387,7 @@ static inline void __user *
 get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
 {
        unsigned long sp = regs->ARM_sp;
+       void __user *frame;
 
        /*
         * This is the X/Open sanctioned signal stack switching.
@@ -312,7 +398,15 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
        /*
         * ATPCS B01 mandates 8-byte alignment
         */
-       return (void __user *)((sp - framesize) & ~7);
+       frame = (void __user *)((sp - framesize) & ~7);
+
+       /*
+        * Check that we can actually write to the signal frame.
+        */
+       if (!access_ok(VERIFY_WRITE, frame, framesize))
+               frame = NULL;
+
+       return frame;
 }
 
 static int
@@ -348,22 +442,31 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka,
        if (ka->sa.sa_flags & SA_RESTORER) {
                retcode = (unsigned long)ka->sa.sa_restorer;
        } else {
-               unsigned int idx = thumb;
+               unsigned int idx = thumb << 1;
 
                if (ka->sa.sa_flags & SA_SIGINFO)
-                       idx += 2;
+                       idx += 3;
 
-               if (__put_user(retcodes[idx], rc))
+               if (__put_user(sigreturn_codes[idx],   rc) ||
+                   __put_user(sigreturn_codes[idx+1], rc+1))
                        return 1;
 
-               /*
-                * Ensure that the instruction cache sees
-                * the return code written onto the stack.
-                */
-               flush_icache_range((unsigned long)rc,
-                                  (unsigned long)(rc + 1));
-
-               retcode = ((unsigned long)rc) + thumb;
+               if (cpsr & MODE32_BIT) {
+                       /*
+                        * 32-bit code can use the new high-page
+                        * signal return code support.
+                        */
+                       retcode = KERN_SIGRETURN_CODE + (idx << 2) + thumb;
+               } else {
+                       /*
+                        * Ensure that the instruction cache sees
+                        * the return code written onto the stack.
+                        */
+                       flush_icache_range((unsigned long)rc,
+                                          (unsigned long)(rc + 2));
+
+                       retcode = ((unsigned long)rc) + thumb;
+               }
        }
 
        regs->ARM_r0 = usig;
@@ -381,10 +484,10 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg
        struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
        int err = 0;
 
-       if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+       if (!frame)
                return 1;
 
-       err |= setup_sigcontext(&frame->sc, /*&frame->fpstate,*/ regs, set->sig[0]);
+       err |= setup_sigcontext(&frame->sc, &frame->aux, regs, set->sig[0]);
 
        if (_NSIG_WORDS > 1) {
                err |= __copy_to_user(frame->extramask, &set->sig[1],
@@ -392,7 +495,7 @@ setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *reg
        }
 
        if (err == 0)
-               err = setup_return(regs, ka, &frame->retcode, frame, usig);
+               err = setup_return(regs, ka, frame->retcode, frame, usig);
 
        return err;
 }
@@ -402,24 +505,31 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
               sigset_t *set, struct pt_regs *regs)
 {
        struct rt_sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
+       stack_t stack;
        int err = 0;
 
-       if (!access_ok(VERIFY_WRITE, frame, sizeof (*frame)))
+       if (!frame)
                return 1;
 
        __put_user_error(&frame->info, &frame->pinfo, err);
        __put_user_error(&frame->uc, &frame->puc, err);
        err |= copy_siginfo_to_user(&frame->info, info);
 
-       /* Clear all the bits of the ucontext we don't use.  */
-       err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
+       __put_user_error(0, &frame->uc.uc_flags, err);
+       __put_user_error(NULL, &frame->uc.uc_link, err);
+
+       memset(&stack, 0, sizeof(stack));
+       stack.ss_sp = (void __user *)current->sas_ss_sp;
+       stack.ss_flags = sas_ss_flags(regs->ARM_sp);
+       stack.ss_size = current->sas_ss_size;
+       err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack));
 
-       err |= setup_sigcontext(&frame->uc.uc_mcontext, /*&frame->fpstate,*/
+       err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->aux,
                                regs, set->sig[0]);
        err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
 
        if (err == 0)
-               err = setup_return(regs, ka, &frame->retcode, frame, usig);
+               err = setup_return(regs, ka, frame->retcode, frame, usig);
 
        if (err == 0) {
                /*
@@ -427,8 +537,8 @@ setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
                 * arguments for the signal handler.
                 *   -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
                 */
-               regs->ARM_r1 = (unsigned long)frame->pinfo;
-               regs->ARM_r2 = (unsigned long)frame->puc;
+               regs->ARM_r1 = (unsigned long)&frame->info;
+               regs->ARM_r2 = (unsigned long)&frame->uc;
        }
 
        return err;
@@ -444,12 +554,12 @@ static inline void restart_syscall(struct pt_regs *regs)
  * OK, we're invoking a handler
  */    
 static void
-handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
+handle_signal(unsigned long sig, struct k_sigaction *ka,
+             siginfo_t *info, sigset_t *oldset,
              struct pt_regs * regs, int syscall)
 {
        struct thread_info *thread = current_thread_info();
        struct task_struct *tsk = current;
-       struct k_sigaction *ka = &tsk->sighand->action[sig-1];
        int usig = sig;
        int ret;
 
@@ -492,27 +602,22 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
         */
        ret |= !valid_user_regs(regs);
 
+       if (ret != 0) {
+               force_sigsegv(sig, tsk);
+               return;
+       }
+
        /*
-        * Block the signal if we were unsuccessful.
+        * Block the signal if we were successful.
         */
-       if (ret != 0 || !(ka->sa.sa_flags & SA_NODEFER)) {
-               spin_lock_irq(&tsk->sighand->siglock);
-               sigorsets(&tsk->blocked, &tsk->blocked,
-                         &ka->sa.sa_mask);
+       spin_lock_irq(&tsk->sighand->siglock);
+       sigorsets(&tsk->blocked, &tsk->blocked,
+                 &ka->sa.sa_mask);
+       if (!(ka->sa.sa_flags & SA_NODEFER))
                sigaddset(&tsk->blocked, sig);
-               recalc_sigpending();
-               spin_unlock_irq(&tsk->sighand->siglock);
-       }
-
-       if (ret == 0) {
-               if (ka->sa.sa_flags & SA_ONESHOT)
-                       ka->sa.sa_handler = SIG_DFL;
-               return;
-       }
+       recalc_sigpending();
+       spin_unlock_irq(&tsk->sighand->siglock);
 
-       if (sig == SIGSEGV)
-               ka->sa.sa_handler = SIG_DFL;
-       force_sig(SIGSEGV, tsk);
 }
 
 /*
@@ -526,6 +631,7 @@ handle_signal(unsigned long sig, siginfo_t *info, sigset_t *oldset,
  */
 static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
 {
+       struct k_sigaction ka;
        siginfo_t info;
        int signr;
 
@@ -538,17 +644,15 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
        if (!user_mode(regs))
                return 0;
 
-       if (current->flags & PF_FREEZE) {
-               refrigerator(0);
+       if (try_to_freeze())
                goto no_signal;
-       }
 
        if (current->ptrace & PT_SINGLESTEP)
                ptrace_cancel_bpt(current);
 
-       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, syscall);
+               handle_signal(signr, &ka, &info, oldset, regs, syscall);
                if (current->ptrace & PT_SINGLESTEP)
                        ptrace_set_bpt(current);
                return 1;
@@ -564,10 +668,10 @@ static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
                                regs->ARM_r7 = __NR_restart_syscall;
                                regs->ARM_pc -= 2;
                        } else {
-                               u32 *usp;
+                               u32 __user *usp;
 
                                regs->ARM_sp -= 12;
-                               usp = (u32 *)regs->ARM_sp;
+                               usp = (u32 __user *)regs->ARM_sp;
 
                                put_user(regs->ARM_pc, &usp[0]);
                                /* swi __NR_restart_syscall */