diff options
Diffstat (limited to 'arch/mips/kernel/signal32.c')
-rw-r--r-- | arch/mips/kernel/signal32.c | 137 |
1 files changed, 117 insertions, 20 deletions
diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c index 3d60f7750fa..299f956e4db 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,8 +43,8 @@ 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 fpu_emulator_save_context32(struct sigcontext32 __user *sc); -extern asmlinkage int fpu_emulator_restore_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 ... @@ -78,17 +79,96 @@ struct rt_sigframe32 { }; /* + * Thread saved context copy to/from a signal context presumed to be on the + * user stack, and therefore accessed with appropriate macros from uaccess.h. + */ +static int copy_fp_to_sigcontext32(struct sigcontext32 __user *sc) +{ + int i; + int err = 0; + int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1; + + for (i = 0; i < NUM_FPU_REGS; i += inc) { + err |= + __put_user(get_fpr64(¤t->thread.fpu.fpr[i], 0), + &sc->sc_fpregs[i]); + } + err |= __put_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr); + + return err; +} + +static int copy_fp_from_sigcontext32(struct sigcontext32 __user *sc) +{ + int i; + int err = 0; + int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1; + u64 fpr_val; + + for (i = 0; i < NUM_FPU_REGS; i += inc) { + err |= __get_user(fpr_val, &sc->sc_fpregs[i]); + set_fpr64(¤t->thread.fpu.fpr[i], 0, fpr_val); + } + err |= __get_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr); + + 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(); - err = own_fpu_inatomic(1); - if (!err) - err = save_fp_context32(sc); /* this might fail */ - unlock_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; /* touch the sigcontext and try again */ @@ -101,15 +181,29 @@ 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(); - err = own_fpu_inatomic(0); - if (!err) - err = restore_fp_context32(sc); /* this might fail */ - unlock_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; /* touch the sigcontext and try again */ @@ -147,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) { @@ -155,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; } @@ -205,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); } @@ -566,8 +663,8 @@ static int signal32_init(void) save_fp_context32 = _save_fp_context32; restore_fp_context32 = _restore_fp_context32; } else { - save_fp_context32 = fpu_emulator_save_context32; - restore_fp_context32 = fpu_emulator_restore_context32; + save_fp_context32 = copy_fp_to_sigcontext32; + restore_fp_context32 = copy_fp_from_sigcontext32; } return 0; |