mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v8 0/6] alpha: improve Tsunami machine check reporting
@ 2026-10-15 10:22 Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 1/6] alpha: move Tsunami machine check handling to err_tsunami.c Magnus Lindholm
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

This series adds Tsunami/Typhoon system-error diagnostics and Clipper
ES40 environmental reporting. It applies to v7.3-rc1 on Alpha for-next
at 33465f6ab697.

Thanks to Matt Turner for reviewing v7 and checking the register and
frame layouts against the manuals. His Reviewed-by tags are carried on
patches 1 and 2 from v5, patch 3 from v6, and patch 4 from v7. Patches
1-3 are unchanged. The new panic policy remains deferred as agreed.

Changes since v7:
- Restrict live chipset reads, acknowledgment and live-status reporting
  to system vectors 660/620. CPU and environmental vectors leave those
  registers alone so they cannot consume a pending Pchip error.
- Correct the PERROR comment: the first error freezes the register,
  mixed error bits were detected together, and only LOST can be set
  afterwards. Remove the detached PERRMASK comment.
- Split live environmental reporting (patch 5) from console-log support
  (patch 6), so the scanner can be reviewed or deferred independently.
- Remove the environmental decoder's unused return value and print
  parameter, and keep version-history notes below the separator.
- Print console wrapper names, event/halt timestamps and halt codes.
- Replace the open-coded byte dump with print_hex_dump().
- Remove the unused console-handler registration and mark the private
  scanner helpers __init.
- Remove the unverified type-6/type-7 definitions and register annotation
  tables. Those payloads remain raw; no register ordering is assumed.

PERRMASK remains as firmware configured it. Its reset value is zero,
and the HRM says mask bits gate PERROR latching. On my ES40,
both Pchips read 0x0000000000000ff7 under the v7 console-test kernel,
and the same values were confirmed after booting v8.
DCRTO is masked; the other defined error bits are enabled. Bit 9 is also
set but is not interpreted as a defined PERROR error. These were read
through read-only /dev/mem mappings without accessing PERROR or writing
any CSR; dmesg was unchanged and kernel taint remained zero. This is an
observed runtime setting, not a measurement at reset or a claim about
other firmware configurations.

The class-5/type-4 console record observed on ES40 remains unsupported.
Patch 6 retains its firmware pointer and prints at most 1 KiB of raw
bytes per log, so it may be reported again on subsequent boots. Console
environmental payloads also remain raw because Table D-18's sys_offset
conflicts with its field offsets. Successful decoding of a nonempty
hardware console log is not claimed.

Validation:
- Native ASan/UBSan handler tests pass with CONFIG_VERBOSE_MCHECK enabled
  and disabled. Protected mock CSR pages establish that CPU and
  environmental vectors make no live chipset accesses. Tests also cover
  stale INV state, system-vector acknowledgment, malformed frames,
  environmental status, wrapper metadata and bounded console dumps.
- The six-patch mailbox applies cleanly to the stated base and reproduces
  the source tree exactly.
- Each of patches 4, 5 and 6 builds as vmlinux with W=1 and GCC 15.0.1
  for GENERIC, SHARK, DP264, EIGER and DP264 with VERBOSE_MCHECK enabled
  (15 builds). The other four configurations have it disabled. No warnings
  in touched files; logs contain unrelated kernel-doc warnings and the
  existing saved_config COMMON-symbol modpost warning. These are minimal
  configurations, not ES40 deployment builds.

V8, built with my ES40 configuration, boots on the ES40 with three CPUs
online and the root filesystem mounted read/write. Available dmesg shows
no oops, panic or new I/O/filesystem errors; kernel taint was zero during
inspection. I unplugged and reconnected one PSU. The final notification
at 926.597181 seconds reports no recognized environmental fault bits and
all three supplies enabled; all three PSU LEDs are green. SMIR bit 0
remained set, so keeping it as raw context avoids a false failure message.
This exercises reporting of a valid notification with no fault bits set.
Earlier notifications named both PSU 0 and PSU 1 despite only one supply
being disconnected; the extra indication's origin remains unexplained.

Earlier CPU, memory and file I/O stress results apply to v7, not v8.
V8's available boot log shows raw console fallback but omits the start of
scanning, so it does not independently verify firmware-pointer retention.
V8 hatch tests, ECC/PCI fault injection, successful decoding of nonempty
hardware console logs and testing on other boards remain outstanding.

Matt's v7 build report covered patches 4 and 5 for GENERIC, DP264, SHARK
and EIGER, plus DP264 without CONFIG_VERBOSE_MCHECK, with no warnings in
touched files. Those minimal configurations were not booted. His manual
review confirmed the PERROR/MISC decoding, frame layouts and Clipper bit
tables, including unconditional SERR suppression and the fact that
configuration reads do not set NDS.

Magnus Lindholm (6):
  alpha: move Tsunami machine check handling to err_tsunami.c
  alpha: describe the Tsunami system machine check frame
  alpha: bound EV6 logout decoding by the processor area
  alpha: decode and acknowledge Tsunami system machine checks
  alpha: decode Clipper environmental events
  alpha: add bounded Clipper console-log scanning

 arch/alpha/include/asm/core_tsunami.h |   8 +-
 arch/alpha/kernel/Makefile            |   4 +-
 arch/alpha/kernel/core_tsunami.c      |  34 --
 arch/alpha/kernel/err_common.c        |  13 +
 arch/alpha/kernel/err_ev6.c           |  38 +-
 arch/alpha/kernel/err_impl.h          |   1 +
 arch/alpha/kernel/err_tsunami.c       | 682 ++++++++++++++++++++++++++
 arch/alpha/kernel/proto.h             |   2 +
 arch/alpha/kernel/sys_dp264.c         |   3 +-
 9 files changed, 737 insertions(+), 48 deletions(-)
 create mode 100644 arch/alpha/kernel/err_tsunami.c


base-commit: 33465f6ab697abceafd9a340067a544d551c4412
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 1/6] alpha: move Tsunami machine check handling to err_tsunami.c
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 2/6] alpha: describe the Tsunami system machine check frame Magnus Lindholm
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

Move the existing handler and PCI error clearing helpers without changing
their behavior. Build the error objects for ALPHA_TSUNAMI so that all
Tsunami configurations, including non-generic Shark, can use them.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/Makefile       |  4 ++-
 arch/alpha/kernel/core_tsunami.c | 34 -----------------------
 arch/alpha/kernel/err_tsunami.c  | 47 ++++++++++++++++++++++++++++++++
 3 files changed, 50 insertions(+), 35 deletions(-)
 create mode 100644 arch/alpha/kernel/err_tsunami.c

diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 4ea5c189e60e..a453e487b9d0 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -39,7 +39,7 @@ obj-y    += err_ev7.o err_titan.o err_marvel.o
 endif
 
 obj-y	 += irq_pyxis.o irq_i8259.o irq_srm.o
-obj-y	 += err_ev6.o
+obj-y	 += err_ev6.o err_tsunami.o
 obj-y	 += es1888.o smc37c669.o smc37c93x.o pc873xx.o gct.o
 obj-y    += srmcons.o
 
@@ -89,4 +89,6 @@ obj-$(CONFIG_ALPHA_MARVEL)	+= err_ev7.o err_marvel.o
 obj-$(CONFIG_ALPHA_NAUTILUS)	+= err_ev6.o
 obj-$(CONFIG_ALPHA_TITAN)	+= err_ev6.o err_titan.o
 
+obj-$(CONFIG_ALPHA_TSUNAMI)	+= err_tsunami.o
+
 endif # GENERIC
diff --git a/arch/alpha/kernel/core_tsunami.c b/arch/alpha/kernel/core_tsunami.c
index fc1ab73f23de..d435ec9fb828 100644
--- a/arch/alpha/kernel/core_tsunami.c
+++ b/arch/alpha/kernel/core_tsunami.c
@@ -448,37 +448,3 @@ tsunami_kill_arch(int mode)
 	if (TSUNAMI_cchip->csc.csr & 1L<<14)
 		tsunami_kill_one_pchip(TSUNAMI_pchip1, 1);
 }
-
-static inline void
-tsunami_pci_clr_err_1(tsunami_pchip *pchip)
-{
-	pchip->perror.csr;
-	pchip->perror.csr = 0x040;
-	mb();
-	pchip->perror.csr;
-}
-
-static inline void
-tsunami_pci_clr_err(void)
-{
-	tsunami_pci_clr_err_1(TSUNAMI_pchip0);
-
-	/* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */
-	if (TSUNAMI_cchip->csc.csr & 1L<<14)
-		tsunami_pci_clr_err_1(TSUNAMI_pchip1);
-}
-
-void
-tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
-{
-	/* Clear error before any reporting.  */
-	mb();
-	mb();  /* magic */
-	draina();
-	tsunami_pci_clr_err();
-	wrmces(0x7);
-	mb();
-
-	process_mcheck_info(vector, la_ptr, "TSUNAMI",
-			    mcheck_expected(smp_processor_id()));
-}
diff --git a/arch/alpha/kernel/err_tsunami.c b/arch/alpha/kernel/err_tsunami.c
new file mode 100644
index 000000000000..1045b920fa6b
--- /dev/null
+++ b/arch/alpha/kernel/err_tsunami.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Machine check handling for Tsunami/Typhoon systems. */
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/core_tsunami.h>
+#include <asm/smp.h>
+#include <asm/err_common.h>
+#include <asm/irq_regs.h>
+#include "err_impl.h"
+#include "proto.h"
+#include "pci_impl.h"
+
+static inline void
+tsunami_pci_clr_err_1(tsunami_pchip *pchip)
+{
+	pchip->perror.csr;
+	pchip->perror.csr = 0x040;
+	mb();
+	pchip->perror.csr;
+}
+
+static inline void
+tsunami_pci_clr_err(void)
+{
+	tsunami_pci_clr_err_1(TSUNAMI_pchip0);
+
+	/* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */
+	if (TSUNAMI_cchip->csc.csr & 1L<<14)
+		tsunami_pci_clr_err_1(TSUNAMI_pchip1);
+}
+
+void
+tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
+{
+	/* Clear error before any reporting.  */
+	mb();
+	mb();  /* magic */
+	draina();
+	tsunami_pci_clr_err();
+	wrmces(0x7);
+	mb();
+
+	process_mcheck_info(vector, la_ptr, "TSUNAMI",
+			    mcheck_expected(smp_processor_id()));
+}
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 2/6] alpha: describe the Tsunami system machine check frame
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 1/6] alpha: move Tsunami machine check handling to err_tsunami.c Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 3/6] alpha: bound EV6 logout decoding by the processor area Magnus Lindholm
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

Define the five system-specific quadwords in the SRM logout frame,
as documented in the AlphaServer ES40 Service Guide, Table D-19.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/include/asm/core_tsunami.h | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/alpha/include/asm/core_tsunami.h b/arch/alpha/include/asm/core_tsunami.h
index 3391e95754f2..fe3fd4570fac 100644
--- a/arch/alpha/include/asm/core_tsunami.h
+++ b/arch/alpha/include/asm/core_tsunami.h
@@ -283,6 +283,11 @@ union TPchipPERRMASK {
  * Data structure for handling TSUNAMI machine checks:
  */
 struct el_TSUNAMI_sysdata_mcheck {
+	u64 sesf;
+	u64 dir;
+	u64 misc;
+	u64 p0_perror;
+	u64 p1_perror;
 };
 
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 3/6] alpha: bound EV6 logout decoding by the processor area
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 1/6] alpha: move Tsunami machine check handling to err_tsunami.c Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 2/6] alpha: describe the Tsunami system machine check frame Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 4/6] alpha: decode and acknowledge Tsunami system machine checks Magnus Lindholm
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

The ES40 Service Guide, Table D-19, documents a correctable frame with
its system area at 0x58 and total size 0x80. The existing EV6 decoder
interprets system words as extended CPU registers and reads past its end.
This establishes the layout for ES40; whether Titan and Nautilus PALcode
also emits this short frame has not been verified.

Validate the processor area before decoding it and only print extended
registers when present. Preserve rejected frames with a raw dump rather
than trusting their invalid offsets. Cap the dump at 1 KiB and round down
to complete quadwords. This still relies on firmware supplying an
accessible frame of its advertised size; the cap is not pointer validation.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
---
 arch/alpha/kernel/err_common.c | 13 ++++++++++++
 arch/alpha/kernel/err_ev6.c    | 38 +++++++++++++++++++++++++---------
 arch/alpha/kernel/err_impl.h   |  1 +
 3 files changed, 42 insertions(+), 10 deletions(-)

diff --git a/arch/alpha/kernel/err_common.c b/arch/alpha/kernel/err_common.c
index 94e1b3dcf6d4..9271ea425a7b 100644
--- a/arch/alpha/kernel/err_common.c
+++ b/arch/alpha/kernel/err_common.c
@@ -44,6 +44,19 @@ mchk_dump_mem(void *data, size_t length, char **annotation)
 	}
 }
 
+/* Preserve rejected frames without trusting their internal offsets. */
+void
+mchk_dump_logout_frame_raw(struct el_common *header)
+{
+	size_t length = header->size;
+
+	/* Bound diagnostic output and never read a partial final quadword. */
+	if (length > 1024)
+		length = 1024;
+	length &= ~7UL;
+	mchk_dump_mem(header, length, NULL);
+}
+
 void
 mchk_dump_logout_frame(struct el_common *mchk_header)
 {
diff --git a/arch/alpha/kernel/err_ev6.c b/arch/alpha/kernel/err_ev6.c
index 8144f2045b5b..f12299ae1f24 100644
--- a/arch/alpha/kernel/err_ev6.c
+++ b/arch/alpha/kernel/err_ev6.c
@@ -190,6 +190,25 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print)
 		(struct el_common_EV6_mcheck *)mchk_header;
 	int status = MCHK_DISPOSITION_UNKNOWN_ERROR;
 
+	/*
+	 * ES40 correctable frames end the CPU area before EXC_ADDR (Table
+	 * D-19). Validate both regions before decoding or dumping any data.
+	 */
+	if (mchk_header->proc_offset != offsetof(struct el_common_EV6_mcheck,
+					       I_STAT) ||
+	    mchk_header->sys_offset < offsetof(struct el_common_EV6_mcheck,
+					      EXC_ADDR) ||
+	    mchk_header->sys_offset > mchk_header->size ||
+	    ((mchk_header->sys_offset | mchk_header->size) & 7)) {
+		if (print) {
+			printk("%s    Invalid EV6 logout frame: size %x, CPU %x, system %x\n",
+			       err_print_prefix, mchk_header->size,
+			       mchk_header->proc_offset, mchk_header->sys_offset);
+			mchk_dump_logout_frame_raw(mchk_header);
+		}
+		return MCHK_DISPOSITION_UNKNOWN_ERROR;
+	}
+
 	status |= ev6_parse_ibox(ev6mchk->I_STAT, print);
 	status |= ev6_parse_mbox(ev6mchk->MM_STAT, ev6mchk->DC_STAT, 
 				 ev6mchk->C_STAT, print);
@@ -203,16 +222,15 @@ ev6_process_logout_frame(struct el_common *mchk_header, int print)
 	if (status != MCHK_DISPOSITION_DISMISS) {
 		char *saved_err_prefix = err_print_prefix;
 
-		/*
-		 * Dump some additional information from the frame
-		 */
-		printk("%s    EXC_ADDR: 0x%016lx   IER_CM: 0x%016lx"
-		            "   ISUM: 0x%016lx\n"
-		         "    PAL_BASE: 0x%016lx   I_CTL:  0x%016lx"
-		            "   PCTX: 0x%016lx\n",
-		       err_print_prefix,
-		       ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM,
-		       ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX);
+		/* These registers are absent from short correctable frames. */
+		if (mchk_header->sys_offset >= sizeof(*ev6mchk))
+			printk("%s    EXC_ADDR: 0x%016lx   IER_CM: 0x%016lx"
+			       "   ISUM: 0x%016lx\n"
+			       "    PAL_BASE: 0x%016lx   I_CTL:  0x%016lx"
+			       "   PCTX: 0x%016lx\n",
+			       err_print_prefix,
+			       ev6mchk->EXC_ADDR, ev6mchk->IER_CM, ev6mchk->ISUM,
+			       ev6mchk->PAL_BASE, ev6mchk->I_CTL, ev6mchk->PCTX);
 
 		if (status == MCHK_DISPOSITION_UNKNOWN_ERROR) {
 			printk("%s    UNKNOWN error, frame follows:\n",
diff --git a/arch/alpha/kernel/err_impl.h b/arch/alpha/kernel/err_impl.h
index 737b958a586d..5bef23a00942 100644
--- a/arch/alpha/kernel/err_impl.h
+++ b/arch/alpha/kernel/err_impl.h
@@ -47,6 +47,7 @@ struct el_subpacket_handler {
 extern char *err_print_prefix;
 
 extern void mchk_dump_mem(void *, size_t, char **);
+extern void mchk_dump_logout_frame_raw(struct el_common *header);
 extern void mchk_dump_logout_frame(struct el_common *);
 extern void el_print_timestamp(union el_timestamp *);
 extern void el_process_subpackets(struct el_subpacket *, int);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 4/6] alpha: decode and acknowledge Tsunami system machine checks
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
                   ` (2 preceding siblings ...)
  2026-10-15 10:22 ` [PATCH v8 3/6] alpha: bound EV6 logout decoding by the processor area Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 5/6] alpha: decode Clipper environmental events Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning Magnus Lindholm
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

Decode PCI and ECC transactions separately, honor invalid transaction
information, and select Pchip errors using the logout summary. Report
master aborts without importing Titan-specific dismissal policy.
Delegate processor vectors to the bounded EV6 decoder.

On system vectors 660/620, capture live PERROR and MISC before acknowledging
their defined W1C bits and report live status absent from the saved frame.
Leave live chipset registers alone on CPU and environmental vectors:
HRM 10.2.5.6 warns that PERROR can have INV set before the error reaches
DRIR. Consuming that state from a CPU vector could lose the transaction
and its pending system-error notification.

Leave PERRMASK as configured by firmware. HRM 10.2.5.7 says a cleared mask
bit prevents the corresponding PERROR bit from being set, including by
PERRSET. Table 10-43 gives a reset value of zero, so this reporting depends
on firmware enabling the errors; changing masks does not establish that
previously latched state is cleared. Table 10-42 marks PERROR bit 9 reserved
(MBZ/RAZ); remove the obsolete RTO definition and reserve its bitfield.

Use the system vector and frame bounds without restricting the reason
code to the ES40 values. Preserve invalid frames and unknown environmental
layouts in bounded raw dumps. Print CPU registers for uncorrectable system
errors, including the PC needed to diagnose NXM. Gate correctable system
register dumps on CONFIG_VERBOSE_MCHECK and alpha_verbose_mcheck while
retaining their error decode.

Keep transaction information unavailable whenever SERR is set, as required
by HRM 8.8.2.6. Table 10-42 selects ECC fields for CRE/UECC. The first error
freezes PERROR and only LOST can be set afterwards, so mixed error bits
were detected together.

The expected-probe path has no active Tsunami writer. Reporting and
acknowledgment do not establish recovery; panic policy remains separate.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
Reviewed-by: Matt Turner <mattst88@gmail.com>
---
Changes since v7:
- Restrict live chipset reads, W1C acknowledgments and live-status reporting
  to system vectors 660/620. CPU and environmental vectors leave them alone.
- Correct the mixed-error comment: first-error freeze means mixed error
  bits were detected together; only LOST can be set afterwards.
- Remove the detached PERRMASK comment and add Matt Turner's Reviewed-by.
- Document the firmware dependency. Both ES40 Pchips read PERRMASK
  0x0000000000000ff7 under the v7 console-test kernel (read-only check).

 arch/alpha/include/asm/core_tsunami.h |   3 +-
 arch/alpha/kernel/Makefile            |   2 +-
 arch/alpha/kernel/err_tsunami.c       | 254 ++++++++++++++++++++++++--
 3 files changed, 238 insertions(+), 21 deletions(-)

diff --git a/arch/alpha/include/asm/core_tsunami.h b/arch/alpha/include/asm/core_tsunami.h
index fe3fd4570fac..5c5d3ff849ed 100644
--- a/arch/alpha/include/asm/core_tsunami.h
+++ b/arch/alpha/include/asm/core_tsunami.h
@@ -105,7 +105,6 @@ extern int TSUNAMI_bootcpu;
 #define perror_m_ta 0x40
 #define perror_m_rdpe 0x80
 #define perror_m_nds 0x100
-#define perror_m_rto 0x200
 #define perror_m_uecc 0x400
 #define perror_m_cre 0x800
 #define perror_m_addrl 0xFFFFFFFF0000UL
@@ -123,7 +122,7 @@ union TPchipPERROR {
 		unsigned perror_v_ta : 1;
 		unsigned perror_v_rdpe : 1;
 		unsigned perror_v_nds : 1;
-		unsigned perror_v_rto : 1;
+		unsigned perror_v_reserved : 1; /* PERROR<9>: MBZ, RAZ */
 		unsigned perror_v_uecc : 1;
 		unsigned perror_v_cre : 1;                 
 		unsigned perror_v_rsvd1 : 4;
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index a453e487b9d0..0178f6a5bdb8 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -89,6 +89,6 @@ obj-$(CONFIG_ALPHA_MARVEL)	+= err_ev7.o err_marvel.o
 obj-$(CONFIG_ALPHA_NAUTILUS)	+= err_ev6.o
 obj-$(CONFIG_ALPHA_TITAN)	+= err_ev6.o err_titan.o
 
-obj-$(CONFIG_ALPHA_TSUNAMI)	+= err_tsunami.o
+obj-$(CONFIG_ALPHA_TSUNAMI)	+= err_ev6.o err_tsunami.o
 
 endif # GENERIC
diff --git a/arch/alpha/kernel/err_tsunami.c b/arch/alpha/kernel/err_tsunami.c
index 1045b920fa6b..03080d219cde 100644
--- a/arch/alpha/kernel/err_tsunami.c
+++ b/arch/alpha/kernel/err_tsunami.c
@@ -1,5 +1,10 @@
 // SPDX-License-Identifier: GPL-2.0
-/* Machine check handling for Tsunami/Typhoon systems. */
+/*
+ * Machine check handling for Tsunami/Typhoon systems.
+ * Author: Magnus Lindholm <linmag7@gmail.com>
+ * Based on Titan error handling by Jeff Wiedemeier (Compaq).
+ * Register definitions: 21272 Hardware Reference Manual, revision 4.0.
+ */
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
@@ -12,36 +17,249 @@
 #include "proto.h"
 #include "pci_impl.h"
 
-static inline void
-tsunami_pci_clr_err_1(tsunami_pchip *pchip)
+#define TSUNAMI_PERROR_ERRMASK	0xdffUL
+#define TSUNAMI_PERROR_ECCMASK	(perror_m_uecc | perror_m_cre)
+#define TSUNAMI_PERROR_INV	(1UL << 51)
+#define TSUNAMI_MISC_NXM		(1UL << 28)
+
+static int
+tsunami_frame_valid(struct el_common *header, size_t size)
 {
-	pchip->perror.csr;
-	pchip->perror.csr = 0x040;
+	return !((header->sys_offset | header->size) & 7) &&
+	       header->sys_offset >= sizeof(*header) &&
+	       header->sys_offset <= header->size &&
+	       size <= header->size - header->sys_offset;
+}
+
+static int
+tsunami_parse_p_perror(int which, u64 perror, int print)
+{
+	static const char * const errors[] = {
+		"Lost error", "SERR asserted", "PERR asserted",
+		"Delayed-completion retry timeout", "Invalid scatter-gather PTE",
+		"Address parity error", "Target abort", "Read data parity error",
+		"No DEVSEL (master abort)", NULL,
+		"Uncorrectable ECC error", "Correctable ECC error"
+	};
+	static const char * const pci_cmd[] = {
+		"Interrupt acknowledge", "Special cycle", "I/O read", "I/O write",
+		"Reserved", "Reserved", "Memory read", "Memory write",
+		"Reserved", "Reserved", "Configuration read", "Configuration write",
+		"Memory read multiple", "Dual address cycle",
+		"Memory read line", "Memory write and invalidate"
+	};
+	unsigned long addr;
+	unsigned int cmd;
+	int i;
+
+	if (!(perror & TSUNAMI_PERROR_ERRMASK))
+		return MCHK_DISPOSITION_UNKNOWN_ERROR;
+	/* Do not import Titan's BIOS master-abort dismissal policy. */
+	if (!print)
+		return MCHK_DISPOSITION_REPORT;
+
+	printk("%s  Pchip %d PERROR: %016llx\n", err_print_prefix, which, perror);
+	for (i = 0; i < ARRAY_SIZE(errors); i++)
+		if ((perror & TSUNAMI_PERROR_ERRMASK & (1UL << i)) && errors[i])
+			printk("%s    %s\n", err_print_prefix, errors[i]);
+
+	/*
+	 * INV invalidates SYN, CMD and ADDR. HRM 8.8.2.6 leaves command/address
+	 * undefined whenever SERR is set, including with other error bits.
+	 */
+	if (perror & (TSUNAMI_PERROR_INV | perror_m_serr)) {
+		printk("%s    Transaction information unavailable\n", err_print_prefix);
+		return MCHK_DISPOSITION_REPORT;
+	}
+	cmd = (perror & perror_m_cmd) >> 52;
+	/*
+	 * Table 10-42 selects the ECC fields whenever CRE or UECC is set.
+	 * The first error freezes PERROR (10.2.5.6); mixed error bits were
+	 * detected together. Only LOST can be set afterwards.
+	 */
+	if (perror & TSUNAMI_PERROR_ECCMASK) {
+		const char *command = "Reserved";
+
+		if (cmd == 0)
+			command = "DMA read";
+		else if (cmd == 1)
+			command = "DMA RMW";
+		else if (cmd == 3)
+			command = "SGTE read";
+		addr = ((perror >> 19) & 0xffffffffUL) << 3;
+		printk("%s    Command: %s (%x), syndrome: %02llx\n",
+		       err_print_prefix, command, cmd, (perror & perror_m_syn) >> 56);
+		printk("%s    System address: %lx\n", err_print_prefix, addr);
+	} else {
+		addr = ((perror >> 18) & 0x3fffffffUL) << 2;
+		/* SGE captures only PCI address bits <31:13> (8KB page). */
+		if (perror & perror_m_sge)
+			addr &= ~0x1fffUL;
+		printk("%s    Command: %s (%x), PCI address: %lx%s\n",
+		       err_print_prefix, pci_cmd[cmd], cmd, addr,
+		       (perror & perror_m_sge) ? " (8KB page)" : "");
+		if (perror & (1UL << 17))
+			printk("%s    Monster window\n", err_print_prefix);
+		else if (perror & (1UL << 16))
+			printk("%s    DAC scatter-gather window 3\n", err_print_prefix);
+	}
+	return MCHK_DISPOSITION_REPORT;
+}
+
+/* SESF bits 0/1 identify PCI errors, bit 2 covers ECC on either Pchip/CPU. */
+static u64
+tsunami_pchip_error(struct el_TSUNAMI_sysdata_mcheck *frame, int which)
+{
+	u64 perror = which ? frame->p1_perror : frame->p0_perror;
+
+	if (frame->sesf & (1UL << which))
+		return perror;
+	if ((frame->sesf & 4) && (perror & TSUNAMI_PERROR_ECCMASK))
+		return perror;
+	return 0;
+}
+
+static struct el_TSUNAMI_sysdata_mcheck *
+tsunami_system_frame(struct el_common *header)
+{
+	if (!tsunami_frame_valid(header, sizeof(struct el_TSUNAMI_sysdata_mcheck)))
+		return NULL;
+	return (void *)header + header->sys_offset;
+}
+
+static int
+tsunami_process_logout_frame(struct el_common *header, int print)
+{
+	struct el_TSUNAMI_sysdata_mcheck *frame = tsunami_system_frame(header);
+	int status = MCHK_DISPOSITION_UNKNOWN_ERROR;
+	int i;
+
+	if (!frame) {
+		if (print) {
+			printk("%s  Invalid Tsunami system frame; raw frame follows\n",
+			       err_print_prefix);
+			mchk_dump_logout_frame_raw(header);
+		}
+		return MCHK_DISPOSITION_UNKNOWN_ERROR;
+	}
+	if (print)
+		printk("%s  SESF: %016llx DIR: %016llx MISC: %016llx\n",
+		       err_print_prefix, frame->sesf, frame->dir, frame->misc);
+	if (frame->misc & TSUNAMI_MISC_NXM) {
+		status = MCHK_DISPOSITION_REPORT;
+		if (print)
+			printk("%s  Non-existent memory, NXS: %llu\n",
+			       err_print_prefix, (frame->misc >> 29) & 7);
+	}
+	for (i = 0; i < 2; i++)
+		status |= tsunami_parse_p_perror(i, tsunami_pchip_error(frame, i), print);
+	return status;
+}
+
+struct tsunami_error_state {
+	u64 perror[2];
+	u64 misc;
+};
+
+static void
+tsunami_ack_pchip(tsunami_pchip *pchip, u64 perror)
+{
+	if (!(perror & TSUNAMI_PERROR_ERRMASK))
+		return;
+	pchip->perror.csr = perror & TSUNAMI_PERROR_ERRMASK;
+	/* Complete the W1C write before releasing the logout frame. */
 	mb();
 	pchip->perror.csr;
 }
 
-static inline void
-tsunami_pci_clr_err(void)
+static void
+tsunami_clear_errors(struct tsunami_error_state *state)
+{
+	bool pchip1 = TSUNAMI_cchip->csc.csr & (1UL << 14);
+
+	/* Preserve each chip's transaction and the NXM source before clearing. */
+	state->perror[0] = TSUNAMI_pchip0->perror.csr;
+	state->perror[1] = pchip1 ? TSUNAMI_pchip1->perror.csr : 0;
+	state->misc = TSUNAMI_cchip->misc.csr;
+
+	tsunami_ack_pchip(TSUNAMI_pchip0, state->perror[0]);
+	/* Do not access the second Pchip on single-Pchip systems. */
+	if (pchip1)
+		tsunami_ack_pchip(TSUNAMI_pchip1, state->perror[1]);
+	if (state->misc & TSUNAMI_MISC_NXM) {
+		/* Zero writes leave the other MISC fields unchanged. */
+		TSUNAMI_cchip->misc.csr = TSUNAMI_MISC_NXM;
+		/* Complete the NXM acknowledgment before releasing the frame. */
+		mb();
+		TSUNAMI_cchip->misc.csr;
+	}
+}
+
+static void
+tsunami_report_live_errors(struct tsunami_error_state *state,
+			  struct el_TSUNAMI_sysdata_mcheck *frame)
 {
-	tsunami_pci_clr_err_1(TSUNAMI_pchip0);
+	int i;
 
-	/* TSUNAMI and TYPHOON can have 2, but might only have 1 (DS10) */
-	if (TSUNAMI_cchip->csc.csr & 1L<<14)
-		tsunami_pci_clr_err_1(TSUNAMI_pchip1);
+	/* PALcode's frame may predate these errors, or describe another chip. */
+	for (i = 0; i < 2; i++) {
+		if (!(state->perror[i] & TSUNAMI_PERROR_ERRMASK) ||
+		    (frame && state->perror[i] == tsunami_pchip_error(frame, i)))
+			continue;
+		printk("%s  Live Pchip status before acknowledgment:\n", err_print_prefix);
+		tsunami_parse_p_perror(i, state->perror[i], 1);
+	}
+	if ((state->misc & TSUNAMI_MISC_NXM) &&
+	    (!frame || ((state->misc ^ frame->misc) & (0xfUL << 28))))
+		printk("%s  Live MISC: %016llx, non-existent memory, NXS: %llu\n",
+		       err_print_prefix, state->misc, (state->misc >> 29) & 7);
 }
 
 void
 tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
 {
-	/* Clear error before any reporting.  */
+	struct el_common *header = (void *)la_ptr;
+	struct el_TSUNAMI_sysdata_mcheck *frame = NULL;
+	struct tsunami_error_state live;
+	char *saved_err_prefix = err_print_prefix;
+	bool show_regs = vector == SCB_Q_SYSMCHK;
+
+	/* Synchronize pending accesses before inspecting the chipset. */
 	mb();
-	mb();  /* magic */
-	draina();
-	tsunami_pci_clr_err();
-	wrmces(0x7);
 	mb();
+	draina();
+	/* CPU/event vectors must not consume a pending Pchip system error. */
+	if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) {
+		tsunami_clear_errors(&live);
+		frame = tsunami_system_frame(header);
+	}
+
+#ifdef CONFIG_VERBOSE_MCHECK
+	show_regs |= alpha_verbose_mcheck != 0;
+#endif
+	err_print_prefix = KERN_CRIT;
+	if (vector == SCB_Q_SYSMCHK || vector == SCB_Q_SYSERR) {
+		printk("%sSystem %s error (vector %lx, code %x) on CPU %d:\n",
+		       err_print_prefix, vector == SCB_Q_SYSERR ? "correctable" :
+		       "uncorrectable", vector, header->code, smp_processor_id());
+		tsunami_process_logout_frame(header, 1);
+		if (show_regs)
+			dik_show_regs(get_irq_regs(), NULL);
+		tsunami_report_live_errors(&live, frame);
+	} else if (vector == SCB_Q_SYSEVENT) {
+		printk("%sTsunami environmental event (code %x): layout unknown\n",
+		       err_print_prefix, header->code);
+		mchk_dump_logout_frame_raw(header);
+	}
+	err_print_prefix = saved_err_prefix;
 
-	process_mcheck_info(vector, la_ptr, "TSUNAMI",
-			    mcheck_expected(smp_processor_id()));
+	if (vector != SCB_Q_SYSMCHK && vector != SCB_Q_SYSERR &&
+	    vector != SCB_Q_SYSEVENT) {
+		/* EV6 decoding also releases the logout frame. */
+		ev6_machine_check(vector, la_ptr);
+	} else {
+		wrmces(0x7);
+		/* Complete acknowledgment before resuming. */
+		mb();
+	}
 }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 5/6] alpha: decode Clipper environmental events
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
                   ` (3 preceding siblings ...)
  2026-10-15 10:22 ` [PATCH v8 4/6] alpha: decode and acknowledge Tsunami system machine checks Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  2026-10-15 10:22 ` [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning Magnus Lindholm
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

Restrict ES40 live environmental decoding to Clipper. Validate frame bounds
and string-table masks, and separate fault bits from status context.
Report valid notifications even when no recognized fault bits are set,
so door-closed and supply-return notifications remain visible. A single
status snapshot does not establish a recovery transition.

Keep SMIR bit 0 and inverted halt/reset bits as raw context. During the
ES40 PSU cable test, bit 0 remained set after the individual PSU failure
indication cleared. Its polarity/latching is unverified, so do not call
it a current PSU failure. Use mask 0x14 for the remaining SMIR faults.
Decode only QW1 through QW7 as live environmental status, as documented
in the ES40 Service Guide, Tables D-20 and D-21.

Use plain door-status messages. Preserve unknown or truncated frames
in bounded raw dumps. Firmware console-log scanning is independent of
the live environmental handler.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
Changes since v7:
- Split console-log support into patch 6 so it can be reviewed separately.
- Make clipper_process_environment() void and remove its unused print
  parameter. Always print from the bit helper, retaining a boolean only
  to decide whether to report that no recognized faults are present.
- Keep version-history remarks here rather than in the commit message.

Changes since v6:
- Remove the redundant environmental silent pass, treat SMIR bit 0 as raw
  context, and retain only mask 0x14 for recognized SMIR faults.

 arch/alpha/kernel/err_tsunami.c | 221 ++++++++++++++++++++++++++++++++
 arch/alpha/kernel/proto.h       |   1 +
 arch/alpha/kernel/sys_dp264.c   |   2 +-
 3 files changed, 223 insertions(+), 1 deletion(-)

diff --git a/arch/alpha/kernel/err_tsunami.c b/arch/alpha/kernel/err_tsunami.c
index 03080d219cde..471d6888b9d4 100644
--- a/arch/alpha/kernel/err_tsunami.c
+++ b/arch/alpha/kernel/err_tsunami.c
@@ -263,3 +263,224 @@ tsunami_machine_check(unsigned long vector, unsigned long la_ptr)
 		mb();
 	}
 }
+
+static const char * const CLIPPER_EnvQW1SMIR[] = {
+	"", /* SMIR<0>: retain as raw context until its semantics are known. */
+	"OCP or RMC halt detected",
+	"Sys_DC_Notok failure detected",
+	"",
+	"System temperature over 50 degrees C failure",
+	"PCI Bus #0 is in reset",
+	"PCI Bus #1 is in reset",
+	"System is being reset"
+};
+
+static const char * const CLIPPER_EnvQW2CPUIR[] = {
+	"CPU0 regulator is enabled",
+	"CPU1 regulator is enabled",
+	"CPU2 regulator is enabled",
+	"CPU3 regulator is enabled",
+	"CPU0 regulator or configuration sequence fail",
+	"CPU1 regulator or configuration sequence fail",
+	"CPU2 regulator or configuration sequence fail",
+	"CPU3 regulator or configuration sequence fail"
+};
+
+static const char * const CLIPPER_EnvQW3PSIR[] = {
+	"Power Supply 0 is enabled",
+	"Power Supply 1 is enabled",
+	"Power Supply 2 is enabled",
+	"",
+	"Power Supply 0 was enabled but failed",
+	"Power Supply 1 was enabled but failed",
+	"Power Supply 2 was enabled but failed"
+};
+
+static const char * const CLIPPER_EnvQW4LM78ISR[] = {
+	"PS +3.3V out of tolerance",
+	"PS +5V out of tolerance",
+	"PS +12V out of tolerance",
+	"VTERM out of tolerance",
+	"Temperature zone 0 (PCI Backplane slots 1-3 area) over limit failure",
+	"LM75 CPU0-3 Temperature over limit failure",
+	"System Fan 1 failure",
+	"System Fan 2 failure",
+	"CTERM out of tolerance",
+	"",
+	"-12V out of tolerance",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"CPU0_VCORE +2V out of tolerance",
+	"CPU0_VIO +1.5V out of tolerance",
+	"CPU1_VCORE +2V out of tolerance",
+	"CPU1_VIO +1.5V out of tolerance",
+	"Temperature zone 1 (PCI Backplane slots 7-10 area) over limit failure",
+	"",
+	"System Fan 4 failure",
+	"System Fan 5 failure",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"",
+	"CPU2_VCORE +2V out of tolerance",
+	"CPU2_VIO +1.5V out of tolerance",
+	"CPU3_VCORE +2V out of tolerance",
+	"CPU3_VIO +1.5V out of tolerance",
+	"Temperature zone 2 (PCI Backplane slots 4-6 area) over limit failure",
+	"",
+	"System Fan 3 failure",
+	"System Fan 6 failure",
+	"",
+	"",
+	"Power supply 3.3V rail above high amperage warning",
+	"Power supply 5.0V rail above high amperage warning",
+	"Power supply 12V rail above high amperage warning",
+	"Power supply high temperature warning",
+	"Power supply AC input low limit warning",
+	"Power supply AC input high limit warning"
+};
+
+static const char * const CLIPPER_EnvQW5DOOR[] = {
+	"",
+	"System CPU door is open",
+	"System Fan door is open",
+	"System PCI door is open",
+	"",
+	"System CPU door is closed",
+	"System Fan door is closed",
+	"System PCI door is closed"
+};
+
+static const char * const CLIPPER_EnvQW6TEMP[] = {
+	"CPU0 temperature warning fault has occurred",
+	"CPU1 temperature warning fault has occurred",
+	"CPU2 temperature warning fault has occurred",
+	"CPU3 temperature warning fault has occurred",
+	"System temperature zone 0 warning fault has occurred",
+	"System temperature zone 1 warning fault has occurred",
+	"System temperature zone 2 warning fault has occurred"
+};
+
+static const char * const CLIPPER_EnvQW7FAN[] = {
+	"System Fan 1 is not responding to RMC Commands",
+	"System Fan 2 is not responding to RMC Commands",
+	"System Fan 3 is not responding to RMC Commands",
+	"System Fan 4 is not responding to RMC Commands",
+	"System Fan 5 is not responding to RMC Commands",
+	"System Fan 6 is not responding to RMC Commands",
+	"",
+	"",
+	"CPU fans 5/6 at maximum speed",
+	"CPU fans 5/6 reduced speed from maximum",
+	"PCI fans 1-4 at maximum speed",
+	"PCI fans 1-4 reduced speed from maximum"
+};
+
+#define CLIPPER_ENV_LM78ISR_MASK	0xfcdf00df05ffUL
+
+/* ES40 Service Guide EK-ES240-SV A01, live frame: Tables D-20 and D-21. */
+struct el_clipper_envdata {
+	u64 summary;
+	u64 dir;
+	u64 smir;
+	u64 cpuir;
+	u64 psir;
+	u64 fault;
+	u64 doors;
+	u64 temp_warn;
+	u64 fan_ctrl;
+	u64 fatal_power_down;
+	u64 reserved;
+};
+
+static bool
+clipper_print_bits(const char * const *messages, size_t count,
+		   u64 value, u64 mask)
+{
+	size_t i;
+
+	if (!(value & mask))
+		return false;
+	for (i = 0; i < count; i++)
+		if (value & mask & (1UL << i))
+			printk("%s    %s\n", err_print_prefix, messages[i]);
+	return true;
+}
+
+#define CLIPPER_BITS(table, value, mask) \
+	clipper_print_bits(table, ARRAY_SIZE(table), value, mask)
+
+static void
+clipper_process_environment(struct el_common *header)
+{
+	struct el_clipper_envdata *frame;
+	bool fault = false;
+
+	if (!tsunami_frame_valid(header, sizeof(*frame))) {
+		printk("%s  Truncated Clipper environmental frame\n", err_print_prefix);
+		mchk_dump_logout_frame_raw(header);
+		return;
+	}
+	frame = (void *)header + header->sys_offset;
+	/* Only QW1 through QW7 carry error state in the live 680 frame. */
+	/* PSU summary and inverted halt/reset signals remain raw context. */
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW1SMIR, frame->smir, 0x14);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0xf0);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x70);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW4LM78ISR,
+			       frame->fault, CLIPPER_ENV_LM78ISR_MASK);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0x0e);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW6TEMP, frame->temp_warn, 0x7f);
+	fault |= CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0x3f);
+	/* A notification with no recognized fault can describe recovery. */
+	if (!fault)
+		printk("%s    No recognized environmental fault bits set\n",
+		       err_print_prefix);
+	printk("%s  Environmental summary: %016llx DIR: %016llx\n",
+	       err_print_prefix, frame->summary, frame->dir);
+	printk("%s  SMIR: %016llx (PSU summary/halt/reset bits undecoded)\n",
+	       err_print_prefix, frame->smir);
+	CLIPPER_BITS(CLIPPER_EnvQW2CPUIR, frame->cpuir, 0x0f);
+	CLIPPER_BITS(CLIPPER_EnvQW3PSIR, frame->psir, 0x07);
+	CLIPPER_BITS(CLIPPER_EnvQW5DOOR, frame->doors, 0xe0);
+	CLIPPER_BITS(CLIPPER_EnvQW7FAN, frame->fan_ctrl, 0xf00);
+	if (frame->fault & (0x3fUL << 42))
+		printk("%s  Power supply warning source: %llu (3 = reserved)\n",
+		       err_print_prefix, (frame->fault >> 40) & 3);
+}
+
+void
+clipper_machine_check(unsigned long vector, unsigned long la_ptr)
+{
+	struct el_common *header = (void *)la_ptr;
+	char *saved_err_prefix = err_print_prefix;
+
+	if (vector != SCB_Q_SYSEVENT) {
+		tsunami_machine_check(vector, la_ptr);
+		return;
+	}
+	/* Drain pending accesses before inspecting the environmental frame. */
+	mb();
+	draina();
+	err_print_prefix = KERN_CRIT;
+	printk("%sSystem correctable environmental event (code %x) on CPU %d:\n",
+	       err_print_prefix, header->code, smp_processor_id());
+	if (header->code == 0x206)
+		clipper_process_environment(header);
+	else {
+		printk("%s  Unrecognized environmental event; raw frame follows\n",
+		       err_print_prefix);
+		mchk_dump_logout_frame_raw(header);
+	}
+	err_print_prefix = saved_err_prefix;
+	wrmces(0x7);
+	/* Complete acknowledgment before returning from the event. */
+	mb();
+}
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index f138bd494628..8cdf72f4ab8b 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -78,6 +78,7 @@ extern struct _alpha_agp_info *titan_agp_info(void);
 extern struct pci_ops tsunami_pci_ops;
 extern void tsunami_init_arch(void);
 extern void tsunami_kill_arch(int);
+extern void clipper_machine_check(unsigned long vector, unsigned long la_ptr);
 extern void tsunami_machine_check(unsigned long vector, unsigned long la_ptr);
 extern void tsunami_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t);
 
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 0a2d319bb1c8..0b5687ab45f2 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -627,7 +627,7 @@ struct alpha_machine_vector clipper_mv __initmv = {
 	DO_EV6_MMU,
 	DO_DEFAULT_RTC,
 	DO_TSUNAMI_IO,
-	.machine_check		= tsunami_machine_check,
+	.machine_check		= clipper_machine_check,
 	.max_isa_dma_address	= ALPHA_MAX_ISA_DMA_ADDRESS,
 	.min_io_address		= DEFAULT_IO_BASE,
 	.min_mem_address	= DEFAULT_MEM_BASE,
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning
  2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
                   ` (4 preceding siblings ...)
  2026-10-15 10:22 ` [PATCH v8 5/6] alpha: decode Clipper environmental events Magnus Lindholm
@ 2026-10-15 10:22 ` Magnus Lindholm
  5 siblings, 0 replies; 7+ messages in thread
From: Magnus Lindholm @ 2026-10-15 10:22 UTC (permalink / raw)
  To: richard.henderson, mattst88, linux-kernel, linux-alpha; +Cc: linmag7

Clipper console logs have an HWRPB byte count. Bound wrapper and child
traversal by that count before decoding embedded processor and system
logout frames. Print the wrapper name and event/halt timestamp, halt
code, and recorded CPU ID for processor packets.

Acknowledge the log only after a complete termination packet. Retain the
firmware pointer on unknown or incomplete wrappers and print at most
1 KiB of raw bytes using print_hex_dump(). This avoids the generic
scanner's unconditional acknowledgment after a parsing failure.

The ES40 record observed during testing is class 5, type 4. Its wrapper
layout is unknown and remains raw and unacknowledged; it may therefore
be printed again at each boot. Successful decoding of a nonempty hardware
log remains unverified. Keep console scanning independent of the live
machine-check and environmental handlers and restrict it to Clipper.

Console environmental payloads also remain raw: ES40 Service Guide
Table D-18's sys_offset conflicts with its field offsets. No register
layout is assumed for unverified extended subpacket types 6 and 7.

The scanner invokes the payload decoder directly during PCI init; no
generic subpacket-handler registration is needed. Mark its helpers
__init and retain the PCI init hook in sys_dp264.c.

Signed-off-by: Magnus Lindholm <linmag7@gmail.com>
---
Changes since v7 patch 5:
- Separate the console scanner from live environmental decoding.
- Print wrapper names, event/halt timestamps and the halt code.
- Use print_hex_dump() for the bounded raw fallback.
- Remove the unused subpacket-handler registration and mark the three
  private console helpers __init. The empty registration wrapper and its
  call to the currently empty EV6 registration function are unnecessary.
- Remove the unverified extended-type 6/7 definitions and annotations.
  Those payloads remain raw, without assigning register names.
- Keep the observed class-5/type-4 record unsupported and preserve its
  pointer on incomplete parsing. Successful hardware decode is unverified.

 arch/alpha/kernel/err_tsunami.c | 196 ++++++++++++++++++++++++++++++++
 arch/alpha/kernel/proto.h       |   1 +
 arch/alpha/kernel/sys_dp264.c   |   1 +
 3 files changed, 198 insertions(+)

diff --git a/arch/alpha/kernel/err_tsunami.c b/arch/alpha/kernel/err_tsunami.c
index 471d6888b9d4..ea4cce54d042 100644
--- a/arch/alpha/kernel/err_tsunami.c
+++ b/arch/alpha/kernel/err_tsunami.c
@@ -6,9 +6,11 @@
  * Register definitions: 21272 Hardware Reference Manual, revision 4.0.
  */
 #include <linux/init.h>
+#include <linux/printk.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
 #include <asm/io.h>
+#include <asm/hwrpb.h>
 #include <asm/core_tsunami.h>
 #include <asm/smp.h>
 #include <asm/err_common.h>
@@ -484,3 +486,197 @@ clipper_machine_check(unsigned long vector, unsigned long la_ptr)
 	/* Complete acknowledgment before returning from the event. */
 	mb();
 }
