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

Commit cd21dfcf authored by Ralf Baechle's avatar Ralf Baechle
Browse files

Fix preemption and SMP problems in the FP emulator code.

parent 63b2d2f4
Loading
Loading
Loading
Loading
+17 −2
Original line number Diff line number Diff line
@@ -551,6 +551,14 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)

		preempt_disable();

#ifdef CONFIG_PREEMPT
		if (!is_fpu_owner()) {
			/* We might lose fpu before disabling preempt... */
			own_fpu();
			BUG_ON(!used_math());
			restore_fp(current);
		}
#endif
		/*
	 	 * Unimplemented operation exception.  If we've got the full
		 * software emulator on-board, let's use it...
@@ -562,11 +570,18 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
		 * a bit extreme for what should be an infrequent event.
		 */
		save_fp(current);
		/* Ensure 'resume' not overwrite saved fp context again. */
		lose_fpu();

		preempt_enable();

		/* Run the emulator */
		sig = fpu_emulator_cop1Handler (0, regs,
			&current->thread.fpu.soft);

		preempt_disable();

		own_fpu();	/* Using the FPU again.  */
		/*
		 * We can't allow the emulated instruction to leave any of
		 * the cause bit set in $fcr31.
@@ -712,6 +727,8 @@ asmlinkage void do_cpu(struct pt_regs *regs)
			set_used_math();
		}

		preempt_enable();

		if (!cpu_has_fpu) {
			int sig = fpu_emulator_cop1Handler(0, regs,
						&current->thread.fpu.soft);
@@ -719,8 +736,6 @@ asmlinkage void do_cpu(struct pt_regs *regs)
				force_sig(sig, current);
		}

		preempt_enable();

		return;

	case 2:
+27 −14
Original line number Diff line number Diff line
@@ -79,7 +79,17 @@ struct mips_fpu_emulator_private fpuemuprivate;

/* Convert Mips rounding mode (0..3) to IEEE library modes. */
static const unsigned char ieee_rm[4] = {
	IEEE754_RN, IEEE754_RZ, IEEE754_RU, IEEE754_RD
	[FPU_CSR_RN] = IEEE754_RN,
	[FPU_CSR_RZ] = IEEE754_RZ,
	[FPU_CSR_RU] = IEEE754_RU,
	[FPU_CSR_RD] = IEEE754_RD,
};
/* Convert IEEE library modes to Mips rounding mode (0..3). */
static const unsigned char mips_rm[4] = {
	[IEEE754_RN] = FPU_CSR_RN,
	[IEEE754_RZ] = FPU_CSR_RZ,
	[IEEE754_RD] = FPU_CSR_RD,
	[IEEE754_RU] = FPU_CSR_RU,
};

