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>
This commit is contained in:
Paul Burton 2014-02-13 11:27:42 +00:00 committed by Ralf Baechle
parent a8ad136789
commit eec43a224c
6 changed files with 357 additions and 16 deletions

View 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);
}