Skip to content

Commit 4227a2d

Browse files
paulburtonralfbaechle
authored andcommitted
MIPS: Support for hybrid FPRs
Hybrid FPRs is a scheme where scalar FP registers are 64b wide, but accesses to odd indexed single registers use bits 63:32 of the preceeding even indexed 64b register. In this mode all FP code except that built for the plain FP64 ABI can execute correctly. Most notably a combination of FP64A & FP32 code can execute correctly, allowing for existing FP32 binaries to be linked with new FP64A binaries that can make use of 64 bit FP & MSA. Hybrid FPRs are implemented by setting both the FR & FRE bits, trapping & emulating single precision FP instructions (via Reserved Instruction exceptions) whilst allowing others to execute natively. It therefore has a penalty in terms of execution speed, and should only be used when no fully native mode can be. As more binaries are recompiled to use either the FPXX or FP64(A) ABIs, the need for hybrid FPRs should diminish. However in the short to mid term it allows for a gradual transition towards that world, rather than a complete ABI break which is not feasible for some users & not desirable for many. A task will be executed using the hybrid FPR scheme when its TIF_HYBRID_FPREGS flag is set & TIF_32BIT_FPREGS is clear. A further patch will set the flags as necessary, this patch simply adds the infrastructure necessary for the hybrid FPR mode to work. Signed-off-by: Paul Burton <[email protected]> Cc: [email protected] Cc: Alexander Viro <[email protected]> Cc: [email protected] Cc: [email protected] Patchwork: https://patchwork.linux-mips.org/patch/7683/ Signed-off-by: Ralf Baechle <[email protected]>
1 parent d175ed2 commit 4227a2d

File tree

5 files changed

+100
-10
lines changed

5 files changed

+100
-10
lines changed

arch/mips/include/asm/elf.h

+3
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ do { \
269269
else \
270270
set_thread_flag(TIF_32BIT_FPREGS); \
271271
\
272+
clear_thread_flag(TIF_HYBRID_FPREGS); \
273+
\
272274
if (personality(current->personality) != PER_LINUX) \
273275
set_personality(PER_LINUX); \
274276
\
@@ -325,6 +327,7 @@ do { \
325327
\
326328
clear_thread_flag(TIF_32BIT_REGS); \
327329
clear_thread_flag(TIF_32BIT_FPREGS); \
330+
clear_thread_flag(TIF_HYBRID_FPREGS); \
328331
clear_thread_flag(TIF_32BIT_ADDR); \
329332
\
330333
if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \

arch/mips/include/asm/fpu.h

+41-8
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,16 @@ extern void _restore_fp(struct task_struct *);
3636

3737
/*
3838
* This enum specifies a mode in which we want the FPU to operate, for cores
39-
* which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
40-
* purposefully have the values 0 & 1 respectively, so that an integer value
41-
* of Status.FR can be trivially casted to the corresponding enum fpu_mode.
39+
* which implement the Status.FR bit. Note that the bottom bit of the value
40+
* purposefully matches the desired value of the Status.FR bit.
4241
*/
4342
enum fpu_mode {
4443
FPU_32BIT = 0, /* FR = 0 */
45-
FPU_64BIT, /* FR = 1 */
44+
FPU_64BIT, /* FR = 1, FRE = 0 */
4645
FPU_AS_IS,
46+
FPU_HYBRID, /* FR = 1, FRE = 1 */
47+
48+
#define FPU_FR_MASK 0x1
4749
};
4850

4951
static inline int __enable_fpu(enum fpu_mode mode)
@@ -57,15 +59,26 @@ static inline int __enable_fpu(enum fpu_mode mode)
5759
enable_fpu_hazard();
5860
return 0;
5961

62+
case FPU_HYBRID:
63+
if (!cpu_has_fre)
64+
return SIGFPE;
65+
66+
/* set FRE */
67+
write_c0_config5(read_c0_config5() | MIPS_CONF5_FRE);
68+
goto fr_common;
69+
6070
case FPU_64BIT:
6171
#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
6272
/* we only have a 32-bit FPU */
6373
return SIGFPE;
6474
#endif
6575
/* fall through */
6676
case FPU_32BIT:
77+
/* clear FRE */
78+
write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
79+
fr_common:
6780
/* set CU1 & change FR appropriately */
68-
fr = (int)mode;
81+
fr = (int)mode & FPU_FR_MASK;
6982
change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
7083
enable_fpu_hazard();
7184

@@ -102,13 +115,17 @@ static inline int __own_fpu(void)
102115
enum fpu_mode mode;
103116
int ret;
104117

105-
mode = !test_thread_flag(TIF_32BIT_FPREGS);
118+
if (test_thread_flag(TIF_HYBRID_FPREGS))
119+
mode = FPU_HYBRID;
120+
else
121+
mode = !test_thread_flag(TIF_32BIT_FPREGS);
122+
106123
ret = __enable_fpu(mode);
107124
if (ret)
108125
return ret;
109126

110127
KSTK_STATUS(current) |= ST0_CU1;
111-
if (mode == FPU_64BIT)
128+
if (mode == FPU_64BIT || mode == FPU_HYBRID)
112129
KSTK_STATUS(current) |= ST0_FR;
113130
else /* mode == FPU_32BIT */
114131
KSTK_STATUS(current) &= ~ST0_FR;
@@ -166,8 +183,24 @@ static inline int init_fpu(void)
166183

167184
if (cpu_has_fpu) {
168185
ret = __own_fpu();
169-
if (!ret)
186+
if (!ret) {
187+
unsigned int config5 = read_c0_config5();
188+
189+
/*
190+
* Ensure FRE is clear whilst running _init_fpu, since
191+
* single precision FP instructions are used. If FRE
192+
* was set then we'll just end up initialising all 32
193+
* 64b registers.
194+
*/
195+
write_c0_config5(config5 & ~MIPS_CONF5_FRE);
196+
enable_fpu_hazard();
197+
170198
_init_fpu();
199+
200+
/* Restore FRE */
201+
write_c0_config5(config5);
202+
enable_fpu_hazard();
203+
}
171204
} else
172205
fpu_emulator_init_fpu();
173206

arch/mips/include/asm/thread_info.h

+2
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ static inline struct thread_info *current_thread_info(void)
116116
#define TIF_LOAD_WATCH 25 /* If set, load watch registers */
117117
#define TIF_SYSCALL_TRACEPOINT 26 /* syscall tracepoint instrumentation */
118118
#define TIF_32BIT_FPREGS 27 /* 32-bit floating point registers */
119+
#define TIF_HYBRID_FPREGS 28 /* 64b FP registers, odd singles in bits 63:32 of even doubles */
119120
#define TIF_USEDMSA 29 /* MSA has been used this quantum */
120121
#define TIF_MSA_CTX_LIVE 30 /* MSA context must be preserved */
121122
#define TIF_SYSCALL_TRACE 31 /* syscall trace active */
@@ -135,6 +136,7 @@ static inline struct thread_info *current_thread_info(void)
135136
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
136137
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)
137138
#define _TIF_32BIT_FPREGS (1<<TIF_32BIT_FPREGS)
139+
#define _TIF_HYBRID_FPREGS (1<<TIF_HYBRID_FPREGS)
138140
#define _TIF_USEDMSA (1<<TIF_USEDMSA)
139141
#define _TIF_MSA_CTX_LIVE (1<<TIF_MSA_CTX_LIVE)
140142
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)

