141df5928SHari Bathini // SPDX-License-Identifier: GPL-2.0-or-later
241df5928SHari Bathini /*
341df5928SHari Bathini  * Firmware-Assisted Dump support on POWER platform (OPAL).
441df5928SHari Bathini  *
541df5928SHari Bathini  * Copyright 2019, Hari Bathini, IBM Corporation.
641df5928SHari Bathini  */
741df5928SHari Bathini 
841df5928SHari Bathini #define pr_fmt(fmt) "opal fadump: " fmt
941df5928SHari Bathini 
1041df5928SHari Bathini #include <linux/string.h>
1141df5928SHari Bathini #include <linux/seq_file.h>
1241df5928SHari Bathini #include <linux/of_fdt.h>
1341df5928SHari Bathini #include <linux/libfdt.h>
14742a265aSHari Bathini #include <linux/mm.h>
1541df5928SHari Bathini 
16742a265aSHari Bathini #include <asm/page.h>
1741df5928SHari Bathini #include <asm/opal.h>
1841df5928SHari Bathini #include <asm/fadump-internal.h>
1941df5928SHari Bathini 
20742a265aSHari Bathini #include "opal-fadump.h"
21742a265aSHari Bathini 
22742a265aSHari Bathini static struct opal_fadump_mem_struct *opal_fdm;
23742a265aSHari Bathini 
24742a265aSHari Bathini /* Initialize kernel metadata */
25742a265aSHari Bathini static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm)
26742a265aSHari Bathini {
27742a265aSHari Bathini 	fdm->version = OPAL_FADUMP_VERSION;
28742a265aSHari Bathini 	fdm->region_cnt = 0;
29742a265aSHari Bathini 	fdm->registered_regions = 0;
30742a265aSHari Bathini 	fdm->fadumphdr_addr = 0;
31742a265aSHari Bathini }
32742a265aSHari Bathini 
3341df5928SHari Bathini static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf)
3441df5928SHari Bathini {
35742a265aSHari Bathini 	u64 addr = fadump_conf->reserve_dump_area_start;
36742a265aSHari Bathini 
37742a265aSHari Bathini 	opal_fdm = __va(fadump_conf->kernel_metadata);
38742a265aSHari Bathini 	opal_fadump_init_metadata(opal_fdm);
39742a265aSHari Bathini 
40742a265aSHari Bathini 	opal_fdm->region_cnt = 1;
41742a265aSHari Bathini 	opal_fdm->rgn[0].src	= 0;
42742a265aSHari Bathini 	opal_fdm->rgn[0].dest	= addr;
43742a265aSHari Bathini 	opal_fdm->rgn[0].size	= fadump_conf->boot_memory_size;
44742a265aSHari Bathini 	addr += fadump_conf->boot_memory_size;
45742a265aSHari Bathini 
46742a265aSHari Bathini 	/*
47742a265aSHari Bathini 	 * Kernel metadata is passed to f/w and retrieved in capture kerenl.
48742a265aSHari Bathini 	 * So, use it to save fadump header address instead of calculating it.
49742a265aSHari Bathini 	 */
50742a265aSHari Bathini 	opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest +
51742a265aSHari Bathini 				    fadump_conf->boot_memory_size);
52742a265aSHari Bathini 
53742a265aSHari Bathini 	return addr;
54742a265aSHari Bathini }
55742a265aSHari Bathini 
56742a265aSHari Bathini static u64 opal_fadump_get_metadata_size(void)
57742a265aSHari Bathini {
58742a265aSHari Bathini 	return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct));
59742a265aSHari Bathini }
60742a265aSHari Bathini 
61742a265aSHari Bathini static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf)
62742a265aSHari Bathini {
63742a265aSHari Bathini 	int err = 0;
64742a265aSHari Bathini 	s64 ret;
65742a265aSHari Bathini 
66742a265aSHari Bathini 	/*
67742a265aSHari Bathini 	 * Use the last page(s) in FADump memory reservation for
68742a265aSHari Bathini 	 * kernel metadata.
69742a265aSHari Bathini 	 */
70742a265aSHari Bathini 	fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start +
71742a265aSHari Bathini 					fadump_conf->reserve_dump_area_size -
72742a265aSHari Bathini 					opal_fadump_get_metadata_size());
73742a265aSHari Bathini 	pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata);
74742a265aSHari Bathini 
75742a265aSHari Bathini 	/* Initialize kernel metadata before registering the address with f/w */
76742a265aSHari Bathini 	opal_fdm = __va(fadump_conf->kernel_metadata);
77742a265aSHari Bathini 	opal_fadump_init_metadata(opal_fdm);
78742a265aSHari Bathini 
79742a265aSHari Bathini 	/*
80742a265aSHari Bathini 	 * Register metadata address with f/w. Can be retrieved in
81742a265aSHari Bathini 	 * the capture kernel.
82742a265aSHari Bathini 	 */
83742a265aSHari Bathini 	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL,
84742a265aSHari Bathini 				      fadump_conf->kernel_metadata);
85742a265aSHari Bathini 	if (ret != OPAL_SUCCESS) {
86742a265aSHari Bathini 		pr_err("Failed to set kernel metadata tag!\n");
87742a265aSHari Bathini 		err = -EPERM;
88742a265aSHari Bathini 	}
89742a265aSHari Bathini 
90742a265aSHari Bathini 	return err;
9141df5928SHari Bathini }
9241df5928SHari Bathini 
9341df5928SHari Bathini static int opal_fadump_register(struct fw_dump *fadump_conf)
9441df5928SHari Bathini {
9541df5928SHari Bathini 	return -EIO;
9641df5928SHari Bathini }
9741df5928SHari Bathini 
9841df5928SHari Bathini static int opal_fadump_unregister(struct fw_dump *fadump_conf)
9941df5928SHari Bathini {
10041df5928SHari Bathini 	return -EIO;
10141df5928SHari Bathini }
10241df5928SHari Bathini 
10341df5928SHari Bathini static int opal_fadump_invalidate(struct fw_dump *fadump_conf)
10441df5928SHari Bathini {
10541df5928SHari Bathini 	return -EIO;
10641df5928SHari Bathini }
10741df5928SHari Bathini 
10841df5928SHari Bathini static int __init opal_fadump_process(struct fw_dump *fadump_conf)
10941df5928SHari Bathini {
11041df5928SHari Bathini 	return -EINVAL;
11141df5928SHari Bathini }
11241df5928SHari Bathini 
11341df5928SHari Bathini static void opal_fadump_region_show(struct fw_dump *fadump_conf,
11441df5928SHari Bathini 				    struct seq_file *m)
11541df5928SHari Bathini {
116742a265aSHari Bathini 	const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm;
117742a265aSHari Bathini 	u64 dumped_bytes = 0;
118742a265aSHari Bathini 	int i;
119742a265aSHari Bathini 
120742a265aSHari Bathini 	for (i = 0; i < fdm_ptr->region_cnt; i++) {
121742a265aSHari Bathini 		seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
122742a265aSHari Bathini 			   fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest);
123742a265aSHari Bathini 		seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
124742a265aSHari Bathini 			   fdm_ptr->rgn[i].size, dumped_bytes);
125742a265aSHari Bathini 	}
12641df5928SHari Bathini }
12741df5928SHari Bathini 
12841df5928SHari Bathini static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
12941df5928SHari Bathini 				const char *msg)
13041df5928SHari Bathini {
13141df5928SHari Bathini 	int rc;
13241df5928SHari Bathini 
13341df5928SHari Bathini 	rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg);
13441df5928SHari Bathini 	if (rc == OPAL_UNSUPPORTED) {
13541df5928SHari Bathini 		pr_emerg("Reboot type %d not supported.\n",
13641df5928SHari Bathini 			 OPAL_REBOOT_MPIPL);
13741df5928SHari Bathini 	} else if (rc == OPAL_HARDWARE)
13841df5928SHari Bathini 		pr_emerg("No backend support for MPIPL!\n");
13941df5928SHari Bathini }
14041df5928SHari Bathini 
14141df5928SHari Bathini static struct fadump_ops opal_fadump_ops = {
14241df5928SHari Bathini 	.fadump_init_mem_struct		= opal_fadump_init_mem_struct,
143742a265aSHari Bathini 	.fadump_get_metadata_size	= opal_fadump_get_metadata_size,
144742a265aSHari Bathini 	.fadump_setup_metadata		= opal_fadump_setup_metadata,
14541df5928SHari Bathini 	.fadump_register		= opal_fadump_register,
14641df5928SHari Bathini 	.fadump_unregister		= opal_fadump_unregister,
14741df5928SHari Bathini 	.fadump_invalidate		= opal_fadump_invalidate,
14841df5928SHari Bathini 	.fadump_process			= opal_fadump_process,
14941df5928SHari Bathini 	.fadump_region_show		= opal_fadump_region_show,
15041df5928SHari Bathini 	.fadump_trigger			= opal_fadump_trigger,
15141df5928SHari Bathini };
15241df5928SHari Bathini 
15341df5928SHari Bathini void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
15441df5928SHari Bathini {
15541df5928SHari Bathini 	unsigned long dn;
15641df5928SHari Bathini 
15741df5928SHari Bathini 	/*
15841df5928SHari Bathini 	 * Check if Firmware-Assisted Dump is supported. if yes, check
15941df5928SHari Bathini 	 * if dump has been initiated on last reboot.
16041df5928SHari Bathini 	 */
16141df5928SHari Bathini 	dn = of_get_flat_dt_subnode_by_name(node, "dump");
16241df5928SHari Bathini 	if (dn == -FDT_ERR_NOTFOUND) {
16341df5928SHari Bathini 		pr_debug("FADump support is missing!\n");
16441df5928SHari Bathini 		return;
16541df5928SHari Bathini 	}
16641df5928SHari Bathini 
16741df5928SHari Bathini 	if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) {
16841df5928SHari Bathini 		pr_err("Support missing for this f/w version!\n");
16941df5928SHari Bathini 		return;
17041df5928SHari Bathini 	}
17141df5928SHari Bathini 
17241df5928SHari Bathini 	fadump_conf->ops		= &opal_fadump_ops;
17341df5928SHari Bathini 	fadump_conf->fadump_supported	= 1;
17441df5928SHari Bathini }
175