diff options
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: |
