Skip to content

Commit

Permalink
MIPS: Respect the ISA level in FCSR handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Maciej W. Rozycki authored and Ralf Baechle committed Apr 7, 2015
1 parent 232b6ec commit 9b26616
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 21 deletions.
2 changes: 2 additions & 0 deletions arch/mips/include/asm/cpu-info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 7 additions & 0 deletions arch/mips/include/asm/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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); \
Expand Down
7 changes: 4 additions & 3 deletions arch/mips/include/asm/fpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);

Expand Down Expand Up @@ -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) {
Expand All @@ -192,7 +193,7 @@ static inline int init_fpu(void)
return ret;

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

return 0;
}
Expand All @@ -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);
Expand Down
2 changes: 0 additions & 2 deletions arch/mips/include/asm/fpu_emulator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
53 changes: 53 additions & 0 deletions arch/mips/kernel/cpu-probe.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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;

Expand Down Expand Up @@ -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())
Expand All @@ -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())
Expand Down Expand Up @@ -664,13 +699,15 @@ 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;
c->tlbsize = 48;
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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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)) {
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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 |
Expand All @@ -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);

Expand Down
10 changes: 8 additions & 2 deletions arch/mips/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

#include <asm/byteorder.h>
#include <asm/cpu.h>
#include <asm/cpu-info.h>
#include <asm/dsp.h>
#include <asm/fpu.h>
#include <asm/mipsregs.h>
Expand Down Expand Up @@ -137,6 +138,9 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
{
union fpureg *fregs;
u64 fpr_val;
u32 fcr31;
u32 value;
u32 mask;
int i;

if (!access_ok(VERIFY_READ, data, 33 * 8))
Expand All @@ -149,8 +153,10 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
set_fpr64(&fregs[i], 0, fpr_val);
}

__get_user(child->thread.fpu.fcr31, data + 64);
child->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
__get_user(value, data + 64);
fcr31 = child->thread.fpu.fcr31;
mask = current_cpu_data.fpu_msk31;
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);

/* FIR may not be written. */

Expand Down
7 changes: 2 additions & 5 deletions arch/mips/kernel/r2300_switch.S
Original file line number Diff line number Diff line change
Expand Up @@ -115,11 +115,9 @@ LEAF(_restore_fp)
* the property that no matter whether considered as single or as double
* 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_HARDFLOAT

Expand All @@ -129,8 +127,7 @@ LEAF(_init_fpu)
or t0, t1
mtc0 t0, CP0_STATUS

li t1, FPU_DEFAULT
ctc1 t1, fcr31
ctc1 a0, fcr31

li t0, -1

Expand Down
7 changes: 2 additions & 5 deletions arch/mips/kernel/r4k_switch.S
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,9 @@ LEAF(_init_msa_upper)
* the property that no matter whether considered as single or as double
* 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_HARDFLOAT

Expand All @@ -180,8 +178,7 @@ LEAF(_init_fpu)
mtc0 t0, CP0_STATUS
enable_fpu_hazard

li t1, FPU_DEFAULT
ctc1 t1, fcr31
ctc1 a0, fcr31

li t1, -1 # SNaN

Expand Down
2 changes: 1 addition & 1 deletion arch/mips/loongson/loongson-3/cop2-ex.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
if (!fpu_owned) {
set_thread_flag(TIF_USEDFPU);
if (!used_math()) {
_init_fpu();
_init_fpu(current->thread.fpu.fcr31);
set_used_math();
} else
_restore_fp(current);
Expand Down
7 changes: 4 additions & 3 deletions arch/mips/math-emu/cp1emu.c
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
{
u32 fcr31 = ctx->fcr31;
u32 value;
u32 mask;

if (MIPSInst_RT(ir) == 0)
value = 0;
Expand All @@ -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",
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);

/* Don't write unsupported bits. */
fcr31 = value &
~(FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008);
/* Preserve read-only bits. */
mask = current_cpu_data.fpu_msk31;
fcr31 = (value & ~mask) | (fcr31 & mask);
break;

case FPCREG_FENR:
Expand Down

0 comments on commit 9b26616

Please sign in to comment.