mirror of https://lore.kernel.org/lkml/
 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: 6+ 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-09-27  0:58 ` [PATCH 2/3] sparc32: emulate casa on V8 CPUs Imre Kaloz
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-09-27 21:42 ` [PATCH 0/3] sparc32: SMP futexes, casa and idiv emulation 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®