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

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

MIPS: Simplify FP context access



This patch replaces the fpureg_t typedef with a "union fpureg" enabling
easier access to 32 & 64 bit values. This allows the access macros used
in cp1emu.c to be simplified somewhat. It will also make it easier to
expand the width of the FP registers as will be done in a future
patch in order to support the 128 bit registers introduced with MSA.

No behavioural change is intended by this patch.

Signed-off-by: default avatarPaul Burton <paul.burton@imgtec.com>
Reviewed-by: default avatarQais Yousef <qais.yousef@imgtec.com>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/6532/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 490b004f
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -180,7 +180,7 @@ static inline void restore_fp(struct task_struct *tsk)
		_restore_fp(tsk);
		_restore_fp(tsk);
}
}


static inline fpureg_t *get_fpu_regs(struct task_struct *tsk)
static inline union fpureg *get_fpu_regs(struct task_struct *tsk)
{
{
	if (tsk == current) {
	if (tsk == current) {
		preempt_disable();
		preempt_disable();
+28 −3
Original line number Original line Diff line number Diff line
@@ -96,8 +96,33 @@ extern unsigned int vced_count, vcei_count;




#define NUM_FPU_REGS	32
#define NUM_FPU_REGS	32
#define FPU_REG_WIDTH	64


typedef __u64 fpureg_t;
union fpureg {
	__u32	val32[FPU_REG_WIDTH / 32];
	__u64	val64[FPU_REG_WIDTH / 64];
};

#ifdef CONFIG_CPU_LITTLE_ENDIAN
# define FPR_IDX(width, idx)	(idx)
#else
# define FPR_IDX(width, idx)	((FPU_REG_WIDTH / (width)) - 1 - (idx))
#endif

#define BUILD_FPR_ACCESS(width) \
static inline u##width get_fpr##width(union fpureg *fpr, unsigned idx)	\
{									\
	return fpr->val##width[FPR_IDX(width, idx)];			\
}									\
									\
static inline void set_fpr##width(union fpureg *fpr, unsigned idx,	\
				  u##width val)				\
{									\
	fpr->val##width[FPR_IDX(width, idx)] = val;			\
}

BUILD_FPR_ACCESS(32)
BUILD_FPR_ACCESS(64)


/*
/*
 * It would be nice to add some more fields for emulator statistics, but there
 * It would be nice to add some more fields for emulator statistics, but there
@@ -107,7 +132,7 @@ typedef __u64 fpureg_t;
 */
 */


struct mips_fpu_struct {
struct mips_fpu_struct {
	fpureg_t	fpr[NUM_FPU_REGS];
	union fpureg	fpr[NUM_FPU_REGS];
	unsigned int	fcr31;
	unsigned int	fcr31;
};
};


@@ -284,7 +309,7 @@ struct thread_struct {
	 * Saved FPU/FPU emulator stuff				\
	 * Saved FPU/FPU emulator stuff				\
	 */							\
	 */							\
	.fpu			= {				\
	.fpu			= {				\
		.fpr		= {0,},				\
		.fpr		= {{{0,},},},			\
		.fcr31		= 0,				\
		.fcr31		= 0,				\
	},							\
	},							\
	/*							\
	/*							\
+17 −22
Original line number Original line Diff line number Diff line
@@ -120,9 +120,10 @@ int ptrace_getfpregs(struct task_struct *child, __u32 __user *data)
		return -EIO;
		return -EIO;


	if (tsk_used_math(child)) {
	if (tsk_used_math(child)) {
		fpureg_t *fregs = get_fpu_regs(child);
		union fpureg *fregs = get_fpu_regs(child);
		for (i = 0; i < 32; i++)
		for (i = 0; i < 32; i++)
			__put_user(fregs[i], i + (__u64 __user *) data);
			__put_user(get_fpr64(&fregs[i], 0),
				   i + (__u64 __user *)data);
	} else {
	} else {
		for (i = 0; i < 32; i++)
		for (i = 0; i < 32; i++)
			__put_user((__u64) -1, i + (__u64 __user *) data);
			__put_user((__u64) -1, i + (__u64 __user *) data);
@@ -158,7 +159,8 @@ int ptrace_getfpregs(struct task_struct *child, __u32 __user *data)


int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
{
{
	fpureg_t *fregs;
	union fpureg *fregs;
	u64 fpr_val;
	int i;
	int i;


	if (!access_ok(VERIFY_READ, data, 33 * 8))
	if (!access_ok(VERIFY_READ, data, 33 * 8))
@@ -166,8 +168,10 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)


	fregs = get_fpu_regs(child);
	fregs = get_fpu_regs(child);


	for (i = 0; i < 32; i++)
	for (i = 0; i < 32; i++) {
		__get_user(fregs[i], i + (__u64 __user *) data);
		__get_user(fpr_val, i + (__u64 __user *)data);
		set_fpr64(&fregs[i], 0, fpr_val);
	}


	__get_user(child->thread.fpu.fcr31, data + 64);
	__get_user(child->thread.fpu.fcr31, data + 64);


@@ -408,7 +412,7 @@ long arch_ptrace(struct task_struct *child, long request,
	/* Read the word at location addr in the USER area. */
	/* Read the word at location addr in the USER area. */
	case PTRACE_PEEKUSR: {
	case PTRACE_PEEKUSR: {
		struct pt_regs *regs;
		struct pt_regs *regs;
		fpureg_t *fregs;
		union fpureg *fregs;
		unsigned long tmp = 0;
		unsigned long tmp = 0;


		regs = task_pt_regs(child);
		regs = task_pt_regs(child);
@@ -433,14 +437,12 @@ long arch_ptrace(struct task_struct *child, long request,
				 * order bits of the values stored in the even
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 * registers - unless we're using r2k_switch.S.
				 */
				 */
				if (addr & 1)
				tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE],
					tmp = fregs[(addr & ~1) - 32] >> 32;
						addr & 1);
				else
					tmp = fregs[addr - 32];
				break;
				break;
			}
			}
#endif
#endif
			tmp = fregs[addr - FPR_BASE];
			tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
			break;
			break;
		case PC:
		case PC:
			tmp = regs->cp0_epc;
			tmp = regs->cp0_epc;
@@ -548,7 +550,7 @@ long arch_ptrace(struct task_struct *child, long request,
			regs->regs[addr] = data;
			regs->regs[addr] = data;
			break;
			break;
		case FPR_BASE ... FPR_BASE + 31: {
		case FPR_BASE ... FPR_BASE + 31: {
			fpureg_t *fregs = get_fpu_regs(child);
			union fpureg *fregs = get_fpu_regs(child);


			if (!tsk_used_math(child)) {
			if (!tsk_used_math(child)) {
				/* FP not yet used  */
				/* FP not yet used  */
@@ -563,19 +565,12 @@ long arch_ptrace(struct task_struct *child, long request,
				 * order bits of the values stored in the even
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 * registers - unless we're using r2k_switch.S.
				 */
				 */
				if (addr & 1) {
				set_fpr32(&fregs[(addr & ~1) - FPR_BASE],
					fregs[(addr & ~1) - FPR_BASE] &=
					  addr & 1, data);
						0xffffffff;
					fregs[(addr & ~1) - FPR_BASE] |=
						((u64)data) << 32;
				} else {
					fregs[addr - FPR_BASE] &= ~0xffffffffLL;
					fregs[addr - FPR_BASE] |= data;
				}
				break;
				break;
			}
			}
#endif
#endif
			fregs[addr - FPR_BASE] = data;
			set_fpr64(&fregs[addr - FPR_BASE], 0, data);
			break;
			break;
		}
		}
		case PC:
		case PC:
+8 −17
Original line number Original line Diff line number Diff line
@@ -80,7 +80,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
	/* Read the word at location addr in the USER area. */
	/* Read the word at location addr in the USER area. */
	case PTRACE_PEEKUSR: {
	case PTRACE_PEEKUSR: {
		struct pt_regs *regs;
		struct pt_regs *regs;
		fpureg_t *fregs;
		union fpureg *fregs;
		unsigned int tmp;
		unsigned int tmp;


		regs = task_pt_regs(child);
		regs = task_pt_regs(child);
@@ -103,13 +103,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
				 * order bits of the values stored in the even
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 * registers - unless we're using r2k_switch.S.
				 */
				 */
				if (addr & 1)
				tmp = get_fpr32(&fregs[(addr & ~1) - FPR_BASE],
					tmp = fregs[(addr & ~1) - 32] >> 32;
						addr & 1);
				else
					tmp = fregs[addr - 32];
				break;
				break;
			}
			}
			tmp = fregs[addr - FPR_BASE];
			tmp = get_fpr32(&fregs[addr - FPR_BASE], 0);
			break;
			break;
		case PC:
		case PC:
			tmp = regs->cp0_epc;
			tmp = regs->cp0_epc;
@@ -233,7 +231,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
			regs->regs[addr] = data;
			regs->regs[addr] = data;
			break;
			break;
		case FPR_BASE ... FPR_BASE + 31: {
		case FPR_BASE ... FPR_BASE + 31: {
			fpureg_t *fregs = get_fpu_regs(child);
			union fpureg *fregs = get_fpu_regs(child);


			if (!tsk_used_math(child)) {
			if (!tsk_used_math(child)) {
				/* FP not yet used  */
				/* FP not yet used  */
@@ -247,18 +245,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
				 * order bits of the values stored in the even
				 * order bits of the values stored in the even
				 * registers - unless we're using r2k_switch.S.
				 * registers - unless we're using r2k_switch.S.
				 */
				 */
				if (addr & 1) {
				set_fpr32(&fregs[(addr & ~1) - FPR_BASE],
					fregs[(addr & ~1) - FPR_BASE] &=
					  addr & 1, data);
						0xffffffff;
					fregs[(addr & ~1) - FPR_BASE] |=
						((u64)data) << 32;
				} else {
					fregs[addr - FPR_BASE] &= ~0xffffffffLL;
					fregs[addr - FPR_BASE] |= data;
				}
				break;
				break;
			}
			}
			fregs[addr - FPR_BASE] = data;
			set_fpr64(&fregs[addr - FPR_BASE], 0, data);
			break;
			break;
		}
		}
		case PC:
		case PC:
+24 −13
Original line number Original line Diff line number Diff line
@@ -876,20 +876,28 @@ static inline int cop1_64bit(struct pt_regs *xcp)
#endif
#endif
}
}


#define SIFROMREG(si, x) ((si) = cop1_64bit(xcp) || !(x & 1) ? \
#define SIFROMREG(si, x) do {						\
			(int)ctx->fpr[x] : (int)(ctx->fpr[x & ~1] >> 32))
	if (cop1_64bit(xcp))						\
		(si) = get_fpr32(&ctx->fpr[x], 0);			\
	else								\
		(si) = get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1);		\
} while (0)

#define SITOREG(si, x) do {						\
	if (cop1_64bit(xcp))						\
		set_fpr32(&ctx->fpr[x], 0, si);				\
	else								\
		set_fpr32(&ctx->fpr[(x) & ~1], (x) & 1, si);		\
} while (0)


