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