]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
s390/fault: Print unmodified PSW address on protection exception
authorHeiko Carstens <hca@linux.ibm.com>
Tue, 4 Nov 2025 10:48:57 +0000 (11:48 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Fri, 14 Nov 2025 10:34:28 +0000 (11:34 +0100)
In case of a kernel crash caused by a protection exception, print the
unmodified PSW address as reported by the CPU. The protection exception
handler modifies the PSW address in order to keep fault handling easy,
however that leads to misleading call traces.

Therefore restore the original PSW address before printing it.

Before this change the output in case of a protection exception looks like
this:

 Oops: 0004 ilc:2 [#1]SMP
 Krnl PSW : 0704c00180000000 000003ffe0b40d78 (sysrq_handle_crash+0x28/0x40)
            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 RI:0 EA:3
...
 Krnl Code: 000003ffe0b40d66e3e0f0980024        stg     %r14,152(%r15)
            000003ffe0b40d6cc010fffffff2        larl    %r1,000003ffe0b40d50
           #000003ffe0b40d72c0200046b6bc        larl    %r2,000003ffe1417aea
           >000003ffe0b40d7892021000            mvi     0(%r1),2
            000003ffe0b40d7cc0e5ffae03d6        brasl   %r14,000003ffe0101528

With this change it looks like this:

 Oops: 0004 ilc:2 [#1]SMP
 Krnl PSW : 0704c00180000000 000003ffe0b40dfc (sysrq_handle_crash+0x2c/0x40)
            R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 RI:0 EA:3
...
 Krnl Code: 000003ffe0b40decc010fffffff2        larl    %r1,000003ffe0b40dd0
            000003ffe0b40df2c0200046b67c        larl    %r2,000003ffe1417aea
           *000003ffe0b40df892021000            mvi     0(%r1),2
           >000003ffe0b40dfcc0e5ffae03b6        brasl   %r14,000003ffe0101568
            000003ffe0b40e02: 0707                bcr     0,%r7

Note that with this change the PSW address points to the instruction behind
the instruction which caused the exception like it is expected for
protection exceptions.

This also replaces the '#' marker in the disassembly with '*', which allows
to distinguish between new and old behavior.

Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/include/asm/ptrace.h
arch/s390/kernel/dis.c
arch/s390/kernel/dumpstack.c
arch/s390/mm/fault.c

index 8fae167c1bd66929d20225813dfc8bc1eb2412bd..dd87466ba0ea0ab406b0ae41d521cd9bd25338d8 100644 (file)
 #include <asm/tpi.h>
 
 #define PIF_SYSCALL                    0       /* inside a system call */
+#define PIF_PSW_ADDR_ADJUSTED          1       /* psw address has been adjusted */
 #define PIF_SYSCALL_RET_SET            2       /* return value was set via ptrace */
 #define PIF_GUEST_FAULT                        3       /* indicates program check in sie64a */
 #define PIF_FTRACE_FULL_REGS           4       /* all register contents valid (ftrace) */
 
 #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_GUEST_FAULT               BIT(PIF_GUEST_FAULT)
 #define _PIF_FTRACE_FULL_REGS          BIT(PIF_FTRACE_FULL_REGS)
index 63a1d4226ff8226b61ecaf23273e28227046a973..1cec93895b3a2f1aef8debf899da5baffce27bbe 100644 (file)
@@ -503,24 +503,27 @@ 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 char code[64];
        char buffer[128], *ptr;
-       unsigned long addr;
        int start, end, opsize, hops, i;
 
+       pswaddr = regs->psw.addr;
+       if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
+               pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
        /* Get a snapshot of the 64 bytes surrounding the fault address. */
-       for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) {
-               addr = regs->psw.addr - 34 + start;
+       for (start = 32; start && pswaddr >= 34 - start; start -= 2) {
+               addr = pswaddr - 34 + start;
                if (copy_from_regs(regs, code + start - 2, (void *)addr, 2))
                        break;
        }
        for (end = 32; end < 64; end += 2) {
-               addr = regs->psw.addr + end - 32;
+               addr = pswaddr + end - 32;
                if (copy_from_regs(regs, code + end, (void *)addr, 2))
                        break;
        }
        /* Code snapshot usable ? */
-       if ((regs->psw.addr & 1) || start >= end) {
+       if ((pswaddr & 1) || start >= end) {
                printk("%s Code: Bad PSW.\n", mode);
                return;
        }
@@ -543,12 +546,12 @@ void show_code(struct pt_regs *regs)
        while (start < end && hops < 8) {
                opsize = insn_length(code[start]);
                if  (start + opsize == 32)
-                       *ptr++ = '#';
+                       *ptr++ = '*';
                else if (start == 32)
                        *ptr++ = '>';
                else
                        *ptr++ = ' ';
-               addr = regs->psw.addr + start - 32;
+               addr = pswaddr + start - 32;
                ptr += sprintf(ptr, "%px: ", (void *)addr);
                if (start + opsize >= end)
                        break;
index dd410962ecbe1d64a3347b4f2d881ca4610273d8..f9d52e05e01e049dd8b5f446c6588f99670146f1 100644 (file)
@@ -155,12 +155,16 @@ static void show_last_breaking_event(struct pt_regs *regs)
 void show_registers(struct pt_regs *regs)
 {
        struct psw_bits *psw = &psw_bits(regs->psw);
+       unsigned long pswaddr;
        char *mode;
 
+       pswaddr = regs->psw.addr;
+       if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
+               pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
        mode = user_mode(regs) ? "User" : "Krnl";
-       printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)regs->psw.addr);
+       printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)pswaddr);
        if (!user_mode(regs))
-               pr_cont(" (%pSR)", (void *)regs->psw.addr);
+               pr_cont(" (%pSR)", (void *)pswaddr);
        pr_cont("\n");
        printk("           R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x "
               "P:%x AS:%x CC:%x PM:%x", psw->per, psw->dat, psw->io, psw->ext,
index 20804f1f781a47da1f93e898c3e42eba807d637a..a8c9aeee632b0d18a54093979ca5fee104e09466 100644 (file)
@@ -374,8 +374,10 @@ void do_protection_exception(struct pt_regs *regs)
         * The exception to this rule are aborted transactions, for these
         * the PSW already points to the correct location.
         */
-       if (!(regs->int_code & 0x200))
+       if (!(regs->int_code & 0x200)) {
                regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
+               set_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED);
+       }
        /*
         * If bit 61 if the TEID is not set, the remainder of the
         * TEID is unpredictable. Special handling is required.