Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/fault: Print unmodified PSW address on protection exception

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: 000003ffe0b40d66: e3e0f0980024 stg %r14,152(%r15)
000003ffe0b40d6c: c010fffffff2 larl %r1,000003ffe0b40d50
#000003ffe0b40d72: c0200046b6bc larl %r2,000003ffe1417aea
>000003ffe0b40d78: 92021000 mvi 0(%r1),2
000003ffe0b40d7c: c0e5ffae03d6 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: 000003ffe0b40dec: c010fffffff2 larl %r1,000003ffe0b40dd0
000003ffe0b40df2: c0200046b67c larl %r2,000003ffe1417aea
*000003ffe0b40df8: 92021000 mvi 0(%r1),2
>000003ffe0b40dfc: c0e5ffae03b6 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>

+21 -10
+2
arch/s390/include/asm/ptrace.h
··· 14 14 #include <asm/tpi.h> 15 15 16 16 #define PIF_SYSCALL 0 /* inside a system call */ 17 + #define PIF_PSW_ADDR_ADJUSTED 1 /* psw address has been adjusted */ 17 18 #define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */ 18 19 #define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */ 19 20 #define PIF_FTRACE_FULL_REGS 4 /* all register contents valid (ftrace) */ 20 21 21 22 #define _PIF_SYSCALL BIT(PIF_SYSCALL) 23 + #define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED) 22 24 #define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET) 23 25 #define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT) 24 26 #define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS)
+10 -7
arch/s390/kernel/dis.c
··· 503 503 void show_code(struct pt_regs *regs) 504 504 { 505 505 char *mode = user_mode(regs) ? "User" : "Krnl"; 506 + unsigned long addr, pswaddr; 506 507 unsigned char code[64]; 507 508 char buffer[128], *ptr; 508 - unsigned long addr; 509 509 int start, end, opsize, hops, i; 510 510 511 + pswaddr = regs->psw.addr; 512 + if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED)) 513 + pswaddr = __forward_psw(regs->psw, regs->int_code >> 16); 511 514 /* Get a snapshot of the 64 bytes surrounding the fault address. */ 512 - for (start = 32; start && regs->psw.addr >= 34 - start; start -= 2) { 513 - addr = regs->psw.addr - 34 + start; 515 + for (start = 32; start && pswaddr >= 34 - start; start -= 2) { 516 + addr = pswaddr - 34 + start; 514 517 if (copy_from_regs(regs, code + start - 2, (void *)addr, 2)) 515 518 break; 516 519 } 517 520 for (end = 32; end < 64; end += 2) { 518 - addr = regs->psw.addr + end - 32; 521 + addr = pswaddr + end - 32; 519 522 if (copy_from_regs(regs, code + end, (void *)addr, 2)) 520 523 break; 521 524 } 522 525 /* Code snapshot usable ? */ 523 - if ((regs->psw.addr & 1) || start >= end) { 526 + if ((pswaddr & 1) || start >= end) { 524 527 printk("%s Code: Bad PSW.\n", mode); 525 528 return; 526 529 } ··· 546 543 while (start < end && hops < 8) { 547 544 opsize = insn_length(code[start]); 548 545 if (start + opsize == 32) 549 - *ptr++ = '#'; 546 + *ptr++ = '*'; 550 547 else if (start == 32) 551 548 *ptr++ = '>'; 552 549 else 553 550 *ptr++ = ' '; 554 - addr = regs->psw.addr + start - 32; 551 + addr = pswaddr + start - 32; 555 552 ptr += sprintf(ptr, "%px: ", (void *)addr); 556 553 if (start + opsize >= end) 557 554 break;
+6 -2
arch/s390/kernel/dumpstack.c
··· 155 155 void show_registers(struct pt_regs *regs) 156 156 { 157 157 struct psw_bits *psw = &psw_bits(regs->psw); 158 + unsigned long pswaddr; 158 159 char *mode; 159 160 161 + pswaddr = regs->psw.addr; 162 + if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED)) 163 + pswaddr = __forward_psw(regs->psw, regs->int_code >> 16); 160 164 mode = user_mode(regs) ? "User" : "Krnl"; 161 - printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)regs->psw.addr); 165 + printk("%s PSW : %px %px", mode, (void *)regs->psw.mask, (void *)pswaddr); 162 166 if (!user_mode(regs)) 163 - pr_cont(" (%pSR)", (void *)regs->psw.addr); 167 + pr_cont(" (%pSR)", (void *)pswaddr); 164 168 pr_cont("\n"); 165 169 printk(" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x " 166 170 "P:%x AS:%x CC:%x PM:%x", psw->per, psw->dat, psw->io, psw->ext,
+3 -1
arch/s390/mm/fault.c
··· 374 374 * The exception to this rule are aborted transactions, for these 375 375 * the PSW already points to the correct location. 376 376 */ 377 - if (!(regs->int_code & 0x200)) 377 + if (!(regs->int_code & 0x200)) { 378 378 regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); 379 + set_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED); 380 + } 379 381 /* 380 382 * If bit 61 if the TEID is not set, the remainder of the 381 383 * TEID is unpredictable. Special handling is required.