#if __mips >= 4
@@ -368,6 +378,7 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx)
			}
			if (MIPSInst_RD(ir) == FPCREG_CSR) {
				value = ctx->fcr31;
				value = (value & ~0x3) | mips_rm[value & 0x3];
#ifdef CSRTRACE
				printk("%p gpr[%d]<-csr=%08x\n",
					(void *) (xcp->cp0_epc),
@@ -400,11 +411,10 @@ static int cop1Emulate(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx)
					(void *) (xcp->cp0_epc),
					MIPSInst_RT(ir), value);
#endif
				ctx->fcr31 = value;
				/* copy new rounding mode and
				   flush bit to ieee library state! */
				ieee754_csr.nod = (ctx->fcr31 & 0x1000000) != 0;
				ieee754_csr.rm = ieee_rm[value & 0x3];
				value &= (FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
				ctx->fcr31 &= ~(FPU_CSR_FLUSH | FPU_CSR_ALL_E | FPU_CSR_ALL_S | 0x03);
				/* convert to ieee library modes */
				ctx->fcr31 |= (value & ~0x3) | ieee_rm[value & 0x3];
			}
			if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
				return SIGFPE;
@@ -570,7 +580,7 @@ static const unsigned char cmptab[8] = {
static ieee754##p fpemu_##p##_##name (ieee754##p r, ieee754##p s, \
    ieee754##p t) \
{ \
	struct ieee754_csr ieee754_csr_save; \
	struct _ieee754_csr ieee754_csr_save; \
	s = f1 (s, t); \
	ieee754_csr_save = ieee754_csr; \
	s = f2 (s, r); \
@@ -699,8 +709,6 @@ static int fpux_emu(struct pt_regs *xcp, struct mips_fpu_soft_struct *ctx,
				rcsr |= FPU_CSR_INV_X | FPU_CSR_INV_S;

			ctx->fcr31 = (ctx->fcr31 & ~FPU_CSR_ALL_X) | rcsr;
			if (ieee754_csr.nod)
				ctx->fcr31 |= 0x1000000;
			if ((ctx->fcr31 >> 5) & ctx->fcr31 & FPU_CSR_ALL_E) {
				/*printk ("SIGFPE: fpu csr = %08x\n",
				   ctx->fcr31); */
@@ -1297,12 +1305,17 @@ int fpu_emulator_cop1Handler(int xcptno, struct pt_regs *xcp,
		if (insn == 0)
			xcp->cp0_epc += 4;	/* skip nops */
		else {
			/* Update ieee754_csr. Only relevant if we have a
			   h/w FPU */
			ieee754_csr.nod = (ctx->fcr31 & 0x1000000) != 0;
			ieee754_csr.rm = ieee_rm[ctx->fcr31 & 0x3];
			ieee754_csr.cx = (ctx->fcr31 >> 12) & 0x1f;
			/*
			 * The 'ieee754_csr' is an alias of
			 * ctx->fcr31.  No need to copy ctx->fcr31 to
			 * ieee754_csr.  But ieee754_csr.rm is ieee
			 * library modes. (not mips rounding mode)
			 */
			/* convert to ieee library modes */
			ieee754_csr.rm = ieee_rm[ieee754_csr.rm];
			sig = cop1Emulate(xcp, ctx);
			/* revert to mips rounding mode */
			ieee754_csr.rm = mips_rm[ieee754_csr.rm];
		}

		if (cpu_has_fpu)
+1 −1
Original line number Diff line number Diff line
@@ -37,7 +37,7 @@ static const unsigned table[] = {

ieee754dp ieee754dp_sqrt(ieee754dp x)
{
	struct ieee754_csr oldcsr;
	struct _ieee754_csr oldcsr;
	ieee754dp y, z, t;
	unsigned scalx, yh;
	COMPXDP;
+0 −4
Original line number Diff line number Diff line
@@ -50,10 +50,6 @@ const char *const ieee754_cname[] = {
	"SNaN",
};

/* the control status register
*/
struct ieee754_csr ieee754_csr;

/* special constants
*/

+77 −98
Original line number Diff line number Diff line
/* single and double precision fp ops
 * missing extended precision.
*/
/*
 * MIPS floating point support
 * Copyright (C) 1994-2000 Algorithmics Ltd.
 * http://www.algor.co.uk
 *
 * ########################################################################
 *
 *  This program is free software; you can distribute it and/or modify it
 *  under the terms of the GNU General Public License (Version 2) as
 *  published by the Free Software Foundation.
@@ -21,20 +16,16 @@
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
 *
 * ########################################################################
 */

/**************************************************************************
 *  Nov 7, 2000
 *  Modification to allow integration with Linux kernel
 *
 *  Kevin D. Kissell, kevink@mips.com and Carsten Langgard, carstenl@mips.com
 *  Copyright (C) 2000 MIPS Technologies, Inc. All rights reserved.
 *************************************************************************/
 */

#ifdef __KERNEL__
/* Going from Algorithmics to Linux native environment, add this */
#include <asm/byteorder.h>
#include <linux/types.h>
#include <linux/sched.h>

/*
 * Not very pretty, but the Linux kernel's normal va_list definition
@@ -44,18 +35,7 @@
#include <stdarg.h>
#endif

#else

/* Note that __KERNEL__ is taken to mean Linux kernel */

#if #system(OpenBSD)
#include <machine/types.h>
#endif
#include <machine/endian.h>

#endif				/* __KERNEL__ */

#if (defined(BYTE_ORDER) && BYTE_ORDER == LITTLE_ENDIAN) || defined(__MIPSEL__)
#ifdef __LITTLE_ENDIAN
struct ieee754dp_konst {
	unsigned mantlo:32;
	unsigned manthi:20;
@@ -86,13 +66,14 @@ typedef union _ieee754sp {
} ieee754sp;
#endif

#if (defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN) || defined(__MIPSEB__)
#ifdef __BIG_ENDIAN
struct ieee754dp_konst {
	unsigned sign:1;
	unsigned bexp:11;
	unsigned manthi:20;
	unsigned mantlo:32;
};

typedef union _ieee754dp {
	struct ieee754dp_konst oparts;
	struct {
@@ -251,93 +232,109 @@ extern const char *const ieee754_cname[];

/* "normal" comparisons
*/
static __inline int ieee754sp_eq(ieee754sp x, ieee754sp y)
static inline int ieee754sp_eq(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y, IEEE754_CEQ, 0);
}

static __inline int ieee754sp_ne(ieee754sp x, ieee754sp y)
static inline int ieee754sp_ne(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y,
			     IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 0);
}

static __inline int ieee754sp_lt(ieee754sp x, ieee754sp y)
static inline int ieee754sp_lt(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y, IEEE754_CLT, 0);
}

static __inline int ieee754sp_le(ieee754sp x, ieee754sp y)
static inline int ieee754sp_le(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ, 0);
}

static __inline int ieee754sp_gt(ieee754sp x, ieee754sp y)
static inline int ieee754sp_gt(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y, IEEE754_CGT, 0);
}


static __inline int ieee754sp_ge(ieee754sp x, ieee754sp y)
static inline int ieee754sp_ge(ieee754sp x, ieee754sp y)
{
	return ieee754sp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ, 0);
}

static __inline int ieee754dp_eq(ieee754dp x, ieee754dp y)
static inline int ieee754dp_eq(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y, IEEE754_CEQ, 0);
}

static __inline int ieee754dp_ne(ieee754dp x, ieee754dp y)
static inline int ieee754dp_ne(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y,
			     IEEE754_CLT | IEEE754_CGT | IEEE754_CUN, 0);
}

static __inline int ieee754dp_lt(ieee754dp x, ieee754dp y)
static inline int ieee754dp_lt(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y, IEEE754_CLT, 0);
}

static __inline int ieee754dp_le(ieee754dp x, ieee754dp y)
static inline int ieee754dp_le(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y, IEEE754_CLT | IEEE754_CEQ, 0);
}

static __inline int ieee754dp_gt(ieee754dp x, ieee754dp y)
static inline int ieee754dp_gt(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y, IEEE754_CGT, 0);
}

static __inline int ieee754dp_ge(ieee754dp x, ieee754dp y)
static inline int ieee754dp_ge(ieee754dp x, ieee754dp y)
{
	return ieee754dp_cmp(x, y, IEEE754_CGT | IEEE754_CEQ, 0);
}


/* like strtod
/*
 * Like strtod
 */
ieee754dp ieee754dp_fstr(const char *s, char **endp);
char *ieee754dp_tstr(ieee754dp x, int prec, int fmt, int af);


/* the control status register
/*
 * The control status register
 */
struct ieee754_csr {
	unsigned pad:13;
struct _ieee754_csr {
#ifdef __BIG_ENDIAN
	unsigned pad0:7;
	unsigned nod:1;		/* set 1 for no denormalised numbers */
	unsigned cx:5;		/* exceptions this operation */
	unsigned c:1;		/* condition */
	unsigned pad1:5;
	unsigned cx:6;		/* exceptions this operation */
	unsigned mx:5;		/* exception enable  mask */
	unsigned sx:5;		/* exceptions total */
	unsigned rm:2;		/* current rounding mode */
#endif
#ifdef __LITTLE_ENDIAN
	unsigned rm:2;		/* current rounding mode */
	unsigned sx:5;		/* exceptions total */
	unsigned mx:5;		/* exception enable  mask */
	unsigned cx:6;		/* exceptions this operation */
	unsigned pad1:5;
	unsigned c:1;		/* condition */
	unsigned nod:1;		/* set 1 for no denormalised numbers */
	unsigned pad0:7;
#endif
};
extern struct ieee754_csr ieee754_csr;
#define ieee754_csr (*(struct _ieee754_csr *)(&current->thread.fpu.soft.fcr31))

static __inline unsigned ieee754_getrm(void)
static inline unsigned ieee754_getrm(void)
{
	return (ieee754_csr.rm);
}
static __inline unsigned ieee754_setrm(unsigned rm)
static inline unsigned ieee754_setrm(unsigned rm)
{
	return (ieee754_csr.rm = rm);
}
@@ -345,14 +342,14 @@ static __inline unsigned ieee754_setrm(unsigned rm)
/*
 * get current exceptions
 */
static __inline unsigned ieee754_getcx(void)
static inline unsigned ieee754_getcx(void)
{
	return (ieee754_csr.cx);
}

/* test for current exception condition
 */
static __inline int ieee754_cxtest(unsigned n)
static inline int ieee754_cxtest(unsigned n)
{
	return (ieee754_csr.cx & n);
}
@@ -360,21 +357,21 @@ static __inline int ieee754_cxtest(unsigned n)
/*
 * get sticky exceptions
 */
static __inline unsigned ieee754_getsx(void)
static inline unsigned ieee754_getsx(void)
{
	return (ieee754_csr.sx);
}

/* clear sticky conditions
*/
static __inline unsigned ieee754_clrsx(void)
static inline unsigned ieee754_clrsx(void)
{
	return (ieee754_csr.sx = 0);
}

/* test for sticky exception condition
 */
static __inline int ieee754_sxtest(unsigned n)
static inline int ieee754_sxtest(unsigned n)
{
	return (ieee754_csr.sx & n);
}
@@ -406,51 +403,33 @@ extern const struct ieee754sp_konst __ieee754sp_spcvals[];
#define ieee754dp_spcvals ((const ieee754dp *)__ieee754dp_spcvals)
#define ieee754sp_spcvals ((const ieee754sp *)__ieee754sp_spcvals)

/* return infinity with given sign
/*
 * Return infinity with given sign
 */
#define ieee754dp_inf(sn)	\
  (ieee754dp_spcvals[IEEE754_SPCVAL_PINFINITY+(sn)])
#define ieee754dp_zero(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PZERO+(sn)])
#define ieee754dp_one(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PONE+(sn)])
#define ieee754dp_ten(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PTEN+(sn)])
#define ieee754dp_indef() \
  (ieee754dp_spcvals[IEEE754_SPCVAL_INDEF])
#define ieee754dp_max(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PMAX+(sn)])
#define ieee754dp_min(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PMIN+(sn)])
#define ieee754dp_mind(sn) \
  (ieee754dp_spcvals[IEEE754_SPCVAL_PMIND+(sn)])
#define ieee754dp_1e31() \
  (ieee754dp_spcvals[IEEE754_SPCVAL_P1E31])
#define ieee754dp_1e63() \
  (ieee754dp_spcvals[IEEE754_SPCVAL_P1E63])

#define ieee754sp_inf(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PINFINITY+(sn)])
#define ieee754sp_zero(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PZERO+(sn)])
#define ieee754sp_one(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PONE+(sn)])
#define ieee754sp_ten(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PTEN+(sn)])
#define ieee754sp_indef() \
  (ieee754sp_spcvals[IEEE754_SPCVAL_INDEF])
#define ieee754sp_max(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PMAX+(sn)])
#define ieee754sp_min(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PMIN+(sn)])
#define ieee754sp_mind(sn) \
  (ieee754sp_spcvals[IEEE754_SPCVAL_PMIND+(sn)])
#define ieee754sp_1e31() \
  (ieee754sp_spcvals[IEEE754_SPCVAL_P1E31])
#define ieee754sp_1e63() \
  (ieee754sp_spcvals[IEEE754_SPCVAL_P1E63])

/* indefinite integer value
#define ieee754dp_inf(sn)     (ieee754dp_spcvals[IEEE754_SPCVAL_PINFINITY+(sn)])
#define ieee754dp_zero(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PZERO+(sn)])
#define ieee754dp_one(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PONE+(sn)])
#define ieee754dp_ten(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PTEN+(sn)])
#define ieee754dp_indef()	(ieee754dp_spcvals[IEEE754_SPCVAL_INDEF])
#define ieee754dp_max(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PMAX+(sn)])
#define ieee754dp_min(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PMIN+(sn)])
#define ieee754dp_mind(sn)	(ieee754dp_spcvals[IEEE754_SPCVAL_PMIND+(sn)])
#define ieee754dp_1e31()	(ieee754dp_spcvals[IEEE754_SPCVAL_P1E31])
#define ieee754dp_1e63()	(ieee754dp_spcvals[IEEE754_SPCVAL_P1E63])

#define ieee754sp_inf(sn)     (ieee754sp_spcvals[IEEE754_SPCVAL_PINFINITY+(sn)])
#define ieee754sp_zero(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PZERO+(sn)])
#define ieee754sp_one(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PONE+(sn)])
#define ieee754sp_ten(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PTEN+(sn)])
#define ieee754sp_indef()	(ieee754sp_spcvals[IEEE754_SPCVAL_INDEF])
#define ieee754sp_max(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PMAX+(sn)])
#define ieee754sp_min(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PMIN+(sn)])
#define ieee754sp_mind(sn)	(ieee754sp_spcvals[IEEE754_SPCVAL_PMIND+(sn)])
#define ieee754sp_1e31()	(ieee754sp_spcvals[IEEE754_SPCVAL_P1E31])
#define ieee754sp_1e63()	(ieee754sp_spcvals[IEEE754_SPCVAL_P1E63])

/*
 * Indefinite integer value
 */
#define ieee754si_indef()	INT_MAX
#ifdef LONG_LONG_MAX