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 
1082790d01dSHari Bathini static void opal_fadump_cleanup(struct fw_dump *fadump_conf)
1092790d01dSHari Bathini {
1102790d01dSHari Bathini 	s64 ret;
1112790d01dSHari Bathini 
1122790d01dSHari Bathini 	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0);
1132790d01dSHari Bathini 	if (ret != OPAL_SUCCESS)
1142790d01dSHari Bathini 		pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret);
1152790d01dSHari Bathini }
1162790d01dSHari Bathini 
11741df5928SHari Bathini static int __init opal_fadump_process(struct fw_dump *fadump_conf)
11841df5928SHari Bathini {
11941df5928SHari Bathini 	return -EINVAL;
12041df5928SHari Bathini }
12141df5928SHari Bathini 
12241df5928SHari Bathini static void opal_fadump_region_show(struct fw_dump *fadump_conf,
12341df5928SHari Bathini 				    struct seq_file *m)
12441df5928SHari Bathini {
125742a265aSHari Bathini 	const struct opal_fadump_mem_struct *fdm_ptr = opal_fdm;
126742a265aSHari Bathini 	u64 dumped_bytes = 0;
127742a265aSHari Bathini 	int i;
128742a265aSHari Bathini 
129742a265aSHari Bathini 	for (i = 0; i < fdm_ptr->region_cnt; i++) {
130742a265aSHari Bathini 		seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
131742a265aSHari Bathini 			   fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest);
132742a265aSHari Bathini 		seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
133742a265aSHari Bathini 			   fdm_ptr->rgn[i].size, dumped_bytes);
134742a265aSHari Bathini 	}
13541df5928SHari Bathini }
13641df5928SHari Bathini 
13741df5928SHari Bathini static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
13841df5928SHari Bathini 				const char *msg)
13941df5928SHari Bathini {
14041df5928SHari Bathini 	int rc;
14141df5928SHari Bathini 
14241df5928SHari Bathini 	rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg);
14341df5928SHari Bathini 	if (rc == OPAL_UNSUPPORTED) {
14441df5928SHari Bathini 		pr_emerg("Reboot type %d not supported.\n",
14541df5928SHari Bathini 			 OPAL_REBOOT_MPIPL);
14641df5928SHari Bathini 	} else if (rc == OPAL_HARDWARE)
14741df5928SHari Bathini 		pr_emerg("No backend support for MPIPL!\n");
14841df5928SHari Bathini }
14941df5928SHari Bathini 
15041df5928SHari Bathini static struct fadump_ops opal_fadump_ops = {
15141df5928SHari Bathini 	.fadump_init_mem_struct		= opal_fadump_init_mem_struct,
152742a265aSHari Bathini 	.fadump_get_metadata_size	= opal_fadump_get_metadata_size,
153742a265aSHari Bathini 	.fadump_setup_metadata		= opal_fadump_setup_metadata,
15441df5928SHari Bathini 	.fadump_register		= opal_fadump_register,
15541df5928SHari Bathini 	.fadump_unregister		= opal_fadump_unregister,
15641df5928SHari Bathini 	.fadump_invalidate		= opal_fadump_invalidate,
1572790d01dSHari Bathini 	.fadump_cleanup			= opal_fadump_cleanup,
15841df5928SHari Bathini 	.fadump_process			= opal_fadump_process,
15941df5928SHari Bathini 	.fadump_region_show		= opal_fadump_region_show,
16041df5928SHari Bathini 	.fadump_trigger			= opal_fadump_trigger,
16141df5928SHari Bathini };
16241df5928SHari Bathini 
16341df5928SHari Bathini void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
16441df5928SHari Bathini {
16541df5928SHari Bathini 	unsigned long dn;
16641df5928SHari Bathini 
16741df5928SHari Bathini 	/*
16841df5928SHari Bathini 	 * Check if Firmware-Assisted Dump is supported. if yes, check
16941df5928SHari Bathini 	 * if dump has been initiated on last reboot.
17041df5928SHari Bathini 	 */
17141df5928SHari Bathini 	dn = of_get_flat_dt_subnode_by_name(node, "dump");
17241df5928SHari Bathini 	if (dn == -FDT_ERR_NOTFOUND) {
17341df5928SHari Bathini 		pr_debug("FADump support is missing!\n");
17441df5928SHari Bathini 		return;
17541df5928SHari Bathini 	}
17641df5928SHari Bathini 
17741df5928SHari Bathini 	if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) {
17841df5928SHari Bathini 		pr_err("Support missing for this f/w version!\n");
17941df5928SHari Bathini 		return;
18041df5928SHari Bathini 	}
18141df5928SHari Bathini 
18241df5928SHari Bathini 	fadump_conf->ops		= &opal_fadump_ops;
18341df5928SHari Bathini 	fadump_conf->fadump_supported	= 1;
18441df5928SHari Bathini }
185