]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/kernel/signal_64.c
powerpc: Explicitly copy elements of pt_regs
[linux-2.6-omap-h63xx.git] / arch / powerpc / kernel / signal_64.c
index 1ce0ae3f6ffc2bdd7fc4536c297b305db7e80178..6e6b01a6db5fff1b70c99c1e16030af33fc116e0 100644 (file)
@@ -34,9 +34,9 @@
 #include <asm/syscalls.h>
 #include <asm/vdso.h>
 
-#define DEBUG_SIG 0
+#include "signal.h"
 
-#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+#define DEBUG_SIG 0
 
 #define GP_REGS_SIZE   min(sizeof(elf_gregset_t), sizeof(struct pt_regs))
 #define FP_REGS_SIZE   sizeof(elf_fpregset_t)
@@ -64,13 +64,10 @@ struct rt_sigframe {
        char abigap[288];
 } __attribute__ ((aligned (16)));
 
-long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, unsigned long r5,
-                    unsigned long r6, unsigned long r7, unsigned long r8,
-                    struct pt_regs *regs)
-{
-       return do_sigaltstack(uss, uoss, regs->gpr[1]);
-}
-
+static const char fmt32[] = KERN_INFO \
+       "%s[%d]: bad frame in %s: %08lx nip %08lx lr %08lx\n";
+static const char fmt64[] = KERN_INFO \
+       "%s[%d]: bad frame in %s: %016lx nip %016lx lr %016lx\n";
 
 /*
  * Set up the sigcontext for the signal frame.
@@ -90,7 +87,12 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
 #ifdef CONFIG_ALTIVEC
        elf_vrreg_t __user *v_regs = (elf_vrreg_t __user *)(((unsigned long)sc->vmx_reserve + 15) & ~0xful);
 #endif
+       unsigned long msr = regs->msr;
        long err = 0;
+#ifdef CONFIG_VSX
+       double buf[FP_REGS_SIZE];
+       int i;
+#endif
 
        flush_fp_to_thread(current);
 
@@ -105,7 +107,7 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
                /* set MSR_VEC in the MSR value in the frame to indicate that sc->v_reg)
                 * contains valid data.
                 */
-               regs->msr |= MSR_VEC;
+               msr |= MSR_VEC;
        }
        /* We always copy to/from vrsave, it's 0 if we don't have or don't
         * use altivec.
@@ -114,10 +116,37 @@ static long setup_sigcontext(struct sigcontext __user *sc, struct pt_regs *regs,
 #else /* CONFIG_ALTIVEC */
        err |= __put_user(0, &sc->v_regs);
 #endif /* CONFIG_ALTIVEC */
+       flush_fp_to_thread(current);
+#ifdef CONFIG_VSX
+       /* Copy FP to local buffer then write that out */
+       for (i = 0; i < 32 ; i++)
+               buf[i] = current->thread.TS_FPR(i);
+       memcpy(&buf[i], &current->thread.fpscr, sizeof(double));
+       err |= __copy_to_user(&sc->fp_regs, buf, FP_REGS_SIZE);
+       /*
+        * Copy VSX low doubleword to local buffer for formatting,
+        * then out to userspace.  Update v_regs to point after the
+        * VMX data.
+        */
+       if (current->thread.used_vsr) {
+               flush_vsx_to_thread(current);
+               v_regs += ELF_NVRREG;
+               for (i = 0; i < 32 ; i++)
+                       buf[i] = current->thread.fpr[i][TS_VSRLOWOFFSET];
+               err |= __copy_to_user(v_regs, buf, 32 * sizeof(double));
+               /* set MSR_VSX in the MSR value in the frame to
+                * indicate that sc->vs_reg) contains valid data.
+                */
+               msr |= MSR_VSX;
+       }
+#else /* CONFIG_VSX */
+       /* copy fpr regs and fpscr */
+       err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE);
+#endif /* CONFIG_VSX */
        err |= __put_user(&sc->gp_regs, &sc->regs);
        WARN_ON(!FULL_REGS(regs));
        err |= __copy_to_user(&sc->gp_regs, regs, GP_REGS_SIZE);
