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: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9711/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
Maciej W. Rozycki 2015-04-03 23:27:48 +01:00 committed by Ralf Baechle
parent 232b6ec5df
commit 9b26616c8d
10 changed files with 83 additions and 21 deletions

View File

@ -49,6 +49,8 @@ struct cpuinfo_mips {
unsigned int udelay_val; unsigned int udelay_val;
unsigned int processor_id; unsigned int processor_id;
unsigned int fpu_id; unsigned int fpu_id;
unsigned int fpu_csr31;
unsigned int fpu_msk31;
unsigned int msa_id; unsigned int msa_id;
unsigned int cputype; unsigned int cputype;
int isa_level; int isa_level;

View File

@ -11,6 +11,9 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <uapi/linux/elf.h> #include <uapi/linux/elf.h>
#include <asm/cpu-info.h>
#include <asm/current.h>
/* ELF header e_flags defines. */ /* ELF header e_flags defines. */
/* MIPS architecture level. */ /* MIPS architecture level. */
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ #define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
@ -297,6 +300,8 @@ do { \
mips_set_personality_fp(state); \ mips_set_personality_fp(state); \
\ \
current->thread.abi = &mips_abi; \ current->thread.abi = &mips_abi; \
\
current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31; \
} while (0) } while (0)
#endif /* CONFIG_32BIT */ #endif /* CONFIG_32BIT */
@ -356,6 +361,8 @@ do { \
else \ else \
current->thread.abi = &mips_abi; \ current->thread.abi = &mips_abi; \
\ \
current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31; \
\
p = personality(current->personality); \ p = personality(current->personality); \
if (p != PER_LINUX32 && p != PER_LINUX) \ if (p != PER_LINUX32 && p != PER_LINUX) \
set_personality(PER_LINUX); \ set_personality(PER_LINUX); \

View File

@ -30,7 +30,7 @@
struct sigcontext; struct sigcontext;
struct sigcontext32; struct sigcontext32;
extern void _init_fpu(void); extern void _init_fpu(unsigned int);
extern void _save_fp(struct task_struct *); extern void _save_fp(struct task_struct *);
extern void _restore_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) static inline int init_fpu(void)
{ {
unsigned int fcr31 = current->thread.fpu.fcr31;
int ret = 0; int ret = 0;
if (cpu_has_fpu) { if (cpu_has_fpu) {
@ -192,7 +193,7 @@ static inline int init_fpu(void)
return ret; return ret;
if (!cpu_has_fre) { if (!cpu_has_fre) {
_init_fpu(); _init_fpu(fcr31);
return 0; return 0;
} }
@ -206,7 +207,7 @@ static inline int init_fpu(void)
config5 = clear_c0_config5(MIPS_CONF5_FRE); config5 = clear_c0_config5(MIPS_CONF5_FRE);
enable_fpu_hazard(); enable_fpu_hazard();
_init_fpu(); _init_fpu(fcr31);
/* Restore FRE */ /* Restore FRE */
write_c0_config5(config5); write_c0_config5(config5);

View File

@ -88,8 +88,6 @@ static inline void fpu_emulator_init_fpu(void)
struct task_struct *t = current; struct task_struct *t = current;
int i; int i;
t->thread.fpu.fcr31 = 0;
for (i = 0; i < 32; i++) for (i = 0; i < 32; i++)
set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN); set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN);
} }

View File

@ -32,6 +32,35 @@
#include <asm/spram.h> #include <asm/spram.h>
#include <asm/uaccess.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. * 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; 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 mips_fpu_disabled;
static int __init fpu_disable(char *s) static int __init fpu_disable(char *s)
{ {
boot_cpu_data.options &= ~MIPS_CPU_FPU; boot_cpu_data.options &= ~MIPS_CPU_FPU;
boot_cpu_data.fpu_msk31 = mips_nofpu_msk31;
cpu_set_nofpu_id(&boot_cpu_data); cpu_set_nofpu_id(&boot_cpu_data);
mips_fpu_disabled = 1; 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: case PRID_IMP_R2000:
c->cputype = CPU_R2000; c->cputype = CPU_R2000;
__cpu_name[cpu] = "R2000"; __cpu_name[cpu] = "R2000";
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
MIPS_CPU_NOFPUEX; MIPS_CPU_NOFPUEX;
if (__cpu_has_fpu()) 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; c->cputype = CPU_R3000;
__cpu_name[cpu] = "R3000"; __cpu_name[cpu] = "R3000";
} }
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE | c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
MIPS_CPU_NOFPUEX; MIPS_CPU_NOFPUEX;
if (__cpu_has_fpu()) 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); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
MIPS_CPU_WATCH | MIPS_CPU_VCE | MIPS_CPU_WATCH | MIPS_CPU_VCE |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
@ -671,6 +707,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
break; break;
case PRID_IMP_VR41XX: case PRID_IMP_VR41XX:
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS; c->options = R4K_OPTS;
c->tlbsize = 32; c->tlbsize = 32;
switch (c->processor_id & 0xf0) { 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; c->cputype = CPU_R4300;
__cpu_name[cpu] = "R4300"; __cpu_name[cpu] = "R4300";
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
c->tlbsize = 32; c->tlbsize = 32;
@ -720,6 +758,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
c->cputype = CPU_R4600; c->cputype = CPU_R4600;
__cpu_name[cpu] = "R4600"; __cpu_name[cpu] = "R4600";
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
c->tlbsize = 48; c->tlbsize = 48;
@ -735,11 +774,13 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
c->cputype = CPU_R4650; c->cputype = CPU_R4650;
__cpu_name[cpu] = "R4650"; __cpu_name[cpu] = "R4650";
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC; c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC;
c->tlbsize = 48; c->tlbsize = 48;
break; break;
#endif #endif
case PRID_IMP_TX39: case PRID_IMP_TX39:
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE; c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;
if ((c->processor_id & 0xf0) == (PRID_REV_TX3927 & 0xf0)) { 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; c->cputype = CPU_R4700;
__cpu_name[cpu] = "R4700"; __cpu_name[cpu] = "R4700";
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR | c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
c->tlbsize = 48; c->tlbsize = 48;
@ -773,6 +815,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
c->cputype = CPU_TX49XX; c->cputype = CPU_TX49XX;
__cpu_name[cpu] = "R49XX"; __cpu_name[cpu] = "R49XX";
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
c->options = R4K_OPTS | MIPS_CPU_LLSC; c->options = R4K_OPTS | MIPS_CPU_LLSC;
if (!(c->processor_id & 0x08)) if (!(c->processor_id & 0x08))
c->options |= MIPS_CPU_FPU | MIPS_CPU_32FPR; 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; c->cputype = CPU_R6000;
__cpu_name[cpu] = "R6000"; __cpu_name[cpu] = "R6000";
set_isa(c, MIPS_CPU_ISA_II); set_isa(c, MIPS_CPU_ISA_II);
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
c->tlbsize = 32; c->tlbsize = 32;
@ -822,6 +866,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
c->cputype = CPU_R6000A; c->cputype = CPU_R6000A;
__cpu_name[cpu] = "R6000A"; __cpu_name[cpu] = "R6000A";
set_isa(c, MIPS_CPU_ISA_II); set_isa(c, MIPS_CPU_ISA_II);
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
c->options = MIPS_CPU_TLB | MIPS_CPU_FPU | c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
MIPS_CPU_LLSC; MIPS_CPU_LLSC;
c->tlbsize = 32; 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"; __cpu_name[cpu] = "ICT Loongson-2";
set_elf_platform(cpu, "loongson2e"); set_elf_platform(cpu, "loongson2e");
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
break; break;
case PRID_REV_LOONGSON2F: case PRID_REV_LOONGSON2F:
c->cputype = CPU_LOONGSON2; c->cputype = CPU_LOONGSON2;
__cpu_name[cpu] = "ICT Loongson-2"; __cpu_name[cpu] = "ICT Loongson-2";
set_elf_platform(cpu, "loongson2f"); set_elf_platform(cpu, "loongson2f");
set_isa(c, MIPS_CPU_ISA_III); set_isa(c, MIPS_CPU_ISA_III);
c->fpu_msk31 |= FPU_CSR_CONDX;
break; break;
case PRID_REV_LOONGSON3A: case PRID_REV_LOONGSON3A:
c->cputype = CPU_LOONGSON3; c->cputype = CPU_LOONGSON3;
@ -1335,6 +1382,9 @@ void cpu_probe(void)
c->cputype = CPU_UNKNOWN; c->cputype = CPU_UNKNOWN;
c->writecombine = _CACHE_UNCACHED; 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(); c->processor_id = read_c0_prid();
switch (c->processor_id & PRID_COMP_MASK) { switch (c->processor_id & PRID_COMP_MASK) {
case PRID_COMP_LEGACY: case PRID_COMP_LEGACY:
@ -1393,6 +1443,7 @@ void cpu_probe(void)
if (c->options & MIPS_CPU_FPU) { if (c->options & MIPS_CPU_FPU) {
c->fpu_id = cpu_get_fpu_id(); 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 | if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 | MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
@ -1402,6 +1453,8 @@ void cpu_probe(void)
if (c->fpu_id & MIPS_FPIR_FREP) if (c->fpu_id & MIPS_FPIR_FREP)
c->options |= MIPS_CPU_FRE; c->options |= MIPS_CPU_FRE;
} }
cpu_set_fpu_fcsr_mask(c);
} else } else
cpu_set_nofpu_id(c); cpu_set_nofpu_id(c);

View File

@ -32,6 +32,7 @@
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/cpu-info.h>
#include <asm/dsp.h> #include <asm/dsp.h>
#include <asm/fpu.h> #include <asm/fpu.h>
#include <asm/mipsregs.h> #include <asm/mipsregs.h>
@ -137,6 +138,9 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
{ {
union fpureg *fregs; union fpureg *fregs;
u64 fpr_val; u64 fpr_val;
u32 fcr31;
u32 value;
u32 mask;
int i; int i;
if (!access_ok(VERIFY_READ, data, 33 * 8)) if (!access_ok(VERIFY_READ, data, 33 * 8))
@ -149,8 +153,10 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
set_fpr64(&fregs[i], 0, fpr_val); set_fpr64(&fregs[i], 0, fpr_val);
} }
__get_user(child->thread.fpu.fcr31, data + 64); __get_user(value, data + 64);
child->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; fcr31 = child->thread.fpu.fcr31;
mask = current_cpu_data.fpu_msk31;
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
/* FIR may not be written. */ /* FIR may not be written. */

View File

@ -115,11 +115,9 @@ LEAF(_restore_fp)
* the property that no matter whether considered as single or as double * the property that no matter whether considered as single or as double
* precision represents signaling NANS. * precision represents signaling NANS.
* *
* We initialize fcr31 to rounding to nearest, no exceptions. * The value to initialize fcr31 to comes in $a0.
*/ */
#define FPU_DEFAULT 0x00000000
.set push .set push
SET_HARDFLOAT SET_HARDFLOAT
@ -129,8 +127,7 @@ LEAF(_init_fpu)
or t0, t1 or t0, t1
mtc0 t0, CP0_STATUS mtc0 t0, CP0_STATUS
li t1, FPU_DEFAULT ctc1 a0, fcr31
ctc1 t1, fcr31
li t0, -1 li t0, -1

View File

@ -165,11 +165,9 @@ LEAF(_init_msa_upper)
* the property that no matter whether considered as single or as double * the property that no matter whether considered as single or as double
* precision represents signaling NANS. * precision represents signaling NANS.
* *
* We initialize fcr31 to rounding to nearest, no exceptions. * The value to initialize fcr31 to comes in $a0.
*/ */
#define FPU_DEFAULT 0x00000000
.set push .set push
SET_HARDFLOAT SET_HARDFLOAT
@ -180,8 +178,7 @@ LEAF(_init_fpu)
mtc0 t0, CP0_STATUS mtc0 t0, CP0_STATUS
enable_fpu_hazard enable_fpu_hazard
li t1, FPU_DEFAULT ctc1 a0, fcr31
ctc1 t1, fcr31
li t1, -1 # SNaN li t1, -1 # SNaN

View File

@ -43,7 +43,7 @@ static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
if (!fpu_owned) { if (!fpu_owned) {
set_thread_flag(TIF_USEDFPU); set_thread_flag(TIF_USEDFPU);
if (!used_math()) { if (!used_math()) {
_init_fpu(); _init_fpu(current->thread.fpu.fcr31);
set_used_math(); set_used_math();
} else } else
_restore_fp(current); _restore_fp(current);

View File

@ -908,6 +908,7 @@ static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
{ {
u32 fcr31 = ctx->fcr31; u32 fcr31 = ctx->fcr31;
u32 value; u32 value;
u32 mask;
if (MIPSInst_RT(ir) == 0) if (MIPSInst_RT(ir) == 0)
value = 0; value = 0;
@ -919,9 +920,9 @@ static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
pr_debug("%p gpr[%d]->csr=%08x\n", pr_debug("%p gpr[%d]->csr=%08x\n",
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value); (void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
/* Don't write unsupported bits. */ /* Preserve read-only bits. */
fcr31 = value & mask = current_cpu_data.fpu_msk31;
~(FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008); fcr31 = (value & ~mask) | (fcr31 & mask);
break; break;
case FPCREG_FENR: case FPCREG_FENR: