diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2008-04-20 11:28:54 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2008-04-28 12:14:33 -0400 |
commit | df2700519c84ee8ee1e5ea165725c651f6d4d1a4 (patch) | |
tree | 51a7a8c8c7ee9dcd5a3b232afd14241ed46bad45 /arch/mips/kernel/traps.c | |
parent | cf85c109831ce11ffa9befd4e970d6363e410a10 (diff) |
[MIPS] Fix handling of trap and breakpoint instructions
With fixes and cleanups from Atsushi Nemoto (anemo@mba.ocn.ne.jp).
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'arch/mips/kernel/traps.c')
-rw-r--r-- | arch/mips/kernel/traps.c | 89 |
1 files changed, 37 insertions, 52 deletions
diff --git a/arch/mips/kernel/traps.c b/arch/mips/kernel/traps.c index 88185cd40c3b..cb8b0e2c7954 100644 --- a/arch/mips/kernel/traps.c +++ b/arch/mips/kernel/traps.c | |||
@@ -675,35 +675,24 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) | |||
675 | force_sig_info(SIGFPE, &info, current); | 675 | force_sig_info(SIGFPE, &info, current); |
676 | } | 676 | } |
677 | 677 | ||
678 | asmlinkage void do_bp(struct pt_regs *regs) | 678 | static void do_trap_or_bp(struct pt_regs *regs, unsigned int code, |
679 | const char *str) | ||
679 | { | 680 | { |
680 | unsigned int opcode, bcode; | ||
681 | siginfo_t info; | 681 | siginfo_t info; |
682 | 682 | char b[40]; | |
683 | if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) | ||
684 | goto out_sigsegv; | ||
685 | |||
686 | /* | ||
687 | * There is the ancient bug in the MIPS assemblers that the break | ||
688 | * code starts left to bit 16 instead to bit 6 in the opcode. | ||
689 | * Gas is bug-compatible, but not always, grrr... | ||
690 | * We handle both cases with a simple heuristics. --macro | ||
691 | */ | ||
692 | bcode = ((opcode >> 6) & ((1 << 20) - 1)); | ||
693 | if (bcode < (1 << 10)) | ||
694 | bcode <<= 10; | ||
695 | 683 | ||
696 | /* | 684 | /* |
697 | * (A short test says that IRIX 5.3 sends SIGTRAP for all break | 685 | * A short test says that IRIX 5.3 sends SIGTRAP for all trap |
698 | * insns, even for break codes that indicate arithmetic failures. | 686 | * insns, even for trap and break codes that indicate arithmetic |
699 | * Weird ...) | 687 | * failures. Weird ... |
700 | * But should we continue the brokenness??? --macro | 688 | * But should we continue the brokenness??? --macro |
701 | */ | 689 | */ |
702 | switch (bcode) { | 690 | switch (code) { |
703 | case BRK_OVERFLOW << 10: | 691 | case BRK_OVERFLOW: |
704 | case BRK_DIVZERO << 10: | 692 | case BRK_DIVZERO: |
705 | die_if_kernel("Break instruction in kernel code", regs); | 693 | scnprintf(b, sizeof(b), "%s instruction in kernel code", str); |
706 | if (bcode == (BRK_DIVZERO << 10)) | 694 | die_if_kernel(b, regs); |
695 | if (code == BRK_DIVZERO) | ||
707 | info.si_code = FPE_INTDIV; | 696 | info.si_code = FPE_INTDIV; |
708 | else | 697 | else |
709 | info.si_code = FPE_INTOVF; | 698 | info.si_code = FPE_INTOVF; |
@@ -713,12 +702,34 @@ asmlinkage void do_bp(struct pt_regs *regs) | |||
713 | force_sig_info(SIGFPE, &info, current); | 702 | force_sig_info(SIGFPE, &info, current); |
714 | break; | 703 | break; |
715 | case BRK_BUG: | 704 | case BRK_BUG: |
716 | die("Kernel bug detected", regs); | 705 | die_if_kernel("Kernel bug detected", regs); |
706 | force_sig(SIGTRAP, current); | ||
717 | break; | 707 | break; |
718 | default: | 708 | default: |
719 | die_if_kernel("Break instruction in kernel code", regs); | 709 | scnprintf(b, sizeof(b), "%s instruction in kernel code", str); |
710 | die_if_kernel(b, regs); | ||
720 | force_sig(SIGTRAP, current); | 711 | force_sig(SIGTRAP, current); |
721 | } | 712 | } |
713 | } | ||
714 | |||
715 | asmlinkage void do_bp(struct pt_regs *regs) | ||
716 | { | ||
717 | unsigned int opcode, bcode; | ||
718 | |||
719 | if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) | ||
720 | goto out_sigsegv; | ||
721 | |||
722 | /* | ||
723 | * There is the ancient bug in the MIPS assemblers that the break | ||
724 | * code starts left to bit 16 instead to bit 6 in the opcode. | ||
725 | * Gas is bug-compatible, but not always, grrr... | ||
726 | * We handle both cases with a simple heuristics. --macro | ||
727 | */ | ||
728 | bcode = ((opcode >> 6) & ((1 << 20) - 1)); | ||
729 | if (bcode >= (1 << 10)) | ||
730 | bcode >>= 10; | ||
731 | |||
732 | do_trap_or_bp(regs, bcode, "Break"); | ||
722 | return; | 733 | return; |
723 | 734 | ||
724 | out_sigsegv: | 735 | out_sigsegv: |
@@ -728,7 +739,6 @@ out_sigsegv: | |||
728 | asmlinkage void do_tr(struct pt_regs *regs) | 739 | asmlinkage void do_tr(struct pt_regs *regs) |
729 | { | 740 | { |
730 | unsigned int opcode, tcode = 0; | 741 | unsigned int opcode, tcode = 0; |
731 | siginfo_t info; | ||
732 | 742 | ||
733 | if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) | 743 | if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
734 | goto out_sigsegv; | 744 | goto out_sigsegv; |
@@ -737,32 +747,7 @@ asmlinkage void do_tr(struct pt_regs *regs) | |||
737 | if (!(opcode & OPCODE)) | 747 | if (!(opcode & OPCODE)) |
738 | tcode = ((opcode >> 6) & ((1 << 10) - 1)); | 748 | tcode = ((opcode >> 6) & ((1 << 10) - 1)); |
739 | 749 | ||
740 | /* | 750 | do_trap_or_bp(regs, tcode, "Trap"); |
741 | * (A short test says that IRIX 5.3 sends SIGTRAP for all trap | ||
742 | * insns, even for trap codes that indicate arithmetic failures. | ||
743 | * Weird ...) | ||
744 | * But should we continue the brokenness??? --macro | ||
745 | */ | ||
746 | switch (tcode) { | ||
747 | case BRK_OVERFLOW: | ||
748 | case BRK_DIVZERO: | ||
749 | die_if_kernel("Trap instruction in kernel code", regs); | ||
750 | if (tcode == BRK_DIVZERO) | ||
751 | info.si_code = FPE_INTDIV; | ||
752 | else | ||
753 | info.si_code = FPE_INTOVF; | ||
754 | info.si_signo = SIGFPE; | ||
755 | info.si_errno = 0; | ||
756 | info.si_addr = (void __user *) regs->cp0_epc; | ||
757 | force_sig_info(SIGFPE, &info, current); | ||
758 | break; | ||
759 | case BRK_BUG: | ||
760 | die("Kernel bug detected", regs); | ||
761 | break; | ||
762 | default: | ||
763 | die_if_kernel("Trap instruction in kernel code", regs); | ||
764 | force_sig(SIGTRAP, current); | ||
765 | } | ||
766 | return; | 751 | return; |
767 | 752 | ||
768 | out_sigsegv: | 753 | out_sigsegv: |