s390/startup: add pgm check info printing
authorVasily Gorbik <gor@linux.ibm.com>
Thu, 8 Aug 2019 18:09:08 +0000 (20:09 +0200)
committerHeiko Carstens <heiko.carstens@de.ibm.com>
Fri, 13 Sep 2019 10:17:37 +0000 (12:17 +0200)
Try to print out startup pgm check info including exact linux kernel
version, pgm interruption code and ilc, psw and general registers. Like
the following:

Linux version 5.3.0-rc7-07282-ge7b4d41d61bd-dirty (gor@tuxmaker) #3 SMP PREEMPT Thu Sep 5 16:07:34 CEST 2019
Kernel fault: interruption code 0005 ilc:2
PSW : 0000000180000000 0000000000012e52
      R:0 T:0 IO:0 EX:0 Key:0 M:0 W:0 P:0 AS:0 CC:0 PM:0 RI:0 EA:3
GPRS: 0000000000000000 00ffffffffffffff 0000000000000000 0000000000019a58
      000000000000bf68 0000000000000000 0000000000000000 0000000000000000
      0000000000000000 0000000000000000 000000000001a041 0000000000000000
      0000000004c9c000 0000000000010070 0000000000012e42 000000000000beb0

This info makes it apparent that kernel startup failed and might help
to understand what went wrong without actual standalone dump.

Printing code runs on its own stack of 1 page (at unused 0x5000), which
should be sufficient for sclp_early_printk usage (typical stack usage
observed has been around 512 bytes).

The code has pgm check recursion prevention, despite pgm check info
printing failure (follow on pgm check) or success it restores original
faulty psw and gprs and does disabled wait.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
arch/s390/boot/Makefile
arch/s390/boot/boot.h
arch/s390/boot/head.S
arch/s390/boot/pgm_check_info.c [new file with mode: 0644]

index 4cf0bddb7d92e1496ea16c690871274fc8c6ed47..e2c47d3a1c891b7bf5bb783f156ca902c69f1fdf 100644 (file)
@@ -36,7 +36,7 @@ CFLAGS_sclp_early_core.o += -I$(srctree)/drivers/s390/char
 
 obj-y  := head.o als.o startup.o mem_detect.o ipl_parm.o ipl_report.o
 obj-y  += string.o ebcdic.o sclp_early_core.o mem.o ipl_vmparm.o cmdline.o
-obj-y  += version.o ctype.o text_dma.o
+obj-y  += version.o pgm_check_info.o ctype.o text_dma.o
 obj-$(CONFIG_PROTECTED_VIRTUALIZATION_GUEST)   += uv.o
 obj-$(CONFIG_RELOCATABLE)      += machine_kexec_reloc.o
 obj-$(CONFIG_RANDOMIZE_BASE)   += kaslr.o
index 082905d97309c1d9f2abe20d55b7f78121c19799..1f7a8d4aff28459ffd8f736824fe845c8ef7b866 100644 (file)
@@ -9,6 +9,7 @@ void setup_boot_command_line(void);
 void parse_boot_command_line(void);
 void setup_memory_end(void);
 void print_missing_facilities(void);
+void print_pgm_check_info(void);
 unsigned long get_random_base(unsigned long safe_addr);
 
 extern int kaslr_enabled;
index 5b79ae7b44f3ff62016f9077e6f6d68b491cbd6c..4b86a8d3c1219a4528bf4f0e1bba9fac2788ae06 100644 (file)
@@ -365,12 +365,20 @@ ENTRY(startup_pgm_check_handler)
        stctg   %c0,%c15,__LC_CREGS_SAVE_AREA-4095(%r1)
        mvc     __LC_GPREGS_SAVE_AREA-4095(128,%r1),__LC_SAVE_AREA_SYNC
        mvc     __LC_PSW_SAVE_AREA-4095(16,%r1),__LC_PGM_OLD_PSW
-       lg      %r1,__LC_SAVE_AREA_SYNC+8
        mvc     __LC_RETURN_PSW(16),__LC_PGM_OLD_PSW
        ni      __LC_RETURN_PSW,0xfc    # remove IO and EX bits
        ni      __LC_RETURN_PSW+1,0xfb  # remove MCHK bit
        oi      __LC_RETURN_PSW+1,0x2   # set wait state bit
