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 2/3] sparc32: emulate casa on V8 CPUs
Date: Sun, 27 Sep 2026 02:58:20 +0200	[thread overview]
Message-ID: <20260927005821.8369-3-kaloz@kernel.org> (raw)
In-Reply-To: <20260927005821.8369-1-kaloz@kernel.org>

sun4m implements no compare-and-swap instruction, only ldstub and swap,
so the V9 casa opcode is undefined there and raises illegal_instruction.
Complete the immediate-ASI form from do_illegal_instruction() for both
ASIs a compiler emits -- 0x80, the V9 primary address space, and 0x0A
from gcc -mcpu=leon3 -- so a C library can use one casa for its 32-bit
atomics on any V8 part. The opcode is unimplemented, so the ASI carries
no distinction the emulated CAS has to honour. LEON parts built without
casa take the same trap and are the LEON parts this serves.

The CAS is futex_atomic_cmpxchg_inatomic(), so it is atomic against the
futex operations and against itself on SMP. Everything else, including
the register-ASI form and casxa, raises SIGILL.

Confirmed on a dual-SuperSPARC SPARCstation 20.

Signed-off-by: Imre Kaloz <kaloz@kernel.org>
---
 arch/sparc/kernel/Makefile     |   1 +
 arch/sparc/kernel/cas_emu_32.c | 117 +++++++++++++++++++++++++++++++++
 arch/sparc/kernel/entry.h      |   1 +
 arch/sparc/kernel/traps_32.c   |   7 ++
 arch/sparc/kernel/unimp_32.h   |  71 ++++++++++++++++++++
 5 files changed, 197 insertions(+)
 create mode 100644 arch/sparc/kernel/cas_emu_32.c
 create mode 100644 arch/sparc/kernel/unimp_32.h

diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index 497b5714fa8f..fbea79a4ab58 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -40,6 +40,7 @@ obj-y                   += sys_sparc_$(BITS).o
 obj-$(CONFIG_SPARC32)   += systbls_32.o
 obj-y                   += time_$(BITS).o
 obj-$(CONFIG_SPARC32)   += windows.o
+obj-$(CONFIG_SPARC32)   += cas_emu_32.o
 obj-y                   += cpu.o
 obj-$(CONFIG_SPARC32)   += devices.o
 obj-y                   += ptrace_$(BITS).o
diff --git a/arch/sparc/kernel/cas_emu_32.c b/arch/sparc/kernel/cas_emu_32.c
new file mode 100644
index 000000000000..257f528d9a2c
--- /dev/null
+++ b/arch/sparc/kernel/cas_emu_32.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Emulation of the V9 casa compare-and-swap for V8 CPUs.
+ *
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ *
+ * sun4m implements no compare-and-swap at all, only ldstub and swap, and
+ * casa is optional on LEON. Where it is missing, casa is an undefined opcode
+ * and raises illegal_instruction (tt 0x02).
+ *
+ * Both immediate ASIs a compiler emits are accepted: 0x80, the V9 primary
+ * address space, and 0x0A, which gcc -mcpu=leon3 uses. The opcode is
+ * unimplemented wherever this runs, so the ASI carries no distinction the
+ * emulated CAS has to honour.
+ *
+ * The register-ASI form is not emulated: it reads %asi, which V8 does not
+ * have. casxa is a 64-bit CAS and is meaningless on a 32-bit CPU.
+ *
+ * The CAS takes the futex ops' lock, so it is atomic against them and
+ * against itself, but not against a plain store to the same word.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/mm.h>
+#include <linux/uaccess.h>
+#include <linux/ptrace.h>
+#include <asm/futex.h>
+#include <asm/ptrace.h>
+
+#include "entry.h"
+#include "unimp_32.h"
+
+#define CASA_OP		3	/* insn[31:30] */
+#define CASA_OP3	0x3c	/* insn[24:19] */
+
+#define ASI_P		0x80
+#define ASI_LEON	0x0a
+
+/* A fault returns -EFAULT, and the caller pages the word in. */
+static int cas_try(u32 __user *uaddr, u32 cmp, u32 newval, u32 *oldval)
+{
+	int ret;
+
+	pagefault_disable();
+	ret = futex_atomic_cmpxchg_inatomic(oldval, uaddr, cmp, newval);
+	pagefault_enable();
+	return ret;
+}
+
+/*
+ * Returns 1 if the trap was consumed, either by emulating the CAS and
+ * advancing the PC or by delivering a fault signal for a bad address, and 0
+ * if this is not a casa we emulate, in which case the caller raises SIGILL.
+ */
+int try_emulate_casa(struct pt_regs *regs, unsigned int insn)
+{
+	unsigned int rd, rs1, rs2, asi;
+	unsigned long addr;
+	u32 cmp, newval, old;
+	int fault = 0, ret;
+
+	if ((insn >> 30) != CASA_OP)
+		return 0;
+	if (((insn >> 19) & 0x3f) != CASA_OP3)
+		return 0;
+	if (insn & (1 << 13))			/* i == 1: register ASI */
+		return 0;
+	asi = (insn >> 5) & 0xff;
+	if (asi != ASI_P && asi != ASI_LEON)
+		return 0;
+
+	rd = (insn >> 25) & 0x1f;
+	rs1 = (insn >> 14) & 0x1f;
+	rs2 = insn & 0x1f;
+
+	unimp_flush_windows(rs1, rs2, rd);
+
+	addr = unimp_get_reg(rs1, regs, &fault);
+	cmp = unimp_get_reg(rs2, regs, &fault);
+	newval = unimp_get_reg(rd, regs, &fault);
+	if (fault)
+		goto sigsegv;
+
+	if (addr & 3) {				/* CAS is naturally aligned */
+		send_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)addr, current);
+		return 1;
+	}
+	if (!access_ok((void __user *)addr, sizeof(u32)))
+		goto sigsegv;
+
+	while ((ret = cas_try((u32 __user *)addr, cmp, newval, &old)) == -EFAULT) {
+		/*
+		 * Page the word in writably and retry, as
+		 * fault_in_user_writeable() does.
+		 */
+		struct mm_struct *mm = current->mm;
+		int fx;
+
+		mmap_read_lock(mm);
+		fx = fixup_user_fault(mm, addr, FAULT_FLAG_WRITE, NULL);
+		mmap_read_unlock(mm);
+		if (fx)
+			goto sigsegv;
+	}
+
+	if (unimp_put_reg(rd, old, regs))
+		goto sigsegv;
+
+	regs->pc = regs->npc;
+	regs->npc += 4;
+	return 1;
+
+sigsegv:
+	send_sig_fault(SIGSEGV, SEGV_MAPERR, (void __user *)addr, current);
+	return 1;
+}
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index c746c0fd5d6b..2eb2b0be90df 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -14,6 +14,7 @@ void handler_irq(int irq, struct pt_regs *regs);
 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);
 
 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 e1d50820c376..69851b19766a 100644
