[MIPS] Fix handling of trap and breakpoint instructions
authorRalf Baechle <ralf@linux-mips.org>
Sun, 20 Apr 2008 15:28:54 +0000 (16:28 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Mon, 28 Apr 2008 16:14:33 +0000 (17:14 +0100)
With fixes and cleanups from Atsushi Nemoto (anemo@mba.ocn.ne.jp).

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/kernel/traps.c

index 88185cd40c3b5eb04c1c6f2318df575f4db1400c..cb8b0e2c79547ba1c67a577cc852daf51f558dab 100644 (file)
@@ -675,35 +675,24 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
        force_sig_info(SIGFPE, &info, current);
 }
 
-asmlinkage void do_bp(struct pt_regs *regs)
+static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
+       const char *str)
 {
-       unsigned int opcode, bcode;
        siginfo_t info;
-
-       if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
-               goto out_sigsegv;
-
-       /*
-        * There is the ancient bug in the MIPS assemblers that the break
-        * code starts left to bit 16 instead to bit 6 in the opcode.
-        * Gas is bug-compatible, but not always, grrr...
-        * We handle both cases with a simple heuristics.  --macro
-        */
-       bcode = ((opcode >> 6) & ((1 << 20) - 1));
-       if (bcode < (1 << 10))
-               bcode <<= 10;
+       char b[40];
 
        /*
-        * (A short test says that IRIX 5.3 sends SIGTRAP for all break
-        * insns, even for break codes that indicate arithmetic failures.
-        * Weird ...)
+        * A short test says that IRIX 5.3 sends SIGTRAP for all trap
+        * insns, even for trap and break codes that indicate arithmetic
+        * failures.  Weird ...
         * But should we continue the brokenness???  --macro
         */
-       switch (bcode) {
-       case BRK_OVERFLOW << 10:
-       case BRK_DIVZERO << 10:
-               die_if_kernel("Break instruction in kernel code", regs);
-               if (bcode == (BRK_DIVZERO << 10))
+       switch (code) {
+       case BRK_OVERFLOW:
+       case BRK_DIVZERO:
+               scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
+               die_if_kernel(b, regs);
+               if (code == BRK_DIVZERO)
                        info.si_code = FPE_INTDIV;
                else
                        info.si_code = FPE_INTOVF;
@@ -713,12 +702,34 @@ asmlinkage void do_bp(struct pt_regs *regs)
                force_sig_info(SIGFPE, &info, current);
                break;
        case BRK_BUG:
-               die("Kernel bug detected", regs);
+               die_if_kernel("Kernel bug detected", regs);
+               force_sig(SIGTRAP, current);
                break;
        default:
-               die_if_kernel("Break instruction in kernel code", regs);
+               scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
+               die_if_kernel(b, regs);
                force_sig(SIGTRAP, current);
        }
+}
+
+asmlinkage void do_bp(struct pt_regs *regs)
+{
+       unsigned int opcode, bcode;
+
+       if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
+               goto out_sigsegv;
+
+       /*
+        * There is the ancient bug in the MIPS assemblers that the break
+        * code starts left to bit 16 instead to bit 6 in the opcode.
+        * Gas is bug-compatible, but not always, grrr...
+        * We handle both cases with a simple heuristics.  --macro
+        */
+       bcode = ((opcode >> 6) & ((1 << 20) - 1));
+       if (bcode >= (1 << 10))
+               bcode >>= 10;
+
+       do_trap_or_bp(regs, bcode, "Break");
        return;
 
 out_sigsegv:
@@ -728,7 +739,6 @@ out_sigsegv:
 asmlinkage void do_tr(struct pt_regs *regs)
 {
        unsigned int opcode, tcode = 0;
-       siginfo_t info;
 
        if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
                goto out_sigsegv;
@@ -737,32 +747,7 @@ asmlinkage void do_tr(struct pt_regs *regs)
        if (!(opcode & OPCODE))
                tcode = ((opcode >> 6) & ((1 << 10) - 1));
 
-       /*
-        * (A short test says that IRIX 5.3 sends SIGTRAP for all trap
-        * insns, even for trap codes that indicate arithmetic failures.
-        * Weird ...)
-        * But should we continue the brokenness???  --macro
-        */
-       switch (tcode) {
-       case BRK_OVERFLOW:
-       case BRK_DIVZERO:
-               die_if_kernel("Trap instruction in kernel code", regs);
-               if (tcode == BRK_DIVZERO)
-                       info.si_code = FPE_INTDIV;
-               else
-                       info.si_code = FPE_INTOVF;
-               info.si_signo = SIGFPE;
-               info.si_errno = 0;
-               info.si_addr = (void __user *) regs->cp0_epc;
-               force_sig_info(SIGFPE, &info, current);
-               break;
-       case BRK_BUG:
-               die("Kernel bug detected", regs);
-               break;
-       default:
-               die_if_kernel("Trap instruction in kernel code", regs);
-               force_sig(SIGTRAP, current);
-       }
+       do_trap_or_bp(regs, tcode, "Trap");
        return;
 
 out_sigsegv: