diff options
author | Maciej W. Rozycki <macro@linux-mips.org> | 2015-04-03 18:27:15 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-04-07 19:10:19 -0400 |
commit | 304acb717e5b67cf56f05bc5b21123758e1f7ea0 (patch) | |
tree | 031d9969f99864c02701d9df056f92c894a56116 /arch/mips/kernel | |
parent | 443c44032a54f9acf027a8e688380fddc809bc19 (diff) |
MIPS: Set `si_code' for SIGFPE signals sent from emulation too
Rework `process_fpemu_return' and move IEEE 754 exception interpretation
there, from `do_fpe'. Record the cause bits set in FCSR before they are
cleared and pass them through to `process_fpemu_return' so as to set
`si_code' correctly too for SIGFPE signals sent from emulation rather
than those issued by hardware with the FPE processor exception only.
For simplicity `mipsr2_decoder' assumes `*fcr31' has been preinitialised
and only sets it to anything if an FPU instruction has been emulated,
which in turn is the only case SIGFPE can be issued for here.
Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org>
Cc: linux-mips@linux-mips.org
Patchwork: https://patchwork.linux-mips.org/patch/9705/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel')
-rw-r--r-- | arch/mips/kernel/mips-r2-to-r6-emul.c | 4 | ||||
-rw-r--r-- | arch/mips/kernel/traps.c | 150 | ||||
-rw-r--r-- | arch/mips/kernel/unaligned.c | 4 |
3 files changed, 88 insertions, 70 deletions
diff --git a/arch/mips/kernel/mips-r2-to-r6-emul.c b/arch/mips/kernel/mips-r2-to-r6-emul.c index 6633fa97d431..f2977f00911b 100644 --- a/arch/mips/kernel/mips-r2-to-r6-emul.c +++ b/arch/mips/kernel/mips-r2-to-r6-emul.c | |||
@@ -898,8 +898,9 @@ static inline int mipsr2_find_op_func(struct pt_regs *regs, u32 inst, | |||
898 | * mipsr2_decoder: Decode and emulate a MIPS R2 instruction | 898 | * mipsr2_decoder: Decode and emulate a MIPS R2 instruction |
899 | * @regs: Process register set | 899 | * @regs: Process register set |
900 | * @inst: Instruction to decode and emulate | 900 | * @inst: Instruction to decode and emulate |
901 | * @fcr31: Floating Point Control and Status Register returned | ||
901 | */ | 902 | */ |
902 | int mipsr2_decoder(struct pt_regs *regs, u32 inst) | 903 | int mipsr2_decoder(struct pt_regs *regs, u32 inst, unsigned long *fcr31) |
903 | { | 904 | { |
904 | int err = 0; | 905 | int err = 0; |
905 | unsigned long vaddr; | 906 | unsigned long vaddr; |
@@ -1168,6 +1169,7 @@ fpu_emul: | |||
1168 | 1169 | ||
1169 | err = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0, | 1170 | err = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0, |
1170 | &fault_addr); | 1171 | &fault_addr); |
1172 | *fcr31 = current->thread.fpu.fcr31; | ||
1171 | 1173 | ||
1172 | /* | 1174 | /* |
1173 | * We can't allow the emulated instruction to leave any of | 1175 | * We can't allow the emulated instruction to leave any of |
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 4a0552dbcf4a..7d5532adc890 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -700,29 +700,60 @@ asmlinkage void do_ov(struct pt_regs *regs) | |||
700 | exception_exit(prev_state); | 700 | exception_exit(prev_state); |
701 | } | 701 | } |
702 | 702 | ||
703 | int process_fpemu_return(int sig, void __user *fault_addr) | 703 | int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcr31) |
704 | { | 704 | { |
705 | if (sig == SIGSEGV || sig == SIGBUS) { | 705 | struct siginfo si = { 0 }; |
706 | struct siginfo si = {0}; | 706 | |
707 | switch (sig) { | ||
708 | case 0: | ||
709 | return 0; | ||
710 | |||
711 | case SIGFPE: | ||
707 | si.si_addr = fault_addr; | 712 | si.si_addr = fault_addr; |
708 | si.si_signo = sig; | 713 | si.si_signo = sig; |
709 | if (sig == SIGSEGV) { | 714 | /* |
710 | down_read(¤t->mm->mmap_sem); | 715 | * Inexact can happen together with Overflow or Underflow. |
711 | if (find_vma(current->mm, (unsigned long)fault_addr)) | 716 | * Respect the mask to deliver the correct exception. |
712 | si.si_code = SEGV_ACCERR; | 717 | */ |
713 | else | 718 | fcr31 &= (fcr31 & FPU_CSR_ALL_E) << |
714 | si.si_code = SEGV_MAPERR; | 719 | (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E)); |
715 | up_read(¤t->mm->mmap_sem); | 720 | if (fcr31 & FPU_CSR_INV_X) |
716 | } else { | 721 | si.si_code = FPE_FLTINV; |
717 | si.si_code = BUS_ADRERR; | 722 | else if (fcr31 & FPU_CSR_DIV_X) |
718 | } | 723 | si.si_code = FPE_FLTDIV; |
724 | else if (fcr31 & FPU_CSR_OVF_X) | ||
725 | si.si_code = FPE_FLTOVF; | ||
726 | else if (fcr31 & FPU_CSR_UDF_X) | ||
727 | si.si_code = FPE_FLTUND; | ||
728 | else if (fcr31 & FPU_CSR_INE_X) | ||
729 | si.si_code = FPE_FLTRES; | ||
730 | else | ||
731 | si.si_code = __SI_FAULT; | ||
719 | force_sig_info(sig, &si, current); | 732 | force_sig_info(sig, &si, current); |
720 | return 1; | 733 | return 1; |
721 | } else if (sig) { | 734 | |
735 | case SIGBUS: | ||
736 | si.si_addr = fault_addr; | ||
737 | si.si_signo = sig; | ||
738 | si.si_code = BUS_ADRERR; | ||
739 | force_sig_info(sig, &si, current); | ||
740 | return 1; | ||
741 | |||
742 | case SIGSEGV: | ||
743 | si.si_addr = fault_addr; | ||
744 | si.si_signo = sig; | ||
745 | down_read(¤t->mm->mmap_sem); | ||
746 | if (find_vma(current->mm, (unsigned long)fault_addr)) | ||
747 | si.si_code = SEGV_ACCERR; | ||
748 | else | ||
749 | si.si_code = SEGV_MAPERR; | ||
750 | up_read(¤t->mm->mmap_sem); | ||
751 | force_sig_info(sig, &si, current); | ||
752 | return 1; | ||
753 | |||
754 | default: | ||
722 | force_sig(sig, current); | 755 | force_sig(sig, current); |
723 | return 1; | 756 | return 1; |
724 | } else { | ||
725 | return 0; | ||
726 | } | 757 | } |
727 | } | 758 | } |
728 | 759 | ||
@@ -730,7 +761,8 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode, | |||
730 | unsigned long old_epc, unsigned long old_ra) | 761 | unsigned long old_epc, unsigned long old_ra) |
731 | { | 762 | { |
732 | union mips_instruction inst = { .word = opcode }; | 763 | union mips_instruction inst = { .word = opcode }; |
733 | void __user *fault_addr = NULL; | 764 | void __user *fault_addr; |
765 | unsigned long fcr31; | ||
734 | int sig; | 766 | int sig; |
735 | 767 | ||
736 | /* If it's obviously not an FP instruction, skip it */ | 768 | /* If it's obviously not an FP instruction, skip it */ |
@@ -760,6 +792,7 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode, | |||
760 | /* Run the emulator */ | 792 | /* Run the emulator */ |
761 | sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, | 793 | sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, |
762 | &fault_addr); | 794 | &fault_addr); |
795 | fcr31 = current->thread.fpu.fcr31; | ||
763 | 796 | ||
764 | /* | 797 | /* |
765 | * We can't allow the emulated instruction to leave any of | 798 | * We can't allow the emulated instruction to leave any of |
@@ -767,12 +800,12 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode, | |||
767 | */ | 800 | */ |
768 | current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; | 801 | current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; |
769 | 802 | ||
770 | /* If something went wrong, signal */ | ||
771 | process_fpemu_return(sig, fault_addr); | ||
772 | |||
773 | /* Restore the hardware register state */ | 803 | /* Restore the hardware register state */ |
774 | own_fpu(1); | 804 | own_fpu(1); |
775 | 805 | ||
806 | /* Send a signal if required. */ | ||
807 | process_fpemu_return(sig, fault_addr, fcr31); | ||
808 | |||
776 | return 0; | 809 | return 0; |
777 | } | 810 | } |
778 | 811 | ||
@@ -782,7 +815,8 @@ static int simulate_fp(struct pt_regs *regs, unsigned int opcode, | |||
782 | asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) | 815 | asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) |
783 | { | 816 | { |
784 | enum ctx_state prev_state; | 817 | enum ctx_state prev_state; |
785 | siginfo_t info = {0}; | 818 | void __user *fault_addr; |
819 | int sig; | ||
786 | 820 | ||
787 | prev_state = exception_enter(); | 821 | prev_state = exception_enter(); |
788 | if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs), | 822 | if (notify_die(DIE_FP, "FP exception", regs, 0, regs_to_trapnr(regs), |
@@ -791,9 +825,6 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) | |||
791 | die_if_kernel("FP exception in kernel code", regs); | 825 | die_if_kernel("FP exception in kernel code", regs); |
792 | 826 | ||
793 | if (fcr31 & FPU_CSR_UNI_X) { | 827 | if (fcr31 & FPU_CSR_UNI_X) { |
794 | int sig; | ||
795 | void __user *fault_addr = NULL; | ||
796 | |||
797 | /* | 828 | /* |
798 | * Unimplemented operation exception. If we've got the full | 829 | * Unimplemented operation exception. If we've got the full |
799 | * software emulator on-board, let's use it... | 830 | * software emulator on-board, let's use it... |
@@ -810,6 +841,7 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) | |||
810 | /* Run the emulator */ | 841 | /* Run the emulator */ |
811 | sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, | 842 | sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 1, |
812 | &fault_addr); | 843 | &fault_addr); |
844 | fcr31 = current->thread.fpu.fcr31; | ||
813 | 845 | ||
814 | /* | 846 | /* |
815 | * We can't allow the emulated instruction to leave any of | 847 | * We can't allow the emulated instruction to leave any of |
@@ -819,35 +851,13 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) | |||
819 | 851 | ||
820 | /* Restore the hardware register state */ | 852 | /* Restore the hardware register state */ |
821 | own_fpu(1); /* Using the FPU again. */ | 853 | own_fpu(1); /* Using the FPU again. */ |
822 | 854 | } else { | |
823 | /* If something went wrong, signal */ | 855 | sig = SIGFPE; |
824 | process_fpemu_return(sig, fault_addr); | 856 | fault_addr = (void __user *) regs->cp0_epc; |
825 | |||
826 | goto out; | ||
827 | } | 857 | } |
828 | 858 | ||
829 | /* | 859 | /* Send a signal if required. */ |
830 | * Inexact can happen together with Overflow or Underflow. | 860 | process_fpemu_return(sig, fault_addr, fcr31); |
831 | * Respect the mask to deliver the correct exception. | ||
832 | */ | ||
833 | fcr31 &= (fcr31 & FPU_CSR_ALL_E) << | ||
834 | (ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E)); | ||
835 | if (fcr31 & FPU_CSR_INV_X) | ||
836 | info.si_code = FPE_FLTINV; | ||
837 | else if (fcr31 & FPU_CSR_DIV_X) | ||
838 | info.si_code = FPE_FLTDIV; | ||
839 | else if (fcr31 & FPU_CSR_OVF_X) | ||
840 | info.si_code = FPE_FLTOVF; | ||
841 | else if (fcr31 & FPU_CSR_UDF_X) | ||
842 | info.si_code = FPE_FLTUND; | ||
843 | else if (fcr31 & FPU_CSR_INE_X) | ||
844 | info.si_code = FPE_FLTRES; | ||
845 | else | ||
846 | info.si_code = __SI_FAULT; | ||
847 | info.si_signo = SIGFPE; | ||
848 | info.si_errno = 0; | ||
849 | info.si_addr = (void __user *) regs->cp0_epc; | ||
850 | force_sig_info(SIGFPE, &info, current); | ||
851 | 861 | ||
852 | out: | 862 | out: |
853 | exception_exit(prev_state); | 863 | exception_exit(prev_state); |
@@ -1050,7 +1060,9 @@ asmlinkage void do_ri(struct pt_regs *regs) | |||
1050 | if (mipsr2_emulation && cpu_has_mips_r6 && | 1060 | if (mipsr2_emulation && cpu_has_mips_r6 && |
1051 | likely(user_mode(regs)) && | 1061 | likely(user_mode(regs)) && |
1052 | likely(get_user(opcode, epc) >= 0)) { | 1062 | likely(get_user(opcode, epc) >= 0)) { |
1053 | status = mipsr2_decoder(regs, opcode); | 1063 | unsigned long fcr31 = 0; |
1064 | |||
1065 | status = mipsr2_decoder(regs, opcode, &fcr31); | ||
1054 | switch (status) { | 1066 | switch (status) { |
1055 | case 0: | 1067 | case 0: |
1056 | case SIGEMT: | 1068 | case SIGEMT: |
@@ -1060,7 +1072,8 @@ asmlinkage void do_ri(struct pt_regs *regs) | |||
1060 | goto no_r2_instr; | 1072 | goto no_r2_instr; |
1061 | default: | 1073 | default: |
1062 | process_fpemu_return(status, | 1074 | process_fpemu_return(status, |
1063 | ¤t->thread.cp0_baduaddr); | 1075 | ¤t->thread.cp0_baduaddr, |
1076 | fcr31); | ||
1064 | task_thread_info(current)->r2_emul_return = 1; | 1077 | task_thread_info(current)->r2_emul_return = 1; |
1065 | return; | 1078 | return; |
1066 | } | 1079 | } |
@@ -1307,10 +1320,13 @@ asmlinkage void do_cpu(struct pt_regs *regs) | |||
1307 | enum ctx_state prev_state; | 1320 | enum ctx_state prev_state; |
1308 | unsigned int __user *epc; | 1321 | unsigned int __user *epc; |
1309 | unsigned long old_epc, old31; | 1322 | unsigned long old_epc, old31; |
1323 | void __user *fault_addr; | ||
1310 | unsigned int opcode; | 1324 | unsigned int opcode; |
1325 | unsigned long fcr31; | ||
1311 | unsigned int cpid; | 1326 | unsigned int cpid; |
1312 | int status, err; | 1327 | int status, err; |
1313 | unsigned long __maybe_unused flags; | 1328 | unsigned long __maybe_unused flags; |
1329 | int sig; | ||
1314 | 1330 | ||
1315 | prev_state = exception_enter(); | 1331 | prev_state = exception_enter(); |
1316 | cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; | 1332 | cpid = (regs->cp0_cause >> CAUSEB_CE) & 3; |
@@ -1384,22 +1400,22 @@ asmlinkage void do_cpu(struct pt_regs *regs) | |||
1384 | case 1: | 1400 | case 1: |
1385 | err = enable_restore_fp_context(0); | 1401 | err = enable_restore_fp_context(0); |
1386 | 1402 | ||
1387 | if (!raw_cpu_has_fpu || err) { | 1403 | if (raw_cpu_has_fpu && !err) |
1388 | int sig; | 1404 | break; |
1389 | void __user *fault_addr = NULL; | ||
1390 | sig = fpu_emulator_cop1Handler(regs, | ||
1391 | ¤t->thread.fpu, | ||
1392 | 0, &fault_addr); | ||
1393 | 1405 | ||
1394 | /* | 1406 | sig = fpu_emulator_cop1Handler(regs, ¤t->thread.fpu, 0, |
1395 | * We can't allow the emulated instruction to leave | 1407 | &fault_addr); |
1396 | * any of the cause bits set in $fcr31. | 1408 | fcr31 = current->thread.fpu.fcr31; |
1397 | */ | ||
1398 | current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; | ||
1399 | 1409 | ||
1400 | if (!process_fpemu_return(sig, fault_addr) && !err) | 1410 | /* |
1401 | mt_ase_fp_affinity(); | 1411 | * We can't allow the emulated instruction to leave |
1402 | } | 1412 | * any of the cause bits set in $fcr31. |
1413 | */ | ||
1414 | current->thread.fpu.fcr31 &= ~FPU_CSR_ALL_X; | ||
1415 | |||
1416 | /* Send a signal if required. */ | ||
1417 | if (!process_fpemu_return(sig, fault_addr, fcr31) && !err) | ||
1418 | mt_ase_fp_affinity(); | ||
1403 | 1419 | ||
1404 | break; | 1420 | break; |
1405 | 1421 | ||
diff --git a/arch/mips/kernel/unaligned.c b/arch/mips/kernel/unaligned.c index bbb69695a0a1..cf51ad36f213 100644 --- a/arch/mips/kernel/unaligned.c +++ b/arch/mips/kernel/unaligned.c | |||
@@ -1076,7 +1076,7 @@ static void emulate_load_store_insn(struct pt_regs *regs, | |||
1076 | own_fpu(1); /* Restore FPU state. */ | 1076 | own_fpu(1); /* Restore FPU state. */ |
1077 | 1077 | ||
1078 | /* Signal if something went wrong. */ | 1078 | /* Signal if something went wrong. */ |
1079 | process_fpemu_return(res, fault_addr); | 1079 | process_fpemu_return(res, fault_addr, 0); |
1080 | 1080 | ||
1081 | if (res == 0) | 1081 | if (res == 0) |
1082 | break; | 1082 | break; |
@@ -1511,7 +1511,7 @@ fpu_emul: | |||
1511 | own_fpu(1); /* restore FPU state */ | 1511 | own_fpu(1); /* restore FPU state */ |
1512 | 1512 | ||
1513 | /* If something went wrong, signal */ | 1513 | /* If something went wrong, signal */ |
1514 | process_fpemu_return(res, fault_addr); | 1514 | process_fpemu_return(res, fault_addr, 0); |
1515 | 1515 | ||
1516 | if (res == 0) | 1516 | if (res == 0) |
1517 | goto success; | 1517 | goto success; |