arch/mips/kernel/traps.c

+47
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,50 @@ int process_fpemu_return(int sig, void __user *fault_addr)
724724
}
725725
}
726726

727+
static int simulate_fp(struct pt_regs *regs, unsigned int opcode,
728+
unsigned long old_epc, unsigned long old_ra)
729+
{
730+
union mips_instruction inst = { .word = opcode };
731+
void __user *fault_addr = NULL;
732+
int sig;
733+
734+
/* If it's obviously not an FP instruction, skip it */
735+
switch (inst.i_format.opcode) {
736+
case cop1_op:
737+
case cop1x_op:
738+
case lwc1_op:
739+
case ldc1_op:
740+
case swc1_op:
741+
case sdc1_op:
742+
break;
743+
744+
default:
745+
return -1;
746+
}
747+
748+
/*
749+
* do_ri skipped over the instruction via compute_return_epc, undo
750+
* that for the FPU emulator.
751+
*/
752+
regs->cp0_epc = old_epc;
753+
regs->regs[31] = old_ra;
754+
755+
/* Save the FP context to struct thread_struct */
756+
lose_fpu(1);
757+
758+
/* Run the emulator */
759+
sig = fpu_emulator_cop1Handler(regs, &current->thread.fpu, 1,
760+
&fault_addr);
761+
762+
/* If something went wrong, signal */
763+
process_fpemu_return(sig, fault_addr);
764+
765+
/* Restore the hardware register state */
766+
own_fpu(1);
767+
768+
return 0;
769+
}
770+
727771
/*
728772
* XXX Delayed fp exceptions when doing a lazy ctx switch XXX
729773
*/
@@ -1016,6 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
10161060

10171061
if (status < 0)
10181062
status = simulate_sync(regs, opcode);
1063+
1064+
if (status < 0)
1065+
status = simulate_fp(regs, opcode, old_epc, old31);
10191066
}
10201067

10211068
if (status < 0)

arch/mips/math-emu/cp1emu.c

+7-2
Original file line numberDiff line numberDiff line change
@@ -643,17 +643,22 @@ static inline int cop1_64bit(struct pt_regs *xcp)
643643
return !test_thread_flag(TIF_32BIT_FPREGS);
644644
}
645645

646+
static inline bool hybrid_fprs(void)
647+
{
648+
return test_thread_flag(TIF_HYBRID_FPREGS);
649+
}
650+
646651
#define SIFROMREG(si, x) \
647652
do { \
648-
if (cop1_64bit(xcp)) \
653+
if (cop1_64bit(xcp) && !hybrid_fprs()) \
649654
(si) = (int)get_fpr32(&ctx->fpr[x], 0); \
650655
else \
651656
(si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \
652657
} while (0)
653658

654659
#define SITOREG(si, x) \
655660
do { \
656-
if (cop1_64bit(xcp)) { \
661+
if (cop1_64bit(xcp) && !hybrid_fprs()) { \
657662
unsigned i; \
658663
set_fpr32(&ctx->fpr[x], 0, si); \
659664
for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \

0 commit comments

Comments
 (0)