linux/arch/s390/boot/pgm_check_info.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/kernel.h>
   3#include <linux/string.h>
   4#include <linux/ctype.h>
   5#include <asm/stacktrace.h>
   6#include <asm/boot_data.h>
   7#include <asm/lowcore.h>
   8#include <asm/setup.h>
   9#include <asm/sclp.h>
  10#include <asm/uv.h>
  11#include <stdarg.h>
  12#include "boot.h"
  13
  14const char hex_asc[] = "0123456789abcdef";
  15
  16static char *as_hex(char *dst, unsigned long val, int pad)
  17{
  18        char *p, *end = p = dst + max(pad, (int)__fls(val | 1) / 4 + 1);
  19
  20        for (*p-- = 0; p >= dst; val >>= 4)
  21                *p-- = hex_asc[val & 0x0f];
  22        return end;
  23}
  24
  25static char *symstart(char *p)
  26{
  27        while (*p)
  28                p--;
  29        return p + 1;
  30}
  31
  32extern char _decompressor_syms_start[], _decompressor_syms_end[];
  33static noinline char *findsym(unsigned long ip, unsigned short *off, unsigned short *len)
  34{
  35        /* symbol entries are in a form "10000 c4 startup\0" */
  36        char *a = _decompressor_syms_start;
  37        char *b = _decompressor_syms_end;
  38        unsigned long start;
  39        unsigned long size;
  40        char *pivot;
  41        char *endp;
  42
  43        while (a < b) {
  44                pivot = symstart(a + (b - a) / 2);
  45                start = simple_strtoull(pivot, &endp, 16);
  46                size = simple_strtoull(endp + 1, &endp, 16);
  47                if (ip < start) {
  48                        b = pivot;
  49                        continue;
  50                }
  51                if (ip > start + size) {
  52                        a = pivot + strlen(pivot) + 1;
  53                        continue;
  54                }
  55                *off = ip - start;
  56                *len = size;
  57                return endp + 1;
  58        }
  59        return NULL;
  60}
  61
  62static noinline char *strsym(void *ip)
  63{
  64        static char buf[64];
  65        unsigned short off;
  66        unsigned short len;
  67        char *p;
  68
  69        p = findsym((unsigned long)ip, &off, &len);
  70        if (p) {
  71                strncpy(buf, p, sizeof(buf));
  72                /* reserve 15 bytes for offset/len in symbol+0x1234/0x1234 */
  73                p = buf + strnlen(buf, sizeof(buf) - 15);
  74                strcpy(p, "+0x");
  75                p = as_hex(p + 3, off, 0);
  76                strcpy(p, "/0x");
  77                as_hex(p + 3, len, 0);
  78        } else {
  79                as_hex(buf, (unsigned long)ip, 16);
  80        }
  81        return buf;
  82}
  83
  84void decompressor_printk(const char *fmt, ...)
  85{
  86        char buf[1024] = { 0 };
  87        char *end = buf + sizeof(buf) - 1; /* make sure buf is 0 terminated */
  88        unsigned long pad;
  89        char *p = buf;
  90        va_list args;
  91
  92        va_start(args, fmt);
  93        for (; p < end && *fmt; fmt++) {
  94                if (*fmt != '%') {
  95                        *p++ = *fmt;
  96                        continue;
  97                }
  98                pad = isdigit(*++fmt) ? simple_strtol(fmt, (char **)&fmt, 10) : 0;
  99                switch (*fmt) {
 100                case 's':
 101                        p = buf + strlcat(buf, va_arg(args, char *), sizeof(buf));
 102                        break;
 103                case 'p':
 104                        if (*++fmt != 'S')
 105                                goto out;
 106                        p = buf + strlcat(buf, strsym(va_arg(args, void *)), sizeof(buf));
 107                        break;
 108                case 'l':
 109                        if (*++fmt != 'x' || end - p <= max(sizeof(long) * 2, pad))
 110                                goto out;
 111                        p = as_hex(p, va_arg(args, unsigned long), pad);
 112                        break;
 113                case 'x':
 114                        if (end - p <= max(sizeof(int) * 2, pad))
 115                                goto out;
 116                        p = as_hex(p, va_arg(args, unsigned int), pad);
 117                        break;
 118                default:
 119                        goto out;
 120                }
 121        }
 122out:
 123        va_end(args);
 124        sclp_early_printk(buf);
 125}
 126
 127static noinline void print_stacktrace(void)
 128{
 129        struct stack_info boot_stack = { STACK_TYPE_TASK, BOOT_STACK_OFFSET,
 130                                         BOOT_STACK_OFFSET + BOOT_STACK_SIZE };
 131        unsigned long sp = S390_lowcore.gpregs_save_area[15];
 132        bool first = true;
 133
 134        decompressor_printk("Call Trace:\n");
 135        while (!(sp & 0x7) && on_stack(&boot_stack, sp, sizeof(struct stack_frame))) {
 136                struct stack_frame *sf = (struct stack_frame *)sp;
 137
 138                decompressor_printk(first ? "(sp:%016lx [<%016lx>] %pS)\n" :
 139                                            " sp:%016lx [<%016lx>] %pS\n",
 140                                    sp, sf->gprs[8], (void *)sf->gprs[8]);
 141                if (sf->back_chain <= sp)
 142                        break;
 143                sp = sf->back_chain;
 144                first = false;
 145        }
 146}
 147
 148void print_pgm_check_info(void)
 149{
 150        unsigned long *gpregs = (unsigned long *)S390_lowcore.gpregs_save_area;
 151        struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area);
 152
 153        decompressor_printk("Linux version %s\n", kernel_version);
 154        if (!is_prot_virt_guest() && early_command_line[0])
 155                decompressor_printk("Kernel command line: %s\n", early_command_line);
 156        decompressor_printk("Kernel fault: interruption code %04x ilc:%x\n",
 157                            S390_lowcore.pgm_code, S390_lowcore.pgm_ilc >> 1);
 158        if (kaslr_enabled)
 159                decompressor_printk("Kernel random base: %lx\n", __kaslr_offset);
 160        decompressor_printk("PSW : %016lx %016lx (%pS)\n",
 161                            S390_lowcore.psw_save_area.mask,
 162                            S390_lowcore.psw_save_area.addr,
 163                            (void *)S390_lowcore.psw_save_area.addr);
 164        decompressor_printk(
 165                "      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",
 166                psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck,
 167                psw->wait, psw->pstate, psw->as, psw->cc, psw->pm, psw->ri,
 168                psw->eaba);
 169        decompressor_printk("GPRS: %016lx %016lx %016lx %016lx\n",
 170                            gpregs[0], gpregs[1], gpregs[2], gpregs[3]);
 171        decompressor_printk("      %016lx %016lx %016lx %016lx\n",
 172                            gpregs[4], gpregs[5], gpregs[6], gpregs[7]);
 173        decompressor_printk("      %016lx %016lx %016lx %016lx\n",
 174                            gpregs[8], gpregs[9], gpregs[10], gpregs[11]);
 175        decompressor_printk("      %016lx %016lx %016lx %016lx\n",
 176                            gpregs[12], gpregs[13], gpregs[14], gpregs[15]);
 177        print_stacktrace();
 178        decompressor_printk("Last Breaking-Event-Address:\n");
 179        decompressor_printk(" [<%016lx>] %pS\n", (unsigned long)S390_lowcore.breaking_event_addr,
 180                            (void *)S390_lowcore.breaking_event_addr);
 181}
 182