Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bf82cb30 authored by Paul Burton's avatar Paul Burton Committed by Ralf Baechle
Browse files

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: default avatarPaul 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: default avatarRalf Baechle <ralf@linux-mips.org>
parent f1fe2d21
Loading
Loading
Loading
Loading
+118 −0
Original line number Diff line number Diff line
@@ -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
+3 −0
Original line number Diff line number Diff line
@@ -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 */
+212 −15
Original line number Diff line number Diff line
@@ -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(&current->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(&current->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];