[PATCH 08/32] s390/dis: Improve show_code() output
From: Heiko Carstens
Date: Thu Oct 01 2026 - 06:35:25 EST
Replace the '>' and '*' instruction markers with explicit 'PSW >' and
'PGM >' markers which identify both the PSW address and the faulting
instruction.
Previously, '>' marked the PSW address, while '*' marked the preceding
instruction as a guess for the faulting instruction for suppressing
exceptions:
Krnl Code:
000003ff8dc7bff8: e310f0080004 lg %r1,8(%r15)
*000003ff8dc7bffe: e3a0e0000004 lg %r10,0(%r14)
>000003ff8dc7c004: a7280000 lhi %r2,0
000003ff8dc7c008: eb9ff0900024 stmg %r9,%r15,144(%r15)
Now 'PGM >' explicitly marks the faulting instruction without guessing
and 'PSW >' marks the PSW address:
Krnl Code:
000003ff8dc7bff8: e310f0080004 lg %r1,8(%r15)
PGM >000003ff8dc7bffe: e3a0e0000004 lg %r10,0(%r14)
PSW >000003ff8dc7c004: a7280000 lhi %r2,0
000003ff8dc7c008: eb9ff0900024 stmg %r9,%r15,144(%r15)
For nullifying exceptions where both addresses are the same,
'PGM+PSW >' is printed instead:
Krnl Code:
000003ff8dc7bff8: e310f0080004 lg %r1,8(%r15)
PGM+PSW >000003ff8dc7bffe: e3a0e0000004 lg %r10,0(%r14)
000003ff8dc7c004: a7280000 lhi %r2,0
000003ff8dc7c008: eb9ff0900024 stmg %r9,%r15,144(%r15)
show_code() may also be called in other contexts where pt_regs does not
originate from a program check (e.g. system calls). Introduce and set
PIF_PGM_CHECK to indicate when in program check context, and print the
PGM marker only in such a context.
Note that corner cases are not taken into account. Like before kprobes is
not considered, which means that the disassembly may contain unexpected
instructions, or that disassembly and/or instruction length count as well
as interruption do not match.
Signed-off-by: Heiko Carstens <hca@xxxxxxxxxxxxx>
---
arch/s390/include/asm/ptrace.h | 4 ++++
arch/s390/kernel/dis.c | 16 +++++++++++-----
arch/s390/kernel/early.c | 1 +
arch/s390/kernel/traps.c | 4 +++-
4 files changed, 19 insertions(+), 6 deletions(-)
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index c844b8aa0fd9..3e56ef8c268b 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -18,12 +18,14 @@
#define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */
#define PIF_FTRACE_FULL_REGS 3 /* all register contents valid (ftrace) */
#define PIF_WARN_TRAP_SKIPPED 4 /* __WARN_trap() was skipped */
+#define PIF_PGM_CHECK 5 /* inside a program check */
#define _PIF_SYSCALL BIT(PIF_SYSCALL)
#define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
#define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS)
#define _PIF_WARN_TRAP_SKIPPED BIT(PIF_WARN_TRAP_SKIPPED)
+#define _PIF_PGM_CHECK BIT(PIF_PGM_CHECK)
#define PSW32_MASK_PER _AC(0x40000000, UL)
#define PSW32_MASK_DAT _AC(0x04000000, UL)
@@ -242,6 +244,8 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
regs->psw.addr = val;
}
+unsigned long get_trap_ip(struct pt_regs *regs);
+
int regs_query_register_offset(const char *name);
const char *regs_query_register_name(unsigned int offset);
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index 5a1a24d211c3..ece0b1e46ad4 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -503,7 +503,8 @@ static int copy_from_regs(struct pt_regs *regs, void *dst, void *src, int len)
void show_code(struct pt_regs *regs)
{
char *mode = user_mode(regs) ? "User" : "Krnl";
- unsigned long addr, pswaddr;
+ unsigned long addr, pswaddr, trapaddr;
+ bool pgm_check;
unsigned char code[64];
char buffer[128], *ptr;
int start, end, opsize, hops, i;
@@ -511,6 +512,9 @@ void show_code(struct pt_regs *regs)
pswaddr = regs->psw.addr;
if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
+ pgm_check = test_pt_regs_flag(regs, PIF_PGM_CHECK);
+ if (pgm_check)
+ trapaddr = get_trap_ip(regs);
/* Get a snapshot of the 64 bytes surrounding the fault address. */
for (start = 32; start && pswaddr >= 34 - start; start -= 2) {
addr = pswaddr - 34 + start;
@@ -545,15 +549,17 @@ void show_code(struct pt_regs *regs)
while (start < end && hops < 8) {
ptr = buffer;
opsize = insn_length(code[start]);
+ addr = pswaddr + start - 32;
if (start == 32 && test_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED))
ptr += sprintf(ptr, " WARN RET >");
- else if (start + opsize == 32 && !test_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED))
- ptr += sprintf(ptr, " *");
+ else if (pgm_check && addr == trapaddr && start == 32)
+ ptr += sprintf(ptr, " PGM+PSW >");
+ else if (pgm_check && addr == trapaddr)
+ ptr += sprintf(ptr, " PGM >");
else if (start == 32)
- ptr += sprintf(ptr, " >");
+ ptr += sprintf(ptr, " PSW >");
else
ptr += sprintf(ptr, " ");
- addr = pswaddr + start - 32;
ptr += sprintf(ptr, "%px: ", (void *)addr);
if (start + opsize >= end)
break;
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index d875b3b304be..a603e034fa82 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -159,6 +159,7 @@ void __init __do_early_pgm_check(struct pt_regs *regs)
regs->int_parm_long = lc->trans_exc_code;
regs->last_break = lc->pgm_last_break;
regs->monitor_code = lc->monitor_code;
+ set_pt_regs_flag(regs, PIF_PGM_CHECK);
/* Monitor Event? Might be a warning */
if ((regs->int_code & PGM_INT_CODE_MASK) == 0x40) {
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 5e63032a56db..a0937b4a6cf3 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -42,7 +42,7 @@ struct pgm_stat {
static DEFINE_PER_CPU_SHARED_ALIGNED(struct pgm_stat, pgm_stat);
-static inline unsigned long get_trap_ip(struct pt_regs *regs)
+unsigned long get_trap_ip(struct pt_regs *regs)
{
static const unsigned long nullified[2] = { PGM_CHECKS_NULLIFIED };
psw_t psw = regs->psw;
@@ -56,6 +56,7 @@ static inline unsigned long get_trap_ip(struct pt_regs *regs)
psw.addr = __rewind_psw(psw, regs->int_code >> 16);
return psw.addr;
}
+NOKPROBE_SYMBOL(get_trap_ip);
#ifdef CONFIG_GENERIC_BUG
int is_valid_bugaddr(unsigned long addr)
@@ -354,6 +355,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags)
regs->int_code = lc->pgm_int_code;
regs->int_parm_long = teid.val;
regs->monitor_code = lc->monitor_code;
+ set_pt_regs_flag(regs, PIF_PGM_CHECK);
trapnr = regs->int_code & PGM_INT_CODE_MASK;
stat = this_cpu_ptr(&pgm_stat);
--
2.53.0