Commit df2700519c84ee8ee1e5ea165725c651f6d4d1a4
1 parent
cf85c10983
Exists in
master
and in
20 other branches
[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>
Showing 1 changed file with 37 additions and 52 deletions Side-by-side Diff
arch/mips/kernel/traps.c
... | ... | @@ -675,35 +675,24 @@ |
675 | 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 | 681 | siginfo_t info; |
682 | + char b[40]; | |
682 | 683 | |
683 | - if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) | |
684 | - goto out_sigsegv; | |
685 | - | |
686 | 684 | /* |
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 | - | |
696 | - /* | |
697 | - * (A short test says that IRIX 5.3 sends SIGTRAP for all break | |
698 | - * insns, even for break codes that indicate arithmetic failures. | |
699 | - * Weird ...) | |
685 | + * A short test says that IRIX 5.3 sends SIGTRAP for all trap | |
686 | + * insns, even for trap and break codes that indicate arithmetic | |
687 | + * failures. Weird ... | |
700 | 688 | * But should we continue the brokenness??? --macro |
701 | 689 | */ |
702 | - switch (bcode) { | |
703 | - case BRK_OVERFLOW << 10: | |
704 | - case BRK_DIVZERO << 10: | |
705 | - die_if_kernel("Break instruction in kernel code", regs); | |
706 | - if (bcode == (BRK_DIVZERO << 10)) | |
690 | + switch (code) { | |
691 | + case BRK_OVERFLOW: | |
692 | + case BRK_DIVZERO: | |
693 | + scnprintf(b, sizeof(b), "%s instruction in kernel code", str); | |
694 | + die_if_kernel(b, regs); | |
695 | + if (code == BRK_DIVZERO) | |
707 | 696 | info.si_code = FPE_INTDIV; |
708 | 697 | else |
709 | 698 | info.si_code = FPE_INTOVF; |
710 | 699 | |
711 | 700 | |
... | ... | @@ -713,12 +702,34 @@ |
713 | 702 | force_sig_info(SIGFPE, &info, current); |
714 | 703 | break; |
715 | 704 | case BRK_BUG: |
716 | - die("Kernel bug detected", regs); | |
705 | + die_if_kernel("Kernel bug detected", regs); | |
706 | + force_sig(SIGTRAP, current); | |
717 | 707 | break; |
718 | 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 | 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 | 733 | return; |
723 | 734 | |
724 | 735 | out_sigsegv: |
... | ... | @@ -728,7 +739,6 @@ |
728 | 739 | asmlinkage void do_tr(struct pt_regs *regs) |
729 | 740 | { |
730 | 741 | unsigned int opcode, tcode = 0; |
731 | - siginfo_t info; | |
732 | 742 | |
733 | 743 | if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) |
734 | 744 | goto out_sigsegv; |
... | ... | @@ -737,32 +747,7 @@ |
737 | 747 | if (!(opcode & OPCODE)) |
738 | 748 | tcode = ((opcode >> 6) & ((1 << 10) - 1)); |
739 | 749 | |
740 | - /* | |
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 | - } | |
750 | + do_trap_or_bp(regs, tcode, "Trap"); | |
766 | 751 | return; |
767 | 752 | |
768 | 753 | out_sigsegv: |