aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/traps.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2008-04-20 11:28:54 -0400
committerRalf Baechle <ralf@linux-mips.org>2008-04-28 12:14:33 -0400
commitdf2700519c84ee8ee1e5ea165725c651f6d4d1a4 (patch)
tree51a7a8c8c7ee9dcd5a3b232afd14241ed46bad45 /arch/mips/kernel/traps.c
parentcf85c109831ce11ffa9befd4e970d6363e410a10 (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.c89
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
678asmlinkage void do_bp(struct pt_regs *regs) 678static 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
715asmlinkage 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
724out_sigsegv: 735out_sigsegv:
@@ -728,7 +739,6 @@ out_sigsegv:
728asmlinkage void do_tr(struct pt_regs *regs) 739asmlinkage 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
768out_sigsegv: 753out_sigsegv: