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

Commit c6a2f467 authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle
Browse files

[MIPS] Check FCSR for pending interrupts, alternative version



Commit 6d667106 is incomplete and misses
non-r4k CPUs.  This patch reverts the commit and fixes in other way.

 o Do FCSR checking in caller of restore_fp_context.
 o Send SIGFPE if the signal handler set any FPU exception bits.

Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent f1dbf8e7
Loading
Loading
Loading
Loading
+0 −16
Original line number Diff line number Diff line
@@ -114,14 +114,6 @@ LEAF(_save_fp_context32)
 */
LEAF(_restore_fp_context)
	EX	lw t0, SC_FPC_CSR(a0)

	/* Fail if the CSR has exceptions pending */
	srl	t1, t0, 5
	and	t1, t0
	andi	t1, 0x1f << 7
	bnez	t1, fault
	 nop

#ifdef CONFIG_64BIT
	EX	ldc1 $f1, SC_FPREGS+8(a0)
	EX	ldc1 $f3, SC_FPREGS+24(a0)
@@ -165,14 +157,6 @@ LEAF(_restore_fp_context)
LEAF(_restore_fp_context32)
	/* Restore an o32 sigcontext.  */
	EX	lw t0, SC32_FPC_CSR(a0)

	/* Fail if the CSR has exceptions pending */
	srl	t1, t0, 5
	and	t1, t0
	andi	t1, 0x1f << 7
	bnez	t1, fault
	 nop

	EX	ldc1 $f0, SC32_FPREGS+0(a0)
	EX	ldc1 $f2, SC32_FPREGS+16(a0)
	EX	ldc1 $f4, SC32_FPREGS+32(a0)
+3 −0
Original line number Diff line number Diff line
@@ -31,4 +31,7 @@ extern void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
 */
extern int install_sigtramp(unsigned int __user *tramp, unsigned int syscall);

/* Check and clear pending FPU exceptions in saved CSR */
extern int fpcsr_pending(unsigned int __user *fpcsr);

#endif	/* __SIGNAL_COMMON_H */
+43 −3
Original line number Diff line number Diff line
@@ -124,6 +124,37 @@ int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
	return err;
}

int fpcsr_pending(unsigned int __user *fpcsr)
{
	int err, sig = 0;
	unsigned int csr, enabled;

	err = __get_user(csr, fpcsr);
	enabled = FPU_CSR_UNI_X | ((csr & FPU_CSR_ALL_E) << 5);
	/*
	 * If the signal handler set some FPU exceptions, clear it and
	 * send SIGFPE.
	 */
	if (csr & enabled) {
		csr &= ~enabled;
		err |= __put_user(csr, fpcsr);
		sig = SIGFPE;
	}
	return err ?: sig;
}

static int
check_and_restore_fp_context(struct sigcontext __user *sc)
{
	int err, sig;

	err = sig = fpcsr_pending(&sc->sc_fpc_csr);
	if (err > 0)
		err = 0;
	err |= restore_fp_context(sc);
	return err ?: sig;
}

int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
{
	unsigned int used_math;
@@ -162,7 +193,8 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
	if (used_math()) {
		/* restore fpu context if we have used it before */
		own_fpu();
		err |= restore_fp_context(sc);
		if (!err)
			err = check_and_restore_fp_context(sc);
	} else {
		/* signal handler may have used FPU.  Give it up. */
		lose_fpu();
@@ -332,6 +364,7 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
{
	struct sigframe __user *frame;
	sigset_t blocked;
	int sig;

	frame = (struct sigframe __user *) regs.regs[29];
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -345,8 +378,11 @@ asmlinkage void sys_sigreturn(nabi_no_regargs struct pt_regs regs)
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if (restore_sigcontext(&regs, &frame->sf_sc))
	sig = restore_sigcontext(&regs, &frame->sf_sc);
	if (sig < 0)
		goto badframe;
	else if (sig)
		force_sig(sig, current);

	/*
	 * Don't let your children do this ...
@@ -368,6 +404,7 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	struct rt_sigframe __user *frame;
	sigset_t set;
	stack_t st;
	int sig;

	frame = (struct rt_sigframe __user *) regs.regs[29];
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -381,8 +418,11 @@ asmlinkage void sys_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
	sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
	if (sig < 0)
		goto badframe;
	else if (sig)
		force_sig(sig, current);

	if (__copy_from_user(&st, &frame->rs_uc.uc_stack, sizeof(st)))
		goto badframe;
+24 −3
Original line number Diff line number Diff line
@@ -220,6 +220,18 @@ static int setup_sigcontext32(struct pt_regs *regs,
	return err;
}

static int
check_and_restore_fp_context32(struct sigcontext32 __user *sc)
{
	int err, sig;

	err = sig = fpcsr_pending(&sc->sc_fpc_csr);
	if (err > 0)
		err = 0;
	err |= restore_fp_context32(sc);
	return err ?: sig;
}

static int restore_sigcontext32(struct pt_regs *regs,
				struct sigcontext32 __user *sc)
{
@@ -255,7 +267,8 @@ static int restore_sigcontext32(struct pt_regs *regs,
	if (used_math()) {
		/* restore fpu context if we have used it before */
		own_fpu();
		err |= restore_fp_context32(sc);
		if (!err)
			err = check_and_restore_fp_context32(sc);
	} else {
		/* signal handler may have used FPU.  Give it up. */
		lose_fpu();
@@ -508,6 +521,7 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
{
	struct sigframe32 __user *frame;
	sigset_t blocked;
	int sig;

	frame = (struct sigframe32 __user *) regs.regs[29];
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -521,8 +535,11 @@ asmlinkage void sys32_sigreturn(nabi_no_regargs struct pt_regs regs)
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if (restore_sigcontext32(&regs, &frame->sf_sc))
	sig = restore_sigcontext32(&regs, &frame->sf_sc);
	if (sig < 0)
		goto badframe;
	else if (sig)
		force_sig(sig, current);

	/*
	 * Don't let your children do this ...
@@ -545,6 +562,7 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	sigset_t set;
	stack_t st;
	s32 sp;
	int sig;

	frame = (struct rt_sigframe32 __user *) regs.regs[29];
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -558,8 +576,11 @@ asmlinkage void sys32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if (restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext))
	sig = restore_sigcontext32(&regs, &frame->rs_uc.uc_mcontext);
	if (sig < 0)
		goto badframe;
	else if (sig)
		force_sig(sig, current);

	/* The ucontext contains a stack32_t, so we must convert!  */
	if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp))
+5 −1
Original line number Diff line number Diff line
@@ -127,6 +127,7 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	sigset_t set;
	stack_t st;
	s32 sp;
	int sig;

	frame = (struct rt_sigframe_n32 __user *) regs.regs[29];
	if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
@@ -140,8 +141,11 @@ asmlinkage void sysn32_rt_sigreturn(nabi_no_regargs struct pt_regs regs)
	recalc_sigpending();
	spin_unlock_irq(&current->sighand->siglock);

	if (restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext))
	sig = restore_sigcontext(&regs, &frame->rs_uc.uc_mcontext);
	if (sig < 0)
		goto badframe;
	else if (sig)
		force_sig(sig, current);

	/* The ucontext contains a stack32_t, so we must convert!  */
	if (__get_user(sp, &frame->rs_uc.uc_stack.ss_sp))