* Copyright (C) 1999, 2001 Silicon Graphics, Inc.
  */
 #include <asm/asm.h>
+#include <asm/asmmacro.h>
 #include <asm/errno.h>
 #include <asm/fpregdef.h>
 #include <asm/mipsregs.h>
        END(_restore_fp_context32)
 #endif
 
+#ifdef CONFIG_CPU_HAS_MSA
+
+       .macro  save_sc_msareg  wr, off, sc, tmp
+#ifdef CONFIG_64BIT
+       copy_u_d \tmp, \wr, 1
+       EX sd   \tmp, (\off+(\wr*8))(\sc)
+#elif defined(CONFIG_CPU_LITTLE_ENDIAN)
+       copy_u_w \tmp, \wr, 2
+       EX sw   \tmp, (\off+(\wr*8)+0)(\sc)
+       copy_u_w \tmp, \wr, 3
+       EX sw   \tmp, (\off+(\wr*8)+4)(\sc)
+#else /* CONFIG_CPU_BIG_ENDIAN */
+       copy_u_w \tmp, \wr, 2
+       EX sw   \tmp, (\off+(\wr*8)+4)(\sc)
+       copy_u_w \tmp, \wr, 3
+       EX sw   \tmp, (\off+(\wr*8)+0)(\sc)
+#endif
+       .endm
+
+/*
+ * int _save_msa_context(struct sigcontext *sc)
+ *
+ * Save the upper 64 bits of each vector register along with the MSA_CSR
+ * register into sc. Returns zero on success, else non-zero.
+ */
+LEAF(_save_msa_context)
+       save_sc_msareg  0, SC_MSAREGS, a0, t0
+       save_sc_msareg  1, SC_MSAREGS, a0, t0
+       save_sc_msareg  2, SC_MSAREGS, a0, t0
+       save_sc_msareg  3, SC_MSAREGS, a0, t0
+       save_sc_msareg  4, SC_MSAREGS, a0, t0
+       save_sc_msareg  5, SC_MSAREGS, a0, t0
+       save_sc_msareg  6, SC_MSAREGS, a0, t0
+       save_sc_msareg  7, SC_MSAREGS, a0, t0
+       save_sc_msareg  8, SC_MSAREGS, a0, t0
+       save_sc_msareg  9, SC_MSAREGS, a0, t0
+       save_sc_msareg  10, SC_MSAREGS, a0, t0
+       save_sc_msareg  11, SC_MSAREGS, a0, t0
+       save_sc_msareg  12, SC_MSAREGS, a0, t0
+       save_sc_msareg  13, SC_MSAREGS, a0, t0
+       save_sc_msareg  14, SC_MSAREGS, a0, t0
+       save_sc_msareg  15, SC_MSAREGS, a0, t0
+       save_sc_msareg  16, SC_MSAREGS, a0, t0
+       save_sc_msareg  17, SC_MSAREGS, a0, t0
+       save_sc_msareg  18, SC_MSAREGS, a0, t0
+       save_sc_msareg  19, SC_MSAREGS, a0, t0
+       save_sc_msareg  20, SC_MSAREGS, a0, t0
+       save_sc_msareg  21, SC_MSAREGS, a0, t0
+       save_sc_msareg  22, SC_MSAREGS, a0, t0
+       save_sc_msareg  23, SC_MSAREGS, a0, t0
+       save_sc_msareg  24, SC_MSAREGS, a0, t0
+       save_sc_msareg  25, SC_MSAREGS, a0, t0
+       save_sc_msareg  26, SC_MSAREGS, a0, t0
+       save_sc_msareg  27, SC_MSAREGS, a0, t0
+       save_sc_msareg  28, SC_MSAREGS, a0, t0
+       save_sc_msareg  29, SC_MSAREGS, a0, t0
+       save_sc_msareg  30, SC_MSAREGS, a0, t0
+       save_sc_msareg  31, SC_MSAREGS, a0, t0
+       jr      ra
+        li     v0, 0
+       END(_save_msa_context)
+
+#ifdef CONFIG_MIPS32_COMPAT
+
+/*
+ * int _save_msa_context32(struct sigcontext32 *sc)
+ *
+ * Save the upper 64 bits of each vector register along with the MSA_CSR
+ * register into sc. Returns zero on success, else non-zero.
+ */
+LEAF(_save_msa_context32)
+       save_sc_msareg  0, SC32_MSAREGS, a0, t0
+       save_sc_msareg  1, SC32_MSAREGS, a0, t0
+       save_sc_msareg  2, SC32_MSAREGS, a0, t0
+       save_sc_msareg  3, SC32_MSAREGS, a0, t0
+       save_sc_msareg  4, SC32_MSAREGS, a0, t0
+       save_sc_msareg  5, SC32_MSAREGS, a0, t0
+       save_sc_msareg  6, SC32_MSAREGS, a0, t0
+       save_sc_msareg  7, SC32_MSAREGS, a0, t0
+       save_sc_msareg  8, SC32_MSAREGS, a0, t0
+       save_sc_msareg  9, SC32_MSAREGS, a0, t0
+       save_sc_msareg  10, SC32_MSAREGS, a0, t0
+       save_sc_msareg  11, SC32_MSAREGS, a0, t0
+       save_sc_msareg  12, SC32_MSAREGS, a0, t0
+       save_sc_msareg  13, SC32_MSAREGS, a0, t0
+       save_sc_msareg  14, SC32_MSAREGS, a0, t0
+       save_sc_msareg  15, SC32_MSAREGS, a0, t0
+       save_sc_msareg  16, SC32_MSAREGS, a0, t0
+       save_sc_msareg  17, SC32_MSAREGS, a0, t0
+       save_sc_msareg  18, SC32_MSAREGS, a0, t0
+       save_sc_msareg  19, SC32_MSAREGS, a0, t0
+       save_sc_msareg  20, SC32_MSAREGS, a0, t0
+       save_sc_msareg  21, SC32_MSAREGS, a0, t0
+       save_sc_msareg  22, SC32_MSAREGS, a0, t0
+       save_sc_msareg  23, SC32_MSAREGS, a0, t0
+       save_sc_msareg  24, SC32_MSAREGS, a0, t0
+       save_sc_msareg  25, SC32_MSAREGS, a0, t0
+       save_sc_msareg  26, SC32_MSAREGS, a0, t0
+       save_sc_msareg  27, SC32_MSAREGS, a0, t0
+       save_sc_msareg  28, SC32_MSAREGS, a0, t0
+       save_sc_msareg  29, SC32_MSAREGS, a0, t0
+       save_sc_msareg  30, SC32_MSAREGS, a0, t0
+       save_sc_msareg  31, SC32_MSAREGS, a0, t0
+       jr      ra
+        li     v0, 0
+       END(_save_msa_context32)
+
+#endif /* CONFIG_MIPS32_COMPAT */
+
+       .macro restore_sc_msareg        wr, off, sc, tmp
+#ifdef CONFIG_64BIT
+       EX ld   \tmp, (\off+(\wr*8))(\sc)
+       insert_d \wr, 1, \tmp
+#elif defined(CONFIG_CPU_LITTLE_ENDIAN)
+       EX lw   \tmp, (\off+(\wr*8)+0)(\sc)
+       insert_w \wr, 2, \tmp
+       EX lw   \tmp, (\off+(\wr*8)+4)(\sc)
+       insert_w \wr, 3, \tmp
+#else /* CONFIG_CPU_BIG_ENDIAN */
+       EX lw   \tmp, (\off+(\wr*8)+4)(\sc)
+       insert_w \wr, 2, \tmp
+       EX lw   \tmp, (\off+(\wr*8)+0)(\sc)
+       insert_w \wr, 3, \tmp
+#endif
+       .endm
+
+/*
+ * int _restore_msa_context(struct sigcontext *sc)
+ */
+LEAF(_restore_msa_context)
+       restore_sc_msareg       0, SC_MSAREGS, a0, t0
+       restore_sc_msareg       1, SC_MSAREGS, a0, t0
+       restore_sc_msareg       2, SC_MSAREGS, a0, t0
+       restore_sc_msareg       3, SC_MSAREGS, a0, t0
+       restore_sc_msareg       4, SC_MSAREGS, a0, t0
+       restore_sc_msareg       5, SC_MSAREGS, a0, t0
+       restore_sc_msareg       6, SC_MSAREGS, a0, t0
+       restore_sc_msareg       7, SC_MSAREGS, a0, t0
+       restore_sc_msareg       8, SC_MSAREGS, a0, t0
+       restore_sc_msareg       9, SC_MSAREGS, a0, t0
+       restore_sc_msareg       10, SC_MSAREGS, a0, t0
+       restore_sc_msareg       11, SC_MSAREGS, a0, t0
+       restore_sc_msareg       12, SC_MSAREGS, a0, t0
+       restore_sc_msareg       13, SC_MSAREGS, a0, t0
+       restore_sc_msareg       14, SC_MSAREGS, a0, t0
+       restore_sc_msareg       15, SC_MSAREGS, a0, t0
+       restore_sc_msareg       16, SC_MSAREGS, a0, t0
+       restore_sc_msareg       17, SC_MSAREGS, a0, t0
+       restore_sc_msareg       18, SC_MSAREGS, a0, t0
+       restore_sc_msareg       19, SC_MSAREGS, a0, t0
+       restore_sc_msareg       20, SC_MSAREGS, a0, t0
+       restore_sc_msareg       21, SC_MSAREGS, a0, t0
+       restore_sc_msareg       22, SC_MSAREGS, a0, t0
+       restore_sc_msareg       23, SC_MSAREGS, a0, t0
+       restore_sc_msareg       24, SC_MSAREGS, a0, t0
+       restore_sc_msareg       25, SC_MSAREGS, a0, t0
+       restore_sc_msareg       26, SC_MSAREGS, a0, t0
+       restore_sc_msareg       27, SC_MSAREGS, a0, t0
+       restore_sc_msareg       28, SC_MSAREGS, a0, t0
+       restore_sc_msareg       29, SC_MSAREGS, a0, t0
+       restore_sc_msareg       30, SC_MSAREGS, a0, t0
+       restore_sc_msareg       31, SC_MSAREGS, a0, t0
+       jr      ra
+        li     v0, 0
+       END(_restore_msa_context)
+
+#ifdef CONFIG_MIPS32_COMPAT
+
+/*
+ * int _restore_msa_context32(struct sigcontext32 *sc)
+ */
+LEAF(_restore_msa_context32)
+       restore_sc_msareg       0, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       1, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       2, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       3, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       4, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       5, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       6, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       7, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       8, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       9, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       10, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       11, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       12, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       13, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       14, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       15, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       16, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       17, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       18, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       19, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       20, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       21, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       22, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       23, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       24, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       25, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       26, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       27, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       28, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       29, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       30, SC32_MSAREGS, a0, t0
+       restore_sc_msareg       31, SC32_MSAREGS, a0, t0
+       jr      ra
+        li     v0, 0
+       END(_restore_msa_context32)
+
+#endif /* CONFIG_MIPS32_COMPAT */
+
+#endif /* CONFIG_CPU_HAS_MSA */
+
        .set    reorder
 
        .type   fault@function
 
 #include <linux/bitops.h>
 #include <asm/cacheflush.h>
 #include <asm/fpu.h>
