mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] sparc32: capture SuperSPARC data store error state
@ 2026-09-24 20:44 Magnus Lindholm
  0 siblings, 0 replies; only message in thread
From: Magnus Lindholm @ 2026-09-24 20:44 UTC (permalink / raw)
  To: davem, andreas; +Cc: sam, sparclinux, linux-kernel, linmag7

SuperSPARC reports a failed buffered write with trap 0x2b. The store
has retired by then, while the processor disables the store buffer and
retains its entries for inspection.

MFSR is cleared when read. SAVE_ALL may spill a user window and read
MFSR while checking that spill, so capture MFAR and MFSR first. Keep
them in the trap window's output registers, which SAVE_ALL preserves.

Build pt_regs and set PSR.ET before entering C. The reporter can then
inspect the frozen store buffer and, when present, the MXCC registers
without running C or printk with traps disabled. Use the saved trap PSR
for CPU identification, then continue through the existing bad-trap path.

This is diagnostic support, not error recovery: the handler neither
retries the failed store nor re-enables the store buffer.

This follows SuperSPARC Family User's Manual sections 9.12.3.6, MFSR
Timing and Operation, 10.6.5, Data Store Errors, 10.7, Store Buffer
Diagnostic and Control Interfaces, 12.8.2, Data Store Error Trap,
16.5.4, MXCC Status Register, 16.5.6, MXCC MBus Port Register, and
16.8.4, MXCC Error Register.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
Applies and builds standalone on v7.3-rc1; cross-built with the SPARCstation
20 configuration, no new warnings.

sparc32 does not boot on current mainline without the prerequisite series
below, so this cannot be exercised on real hardware without them:

  sparc32: relocatable kernel / phys_base + Viking fixes
    https://lore.kernel.org/sparclinux/20260816075141.3489194-1-linmag7@gmail.com/T/#t
  sparc32: replace sp_banks with memblock (v3)
    https://lore.kernel.org/sparclinux/20260901214611.60560-1-linmag7@gmail.com/
  sparc32: SuperSPARC SMP synchronization fixes (v2)
    https://lore.kernel.org/sparclinux/20260917075842.784996-1-linmag7@gmail.com/

This and "sparc32: emulate UDIV/SDIV on illegal_instruction" both insert
code immediately after do_hw_interrupt() in arch/sparc/kernel/traps_32.c.
They are independent, adjacent insertions, no shared functionality, but
whichever is applied second needs a trivial context rebase.

 arch/sparc/include/asm/mxcc.h   | 36 +++++++++++++++++++
 arch/sparc/include/asm/viking.h | 41 ++++++++++++++++++++++
 arch/sparc/kernel/entry.S       | 28 +++++++++++++++
 arch/sparc/kernel/entry.h       |  2 ++
 arch/sparc/kernel/traps_32.c    | 61 +++++++++++++++++++++++++++++++++
 arch/sparc/kernel/ttable_32.S   |  8 ++---
 6 files changed, 172 insertions(+), 4 deletions(-)

diff --git a/arch/sparc/include/asm/mxcc.h b/arch/sparc/include/asm/mxcc.h
index bd6339dcf693..bbfeab004e3e 100644
--- a/arch/sparc/include/asm/mxcc.h
+++ b/arch/sparc/include/asm/mxcc.h
@@ -126,6 +126,42 @@ static inline unsigned long mxcc_get_creg(void)
 	return mxcc_control;
 }
 
