s390/decompressor: add decompressor_printk
authorVasily Gorbik <gor@linux.ibm.com>
Tue, 10 Nov 2020 16:05:26 +0000 (17:05 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Fri, 20 Nov 2020 18:19:11 +0000 (19:19 +0100)
The decompressor does not have any special debug means. Running the
kernel under qemu with gdb is helpful but tedious exercise if done
repeatedly. It is also not applicable to debugging under LPAR and z/VM.

One special thing which stands out is a working sclp_early_printk,
which could be used once the kernel switches to 64-bit addressing mode.

But sclp_early_printk does not provide any string formating capabilities.
Formatting and printing string without printk-alike function is a
not fun. The lack of printk-alike function means people would save up on
testing and introduce more bugs.

So, finally, provide decompressor_printk function, which fits on one
screen and trades features for simplicity.

It only supports "%s", "%x" and "%lx" specifiers and zero padding for
hex values.

Reviewed-by: Alexander Egorenkov <egorenar@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/boot/boot.h
arch/s390/boot/pgm_check_info.c

index d5cf2e8c5eb4681d67ef9e879bb99cd3a2e92dc7..ca485bfa4e503bccb73e2ae6f80a53a431c5d173 100644 (file)
@@ -4,6 +4,8 @@
 
 #include <linux/types.h>
 
+#include <linux/compiler.h>
+
 void startup_kernel(void);
 unsigned long detect_memory(void);
 bool is_ipl_block_dump(void);
@@ -14,6 +16,7 @@ void verify_facilities(void);
 void print_missing_facilities(void);
 void print_pgm_check_info(void);
 unsigned long get_random_base(unsigned long safe_addr);
+void __printf(1, 2) decompressor_printk(const char *fmt, ...);
 
 extern const char kernel_version[];
 extern unsigned long memory_limit;
index a3c9862bcede761c8ad1d1892b4165b910afb925..829548c2244459fe9a5b2b089e764014a8bb455c 100644 (file)
@@ -1,99 +1,86 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/kernel.h>
 #include <linux/string.h>
+#include <linux/ctype.h>
 #include <asm/lowcore.h>
 #include <asm/setup.h>
 #include <asm/sclp.h>
+#include <stdarg.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)
+static char *as_hex(char *dst, unsigned long val, int pad)
 {
-       while (count--)
-               dst = hex_byte_pack(dst, *src++);
-       return dst;
-}
+       char *p, *end = p = dst + max(pad, (int)__fls(val | 1) / 4 + 1);
 
-static char *add_str(char *dst, char *src)
-{
-       strcpy(dst, src);
-       return dst + strlen(dst);
+       for (*p-- = 0; p >= dst; val >>= 4)
+               *p-- = hex_asc[val & 0x0f];
+       return end;
 }
 
-void print_pgm_check_info(void)
+void decompressor_printk(const char *fmt, ...)
 {
-       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;
+       char buf[1024] = { 0 };
+       char *end = buf + sizeof(buf) - 1; /* make sure buf is 0 terminated */
+       unsigned long pad;
+       char *p = buf;
+       va_list args;
 
-       add_str(buf, "Linux version ");
-       strlcat(buf, kernel_version, sizeof(buf) - 1);
-       strlcat(buf, "\n", 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);
-
-       if (kaslr_enabled) {
-               p = add_str(buf, "Kernel random base: ");
-               p = add_val_as_hex(p, __kaslr_offset);
-               add_str(p, "\n");
-               sclp_early_printk(buf);
+       va_start(args, fmt);
+       for (; p < end && *fmt; fmt++) {
+               if (*fmt != '%') {
+                       *p++ = *fmt;
+                       continue;
+               }
+               pad = isdigit(*++fmt) ? simple_strtol(fmt, (char **)&fmt, 10) : 0;
+               switch (*fmt) {
+               case 's':
+                       p = buf + strlcat(buf, va_arg(args, char *), sizeof(buf));
+                       break;
+               case 'l':
+                       if (*++fmt != 'x' || end - p <= max(sizeof(long) * 2, pad))
+                               goto out;
+                       p = as_hex(p, va_arg(args, unsigned long), pad);
+                       break;
+               case 'x':
+                       if (end - p <= max(sizeof(int) * 2, pad))
+                               goto out;
+                       p = as_hex(p, va_arg(args, unsigned int), pad);
+                       break;
+               default:
+                       goto out;
+               }
        }
-
-       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");
+out:
+       va_end(args);
        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);
+void print_pgm_check_info(void)
+{
+       unsigned long *gpregs = (unsigned long *)S390_lowcore.gpregs_save_area;
+       struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area);
 
-       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);
-       }
+       decompressor_printk("Linux version %s\n", kernel_version);
+       decompressor_printk("Kernel fault: interruption code %04x ilc:%x\n",
+                           S390_lowcore.pgm_code, S390_lowcore.pgm_ilc >> 1);
+       if (kaslr_enabled)
+               decompressor_printk("Kernel random base: %lx\n", __kaslr_offset);
+       decompressor_printk("PSW : %016lx %016lx\n",
+                           S390_lowcore.psw_save_area.mask,
+                           S390_lowcore.psw_save_area.addr);
+       decompressor_printk(
+               "      R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x P:%x AS:%x CC:%x PM:%x RI:%x EA:%x\n",
+               psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck,
+               psw->wait, psw->pstate, psw->as, psw->cc, psw->pm, psw->ri,
+               psw->eaba);
+       decompressor_printk("GPRS: %016lx %016lx %016lx %016lx\n",
+                           gpregs[0], gpregs[1], gpregs[2], gpregs[3]);
+       decompressor_printk("      %016lx %016lx %016lx %016lx\n",
+                           gpregs[4], gpregs[5], gpregs[6], gpregs[7]);
+       decompressor_printk("      %016lx %016lx %016lx %016lx\n",
+                           gpregs[8], gpregs[9], gpregs[10], gpregs[11]);
+       decompressor_printk("      %016lx %016lx %016lx %016lx\n",
+                           gpregs[12], gpregs[13], gpregs[14], gpregs[15]);
 }