Skip to content

Commit 9b26616

Browse files
Maciej W. Rozyckiralfbaechle
authored andcommitted
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. [[email protected]: Fixed double inclusion of <asm/current.h>.] Signed-off-by: Maciej W. Rozycki <[email protected]> Cc: [email protected] Patchwork: https://patchwork.linux-mips.org/patch/9711/ Signed-off-by: Ralf Baechle <[email protected]>
1 parent 232b6ec commit 9b26616

File tree

10 files changed

+83
-21
lines changed

10 files changed

+83
-21
lines changed

arch/mips/include/asm/cpu-info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct cpuinfo_mips {
4949
unsigned int udelay_val;
5050
unsigned int processor_id;
5151
unsigned int fpu_id;
52+
unsigned int fpu_csr31;
53+
unsigned int fpu_msk31;
5254
unsigned int msa_id;
5355
unsigned int cputype;
5456
int isa_level;

arch/mips/include/asm/elf.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
#include <linux/fs.h>
1212
#include <uapi/linux/elf.h>
1313

14+
#include <asm/cpu-info.h>
15+
#include <asm/current.h>
16+
1417
/* ELF header e_flags defines. */
1518
/* MIPS architecture level. */
1619
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
@@ -297,6 +300,8 @@ do { \
297300
mips_set_personality_fp(state); \
298301
\
299302
current->thread.abi = &mips_abi; \
303+
\
304+
current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31; \
300305
} while (0)
301306

302307
#endif /* CONFIG_32BIT */
@@ -356,6 +361,8 @@ do { \
356361
else \
357362
current->thread.abi = &mips_abi; \
358363
\
364+
current->thread.fpu.fcr31 = current_cpu_data.fpu_csr31; \
365+
\
359366
p = personality(current->personality); \
360367
if (p != PER_LINUX32 && p != PER_LINUX) \
361368
set_personality(PER_LINUX); \

arch/mips/include/asm/fpu.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
struct sigcontext;
3131
struct sigcontext32;
3232

33-
extern void _init_fpu(void);
33+
extern void _init_fpu(unsigned int);
3434
extern void _save_fp(struct task_struct *);
3535
extern void _restore_fp(struct task_struct *);
3636

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

183183
static inline int init_fpu(void)
184184
{
185+
unsigned int fcr31 = current->thread.fpu.fcr31;
185186
int ret = 0;
186187

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

194195
if (!cpu_has_fre) {
195-
_init_fpu();
196+
_init_fpu(fcr31);
196197

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

209-
_init_fpu();
210+
_init_fpu(fcr31);
210211

211212
/* Restore FRE */
212213
write_c0_config5(config5);

arch/mips/include/asm/fpu_emulator.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,6 @@ static inline void fpu_emulator_init_fpu(void)
8888
struct task_struct *t = current;
8989
int i;
9090

91-
t->thread.fpu.fcr31 = 0;
92-
9391
for (i = 0; i < 32; i++)
9492
set_fpr64(&t->thread.fpu.fpr[i], 0, SIGNALLING_NAN);
9593
}

arch/mips/kernel/cpu-probe.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,35 @@
3232
#include <asm/spram.h>
3333
#include <asm/uaccess.h>
3434

35+
/*
36+
* Determine the FCSR mask for FPU hardware.
37+
*/
38+
static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_mips *c)
39+
{
40+
unsigned long sr, mask, fcsr, fcsr0, fcsr1;
41+
42+
mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
43+
44+
sr = read_c0_status();
45+
__enable_fpu(FPU_AS_IS);
46+
47+
fcsr = read_32bit_cp1_register(CP1_STATUS);
48+
49+
fcsr0 = fcsr & mask;
50+
write_32bit_cp1_register(CP1_STATUS, fcsr0);
51+
fcsr0 = read_32bit_cp1_register(CP1_STATUS);
52+
53+
fcsr1 = fcsr | ~mask;
54+
write_32bit_cp1_register(CP1_STATUS, fcsr1);
55+
fcsr1 = read_32bit_cp1_register(CP1_STATUS);
56+
57+
write_32bit_cp1_register(CP1_STATUS, fcsr);
58+
59+
write_c0_status(sr);
60+
61+
c->fpu_msk31 = ~(fcsr0 ^ fcsr1) & ~mask;
62+
}
63+
3564
/*
3665
* Set the FIR feature flags for the FPU emulator.
3766
*/
@@ -50,11 +79,15 @@ static void cpu_set_nofpu_id(struct cpuinfo_mips *c)
5079
c->fpu_id = value;
5180
}
5281

82+
/* Determined FPU emulator mask to use for the boot CPU with "nofpu". */
83+
static unsigned int mips_nofpu_msk31;
84+
5385
static int mips_fpu_disabled;
5486

5587
static int __init fpu_disable(char *s)
5688
{
5789
boot_cpu_data.options &= ~MIPS_CPU_FPU;
90+
boot_cpu_data.fpu_msk31 = mips_nofpu_msk31;
5891
cpu_set_nofpu_id(&boot_cpu_data);
5992
mips_fpu_disabled = 1;
6093

@@ -597,6 +630,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
597630
case PRID_IMP_R2000:
598631
c->cputype = CPU_R2000;
599632
__cpu_name[cpu] = "R2000";
633+
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
600634
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
601635
MIPS_CPU_NOFPUEX;
602636
if (__cpu_has_fpu())
@@ -616,6 +650,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
616650
c->cputype = CPU_R3000;
617651
__cpu_name[cpu] = "R3000";
618652
}
653+
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
619654
c->options = MIPS_CPU_TLB | MIPS_CPU_3K_CACHE |
620655
MIPS_CPU_NOFPUEX;
621656
if (__cpu_has_fpu())
@@ -664,13 +699,15 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
664699
}
665700

666701
set_isa(c, MIPS_CPU_ISA_III);
702+
c->fpu_msk31 |= FPU_CSR_CONDX;
667703
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
668704
MIPS_CPU_WATCH | MIPS_CPU_VCE |
669705
MIPS_CPU_LLSC;
670706
c->tlbsize = 48;
671707
break;
672708
case PRID_IMP_VR41XX:
673709
set_isa(c, MIPS_CPU_ISA_III);
710+
c->fpu_msk31 |= FPU_CSR_CONDX;
674711
c->options = R4K_OPTS;
675712
c->tlbsize = 32;
676713
switch (c->processor_id & 0xf0) {
@@ -712,6 +749,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
712749
c->cputype = CPU_R4300;
713750
__cpu_name[cpu] = "R4300";
714751
set_isa(c, MIPS_CPU_ISA_III);
752+
c->fpu_msk31 |= FPU_CSR_CONDX;
715753
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
716754
MIPS_CPU_LLSC;
717755
c->tlbsize = 32;
@@ -720,6 +758,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
720758
c->cputype = CPU_R4600;
721759
__cpu_name[cpu] = "R4600";
722760
set_isa(c, MIPS_CPU_ISA_III);
761+
c->fpu_msk31 |= FPU_CSR_CONDX;
723762
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
724763
MIPS_CPU_LLSC;
725764
c->tlbsize = 48;
@@ -735,11 +774,13 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
735774
c->cputype = CPU_R4650;
736775
__cpu_name[cpu] = "R4650";
737776
set_isa(c, MIPS_CPU_ISA_III);
777+
c->fpu_msk31 |= FPU_CSR_CONDX;
738778
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_LLSC;
739779
c->tlbsize = 48;
740780
break;
741781
#endif
742782
case PRID_IMP_TX39:
783+
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
743784
c->options = MIPS_CPU_TLB | MIPS_CPU_TX39_CACHE;
744785

745786
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)
765806
c->cputype = CPU_R4700;
766807
__cpu_name[cpu] = "R4700";
767808
set_isa(c, MIPS_CPU_ISA_III);
809+
c->fpu_msk31 |= FPU_CSR_CONDX;
768810
c->options = R4K_OPTS | MIPS_CPU_FPU | MIPS_CPU_32FPR |
769811
MIPS_CPU_LLSC;
770812
c->tlbsize = 48;
@@ -773,6 +815,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
773815
c->cputype = CPU_TX49XX;
774816
__cpu_name[cpu] = "R49XX";
775817
set_isa(c, MIPS_CPU_ISA_III);
818+
c->fpu_msk31 |= FPU_CSR_CONDX;
776819
c->options = R4K_OPTS | MIPS_CPU_LLSC;
777820
if (!(c->processor_id & 0x08))
778821
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)
814857
c->cputype = CPU_R6000;
815858
__cpu_name[cpu] = "R6000";
816859
set_isa(c, MIPS_CPU_ISA_II);
860+
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
817861
c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
818862
MIPS_CPU_LLSC;
819863
c->tlbsize = 32;
@@ -822,6 +866,7 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
822866
c->cputype = CPU_R6000A;
823867
__cpu_name[cpu] = "R6000A";
824868
set_isa(c, MIPS_CPU_ISA_II);
869+
c->fpu_msk31 |= FPU_CSR_CONDX | FPU_CSR_FS;
825870
c->options = MIPS_CPU_TLB | MIPS_CPU_FPU |
826871
MIPS_CPU_LLSC;
827872
c->tlbsize = 32;
@@ -893,12 +938,14 @@ static inline void cpu_probe_legacy(struct cpuinfo_mips *c, unsigned int cpu)
893938
__cpu_name[cpu] = "ICT Loongson-2";
894939
set_elf_platform(cpu, "loongson2e");
895940
set_isa(c, MIPS_CPU_ISA_III);
941+
c->fpu_msk31 |= FPU_CSR_CONDX;
896942
break;
897943
case PRID_REV_LOONGSON2F:
898944
c->cputype = CPU_LOONGSON2;
899945
__cpu_name[cpu] = "ICT Loongson-2";
900946
set_elf_platform(cpu, "loongson2f");
901947
set_isa(c, MIPS_CPU_ISA_III);
948+
c->fpu_msk31 |= FPU_CSR_CONDX;
902949
break;
903950
case PRID_REV_LOONGSON3A:
904951
c->cputype = CPU_LOONGSON3;
@@ -1335,6 +1382,9 @@ void cpu_probe(void)
13351382
c->cputype = CPU_UNKNOWN;
13361383
c->writecombine = _CACHE_UNCACHED;
13371384

1385+
c->fpu_csr31 = FPU_CSR_RN;
1386+
c->fpu_msk31 = FPU_CSR_RSVD | FPU_CSR_ABS2008 | FPU_CSR_NAN2008;
1387+
13381388
c->processor_id = read_c0_prid();
13391389
switch (c->processor_id & PRID_COMP_MASK) {
13401390
case PRID_COMP_LEGACY:
@@ -1393,6 +1443,7 @@ void cpu_probe(void)
13931443

13941444
if (c->options & MIPS_CPU_FPU) {
13951445
c->fpu_id = cpu_get_fpu_id();
1446+
mips_nofpu_msk31 = c->fpu_msk31;
13961447

13971448
if (c->isa_level & (MIPS_CPU_ISA_M32R1 | MIPS_CPU_ISA_M64R1 |
13981449
MIPS_CPU_ISA_M32R2 | MIPS_CPU_ISA_M64R2 |
@@ -1402,6 +1453,8 @@ void cpu_probe(void)
14021453
if (c->fpu_id & MIPS_FPIR_FREP)
14031454
c->options |= MIPS_CPU_FRE;
14041455
}
1456+
1457+
cpu_set_fpu_fcsr_mask(c);
14051458
} else
14061459
cpu_set_nofpu_id(c);
14071460

arch/mips/kernel/ptrace.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
#include <asm/byteorder.h>
3434
#include <asm/cpu.h>
35+
#include <asm/cpu-info.h>
3536
#include <asm/dsp.h>
3637
#include <asm/fpu.h>
3738
#include <asm/mipsregs.h>
@@ -137,6 +138,9 @@ int ptrace_setfpregs(struct task_struct *child, __u32 __user *data)
137138
{
138139
union fpureg *fregs;
139140
u64 fpr_val;
141+
u32 fcr31;
142+
u32 value;
143+
u32 mask;
140144
int i;
141145

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

152-
__get_user(child->thread.fpu.fcr31, data + 64);
153-
child->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X;
156+
__get_user(value, data + 64);
157+
fcr31 = child->thread.fpu.fcr31;
158+
mask = current_cpu_data.fpu_msk31;
159+
child->thread.fpu.fcr31 = (value & ~mask) | (fcr31 & mask);
154160

155161
/* FIR may not be written. */
156162

arch/mips/kernel/r2300_switch.S

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,11 +115,9 @@ LEAF(_restore_fp)
115115
* the property that no matter whether considered as single or as double
116116
* precision represents signaling NANS.
117117
*
118-
* We initialize fcr31 to rounding to nearest, no exceptions.
118+
* The value to initialize fcr31 to comes in $a0.
119119
*/
120120

121-
#define FPU_DEFAULT 0x00000000
122-
123121
.set push
124122
SET_HARDFLOAT
125123

@@ -129,8 +127,7 @@ LEAF(_init_fpu)
129127
or t0, t1
130128
mtc0 t0, CP0_STATUS
131129

132-
li t1, FPU_DEFAULT
133-
ctc1 t1, fcr31
130+
ctc1 a0, fcr31
134131

135132
li t0, -1
136133

arch/mips/kernel/r4k_switch.S

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,9 @@ LEAF(_init_msa_upper)
165165
* the property that no matter whether considered as single or as double
166166
* precision represents signaling NANS.
167167
*
168-
* We initialize fcr31 to rounding to nearest, no exceptions.
168+
* The value to initialize fcr31 to comes in $a0.
169169
*/
170170

171-
#define FPU_DEFAULT 0x00000000
172-
173171
.set push
174172
SET_HARDFLOAT
175173

@@ -180,8 +178,7 @@ LEAF(_init_fpu)
180178
mtc0 t0, CP0_STATUS
181179
enable_fpu_hazard
182180

183-
li t1, FPU_DEFAULT
184-
ctc1 t1, fcr31
181+
ctc1 a0, fcr31
185182

186183
li t1, -1 # SNaN
187184

arch/mips/loongson/loongson-3/cop2-ex.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
4343
if (!fpu_owned) {
4444
set_thread_flag(TIF_USEDFPU);
4545
if (!used_math()) {
46-
_init_fpu();
46+
_init_fpu(current->thread.fpu.fcr31);
4747
set_used_math();
4848
} else
4949
_restore_fp(current);

arch/mips/math-emu/cp1emu.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -908,6 +908,7 @@ static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
908908
{
909909
u32 fcr31 = ctx->fcr31;
910910
u32 value;
911+
u32 mask;
911912

912913
if (MIPSInst_RT(ir) == 0)
913914
value = 0;
@@ -919,9 +920,9 @@ static inline void cop1_ctc(struct pt_regs *xcp, struct mips_fpu_struct *ctx,
919920
pr_debug("%p gpr[%d]->csr=%08x\n",
920921
(void *)xcp->cp0_epc, MIPSInst_RT(ir), value);
921922

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

927928
case FPCREG_FENR:

0 commit comments

Comments
 (0)