#define SITOREG(si, x)	(ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = \
#define SIFROMHREG(si, x)	((si) = get_fpr32(&ctx->fpr[x], 1))
			cop1_64bit(xcp) || !(x & 1) ? \
#define SITOHREG(si, x)		set_fpr32(&ctx->fpr[x], 1, si)
			ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \
			ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32)


#define SIFROMHREG(si, x)	((si) = (int)(ctx->fpr[x] >> 32))
#define DIFROMREG(di, x) \
#define SITOHREG(si, x)		(ctx->fpr[x] = \
	((di) = get_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) == 0)], 0))
				ctx->fpr[x] << 32 >> 32 | (u64)(si) << 32)


#define DIFROMREG(di, x) ((di) = ctx->fpr[x & ~(cop1_64bit(xcp) == 0)])
#define DITOREG(di, x) \
#define DITOREG(di, x)	(ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = (di))
	set_fpr64(&ctx->fpr[(x) & ~(cop1_64bit(xcp) == 0)], 0, di)


#define SPFROMREG(sp, x) SIFROMREG((sp).bits, x)
#define SPFROMREG(sp, x) SIFROMREG((sp).bits, x)
#define SPTOREG(sp, x)	SITOREG((sp).bits, x)
#define SPTOREG(sp, x)	SITOREG((sp).bits, x)
@@ -1960,15 +1968,18 @@ static int fpu_emu(struct pt_regs *xcp, struct mips_fpu_struct *ctx,


#if defined(__mips64)
#if defined(__mips64)
	case l_fmt:{
	case l_fmt:{
		u64 bits;
		DIFROMREG(bits, MIPSInst_FS(ir));

		switch (MIPSInst_FUNC(ir)) {
		switch (MIPSInst_FUNC(ir)) {
		case fcvts_op:
		case fcvts_op:
			/* convert long to single precision real */
			/* convert long to single precision real */
			rv.s = ieee754sp_flong(ctx->fpr[MIPSInst_FS(ir)]);
			rv.s = ieee754sp_flong(bits);
			rfmt = s_fmt;
			rfmt = s_fmt;
			goto copcsr;
			goto copcsr;
		case fcvtd_op:
		case fcvtd_op:
			/* convert long to double precision real */
			/* convert long to double precision real */
			rv.d = ieee754dp_flong(ctx->fpr[MIPSInst_FS(ir)]);
			rv.d = ieee754dp_flong(bits);
			rfmt = d_fmt;
			rfmt = d_fmt;
			goto copcsr;
			goto copcsr;
		default:
		default:
Loading