MIPS: Add uprobes support.
[linux-drm-fsl-dcu.git] / arch / mips / kernel / signal.c
index 6a28c792d862c03b09a8f9cfdcd40d2879c401b0..2fec67bfc457cc969056f6f46258c0f00992be83 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/wait.h>
 #include <linux/ptrace.h>
 #include <linux/unistd.h>
+#include <linux/uprobes.h>
 #include <linux/compiler.h>
 #include <linux/syscalls.h>
 #include <linux/uaccess.h>
 #include <asm/vdso.h>
 #include <asm/dsp.h>
 #include <asm/inst.h>
+#include <asm/msa.h>
 
 #include "signal-common.h"
 
-static int (*save_fp_context)(struct sigcontext __user *sc);
-static int (*restore_fp_context)(struct sigcontext __user *sc);
-
-extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
-extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
+static int (*save_fp_context)(void __user *sc);
+static int (*restore_fp_context)(void __user *sc);
 
 struct sigframe {
        u32 sf_ass[4];          /* argument save space for o32 */
        u32 sf_pad[2];          /* Was: signal trampoline */
+
+       /* Matches struct ucontext from its uc_mcontext field onwards */
        struct sigcontext sf_sc;
        sigset_t sf_mask;
+       unsigned long long sf_extcontext[0];
 };
 
 struct rt_sigframe {
@@ -65,43 +67,255 @@ struct rt_sigframe {
  * Thread saved context copy to/from a signal context presumed to be on the
  * user stack, and therefore accessed with appropriate macros from uaccess.h.
  */
-static int copy_fp_to_sigcontext(struct sigcontext __user *sc)
+static int copy_fp_to_sigcontext(void __user *sc)
 {
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
        int i;
        int err = 0;
+       int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1;
 
-       for (i = 0; i < NUM_FPU_REGS; i++) {
+       for (i = 0; i < NUM_FPU_REGS; i += inc) {
                err |=
                    __put_user(get_fpr64(&current->thread.fpu.fpr[i], 0),
-                              &sc->sc_fpregs[i]);
+                              &fpregs[i]);
        }
-       err |= __put_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr);
+       err |= __put_user(current->thread.fpu.fcr31, csr);
 
        return err;
 }
 
-static int copy_fp_from_sigcontext(struct sigcontext __user *sc)
+static int copy_fp_from_sigcontext(void __user *sc)
 {
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
        int i;
        int err = 0;
+       int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1;
        u64 fpr_val;
 
-       for (i = 0; i < NUM_FPU_REGS; i++) {
-               err |= __get_user(fpr_val, &sc->sc_fpregs[i]);
+       for (i = 0; i < NUM_FPU_REGS; i += inc) {
+               err |= __get_user(fpr_val, &fpregs[i]);
                set_fpr64(&current->thread.fpu.fpr[i], 0, fpr_val);
        }
-       err |= __get_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr);
+       err |= __get_user(current->thread.fpu.fcr31, csr);
 
        return err;
 }
 
+/*
+ * Wrappers for the assembly _{save,restore}_fp_context functions.
+ */
+static int save_hw_fp_context(void __user *sc)
+{
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
+
+       return _save_fp_context(fpregs, csr);
+}
+
+static int restore_hw_fp_context(void __user *sc)
+{
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
+
+       return _restore_fp_context(fpregs, csr);
+}
+
+/*
+ * Extended context handling.
+ */
+
+static inline void __user *sc_to_extcontext(void __user *sc)
+{
+       struct ucontext __user *uc;
+
+       /*
+        * We can just pretend the sigcontext is always embedded in a struct
+        * ucontext here, because the offset from sigcontext to extended
+        * context is the same in the struct sigframe case.
+        */
+       uc = container_of(sc, struct ucontext, uc_mcontext);
+       return &uc->uc_extcontext;
+}
+
+static int save_msa_extcontext(void __user *buf)
+{
+       struct msa_extcontext __user *msa = buf;
+       uint64_t val;
+       int i, err;
+
+       if (!thread_msa_context_live())
+               return 0;
+
+       /*
+        * Ensure that we can't lose the live MSA context between checking
+        * for it & writing it to memory.
+        */
+       preempt_disable();
+
+       if (is_msa_enabled()) {
+               /*
+                * There are no EVA versions of the vector register load/store
+                * instructions, so MSA context has to be saved to kernel memory
+                * and then copied to user memory. The save to kernel memory
+                * should already have been done when handling scalar FP
+                * context.
+                */
+               BUG_ON(config_enabled(CONFIG_EVA));
+
+               err = __put_user(read_msa_csr(), &msa->csr);
+               err |= _save_msa_all_upper(&msa->wr);
+
+               preempt_enable();
+       } else {
+               preempt_enable();
+
+               err = __put_user(current->thread.fpu.msacsr, &msa->csr);
+
+               for (i = 0; i < NUM_FPU_REGS; i++) {
+                       val = get_fpr64(&current->thread.fpu.fpr[i], 1);
+                       err |= __put_user(val, &msa->wr[i]);
+               }
+       }
+
+       err |= __put_user(MSA_EXTCONTEXT_MAGIC, &msa->ext.magic);
+       err |= __put_user(sizeof(*msa), &msa->ext.size);
+
+       return err ? -EFAULT : sizeof(*msa);
+}
+
+static int restore_msa_extcontext(void __user *buf, unsigned int size)
+{
+       struct msa_extcontext __user *msa = buf;
+       unsigned long long val;
+       unsigned int csr;
+       int i, err;
+
+       if (size != sizeof(*msa))
+               return -EINVAL;
+
+       err = get_user(csr, &msa->csr);
+       if (err)
+               return err;
+
+       preempt_disable();
+
+       if (is_msa_enabled()) {
+               /*
+                * There are no EVA versions of the vector register load/store
+                * instructions, so MSA context has to be copied to kernel
+                * memory and later loaded to registers. The same is true of
+                * scalar FP context, so FPU & MSA should have already been
+                * disabled whilst handling scalar FP context.
+                */
+               BUG_ON(config_enabled(CONFIG_EVA));
+
+               write_msa_csr(csr);
+               err |= _restore_msa_all_upper(&msa->wr);
+               preempt_enable();
+       } else {
+               preempt_enable();
+
+               current->thread.fpu.msacsr = csr;
+
+               for (i = 0; i < NUM_FPU_REGS; i++) {
+                       err |= __get_user(val, &msa->wr[i]);
+                       set_fpr64(&current->thread.fpu.fpr[i], 1, val);
+               }
+       }
+
+       return err;
+}
+
+static int save_extcontext(void __user *buf)
+{
+       int sz;
+
+       sz = save_msa_extcontext(buf);
+       if (sz < 0)
+               return sz;
+       buf += sz;
+
+       /* If no context was saved then trivially return */
+       if (!sz)
+               return 0;
+
+       /* Write the end marker */
+       if (__put_user(END_EXTCONTEXT_MAGIC, (u32 *)buf))
+               return -EFAULT;
+
+       sz += sizeof(((struct extcontext *)NULL)->magic);
+       return sz;
+}
+
+static int restore_extcontext(void __user *buf)
+{
+       struct extcontext ext;
+       int err;
+
+       while (1) {
+               err = __get_user(ext.magic, (unsigned int *)buf);
+               if (err)
+                       return err;
+
+               if (ext.magic == END_EXTCONTEXT_MAGIC)
+                       return 0;
+
+               err = __get_user(ext.size, (unsigned int *)(buf
+                       + offsetof(struct extcontext, size)));
+               if (err)
+                       return err;
+
+               switch (ext.magic) {
+               case MSA_EXTCONTEXT_MAGIC:
+                       err = restore_msa_extcontext(buf, ext.size);
+                       break;
+
+               default:
+                       err = -EINVAL;
+                       break;
+               }
+
+               if (err)
+                       return err;
+
+               buf += ext.size;
+       }
+}
+
 /*
  * Helper routines
  */
-static int protected_save_fp_context(struct sigcontext __user *sc)
+int protected_save_fp_context(void __user *sc)
 {
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
+       uint32_t __user *used_math = sc + abi->off_sc_used_math;
+       unsigned int used, ext_sz;
        int err;
-#ifndef CONFIG_EVA
+
+       used = used_math() ? USED_FP : 0;
+       if (!used)
+               goto fp_done;
+
+       if (!test_thread_flag(TIF_32BIT_FPREGS))
+               used |= USED_FR1;
+       if (test_thread_flag(TIF_HYBRID_FPREGS))
+               used |= USED_HYBRID_FPRS;
+
+       /*
+        * EVA does not have userland equivalents of ldc1 or sdc1, so
+        * save to the kernel FP context & copy that to userland below.
+        */
+       if (config_enabled(CONFIG_EVA))
+               lose_fpu(1);
+
        while (1) {
                lock_fpu_owner();
                if (is_fpu_owner()) {
@@ -114,27 +328,57 @@ static int protected_save_fp_context(struct sigcontext __user *sc)
                if (likely(!err))
                        break;
                /* touch the sigcontext and try again */
-               err = __put_user(0, &sc->sc_fpregs[0]) |
-                       __put_user(0, &sc->sc_fpregs[31]) |
-                       __put_user(0, &sc->sc_fpc_csr);
+               err = __put_user(0, &fpregs[0]) |
+                       __put_user(0, &fpregs[31]) |
+                       __put_user(0, csr);
                if (err)
-                       break;  /* really bad sigcontext */
+                       return err;     /* really bad sigcontext */
        }
-#else
-       /*
-        * EVA does not have FPU EVA instructions so saving fpu context directly
-        * does not work.
-        */
-       lose_fpu(1);
-       err = save_fp_context(sc); /* this might fail */
-#endif
-       return err;
+
+fp_done:
+       ext_sz = err = save_extcontext(sc_to_extcontext(sc));
+       if (err < 0)
+               return err;
+       used |= ext_sz ? USED_EXTCONTEXT : 0;
+
+       return __put_user(used, used_math);
 }
 
-static int protected_restore_fp_context(struct sigcontext __user *sc)
+int protected_restore_fp_context(void __user *sc)
 {
-       int err, tmp __maybe_unused;
-#ifndef CONFIG_EVA
+       struct mips_abi *abi = current->thread.abi;
+       uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+       uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
+       uint32_t __user *used_math = sc + abi->off_sc_used_math;
+       unsigned int used;
+       int err, sig = 0, tmp __maybe_unused;
+
+       err = __get_user(used, used_math);
+       conditional_used_math(used & USED_FP);
+
+       /*
+        * The signal handler may have used FPU; give it up if the program
+        * doesn't want it following sigreturn.
+        */
+       if (err || !(used & USED_FP))
+               lose_fpu(0);
+       if (err)
+               return err;
+       if (!(used & USED_FP))
+               goto fp_done;
+
+       err = sig = fpcsr_pending(csr);
+       if (err < 0)
+               return err;
+
+       /*
+        * EVA does not have userland equivalents of ldc1 or sdc1, so we
+        * disable the FPU here such that the code below simply copies to
+        * the kernel FP context.
+        */
+       if (config_enabled(CONFIG_EVA))
+               lose_fpu(0);
+
        while (1) {
                lock_fpu_owner();
                if (is_fpu_owner()) {
@@ -147,28 +391,24 @@ static int protected_restore_fp_context(struct sigcontext __user *sc)
                if (likely(!err))
                        break;
                /* touch the sigcontext and try again */
-               err = __get_user(tmp, &sc->sc_fpregs[0]) |
-                       __get_user(tmp, &sc->sc_fpregs[31]) |
-                       __get_user(tmp, &sc->sc_fpc_csr);
+               err = __get_user(tmp, &fpregs[0]) |
+                       __get_user(tmp, &fpregs[31]) |
+                       __get_user(tmp, csr);
                if (err)
                        break;  /* really bad sigcontext */
        }
-#else
-       /*
-        * EVA does not have FPU EVA instructions so restoring fpu context
-        * directly does not work.
-        */
-       lose_fpu(0);
-       err = restore_fp_context(sc); /* this might fail */
-#endif
-       return err;
+
+fp_done:
+       if (used & USED_EXTCONTEXT)
+               err |= restore_extcontext(sc_to_extcontext(sc));
+
+       return err ?: sig;
 }
 
 int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 {
        int err = 0;
        int i;
-       unsigned int used_math;
 
        err |= __put_user(regs->cp0_epc, &sc->sc_pc);
 
@@ -191,19 +431,38 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
        }
 
-       used_math = !!used_math();
-       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.
-                */
-               err |= protected_save_fp_context(sc);
-       }
+       /*
+        * Save FPU state to signal context. Signal handler
+        * will "inherit" current FPU state.
+        */
+       err |= protected_save_fp_context(sc);
+
        return err;
 }
 
+static size_t extcontext_max_size(void)
+{
+       size_t sz = 0;
+
+       /*
+        * The assumption here is that between this point & the point at which
+        * the extended context is saved the size of the context should only
+        * ever be able to shrink (if the task is preempted), but never grow.
+        * That is, what this function returns is an upper bound on the size of
+        * the extended context for the current task at the current time.
+        */
+
+       if (thread_msa_context_live())
+               sz += sizeof(struct msa_extcontext);
+
+       /* If any context is saved then we'll append the end marker */
+       if (sz)
+               sz += sizeof(((struct extcontext *)NULL)->magic);
+
+       return sz;
+}
+
 int fpcsr_pending(unsigned int __user *fpcsr)
 {
        int err, sig = 0;
@@ -223,21 +482,8 @@ int fpcsr_pending(unsigned int __user *fpcsr)
        return err ?: sig;
 }
 
-static int
-check_and_restore_fp_context(struct sigcontext __user *sc)
-{
-       int err, sig;
-
-       err = sig = fpcsr_pending(&sc->sc_fpc_csr);
-       if (err > 0)
-               err = 0;
-       err |= protected_restore_fp_context(sc);
-       return err ?: sig;
-}
-
 int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
 {
-       unsigned int used_math;
        unsigned long treg;
        int err = 0;
        int i;
@@ -265,19 +511,7 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        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);
-
-       if (used_math) {
-               /* restore fpu context if we have used it before */
-               if (!err)
-                       err = check_and_restore_fp_context(sc);
-       } else {
-               /* signal handler may have used FPU.  Give it up. */
-               lose_fpu(0);
-       }
-
-       return err;
+       return err ?: protected_restore_fp_context(sc);
 }
 
 void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
@@ -285,6 +519,9 @@ void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
 {
        unsigned long sp;
 
+       /* Leave space for potential extended context */
+       frame_size += extcontext_max_size();
+
        /* Default to using normal stack */
        sp = regs->regs[29];
 
@@ -520,7 +757,11 @@ struct mips_abi mips_abi = {
        .setup_rt_frame = setup_rt_frame,
        .rt_signal_return_offset =
                offsetof(struct mips_vdso, rt_signal_trampoline),
-       .restart        = __NR_restart_syscall
+       .restart        = __NR_restart_syscall,
+
+       .off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
+       .off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
+       .off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
 };
 
 static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
@@ -616,6 +857,9 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
 
        user_exit();
 
+       if (thread_info_flags & _TIF_UPROBE)
+               uprobe_notify_resume(regs);
+
        /* deal with pending signal delivery */
        if (thread_info_flags & _TIF_SIGPENDING)
                do_signal(regs);
@@ -629,43 +873,46 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
 }
 
 #ifdef CONFIG_SMP
-#ifndef CONFIG_EVA
-static int smp_save_fp_context(struct sigcontext __user *sc)
+static int smp_save_fp_context(void __user *sc)
 {
        return raw_cpu_has_fpu
-              ? _save_fp_context(sc)
+              ? save_hw_fp_context(sc)
               : copy_fp_to_sigcontext(sc);
 }
 
