xref: /openbmc/linux/arch/powerpc/platforms/pseries/rtas-fadump.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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>
16*e6f6390aSChristophe Leroy #include <linux/of.h>
17*e6f6390aSChristophe Leroy #include <linux/of_fdt.h>
18d3833a70SHari Bathini 
19d3833a70SHari Bathini #include <asm/page.h>
20d3833a70SHari Bathini #include <asm/rtas.h>
21d3833a70SHari Bathini #include <asm/fadump.h>
22d3833a70SHari Bathini #include <asm/fadump-internal.h>
23d3833a70SHari Bathini 
24d3833a70SHari Bathini #include "rtas-fadump.h"
25d3833a70SHari Bathini 
2641a65d16SHari Bathini static struct rtas_fadump_mem_struct fdm;
27f3512011SHari Bathini static const struct rtas_fadump_mem_struct *fdm_active;
2841a65d16SHari Bathini 
rtas_fadump_update_config(struct fw_dump * fadump_conf,const struct rtas_fadump_mem_struct * fdm)2941a65d16SHari Bathini static void rtas_fadump_update_config(struct fw_dump *fadump_conf,
3041a65d16SHari Bathini 				      const struct rtas_fadump_mem_struct *fdm)
3141a65d16SHari Bathini {
3241a65d16SHari Bathini 	fadump_conf->boot_mem_dest_addr =
3341a65d16SHari Bathini 		be64_to_cpu(fdm->rmr_region.destination_address);
3441a65d16SHari Bathini 
3541a65d16SHari Bathini 	fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr +
3641a65d16SHari Bathini 				       fadump_conf->boot_memory_size);
3741a65d16SHari Bathini }
3841a65d16SHari Bathini 
39f3512011SHari Bathini /*
40f3512011SHari Bathini  * This function is called in the capture kernel to get configuration details
41f3512011SHari Bathini  * setup in the first kernel and passed to the f/w.
42f3512011SHari Bathini  */
rtas_fadump_get_config(struct fw_dump * fadump_conf,const struct rtas_fadump_mem_struct * fdm)43e14ff96dSNick Child static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf,
44f3512011SHari Bathini 				   const struct rtas_fadump_mem_struct *fdm)
45f3512011SHari Bathini {
467dee93a9SHari Bathini 	fadump_conf->boot_mem_addr[0] =
477dee93a9SHari Bathini 		be64_to_cpu(fdm->rmr_region.source_address);
487dee93a9SHari Bathini 	fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len);
497dee93a9SHari Bathini 	fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0];
507dee93a9SHari Bathini 
517dee93a9SHari Bathini 	fadump_conf->boot_mem_top = fadump_conf->boot_memory_size;
527dee93a9SHari Bathini 	fadump_conf->boot_mem_regs_cnt = 1;
53f3512011SHari Bathini 
54f3512011SHari Bathini 	/*
55f3512011SHari Bathini 	 * Start address of reserve dump area (permanent reservation) for
56f3512011SHari Bathini 	 * re-registering FADump after dump capture.
57f3512011SHari Bathini 	 */
58f3512011SHari Bathini 	fadump_conf->reserve_dump_area_start =
59f3512011SHari Bathini 		be64_to_cpu(fdm->cpu_state_data.destination_address);
60f3512011SHari Bathini 
61f3512011SHari Bathini 	rtas_fadump_update_config(fadump_conf, fdm);
62f3512011SHari Bathini }
63f3512011SHari Bathini 
rtas_fadump_init_mem_struct(struct fw_dump * fadump_conf)64d3833a70SHari Bathini static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf)
65d3833a70SHari Bathini {
6641a65d16SHari Bathini 	u64 addr = fadump_conf->reserve_dump_area_start;
6741a65d16SHari Bathini 
6841a65d16SHari Bathini 	memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct));
6941a65d16SHari Bathini 	addr = addr & PAGE_MASK;
7041a65d16SHari Bathini 
7141a65d16SHari Bathini 	fdm.header.dump_format_version = cpu_to_be32(0x00000001);
7241a65d16SHari Bathini 	fdm.header.dump_num_sections = cpu_to_be16(3);
7341a65d16SHari Bathini 	fdm.header.dump_status_flag = 0;
7441a65d16SHari Bathini 	fdm.header.offset_first_dump_section =
7541a65d16SHari Bathini 		cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct,
7641a65d16SHari Bathini 					  cpu_state_data));
7741a65d16SHari Bathini 
7841a65d16SHari Bathini 	/*
7941a65d16SHari Bathini 	 * Fields for disk dump option.
8041a65d16SHari Bathini 	 * We are not using disk dump option, hence set these fields to 0.
8141a65d16SHari Bathini 	 */
8241a65d16SHari Bathini 	fdm.header.dd_block_size = 0;
8341a65d16SHari Bathini 	fdm.header.dd_block_offset = 0;
8441a65d16SHari Bathini 	fdm.header.dd_num_blocks = 0;
8541a65d16SHari Bathini 	fdm.header.dd_offset_disk_path = 0;
8641a65d16SHari Bathini 
8741a65d16SHari Bathini 	/* set 0 to disable an automatic dump-reboot. */
8841a65d16SHari Bathini 	fdm.header.max_time_auto = 0;
8941a65d16SHari Bathini 
9041a65d16SHari Bathini 	/* Kernel dump sections */
9141a65d16SHari Bathini 	/* cpu state data section. */
9241a65d16SHari Bathini 	fdm.cpu_state_data.request_flag =
9341a65d16SHari Bathini 		cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
9441a65d16SHari Bathini 	fdm.cpu_state_data.source_data_type =
9541a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA);
9641a65d16SHari Bathini 	fdm.cpu_state_data.source_address = 0;
9741a65d16SHari Bathini 	fdm.cpu_state_data.source_len =
9841a65d16SHari Bathini 		cpu_to_be64(fadump_conf->cpu_state_data_size);
9941a65d16SHari Bathini 	fdm.cpu_state_data.destination_address = cpu_to_be64(addr);
10041a65d16SHari Bathini 	addr += fadump_conf->cpu_state_data_size;
10141a65d16SHari Bathini 
10241a65d16SHari Bathini 	/* hpte region section */
10341a65d16SHari Bathini 	fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
10441a65d16SHari Bathini 	fdm.hpte_region.source_data_type =
10541a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_HPTE_REGION);
10641a65d16SHari Bathini 	fdm.hpte_region.source_address = 0;
10741a65d16SHari Bathini 	fdm.hpte_region.source_len =
10841a65d16SHari Bathini 		cpu_to_be64(fadump_conf->hpte_region_size);
10941a65d16SHari Bathini 	fdm.hpte_region.destination_address = cpu_to_be64(addr);
11041a65d16SHari Bathini 	addr += fadump_conf->hpte_region_size;
11141a65d16SHari Bathini 
1129cf3b3a3SHari Bathini 	/*
1139cf3b3a3SHari Bathini 	 * Align boot memory area destination address to page boundary to
1149cf3b3a3SHari Bathini 	 * be able to mmap read this area in the vmcore.
1159cf3b3a3SHari Bathini 	 */
1169cf3b3a3SHari Bathini 	addr = PAGE_ALIGN(addr);
1179cf3b3a3SHari Bathini 
11841a65d16SHari Bathini 	/* RMA region section */
11941a65d16SHari Bathini 	fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG);
12041a65d16SHari Bathini 	fdm.rmr_region.source_data_type =
12141a65d16SHari Bathini 		cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION);
122becd91d9SHari Bathini 	fdm.rmr_region.source_address = cpu_to_be64(0);
12341a65d16SHari Bathini 	fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size);
12441a65d16SHari Bathini 	fdm.rmr_region.destination_address = cpu_to_be64(addr);
12541a65d16SHari Bathini 	addr += fadump_conf->boot_memory_size;
12641a65d16SHari Bathini 
12741a65d16SHari Bathini 	rtas_fadump_update_config(fadump_conf, &fdm);
12841a65d16SHari Bathini 
12941a65d16SHari Bathini 	return addr;
130d3833a70SHari Bathini }
131d3833a70SHari Bathini 
rtas_fadump_get_bootmem_min(void)1327b1b3b48SHari Bathini static u64 rtas_fadump_get_bootmem_min(void)
1337b1b3b48SHari Bathini {
1347b1b3b48SHari Bathini 	return RTAS_FADUMP_MIN_BOOT_MEM;
1357b1b3b48SHari Bathini }
1367b1b3b48SHari Bathini 
rtas_fadump_register(struct fw_dump * fadump_conf)137d3833a70SHari Bathini static int rtas_fadump_register(struct fw_dump *fadump_conf)
138d3833a70SHari Bathini {
13941a65d16SHari Bathini 	unsigned int wait_time;
14041a65d16SHari Bathini 	int rc, err = -EIO;
14141a65d16SHari Bathini 
14241a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
14341a65d16SHari Bathini 	do {
14441a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
14541a65d16SHari Bathini 				NULL, FADUMP_REGISTER, &fdm,
14641a65d16SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
14741a65d16SHari Bathini 
14841a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
14941a65d16SHari Bathini 		if (wait_time)
15041a65d16SHari Bathini 			mdelay(wait_time);
15141a65d16SHari Bathini 
15241a65d16SHari Bathini 	} while (wait_time);
15341a65d16SHari Bathini 
15441a65d16SHari Bathini 	switch (rc) {
15541a65d16SHari Bathini 	case 0:
15641a65d16SHari Bathini 		pr_info("Registration is successful!\n");
15741a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
15841a65d16SHari Bathini 		err = 0;
15941a65d16SHari Bathini 		break;
16041a65d16SHari Bathini 	case -1:
16141a65d16SHari Bathini 		pr_err("Failed to register. Hardware Error(%d).\n", rc);
16241a65d16SHari Bathini 		break;
16341a65d16SHari Bathini 	case -3:
16441a65d16SHari Bathini 		if (!is_fadump_boot_mem_contiguous())
16541a65d16SHari Bathini 			pr_err("Can't have holes in boot memory area.\n");
16641a65d16SHari Bathini 		else if (!is_fadump_reserved_mem_contiguous())
16741a65d16SHari Bathini 			pr_err("Can't have holes in reserved memory area.\n");
16841a65d16SHari Bathini 
16941a65d16SHari Bathini 		pr_err("Failed to register. Parameter Error(%d).\n", rc);
17041a65d16SHari Bathini 		err = -EINVAL;
17141a65d16SHari Bathini 		break;
17241a65d16SHari Bathini 	case -9:
17341a65d16SHari Bathini 		pr_err("Already registered!\n");
17441a65d16SHari Bathini 		fadump_conf->dump_registered = 1;
17541a65d16SHari Bathini 		err = -EEXIST;
17641a65d16SHari Bathini 		break;
17741a65d16SHari Bathini 	default:
17841a65d16SHari Bathini 		pr_err("Failed to register. Unknown Error(%d).\n", rc);
17941a65d16SHari Bathini 		break;
18041a65d16SHari Bathini 	}
18141a65d16SHari Bathini 
18241a65d16SHari Bathini 	return err;
183d3833a70SHari Bathini }
184d3833a70SHari Bathini 
rtas_fadump_unregister(struct fw_dump * fadump_conf)185d3833a70SHari Bathini static int rtas_fadump_unregister(struct fw_dump *fadump_conf)
186d3833a70SHari Bathini {
18741a65d16SHari Bathini 	unsigned int wait_time;
18841a65d16SHari Bathini 	int rc;
18941a65d16SHari Bathini 
19041a65d16SHari Bathini 	/* TODO: Add upper time limit for the delay */
19141a65d16SHari Bathini 	do {
19241a65d16SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
19341a65d16SHari Bathini 				NULL, FADUMP_UNREGISTER, &fdm,
19441a65d16SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
19541a65d16SHari Bathini 
19641a65d16SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
19741a65d16SHari Bathini 		if (wait_time)
19841a65d16SHari Bathini 			mdelay(wait_time);
19941a65d16SHari Bathini 	} while (wait_time);
20041a65d16SHari Bathini 
20141a65d16SHari Bathini 	if (rc) {
20241a65d16SHari Bathini 		pr_err("Failed to un-register - unexpected error(%d).\n", rc);
203d3833a70SHari Bathini 		return -EIO;
204d3833a70SHari Bathini 	}
205d3833a70SHari Bathini 
20641a65d16SHari Bathini 	fadump_conf->dump_registered = 0;
20741a65d16SHari Bathini 	return 0;
20841a65d16SHari Bathini }
20941a65d16SHari Bathini 
rtas_fadump_invalidate(struct fw_dump * fadump_conf)210d3833a70SHari Bathini static int rtas_fadump_invalidate(struct fw_dump *fadump_conf)
211d3833a70SHari Bathini {
212f3512011SHari Bathini 	unsigned int wait_time;
213f3512011SHari Bathini 	int rc;
214f3512011SHari Bathini 
215f3512011SHari Bathini 	/* TODO: Add upper time limit for the delay */
216f3512011SHari Bathini 	do {
217f3512011SHari Bathini 		rc =  rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1,
218f3512011SHari Bathini 				NULL, FADUMP_INVALIDATE, fdm_active,
219f3512011SHari Bathini 				sizeof(struct rtas_fadump_mem_struct));
220f3512011SHari Bathini 
221f3512011SHari Bathini 		wait_time = rtas_busy_delay_time(rc);
222f3512011SHari Bathini 		if (wait_time)
223f3512011SHari Bathini 			mdelay(wait_time);
224f3512011SHari Bathini 	} while (wait_time);
225f3512011SHari Bathini 
226f3512011SHari Bathini 	if (rc) {
227f3512011SHari Bathini 		pr_err("Failed to invalidate - unexpected error (%d).\n", rc);
228d3833a70SHari Bathini 		return -EIO;
229d3833a70SHari Bathini 	}
230d3833a70SHari Bathini 
231f3512011SHari Bathini 	fadump_conf->dump_active = 0;
232f3512011SHari Bathini 	fdm_active = NULL;
233f3512011SHari Bathini 	return 0;
234f3512011SHari Bathini }
235f3512011SHari Bathini 
236f3512011SHari Bathini #define RTAS_FADUMP_GPR_MASK		0xffffff0000000000
rtas_fadump_gpr_index(u64 id)237f3512011SHari Bathini static inline int rtas_fadump_gpr_index(u64 id)
238f3512011SHari Bathini {
239f3512011SHari Bathini 	char str[3];
240f3512011SHari Bathini 	int i = -1;
241f3512011SHari Bathini 
242f3512011SHari Bathini 	if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) {
243f3512011SHari Bathini 		/* get the digits at the end */
244f3512011SHari Bathini 		id &= ~RTAS_FADUMP_GPR_MASK;
245f3512011SHari Bathini 		id >>= 24;
246f3512011SHari Bathini 		str[2] = '\0';
247f3512011SHari Bathini 		str[1] = id & 0xff;
248f3512011SHari Bathini 		str[0] = (id >> 8) & 0xff;
249f3512011SHari Bathini 		if (kstrtoint(str, 10, &i))
250f3512011SHari Bathini 			i = -EINVAL;
251f3512011SHari Bathini 		if (i > 31)
252f3512011SHari Bathini 			i = -1;
253f3512011SHari Bathini 	}
254f3512011SHari Bathini 	return i;
255f3512011SHari Bathini }
256f3512011SHari Bathini 
rtas_fadump_set_regval(struct pt_regs * regs,u64 reg_id,u64 reg_val)257e14ff96dSNick Child static void __init rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val)
258f3512011SHari Bathini {
259f3512011SHari Bathini 	int i;
260f3512011SHari Bathini 
261f3512011SHari Bathini 	i = rtas_fadump_gpr_index(reg_id);
262f3512011SHari Bathini 	if (i >= 0)
263f3512011SHari Bathini 		regs->gpr[i] = (unsigned long)reg_val;
264f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("NIA"))
265f3512011SHari Bathini 		regs->nip = (unsigned long)reg_val;
266f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("MSR"))
267f3512011SHari Bathini 		regs->msr = (unsigned long)reg_val;
268f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("CTR"))
269f3512011SHari Bathini 		regs->ctr = (unsigned long)reg_val;
270f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("LR"))
271f3512011SHari Bathini 		regs->link = (unsigned long)reg_val;
272f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("XER"))
273f3512011SHari Bathini 		regs->xer = (unsigned long)reg_val;
274f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("CR"))
275f3512011SHari Bathini 		regs->ccr = (unsigned long)reg_val;
276f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("DAR"))
277f3512011SHari Bathini 		regs->dar = (unsigned long)reg_val;
278f3512011SHari Bathini 	else if (reg_id == fadump_str_to_u64("DSISR"))
279f3512011SHari Bathini 		regs->dsisr = (unsigned long)reg_val;
280f3512011SHari Bathini }
281f3512011SHari Bathini 
282e14ff96dSNick Child static struct rtas_fadump_reg_entry* __init
rtas_fadump_read_regs(struct rtas_fadump_reg_entry * reg_entry,struct pt_regs * regs)283f3512011SHari Bathini rtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry,
284f3512011SHari Bathini 		      struct pt_regs *regs)
285f3512011SHari Bathini {
286f3512011SHari Bathini 	memset(regs, 0, sizeof(struct pt_regs));
287f3512011SHari Bathini 
288f3512011SHari Bathini 	while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) {
289f3512011SHari Bathini 		rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id),
290f3512011SHari Bathini 				       be64_to_cpu(reg_entry->reg_value));
291f3512011SHari Bathini 		reg_entry++;
292f3512011SHari Bathini 	}
293f3512011SHari Bathini 	reg_entry++;
294f3512011SHari Bathini 	return reg_entry;
295f3512011SHari Bathini }
296f3512011SHari Bathini 
297f3512011SHari Bathini /*
298f3512011SHari Bathini  * Read CPU state dump data and convert it into ELF notes.
299f3512011SHari Bathini  * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be
300f3512011SHari Bathini  * used to access the data to allow for additional fields to be added without
301f3512011SHari Bathini  * affecting compatibility. Each list of registers for a CPU starts with
302f3512011SHari Bathini  * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes,
303f3512011SHari Bathini  * 8 Byte ASCII identifier and 8 Byte register value. The register entry
304f3512011SHari Bathini  * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part
305f3512011SHari Bathini  * of register value. For more details refer to PAPR document.
306f3512011SHari Bathini  *
307f3512011SHari Bathini  * Only for the crashing cpu we ignore the CPU dump data and get exact
308f3512011SHari Bathini  * state from fadump crash info structure populated by first kernel at the
309f3512011SHari Bathini  * time of crash.
310f3512011SHari Bathini  */
rtas_fadump_build_cpu_notes(struct fw_dump * fadump_conf)311f3512011SHari Bathini static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf)
312f3512011SHari Bathini {
313f3512011SHari Bathini 	struct rtas_fadump_reg_save_area_header *reg_header;
314f3512011SHari Bathini 	struct fadump_crash_info_header *fdh = NULL;
315f3512011SHari Bathini 	struct rtas_fadump_reg_entry *reg_entry;
316f3512011SHari Bathini 	u32 num_cpus, *note_buf;
317f3512011SHari Bathini 	int i, rc = 0, cpu = 0;
318f3512011SHari Bathini 	struct pt_regs regs;
319f3512011SHari Bathini 	unsigned long addr;
320f3512011SHari Bathini 	void *vaddr;
321f3512011SHari Bathini 
322f3512011SHari Bathini 	addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address);
323f3512011SHari Bathini 	vaddr = __va(addr);
324f3512011SHari Bathini 
325f3512011SHari Bathini 	reg_header = vaddr;
326f3512011SHari Bathini 	if (be64_to_cpu(reg_header->magic_number) !=
327f3512011SHari Bathini 	    fadump_str_to_u64("REGSAVE")) {
328f3512011SHari Bathini 		pr_err("Unable to read register save area.\n");
329f3512011SHari Bathini 		return -ENOENT;
330f3512011SHari Bathini 	}
331f3512011SHari Bathini 
332f3512011SHari Bathini 	pr_debug("--------CPU State Data------------\n");
333f3512011SHari Bathini 	pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number));
334f3512011SHari Bathini 	pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset));
335f3512011SHari Bathini 
336f3512011SHari Bathini 	vaddr += be32_to_cpu(reg_header->num_cpu_offset);
337f3512011SHari Bathini 	num_cpus = be32_to_cpu(*((__be32 *)(vaddr)));
338f3512011SHari Bathini 	pr_debug("NumCpus     : %u\n", num_cpus);
339f3512011SHari Bathini 	vaddr += sizeof(u32);
340f3512011SHari Bathini 	reg_entry = (struct rtas_fadump_reg_entry *)vaddr;
341f3512011SHari Bathini 
342f3512011SHari Bathini 	rc = fadump_setup_cpu_notes_buf(num_cpus);
343f3512011SHari Bathini 	if (rc != 0)
344f3512011SHari Bathini 		return rc;
345f3512011SHari Bathini 
346f3512011SHari Bathini 	note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr;
347f3512011SHari Bathini 
348f3512011SHari Bathini 	if (fadump_conf->fadumphdr_addr)
349f3512011SHari Bathini 		fdh = __va(fadump_conf->fadumphdr_addr);
350f3512011SHari Bathini 
351f3512011SHari Bathini 	for (i = 0; i < num_cpus; i++) {
352f3512011SHari Bathini 		if (be64_to_cpu(reg_entry->reg_id) !=
353f3512011SHari Bathini 		    fadump_str_to_u64("CPUSTRT")) {
354f3512011SHari Bathini 			pr_err("Unable to read CPU state data\n");
355f3512011SHari Bathini 			rc = -ENOENT;
356f3512011SHari Bathini 			goto error_out;
357f3512011SHari Bathini 		}
358f3512011SHari Bathini 		/* Lower 4 bytes of reg_value contains logical cpu id */
359f3512011SHari Bathini 		cpu = (be64_to_cpu(reg_entry->reg_value) &
360f3512011SHari Bathini 		       RTAS_FADUMP_CPU_ID_MASK);
3616584cec0SHari Bathini 		if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_mask)) {
362f3512011SHari Bathini 			RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
363f3512011SHari Bathini 			continue;
364f3512011SHari Bathini 		}
365f3512011SHari Bathini 		pr_debug("Reading register data for cpu %d...\n", cpu);
366f3512011SHari Bathini 		if (fdh && fdh->crashing_cpu == cpu) {
367f3512011SHari Bathini 			regs = fdh->regs;
368f3512011SHari Bathini 			note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
369f3512011SHari Bathini 			RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry);
370f3512011SHari Bathini 		} else {
371f3512011SHari Bathini 			reg_entry++;
372f3512011SHari Bathini 			reg_entry = rtas_fadump_read_regs(reg_entry, &regs);
373f3512011SHari Bathini 			note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
374f3512011SHari Bathini 		}
375f3512011SHari Bathini 	}
376f3512011SHari Bathini 	final_note(note_buf);
377f3512011SHari Bathini 
378f3512011SHari Bathini 	if (fdh) {
379f3512011SHari Bathini 		pr_debug("Updating elfcore header (%llx) with cpu notes\n",
380f3512011SHari Bathini 			 fdh->elfcorehdr_addr);
381f3512011SHari Bathini 		fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr));
382f3512011SHari Bathini 	}
383f3512011SHari Bathini 	return 0;
384f3512011SHari Bathini 
385f3512011SHari Bathini error_out:
386f3512011SHari Bathini 	fadump_free_cpu_notes_buf();
387f3512011SHari Bathini 	return rc;
388f3512011SHari Bathini 
389f3512011SHari Bathini }
390f3512011SHari Bathini 
391d3833a70SHari Bathini /*
392d3833a70SHari Bathini  * Validate and process the dump data stored by firmware before exporting
393d3833a70SHari Bathini  * it through '/proc/vmcore'.
394d3833a70SHari Bathini  */
rtas_fadump_process(struct fw_dump * fadump_conf)395d3833a70SHari Bathini static int __init rtas_fadump_process(struct fw_dump *fadump_conf)
396d3833a70SHari Bathini {
397f3512011SHari Bathini 	struct fadump_crash_info_header *fdh;
398f3512011SHari Bathini 	int rc = 0;
399f3512011SHari Bathini 
400f3512011SHari Bathini 	if (!fdm_active || !fadump_conf->fadumphdr_addr)
401d3833a70SHari Bathini 		return -EINVAL;
402f3512011SHari Bathini 
403f3512011SHari Bathini 	/* Check if the dump data is valid. */
404f3512011SHari Bathini 	if ((be16_to_cpu(fdm_active->header.dump_status_flag) ==
405f3512011SHari Bathini 			RTAS_FADUMP_ERROR_FLAG) ||
406f3512011SHari Bathini 			(fdm_active->cpu_state_data.error_flags != 0) ||
407f3512011SHari Bathini 			(fdm_active->rmr_region.error_flags != 0)) {
408f3512011SHari Bathini 		pr_err("Dump taken by platform is not valid\n");
409f3512011SHari Bathini 		return -EINVAL;
410f3512011SHari Bathini 	}
411f3512011SHari Bathini 	if ((fdm_active->rmr_region.bytes_dumped !=
412f3512011SHari Bathini 			fdm_active->rmr_region.source_len) ||
413f3512011SHari Bathini 			!fdm_active->cpu_state_data.bytes_dumped) {
414f3512011SHari Bathini 		pr_err("Dump taken by platform is incomplete\n");
415f3512011SHari Bathini 		return -EINVAL;
416f3512011SHari Bathini 	}
417f3512011SHari Bathini 
418f3512011SHari Bathini 	/* Validate the fadump crash info header */
419f3512011SHari Bathini 	fdh = __va(fadump_conf->fadumphdr_addr);
420f3512011SHari Bathini 	if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
421f3512011SHari Bathini 		pr_err("Crash info header is not valid.\n");
422f3512011SHari Bathini 		return -EINVAL;
423f3512011SHari Bathini 	}
424f3512011SHari Bathini 
425f3512011SHari Bathini 	rc = rtas_fadump_build_cpu_notes(fadump_conf);
426f3512011SHari Bathini 	if (rc)
427f3512011SHari Bathini 		return rc;
428f3512011SHari Bathini 
429f3512011SHari Bathini 	/*
430f3512011SHari Bathini 	 * We are done validating dump info and elfcore header is now ready
431f3512011SHari Bathini 	 * to be exported. set elfcorehdr_addr so that vmcore module will
432f3512011SHari Bathini 	 * export the elfcore header through '/proc/vmcore'.
433f3512011SHari Bathini 	 */
434f3512011SHari Bathini 	elfcorehdr_addr = fdh->elfcorehdr_addr;
435f3512011SHari Bathini 
436f3512011SHari Bathini 	return 0;
437d3833a70SHari Bathini }
438d3833a70SHari Bathini 
rtas_fadump_region_show(struct fw_dump * fadump_conf,struct seq_file * m)439d3833a70SHari Bathini static void rtas_fadump_region_show(struct fw_dump *fadump_conf,
440d3833a70SHari Bathini 				    struct seq_file *m)
441d3833a70SHari Bathini {
44241a65d16SHari Bathini 	const struct rtas_fadump_section *cpu_data_section;
443f3512011SHari Bathini 	const struct rtas_fadump_mem_struct *fdm_ptr;
444f3512011SHari Bathini 
445f3512011SHari Bathini 	if (fdm_active)
446f3512011SHari Bathini 		fdm_ptr = fdm_active;
447f3512011SHari Bathini 	else
448f3512011SHari Bathini 		fdm_ptr = &fdm;
44941a65d16SHari Bathini 
45041a65d16SHari Bathini 	cpu_data_section = &(fdm_ptr->cpu_state_data);
45141a65d16SHari Bathini 	seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
45241a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->destination_address),
45341a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->destination_address) +
45441a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->source_len) - 1,
45541a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->source_len),
45641a65d16SHari Bathini 		   be64_to_cpu(cpu_data_section->bytes_dumped));
45741a65d16SHari Bathini 
45841a65d16SHari Bathini 	seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n",
45941a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.destination_address),
46041a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.destination_address) +
46141a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1,
46241a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.source_len),
46341a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped));
46441a65d16SHari Bathini 
465109f25ccSHari Bathini 	seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
466109f25ccSHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.source_address),
467109f25ccSHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.destination_address));
468109f25ccSHari Bathini 	seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
46941a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.source_len),
47041a65d16SHari Bathini 		   be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped));
471f3512011SHari Bathini 
472a3ceb588SHari Bathini 	/* Dump is active. Show preserved area start address. */
473f3512011SHari Bathini 	if (fdm_active) {
474a3ceb588SHari Bathini 		seq_printf(m, "\nMemory above %#016llx is reserved for saving crash dump\n",
475a3ceb588SHari Bathini 			   fadump_conf->boot_mem_top);
476f3512011SHari Bathini 	}
477d3833a70SHari Bathini }
478d3833a70SHari Bathini 
rtas_fadump_trigger(struct fadump_crash_info_header * fdh,const char * msg)479d3833a70SHari Bathini static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh,
480d3833a70SHari Bathini 				const char *msg)
481d3833a70SHari Bathini {
482d3833a70SHari Bathini 	/* Call ibm,os-term rtas call to trigger firmware assisted dump */
483d3833a70SHari Bathini 	rtas_os_term((char *)msg);
484d3833a70SHari Bathini }
485d3833a70SHari Bathini 
486d3833a70SHari Bathini static struct fadump_ops rtas_fadump_ops = {
487d3833a70SHari Bathini 	.fadump_init_mem_struct		= rtas_fadump_init_mem_struct,
4887b1b3b48SHari Bathini 	.fadump_get_bootmem_min		= rtas_fadump_get_bootmem_min,
489d3833a70SHari Bathini 	.fadump_register		= rtas_fadump_register,
490d3833a70SHari Bathini 	.fadump_unregister		= rtas_fadump_unregister,
491d3833a70SHari Bathini 	.fadump_invalidate		= rtas_fadump_invalidate,
492d3833a70SHari Bathini 	.fadump_process			= rtas_fadump_process,
493d3833a70SHari Bathini 	.fadump_region_show		= rtas_fadump_region_show,
494d3833a70SHari Bathini 	.fadump_trigger			= rtas_fadump_trigger,
495d3833a70SHari Bathini };
496d3833a70SHari Bathini 
rtas_fadump_dt_scan(struct fw_dump * fadump_conf,u64 node)497d3833a70SHari Bathini void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
498d3833a70SHari Bathini {
499d3833a70SHari Bathini 	int i, size, num_sections;
500d3833a70SHari Bathini 	const __be32 *sections;
501d3833a70SHari Bathini 	const __be32 *token;
502d3833a70SHari Bathini 
503d3833a70SHari Bathini 	/*
504d3833a70SHari Bathini 	 * Check if Firmware Assisted dump is supported. if yes, check
505d3833a70SHari Bathini 	 * if dump has been initiated on last reboot.
506d3833a70SHari Bathini 	 */
507d3833a70SHari Bathini 	token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL);
508d3833a70SHari Bathini 	if (!token)
509d3833a70SHari Bathini 		return;
510d3833a70SHari Bathini 
511d3833a70SHari Bathini 	fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token);
512d3833a70SHari Bathini 	fadump_conf->ops		= &rtas_fadump_ops;
513d3833a70SHari Bathini 	fadump_conf->fadump_supported	= 1;
514d3833a70SHari Bathini 
5157dee93a9SHari Bathini 	/* Firmware supports 64-bit value for size, align it to pagesize. */
516e96d904eSChristophe Leroy 	fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE);
5177dee93a9SHari Bathini 
518f3512011SHari Bathini 	/*
519f3512011SHari Bathini 	 * The 'ibm,kernel-dump' rtas node is present only if there is
520f3512011SHari Bathini 	 * dump data waiting for us.
521f3512011SHari Bathini 	 */
522f3512011SHari Bathini 	fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL);
523f3512011SHari Bathini 	if (fdm_active) {
524f3512011SHari Bathini 		pr_info("Firmware-assisted dump is active.\n");
525f3512011SHari Bathini 		fadump_conf->dump_active = 1;
526f3512011SHari Bathini 		rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active));
527f3512011SHari Bathini 	}
528f3512011SHari Bathini 
529d3833a70SHari Bathini 	/* Get the sizes required to store dump data for the firmware provided
530d3833a70SHari Bathini 	 * dump sections.
531d3833a70SHari Bathini 	 * For each dump section type supported, a 32bit cell which defines
532d3833a70SHari Bathini 	 * the ID of a supported section followed by two 32 bit cells which
533d3833a70SHari Bathini 	 * gives the size of the section in bytes.
534d3833a70SHari Bathini 	 */
535d3833a70SHari Bathini 	sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes",
536d3833a70SHari Bathini 					&size);
537d3833a70SHari Bathini 
538d3833a70SHari Bathini 	if (!sections)
539d3833a70SHari Bathini 		return;
540d3833a70SHari Bathini 
541d3833a70SHari Bathini 	num_sections = size / (3 * sizeof(u32));
542d3833a70SHari Bathini 
543d3833a70SHari Bathini 	for (i = 0; i < num_sections; i++, sections += 3) {
544d3833a70SHari Bathini 		u32 type = (u32)of_read_number(sections, 1);
545d3833a70SHari Bathini 
546d3833a70SHari Bathini 		switch (type) {
547d3833a70SHari Bathini 		case RTAS_FADUMP_CPU_STATE_DATA:
548d3833a70SHari Bathini 			fadump_conf->cpu_state_data_size =
549d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
550d3833a70SHari Bathini 			break;
551d3833a70SHari Bathini 		case RTAS_FADUMP_HPTE_REGION:
552d3833a70SHari Bathini 			fadump_conf->hpte_region_size =
553d3833a70SHari Bathini 					of_read_ulong(&sections[1], 2);
554d3833a70SHari Bathini 			break;
555d3833a70SHari Bathini 		}
556d3833a70SHari Bathini 	}
557d3833a70SHari Bathini }
558