diff options
author | Kumar Gala <galak@kernel.crashing.org> | 2007-02-07 02:47:59 -0500 |
---|---|---|
committer | Kumar Gala <galak@kernel.crashing.org> | 2007-02-07 02:47:59 -0500 |
commit | 5fad293bcbd48d9a2370020cf60e4b4a42559b12 (patch) | |
tree | f6bb8c466410dead0471583ec874ca3244c3902b /arch/powerpc/kernel/traps.c | |
parent | 04903a30a327513b97c1271fc6bc4dad6502d1b8 (diff) |
[POWERPC] Fixup error handling when emulating a floating point instruction
When we do full FP emulation its possible that we need to post a SIGFPE based
on the results of the emulation. The previous code ignored this case completely.
Additionally, the Soft_emulate_8xx case had two issues. One, we should never
generate a SIGFPE since the code only does data movement. Second, we were
interpreting the return codes incorrectly, it returns 0 on success, 1 on
illop and -EFAULT on a data access error.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
-rw-r--r-- | arch/powerpc/kernel/traps.c | 82 |
1 files changed, 60 insertions, 22 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index f038caa3c2b9..dcc6f159fd94 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c | |||
@@ -535,34 +535,40 @@ static void emulate_single_step(struct pt_regs *regs) | |||
535 | } | 535 | } |
536 | } | 536 | } |
537 | 537 | ||
538 | static void parse_fpe(struct pt_regs *regs) | 538 | static inline int __parse_fpscr(unsigned long fpscr) |
539 | { | 539 | { |
540 | int code = 0; | 540 | int ret = 0; |
541 | unsigned long fpscr; | ||
542 | |||
543 | flush_fp_to_thread(current); | ||
544 | |||
545 | fpscr = current->thread.fpscr.val; | ||
546 | 541 | ||
547 | /* Invalid operation */ | 542 | /* Invalid operation */ |
548 | if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX)) | 543 | if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX)) |
549 | code = FPE_FLTINV; | 544 | ret = FPE_FLTINV; |
550 | 545 | ||
551 | /* Overflow */ | 546 | /* Overflow */ |
552 | else if ((fpscr & FPSCR_OE) && (fpscr & FPSCR_OX)) | 547 | else if ((fpscr & FPSCR_OE) && (fpscr & FPSCR_OX)) |
553 | code = FPE_FLTOVF; | 548 | ret = FPE_FLTOVF; |
554 | 549 | ||
555 | /* Underflow */ | 550 | /* Underflow */ |
556 | else if ((fpscr & FPSCR_UE) && (fpscr & FPSCR_UX)) | 551 | else if ((fpscr & FPSCR_UE) && (fpscr & FPSCR_UX)) |
557 | code = FPE_FLTUND; | 552 | ret = FPE_FLTUND; |
558 | 553 | ||
559 | /* Divide by zero */ | 554 | /* Divide by zero */ |
560 | else if ((fpscr & FPSCR_ZE) && (fpscr & FPSCR_ZX)) | 555 | else if ((fpscr & FPSCR_ZE) && (fpscr & FPSCR_ZX)) |
561 | code = FPE_FLTDIV; | 556 | ret = FPE_FLTDIV; |
562 | 557 | ||
563 | /* Inexact result */ | 558 | /* Inexact result */ |
564 | else if ((fpscr & FPSCR_XE) && (fpscr & FPSCR_XX)) | 559 | else if ((fpscr & FPSCR_XE) && (fpscr & FPSCR_XX)) |
565 | code = FPE_FLTRES; | 560 | ret = FPE_FLTRES; |
561 | |||
562 | return ret; | ||
563 | } | ||
564 | |||
565 | static void parse_fpe(struct pt_regs *regs) | ||
566 | { | ||
567 | int code = 0; | ||
568 | |||
569 | flush_fp_to_thread(current); | ||
570 | |||
571 | code = __parse_fpscr(current->thread.fpscr.val); | ||
566 | 572 | ||
567 | _exception(SIGFPE, regs, code, regs->nip); | 573 | _exception(SIGFPE, regs, code, regs->nip); |
568 | } | 574 | } |
@@ -773,10 +779,21 @@ void __kprobes program_check_exception(struct pt_regs *regs) | |||
773 | * hardware people - not sure if it can happen on any illegal | 779 | * hardware people - not sure if it can happen on any illegal |
774 | * instruction or only on FP instructions, whether there is a | 780 | * instruction or only on FP instructions, whether there is a |
775 | * pattern to occurences etc. -dgibson 31/Mar/2003 */ | 781 | * pattern to occurences etc. -dgibson 31/Mar/2003 */ |
776 | if (do_mathemu(regs) == 0) { | 782 | switch (do_mathemu(regs)) { |
783 | case 0: | ||
777 | emulate_single_step(regs); | 784 | emulate_single_step(regs); |
778 | return; | 785 | return; |
786 | case 1: { | ||
787 | int code = 0; | ||
788 | code = __parse_fpscr(current->thread.fpscr.val); | ||
789 | _exception(SIGFPE, regs, code, regs->nip); | ||
790 | return; | ||
791 | } | ||
792 | case -EFAULT: | ||
793 | _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); | ||
794 | return; | ||
779 | } | 795 | } |
796 | /* fall through on any other errors */ | ||
780 | #endif /* CONFIG_MATH_EMULATION */ | 797 | #endif /* CONFIG_MATH_EMULATION */ |
781 | 798 | ||
782 | /* Try to emulate it if we should. */ | 799 | /* Try to emulate it if we should. */ |
@@ -892,18 +909,39 @@ void SoftwareEmulation(struct pt_regs *regs) | |||
892 | 909 | ||
893 | #ifdef CONFIG_MATH_EMULATION | 910 | #ifdef CONFIG_MATH_EMULATION |
894 | errcode = do_mathemu(regs); | 911 | errcode = do_mathemu(regs); |
912 | |||
913 | switch (errcode) { | ||
914 | case 0: | ||
915 | emulate_single_step(regs); | ||
916 | return; | ||
917 | case 1: { | ||
918 | int code = 0; | ||
919 | code = __parse_fpscr(current->thread.fpscr.val); | ||
920 | _exception(SIGFPE, regs, code, regs->nip); | ||
921 | return; | ||
922 | } | ||
923 | case -EFAULT: | ||
924 | _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); | ||
925 | return; | ||
926 | default: | ||
927 | _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); | ||
928 | return; | ||
929 | } | ||
930 | |||
895 | #else | 931 | #else |
896 | errcode = Soft_emulate_8xx(regs); | 932 | errcode = Soft_emulate_8xx(regs); |
897 | #endif | 933 | switch (errcode) { |
898 | if (errcode) { | 934 | case 0: |
899 | if (errcode > 0) | ||
900 | _exception(SIGFPE, regs, 0, 0); | ||
901 | else if (errcode == -EFAULT) | ||
902 | _exception(SIGSEGV, regs, 0, 0); | ||
903 | else | ||
904 | _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); | ||
905 | } else | ||
906 | emulate_single_step(regs); | 935 | emulate_single_step(regs); |
936 | return; | ||
937 | case 1: | ||
938 | _exception(SIGILL, regs, ILL_ILLOPC, regs->nip); | ||
939 | return; | ||
940 | case -EFAULT: | ||
941 | _exception(SIGSEGV, regs, SEGV_MAPERR, regs->nip); | ||
942 | return; | ||
943 | } | ||
944 | #endif | ||
907 | } | 945 | } |
908 | #endif /* CONFIG_8xx */ | 946 | #endif /* CONFIG_8xx */ |
909 | 947 | ||