-       err |= __copy_to_user(&sc->fp_regs, &current->thread.fpr, FP_REGS_SIZE);
+       err |= __put_user(msr, &sc->gp_regs[PT_MSR]);
        err |= __put_user(signr, &sc->signal);
        err |= __put_user(handler, &sc->handler);
        if (set != NULL)
@@ -135,32 +164,35 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
 {
 #ifdef CONFIG_ALTIVEC
        elf_vrreg_t __user *v_regs;
+#endif
+#ifdef CONFIG_VSX
+       double buf[FP_REGS_SIZE];
 #endif
        unsigned long err = 0;
        unsigned long save_r13 = 0;
-       elf_greg_t *gregs = (elf_greg_t *)regs;
        unsigned long msr;
-       int i;
 
        /* If this is not a signal return, we preserve the TLS in r13 */
        if (!sig)
                save_r13 = regs->gpr[13];
 
-       /* copy everything before MSR */
-       err |= __copy_from_user(regs, &sc->gp_regs,
-                               PT_MSR*sizeof(unsigned long));
-
+       /* copy the GPRs */
+       err |= __copy_from_user(regs->gpr, sc->gp_regs, sizeof(regs->gpr));
+       err |= __get_user(regs->nip, &sc->gp_regs[PT_NIP]);
        /* get MSR separately, transfer the LE bit if doing signal return */
        err |= __get_user(msr, &sc->gp_regs[PT_MSR]);
        if (sig)
                regs->msr = (regs->msr & ~MSR_LE) | (msr & MSR_LE);
-
+       err |= __get_user(regs->orig_gpr3, &sc->gp_regs[PT_ORIG_R3]);
+       err |= __get_user(regs->ctr, &sc->gp_regs[PT_CTR]);
+       err |= __get_user(regs->link, &sc->gp_regs[PT_LNK]);
+       err |= __get_user(regs->xer, &sc->gp_regs[PT_XER]);
+       err |= __get_user(regs->ccr, &sc->gp_regs[PT_CCR]);
        /* skip SOFTE */
-       for (i = PT_MSR+1; i <= PT_RESULT; i++) {
-               if (i == PT_SOFTE)
-                       continue;
-               err |= __get_user(gregs[i], &sc->gp_regs[i]);
-       }
+       err |= __get_user(regs->trap, &sc->gp_regs[PT_TRAP]);
+       err |= __get_user(regs->dar, &sc->gp_regs[PT_DAR]);
+       err |= __get_user(regs->dsisr, &sc->gp_regs[PT_DSISR]);
+       err |= __get_user(regs->result, &sc->gp_regs[PT_RESULT]);
 
        if (!sig)
                regs->gpr[13] = save_r13;
@@ -176,7 +208,12 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
         */
        discard_lazy_cpu_state();
 
-       err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
+       /*
+        * Force reload of FP/VEC.
+        * This has to be done before copying stuff into current->thread.fpr/vr
+        * for the reasons explained in the previous comment.
+        */
+       regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC | MSR_VSX);
 
 #ifdef CONFIG_ALTIVEC
        err |= __get_user(v_regs, &sc->v_regs);
@@ -196,32 +233,34 @@ static long restore_sigcontext(struct pt_regs *regs, sigset_t *set, int sig,
        else
                current->thread.vrsave = 0;
 #endif /* CONFIG_ALTIVEC */
+#ifdef CONFIG_VSX
+       /* restore floating point */
+       err |= __copy_from_user(buf, &sc->fp_regs, FP_REGS_SIZE);
+       if (err)
+               return err;
+       for (i = 0; i < 32 ; i++)
+               current->thread.TS_FPR(i) = buf[i];
+       memcpy(&current->thread.fpscr, &buf[i], sizeof(double));
 
-       /* Force reload of FP/VEC */
-       regs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1 | MSR_VEC);
+       /*
+        * Get additional VSX data. Update v_regs to point after the
+        * VMX data.  Copy VSX low doubleword from userspace to local
+        * buffer for formatting, then into the taskstruct.
+        */
+       v_regs += ELF_NVRREG;
+       if ((msr & MSR_VSX) != 0)
+               err |= __copy_from_user(buf, v_regs, 32 * sizeof(double));
+       else
+               memset(buf, 0, 32 * sizeof(double));
 
+       for (i = 0; i < 32 ; i++)
+               current->thread.fpr[i][TS_VSRLOWOFFSET] = buf[i];
+#else
+       err |= __copy_from_user(&current->thread.fpr, &sc->fp_regs, FP_REGS_SIZE);
+#endif
        return err;
 }
 
-/*
- * Allocate space for the signal frame
- */
-static inline void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
-                                 size_t frame_size)
-{
-        unsigned long newsp;
-
-        /* Default to using normal stack */
-        newsp = regs->gpr[1];
-
-       if ((ka->sa.sa_flags & SA_ONSTACK) && current->sas_ss_size) {
-               if (! on_sig_stack(regs->gpr[1]))
-                       newsp = (current->sas_ss_sp + current->sas_ss_size);
-       }
-
-        return (void __user *)((newsp - frame_size) & -16ul);
-}
-
 /*
  * Setup the trampoline code on the stack
  */
@@ -248,19 +287,6 @@ static long setup_trampoline(unsigned int syscall, unsigned int __user *tramp)
        return err;
 }
 
-/*
- * Restore the user process's signal mask (also used by signal32.c)
- */
-void restore_sigmask(sigset_t *set)
-{
-       sigdelsetmask(set, ~_BLOCKABLE);
-       spin_lock_irq(&current->sighand->siglock);
-       current->blocked = *set;
-       recalc_sigpending();
-       spin_unlock_irq(&current->sighand->siglock);
-}
-
-
 /*
  * Handle {get,set,swap}_context operations
  */
@@ -351,11 +377,16 @@ badframe:
        printk("badframe in sys_rt_sigreturn, regs=%p uc=%p &uc->uc_mcontext=%p\n",
               regs, uc, &uc->uc_mcontext);
 #endif
+       if (show_unhandled_signals && printk_ratelimit())
+               printk(regs->msr & MSR_SF ? fmt64 : fmt32,
+                       current->comm, current->pid, "rt_sigreturn",
+                       (long)uc, regs->nip, regs->link);
+
        force_sig(SIGSEGV, current);
        return 0;
 }
 
-static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info,
+int handle_rt_signal64(int signr, struct k_sigaction *ka, siginfo_t *info,
                sigset_t *set, struct pt_regs *regs)
 {
        /* Handler is *really* a pointer to the function descriptor for
@@ -369,8 +400,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info,
        long err = 0;
 
        frame = get_sigframe(ka, regs, sizeof(*frame));
-
-       if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+       if (unlikely(frame == NULL))
                goto badframe;
 
        err |= __put_user(&frame->info, &frame->pinfo);
@@ -407,7 +437,7 @@ static int setup_rt_frame(int signr, struct k_sigaction *ka, siginfo_t *info,
        funct_desc_ptr = (func_descr_t __user *) ka->sa.sa_handler;
 
        /* Allocate a dummy caller frame for the signal handler. */
-       newsp = (unsigned long)frame - __SIGNAL_FRAMESIZE;
+       newsp = ((unsigned long)frame) - __SIGNAL_FRAMESIZE;
        err |= put_user(regs->gpr[1], (unsigned long __user *)newsp);
 
        /* Set up "regs" so we "return" to the signal handler. */
@@ -435,137 +465,11 @@ badframe:
        printk("badframe in setup_rt_frame, regs=%p frame=%p newsp=%lx\n",
               regs, frame, newsp);
 #endif
-       force_sigsegv(signr, current);
-       return 0;
-}
-
-
-/*
- * OK, we're invoking a handler
- */
-static int handle_signal(unsigned long sig, struct k_sigaction *ka,
-                        siginfo_t *info, sigset_t *oldset, struct pt_regs *regs)
-{
-       int ret;
-
-       /* Set up Signal Frame */
-       ret = setup_rt_frame(sig, ka, info, oldset, regs);
-
-       if (ret) {
-               spin_lock_irq(&current->sighand->siglock);
-               sigorsets(&current->blocked, &current->blocked, &ka->sa.sa_mask);
-               if (!(ka->sa.sa_flags & SA_NODEFER))
-                       sigaddset(&current->blocked,sig);
-               recalc_sigpending();
-               spin_unlock_irq(&current->sighand->siglock);
-       }
-
-       return ret;
-}
-
-static inline void syscall_restart(struct pt_regs *regs, struct k_sigaction *ka)
-{
-       switch ((int)regs->result) {
-       case -ERESTART_RESTARTBLOCK:
-       case -ERESTARTNOHAND:
-               /* ERESTARTNOHAND means that the syscall should only be
-                * restarted if there was no handler for the signal, and since
-                * we only get here if there is a handler, we dont restart.
-                */
-               regs->result = -EINTR;
-               regs->gpr[3] = EINTR;
-               regs->ccr |= 0x10000000;
-               break;
-       case -ERESTARTSYS:
-               /* ERESTARTSYS means to restart the syscall if there is no
-                * handler or the handler was registered with SA_RESTART
-                */
-               if (!(ka->sa.sa_flags & SA_RESTART)) {
-                       regs->result = -EINTR;
-                       regs->gpr[3] = EINTR;
-                       regs->ccr |= 0x10000000;
-                       break;
-               }
-               /* fallthrough */
-       case -ERESTARTNOINTR:
-               /* ERESTARTNOINTR means that the syscall should be
-                * called again after the signal handler returns.
-                */
-               regs->gpr[3] = regs->orig_gpr3;
-               regs->nip -= 4;
-               regs->result = 0;
-               break;
-       }
-}
-
-/*
- * Note that 'init' is a special process: it doesn't get signals it doesn't
- * want to handle. Thus you cannot kill init even with a SIGKILL even by
- * mistake.
- */
-int do_signal(sigset_t *oldset, struct pt_regs *regs)
-{
-       siginfo_t info;
-       int signr;
-       struct k_sigaction ka;
-
-       /*
-        * If the current thread is 32 bit - invoke the
-        * 32 bit signal handling code
-        */
-       if (test_thread_flag(TIF_32BIT))
-               return do_signal32(oldset, regs);
-
-       if (test_thread_flag(TIF_RESTORE_SIGMASK))
-               oldset = &current->saved_sigmask;
-       else if (!oldset)
-               oldset = &current->blocked;
-
-       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
-       if (signr > 0) {
-               int ret;
-
-               /* Whee!  Actually deliver the signal.  */
-               if (TRAP(regs) == 0x0C00)
-                       syscall_restart(regs, &ka);
-
-               /*
-                * Reenable the DABR before delivering the signal to
-                * user space. The DABR will have been cleared if it
-                * triggered inside the kernel.
-                */
-               if (current->thread.dabr)
-                       set_dabr(current->thread.dabr);
-
-               ret = handle_signal(signr, &ka, &info, oldset, regs);
-
-               /* If a signal was successfully delivered, the saved sigmask is in
-                  its frame, and we can clear the TIF_RESTORE_SIGMASK flag */
-               if (ret && test_thread_flag(TIF_RESTORE_SIGMASK))
-                       clear_thread_flag(TIF_RESTORE_SIGMASK);
-
-               return ret;
-       }
-
-       if (TRAP(regs) == 0x0C00) {     /* System Call! */
-               if ((int)regs->result == -ERESTARTNOHAND ||
-                   (int)regs->result == -ERESTARTSYS ||
-                   (int)regs->result == -ERESTARTNOINTR) {
-                       regs->gpr[3] = regs->orig_gpr3;
-                       regs->nip -= 4; /* Back up & retry system call */
-                       regs->result = 0;
-               } else if ((int)regs->result == -ERESTART_RESTARTBLOCK) {
-                       regs->gpr[0] = __NR_restart_syscall;
-                       regs->nip -= 4;
-                       regs->result = 0;
-               }
-       }
-       /* No signal to deliver -- put the saved sigmask back */
-       if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
-               clear_thread_flag(TIF_RESTORE_SIGMASK);
-               sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
-       }
+       if (show_unhandled_signals && printk_ratelimit())
+               printk(regs->msr & MSR_SF ? fmt64 : fmt32,
+                       current->comm, current->pid, "setup_rt_frame",
+                       (long)frame, regs->nip, regs->link);
 
+       force_sigsegv(signr, current);
        return 0;
 }
-EXPORT_SYMBOL(do_signal);