+
+static struct el_subpacket * __init
+tsunami_process_subpacket(struct el_subpacket *packet)
+{
+	struct el_common *frame;
+	size_t offset = offsetof(struct el_subpacket, by_type.regatta_frame.data_start);
+	int status;
+
+	/* Raw dumps consume whole quadwords, and the next packet must advance. */
+	if (packet->length < offsetof(struct el_subpacket, by_type) ||
+	    (packet->length & 7))
+		return NULL;
+	if (packet->revision != 1) {
+		el_annotate_subpacket(packet);
+		return (void *)packet + packet->length;
+	}
+
+	switch (packet->type) {
+	case EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME:
+	case EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT:
+	case EL_TYPE__REGATTA__SYSTEM_ERROR_FRAME:
+	case EL_TYPE__REGATTA__SYSTEM_DBL_ERROR_HALT:
+		if (packet->length < offset + sizeof(*frame))
+			return NULL;
+		frame = (void *)packet + offset;
+		if (frame->size > packet->length - offset || frame->size < sizeof(*frame))
+			return NULL;
+		if (packet->type == EL_TYPE__REGATTA__PROCESSOR_ERROR_FRAME ||
+		    packet->type == EL_TYPE__REGATTA__PROCESSOR_DBL_ERROR_HALT) {
+			printk("%s  ** Occurred on CPU %d:\n", err_print_prefix,
+			       (int)packet->by_type.regatta_frame.cpuid);
+			status = ev6_process_logout_frame(frame, 0);
+			if (status != MCHK_DISPOSITION_DISMISS)
+				ev6_process_logout_frame(frame, 1);
+		} else {
+			tsunami_process_logout_frame(frame, 1);
+		}
+		break;
+	case EL_TYPE__REGATTA__ENVIRONMENTAL_FRAME:
+		/*
+		 * ES40 Table D-18 gives sys_offset=0x20, but places the
+		 * summary at frame+0x18. Preserve the complete packet until
+		 * the console layout is verified; do not apply the live layout.
+		 */
+		printk("%s  Console environmental layout unverified; raw packet follows\n",
+		       err_print_prefix);
+		el_annotate_subpacket(packet);
+		break;
+	default:
+		el_annotate_subpacket(packet);
+		break;
+	}
+	return (void *)packet + packet->length;
+}
+
+/*
+ * Clipper console logs have an HWRPB byte count. Do not use the generic
+ * unbounded dispatcher: a rejected record must not acknowledge the log.
+ */
+static bool __init
+tsunami_console_log(void *data, size_t remaining)
+{
+	struct el_subpacket *packet;
+	unsigned char *pos = data;
+	size_t size, length;
+	u32 count;
+	union el_timestamp timestamp;
+	const char *name;
+
+	while (remaining >= offsetof(struct el_subpacket, by_type)) {
+		packet = (void *)pos;
+		size = packet->length;
+		if (size < offsetof(struct el_subpacket, by_type) ||
+		    size > remaining || (size & 7))
+			return false;
+		if (packet->class == EL_CLASS__TERMINATION)
+			return packet->type == EL_TYPE__TERMINATION__TERMINATION;
+		if (packet->class != EL_CLASS__HEADER || packet->revision != 1)
+			return false;
+
+		timestamp.as_int = 0;
+		/* Check each header's extent before accessing its union member. */
+#define CONSOLE_HEADER_SIZE(member) \
+	(offsetof(struct el_subpacket, by_type.member) + \
+	 sizeof(packet->by_type.member))
+		switch (packet->type) {
+		case EL_TYPE__HEADER__SYSTEM_ERROR_FRAME:
+			if (size < CONSOLE_HEADER_SIZE(sys_err))
+				return false;
+			name = "SYSTEM ERROR";
+			length = packet->by_type.sys_err.frame_length;
+			count = packet->by_type.sys_err.frame_packet_count;
+			break;
+		case EL_TYPE__HEADER__SYSTEM_EVENT_FRAME:
+			if (size < CONSOLE_HEADER_SIZE(sys_event))
+				return false;
+			name = "SYSTEM EVENT";
+			timestamp = packet->by_type.sys_event.timestamp;
+			length = packet->by_type.sys_event.frame_length;
+			count = packet->by_type.sys_event.frame_packet_count;
+			break;
+		case EL_TYPE__HEADER__HALT_FRAME:
+			if (size < CONSOLE_HEADER_SIZE(err_halt))
+				return false;
+			name = "ERROR HALT";
+			timestamp = packet->by_type.err_halt.timestamp;
+			length = packet->by_type.err_halt.frame_length;
+			count = packet->by_type.err_halt.frame_packet_count;
+			break;
+		case EL_TYPE__HEADER__LOGOUT_FRAME:
+			if (size < CONSOLE_HEADER_SIZE(logout_header))
+				return false;
+			name = "LOGOUT FRAME";
+			length = packet->by_type.logout_header.frame_length;
+			count = 1;
+			break;
+		default:
+			return false;
+		}
+#undef CONSOLE_HEADER_SIZE
+		if (length > remaining - size || (length & 7) ||
+		    count > length / offsetof(struct el_subpacket, by_type))
+			return false;
+		printk("%s*** %s: CLASS %u, TYPE %u\n", err_print_prefix,
+		       name, packet->class, packet->type);
+		el_print_timestamp(&timestamp);
+		if (packet->type == EL_TYPE__HEADER__HALT_FRAME)
+			printk("%s  HALT CODE: %04x\n", err_print_prefix,
+			       packet->by_type.err_halt.halt_code);
+		pos += size;
+		remaining -= size + length;
+		while (count--) {
+			if (length < offsetof(struct el_subpacket, by_type))
+				return false;
+			packet = (void *)pos;
+			size = packet->length;
+			if (size < offsetof(struct el_subpacket, by_type) ||
+			    size > length || (size & 7) ||
+			    packet->class != EL_CLASS__REGATTA_FAMILY)
+				return false;
+			/* This handler checks embedded logout-frame bounds. */
+			if (tsunami_process_subpacket(packet) != (void *)pos + size)
+				return false;
+			pos += size;
+			length -= size;
+		}
+		if (length)
+			return false;
+	}
+	/* Exhaustion without a complete termination packet is incomplete. */
+	return false;
+}
+
+static void __init
+tsunami_console_data_log(int cpu, struct percpu_struct *pcpu)
+{
+	unsigned char *data = (void *)(IDENT_ADDR | pcpu->console_data_log_pa);
+	size_t length = pcpu->console_data_log_length;
+	size_t dump = length < 1024 ? length : 1024;
+
+	printk("%sConsole data log for CPU %d (%zu bytes):\n",
+	       err_print_prefix, cpu, length);
+	if (!(pcpu->console_data_log_pa & 7) &&
+	    length <= ~0UL - (unsigned long)data &&
+	    tsunami_console_log(data, length)) {
+		/* Acknowledge only after reaching a valid termination packet. */
+		pcpu->console_data_log_pa = 0;
+		return;
+	}
+	printk("%s  Incomplete console log; retaining firmware pointer\n",
+	       err_print_prefix);
+	/* Byte reads preserve partial headers without rounding beyond length. */
+	if (dump > ~0UL - (unsigned long)data)
+		return;
+	print_hex_dump(err_print_prefix, "  ", DUMP_PREFIX_OFFSET, 16, 1,
+		       data, dump, false);
+	if (dump < length)
+		printk("%s  Raw console log truncated to %zu bytes\n",
+		       err_print_prefix, dump);
+}
+
+void __init
+tsunami_check_console_data_log(void)
+{
+	struct percpu_struct *pcpu;
+	unsigned long cpu;
+
+	for (cpu = 0; cpu < hwrpb->nr_processors; cpu++) {
+		pcpu = (void *)hwrpb + hwrpb->processor_offset +
+			cpu * hwrpb->processor_size;
+		if (pcpu->console_data_log_pa)
+			tsunami_console_data_log(cpu, pcpu);
+	}
+}
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index 8cdf72f4ab8b..7ece3b635e27 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -78,6 +78,7 @@ extern struct _alpha_agp_info *titan_agp_info(void);
 extern struct pci_ops tsunami_pci_ops;
 extern void tsunami_init_arch(void);
 extern void tsunami_kill_arch(int);
+extern void tsunami_check_console_data_log(void);
 extern void clipper_machine_check(unsigned long vector, unsigned long la_ptr);
 extern void tsunami_machine_check(unsigned long vector, unsigned long la_ptr);
 extern void tsunami_pci_tbi(struct pci_controller *, dma_addr_t, dma_addr_t);
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 0b5687ab45f2..f844d8141553 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -533,6 +533,7 @@ monet_init_pci(void)
 static void __init
 clipper_init_pci(void)
 {
+	tsunami_check_console_data_log();
 	common_init_pci();
 	locate_and_init_vga(NULL);
 }
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-15 10:24 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-15 10:22 [PATCH v8 0/6] alpha: improve Tsunami machine check reporting Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 1/6] alpha: move Tsunami machine check handling to err_tsunami.c Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 2/6] alpha: describe the Tsunami system machine check frame Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 3/6] alpha: bound EV6 logout decoding by the processor area Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 4/6] alpha: decode and acknowledge Tsunami system machine checks Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 5/6] alpha: decode Clipper environmental events Magnus Lindholm
2026-10-15 10:22 ` [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning 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®