Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit df270051 authored by Ralf Baechle's avatar Ralf Baechle
Browse files

[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
Loading
Loading
Loading
Loading
+37 −52
Original line number Diff line number Diff line
@@ -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: