summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@imgtec.com>2014-02-13 11:27:42 +0000
committerRalf Baechle <ralf@linux-mips.org>2014-03-26 23:09:11 +0100
commiteec43a224cf198c7e3538fca16f689e4d17d4471 (patch)
tree24d9236c0cd33dcf7862c09799910e8bbf5abde9
parenta8ad136789b4256fa372d59daaddb91b72aa0753 (diff)
MIPS: Save/restore MSA context around signals
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>
-rw-r--r--arch/mips/include/asm/sigcontext.h2
-rw-r--r--arch/mips/include/uapi/asm/sigcontext.h8
-rw-r--r--arch/mips/kernel/asm-offsets.c3
-rw-r--r--arch/mips/kernel/r4k_fpu.S213
-rw-r--r--arch/mips/kernel/signal.c73
-rw-r--r--arch/mips/kernel/signal32.c74
6 files changed, 357 insertions, 16 deletions
diff --git a/arch/mips/include/asm/sigcontext.h b/arch/mips/include/asm/sigcontext.h
index eeeb0f48c767..f54bdbe85c0d 100644
--- a/arch/mips/include/asm/sigcontext.h
+++ b/arch/mips/include/asm/sigcontext.h
@@ -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 */
diff --git a/arch/mips/include/uapi/asm/sigcontext.h b/arch/mips/include/uapi/asm/sigcontext.h
index 6c9906f59c6e..681c17603a48 100644
--- a/arch/mips/include/uapi/asm/sigcontext.h
+++ b/arch/mips/include/uapi/asm/sigcontext.h
@@ -12,6 +12,10 @@
#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;
};
diff --git a/arch/mips/kernel/asm-offsets.c b/arch/mips/kernel/asm-offsets.c
index 7ff80622c8d9..0ea75c244b48 100644
--- a/arch/mips/kernel/asm-offsets.c
+++ b/arch/mips/kernel/asm-offsets.c
@@ -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
diff --git a/arch/mips/kernel/r4k_fpu.S b/arch/mips/kernel/r4k_fpu.S
index 253b2fb52026..752b50a69264 100644
--- a/arch/mips/kernel/r4k_fpu.S
+++ b/arch/mips/kernel/r4k_fpu.S
@@ -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
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c
index 0f97c7dc54e6..fd61700409bc 100644
--- a/arch/mips/kernel/signal.c
+++ b/arch/mips/kernel/signal.c
@@ -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 */
@@ -95,19 +99,59 @@ static int copy_fp_from_sigcontext(struct sigcontext __user *sc)
}
/*
+ * 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);
}
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c
index bae2e6ee2109..299f956e4db3 100644
--- a/arch/mips/kernel/signal32.c
+++ b/arch/mips/kernel/signal32.c
@@ -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 ...
*/
@@ -111,19 +115,59 @@ static int copy_fp_from_sigcontext32(struct sigcontext32 __user *sc)
}
/*
+ * 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);
}