162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/kernel.h>
362306a36Sopenharmony_ci#include <linux/stdarg.h>
462306a36Sopenharmony_ci#include <linux/string.h>
562306a36Sopenharmony_ci#include <linux/ctype.h>
662306a36Sopenharmony_ci#include <asm/stacktrace.h>
762306a36Sopenharmony_ci#include <asm/boot_data.h>
862306a36Sopenharmony_ci#include <asm/lowcore.h>
962306a36Sopenharmony_ci#include <asm/setup.h>
1062306a36Sopenharmony_ci#include <asm/sclp.h>
1162306a36Sopenharmony_ci#include <asm/uv.h>
1262306a36Sopenharmony_ci#include "boot.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ciconst char hex_asc[] = "0123456789abcdef";
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic char *as_hex(char *dst, unsigned long val, int pad)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	char *p, *end = p = dst + max(pad, (int)__fls(val | 1) / 4 + 1);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	for (*p-- = 0; p >= dst; val >>= 4)
2162306a36Sopenharmony_ci		*p-- = hex_asc[val & 0x0f];
2262306a36Sopenharmony_ci	return end;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic char *symstart(char *p)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	while (*p)
2862306a36Sopenharmony_ci		p--;
2962306a36Sopenharmony_ci	return p + 1;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic noinline char *findsym(unsigned long ip, unsigned short *off, unsigned short *len)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	/* symbol entries are in a form "10000 c4 startup\0" */
3562306a36Sopenharmony_ci	char *a = _decompressor_syms_start;
3662306a36Sopenharmony_ci	char *b = _decompressor_syms_end;
3762306a36Sopenharmony_ci	unsigned long start;
3862306a36Sopenharmony_ci	unsigned long size;
3962306a36Sopenharmony_ci	char *pivot;
4062306a36Sopenharmony_ci	char *endp;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	while (a < b) {
4362306a36Sopenharmony_ci		pivot = symstart(a + (b - a) / 2);
4462306a36Sopenharmony_ci		start = simple_strtoull(pivot, &endp, 16);
4562306a36Sopenharmony_ci		size = simple_strtoull(endp + 1, &endp, 16);
4662306a36Sopenharmony_ci		if (ip < start) {
4762306a36Sopenharmony_ci			b = pivot;
4862306a36Sopenharmony_ci			continue;
4962306a36Sopenharmony_ci		}
5062306a36Sopenharmony_ci		if (ip > start + size) {
5162306a36Sopenharmony_ci			a = pivot + strlen(pivot) + 1;
5262306a36Sopenharmony_ci			continue;
5362306a36Sopenharmony_ci		}
5462306a36Sopenharmony_ci		*off = ip - start;
5562306a36Sopenharmony_ci		*len = size;
5662306a36Sopenharmony_ci		return endp + 1;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci	return NULL;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic noinline char *strsym(void *ip)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	static char buf[64];
6462306a36Sopenharmony_ci	unsigned short off;
6562306a36Sopenharmony_ci	unsigned short len;
6662306a36Sopenharmony_ci	char *p;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	p = findsym((unsigned long)ip, &off, &len);
6962306a36Sopenharmony_ci	if (p) {
7062306a36Sopenharmony_ci		strncpy(buf, p, sizeof(buf));
7162306a36Sopenharmony_ci		/* reserve 15 bytes for offset/len in symbol+0x1234/0x1234 */
7262306a36Sopenharmony_ci		p = buf + strnlen(buf, sizeof(buf) - 15);
7362306a36Sopenharmony_ci		strcpy(p, "+0x");
7462306a36Sopenharmony_ci		p = as_hex(p + 3, off, 0);
7562306a36Sopenharmony_ci		strcpy(p, "/0x");
7662306a36Sopenharmony_ci		as_hex(p + 3, len, 0);
7762306a36Sopenharmony_ci	} else {
7862306a36Sopenharmony_ci		as_hex(buf, (unsigned long)ip, 16);
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci	return buf;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_civoid decompressor_printk(const char *fmt, ...)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	char buf[1024] = { 0 };
8662306a36Sopenharmony_ci	char *end = buf + sizeof(buf) - 1; /* make sure buf is 0 terminated */
8762306a36Sopenharmony_ci	unsigned long pad;
8862306a36Sopenharmony_ci	char *p = buf;
8962306a36Sopenharmony_ci	va_list args;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	va_start(args, fmt);
9262306a36Sopenharmony_ci	for (; p < end && *fmt; fmt++) {
9362306a36Sopenharmony_ci		if (*fmt != '%') {
9462306a36Sopenharmony_ci			*p++ = *fmt;
9562306a36Sopenharmony_ci			continue;
9662306a36Sopenharmony_ci		}
9762306a36Sopenharmony_ci		pad = isdigit(*++fmt) ? simple_strtol(fmt, (char **)&fmt, 10) : 0;
9862306a36Sopenharmony_ci		switch (*fmt) {
9962306a36Sopenharmony_ci		case 's':
10062306a36Sopenharmony_ci			p = buf + strlcat(buf, va_arg(args, char *), sizeof(buf));
10162306a36Sopenharmony_ci			break;
10262306a36Sopenharmony_ci		case 'p':
10362306a36Sopenharmony_ci			if (*++fmt != 'S')
10462306a36Sopenharmony_ci				goto out;
10562306a36Sopenharmony_ci			p = buf + strlcat(buf, strsym(va_arg(args, void *)), sizeof(buf));
10662306a36Sopenharmony_ci			break;
10762306a36Sopenharmony_ci		case 'l':
10862306a36Sopenharmony_ci			if (*++fmt != 'x' || end - p <= max(sizeof(long) * 2, pad))
10962306a36Sopenharmony_ci				goto out;
11062306a36Sopenharmony_ci			p = as_hex(p, va_arg(args, unsigned long), pad);
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		case 'x':
11362306a36Sopenharmony_ci			if (end - p <= max(sizeof(int) * 2, pad))
11462306a36Sopenharmony_ci				goto out;
11562306a36Sopenharmony_ci			p = as_hex(p, va_arg(args, unsigned int), pad);
11662306a36Sopenharmony_ci			break;
11762306a36Sopenharmony_ci		default:
11862306a36Sopenharmony_ci			goto out;
11962306a36Sopenharmony_ci		}
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ciout:
12262306a36Sopenharmony_ci	va_end(args);
12362306a36Sopenharmony_ci	sclp_early_printk(buf);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_civoid print_stacktrace(unsigned long sp)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct stack_info boot_stack = { STACK_TYPE_TASK, (unsigned long)_stack_start,
12962306a36Sopenharmony_ci					 (unsigned long)_stack_end };
13062306a36Sopenharmony_ci	bool first = true;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	decompressor_printk("Call Trace:\n");
13362306a36Sopenharmony_ci	while (!(sp & 0x7) && on_stack(&boot_stack, sp, sizeof(struct stack_frame))) {
13462306a36Sopenharmony_ci		struct stack_frame *sf = (struct stack_frame *)sp;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		decompressor_printk(first ? "(sp:%016lx [<%016lx>] %pS)\n" :
13762306a36Sopenharmony_ci					    " sp:%016lx [<%016lx>] %pS\n",
13862306a36Sopenharmony_ci				    sp, sf->gprs[8], (void *)sf->gprs[8]);
13962306a36Sopenharmony_ci		if (sf->back_chain <= sp)
14062306a36Sopenharmony_ci			break;
14162306a36Sopenharmony_ci		sp = sf->back_chain;
14262306a36Sopenharmony_ci		first = false;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_civoid print_pgm_check_info(void)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned long *gpregs = (unsigned long *)S390_lowcore.gpregs_save_area;
14962306a36Sopenharmony_ci	struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	decompressor_printk("Linux version %s\n", kernel_version);
15262306a36Sopenharmony_ci	if (!is_prot_virt_guest() && early_command_line[0])
15362306a36Sopenharmony_ci		decompressor_printk("Kernel command line: %s\n", early_command_line);
15462306a36Sopenharmony_ci	decompressor_printk("Kernel fault: interruption code %04x ilc:%x\n",
15562306a36Sopenharmony_ci			    S390_lowcore.pgm_code, S390_lowcore.pgm_ilc >> 1);
15662306a36Sopenharmony_ci	if (kaslr_enabled())
15762306a36Sopenharmony_ci		decompressor_printk("Kernel random base: %lx\n", __kaslr_offset);
15862306a36Sopenharmony_ci	decompressor_printk("PSW : %016lx %016lx (%pS)\n",
15962306a36Sopenharmony_ci			    S390_lowcore.psw_save_area.mask,
16062306a36Sopenharmony_ci			    S390_lowcore.psw_save_area.addr,
16162306a36Sopenharmony_ci			    (void *)S390_lowcore.psw_save_area.addr);
16262306a36Sopenharmony_ci	decompressor_printk(
16362306a36Sopenharmony_ci		"      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",
16462306a36Sopenharmony_ci		psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck,
16562306a36Sopenharmony_ci		psw->wait, psw->pstate, psw->as, psw->cc, psw->pm, psw->ri,
16662306a36Sopenharmony_ci		psw->eaba);
16762306a36Sopenharmony_ci	decompressor_printk("GPRS: %016lx %016lx %016lx %016lx\n",
16862306a36Sopenharmony_ci			    gpregs[0], gpregs[1], gpregs[2], gpregs[3]);
16962306a36Sopenharmony_ci	decompressor_printk("      %016lx %016lx %016lx %016lx\n",
17062306a36Sopenharmony_ci			    gpregs[4], gpregs[5], gpregs[6], gpregs[7]);
17162306a36Sopenharmony_ci	decompressor_printk("      %016lx %016lx %016lx %016lx\n",
17262306a36Sopenharmony_ci			    gpregs[8], gpregs[9], gpregs[10], gpregs[11]);
17362306a36Sopenharmony_ci	decompressor_printk("      %016lx %016lx %016lx %016lx\n",
17462306a36Sopenharmony_ci			    gpregs[12], gpregs[13], gpregs[14], gpregs[15]);
17562306a36Sopenharmony_ci	print_stacktrace(S390_lowcore.gpregs_save_area[15]);
17662306a36Sopenharmony_ci	decompressor_printk("Last Breaking-Event-Address:\n");
17762306a36Sopenharmony_ci	decompressor_printk(" [<%016lx>] %pS\n", (unsigned long)S390_lowcore.pgm_last_break,
17862306a36Sopenharmony_ci			    (void *)S390_lowcore.pgm_last_break);
17962306a36Sopenharmony_ci}
180