-static int smp_restore_fp_context(struct sigcontext __user *sc)
+static int smp_restore_fp_context(void __user *sc)
 {
        return raw_cpu_has_fpu
-              ? _restore_fp_context(sc)
+              ? restore_hw_fp_context(sc)
               : copy_fp_from_sigcontext(sc);
 }
-#endif /* CONFIG_EVA */
 #endif
 
 static int signal_setup(void)
 {
-#ifndef CONFIG_EVA
+       /*
+        * The offset from sigcontext to extended context should be the same
+        * regardless of the type of signal, such that userland can always know
+        * where to look if it wishes to find the extended context structures.
+        */
+       BUILD_BUG_ON((offsetof(struct sigframe, sf_extcontext) -
+                     offsetof(struct sigframe, sf_sc)) !=
+                    (offsetof(struct rt_sigframe, rs_uc.uc_extcontext) -
+                     offsetof(struct rt_sigframe, rs_uc.uc_mcontext)));
+
 #ifdef CONFIG_SMP
        /* For now just do the cpu_has_fpu check when the functions are invoked */
        save_fp_context = smp_save_fp_context;
        restore_fp_context = smp_restore_fp_context;
 #else
        if (cpu_has_fpu) {
-               save_fp_context = _save_fp_context;
-               restore_fp_context = _restore_fp_context;
+               save_fp_context = save_hw_fp_context;
+               restore_fp_context = restore_hw_fp_context;
        } else {
                save_fp_context = copy_fp_to_sigcontext;
                restore_fp_context = copy_fp_from_sigcontext;
        }
 #endif /* CONFIG_SMP */
-#else
-       save_fp_context = copy_fp_to_sigcontext;
-       restore_fp_context = copy_fp_from_sigcontext;
-#endif
 
        return 0;
 }