All of lore.kernel.org
 help / color / mirror / Atom feed
From: Imre Kaloz <kaloz@kernel.org>
To: Andreas Larsson <andreas@gaisler.com>, sparclinux@vger.kernel.org
Cc: "David S. Miller" <davem@davemloft.net>,
	linux-kernel@vger.kernel.org, Magnus Lindholm <linmag7@gmail.com>
Subject: [PATCH 3/3] sparc32: emulate integer divide taken as illegal_instruction
Date: Sun, 27 Sep 2026 02:58:21 +0200	[thread overview]
Message-ID: <20260927005821.8369-4-kaloz@kernel.org> (raw)
In-Reply-To: <20260927005821.8369-1-kaloz@kernel.org>

SuperSPARC and SuperSPARC-II implement a 52-bit by 32-bit integer divide
and raise illegal_instruction for udiv and sdiv when the 64-bit dividend
{Y, rs1} has significant bits above bit 51, as the SuperSPARC II
Addendum documents, so whether a divide traps depends on its operands.
gcc emits udiv under -mcpu=v8, including in libgcc's __udivdi3, so
64-bit division in user code on these CPUs dies with SIGILL.

Complete udiv and sdiv, and their -cc forms, from
do_illegal_instruction(), sharing the windowed-operand helpers with the
casa emulation. A CPU that executes the instruction never reaches the
handler, nor does a kernel-mode illegal instruction. A fault reading a
windowed operand raises SIGSEGV, as the casa emulation does. On overflow
the quotient saturates to 0xffffffff for udiv and to 0x7fffffff or
0x80000000 for sdiv, and the -cc forms set V, as the architecture
specifies.

Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
 arch/sparc/kernel/Makefile     |   1 +
 arch/sparc/kernel/div_emu_32.c | 127 +++++++++++++++++++++++++++++++++
 arch/sparc/kernel/entry.h      |   1 +
 arch/sparc/kernel/traps_32.c   |   3 +
 4 files changed, 132 insertions(+)
 create mode 100644 arch/sparc/kernel/div_emu_32.c

diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index fbea79a4ab58..43e15b46b8dd 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -41,6 +41,7 @@ obj-$(CONFIG_SPARC32)   += systbls_32.o
 obj-y                   += time_$(BITS).o
 obj-$(CONFIG_SPARC32)   += windows.o
 obj-$(CONFIG_SPARC32)   += cas_emu_32.o
+obj-$(CONFIG_SPARC32)   += div_emu_32.o
 obj-y                   += cpu.o
 obj-$(CONFIG_SPARC32)   += devices.o
 obj-y                   += ptrace_$(BITS).o
diff --git a/arch/sparc/kernel/div_emu_32.c b/arch/sparc/kernel/div_emu_32.c
new file mode 100644
index 000000000000..208b4071a525
--- /dev/null
+++ b/arch/sparc/kernel/div_emu_32.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ *
+ * SuperSPARC and SuperSPARC-II implement a 52-bit by 32-bit integer divide
+ * and raise illegal_instruction for udiv and sdiv when the dividend {Y, rs1}
+ * has significant bits above bit 51. Complete those divides here.
+ *
+ * Format 3 (op = 2), op3 0x0e udiv, 0x0f sdiv, plus 0x10 for the forms that
+ * set %icc. {Y, rs1} / rs2 saturates on overflow and leaves Y alone.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/psr.h>
+
+#include "entry.h"
+#include "unimp_32.h"
+
+int try_emulate_div(struct pt_regs *regs, unsigned int insn)
+{
+	unsigned int op3, rd, rs1, rs2;
+	unsigned long r1, r2, result = 0;
+	int fault = 0, is_signed, cc, ovf = 0;
+
+	if ((insn >> 30) != 2)
+		return 0;
+	op3 = (insn >> 19) & 0x3f;
+	switch (op3) {
+	case 0x0e: case 0x0f: case 0x1e: case 0x1f:
+		break;
+	default:
+		return 0;
+	}
+	is_signed = op3 & 0x01;
+	cc = op3 & 0x10;
+
+	rd = (insn >> 25) & 0x1f;
+	rs1 = (insn >> 14) & 0x1f;
+	rs2 = (insn & (1 << 13)) ? 0 : (insn & 0x1f);
+
+	unimp_flush_windows(rs1, rs2, rd);
+
+	r1 = unimp_get_reg(rs1, regs, &fault);
+	if (insn & (1 << 13)) {			/* i == 1: sign-extended simm13 */
+		long simm = insn & 0x1fff;
+
+		if (simm & 0x1000)
+			simm |= ~0x1fffL;
+		r2 = (unsigned long)simm;
+	} else {
+		r2 = unimp_get_reg(rs2, regs, &fault);
+	}
+	if (fault) {
+		/* A windowed operand could not be read from the user stack. */
+		send_sig_fault(SIGSEGV, SEGV_MAPERR,
+			       (void __user *)regs->u_regs[UREG_FP], current);
+		return 1;
+	}
+
+	if ((unsigned int)r2 == 0) {
+		send_sig_fault(SIGFPE, FPE_INTDIV,
+			       (void __user *)regs->pc, current);
+		return 1;
+	}
+	if (is_signed) {
+		long long num = ((long long)(int)regs->y << 32) |
+				(unsigned int)r1;
+
+		/*
+		 * INT64_MIN / -1 is undefined in C; the quotient is +2^63,
+		 * a positive overflow.
+		 */
+		if (num == LLONG_MIN && (int)r2 == -1) {
+			result = 0x7fffffff;
+			ovf = 1;
+		} else {
+			long long q = num / (int)r2;
+
+			if (q > 2147483647LL) {
+				result = 0x7fffffff;
+				ovf = 1;
+			} else if (q < -2147483647LL - 1) {
+				result = 0x80000000;
+				ovf = 1;
+			} else {
+				result = (unsigned long)(unsigned int)q;
+			}
+		}
+	} else {
+		unsigned long long num =
+			((unsigned long long)(unsigned int)regs->y << 32) |
+			(unsigned int)r1;
+		unsigned long long q = num / (unsigned int)r2;
+
+		if (q > 0xffffffffULL) {
+			result = 0xffffffff;
+			ovf = 1;
+		} else {
+			result = (unsigned long)(unsigned int)q;
+		}
+	}
+
+	if (unimp_put_reg(rd, result, regs)) {
+		send_sig_fault(SIGSEGV, SEGV_MAPERR,
+			       (void __user *)regs->u_regs[UREG_FP], current);
+		return 1;
+	}
+
+	if (cc) {
+		unsigned long psr = regs->psr & ~PSR_ICC;
+
+		if (result & 0x80000000)
+			psr |= PSR_N;
+		if (result == 0)
+			psr |= PSR_Z;
+		if (ovf)
+			psr |= PSR_V;
+		regs->psr = psr;
+	}
+
+	regs->pc = regs->npc;
+	regs->npc += 4;
+	return 1;
+}
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index 2eb2b0be90df..4911cfd07d60 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -15,6 +15,7 @@ void do_hw_interrupt(struct pt_regs *regs, unsigned long type);
 void do_illegal_instruction(struct pt_regs *regs, unsigned long pc,
                             unsigned long npc, unsigned long psr);
 int try_emulate_casa(struct pt_regs *regs, unsigned int insn);
+int try_emulate_div(struct pt_regs *regs, unsigned int insn);
 
 void do_priv_instruction(struct pt_regs *regs, unsigned long pc,
                          unsigned long npc, unsigned long psr);
diff --git a/arch/sparc/kernel/traps_32.c b/arch/sparc/kernel/traps_32.c
index 69851b19766a..f9050dee0f1c 100644
--- a/arch/sparc/kernel/traps_32.c
+++ b/arch/sparc/kernel/traps_32.c
@@ -131,6 +131,9 @@ void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned lon
 	if (!get_user(insn, (unsigned int __user *)pc)) {
 		if (try_emulate_casa(regs, insn))
 			return;
+		/* SuperSPARC traps divides wider than 52 bits. */
+		if (try_emulate_div(regs, insn))
+			return;
 	}
 
 	send_sig_fault(SIGILL, ILL_ILLOPC, (void __user *)pc, current);
-- 
2.47.3


  parent reply	other threads:[~2026-09-27  0:59 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-27  0:58 [PATCH 0/3] sparc32: SMP futexes, casa and idiv emulation Imre Kaloz
2026-09-27  0:58 ` [PATCH 1/3] sparc32: support futexes on SMP Imre Kaloz
2026-10-04  8:51   ` Magnus Lindholm
2026-09-27  0:58 ` [PATCH 2/3] sparc32: emulate casa on V8 CPUs Imre Kaloz
2026-10-04  9:02   ` Magnus Lindholm
2026-10-15 14:15   ` Ramin Moussavi
2026-10-15 14:32     ` Magnus Lindholm
2026-10-15 19:08     ` Imre Kaloz
2026-10-15 20:01       ` Ramin Moussavi
2026-09-27  0:58 ` Imre Kaloz [this message]
2026-09-27 21:25   ` [PATCH 3/3] sparc32: emulate integer divide taken as illegal_instruction Magnus Lindholm
2026-10-04  9:05   ` Magnus Lindholm
2026-09-27 21:42 ` [PATCH 0/3] sparc32: SMP futexes, casa and idiv emulation Magnus Lindholm
2026-10-04 10:04 ` Magnus Lindholm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260927005821.8369-4-kaloz@kernel.org \
    --to=kaloz@kernel.org \
    --cc=andreas@gaisler.com \
    --cc=davem@davemloft.net \
    --cc=linmag7@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=sparclinux@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.