MIPS: Save MSA extended context around signals
It is desirable for signal handlers to be allowed to make use of MSA, particularly if auto vectorisation is used when compiling a program. The MSA context must therefore be saved & restored before & after invoking the signal handler. Make use of the extended context structs defined in the preceding patch to save MSA context after the sigframe when appropriate. [ralf@linux-mips.org: Fixed conflicts.] Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Guenter Roeck <linux@roeck-us.net> Cc: Matthew Fortune <matthew.fortune@imgtec.com> Cc: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com> Cc: linux-kernel@vger.kernel.org Cc: Richard Weinberger <richard@nod.at> Cc: James Hogan <james.hogan@imgtec.com> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Markos Chandras <markos.chandras@imgtec.com> Cc: Manuel Lauss <manuel.lauss@gmail.com> Cc: Maciej W. Rozycki <macro@codesourcery.com> Patchwork: https://patchwork.linux-mips.org/patch/10796/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
f1fe2d21f4
commit
bf82cb30c7
|
@ -236,6 +236,124 @@ LEAF(\name)
|
|||
op_msa_wr write_msa_wr_w, ld_w
|
||||
op_msa_wr write_msa_wr_d, ld_d
|
||||
|
||||
#endif /* CONFIG_CPU_HAS_MSA */
|
||||
|
||||
#ifdef CONFIG_CPU_HAS_MSA
|
||||
|
||||
.macro save_msa_upper wr, off, base
|
||||
.set push
|
||||
.set noat
|
||||
#ifdef CONFIG_64BIT
|
||||
copy_u_d \wr, 1
|
||||
EX sd $1, \off(\base)
|
||||
#elif defined(CONFIG_CPU_LITTLE_ENDIAN)
|
||||
copy_u_w \wr, 2
|
||||
EX sw $1, \off(\base)
|
||||
copy_u_w \wr, 3
|
||||
EX sw $1, (\off+4)(\base)
|
||||
#else /* CONFIG_CPU_BIG_ENDIAN */
|
||||
copy_u_w \wr, 2
|
||||
EX sw $1, (\off+4)(\base)
|
||||
copy_u_w \wr, 3
|
||||
EX sw $1, \off(\base)
|
||||
#endif
|
||||
.set pop
|
||||
.endm
|
||||
|
||||
LEAF(_save_msa_all_upper)
|
||||
save_msa_upper 0, 0x00, a0
|
||||
save_msa_upper 1, 0x08, a0
|
||||
save_msa_upper 2, 0x10, a0
|
||||
save_msa_upper 3, 0x18, a0
|
||||
save_msa_upper 4, 0x20, a0
|
||||
save_msa_upper 5, 0x28, a0
|
||||
save_msa_upper 6, 0x30, a0
|
||||
save_msa_upper 7, 0x38, a0
|
||||
save_msa_upper 8, 0x40, a0
|
||||
save_msa_upper 9, 0x48, a0
|
||||
save_msa_upper 10, 0x50, a0
|
||||
save_msa_upper 11, 0x58, a0
|
||||
save_msa_upper 12, 0x60, a0
|
||||
save_msa_upper 13, 0x68, a0
|
||||
save_msa_upper 14, 0x70, a0
|
||||
save_msa_upper 15, 0x78, a0
|
||||
save_msa_upper 16, 0x80, a0
|
||||
save_msa_upper 17, 0x88, a0
|
||||
save_msa_upper 18, 0x90, a0
|
||||
save_msa_upper 19, 0x98, a0
|
||||
save_msa_upper 20, 0xa0, a0
|
||||
save_msa_upper 21, 0xa8, a0
|
||||
save_msa_upper 22, 0xb0, a0
|
||||
save_msa_upper 23, 0xb8, a0
|
||||
save_msa_upper 24, 0xc0, a0
|
||||
save_msa_upper 25, 0xc8, a0
|
||||
save_msa_upper 26, 0xd0, a0
|
||||
save_msa_upper 27, 0xd8, a0
|
||||
save_msa_upper 28, 0xe0, a0
|
||||
save_msa_upper 29, 0xe8, a0
|
||||
save_msa_upper 30, 0xf0, a0
|
||||
save_msa_upper 31, 0xf8, a0
|
||||
jr ra
|
||||
li v0, 0
|
||||
END(_save_msa_all_upper)
|
||||
|
||||
.macro restore_msa_upper wr, off, base
|
||||
.set push
|
||||
.set noat
|
||||
#ifdef CONFIG_64BIT
|
||||
EX ld $1, \off(\base)
|
||||
insert_d \wr, 1
|
||||
#elif defined(CONFIG_CPU_LITTLE_ENDIAN)
|
||||
EX lw $1, \off(\base)
|
||||
insert_w \wr, 2
|
||||
EX lw $1, (\off+4)(\base)
|
||||
insert_w \wr, 3
|
||||
#else /* CONFIG_CPU_BIG_ENDIAN */
|
||||
EX lw $1, (\off+4)(\base)
|
||||
insert_w \wr, 2
|
||||
EX lw $1, \off(\base)
|
||||
insert_w \wr, 3
|
||||
#endif
|
||||
.set pop
|
||||
.endm
|
||||
|
||||
LEAF(_restore_msa_all_upper)
|
||||
restore_msa_upper 0, 0x00, a0
|
||||
restore_msa_upper 1, 0x08, a0
|
||||
restore_msa_upper 2, 0x10, a0
|
||||
restore_msa_upper 3, 0x18, a0
|
||||
restore_msa_upper 4, 0x20, a0
|
||||
restore_msa_upper 5, 0x28, a0
|
||||
restore_msa_upper 6, 0x30, a0
|
||||
restore_msa_upper 7, 0x38, a0
|
||||
restore_msa_upper 8, 0x40, a0
|
||||
restore_msa_upper 9, 0x48, a0
|
||||
restore_msa_upper 10, 0x50, a0
|
||||
restore_msa_upper 11, 0x58, a0
|
||||
restore_msa_upper 12, 0x60, a0
|
||||
restore_msa_upper 13, 0x68, a0
|
||||
restore_msa_upper 14, 0x70, a0
|
||||
restore_msa_upper 15, 0x78, a0
|
||||
restore_msa_upper 16, 0x80, a0
|
||||
restore_msa_upper 17, 0x88, a0
|
||||
restore_msa_upper 18, 0x90, a0
|
||||
restore_msa_upper 19, 0x98, a0
|
||||
restore_msa_upper 20, 0xa0, a0
|
||||
restore_msa_upper 21, 0xa8, a0
|
||||
restore_msa_upper 22, 0xb0, a0
|
||||
restore_msa_upper 23, 0xb8, a0
|
||||
restore_msa_upper 24, 0xc0, a0
|
||||
restore_msa_upper 25, 0xc8, a0
|
||||
restore_msa_upper 26, 0xd0, a0
|
||||
restore_msa_upper 27, 0xd8, a0
|
||||
restore_msa_upper 28, 0xe0, a0
|
||||
restore_msa_upper 29, 0xe8, a0
|
||||
restore_msa_upper 30, 0xf0, a0
|
||||
restore_msa_upper 31, 0xf8, a0
|
||||
jr ra
|
||||
li v0, 0
|
||||
END(_restore_msa_all_upper)
|
||||
|
||||
#endif /* CONFIG_CPU_HAS_MSA */
|
||||
|
||||
.set reorder
|
||||
|
|
|
@ -37,4 +37,7 @@ _save_fp_context(void __user *fpregs, void __user *csr);
|
|||
extern asmlinkage int
|
||||
_restore_fp_context(void __user *fpregs, void __user *csr);
|
||||
|
||||
extern asmlinkage int _save_msa_all_upper(void __user *buf);
|
||||
extern asmlinkage int _restore_msa_all_upper(void __user *buf);
|
||||
|
||||
#endif /* __SIGNAL_COMMON_H */
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <asm/vdso.h>
|
||||
#include <asm/dsp.h>
|
||||
#include <asm/inst.h>
|
||||
#include <asm/msa.h>
|
||||
|
||||
#include "signal-common.h"
|
||||
|
||||
|
@ -124,6 +125,168 @@ static int restore_hw_fp_context(void __user *sc)
|
|||
return _restore_fp_context(fpregs, csr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Extended context handling.
|
||||
*/
|
||||
|
||||
static inline void __user *sc_to_extcontext(void __user *sc)
|
||||
{
|
||||
struct ucontext __user *uc;
|
||||
|
||||
/*
|
||||
* We can just pretend the sigcontext is always embedded in a struct
|
||||
* ucontext here, because the offset from sigcontext to extended
|
||||
* context is the same in the struct sigframe case.
|
||||
*/
|
||||
uc = container_of(sc, struct ucontext, uc_mcontext);
|
||||
return &uc->uc_extcontext;
|
||||
}
|
||||
|
||||
static int save_msa_extcontext(void __user *buf)
|
||||
{
|
||||
struct msa_extcontext __user *msa = buf;
|
||||
uint64_t val;
|
||||
int i, err;
|
||||
|
||||
if (!thread_msa_context_live())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Ensure that we can't lose the live MSA context between checking
|
||||
* for it & writing it to memory.
|
||||
*/
|
||||
preempt_disable();
|
||||
|
||||
if (is_msa_enabled()) {
|
||||
/*
|
||||
* There are no EVA versions of the vector register load/store
|
||||
* instructions, so MSA context has to be saved to kernel memory
|
||||
* and then copied to user memory. The save to kernel memory
|
||||
* should already have been done when handling scalar FP
|
||||
* context.
|
||||
*/
|
||||
BUG_ON(config_enabled(CONFIG_EVA));
|
||||
|
||||
err = __put_user(read_msa_csr(), &msa->csr);
|
||||
err |= _save_msa_all_upper(&msa->wr);
|
||||
|
||||
preempt_enable();
|
||||
} else {
|
||||
preempt_enable();
|
||||
|
||||
err = __put_user(current->thread.fpu.msacsr, &msa->csr);
|
||||
|
||||
for (i = 0; i < NUM_FPU_REGS; i++) {
|
||||
val = get_fpr64(¤t->thread.fpu.fpr[i], 1);
|
||||
err |= __put_user(val, &msa->wr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
err |= __put_user(MSA_EXTCONTEXT_MAGIC, &msa->ext.magic);
|
||||
err |= __put_user(sizeof(*msa), &msa->ext.size);
|
||||
|
||||
return err ? -EFAULT : sizeof(*msa);
|
||||
}
|
||||
|
||||
static int restore_msa_extcontext(void __user *buf, unsigned int size)
|
||||
{
|
||||
struct msa_extcontext __user *msa = buf;
|
||||
unsigned long long val;
|
||||
unsigned int csr;
|
||||
int i, err;
|
||||
|
||||
if (size != sizeof(*msa))
|
||||
return -EINVAL;
|
||||
|
||||
err = get_user(csr, &msa->csr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
preempt_disable();
|
||||
|
||||
if (is_msa_enabled()) {
|
||||
/*
|
||||
* There are no EVA versions of the vector register load/store
|
||||
* instructions, so MSA context has to be copied to kernel
|
||||
* memory and later loaded to registers. The same is true of
|
||||
* scalar FP context, so FPU & MSA should have already been
|
||||
* disabled whilst handling scalar FP context.
|
||||
*/
|
||||
BUG_ON(config_enabled(CONFIG_EVA));
|
||||
|
||||
write_msa_csr(csr);
|
||||
err |= _restore_msa_all_upper(&msa->wr);
|
||||
preempt_enable();
|
||||
} else {
|
||||
preempt_enable();
|
||||
|
||||
current->thread.fpu.msacsr = csr;
|
||||
|
||||
for (i = 0; i < NUM_FPU_REGS; i++) {
|
||||
err |= __get_user(val, &msa->wr[i]);
|
||||
set_fpr64(¤t->thread.fpu.fpr[i], 1, val);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int save_extcontext(void __user *buf)
|
||||
{
|
||||
int sz;
|
||||
|
||||
sz = save_msa_extcontext(buf);
|
||||
if (sz < 0)
|
||||
return sz;
|
||||
buf += sz;
|
||||
|
||||
/* If no context was saved then trivially return */
|
||||
if (!sz)
|
||||
return 0;
|
||||
|
||||
/* Write the end marker */
|
||||
if (__put_user(END_EXTCONTEXT_MAGIC, (u32 *)buf))
|
||||
return -EFAULT;
|
||||
|
||||
sz += sizeof(((struct extcontext *)NULL)->magic);
|
||||
return sz;
|
||||
}
|
||||
|
||||
static int restore_extcontext(void __user *buf)
|
||||
{
|
||||
struct extcontext ext;
|
||||
int err;
|
||||
|
||||
while (1) {
|
||||
err = __get_user(ext.magic, (unsigned int *)buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (ext.magic == END_EXTCONTEXT_MAGIC)
|
||||
return 0;
|
||||
|
||||
err = __get_user(ext.size, (unsigned int *)(buf
|
||||
+ offsetof(struct extcontext, size)));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
switch (ext.magic) {
|
||||
case MSA_EXTCONTEXT_MAGIC:
|
||||
err = restore_msa_extcontext(buf, ext.size);
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
buf += ext.size;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper routines
|
||||
*/
|
||||
|
@ -133,20 +296,17 @@ int protected_save_fp_context(void __user *sc)
|
|||
uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
|
||||
uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
|
||||
uint32_t __user *used_math = sc + abi->off_sc_used_math;
|
||||
unsigned int used;
|
||||
unsigned int used, ext_sz;
|
||||
int err;
|
||||
|
||||
used = used_math() ? USED_FP : 0;
|
||||
if (used) {
|
||||
if (!used)
|
||||
goto fp_done;
|
||||
|
||||
if (!test_thread_flag(TIF_32BIT_FPREGS))
|
||||
used |= USED_FR1;
|
||||
if (test_thread_flag(TIF_HYBRID_FPREGS))
|
||||
used |= USED_HYBRID_FPRS;
|
||||
}
|
||||
|
||||
err = __put_user(used, used_math);
|
||||
if (err || !(used & USED_FP))
|
||||
return err;
|
||||
|
||||
/*
|
||||
* EVA does not have userland equivalents of ldc1 or sdc1, so
|
||||
|
@ -171,10 +331,16 @@ int protected_save_fp_context(void __user *sc)
|
|||
__put_user(0, &fpregs[31]) |
|
||||
__put_user(0, csr);
|
||||
if (err)
|
||||
break; /* really bad sigcontext */
|
||||
return err; /* really bad sigcontext */
|
||||
}
|
||||
|
||||
fp_done:
|
||||
ext_sz = err = save_extcontext(sc_to_extcontext(sc));
|
||||
if (err < 0)
|
||||
return err;
|
||||
used |= ext_sz ? USED_EXTCONTEXT : 0;
|
||||
|
||||
return __put_user(used, used_math);
|
||||
}
|
||||
|
||||
int protected_restore_fp_context(void __user *sc)
|
||||
|
@ -184,7 +350,7 @@ int protected_restore_fp_context(void __user *sc)
|
|||
uint32_t __user *csr = sc + abi->off_sc_fpc_csr;
|
||||
uint32_t __user *used_math = sc + abi->off_sc_used_math;
|
||||
unsigned int used;
|
||||
int err, sig, tmp __maybe_unused;
|
||||
int err, sig = 0, tmp __maybe_unused;
|
||||
|
||||
err = __get_user(used, used_math);
|
||||
conditional_used_math(used & USED_FP);
|
||||
|
@ -193,10 +359,12 @@ int protected_restore_fp_context(void __user *sc)
|
|||
* The signal handler may have used FPU; give it up if the program
|
||||
* doesn't want it following sigreturn.
|
||||
*/
|
||||
if (err || !(used & USED_FP)) {
|
||||
if (err || !(used & USED_FP))
|
||||
lose_fpu(0);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (!(used & USED_FP))
|
||||
goto fp_done;
|
||||
|
||||
err = sig = fpcsr_pending(csr);
|
||||
if (err < 0)
|
||||
|
@ -229,6 +397,10 @@ int protected_restore_fp_context(void __user *sc)
|
|||
break; /* really bad sigcontext */
|
||||
}
|
||||
|
||||
fp_done:
|
||||
if (used & USED_EXTCONTEXT)
|
||||
err |= restore_extcontext(sc_to_extcontext(sc));
|
||||
|
||||
return err ?: sig;
|
||||
}
|
||||
|
||||
|
@ -268,6 +440,28 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
|
|||
return err;
|
||||
}
|
||||
|
||||
static size_t extcontext_max_size(void)
|
||||
{
|
||||
size_t sz = 0;
|
||||
|
||||
/*
|
||||
* The assumption here is that between this point & the point at which
|
||||
* the extended context is saved the size of the context should only
|
||||
* ever be able to shrink (if the task is preempted), but never grow.
|
||||
* That is, what this function returns is an upper bound on the size of
|
||||
* the extended context for the current task at the current time.
|
||||
*/
|
||||
|
||||
if (thread_msa_context_live())
|
||||
sz += sizeof(struct msa_extcontext);
|
||||
|
||||
/* If any context is saved then we'll append the end marker */
|
||||
if (sz)
|
||||
sz += sizeof(((struct extcontext *)NULL)->magic);
|
||||
|
||||
return sz;
|
||||
}
|
||||
|
||||
int fpcsr_pending(unsigned int __user *fpcsr)
|
||||
{
|
||||
int err, sig = 0;
|
||||
|
@ -324,6 +518,9 @@ void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
|
|||
{
|
||||
unsigned long sp;
|
||||
|
||||
/* Leave space for potential extended context */
|
||||
frame_size += extcontext_max_size();
|
||||
|
||||
/* Default to using normal stack */
|
||||
sp = regs->regs[29];
|
||||
|
||||
|
|
Loading…
Reference in New Issue