+static inline void mxcc_get_64bit_reg(unsigned long reg,
+				      unsigned long *high,
+				      unsigned long *low)
+{
+	unsigned long reg_high, reg_low;
+
+	__asm__ __volatile__("ldda [%2] %3, %%g2\n\t"
+			     "or %%g0, %%g2, %0\n\t"
+			     "or %%g0, %%g3, %1\n\t"
+			     : "=&r" (reg_high), "=&r" (reg_low)
+			     : "r" (reg), "i" (ASI_M_MXCC)
+			     : "g2", "g3");
+	*high = reg_high;
+	*low = reg_low;
+}
+
+static inline void mxcc_get_sreg(unsigned long *high, unsigned long *low)
+{
+	mxcc_get_64bit_reg(MXCC_SREG, high, low);
+}
+
+static inline void mxcc_get_ereg(unsigned long *high, unsigned long *low)
+{
+	mxcc_get_64bit_reg(MXCC_EREG, high, low);
+}
+
+static inline unsigned long mxcc_get_preg(void)
+{
+	unsigned long mxcc_port;
+
+	__asm__ __volatile__("lda [%1] %2, %0\n\t"
+			     : "=r" (mxcc_port)
+			     : "r" (MXCC_PREG), "i" (ASI_M_MXCC));
+	return mxcc_port;
+}
+
 static inline void mxcc_set_creg(unsigned long mxcc_control)
 {
 	__asm__ __volatile__("sta %0, [%1] %2\n\t" : :
diff --git a/arch/sparc/include/asm/viking.h b/arch/sparc/include/asm/viking.h
index bbb714de43c4..c6ee82fa1c82 100644
--- a/arch/sparc/include/asm/viking.h
+++ b/arch/sparc/include/asm/viking.h
@@ -98,6 +98,11 @@
 #define VIKING_TCENABLE     0x00010000   /* Enable table-walks to be cached */
 #define VIKING_DPENABLE     0x00040000   /* Enable the data prefetcher */
 
+#define ASI_M_VIKING_STOREBUF_TAG	0x30
+#define ASI_M_VIKING_STOREBUF_DATA	0x31
+#define ASI_M_VIKING_STOREBUF_CTRL	0x32
+#define VIKING_STOREBUF_ENTRIES		8
+
 /*
  * GNU/Viking Breakpoint Action Register fields.
  */
@@ -162,6 +167,42 @@ static inline unsigned long viking_get_bpreg(void)
 	return regval;
 }
 
+static inline unsigned long viking_get_storebuf_control(void)
+{
+	unsigned long control;
+
+	__asm__ __volatile__("lda [%%g0] %1, %0\n\t"
+			     : "=r" (control)
+			     : "i" (ASI_M_VIKING_STOREBUF_CTRL));
+	return control;
+}
+
+static inline void
+viking_get_storebuf_entry(unsigned int entry, unsigned long *tag_high,
+			  unsigned long *tag_low, unsigned long *data_high,
+			  unsigned long *data_low)
+{
+	unsigned long address = (entry & 7) << 3;
+	unsigned long th, tl, dh, dl;
+
+	__asm__ __volatile__("ldda [%4] %5, %%g2\n\t"
+			     "or %%g0, %%g2, %0\n\t"
+			     "or %%g0, %%g3, %1\n\t"
+			     "ldda [%4] %6, %%g2\n\t"
+			     "or %%g0, %%g2, %2\n\t"
+			     "or %%g0, %%g3, %3\n\t"
+			     : "=&r" (th), "=&r" (tl), "=&r" (dh),
+			       "=&r" (dl)
+			     : "r" (address),
+			       "i" (ASI_M_VIKING_STOREBUF_TAG),
+			       "i" (ASI_M_VIKING_STOREBUF_DATA)
+			     : "g2", "g3");
+	*tag_high = th;
+	*tag_low = tl;
+	*data_high = dh;
+	*data_low = dl;
+}
+
 static inline void viking_get_dcache_ptag(int set, int block,
 					      unsigned long *data)
 {
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S
index ea51ef52c952..0f07ec9082e7 100644
--- a/arch/sparc/kernel/entry.S
+++ b/arch/sparc/kernel/entry.S
@@ -22,6 +22,7 @@
 #include <asm/psr.h>
 #include <asm/vaddrs.h>
 #include <asm/page.h>
+#include <asm/pgtsrmmu.h>
 #include <asm/winmacro.h>
 #include <asm/signal.h>
 #include <asm/obio.h>
@@ -205,6 +206,33 @@ bad_trap_handler:
 	 mov	%l7, %o1		! trap number
 
 	RESTORE_ALL
+
+	.align	4
+	.globl	data_store_error
+data_store_error:
+	/* SAVE_ALL preserves the trap window's output registers. */
+	mov	SRMMU_FAULT_ADDR, %l5
+	LEON_PI(lda	[%l5] ASI_LEON_MMUREGS, %o1)	! read MFAR first
+	SUN_PI_(lda	[%l5] ASI_M_MMUREGS, %o1)	! read MFAR first
+
+	mov	SRMMU_FAULT_STATUS, %l5
+	LEON_PI(lda	[%l5] ASI_LEON_MMUREGS, %o2)	! read MFSR last
+	SUN_PI_(lda	[%l5] ASI_M_MMUREGS, %o2)	! read MFSR last
+
+	SAVE_ALL
+
+	wr	%l0, PSR_ET, %psr
+	WRITE_PAUSE
+
+	add	%sp, STACKFRAME_SZ, %o0	! pt_regs
+	call	do_data_store_error
+	 nop
+
+	add	%sp, STACKFRAME_SZ, %o0	! pt_regs
+	call	do_hw_interrupt
+	 mov	0x2b, %o1
+
+	RESTORE_ALL
 	
 /* For now all IRQ's not registered get sent here. handler_irq() will
  * see if a routine is registered to handle this interrupt and if not
diff --git a/arch/sparc/kernel/entry.h b/arch/sparc/kernel/entry.h
index c746c0fd5d6b..b421705ab418 100644
--- a/arch/sparc/kernel/entry.h
+++ b/arch/sparc/kernel/entry.h
@@ -12,6 +12,8 @@ void handler_irq(int irq, struct pt_regs *regs);
 #ifdef CONFIG_SPARC32
 /* traps */
 void do_hw_interrupt(struct pt_regs *regs, unsigned long type);
+void do_data_store_error(struct pt_regs *regs, unsigned long mfar,
+			 unsigned long mfsr);
 void do_illegal_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 bb149f6cc34b..b0aa4974d9cc 100644
--- a/arch/sparc/kernel/traps_32.c
+++ b/arch/sparc/kernel/traps_32.c
@@ -25,6 +25,9 @@
 #include <asm/ptrace.h>
 #include <asm/oplib.h>
 #include <asm/page.h>
+#include <asm/mbus.h>
+#include <asm/mxcc.h>
+#include <asm/viking.h>
 #include <asm/unistd.h>
 #include <asm/traps.h>
 
@@ -105,6 +108,64 @@ void do_hw_interrupt(struct pt_regs *regs, unsigned long type)
 			       (void __user *)regs->pc, type - 0x80);
 }
 