+#include <asm/msa.h>
 #include <asm/sim.h>
 #include <asm/ucontext.h>
 #include <asm/cpu-features.h>
 extern asmlinkage int _save_fp_context(struct sigcontext __user *sc);
 extern asmlinkage int _restore_fp_context(struct sigcontext __user *sc);
 
+extern asmlinkage int _save_msa_context(struct sigcontext __user *sc);
+extern asmlinkage int _restore_msa_context(struct sigcontext __user *sc);
+
 struct sigframe {
        u32 sf_ass[4];          /* argument save space for o32 */
        u32 sf_pad[2];          /* Was: signal trampoline */
        return err;
 }
 
+/*
+ * These functions will save only the upper 64 bits of the vector registers,
+ * since the lower 64 bits have already been saved as the scalar FP context.
+ */
+static int copy_msa_to_sigcontext(struct sigcontext __user *sc)
+{
+       int i;
+       int err = 0;
+
+       for (i = 0; i < NUM_FPU_REGS; i++) {
+               err |=
+                   __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 1),
+                              &sc->sc_msaregs[i]);
+       }
+       err |= __put_user(current->thread.fpu.msacsr, &sc->sc_msa_csr);
+
+       return err;
+}
+
+static int copy_msa_from_sigcontext(struct sigcontext __user *sc)
+{
+       int i;
+       int err = 0;
+       u64 val;
+
+       for (i = 0; i < NUM_FPU_REGS; i++) {
+               err |= __get_user(val, &sc->sc_msaregs[i]);
+               set_fpr64(¤t->thread.fpu.fpr[i], 1, val);
+       }
+       err |= __get_user(current->thread.fpu.msacsr, &sc->sc_msa_csr);
+
+       return err;
+}
+
 /*
  * Helper routines
  */
