aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
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 /arch/mips/kernel/traps.c
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>
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r--arch/mips/kernel/traps.c47
1 files changed, 47 insertions, 0 deletions
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)