MIPS: Save/restore MSA context around signals
authorPaul Burton <paul.burton@imgtec.com>
Thu, 13 Feb 2014 11:27:42 +0000 (11:27 +0000)
committerRalf Baechle <ralf@linux-mips.org>
Wed, 26 Mar 2014 22:09:11 +0000 (23:09 +0100)
This patch extends sigcontext in order to hold the most significant 64
bits of each vector register in addition to the MSA control & status
register. The least significant 64 bits are already saved as the scalar
FP context. This makes things a little awkward since the least & most
significant 64 bits of each vector register are not contiguous in
memory. Thus the copy_u & insert instructions are used to transfer the
values of the most significant 64 bits via GP registers.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/6533/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/include/asm/sigcontext.h
arch/mips/include/uapi/asm/sigcontext.h
arch/mips/kernel/asm-offsets.c
arch/mips/kernel/r4k_fpu.S
arch/mips/kernel/signal.c
arch/mips/kernel/signal32.c

index eeeb0f48c76754c2c9111b2f81528361545bb04e..f54bdbe85c0d859888495644a80484b5eaaff787 100644 (file)
@@ -32,6 +32,8 @@ struct sigcontext32 {
        __u32           sc_lo2;
        __u32           sc_hi3;
        __u32           sc_lo3;
+       __u64           sc_msaregs[32]; /* Most significant 64 bits */
+       __u32           sc_msa_csr;
 };
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32 */
 #endif /* _ASM_SIGCONTEXT_H */
index 6c9906f59c6e69f4a6d778064204c8764653b12a..681c17603a48d41fa463028f3cee0731b166d556 100644 (file)
 #include <linux/types.h>
 #include <asm/sgidefs.h>
 
+/* Bits which may be set in sc_used_math */
+#define USEDMATH_FP    (1 << 0)
+#define USEDMATH_MSA   (1 << 1)
+
 #if _MIPS_SIM == _MIPS_SIM_ABI32
 
 /*
@@ -37,6 +41,8 @@ struct sigcontext {
        unsigned long           sc_lo2;
        unsigned long           sc_hi3;
        unsigned long           sc_lo3;
+       unsigned long long      sc_msaregs[32]; /* Most significant 64 bits */
+       unsigned long           sc_msa_csr;
 };
 
 #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */
@@ -70,6 +76,8 @@ struct sigcontext {
        __u32   sc_used_math;
        __u32   sc_dsp;
        __u32   sc_reserved;
+       __u64   sc_msaregs[32];
+       __u32   sc_msa_csr;
 };
 
 
index 7ff80622c8d9b264b0748912e68e2b699cf8c5fc..0ea75c244b487848c36c168b4f2f9c7dad4822a5 100644 (file)
@@ -295,6 +295,7 @@ void output_sc_defines(void)
        OFFSET(SC_LO2, sigcontext, sc_lo2);
        OFFSET(SC_HI3, sigcontext, sc_hi3);
        OFFSET(SC_LO3, sigcontext, sc_lo3);
+       OFFSET(SC_MSAREGS, sigcontext, sc_msaregs);
        BLANK();
 }
 #endif
@@ -309,6 +310,7 @@ void output_sc_defines(void)
        OFFSET(SC_MDLO, sigcontext, sc_mdlo);
        OFFSET(SC_PC, sigcontext, sc_pc);
        OFFSET(SC_FPC_CSR, sigcontext, sc_fpc_csr);
+       OFFSET(SC_MSAREGS, sigcontext, sc_msaregs);
        BLANK();
 }
 #endif
@@ -320,6 +322,7 @@ void output_sc32_defines(void)
        OFFSET(SC32_FPREGS, sigcontext32, sc_fpregs);
        OFFSET(SC32_FPC_CSR, sigcontext32, sc_fpc_csr);
        OFFSET(SC32_FPC_EIR, sigcontext32, sc_fpc_eir);
+       OFFSET(SC32_MSAREGS, sigcontext32, sc_msaregs);
        BLANK();
 }
 #endif
index 253b2fb520267fb3536584df6d395a6b860392f1..752b50a692642e74cb243e6e1dfeb9815be7a324 100644 (file)
@@ -13,6 +13,7 @@
  * 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>
@@ -245,6 +246,218 @@ LEAF(_restore_fp_context32)
        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
index 0f97c7dc54e694e335d0c596666362c7419eeeb2..fd61700409bc15e362e31b11e180be6f4c9347da 100644 (file)
@@ -30,6 +30,7 @@
 #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>
@@ -46,6 +47,9 @@ 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);
 
+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 */
@@ -94,20 +98,60 @@ static int copy_fp_from_sigcontext(struct sigcontext __user *sc)
        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(&current->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(&current->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;
@@ -121,17 +165,28 @@ static int protected_save_fp_context(struct sigcontext __user *sc)
        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;
@@ -172,7 +227,8 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                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) {
@@ -180,7 +236,7 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
                 * 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;
 }
@@ -205,14 +261,14 @@ int fpcsr_pending(unsigned int __user *fpcsr)
 }
 
 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;
 }
 
@@ -252,9 +308,10 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
        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);
        }
 
index bae2e6ee2109f484e079faa21d774a18f0f2f678..299f956e4db3cecc989b14994c3db67b22b50c16 100644 (file)
@@ -30,6 +30,7 @@
 #include <asm/sim.h>
 #include <asm/ucontext.h>
 #include <asm/fpu.h>
+#include <asm/msa.h>
 #include <asm/war.h>
 #include <asm/vdso.h>
 #include <asm/dsp.h>
@@ -42,6 +43,9 @@ static int (*restore_fp_context32)(struct sigcontext32 __user *sc);
 extern asmlinkage int _save_fp_context32(struct sigcontext32 __user *sc);
 extern asmlinkage int _restore_fp_context32(struct sigcontext32 __user *sc);
 
+extern asmlinkage int _save_msa_context32(struct sigcontext32 __user *sc);
+extern asmlinkage int _restore_msa_context32(struct sigcontext32 __user *sc);
+
 /*
  * Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
  */
@@ -110,20 +114,60 @@ static int copy_fp_from_sigcontext32(struct sigcontext32 __user *sc)
        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_sigcontext32(struct sigcontext32 __user *sc)
+{
+       int i;
+       int err = 0;
+
+       for (i = 0; i < NUM_FPU_REGS; i++) {
+               err |=
+                   __put_user(get_fpr64(&current->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_sigcontext32(struct sigcontext32 __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(&current->thread.fpu.fpr[i], 1, val);
+       }
+       err |= __get_user(current->thread.fpu.msacsr, &sc->sc_msa_csr);
+
+       return err;
+}
+
 /*
  * sigcontext handlers
  */
-static int protected_save_fp_context32(struct sigcontext32 __user *sc)
+static int protected_save_fp_context32(struct sigcontext32 __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_context32(sc);
+                       if (save_msa && !err)
+                               err = _save_msa_context32(sc);
                        unlock_fpu_owner();
                } else {
                        unlock_fpu_owner();
                        err = copy_fp_to_sigcontext32(sc);
+                       if (save_msa && !err)
+                               err = copy_msa_to_sigcontext32(sc);
                }
                if (likely(!err))
                        break;
@@ -137,17 +181,28 @@ static int protected_save_fp_context32(struct sigcontext32 __user *sc)
        return err;
 }
 
-static int protected_restore_fp_context32(struct sigcontext32 __user *sc)
+static int protected_restore_fp_context32(struct sigcontext32 __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_context32(sc);
+                       if (restore_msa && !err) {
+                               enable_msa();
+                               err = _restore_msa_context32(sc);
+                       } else {
+                               /* signal handler may have used MSA */
+                               disable_msa();
+                       }
                        unlock_fpu_owner();
                } else {
                        unlock_fpu_owner();
                        err = copy_fp_from_sigcontext32(sc);
+                       if (restore_msa && !err)
+                               err = copy_msa_from_sigcontext32(sc);
                }
                if (likely(!err))
                        break;
@@ -186,7 +241,8 @@ static int setup_sigcontext32(struct pt_regs *regs,
                err |= __put_user(mflo3(), &sc->sc_lo3);
        }
 
-       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) {
@@ -194,20 +250,21 @@ static int setup_sigcontext32(struct pt_regs *regs,
                 * Save FPU state to signal context.  Signal handler
                 * will "inherit" current FPU state.
                 */
-               err |= protected_save_fp_context32(sc);
+               err |= protected_save_fp_context32(sc, used_math);
        }
        return err;
 }
 
 static int
-check_and_restore_fp_context32(struct sigcontext32 __user *sc)
+check_and_restore_fp_context32(struct sigcontext32 __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_context32(sc);
+       err |= protected_restore_fp_context32(sc, used_math);
        return err ?: sig;
 }
 
@@ -244,9 +301,10 @@ static int restore_sigcontext32(struct pt_regs *regs,
        if (used_math) {
                /* restore fpu context if we have used it before */
                if (!err)
-                       err = check_and_restore_fp_context32(sc);
+                       err = check_and_restore_fp_context32(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);
        }