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

Commit 9b26616c authored by Maciej W. Rozycki's avatar Maciej W. Rozycki Committed by Ralf Baechle
Browse files

MIPS: Respect the ISA level in FCSR handling



Define the central place the default FCSR value is set from, initialised
in `cpu_probe'.  Determine the FCSR mask applied to values written to
the register with CTC1 in the full emulation mode and via ptrace(2),
according to the ISA level of processor hardware or the writability of
bits 31:18 if actual FPU hardware is used.

Software may rely on FCSR bits whose functions our emulator does not
implement, so it should not allow them to be set or software may get
confused.  For ptrace(2) it's just sanity.

[ralf@linux-mips.org: Fixed double inclusion of <asm/current.h>.]

Signed-off-by: default avatarMaciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9711/


Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 232b6ec5
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -49,6 +49,8 @@ struct cpuinfo_mips {
	unsigned int		udelay_val;
	unsigned int		processor_id;
	unsigned int		fpu_id;
	unsigned int		fpu_csr31;
	unsigned int		fpu_msk31;
	unsigned int		msa_id;
	unsigned int		cputype;
	int			isa_level;
+7 −0
Original line number Diff line number Diff line
@@ -11,6 +11,9 @@
#include <linux/fs.h>
#include <uapi/linux/elf.h>

#include <asm/cpu-info.h>
#include <asm/current.h>

/* ELF header e_flags defines. */
/* MIPS architecture level. */
#define EF_MIPS_ARCH_1		0x00000000	/* -mips1 code.	 */
@@ -297,6 +300,8 @@ do { \
	mips_set_personality_fp(state);					\
									\
	current->thread.abi = &mips_abi;				\
									\
	current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31;		\
} while (0)

#endif /* CONFIG_32BIT */
@@ -356,6 +361,8 @@ do { \
	else								\
		current->thread.abi = &mips_abi;			\
									\
	current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31;		\
									\
	p = personality(current->personality);				\
	if (p != PER_LINUX32 && p != PER_LINUX)				\
		set_personality(PER_LINUX);				\
+4 −3
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@
struct sigcontext;
struct sigcontext32;

extern void _init_fpu(void);
extern void _init_fpu(unsigned int);
extern void _save_fp(struct task_struct *);
extern void _restore_fp(struct task_struct *);

@@ -182,6 +182,7 @@ static inline void lose_fpu(int save)

static inline int init_fpu(void)
{
	unsigned int fcr31 = current->thread.fpu.fcr31;
	int ret = 0;

	if (cpu_has_fpu) {
@@ -192,7 +193,7 @@ static inline int init_fpu(void)
			return ret;

		if (!cpu_has_fre) {
			_init_fpu();
			_init_fpu(fcr31);

			return 0;
		}
@@ -206,7 +207,7 @@ static inline int init_fpu(void)
		config5 = clear_c0_config5(MIPS_CONF5_FRE);
		enable_fpu_hazard();

		_init_fpu();
		_init_fpu(fcr31);

		/* Restore FRE */
		write_c0_config5(config5);
+0 −2
Original line number Diff line number Diff line
@@ -88,8 +88,6 @@ static inline void fpu_emulator_init_fpu(void)
	struct task_struct *t = current;
	int i;

	t->thread.fpu.fcr31 = 0;

	for (i = 0; i < 32; i++)
		set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN);
}
+53 −0
Original line number Diff line number Diff line
@@ -32,6 +32,35 @@
#include <asm/spram.h>
#include <asm/uaccess.h>

/*
 * Determine the FCSR mask for FPU hardware.
 */
static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_mips *c)
{
	unsigned long sr, mask, fcsr, fcsr0, fcsr1;

	mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;

	sr = read_c0_status();
	__enable_fpu(FPU_AS_IS);

	fcsr = read_32bit_cp1_register(CP1_STATUS);

	fcsr0 = fcsr & mask;
	write_32bit_cp1_register(CP1_STATUS, fcsr0);
	fcsr0 = read_32bit_cp1_register(CP1_STATUS);

	fcsr1 = fcsr | ~mask;
	write_32bit_cp1_register(CP1_STATUS, fcsr1);
	fcsr1 = read_32bit_cp1_register(CP1_STATUS);

	write_32bit_cp1_register(CP1_STATUS, fcsr);

	write_c0_status(sr);

	c->fpu_msk31 = ~(fcsr0 ^ fcsr1) & ~mask;
}

/*
 * Set the FIR feature flags for the FPU emulator.
 */
@@ -50,11 +79,15 @@ static void cpu_set_nofpu_id(struct cpuinfo_mips *c)
	c->fpu_id = value;
}

/* Determined FPU emulator mask to use for the boot CPU with "nofpu".  */
static unsigned int mips_nofpu_msk31;

static int mips_fpu_disabled;

static int __init fpu_disable(char *s)
{
	boot_cpu_data.options &= ~MIPS_CPU_FPU;
	boot_cpu_data.fpu_msk31 = mips_nofpu_msk31;
	cpu_set_nofpu_id(&boot_cpu_data);
	mips_fpu_disabled = 1;

@@ -597,6 +630,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
	case PRID_IMP_R2000:
		c->cputype = CPU_R2000;
		__cpu_name[cpu] = "R2000";
		c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
		c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
			     MIPS_CPU_NOFPUEX;
		if (__cpu_has_fpu())
@@ -616,6 +650,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
			c->cputype = CPU_R3000;
			__cpu_name[cpu] = "R3000";
		}
		c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
		c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
			     MIPS_CPU_NOFPUEX;
		if (__cpu_has_fpu())
@@ -664,6 +699,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		}

		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
			     MIPS_CPU_WATCH | MIPS_CPU_VCE |
			     MIPS_CPU_LLSC;
@@ -671,6 +707,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		break;
	case PRID_IMP_VR41XX:
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS;
		c->tlbsize = 32;
		switch (c->processor_id & 0xf0) {
@@ -712,6 +749,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R4300;
		__cpu_name[cpu] = "R4300";
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
			     MIPS_CPU_LLSC;
		c->tlbsize = 32;
@@ -720,6 +758,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R4600;
		__cpu_name[cpu] = "R4600";
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
			     MIPS_CPU_LLSC;
		c->tlbsize = 48;
@@ -735,11 +774,13 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R4650;
		__cpu_name[cpu] = "R4650";
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC;
		c->tlbsize = 48;
		break;
	#endif
	case PRID_IMP_TX39:
		c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
		c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;

		if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) {
@@ -765,6 +806,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R4700;
		__cpu_name[cpu] = "R4700";
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
			     MIPS_CPU_LLSC;
		c->tlbsize = 48;
@@ -773,6 +815,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_TX49XX;
		__cpu_name[cpu] = "R49XX";
		set_isa(c, MIPS_CPU_ISA_III);
		c->fpu_msk31 |= FPU_CSR_CONDX;
		c->options = R4K_OPTS | MIPS_CPU_LLSC;
		if (!(c->processor_id & 0x08))
			c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR;
@@ -814,6 +857,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R6000;
		__cpu_name[cpu] = "R6000";
		set_isa(c, MIPS_CPU_ISA_II);
		c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
		c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
			     MIPS_CPU_LLSC;
		c->tlbsize = 32;
@@ -822,6 +866,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
		c->cputype = CPU_R6000A;
		__cpu_name[cpu] = "R6000A";
		set_isa(c, MIPS_CPU_ISA_II);
		c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
		c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
			     MIPS_CPU_LLSC;
		c->tlbsize = 32;
@@ -893,12 +938,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
			__cpu_name[cpu] = "ICT Loongson-2";
			set_elf_platform(cpu, "loongson2e");
			set_isa(c, MIPS_CPU_ISA_III);
			c->fpu_msk31 |= FPU_CSR_CONDX;
			break;
		case PRID_REV_LOONGSON2F:
			c->cputype = CPU_LOONGSON2;
			__cpu_name[cpu] = "ICT Loongson-2";
			set_elf_platform(cpu, "loongson2f");
			set_isa(c, MIPS_CPU_ISA_III);
			c->fpu_msk31 |= FPU_CSR_CONDX;
			break;
		case PRID_REV_LOONGSON3A:
			c->cputype = CPU_LOONGSON3;
@@ -1335,6 +1382,9 @@ void cpu_probe(void)
	c->cputype	= CPU_UNKNOWN;
	c->writecombine = _CACHE_UNCACHED;

	c->fpu_csr31	= FPU_CSR_RN;
	c->fpu_msk31	= FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008;

	c->processor_id = read_c0_prid();
	switch (c->processor_id & PRID_COMP_MASK) {
	case PRID_COMP_LEGACY:
@@ -1393,6 +1443,7 @@ void cpu_probe(void)

	if (c->options & MIPS_CPU_FPU) {
		c->fpu_id = cpu_get_fpu_id();
		mips_nofpu_msk31 = c->fpu_msk31;

		if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
				    MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
@@ -1402,6 +1453,8 @@ void cpu_probe(void)
			if (c->fpu_id & MIPS_FPIR_FREP)
				c->options |= MIPS_CPU_FRE;
		}

		cpu_set_fpu_fcsr_mask(c);
	} else
		cpu_set_nofpu_id(c);

Loading