mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Magnus Lindholm <linmag7@gmail.com>
To: richard.henderson@linaro.org, mattst88@gmail.com,
	linux-kernel@vger.kernel.org, linux-alpha@vger.kernel.org
Cc: linmag7@gmail.com
Subject: [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning
Date: Thu, 15 Oct 2026 12:22:55 +0200	[thread overview]
Message-ID: <20261015102353.999695-7-linmag7@gmail.com> (raw)
In-Reply-To: <20261015102353.999695-1-linmag7@gmail.com>

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


  parent reply	other threads:[~2026-10-15 10:24 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-17  3:13   ` Matt Turner
2026-10-15 10:22 ` [PATCH v8 5/6] alpha: decode Clipper environmental events Magnus Lindholm
2026-10-17  3:13   ` Matt Turner
2026-10-15 10:22 ` Magnus Lindholm [this message]
2026-10-17  3:14   ` [PATCH v8 6/6] alpha: add bounded Clipper console-log scanning Matt Turner

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=20261015102353.999695-7-linmag7@gmail.com \
    --to=linmag7@gmail.com \
    --cc=linux-alpha@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mattst88@gmail.com \
    --cc=richard.henderson@linaro.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®