+static bool cpu_is_viking(unsigned long psr, unsigned long mcr)
+{
+	unsigned int psr_impl = (psr >> 28) & 0xf;
+	unsigned int psr_vers = (psr >> 24) & 0xf;
+	unsigned int mcr_impl = (mcr >> 28) & 0xf;
+	unsigned int mcr_vers = (mcr >> 24) & 0xf;
+
+	return psr_impl == MBUS_VIKING &&
+	       (psr_vers == 0 ||
+		(psr_vers == 1 && mcr_impl == 0 && mcr_vers == 0));
+}
+
+void do_data_store_error(struct pt_regs *regs, unsigned long mfar,
+			 unsigned long mfsr)
+{
+	unsigned long tag_high[VIKING_STOREBUF_ENTRIES];
+	unsigned long tag_low[VIKING_STOREBUF_ENTRIES];
+	unsigned long data_high[VIKING_STOREBUF_ENTRIES];
+	unsigned long data_low[VIKING_STOREBUF_ENTRIES];
+	unsigned long mxcc_status_high = 0, mxcc_status_low = 0;
+	unsigned long mxcc_error_high = 0, mxcc_error_low = 0;
+	unsigned long mxcc_port = 0, storebuf_control;
+	unsigned long mcr = srmmu_get_mmureg();
+	unsigned long psr = regs->psr;
+	bool has_mxcc;
+	int i;
+
+	if (!cpu_is_viking(psr, mcr)) {
+		pr_emerg("Data Store Error: CPU%d PSR=%08lx MCR=%08lx MFAR=%08lx MFSR=%08lx\n",
+			 raw_smp_processor_id(), psr, mcr, mfar, mfsr);
+		return;
+	}
+
+	storebuf_control = viking_get_storebuf_control();
+	for (i = 0; i < VIKING_STOREBUF_ENTRIES; i++)
+		viking_get_storebuf_entry(i, &tag_high[i], &tag_low[i],
+					  &data_high[i], &data_low[i]);
+
+	has_mxcc = !(mcr & VIKING_MMODE);
+	if (has_mxcc) {
+		mxcc_get_sreg(&mxcc_status_high, &mxcc_status_low);
+		mxcc_get_ereg(&mxcc_error_high, &mxcc_error_low);
+		mxcc_port = mxcc_get_preg();
+	}
+
+	pr_emerg("Viking Data Store Error: CPU%d PSR=%08lx MCR=%08lx MFAR=%08lx MFSR=%08lx SBCTRL=%08lx\n",
+		 raw_smp_processor_id(), psr, mcr, mfar, mfsr,
+		 storebuf_control);
+	if (has_mxcc)
+		pr_emerg("MXCC: CCSR=%08lx:%08lx CCER=%08lx:%08lx PREG=%08lx\n",
+			 mxcc_status_high, mxcc_status_low,
+			 mxcc_error_high, mxcc_error_low, mxcc_port);
+
+	for (i = 0; i < VIKING_STOREBUF_ENTRIES; i++)
+		pr_emerg("Viking SB%d: TAG=%08lx:%08lx DATA=%08lx:%08lx\n",
+			 i, tag_high[i], tag_low[i], data_high[i], data_low[i]);
+}
+
 void do_illegal_instruction(struct pt_regs *regs, unsigned long pc, unsigned long npc,
 			    unsigned long psr)
 {
diff --git a/arch/sparc/kernel/ttable_32.S b/arch/sparc/kernel/ttable_32.S
index e79fd786fbbb..ec6c885ddd33 100644
--- a/arch/sparc/kernel/ttable_32.S
+++ b/arch/sparc/kernel/ttable_32.S
@@ -56,7 +56,7 @@ t_bad26:BAD_TRAP(0x26) BAD_TRAP(0x27)
 t_cpexc:TRAP_ENTRY(0x28, do_cp_exception)   /* Co-Processor Exception        */
 t_dacce:SRMMU_DFAULT                        /* Data Access Error             */
 t_hwdz:	TRAP_ENTRY(0x2a, do_hw_divzero)     /* Division by zero, you lose... */
-t_dserr:BAD_TRAP(0x2b)                      /* Data Store Error              */
+t_dserr:TRAP_ENTRY(0x2b, data_store_error)  /* Data Store Error              */
 t_daccm:BAD_TRAP(0x2c)                      /* Data Access MMU-Miss          */
 t_bad2d:BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
 t_bad32:BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
@@ -158,7 +158,7 @@ trapbase_cpu1:
 	TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT
 	TRAP_ENTRY(0x2a, do_hw_divzero)
-	BAD_TRAP(0x2b)
+	TRAP_ENTRY(0x2b, data_store_error)
 	BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
 	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
@@ -257,7 +257,7 @@ trapbase_cpu2:
 	TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT
 	TRAP_ENTRY(0x2a, do_hw_divzero)
-	BAD_TRAP(0x2b)
+	TRAP_ENTRY(0x2b, data_store_error)
 	BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
 	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
@@ -357,7 +357,7 @@ trapbase_cpu3:
 	TRAP_ENTRY(0x28, do_cp_exception)
 	SRMMU_DFAULT
 	TRAP_ENTRY(0x2a, do_hw_divzero)
-	BAD_TRAP(0x2b) BAD_TRAP(0x2c)
+	TRAP_ENTRY(0x2b, data_store_error) BAD_TRAP(0x2c)
 	BAD_TRAP(0x2d) BAD_TRAP(0x2e) BAD_TRAP(0x2f) BAD_TRAP(0x30) BAD_TRAP(0x31)
 	BAD_TRAP(0x32) BAD_TRAP(0x33) BAD_TRAP(0x34) BAD_TRAP(0x35) BAD_TRAP(0x36)
 	BAD_TRAP(0x37) BAD_TRAP(0x38) BAD_TRAP(0x39) BAD_TRAP(0x3a) BAD_TRAP(0x3b)

base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
-- 
2.43.0


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-09-24 20:45 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-24 20:44 [PATCH] sparc32: capture SuperSPARC data store error state Magnus Lindholm

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®