diff options
Diffstat (limited to 'arch/mips/kernel/signal.c')
-rw-r--r-- | arch/mips/kernel/signal.c | 170 |
1 files changed, 148 insertions, 22 deletions
diff --git a/arch/mips/kernel/signal.c b/arch/mips/kernel/signal.c index 5199563c4403..33133d3df3e5 100644 --- a/arch/mips/kernel/signal.c +++ b/arch/mips/kernel/signal.c @@ -6,6 +6,7 @@ * Copyright (C) 1991, 1992 Linus Torvalds * Copyright (C) 1994 - 2000 Ralf Baechle * Copyright (C) 1999, 2000 Silicon Graphics, Inc. + * Copyright (C) 2014, Imagination Technologies Ltd. */ #include <linux/cache.h> #include <linux/context_tracking.h> @@ -30,6 +31,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,8 +48,8 @@ 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 fpu_emulator_save_context(struct sigcontext __user *sc); -extern asmlinkage int fpu_emulator_restore_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 */ @@ -64,17 +66,95 @@ struct rt_sigframe { }; /* + * 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_sigcontext(struct sigcontext __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], 0), + &sc->sc_fpregs[i]); + } + err |= __put_user(current->thread.fpu.fcr31, &sc->sc_fpc_csr); + + return err; +} + +static int copy_fp_from_sigcontext(struct sigcontext __user *sc) +{ + int i; + int err = 0; + u64 fpr_val; + + for (i = 0; i < NUM_FPU_REGS; i++) { + 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_sigcontext(struct sigcontext __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_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(¤t->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); +#ifndef CONFIG_EVA while (1) { lock_fpu_owner(); - err = own_fpu_inatomic(1); - if (!err) - err = save_fp_context(sc); /* this might fail */ - unlock_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; /* touch the sigcontext and try again */ @@ -84,18 +164,44 @@ static int protected_save_fp_context(struct sigcontext __user *sc) if (err) break; /* really bad sigcontext */ } +#else + /* + * EVA does not have FPU EVA instructions so saving fpu context directly + * does not work. + */ + disable_msa(); + lose_fpu(1); + err = save_fp_context(sc); /* this might fail */ + if (save_msa && !err) + err = copy_msa_to_sigcontext(sc); +#endif 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); +#ifndef CONFIG_EVA while (1) { lock_fpu_owner(); - err = own_fpu_inatomic(0); - if (!err) - err = restore_fp_context(sc); /* this might fail */ - unlock_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; /* touch the sigcontext and try again */ @@ -105,6 +211,17 @@ static int protected_restore_fp_context(struct sigcontext __user *sc) if (err) break; /* really bad sigcontext */ } +#else + /* + * EVA does not have FPU EVA instructions so restoring fpu context + * directly does not work. + */ + enable_msa(); + lose_fpu(0); + err = restore_fp_context(sc); /* this might fail */ + if (restore_msa && !err) + err = copy_msa_from_sigcontext(sc); +#endif return err; } @@ -135,7 +252,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) { @@ -143,7 +261,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; } @@ -168,14 +286,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; } @@ -215,9 +333,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); } @@ -591,23 +710,26 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, } #ifdef CONFIG_SMP +#ifndef CONFIG_EVA static int smp_save_fp_context(struct sigcontext __user *sc) { return raw_cpu_has_fpu ? _save_fp_context(sc) - : fpu_emulator_save_context(sc); + : copy_fp_to_sigcontext(sc); } static int smp_restore_fp_context(struct sigcontext __user *sc) { return raw_cpu_has_fpu ? _restore_fp_context(sc) - : fpu_emulator_restore_context(sc); + : copy_fp_from_sigcontext(sc); } +#endif /* CONFIG_EVA */ #endif static int signal_setup(void) { +#ifndef CONFIG_EVA #ifdef CONFIG_SMP /* For now just do the cpu_has_fpu check when the functions are invoked */ save_fp_context = smp_save_fp_context; @@ -617,9 +739,13 @@ static int signal_setup(void) save_fp_context = _save_fp_context; restore_fp_context = _restore_fp_context; } else { - save_fp_context = fpu_emulator_save_context; - restore_fp_context = fpu_emulator_restore_context; + save_fp_context = copy_fp_from_sigcontext; + restore_fp_context = copy_fp_to_sigcontext; } +#endif /* CONFIG_SMP */ +#else + save_fp_context = copy_fp_from_sigcontext;; + restore_fp_context = copy_fp_to_sigcontext; #endif return 0; |