+       larl    %r2,.Lold_psw_disabled_wait
+       stg     %r2,__LC_PGM_NEW_PSW+8
+       l       %r15,.Ldump_info_stack-.Lold_psw_disabled_wait(%r2)
+       brasl   %r14,print_pgm_check_info
+.Lold_psw_disabled_wait:
+       la      %r1,4095
+       lmg     %r0,%r15,__LC_GPREGS_SAVE_AREA-4095(%r1)
        lpswe   __LC_RETURN_PSW         # disabled wait
+.Ldump_info_stack:
+       .long   0x5000 + PAGE_SIZE - STACK_FRAME_OVERHEAD
 ENDPROC(startup_pgm_check_handler)
 
 #
diff --git a/arch/s390/boot/pgm_check_info.c b/arch/s390/boot/pgm_check_info.c
new file mode 100644 (file)
index 0000000..83b5b79
--- /dev/null
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <asm/lowcore.h>
+#include <asm/sclp.h>
+#include "boot.h"
+
+const char hex_asc[] = "0123456789abcdef";
+
+#define add_val_as_hex(dst, val)                                              \
+       __add_val_as_hex(dst, (const unsigned char *)&val, sizeof(val))
+
+static char *__add_val_as_hex(char *dst, const unsigned char *src, size_t count)
+{
+       while (count--)
+               dst = hex_byte_pack(dst, *src++);
+       return dst;
+}
+
+static char *add_str(char *dst, char *src)
+{
+       strcpy(dst, src);
+       return dst + strlen(dst);
+}
+
+void print_pgm_check_info(void)
+{
+       struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area);
+       unsigned short ilc = S390_lowcore.pgm_ilc >> 1;
+       char buf[256];
+       int row, col;
+       char *p;
+
+       add_str(buf, "Linux version ");
+       strlcat(buf, kernel_version, sizeof(buf));
+       sclp_early_printk(buf);
+
+       p = add_str(buf, "Kernel fault: interruption code ");
+       p = add_val_as_hex(buf + strlen(buf), S390_lowcore.pgm_code);
+       p = add_str(p, " ilc:");
+       *p++ = hex_asc_lo(ilc);
+       add_str(p, "\n");
+       sclp_early_printk(buf);
+
+       p = add_str(buf, "PSW : ");
+       p = add_val_as_hex(p, S390_lowcore.psw_save_area.mask);
+       p = add_str(p, " ");
+       p = add_val_as_hex(p, S390_lowcore.psw_save_area.addr);
+       add_str(p, "\n");
+       sclp_early_printk(buf);
+
+       p = add_str(buf, "      R:");
+       *p++ = hex_asc_lo(psw->per);
+       p = add_str(p, " T:");
+       *p++ = hex_asc_lo(psw->dat);
+       p = add_str(p, " IO:");
+       *p++ = hex_asc_lo(psw->io);
+       p = add_str(p, " EX:");
+       *p++ = hex_asc_lo(psw->ext);
+       p = add_str(p, " Key:");
+       *p++ = hex_asc_lo(psw->key);
+       p = add_str(p, " M:");
+       *p++ = hex_asc_lo(psw->mcheck);
+       p = add_str(p, " W:");
+       *p++ = hex_asc_lo(psw->wait);
+       p = add_str(p, " P:");
+       *p++ = hex_asc_lo(psw->pstate);
+       p = add_str(p, " AS:");
+       *p++ = hex_asc_lo(psw->as);
+       p = add_str(p, " CC:");
+       *p++ = hex_asc_lo(psw->cc);
+       p = add_str(p, " PM:");
+       *p++ = hex_asc_lo(psw->pm);
+       p = add_str(p, " RI:");
+       *p++ = hex_asc_lo(psw->ri);
+       p = add_str(p, " EA:");
+       *p++ = hex_asc_lo(psw->eaba);
+       add_str(p, "\n");
+       sclp_early_printk(buf);
+
+       for (row = 0; row < 4; row++) {
+               p = add_str(buf, row == 0 ? "GPRS:" : "     ");
+               for (col = 0; col < 4; col++) {
+                       p = add_str(p, " ");
+                       p = add_val_as_hex(p, S390_lowcore.gpregs_save_area[row * 4 + col]);
+               }
+               add_str(p, "\n");
+               sclp_early_printk(buf);
+       }
+}