Commit df270051 authored by Ralf Baechle's avatar Ralf Baechle

[MIPS] Fix handling of trap and breakpoint instructions

With fixes and cleanups from Atsushi Nemoto (anemo@mba.ocn.ne.jp).
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent cf85c109
...@@ -675,35 +675,24 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31) ...@@ -675,35 +675,24 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
force_sig_info(SIGFPE, &info, current); 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; siginfo_t info;
char b[40];
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;
/* /*
* (A short test says that IRIX 5.3 sends SIGTRAP for all break * A short test says that IRIX 5.3 sends SIGTRAP for all trap
* insns, even for break codes that indicate arithmetic failures. * insns, even for trap and break codes that indicate arithmetic
* Weird ...) * failures. Weird ...
* But should we continue the brokenness??? --macro * But should we continue the brokenness??? --macro
*/ */
switch (bcode) { switch (code) {
case BRK_OVERFLOW << 10: case BRK_OVERFLOW:
case BRK_DIVZERO << 10: case BRK_DIVZERO:
die_if_kernel("Break instruction in kernel code", regs); scnprintf(b, sizeof(b), "%s instruction in kernel code", str);
if (bcode == (BRK_DIVZERO << 10)) die_if_kernel(b, regs);
if (code == BRK_DIVZERO)
info.si_code = FPE_INTDIV; info.si_code = FPE_INTDIV;
else else
info.si_code = FPE_INTOVF; info.si_code = FPE_INTOVF;
...@@ -713,12 +702,34 @@ asmlinkage void do_bp(struct pt_regs *regs) ...@@ -713,12 +702,34 @@ asmlinkage void do_bp(struct pt_regs *regs)
force_sig_info(SIGFPE, &info, current); force_sig_info(SIGFPE, &info, current);
break; break;
case BRK_BUG: case BRK_BUG:
die("Kernel bug detected", regs); die_if_kernel("Kernel bug detected", regs);
force_sig(SIGTRAP, current);
break; break;
default: 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); 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; return;
out_sigsegv: out_sigsegv:
...@@ -728,7 +739,6 @@ out_sigsegv: ...@@ -728,7 +739,6 @@ out_sigsegv:
asmlinkage void do_tr(struct pt_regs *regs) asmlinkage void do_tr(struct pt_regs *regs)
{ {
unsigned int opcode, tcode = 0; unsigned int opcode, tcode = 0;
siginfo_t info;
if (__get_user(opcode, (unsigned int __user *) exception_epc(regs))) if (__get_user(opcode, (unsigned int __user *) exception_epc(regs)))
goto out_sigsegv; goto out_sigsegv;
...@@ -737,32 +747,7 @@ asmlinkage void do_tr(struct pt_regs *regs) ...@@ -737,32 +747,7 @@ asmlinkage void do_tr(struct pt_regs *regs)
if (!(opcode & OPCODE)) if (!(opcode & OPCODE))
tcode = ((opcode >> 6) & ((1 << 10) - 1)); tcode = ((opcode >> 6) & ((1 << 10) - 1));
/* do_trap_or_bp(regs, tcode, "Trap");
* (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);
}
return; return;
out_sigsegv: out_sigsegv:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment