rk: revert to v3.10
[firefly-linux-kernel-4.4.55.git] / arch / openrisc / kernel / signal.c
index c277ec82783d6fadef157be24c91ff734d76ec00..ae167f7e081aa0368cb571093df8f16a9dcf71d9 100644 (file)
 #include <linux/tracehook.h>
 
 #include <asm/processor.h>
-#include <asm/syscall.h>
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 
 #define DEBUG_SIG 0
 
 struct rt_sigframe {
+       struct siginfo *pinfo;
+       void *puc;
        struct siginfo info;
        struct ucontext uc;
        unsigned char retcode[16];      /* trampoline code */
 };
 
-static int restore_sigcontext(struct pt_regs *regs,
-                             struct sigcontext __user *sc)
+static int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc)
 {
-       int err = 0;
+       unsigned int err = 0;
 
-       /* Always make any pending restarted system calls return -EINTR */
+       /* Alwys make any pending restarted system call return -EINTR */
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
 
        /*
@@ -53,21 +53,25 @@ static int restore_sigcontext(struct pt_regs *regs,
         * (sc is already checked for VERIFY_READ since the sigframe was
         *  checked in sys_sigreturn previously)
         */
-       err |= __copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long));
-       err |= __copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long));
-       err |= __copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long));
+       if (__copy_from_user(regs, sc->regs.gpr, 32 * sizeof(unsigned long)))
+               goto badframe;
+       if (__copy_from_user(&regs->pc, &sc->regs.pc, sizeof(unsigned long)))
+               goto badframe;
+       if (__copy_from_user(&regs->sr, &sc->regs.sr, sizeof(unsigned long)))
+               goto badframe;
 
        /* make sure the SM-bit is cleared so user-mode cannot fool us */
        regs->sr &= ~SPR_SR_SM;
 
-       regs->orig_gpr11 = -1;  /* Avoid syscall restart checks */
-
        /* TODO: the other ports use regs->orig_XX to disable syscall checks
         * after this completes, but we don't use that mechanism. maybe we can
         * use it now ?
         */
 
        return err;
+
+badframe:
+       return 1;
 }
 
 asmlinkage long _sys_rt_sigreturn(struct pt_regs *regs)
@@ -107,18 +111,21 @@ badframe:
  * Set up a signal frame.
  */
 
-static int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+static int setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs,
+                           unsigned long mask)
 {
        int err = 0;
 
        /* copy the regs */
-       /* There should be no need to save callee-saved registers here...
-        * ...but we save them anyway.  Revisit this
-        */
+
        err |= __copy_to_user(sc->regs.gpr, regs, 32 * sizeof(unsigned long));
        err |= __copy_to_user(&sc->regs.pc, &regs->pc, sizeof(unsigned long));
        err |= __copy_to_user(&sc->regs.sr, &regs->sr, sizeof(unsigned long));
 
+       /* then some other stuff */
+
+       err |= __put_user(mask, &sc->oldmask);
+
        return err;
 }
 
@@ -174,18 +181,24 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
        int err = 0;
 
        frame = get_sigframe(ka, regs, sizeof(*frame));
+
        if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
                goto give_sigsegv;
 
-       /* Create siginfo.  */
+       err |= __put_user(&frame->info, &frame->pinfo);
+       err |= __put_user(&frame->uc, &frame->puc);
+
        if (ka->sa.sa_flags & SA_SIGINFO)
                err |= copy_siginfo_to_user(&frame->info, info);
+       if (err)
+               goto give_sigsegv;
 
-       /* Create the ucontext.  */
+       /* Clear all the bits of the ucontext we don't use.  */
+       err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext));
        err |= __put_user(0, &frame->uc.uc_flags);
        err |= __put_user(NULL, &frame->uc.uc_link);
        err |= __save_altstack(&frame->uc.uc_stack, regs->sp);
-       err |= setup_sigcontext(regs, &frame->uc.uc_mcontext);
+       err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]);
 
        err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
 
@@ -194,12 +207,9 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
 
        /* trampoline - the desired return ip is the retcode itself */
        return_ip = (unsigned long)&frame->retcode;
-       /* This is:
-               l.ori r11,r0,__NR_sigreturn
-               l.sys 1
-        */
-       err |= __put_user(0xa960,             (short *)(frame->retcode + 0));
-       err |= __put_user(__NR_rt_sigreturn,  (short *)(frame->retcode + 2));
+       /* This is l.ori r11,r0,__NR_sigreturn, l.sys 1 */
+       err |= __put_user(0xa960, (short *)(frame->retcode + 0));
+       err |= __put_user(__NR_rt_sigreturn, (short *)(frame->retcode + 2));
        err |= __put_user(0x20000001, (unsigned long *)(frame->retcode + 4));
        err |= __put_user(0x15000000, (unsigned long *)(frame->retcode + 8));
 
@@ -252,106 +262,82 @@ handle_signal(unsigned long sig,
  * mode below.
  */
 
-int do_signal(struct pt_regs *regs, int syscall)
+void do_signal(struct pt_regs *regs)
 {
        siginfo_t info;
        int signr;
        struct k_sigaction ka;
-       unsigned long continue_addr = 0;
-       unsigned long restart_addr = 0;
-       unsigned long retval = 0;
-       int restart = 0;
-
-       if (syscall) {
-               continue_addr = regs->pc;
-               restart_addr = continue_addr - 4;
-               retval = regs->gpr[11];
-
-               /*
-                * Setup syscall restart here so that a debugger will
-                * see the already changed PC.
-                */
-               switch (retval) {
+
+       /*
+        * 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;
+
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+
+       /* If we are coming out of a syscall then we need
+        * to check if the syscall was interrupted and wants to be
+        * restarted after handling the signal.  If so, the original
+        * syscall number is put back into r11 and the PC rewound to
+        * point at the l.sys instruction that resulted in the
+        * original syscall.  Syscall results other than the four
+        * below mean that the syscall executed to completion and no
+        * restart is necessary.
+        */
+       if (regs->orig_gpr11) {
+               int restart = 0;
+
+               switch (regs->gpr[11]) {
                case -ERESTART_RESTARTBLOCK:
-                       restart = -2;
-                       /* Fall through */
                case -ERESTARTNOHAND:
+                       /* Restart if there is no signal handler */
+                       restart = (signr <= 0);
+                       break;
                case -ERESTARTSYS:
+                       /* Restart if there no signal handler or
+                        * SA_RESTART flag is set */
+                       restart = (signr <= 0 || (ka.sa.sa_flags & SA_RESTART));
+                       break;
                case -ERESTARTNOINTR:
-                       restart++;
-                       regs->gpr[11] = regs->orig_gpr11;
-                       regs->pc = restart_addr;
+                       /* Always restart */
+                       restart = 1;
                        break;
                }
-       }
 
-       /*
-        * Get the signal to deliver.  When running under ptrace, at this
-        * point the debugger may change all our registers ...
-        */
-       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
-       /*
-        * Depending on the signal settings we may need to revert the
-        * decision to restart the system call.  But skip this if a
-        * debugger has chosen to restart at a different PC.
-        */
-       if (signr > 0) {
-               if (unlikely(restart) && regs->pc == restart_addr) {
-                       if (retval == -ERESTARTNOHAND ||
-                           retval == -ERESTART_RESTARTBLOCK
-                           || (retval == -ERESTARTSYS
-                               && !(ka.sa.sa_flags & SA_RESTART))) {
-                               /* No automatic restart */
-                               regs->gpr[11] = -EINTR;
-                               regs->pc = continue_addr;
-                       }
+               if (restart) {
+                       if (regs->gpr[11] == -ERESTART_RESTARTBLOCK)
+                               regs->gpr[11] = __NR_restart_syscall;
+                       else
+                               regs->gpr[11] = regs->orig_gpr11;
+                       regs->pc -= 4;
+               } else {
+                       regs->gpr[11] = -EINTR;
                }
+       }
 
-               handle_signal(signr, &info, &ka, regs);
-       } else {
-               /* no handler */
+       if (signr <= 0) {
+               /* no signal to deliver so we just put the saved sigmask
+                * back */
                restore_saved_sigmask();
-               /*
-                * Restore pt_regs PC as syscall restart will be handled by
-                * kernel without return to userspace
-                */
-               if (unlikely(restart) && regs->pc == restart_addr) {
-                       regs->pc = continue_addr;
-                       return restart;
-               }
+       } else {                /* signr > 0 */
+               /* Whee!  Actually deliver the signal.  */
+               handle_signal(signr, &info, &ka, regs);
        }
 
-       return 0;
+       return;
 }
 
-asmlinkage int
-do_work_pending(struct pt_regs *regs, unsigned int thread_flags, int syscall)
+asmlinkage void do_notify_resume(struct pt_regs *regs)
 {
-       do {
-               if (likely(thread_flags & _TIF_NEED_RESCHED)) {
-                       schedule();
-               } else {
-                       if (unlikely(!user_mode(regs)))
-                               return 0;
-                       local_irq_enable();
-                       if (thread_flags & _TIF_SIGPENDING) {
-                               int restart = do_signal(regs, syscall);
-                               if (unlikely(restart)) {
-                                       /*
-                                        * Restart without handlers.
-                                        * Deal with it without leaving
-                                        * the kernel space.
-                                        */
-                                       return restart;
-                               }
-                               syscall = 0;
-                       } else {
-                               clear_thread_flag(TIF_NOTIFY_RESUME);
-                               tracehook_notify_resume(regs);
-                       }
-               }
-               local_irq_disable();
-               thread_flags = current_thread_info()->flags;
-       } while (thread_flags & _TIF_WORK_MASK);
-       return 0;
+       if (current_thread_info()->flags & _TIF_SIGPENDING)
+               do_signal(regs);
+
+       if (current_thread_info()->flags & _TIF_NOTIFY_RESUME) {
+               clear_thread_flag(TIF_NOTIFY_RESUME);
+               tracehook_notify_resume(regs);
+       }
 }