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:
committed by
Ralf Baechle
parent
a8ad136789
commit
eec43a224c
@@ -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(¤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_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(¤t->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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user