aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@imgtec.com>2014-09-11 03:30:20 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-11-24 01:45:07 -0500
commit4227a2d4efc9c84f35826dc4d1e6dc183f6c1c05 (patch)
treebea7e276a3dc164bc215f0a5feb4604305ff0547
parentd175ed2bd6544474dcc99d74f8155c2ba44e8db2 (diff)
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 <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/7683/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r--arch/mips/include/asm/elf.h3
-rw-r--r--arch/mips/include/asm/fpu.h49
-rw-r--r--arch/mips/include/asm/thread_info.h2
-rw-r--r--arch/mips/kernel/traps.c47
-rw-r--r--arch/mips/math-emu/cp1emu.c9
5 files changed, 100 insertions, 10 deletions
diff --git a/arch/mips/include/asm/elf.h b/arch/mips/include/asm/elf.h
index 1d38fe0edd2d..9343529db7bc 100644
--- a/arch/mips/include/asm/elf.h
+++ b/arch/mips/include/asm/elf.h
@@ -269,6 +269,8 @@ do { \
269 else \ 269 else \
270 set_thread_flag(TIF_32BIT_FPREGS); \ 270 set_thread_flag(TIF_32BIT_FPREGS); \
271 \ 271 \
272 clear_thread_flag(TIF_HYBRID_FPREGS); \
273 \
272 if (personality(current->personality) != PER_LINUX) \ 274 if (personality(current->personality) != PER_LINUX) \
273 set_personality(PER_LINUX); \ 275 set_personality(PER_LINUX); \
274 \ 276 \
@@ -325,6 +327,7 @@ do { \
325 \ 327 \
326 clear_thread_flag(TIF_32BIT_REGS); \ 328 clear_thread_flag(TIF_32BIT_REGS); \
327 clear_thread_flag(TIF_32BIT_FPREGS); \ 329 clear_thread_flag(TIF_32BIT_FPREGS); \
330 clear_thread_flag(TIF_HYBRID_FPREGS); \
328 clear_thread_flag(TIF_32BIT_ADDR); \ 331 clear_thread_flag(TIF_32BIT_ADDR); \
329 \ 332 \
330 if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \ 333 if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
diff --git a/arch/mips/include/asm/fpu.h b/arch/mips/include/asm/fpu.h
index dd562414cd5e..994d21939676 100644
--- a/arch/mips/include/asm/fpu.h
+++ b/arch/mips/include/asm/fpu.h
@@ -36,14 +36,16 @@ extern void _restore_fp(struct task_struct *);
36 36
37/* 37/*
38 * This enum specifies a mode in which we want the FPU to operate, for cores 38 * 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 39 * which implement the Status.FR bit. Note that the bottom bit of the value
40 * purposefully have the values 0 & 1 respectively, so that an integer value 40 * purposefully matches the desired value of the Status.FR bit.
41 * of Status.FR can be trivially casted to the corresponding enum fpu_mode.
42 */ 41 */
43enum fpu_mode { 42enum fpu_mode {
44 FPU_32BIT = 0, /* FR = 0 */ 43 FPU_32BIT = 0, /* FR = 0 */
45 FPU_64BIT, /* FR = 1 */ 44 FPU_64BIT, /* FR = 1, FRE = 0 */
46 FPU_AS_IS, 45 FPU_AS_IS,
46 FPU_HYBRID, /* FR = 1, FRE = 1 */
47
48#define FPU_FR_MASK 0x1
47}; 49};
48 50
49static inline int __enable_fpu(enum fpu_mode mode) 51static inline int __enable_fpu(enum fpu_mode mode)
@@ -57,6 +59,14 @@ static inline int __enable_fpu(enum fpu_mode mode)
57 enable_fpu_hazard(); 59 enable_fpu_hazard();
58 return 0; 60 return 0;
59 61
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
60 case FPU_64BIT: 70 case FPU_64BIT:
61#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT)) 71#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_64BIT))
62 /* we only have a 32-bit FPU */ 72 /* we only have a 32-bit FPU */
@@ -64,8 +74,11 @@ static inline int __enable_fpu(enum fpu_mode mode)
64#endif 74#endif
65 /* fall through */ 75 /* fall through */
66 case FPU_32BIT: 76 case FPU_32BIT:
77 /* clear FRE */
78 write_c0_config5(read_c0_config5() & ~MIPS_CONF5_FRE);
79fr_common:
67 /* set CU1 & change FR appropriately */ 80 /* set CU1 & change FR appropriately */
68 fr = (int)mode; 81 fr = (int)mode & FPU_FR_MASK;
69 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0)); 82 change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
70 enable_fpu_hazard(); 83 enable_fpu_hazard();
71 84
@@ -102,13 +115,17 @@ static inline int __own_fpu(void)
102 enum fpu_mode mode; 115 enum fpu_mode mode;
103 int ret; 116 int ret;
104 117
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
106 ret = __enable_fpu(mode); 123 ret = __enable_fpu(mode);
107 if (ret) 124 if (ret)
108 return ret; 125 return ret;
109 126
110 KSTK_STATUS(current) |= ST0_CU1; 127 KSTK_STATUS(current) |= ST0_CU1;
111 if (mode == FPU_64BIT) 128 if (mode == FPU_64BIT || mode == FPU_HYBRID)
112 KSTK_STATUS(current) |= ST0_FR; 129 KSTK_STATUS(current) |= ST0_FR;
113 else /* mode == FPU_32BIT */ 130 else /* mode == FPU_32BIT */
114 KSTK_STATUS(current) &= ~ST0_FR; 131 KSTK_STATUS(current) &= ~ST0_FR;
@@ -166,8 +183,24 @@ static inline int init_fpu(void)
166 183
167 if (cpu_has_fpu) { 184 if (cpu_has_fpu) {
168 ret = __own_fpu(); 185 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
170 _init_fpu(); 198 _init_fpu();
199
200 /* Restore FRE */
201 write_c0_config5(config5);
202 enable_fpu_hazard();
203 }
171 } else 204 } else
172 fpu_emulator_init_fpu(); 205 fpu_emulator_init_fpu();
173 206
diff --git a/arch/mips/include/asm/thread_info.h b/arch/mips/include/asm/thread_info.h
index 7de865805deb..99eea59604e9 100644
--- a/arch/mips/include/asm/thread_info.h
+++ b/arch/mips/include/asm/thread_info.h
@@ -116,6 +116,7 @@ static inline struct thread_info *current_thread_info(void)
116#define TIF_LOAD_WATCH 25 /* If set, load watch registers */ 116#define TIF_LOAD_WATCH 25 /* If set, load watch registers */
117#define TIF_SYSCALL_TRACEPOINT 26 /* syscall tracepoint instrumentation */ 117#define TIF_SYSCALL_TRACEPOINT 26 /* syscall tracepoint instrumentation */
118#define TIF_32BIT_FPREGS 27 /* 32-bit floating point registers */ 118#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 */
119#define TIF_USEDMSA 29 /* MSA has been used this quantum */ 120#define TIF_USEDMSA 29 /* MSA has been used this quantum */
120#define TIF_MSA_CTX_LIVE 30 /* MSA context must be preserved */ 121#define TIF_MSA_CTX_LIVE 30 /* MSA context must be preserved */
121#define TIF_SYSCALL_TRACE 31 /* syscall trace active */ 122#define TIF_SYSCALL_TRACE 31 /* syscall trace active */
@@ -135,6 +136,7 @@ static inline struct thread_info *current_thread_info(void)
135#define _TIF_FPUBOUND (1<<TIF_FPUBOUND) 136#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
136#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH) 137#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)
137#define _TIF_32BIT_FPREGS (1<<TIF_32BIT_FPREGS) 138#define _TIF_32BIT_FPREGS (1<<TIF_32BIT_FPREGS)
139#define _TIF_HYBRID_FPREGS (1<<TIF_HYBRID_FPREGS)
138#define _TIF_USEDMSA (1<<TIF_USEDMSA) 140#define _TIF_USEDMSA (1<<TIF_USEDMSA)
139#define _TIF_MSA_CTX_LIVE (1<<TIF_MSA_CTX_LIVE) 141#define _TIF_MSA_CTX_LIVE (1<<TIF_MSA_CTX_LIVE)
140#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) 142#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c
index 22b19c275044..165c275163b8 100644
--- a/arch/mips/kernel/traps.c
+++ b/arch/mips/kernel/traps.c
@@ -724,6 +724,50 @@ int process_fpemu_return(int sig, void __user *fault_addr)
724 } 724 }
725} 725}
726 726
727static 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
727/* 771/*
728 * XXX Delayed fp exceptions when doing a lazy ctx switch XXX 772 * XXX Delayed fp exceptions when doing a lazy ctx switch XXX
729 */ 773 */
@@ -1016,6 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs)
1016 1060
1017 if (status < 0) 1061 if (status < 0)
1018 status = simulate_sync(regs, opcode); 1062 status = simulate_sync(regs, opcode);
1063
1064 if (status < 0)
1065 status = simulate_fp(regs, opcode, old_epc, old31);
1019 } 1066 }
1020 1067
1021 if (status < 0) 1068 if (status < 0)
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c
index cac529a405b8..9dfcd7fc1bc3 100644
--- a/arch/mips/math-emu/cp1emu.c
+++ b/arch/mips/math-emu/cp1emu.c
@@ -643,9 +643,14 @@ static inline int cop1_64bit(struct pt_regs *xcp)
643 return !test_thread_flag(TIF_32BIT_FPREGS); 643 return !test_thread_flag(TIF_32BIT_FPREGS);
644} 644}
645 645
646static inline bool hybrid_fprs(void)
647{
648 return test_thread_flag(TIF_HYBRID_FPREGS);
649}
650
646#define SIFROMREG(si, x) \ 651#define SIFROMREG(si, x) \
647do { \ 652do { \
648 if (cop1_64bit(xcp)) \ 653 if (cop1_64bit(xcp) && !hybrid_fprs()) \
649 (si) = (int)get_fpr32(&ctx->fpr[x], 0); \ 654 (si) = (int)get_fpr32(&ctx->fpr[x], 0); \
650 else \ 655 else \
651 (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \ 656 (si) = (int)get_fpr32(&ctx->fpr[(x) & ~1], (x) & 1); \
@@ -653,7 +658,7 @@ do { \
653 658
654#define SITOREG(si, x) \ 659#define SITOREG(si, x) \
655do { \ 660do { \
656 if (cop1_64bit(xcp)) { \ 661 if (cop1_64bit(xcp) && !hybrid_fprs()) { \
657 unsigned i; \ 662 unsigned i; \
658 set_fpr32(&ctx->fpr[x], 0, si); \ 663 set_fpr32(&ctx->fpr[x], 0, si); \
659 for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \ 664 for (i = 1; i < ARRAY_SIZE(ctx->fpr[x].val32); i++) \