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)
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:
......
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