--- a/arch/sparc/kernel/traps_32.c
+++ b/arch/sparc/kernel/traps_32.c
@@ -109,6 +109,8 @@ 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)
 {
+	unsigned int insn;
+
 	if (psr & PSR_PS) {
 		const struct exception_table_entry *entry;
 
@@ -126,6 +128,11 @@ void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned lon
 	       regs->pc, *(unsigned long *)regs->pc);
 #endif
 
+	if (!get_user(insn, (unsigned int __user *)pc)) {
+		if (try_emulate_casa(regs, insn))
+			return;
+	}
+
 	send_sig_fault(SIGILL, ILL_ILLOPC, (void __user *)pc, current);
 }
 
diff --git a/arch/sparc/kernel/unimp_32.h b/arch/sparc/kernel/unimp_32.h
new file mode 100644
index 000000000000..9ab1705b9d04
--- /dev/null
+++ b/arch/sparc/kernel/unimp_32.h
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 Imre Kaloz <kaloz@kernel.org>
+ */
+#ifndef _SPARC_UNIMP_32_H
+#define _SPARC_UNIMP_32_H
+
+#include <linux/uaccess.h>
+#include <asm/ptrace.h>
+
+/*
+ * Operand access for instructions completed out of do_illegal_instruction().
+ *
+ * Like maybe_flush_windows() in unaligned_32.c this has to be inlined: the
+ * save/restore chain must run in the trapping window, not one level deeper
+ * behind a call, or the outermost user window is left un-flushed and
+ * try_to_clear_window_buffer() kills the task on its next fork.
+ */
+static __always_inline void unimp_flush_windows(unsigned int rs1,
+						unsigned int rs2,
+						unsigned int rd)
+{
+	if (rs1 >= 16 || rs2 >= 16 || rd >= 16)
+		__asm__ __volatile__("save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "save %sp, -0x40, %sp\n\t"
+				     "restore; restore; restore; restore;\n\t"
+				     "restore; restore; restore;\n\t");
+}
+
+/*
+ * Globals and outs come from pt_regs, locals and ins from the flushed window
+ * on the user stack. %g0 reads as zero.
+ */
+static inline unsigned long unimp_get_reg(unsigned int reg,
+					  struct pt_regs *regs, int *fault)
+{
+	struct reg_window32 __user *win;
+	unsigned long val;
+
+	if (reg < 16)
+		return reg ? regs->u_regs[reg] : 0;
+
+	win = (struct reg_window32 __user *)regs->u_regs[UREG_FP];
+	if (get_user(val, &win->locals[reg - 16])) {
+		*fault = 1;
+		return 0;
+	}
+	return val;
+}
+
+/* %g0 discards the result. */
+static inline int unimp_put_reg(unsigned int reg, unsigned long val,
+				struct pt_regs *regs)
+{
+	struct reg_window32 __user *win;
+
+	if (reg < 16) {
+		if (reg)
+			regs->u_regs[reg] = val;
+		return 0;
+	}
+	win = (struct reg_window32 __user *)regs->u_regs[UREG_FP];
+	return put_user(val, &win->locals[reg - 16]);
+}
+
+#endif /* _SPARC_UNIMP_32_H */
-- 
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 ` Imre Kaloz [this message]
2026-09-27  0:58 ` [PATCH 3/3] sparc32: emulate integer divide taken as illegal_instruction Imre Kaloz
2026-09-27 21:25   ` 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-3-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®