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(×tamp);
+ 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
next prev 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®