-static int protected_save_fp_context(struct sigcontext __user *sc)
+static int protected_save_fp_context(struct sigcontext __user *sc,
+                                    unsigned used_math)
 {
        int err;
+       bool save_msa = cpu_has_msa && (used_math & USEDMATH_MSA);
        while (1) {
                lock_fpu_owner();
                if (is_fpu_owner()) {
                        err = save_fp_context(sc);
+                       if (save_msa && !err)
+                               err = _save_msa_context(sc);
                        unlock_fpu_owner();
                } else {
                        unlock_fpu_owner();
                        err = copy_fp_to_sigcontext(sc);
+                       if (save_msa && !err)
+                               err = copy_msa_to_sigcontext(sc);
                }
                if (likely(!err))
                        break;
        return err;
 }
 
-static int protected_restore_fp_context(struct sigcontext __user *sc)
+static int protected_restore_fp_context(struct sigcontext __user *sc,
+                                       unsigned used_math)
 {
        int err, tmp __maybe_unused;
+       bool restore_msa = cpu_has_msa && (used_math & USEDMATH_MSA);
        while (1) {
                lock_fpu_owner();
                if (is_fpu_owner()) {
                        err = restore_fp_context(sc);
+                       if (restore_msa && !err) {
+                               enable_msa();
+                               err = _restore_msa_context(sc);
+                       } else {
+                               /* signal handler may have used MSA */
+                               disable_msa();
+                       }
                        unlock_fpu_owner();
                } else {
                        unlock_fpu_owner();
                        err = copy_fp_from_sigcontext(sc);
+                       if (!err && (used_math & USEDMATH_MSA))
+                               err = copy_msa_from_sigcontext(sc);
                }
                if (likely(!err))
                        break;
                err |= __put_user(rddsp(DSP_MASK), &sc->sc_dsp);
        }
 
-       used_math = !!used_math();
+       used_math = used_math() ? USEDMATH_FP : 0;
+       used_math |= thread_msa_context_live() ? USEDMATH_MSA : 0;
        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);
+               err |= protected_save_fp_context(sc, used_math);
        }
        return err;
 }
 }
 
 static int
-check_and_restore_fp_context(struct sigcontext __user *sc)
+check_and_restore_fp_context(struct sigcontext __user *sc, unsigned used_math)
 {
        int err, sig;
 
        err = sig = fpcsr_pending(&sc->sc_fpc_csr);
        if (err > 0)
                err = 0;
-       err |= protected_restore_fp_context(sc);
+       err |= protected_restore_fp_context(sc, used_math);
        return err ?: sig;
 }
 
        if (used_math) {
                /* restore fpu context if we have used it before */
                if (!err)
-                       err = check_and_restore_fp_context(sc);
+                       err = check_and_restore_fp_context(sc, used_math);
        } else {
-               /* signal handler may have used FPU.  Give it up. */
+               /* signal handler may have used FPU or MSA. Disable them. */
+               disable_msa();
                lose_fpu(0);
        }