xref: /linux/arch/powerpc/platforms/pseries/rtas-fadump.c (revision ff2632d7d08edc11e8bd0629e9fcfebab25c78b4)
1d3833a70SHari Bathini // SPDX-License-Identifier: GPL-2.0-or-later
2d3833a70SHari Bathini /*
3d3833a70SHari Bathini  * Firmware-Assisted Dump support on POWERVM platform.
4d3833a70SHari Bathini  *
5d3833a70SHari Bathini  * Copyright 2011, Mahesh Salgaonkar, IBM Corporation.
6d3833a70SHari Bathini  * Copyright 2019, Hari Bathini, IBM Corporation.
7d3833a70SHari Bathini  */
8d3833a70SHari Bathini 
9d3833a70SHari Bathini #define pr_fmt(fmt) "rtas fadump: " fmt
10d3833a70SHari Bathini 
11d3833a70SHari Bathini #include <linux/string.h>
12d3833a70SHari Bathini #include <linux/memblock.h>
13d3833a70SHari Bathini #include <linux/delay.h>
14d3833a70SHari Bathini #include <linux/seq_file.h>
15d3833a70SHari Bathini #include <linux/crash_dump.h>
16e6f6390aSChristophe Leroy #include <linux/of.h>
17e6f6390aSChristophe Leroy #include <linux/of_fdt.h>
18d3833a70SHari Bathini 
19d3833a70SHari Bathini #include <asm/page.h>
20d3833a70SHari Bathini #include <asm/rtas.h>
21*683eab94SHari Bathini #include <asm/setup.h>
22d3833a70SHari Bathini #include <asm/fadump.h>
23d3833a70SHari Bathini #include <asm/fadump-internal.h>
24d3833a70SHari Bathini 
25d3833a70SHari Bathini #include "rtas-fadump.h"
26d3833a70SHari Bathini 
2741a65d16SHari Bathini static struct rtas_fadump_mem_struct fdm;
28f3512011SHari Bathini static const struct rtas_fadump_mem_struct *fdm_active;
2941a65d16SHari Bathini 
rtas_fadump_update_config(struct fw_dump * fadump_conf,const struct rtas_fadump_mem_struct * fdm)3041a65d16SHari Bathini static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
3141a65d16SHari Bathini 				      const struct rtas_fadump_mem_struct *fdm)
3241a65d16SHari Bathini {
3341a65d16SHari Bathini 	fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
3441a65d16SHari Bathini 				       fadump_conf->boot_memory_size);
3541a65d16SHari Bathini }
3641a65d16SHari Bathini 
37f3512011SHari Bathini /*
38f3512011SHari Bathini  * This function is called in the capture kernel to get configuration details
39f3512011SHari Bathini  * setup in the first kernel and passed to the f/w.
40f3512011SHari Bathini  */
rtas_fadump_get_config(struct fw_dump * fadump_conf,const struct rtas_fadump_mem_struct * fdm)41e14ff96dSNick Child static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
42f3512011SHari Bathini 				   const struct rtas_fadump_mem_struct *fdm)
43f3512011SHari Bathini {
4478d5cc15SHari Bathini 	unsigned long base, size, last_end, hole_size;
457dee93a9SHari Bathini 
4678d5cc15SHari Bathini 	last_end = 0;
4778d5cc15SHari Bathini 	hole_size = 0;
4878d5cc15SHari Bathini 	fadump_conf->boot_memory_size = 0;
4978d5cc15SHari Bathini 	fadump_conf->boot_mem_regs_cnt = 0;
5078d5cc15SHari Bathini 	pr_debug("Boot memory regions:\n");
5178d5cc15SHari Bathini 	for (int i = 0; i < be16_to_cpu(fdm->header.dump_num_sections); i++) {
5278d5cc15SHari Bathini 		int type = be16_to_cpu(fdm->rgn[i].source_data_type);
5378d5cc15SHari Bathini 		u64 addr;
54f3512011SHari Bathini 
5578d5cc15SHari Bathini 		switch (type) {
5678d5cc15SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
5778d5cc15SHari Bathini 			addr = be64_to_cpu(fdm->rgn[i].destination_address);
5878d5cc15SHari Bathini 
5978d5cc15SHari Bathini 			fadump_conf->cpu_state_dest_vaddr = (u64)__va(addr);
60f3512011SHari Bathini 			/*
61f3512011SHari Bathini 			 * Start address of reserve dump area (permanent reservation) for
62f3512011SHari Bathini 			 * re-registering FADump after dump capture.
63f3512011SHari Bathini 			 */
6478d5cc15SHari Bathini 			fadump_conf->reserve_dump_area_start = addr;
6578d5cc15SHari Bathini 			break;
6678d5cc15SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
6778d5cc15SHari Bathini 			/* Not processed currently. */
6878d5cc15SHari Bathini 			break;
6978d5cc15SHari Bathini 		case RTAS_FADUMP_REAL_MODE_REGION:
7078d5cc15SHari Bathini 			base = be64_to_cpu(fdm->rgn[i].source_address);
7178d5cc15SHari Bathini 			size = be64_to_cpu(fdm->rgn[i].source_len);
7278d5cc15SHari Bathini 			pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
7378d5cc15SHari Bathini 			if (!base) {
7478d5cc15SHari Bathini 				fadump_conf->boot_mem_dest_addr =
7578d5cc15SHari Bathini 					be64_to_cpu(fdm->rgn[i].destination_address);
7678d5cc15SHari Bathini 			}
7778d5cc15SHari Bathini 
7878d5cc15SHari Bathini 			fadump_conf->boot_mem_addr[fadump_conf->boot_mem_regs_cnt] = base;
7978d5cc15SHari Bathini 			fadump_conf->boot_mem_sz[fadump_conf->boot_mem_regs_cnt] = size;
8078d5cc15SHari Bathini 			fadump_conf->boot_memory_size += size;
8178d5cc15SHari Bathini 			hole_size += (base - last_end);
8278d5cc15SHari Bathini 			last_end = base + size;
8378d5cc15SHari Bathini 			fadump_conf->boot_mem_regs_cnt++;
8478d5cc15SHari Bathini 			break;
85*683eab94SHari Bathini 		case RTAS_FADUMP_PARAM_AREA:
86*683eab94SHari Bathini 			fadump_conf->param_area = be64_to_cpu(fdm->rgn[i].destination_address);
87*683eab94SHari Bathini 			break;
8878d5cc15SHari Bathini 		default:
8978d5cc15SHari Bathini 			pr_warn("Section type %d unsupported on this kernel. Ignoring!\n", type);
9078d5cc15SHari Bathini 			break;
9178d5cc15SHari Bathini 		}
9278d5cc15SHari Bathini 	}
9378d5cc15SHari Bathini 	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size + hole_size;
94f3512011SHari Bathini 
95f3512011SHari Bathini 	rtas_fadump_update_config(fadump_conf, fdm);
96f3512011SHari Bathini }
97f3512011SHari Bathini 
rtas_fadump_init_mem_struct(struct fw_dump * fadump_conf)98d3833a70SHari Bathini static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
99d3833a70SHari Bathini {
10041a65d16SHari Bathini 	u64 addr = fadump_conf->reserve_dump_area_start;
10178d5cc15SHari Bathini 	u16 sec_cnt = 0;
10241a65d16SHari Bathini 
10341a65d16SHari Bathini 	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
10441a65d16SHari Bathini 	addr = addr & PAGE_MASK;
10541a65d16SHari Bathini 
10641a65d16SHari Bathini 	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
10741a65d16SHari Bathini 	fdm.header.dump_status_flag = 0;
10841a65d16SHari Bathini 	fdm.header.offset_first_dump_section =
10978d5cc15SHari Bathini 		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, rgn));
11041a65d16SHari Bathini 
11141a65d16SHari Bathini 	/*
11241a65d16SHari Bathini 	 * Fields for disk dump option.
11341a65d16SHari Bathini 	 * We are not using disk dump option, hence set these fields to 0.
11441a65d16SHari Bathini 	 */
11541a65d16SHari Bathini 	fdm.header.dd_block_size = 0;
11641a65d16SHari Bathini 	fdm.header.dd_block_offset = 0;
11741a65d16SHari Bathini 	fdm.header.dd_num_blocks = 0;
11841a65d16SHari Bathini 	fdm.header.dd_offset_disk_path = 0;
11941a65d16SHari Bathini 
12041a65d16SHari Bathini 	/* set 0 to disable an automatic dump-reboot. */
12141a65d16SHari Bathini 	fdm.header.max_time_auto = 0;
12241a65d16SHari Bathini 
12341a65d16SHari Bathini 	/* Kernel dump sections */
12441a65d16SHari Bathini 	/* cpu state data section. */
12578d5cc15SHari Bathini 	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
12678d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
12778d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_address = 0;
12878d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->cpu_state_data_size);
12978d5cc15SHari Bathini 	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
13041a65d16SHari Bathini 	addr += fadump_conf->cpu_state_data_size;
13178d5cc15SHari Bathini 	sec_cnt++;
13241a65d16SHari Bathini 
13341a65d16SHari Bathini 	/* hpte region section */
13478d5cc15SHari Bathini 	fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
13578d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
13678d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_address = 0;
13778d5cc15SHari Bathini 	fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->hpte_region_size);
13878d5cc15SHari Bathini 	fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
13941a65d16SHari Bathini 	addr += fadump_conf->hpte_region_size;
14078d5cc15SHari Bathini 	sec_cnt++;
14141a65d16SHari Bathini 
1429cf3b3a3SHari Bathini 	/*
1439cf3b3a3SHari Bathini 	 * Align boot memory area destination address to page boundary to
1449cf3b3a3SHari Bathini 	 * be able to mmap read this area in the vmcore.
1459cf3b3a3SHari Bathini 	 */
1469cf3b3a3SHari Bathini 	addr = PAGE_ALIGN(addr);
1479cf3b3a3SHari Bathini 
14878d5cc15SHari Bathini 	/* First boot memory region destination address */
14978d5cc15SHari Bathini 	fadump_conf->boot_mem_dest_addr = addr;
15078d5cc15SHari Bathini 	for (int i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
15178d5cc15SHari Bathini 		/* Boot memory regions */
15278d5cc15SHari Bathini 		fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
15378d5cc15SHari Bathini 		fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
15478d5cc15SHari Bathini 		fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->boot_mem_addr[i]);
15578d5cc15SHari Bathini 		fdm.rgn[sec_cnt].source_len = cpu_to_be64(fadump_conf->boot_mem_sz[i]);
15678d5cc15SHari Bathini 		fdm.rgn[sec_cnt].destination_address = cpu_to_be64(addr);
15778d5cc15SHari Bathini 		addr += fadump_conf->boot_mem_sz[i];
15878d5cc15SHari Bathini 		sec_cnt++;
15978d5cc15SHari Bathini 	}
16041a65d16SHari Bathini 
161*683eab94SHari Bathini 	/* Parameters area */
162*683eab94SHari Bathini 	if (fadump_conf->param_area) {
163*683eab94SHari Bathini 		fdm.rgn[sec_cnt].request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
164*683eab94SHari Bathini 		fdm.rgn[sec_cnt].source_data_type = cpu_to_be16(RTAS_FADUMP_PARAM_AREA);
165*683eab94SHari Bathini 		fdm.rgn[sec_cnt].source_address = cpu_to_be64(fadump_conf->param_area);
166*683eab94SHari Bathini 		fdm.rgn[sec_cnt].source_len = cpu_to_be64(COMMAND_LINE_SIZE);
167*683eab94SHari Bathini 		fdm.rgn[sec_cnt].destination_address = cpu_to_be64(fadump_conf->param_area);
168*683eab94SHari Bathini 		sec_cnt++;
169*683eab94SHari Bathini 	}
17078d5cc15SHari Bathini 	fdm.header.dump_num_sections = cpu_to_be16(sec_cnt);
171*683eab94SHari Bathini 
17241a65d16SHari Bathini 	rtas_fadump_update_config(fadump_conf, &fdm);
17341a65d16SHari Bathini 
17441a65d16SHari Bathini 	return addr;
175d3833a70SHari Bathini }
176d3833a70SHari Bathini 
rtas_fadump_get_bootmem_min(void)1777b1b3b48SHari Bathini static u64 rtas_fadump_get_bootmem_min(void)
1787b1b3b48SHari Bathini {
1797b1b3b48SHari Bathini 	return RTAS_FADUMP_MIN_BOOT_MEM;
1807b1b3b48SHari Bathini }
1817b1b3b48SHari Bathini 
rtas_fadump_register(struct fw_dump * fadump_conf)182d3833a70SHari Bathini static int rtas_fadump_register(struct fw_dump *fadump_conf)
183d3833a70SHari Bathini {
18478d5cc15SHari Bathini 	unsigned int wait_time, fdm_size;
18541a65d16SHari Bathini 	int rc, err = -EIO;
18641a65d16SHari Bathini 
18778d5cc15SHari Bathini 	/*
18878d5cc15SHari Bathini 	 * Platform requires the exact size of the Dump Memory Structure.
18978d5cc15SHari Bathini 	 * Avoid including any unused rgns in the calculation, as this
19078d5cc15SHari Bathini 	 * could result in a parameter error (-3) from the platform.
19178d5cc15SHari Bathini 	 */
19278d5cc15SHari Bathini 	fdm_size = sizeof(struct rtas_fadump_section_header);
19378d5cc15SHari Bathini 	fdm_size += be16_to_cpu(fdm.header.dump_num_sections) * sizeof(struct rtas_fadump_section);
19478d5cc15SHari Bathini 
19541a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
19641a65d16SHari Bathini 	do {
19741a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
19878d5cc15SHari Bathini 				NULL, FADUMP_REGISTER, &fdm, fdm_size);
19941a65d16SHari Bathini 
20041a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
20141a65d16SHari Bathini 		if (wait_time)
20241a65d16SHari Bathini 			mdelay(wait_time);
20341a65d16SHari Bathini 
20441a65d16SHari Bathini 	} while (wait_time);
20541a65d16SHari Bathini 
20641a65d16SHari Bathini 	switch (rc) {
20741a65d16SHari Bathini 	case 0:
20841a65d16SHari Bathini 		pr_info("Registration is successful!\n");
20941a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
21041a65d16SHari Bathini 		err = 0;
21141a65d16SHari Bathini 		break;
21241a65d16SHari Bathini 	case -1:
21341a65d16SHari Bathini 		pr_err("Failed to register. Hardware Error(%d).\n", rc);
21441a65d16SHari Bathini 		break;
21541a65d16SHari Bathini 	case -3:
21678d5cc15SHari Bathini 		if (!is_fadump_reserved_mem_contiguous())
21741a65d16SHari Bathini 			pr_err("Can't have holes in reserved memory area.\n");
21841a65d16SHari Bathini 
21941a65d16SHari Bathini 		pr_err("Failed to register. Parameter Error(%d).\n", rc);
22041a65d16SHari Bathini 		err = -EINVAL;
22141a65d16SHari Bathini 		break;
22241a65d16SHari Bathini 	case -9:
22341a65d16SHari Bathini 		pr_err("Already registered!\n");
22441a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
22541a65d16SHari Bathini 		err = -EEXIST;
22641a65d16SHari Bathini 		break;
22741a65d16SHari Bathini 	default:
22841a65d16SHari Bathini 		pr_err("Failed to register. Unknown Error(%d).\n", rc);
22941a65d16SHari Bathini 		break;
23041a65d16SHari Bathini 	}
23141a65d16SHari Bathini 
23241a65d16SHari Bathini 	return err;
233d3833a70SHari Bathini }
234d3833a70SHari Bathini 
rtas_fadump_unregister(struct fw_dump * fadump_conf)235d3833a70SHari Bathini static int rtas_fadump_unregister(struct fw_dump *fadump_conf)
236d3833a70SHari Bathini {
23741a65d16SHari Bathini 	unsigned int wait_time;
23841a65d16SHari Bathini 	int rc;
23941a65d16SHari Bathini 
24041a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
24141a65d16SHari Bathini 	do {
24241a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
24341a65d16SHari Bathini 				NULL, FADUMP_UNREGISTER, &fdm,
24441a65d16SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
24541a65d16SHari Bathini 
24641a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
24741a65d16SHari Bathini 		if (wait_time)
24841a65d16SHari Bathini 			mdelay(wait_time);
24941a65d16SHari Bathini 	} while (wait_time);
25041a65d16SHari Bathini 
25141a65d16SHari Bathini 	if (rc) {
25241a65d16SHari Bathini 		pr_err("Failed to un-register - unexpected error(%d).\n", rc);
253d3833a70SHari Bathini 		return -EIO;
254d3833a70SHari Bathini 	}
255d3833a70SHari Bathini 
25641a65d16SHari Bathini 	fadump_conf->dump_registered = 0;
25741a65d16SHari Bathini 	return 0;
25841a65d16SHari Bathini }
25941a65d16SHari Bathini 
rtas_fadump_invalidate(struct fw_dump * fadump_conf)260d3833a70SHari Bathini static int rtas_fadump_invalidate(struct fw_dump *fadump_conf)
261d3833a70SHari Bathini {
262f3512011SHari Bathini 	unsigned int wait_time;
263f3512011SHari Bathini 	int rc;
264f3512011SHari Bathini 
265f3512011SHari Bathini 	/* TODO: Add upper time limit for the delay */
266f3512011SHari Bathini 	do {
267f3512011SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
268f3512011SHari Bathini 				NULL, FADUMP_INVALIDATE, fdm_active,
269f3512011SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
270f3512011SHari Bathini 
271f3512011SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
272f3512011SHari Bathini 		if (wait_time)
273f3512011SHari Bathini 			mdelay(wait_time);
274f3512011SHari Bathini 	} while (wait_time);
275f3512011SHari Bathini 
276f3512011SHari Bathini 	if (rc) {
277f3512011SHari Bathini 		pr_err("Failed to invalidate - unexpected error (%d).\n", rc);
278d3833a70SHari Bathini 		return -EIO;
279d3833a70SHari Bathini 	}
280d3833a70SHari Bathini 
281f3512011SHari Bathini 	fadump_conf->dump_active = 0;
282f3512011SHari Bathini 	fdm_active = NULL;
283f3512011SHari Bathini 	return 0;
284f3512011SHari Bathini }
285f3512011SHari Bathini 
286f3512011SHari Bathini #define RTAS_FADUMP_GPR_MASK		0xffffff0000000000
rtas_fadump_gpr_index(u64 id)287f3512011SHari Bathini static inline int rtas_fadump_gpr_index(u64 id)
288f3512011SHari Bathini {
289f3512011SHari Bathini 	char str[3];
290f3512011SHari Bathini 	int i = -1;
291f3512011SHari Bathini 
292f3512011SHari Bathini 	if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) {
293f3512011SHari Bathini 		/* get the digits at the end */
294f3512011SHari Bathini 		id &= ~RTAS_FADUMP_GPR_MASK;
295f3512011SHari Bathini 		id >>= 24;
296f3512011SHari Bathini 		str[2] = '\0';
297f3512011SHari Bathini 		str[1] = id & 0xff;
298f3512011SHari Bathini 		str[0] = (id >> 8) & 0xff;
299f3512011SHari Bathini 		if (kstrtoint(str, 10, &i))
300f3512011SHari Bathini 			i = -EINVAL;
301f3512011SHari Bathini 		if (i > 31)
302f3512011SHari Bathini 			i = -1;
303f3512011SHari Bathini 	}
304f3512011SHari Bathini 	return i;
305f3512011SHari Bathini }
306f3512011SHari Bathini 
rtas_fadump_set_regval(struct pt_regs * regs,u64 reg_id,u64 reg_val)307e14ff96dSNick Child static void __init rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val)
308f3512011SHari Bathini {
309f3512011SHari Bathini 	int i;
310f3512011SHari Bathini 
311f3512011SHari Bathini 	i = rtas_fadump_gpr_index(reg_id);
312f3512011SHari Bathini 	if (i >= 0)
313f3512011SHari Bathini 		regs->gpr[i] = (unsigned long)reg_val;
314f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("NIA"))
315f3512011SHari Bathini 		regs->nip = (unsigned long)reg_val;
316f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("MSR"))
317f3512011SHari Bathini 		regs->msr = (unsigned long)reg_val;
318f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("CTR"))
319f3512011SHari Bathini 		regs->ctr = (unsigned long)reg_val;
320f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("LR"))
321f3512011SHari Bathini 		regs->link = (unsigned long)reg_val;
322f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("XER"))
323f3512011SHari Bathini 		regs->xer = (unsigned long)reg_val;
324f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("CR"))
325f3512011SHari Bathini 		regs->ccr = (unsigned long)reg_val;
326f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("DAR"))
327f3512011SHari Bathini 		regs->dar = (unsigned long)reg_val;
328f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("DSISR"))
329f3512011SHari Bathini 		regs->dsisr = (unsigned long)reg_val;
330f3512011SHari Bathini }
331f3512011SHari Bathini 
332e14ff96dSNick Child static struct rtas_fadump_reg_entry* __init
rtas_fadump_read_regs(struct rtas_fadump_reg_entry * reg_entry,struct pt_regs * regs)333f3512011SHari Bathini rtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry,
334f3512011SHari Bathini 		      struct pt_regs *regs)
335f3512011SHari Bathini {
336f3512011SHari Bathini 	memset(regs, 0, sizeof(struct pt_regs));
337f3512011SHari Bathini 
338f3512011SHari Bathini 	while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) {
339f3512011SHari Bathini 		rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
340f3512011SHari Bathini 				       be64_to_cpu(reg_entry->reg_value));
341f3512011SHari Bathini 		reg_entry++;
342f3512011SHari Bathini 	}
343f3512011SHari Bathini 	reg_entry++;
344f3512011SHari Bathini 	return reg_entry;
345f3512011SHari Bathini }
346f3512011SHari Bathini 
347f3512011SHari Bathini /*
348f3512011SHari Bathini  * Read CPU state dump data and convert it into ELF notes.
349f3512011SHari Bathini  * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
350f3512011SHari Bathini  * used to access the data to allow for additional fields to be added without
351f3512011SHari Bathini  * affecting compatibility. Each list of registers for a CPU starts with
352f3512011SHari Bathini  * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
353f3512011SHari Bathini  * 8 Byte ASCII identifier and 8 Byte register value. The register entry
354f3512011SHari Bathini  * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
355f3512011SHari Bathini  * of register value. For more details refer to PAPR document.
356f3512011SHari Bathini  *
357f3512011SHari Bathini  * Only for the crashing cpu we ignore the CPU dump data and get exact
358f3512011SHari Bathini  * state from fadump crash info structure populated by first kernel at the
359f3512011SHari Bathini  * time of crash.
360f3512011SHari Bathini  */
rtas_fadump_build_cpu_notes(struct fw_dump * fadump_conf)361f3512011SHari Bathini static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
362f3512011SHari Bathini {
363f3512011SHari Bathini 	struct rtas_fadump_reg_save_area_header *reg_header;
364f3512011SHari Bathini 	struct fadump_crash_info_header *fdh = NULL;
365f3512011SHari Bathini 	struct rtas_fadump_reg_entry *reg_entry;
366f3512011SHari Bathini 	u32 num_cpus, *note_buf;
367f3512011SHari Bathini 	int i, rc = 0, cpu = 0;
368f3512011SHari Bathini 	struct pt_regs regs;
369f3512011SHari Bathini 	void *vaddr;
370f3512011SHari Bathini 
37178d5cc15SHari Bathini 	vaddr = (void *)fadump_conf->cpu_state_dest_vaddr;
372f3512011SHari Bathini 
373f3512011SHari Bathini 	reg_header = vaddr;
374f3512011SHari Bathini 	if (be64_to_cpu(reg_header->magic_number) !=
375f3512011SHari Bathini 	    fadump_str_to_u64("REGSAVE")) {
376f3512011SHari Bathini 		pr_err("Unable to read register save area.\n");
377f3512011SHari Bathini 		return -ENOENT;
378f3512011SHari Bathini 	}
379f3512011SHari Bathini 
380f3512011SHari Bathini 	pr_debug("--------CPU State Data------------\n");
381f3512011SHari Bathini 	pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
382f3512011SHari Bathini 	pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
383f3512011SHari Bathini 
384f3512011SHari Bathini 	vaddr += be32_to_cpu(reg_header->num_cpu_offset);
385f3512011SHari Bathini 	num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
386f3512011SHari Bathini 	pr_debug("NumCpus     : %u\n", num_cpus);
387f3512011SHari Bathini 	vaddr += sizeof(u32);
388f3512011SHari Bathini 	reg_entry = (struct rtas_fadump_reg_entry *)vaddr;
389f3512011SHari Bathini 
390f3512011SHari Bathini 	rc = fadump_setup_cpu_notes_buf(num_cpus);
391f3512011SHari Bathini 	if (rc != 0)
392f3512011SHari Bathini 		return rc;
393f3512011SHari Bathini 
394f3512011SHari Bathini 	note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr;
395f3512011SHari Bathini 
396f3512011SHari Bathini 	if (fadump_conf->fadumphdr_addr)
397f3512011SHari Bathini 		fdh = __va(fadump_conf->fadumphdr_addr);
398f3512011SHari Bathini 
399f3512011SHari Bathini 	for (i = 0; i < num_cpus; i++) {
400f3512011SHari Bathini 		if (be64_to_cpu(reg_entry->reg_id) !=
401f3512011SHari Bathini 		    fadump_str_to_u64("CPUSTRT")) {
402f3512011SHari Bathini 			pr_err("Unable to read CPU state data\n");
403f3512011SHari Bathini 			rc = -ENOENT;
404f3512011SHari Bathini 			goto error_out;
405f3512011SHari Bathini 		}
406f3512011SHari Bathini 		/* Lower 4 bytes of reg_value contains logical cpu id */
407f3512011SHari Bathini 		cpu = (be64_to_cpu(reg_entry->reg_value) &
408f3512011SHari Bathini 		       RTAS_FADUMP_CPU_ID_MASK);
4096584cec0SHari Bathini 		if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_mask)) {
410f3512011SHari Bathini 			RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
411f3512011SHari Bathini 			continue;
412f3512011SHari Bathini 		}
413f3512011SHari Bathini 		pr_debug("Reading register data for cpu %d...\n", cpu);
414f3512011SHari Bathini 		if (fdh && fdh->crashing_cpu == cpu) {
415f3512011SHari Bathini 			regs = fdh->regs;
416f3512011SHari Bathini 			note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
417f3512011SHari Bathini 			RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
418f3512011SHari Bathini 		} else {
419f3512011SHari Bathini 			reg_entry++;
420f3512011SHari Bathini 			reg_entry = rtas_fadump_read_regs(reg_entry, &regs);
421f3512011SHari Bathini 			note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
422f3512011SHari Bathini 		}
423f3512011SHari Bathini 	}
424f3512011SHari Bathini 	final_note(note_buf);
425f3512011SHari Bathini 
426c6c5b14dSSourabh Jain 	pr_debug("Updating elfcore header (%llx) with cpu notes\n", fadump_conf->elfcorehdr_addr);
427c6c5b14dSSourabh Jain 	fadump_update_elfcore_header((char *)fadump_conf->elfcorehdr_addr);
428f3512011SHari Bathini 	return 0;
429f3512011SHari Bathini 
430f3512011SHari Bathini error_out:
431f3512011SHari Bathini 	fadump_free_cpu_notes_buf();
432f3512011SHari Bathini 	return rc;
433f3512011SHari Bathini 
434f3512011SHari Bathini }
435f3512011SHari Bathini 
436d3833a70SHari Bathini /*
437c6c5b14dSSourabh Jain  * Validate and process the dump data stored by the firmware, and update
438c6c5b14dSSourabh Jain  * the CPU notes of elfcorehdr.
439d3833a70SHari Bathini  */
rtas_fadump_process(struct fw_dump * fadump_conf)440d3833a70SHari Bathini static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
441d3833a70SHari Bathini {
442f3512011SHari Bathini 	if (!fdm_active || !fadump_conf->fadumphdr_addr)
443d3833a70SHari Bathini 		return -EINVAL;
444f3512011SHari Bathini 
445f3512011SHari Bathini 	/* Check if the dump data is valid. */
44678d5cc15SHari Bathini 	for (int i = 0; i < be16_to_cpu(fdm_active->header.dump_num_sections); i++) {
44778d5cc15SHari Bathini 		int type = be16_to_cpu(fdm_active->rgn[i].source_data_type);
44878d5cc15SHari Bathini 		int rc = 0;
44978d5cc15SHari Bathini 
45078d5cc15SHari Bathini 		switch (type) {
45178d5cc15SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
45278d5cc15SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
45378d5cc15SHari Bathini 		case RTAS_FADUMP_REAL_MODE_REGION:
45478d5cc15SHari Bathini 			if (fdm_active->rgn[i].error_flags != 0) {
45578d5cc15SHari Bathini 				pr_err("Dump taken by platform is not valid (%d)\n", i);
45678d5cc15SHari Bathini 				rc = -EINVAL;
457f3512011SHari Bathini 			}
45878d5cc15SHari Bathini 			if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len) {
45978d5cc15SHari Bathini 				pr_err("Dump taken by platform is incomplete (%d)\n", i);
46078d5cc15SHari Bathini 				rc = -EINVAL;
46178d5cc15SHari Bathini 			}
46278d5cc15SHari Bathini 			if (rc) {
46378d5cc15SHari Bathini 				pr_warn("Region type: %u src addr: 0x%llx dest addr: 0x%llx\n",
46478d5cc15SHari Bathini 					be16_to_cpu(fdm_active->rgn[i].source_data_type),
46578d5cc15SHari Bathini 					be64_to_cpu(fdm_active->rgn[i].source_address),
46678d5cc15SHari Bathini 					be64_to_cpu(fdm_active->rgn[i].destination_address));
46778d5cc15SHari Bathini 				return rc;
46878d5cc15SHari Bathini 			}
46978d5cc15SHari Bathini 			break;
470*683eab94SHari Bathini 		case RTAS_FADUMP_PARAM_AREA:
471*683eab94SHari Bathini 			if (fdm_active->rgn[i].bytes_dumped != fdm_active->rgn[i].source_len ||
472*683eab94SHari Bathini 			    fdm_active->rgn[i].error_flags != 0) {
473*683eab94SHari Bathini 				pr_warn("Failed to process additional parameters! Proceeding anyway..\n");
474*683eab94SHari Bathini 				fadump_conf->param_area = 0;
475*683eab94SHari Bathini 			}
476*683eab94SHari Bathini 			break;
47778d5cc15SHari Bathini 		default:
47878d5cc15SHari Bathini 			/*
47978d5cc15SHari Bathini 			 * If the first/crashed kernel added a new region type that the
48078d5cc15SHari Bathini 			 * second/fadump kernel doesn't recognize, skip it and process
48178d5cc15SHari Bathini 			 * assuming backward compatibility.
48278d5cc15SHari Bathini 			 */
48378d5cc15SHari Bathini 			pr_warn("Unknown region found: type: %u src addr: 0x%llx dest addr: 0x%llx\n",
48478d5cc15SHari Bathini 				be16_to_cpu(fdm_active->rgn[i].source_data_type),
48578d5cc15SHari Bathini 				be64_to_cpu(fdm_active->rgn[i].source_address),
48678d5cc15SHari Bathini 				be64_to_cpu(fdm_active->rgn[i].destination_address));
48778d5cc15SHari Bathini 			break;
48878d5cc15SHari Bathini 		}
489f3512011SHari Bathini 	}
490f3512011SHari Bathini 
491c6c5b14dSSourabh Jain 	return rtas_fadump_build_cpu_notes(fadump_conf);
492d3833a70SHari Bathini }
493d3833a70SHari Bathini 
rtas_fadump_region_show(struct fw_dump * fadump_conf,struct seq_file * m)494d3833a70SHari Bathini static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
495d3833a70SHari Bathini 				    struct seq_file *m)
496d3833a70SHari Bathini {
497f3512011SHari Bathini 	const struct rtas_fadump_mem_struct *fdm_ptr;
498f3512011SHari Bathini 
499f3512011SHari Bathini 	if (fdm_active)
500f3512011SHari Bathini 		fdm_ptr = fdm_active;
501f3512011SHari Bathini 	else
502f3512011SHari Bathini 		fdm_ptr = &fdm;
50341a65d16SHari Bathini 
50478d5cc15SHari Bathini 
50578d5cc15SHari Bathini 	for (int i = 0; i < be16_to_cpu(fdm_ptr->header.dump_num_sections); i++) {
50678d5cc15SHari Bathini 		int type = be16_to_cpu(fdm_ptr->rgn[i].source_data_type);
50778d5cc15SHari Bathini 
50878d5cc15SHari Bathini 		switch (type) {
50978d5cc15SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
51041a65d16SHari Bathini 			seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
51178d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
51278d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
51378d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
51478d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
51578d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
51678d5cc15SHari Bathini 			break;
51778d5cc15SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
51841a65d16SHari Bathini 			seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
51978d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
52078d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
52178d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
52278d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
52378d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
52478d5cc15SHari Bathini 			break;
52578d5cc15SHari Bathini 		case RTAS_FADUMP_REAL_MODE_REGION:
526109f25ccSHari Bathini 			seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
52778d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_address),
52878d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
529109f25ccSHari Bathini 			seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
53078d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len),
53178d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].bytes_dumped));
53278d5cc15SHari Bathini 			break;
533*683eab94SHari Bathini 		case RTAS_FADUMP_PARAM_AREA:
534*683eab94SHari Bathini 			seq_printf(m, "\n[%#016llx-%#016llx]: cmdline append: '%s'\n",
535*683eab94SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address),
536*683eab94SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address) +
537*683eab94SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].source_len) - 1,
538*683eab94SHari Bathini 				   (char *)__va(be64_to_cpu(fdm_ptr->rgn[i].destination_address)));
539*683eab94SHari Bathini 			break;
54078d5cc15SHari Bathini 		default:
54178d5cc15SHari Bathini 			seq_printf(m, "Unknown region type %d : Src: %#016llx, Dest: %#016llx, ",
54278d5cc15SHari Bathini 				   type, be64_to_cpu(fdm_ptr->rgn[i].source_address),
54378d5cc15SHari Bathini 				   be64_to_cpu(fdm_ptr->rgn[i].destination_address));
54478d5cc15SHari Bathini 			break;
54578d5cc15SHari Bathini 		}
54678d5cc15SHari Bathini 	}
547f3512011SHari Bathini 
548a3ceb588SHari Bathini 	/* Dump is active. Show preserved area start address. */
549f3512011SHari Bathini 	if (fdm_active) {
550a3ceb588SHari Bathini 		seq_printf(m, "\nMemory above %#016llx is reserved for saving crash dump\n",
551a3ceb588SHari Bathini 			   fadump_conf->boot_mem_top);
552f3512011SHari Bathini 	}
553d3833a70SHari Bathini }
554d3833a70SHari Bathini 
rtas_fadump_trigger(struct fadump_crash_info_header * fdh,const char * msg)555d3833a70SHari Bathini static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
556d3833a70SHari Bathini 				const char *msg)
557d3833a70SHari Bathini {
558d3833a70SHari Bathini 	/* Call ibm,os-term rtas call to trigger firmware assisted dump */
559d3833a70SHari Bathini 	rtas_os_term((char *)msg);
560d3833a70SHari Bathini }
561d3833a70SHari Bathini 
56278d5cc15SHari Bathini /* FADUMP_MAX_MEM_REGS or lower */
rtas_fadump_max_boot_mem_rgns(void)56378d5cc15SHari Bathini static int rtas_fadump_max_boot_mem_rgns(void)
56478d5cc15SHari Bathini {
56578d5cc15SHari Bathini 	/*
56678d5cc15SHari Bathini 	 * Version 1 of Kernel Assisted Dump Memory Structure (PAPR) supports 10 sections.
56778d5cc15SHari Bathini 	 * With one each section taken for CPU state data & HPTE respectively, 8 sections
56878d5cc15SHari Bathini 	 * can be used for boot memory regions.
56978d5cc15SHari Bathini 	 *
57078d5cc15SHari Bathini 	 * If new region(s) is(are) defined, maximum boot memory regions will decrease
57178d5cc15SHari Bathini 	 * proportionally.
57278d5cc15SHari Bathini 	 */
57378d5cc15SHari Bathini 	return RTAS_FADUMP_MAX_BOOT_MEM_REGS;
57478d5cc15SHari Bathini }
57578d5cc15SHari Bathini 
576d3833a70SHari Bathini static struct fadump_ops rtas_fadump_ops = {
577d3833a70SHari Bathini 	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
5787b1b3b48SHari Bathini 	.fadump_get_bootmem_min		= rtas_fadump_get_bootmem_min,
579d3833a70SHari Bathini 	.fadump_register		= rtas_fadump_register,
580d3833a70SHari Bathini 	.fadump_unregister		= rtas_fadump_unregister,
581d3833a70SHari Bathini 	.fadump_invalidate		= rtas_fadump_invalidate,
582d3833a70SHari Bathini 	.fadump_process			= rtas_fadump_process,
583d3833a70SHari Bathini 	.fadump_region_show		= rtas_fadump_region_show,
584d3833a70SHari Bathini 	.fadump_trigger			= rtas_fadump_trigger,
58578d5cc15SHari Bathini 	.fadump_max_boot_mem_rgns	= rtas_fadump_max_boot_mem_rgns,
586d3833a70SHari Bathini };
587d3833a70SHari Bathini 
rtas_fadump_dt_scan(struct fw_dump * fadump_conf,u64 node)588d3833a70SHari Bathini void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
589d3833a70SHari Bathini {
590d3833a70SHari Bathini 	int i, size, num_sections;
591d3833a70SHari Bathini 	const __be32 *sections;
592d3833a70SHari Bathini 	const __be32 *token;
593d3833a70SHari Bathini 
594d3833a70SHari Bathini 	/*
595d3833a70SHari Bathini 	 * Check if Firmware Assisted dump is supported. if yes, check
596d3833a70SHari Bathini 	 * if dump has been initiated on last reboot.
597d3833a70SHari Bathini 	 */
598d3833a70SHari Bathini 	token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL);
599d3833a70SHari Bathini 	if (!token)
600d3833a70SHari Bathini 		return;
601d3833a70SHari Bathini 
602d3833a70SHari Bathini 	fadump_conf->ibm_configure_kernel_dump	= be32_to_cpu(*token);
603d3833a70SHari Bathini 	fadump_conf->ops			= &rtas_fadump_ops;
604d3833a70SHari Bathini 	fadump_conf->fadump_supported		= 1;
605*683eab94SHari Bathini 	fadump_conf->param_area_supported	= 1;
606d3833a70SHari Bathini 
6077dee93a9SHari Bathini 	/* Firmware supports 64-bit value for size, align it to pagesize. */
608e96d904eSChristophe Leroy 	fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE);
6097dee93a9SHari Bathini 
610f3512011SHari Bathini 	/*
611f3512011SHari Bathini 	 * The 'ibm,kernel-dump' rtas node is present only if there is
612f3512011SHari Bathini 	 * dump data waiting for us.
613f3512011SHari Bathini 	 */
614f3512011SHari Bathini 	fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
615f3512011SHari Bathini 	if (fdm_active) {
616f3512011SHari Bathini 		pr_info("Firmware-assisted dump is active.\n");
617f3512011SHari Bathini 		fadump_conf->dump_active = 1;
618f3512011SHari Bathini 		rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active));
619f3512011SHari Bathini 	}
620f3512011SHari Bathini 
621d3833a70SHari Bathini 	/* Get the sizes required to store dump data for the firmware provided
622d3833a70SHari Bathini 	 * dump sections.
623d3833a70SHari Bathini 	 * For each dump section type supported, a 32bit cell which defines
624d3833a70SHari Bathini 	 * the ID of a supported section followed by two 32 bit cells which
625d3833a70SHari Bathini 	 * gives the size of the section in bytes.
626d3833a70SHari Bathini 	 */
627d3833a70SHari Bathini 	sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
628d3833a70SHari Bathini 					&size);
629d3833a70SHari Bathini 
630d3833a70SHari Bathini 	if (!sections)
631d3833a70SHari Bathini 		return;
632d3833a70SHari Bathini 
633d3833a70SHari Bathini 	num_sections = size / (3 * sizeof(u32));
634d3833a70SHari Bathini 
635d3833a70SHari Bathini 	for (i = 0; i < num_sections; i++, sections += 3) {
636d3833a70SHari Bathini 		u32 type = (u32)of_read_number(sections, 1);
637d3833a70SHari Bathini 
638d3833a70SHari Bathini 		switch (type) {
639d3833a70SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
640d3833a70SHari Bathini 			fadump_conf->cpu_state_data_size =
641d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
642d3833a70SHari Bathini 			break;
643d3833a70SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
644d3833a70SHari Bathini 			fadump_conf->hpte_region_size =
645d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
646d3833a70SHari Bathini 			break;
647d3833a70SHari Bathini 		}
648d3833a70SHari Bathini 	}
649d3833a70SHari Bathini }
650