xref: /openbmc/linux/arch/powerpc/platforms/pseries/rtas-fadump.c (revision 109f25cc5fae5f8fb403d925f7b3731f6f18218a)
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>
16d3833a70SHari Bathini 
17d3833a70SHari Bathini #include <asm/page.h>
18d3833a70SHari Bathini #include <asm/prom.h>
19d3833a70SHari Bathini #include <asm/rtas.h>
20d3833a70SHari Bathini #include <asm/fadump.h>
21d3833a70SHari Bathini #include <asm/fadump-internal.h>
22d3833a70SHari Bathini 
23d3833a70SHari Bathini #include "rtas-fadump.h"
24d3833a70SHari Bathini 
2541a65d16SHari Bathini static struct rtas_fadump_mem_struct fdm;
2641a65d16SHari Bathini 
2741a65d16SHari Bathini static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
2841a65d16SHari Bathini 				      const struct rtas_fadump_mem_struct *fdm)
2941a65d16SHari Bathini {
3041a65d16SHari Bathini 	fadump_conf->boot_mem_dest_addr =
3141a65d16SHari Bathini 		be64_to_cpu(fdm->rmr_region.destination_address);
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 
37d3833a70SHari Bathini static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
38d3833a70SHari Bathini {
3941a65d16SHari Bathini 	u64 addr = fadump_conf->reserve_dump_area_start;
4041a65d16SHari Bathini 
4141a65d16SHari Bathini 	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
4241a65d16SHari Bathini 	addr = addr & PAGE_MASK;
4341a65d16SHari Bathini 
4441a65d16SHari Bathini 	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
4541a65d16SHari Bathini 	fdm.header.dump_num_sections = cpu_to_be16(3);
4641a65d16SHari Bathini 	fdm.header.dump_status_flag = 0;
4741a65d16SHari Bathini 	fdm.header.offset_first_dump_section =
4841a65d16SHari Bathini 		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct,
4941a65d16SHari Bathini 					  cpu_state_data));
5041a65d16SHari Bathini 
5141a65d16SHari Bathini 	/*
5241a65d16SHari Bathini 	 * Fields for disk dump option.
5341a65d16SHari Bathini 	 * We are not using disk dump option, hence set these fields to 0.
5441a65d16SHari Bathini 	 */
5541a65d16SHari Bathini 	fdm.header.dd_block_size = 0;
5641a65d16SHari Bathini 	fdm.header.dd_block_offset = 0;
5741a65d16SHari Bathini 	fdm.header.dd_num_blocks = 0;
5841a65d16SHari Bathini 	fdm.header.dd_offset_disk_path = 0;
5941a65d16SHari Bathini 
6041a65d16SHari Bathini 	/* set 0 to disable an automatic dump-reboot. */
6141a65d16SHari Bathini 	fdm.header.max_time_auto = 0;
6241a65d16SHari Bathini 
6341a65d16SHari Bathini 	/* Kernel dump sections */
6441a65d16SHari Bathini 	/* cpu state data section. */
6541a65d16SHari Bathini 	fdm.cpu_state_data.request_flag =
6641a65d16SHari Bathini 		cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
6741a65d16SHari Bathini 	fdm.cpu_state_data.source_data_type =
6841a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
6941a65d16SHari Bathini 	fdm.cpu_state_data.source_address = 0;
7041a65d16SHari Bathini 	fdm.cpu_state_data.source_len =
7141a65d16SHari Bathini 		cpu_to_be64(fadump_conf->cpu_state_data_size);
7241a65d16SHari Bathini 	fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
7341a65d16SHari Bathini 	addr += fadump_conf->cpu_state_data_size;
7441a65d16SHari Bathini 
7541a65d16SHari Bathini 	/* hpte region section */
7641a65d16SHari Bathini 	fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
7741a65d16SHari Bathini 	fdm.hpte_region.source_data_type =
7841a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
7941a65d16SHari Bathini 	fdm.hpte_region.source_address = 0;
8041a65d16SHari Bathini 	fdm.hpte_region.source_len =
8141a65d16SHari Bathini 		cpu_to_be64(fadump_conf->hpte_region_size);
8241a65d16SHari Bathini 	fdm.hpte_region.destination_address = cpu_to_be64(addr);
8341a65d16SHari Bathini 	addr += fadump_conf->hpte_region_size;
8441a65d16SHari Bathini 
8541a65d16SHari Bathini 	/* RMA region section */
8641a65d16SHari Bathini 	fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
8741a65d16SHari Bathini 	fdm.rmr_region.source_data_type =
8841a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
8941a65d16SHari Bathini 	fdm.rmr_region.source_address = cpu_to_be64(RMA_START);
9041a65d16SHari Bathini 	fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size);
9141a65d16SHari Bathini 	fdm.rmr_region.destination_address = cpu_to_be64(addr);
9241a65d16SHari Bathini 	addr += fadump_conf->boot_memory_size;
9341a65d16SHari Bathini 
9441a65d16SHari Bathini 	rtas_fadump_update_config(fadump_conf, &fdm);
9541a65d16SHari Bathini 
9641a65d16SHari Bathini 	return addr;
97d3833a70SHari Bathini }
98d3833a70SHari Bathini 
99d3833a70SHari Bathini static int rtas_fadump_register(struct fw_dump *fadump_conf)
100d3833a70SHari Bathini {
10141a65d16SHari Bathini 	unsigned int wait_time;
10241a65d16SHari Bathini 	int rc, err = -EIO;
10341a65d16SHari Bathini 
10441a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
10541a65d16SHari Bathini 	do {
10641a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
10741a65d16SHari Bathini 				NULL, FADUMP_REGISTER, &fdm,
10841a65d16SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
10941a65d16SHari Bathini 
11041a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
11141a65d16SHari Bathini 		if (wait_time)
11241a65d16SHari Bathini 			mdelay(wait_time);
11341a65d16SHari Bathini 
11441a65d16SHari Bathini 	} while (wait_time);
11541a65d16SHari Bathini 
11641a65d16SHari Bathini 	switch (rc) {
11741a65d16SHari Bathini 	case 0:
11841a65d16SHari Bathini 		pr_info("Registration is successful!\n");
11941a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
12041a65d16SHari Bathini 		err = 0;
12141a65d16SHari Bathini 		break;
12241a65d16SHari Bathini 	case -1:
12341a65d16SHari Bathini 		pr_err("Failed to register. Hardware Error(%d).\n", rc);
12441a65d16SHari Bathini 		break;
12541a65d16SHari Bathini 	case -3:
12641a65d16SHari Bathini 		if (!is_fadump_boot_mem_contiguous())
12741a65d16SHari Bathini 			pr_err("Can't have holes in boot memory area.\n");
12841a65d16SHari Bathini 		else if (!is_fadump_reserved_mem_contiguous())
12941a65d16SHari Bathini 			pr_err("Can't have holes in reserved memory area.\n");
13041a65d16SHari Bathini 
13141a65d16SHari Bathini 		pr_err("Failed to register. Parameter Error(%d).\n", rc);
13241a65d16SHari Bathini 		err = -EINVAL;
13341a65d16SHari Bathini 		break;
13441a65d16SHari Bathini 	case -9:
13541a65d16SHari Bathini 		pr_err("Already registered!\n");
13641a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
13741a65d16SHari Bathini 		err = -EEXIST;
13841a65d16SHari Bathini 		break;
13941a65d16SHari Bathini 	default:
14041a65d16SHari Bathini 		pr_err("Failed to register. Unknown Error(%d).\n", rc);
14141a65d16SHari Bathini 		break;
14241a65d16SHari Bathini 	}
14341a65d16SHari Bathini 
14441a65d16SHari Bathini 	return err;
145d3833a70SHari Bathini }
146d3833a70SHari Bathini 
147d3833a70SHari Bathini static int rtas_fadump_unregister(struct fw_dump *fadump_conf)
148d3833a70SHari Bathini {
14941a65d16SHari Bathini 	unsigned int wait_time;
15041a65d16SHari Bathini 	int rc;
15141a65d16SHari Bathini 
15241a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
15341a65d16SHari Bathini 	do {
15441a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
15541a65d16SHari Bathini 				NULL, FADUMP_UNREGISTER, &fdm,
15641a65d16SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
15741a65d16SHari Bathini 
15841a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
15941a65d16SHari Bathini 		if (wait_time)
16041a65d16SHari Bathini 			mdelay(wait_time);
16141a65d16SHari Bathini 	} while (wait_time);
16241a65d16SHari Bathini 
16341a65d16SHari Bathini 	if (rc) {
16441a65d16SHari Bathini 		pr_err("Failed to un-register - unexpected error(%d).\n", rc);
165d3833a70SHari Bathini 		return -EIO;
166d3833a70SHari Bathini 	}
167d3833a70SHari Bathini 
16841a65d16SHari Bathini 	fadump_conf->dump_registered = 0;
16941a65d16SHari Bathini 	return 0;
17041a65d16SHari Bathini }
17141a65d16SHari Bathini 
172d3833a70SHari Bathini static int rtas_fadump_invalidate(struct fw_dump *fadump_conf)
173d3833a70SHari Bathini {
174d3833a70SHari Bathini 	return -EIO;
175d3833a70SHari Bathini }
176d3833a70SHari Bathini 
177d3833a70SHari Bathini /*
178d3833a70SHari Bathini  * Validate and process the dump data stored by firmware before exporting
179d3833a70SHari Bathini  * it through '/proc/vmcore'.
180d3833a70SHari Bathini  */
181d3833a70SHari Bathini static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
182d3833a70SHari Bathini {
183d3833a70SHari Bathini 	return -EINVAL;
184d3833a70SHari Bathini }
185d3833a70SHari Bathini 
186d3833a70SHari Bathini static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
187d3833a70SHari Bathini 				    struct seq_file *m)
188d3833a70SHari Bathini {
18941a65d16SHari Bathini 	const struct rtas_fadump_mem_struct *fdm_ptr = &fdm;
19041a65d16SHari Bathini 	const struct rtas_fadump_section *cpu_data_section;
19141a65d16SHari Bathini 
19241a65d16SHari Bathini 	cpu_data_section = &(fdm_ptr->cpu_state_data);
19341a65d16SHari Bathini 	seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
19441a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->destination_address),
19541a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->destination_address) +
19641a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->source_len) - 1,
19741a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->source_len),
19841a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->bytes_dumped));
19941a65d16SHari Bathini 
20041a65d16SHari Bathini 	seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
20141a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.destination_address),
20241a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
20341a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
20441a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.source_len),
20541a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
20641a65d16SHari Bathini 
207*109f25ccSHari Bathini 	seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
208*109f25ccSHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.source_address),
209*109f25ccSHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.destination_address));
210*109f25ccSHari Bathini 	seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
21141a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.source_len),
21241a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
213d3833a70SHari Bathini }
214d3833a70SHari Bathini 
215d3833a70SHari Bathini static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
216d3833a70SHari Bathini 				const char *msg)
217d3833a70SHari Bathini {
218d3833a70SHari Bathini 	/* Call ibm,os-term rtas call to trigger firmware assisted dump */
219d3833a70SHari Bathini 	rtas_os_term((char *)msg);
220d3833a70SHari Bathini }
221d3833a70SHari Bathini 
222d3833a70SHari Bathini static struct fadump_ops rtas_fadump_ops = {
223d3833a70SHari Bathini 	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
224d3833a70SHari Bathini 	.fadump_register		= rtas_fadump_register,
225d3833a70SHari Bathini 	.fadump_unregister		= rtas_fadump_unregister,
226d3833a70SHari Bathini 	.fadump_invalidate		= rtas_fadump_invalidate,
227d3833a70SHari Bathini 	.fadump_process			= rtas_fadump_process,
228d3833a70SHari Bathini 	.fadump_region_show		= rtas_fadump_region_show,
229d3833a70SHari Bathini 	.fadump_trigger			= rtas_fadump_trigger,
230d3833a70SHari Bathini };
231d3833a70SHari Bathini 
232d3833a70SHari Bathini void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
233d3833a70SHari Bathini {
234d3833a70SHari Bathini 	int i, size, num_sections;
235d3833a70SHari Bathini 	const __be32 *sections;
236d3833a70SHari Bathini 	const __be32 *token;
237d3833a70SHari Bathini 
238d3833a70SHari Bathini 	/*
239d3833a70SHari Bathini 	 * Check if Firmware Assisted dump is supported. if yes, check
240d3833a70SHari Bathini 	 * if dump has been initiated on last reboot.
241d3833a70SHari Bathini 	 */
242d3833a70SHari Bathini 	token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL);
243d3833a70SHari Bathini 	if (!token)
244d3833a70SHari Bathini 		return;
245d3833a70SHari Bathini 
246d3833a70SHari Bathini 	fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token);
247d3833a70SHari Bathini 	fadump_conf->ops		= &rtas_fadump_ops;
248d3833a70SHari Bathini 	fadump_conf->fadump_supported	= 1;
249d3833a70SHari Bathini 
250d3833a70SHari Bathini 	/* Get the sizes required to store dump data for the firmware provided
251d3833a70SHari Bathini 	 * dump sections.
252d3833a70SHari Bathini 	 * For each dump section type supported, a 32bit cell which defines
253d3833a70SHari Bathini 	 * the ID of a supported section followed by two 32 bit cells which
254d3833a70SHari Bathini 	 * gives the size of the section in bytes.
255d3833a70SHari Bathini 	 */
256d3833a70SHari Bathini 	sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
257d3833a70SHari Bathini 					&size);
258d3833a70SHari Bathini 
259d3833a70SHari Bathini 	if (!sections)
260d3833a70SHari Bathini 		return;
261d3833a70SHari Bathini 
262d3833a70SHari Bathini 	num_sections = size / (3 * sizeof(u32));
263d3833a70SHari Bathini 
264d3833a70SHari Bathini 	for (i = 0; i < num_sections; i++, sections += 3) {
265d3833a70SHari Bathini 		u32 type = (u32)of_read_number(sections, 1);
266d3833a70SHari Bathini 
267d3833a70SHari Bathini 		switch (type) {
268d3833a70SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
269d3833a70SHari Bathini 			fadump_conf->cpu_state_data_size =
270d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
271d3833a70SHari Bathini 			break;
272d3833a70SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
273d3833a70SHari Bathini 			fadump_conf->hpte_region_size =
274d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
275d3833a70SHari Bathini 			break;
276d3833a70SHari Bathini 		}
277d3833a70SHari Bathini 	}
278d3833a70SHari Bathini }
279