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>
152a1b06ddSHari Bathini #include <linux/crash_dump.h>
1641df5928SHari Bathini 
17742a265aSHari Bathini #include <asm/page.h>
1841df5928SHari Bathini #include <asm/opal.h>
1941df5928SHari Bathini #include <asm/fadump-internal.h>
2041df5928SHari Bathini 
21742a265aSHari Bathini #include "opal-fadump.h"
22742a265aSHari Bathini 
23bec53196SHari Bathini 
24bec53196SHari Bathini #ifdef CONFIG_PRESERVE_FA_DUMP
25bec53196SHari Bathini /*
26bec53196SHari Bathini  * When dump is active but PRESERVE_FA_DUMP is enabled on the kernel,
27bec53196SHari Bathini  * ensure crash data is preserved in hope that the subsequent memory
28bec53196SHari Bathini  * preserving kernel boot is going to process this crash data.
29bec53196SHari Bathini  */
30bec53196SHari Bathini void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
31bec53196SHari Bathini {
32bec53196SHari Bathini 	const struct opal_fadump_mem_struct *opal_fdm_active;
33bec53196SHari Bathini 	const __be32 *prop;
34bec53196SHari Bathini 	unsigned long dn;
35bec53196SHari Bathini 	u64 addr = 0;
36bec53196SHari Bathini 	s64 ret;
37bec53196SHari Bathini 
38bec53196SHari Bathini 	dn = of_get_flat_dt_subnode_by_name(node, "dump");
39bec53196SHari Bathini 	if (dn == -FDT_ERR_NOTFOUND)
40bec53196SHari Bathini 		return;
41bec53196SHari Bathini 
42bec53196SHari Bathini 	/*
43bec53196SHari Bathini 	 * Check if dump has been initiated on last reboot.
44bec53196SHari Bathini 	 */
45bec53196SHari Bathini 	prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL);
46bec53196SHari Bathini 	if (!prop)
47bec53196SHari Bathini 		return;
48bec53196SHari Bathini 
49bec53196SHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr);
50bec53196SHari Bathini 	if ((ret != OPAL_SUCCESS) || !addr) {
51bec53196SHari Bathini 		pr_debug("Could not get Kernel metadata (%lld)\n", ret);
52bec53196SHari Bathini 		return;
53bec53196SHari Bathini 	}
54bec53196SHari Bathini 
55bec53196SHari Bathini 	/*
56bec53196SHari Bathini 	 * Preserve memory only if kernel memory regions are registered
57bec53196SHari Bathini 	 * with f/w for MPIPL.
58bec53196SHari Bathini 	 */
59bec53196SHari Bathini 	addr = be64_to_cpu(addr);
60bec53196SHari Bathini 	pr_debug("Kernel metadata addr: %llx\n", addr);
61bec53196SHari Bathini 	opal_fdm_active = (void *)addr;
62bec53196SHari Bathini 	if (opal_fdm_active->registered_regions == 0)
63bec53196SHari Bathini 		return;
64bec53196SHari Bathini 
65bec53196SHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_BOOT_MEM, &addr);
66bec53196SHari Bathini 	if ((ret != OPAL_SUCCESS) || !addr) {
67bec53196SHari Bathini 		pr_err("Failed to get boot memory tag (%lld)\n", ret);
68bec53196SHari Bathini 		return;
69bec53196SHari Bathini 	}
70bec53196SHari Bathini 
71bec53196SHari Bathini 	/*
72bec53196SHari Bathini 	 * Memory below this address can be used for booting a
73bec53196SHari Bathini 	 * capture kernel or petitboot kernel. Preserve everything
74bec53196SHari Bathini 	 * above this address for processing crashdump.
75bec53196SHari Bathini 	 */
76bec53196SHari Bathini 	fadump_conf->boot_mem_top = be64_to_cpu(addr);
77bec53196SHari Bathini 	pr_debug("Preserve everything above %llx\n", fadump_conf->boot_mem_top);
78bec53196SHari Bathini 
79bec53196SHari Bathini 	pr_info("Firmware-assisted dump is active.\n");
80bec53196SHari Bathini 	fadump_conf->dump_active = 1;
81bec53196SHari Bathini }
82bec53196SHari Bathini 
83bec53196SHari Bathini #else /* CONFIG_PRESERVE_FA_DUMP */
842a1b06ddSHari Bathini static const struct opal_fadump_mem_struct *opal_fdm_active;
855000a17aSHari Bathini static const struct opal_mpipl_fadump *opal_cpu_metadata;
86742a265aSHari Bathini static struct opal_fadump_mem_struct *opal_fdm;
87742a265aSHari Bathini 
886f713d18SHari Bathini #ifdef CONFIG_OPAL_CORE
896f713d18SHari Bathini extern bool kernel_initiated;
906f713d18SHari Bathini #endif
916f713d18SHari Bathini 
92a20a8fa4SHari Bathini static int opal_fadump_unregister(struct fw_dump *fadump_conf);
93a20a8fa4SHari Bathini 
94a20a8fa4SHari Bathini static void opal_fadump_update_config(struct fw_dump *fadump_conf,
95a20a8fa4SHari Bathini 				      const struct opal_fadump_mem_struct *fdm)
96a20a8fa4SHari Bathini {
9751bba8edSHari Bathini 	pr_debug("Boot memory regions count: %d\n", fdm->region_cnt);
9851bba8edSHari Bathini 
99a20a8fa4SHari Bathini 	/*
100a20a8fa4SHari Bathini 	 * The destination address of the first boot memory region is the
101a20a8fa4SHari Bathini 	 * destination address of boot memory regions.
102a20a8fa4SHari Bathini 	 */
103a20a8fa4SHari Bathini 	fadump_conf->boot_mem_dest_addr = fdm->rgn[0].dest;
104a20a8fa4SHari Bathini 	pr_debug("Destination address of boot memory regions: %#016llx\n",
105a20a8fa4SHari Bathini 		 fadump_conf->boot_mem_dest_addr);
106a20a8fa4SHari Bathini 
107a20a8fa4SHari Bathini 	fadump_conf->fadumphdr_addr = fdm->fadumphdr_addr;
108a20a8fa4SHari Bathini }
109a20a8fa4SHari Bathini 
1102a1b06ddSHari Bathini /*
1112a1b06ddSHari Bathini  * This function is called in the capture kernel to get configuration details
1122a1b06ddSHari Bathini  * from metadata setup by the first kernel.
1132a1b06ddSHari Bathini  */
1142a1b06ddSHari Bathini static void opal_fadump_get_config(struct fw_dump *fadump_conf,
1152a1b06ddSHari Bathini 				   const struct opal_fadump_mem_struct *fdm)
1162a1b06ddSHari Bathini {
1172a1b06ddSHari Bathini 	int i;
1182a1b06ddSHari Bathini 
1192a1b06ddSHari Bathini 	if (!fadump_conf->dump_active)
1202a1b06ddSHari Bathini 		return;
1212a1b06ddSHari Bathini 
1222a1b06ddSHari Bathini 	fadump_conf->boot_memory_size = 0;
1232a1b06ddSHari Bathini 
1242a1b06ddSHari Bathini 	pr_debug("Boot memory regions:\n");
1252a1b06ddSHari Bathini 	for (i = 0; i < fdm->region_cnt; i++) {
1262a1b06ddSHari Bathini 		pr_debug("\t%d. base: 0x%llx, size: 0x%llx\n",
1272a1b06ddSHari Bathini 			 (i + 1), fdm->rgn[i].src, fdm->rgn[i].size);
1282a1b06ddSHari Bathini 
1292a1b06ddSHari Bathini 		fadump_conf->boot_memory_size += fdm->rgn[i].size;
1302a1b06ddSHari Bathini 	}
1312a1b06ddSHari Bathini 
1322a1b06ddSHari Bathini 	/*
1332a1b06ddSHari Bathini 	 * Start address of reserve dump area (permanent reservation) for
1342a1b06ddSHari Bathini 	 * re-registering FADump after dump capture.
1352a1b06ddSHari Bathini 	 */
1362a1b06ddSHari Bathini 	fadump_conf->reserve_dump_area_start = fdm->rgn[0].dest;
1372a1b06ddSHari Bathini 
1386071e8f9SHari Bathini 	/*
1396071e8f9SHari Bathini 	 * Rarely, but it can so happen that system crashes before all
1406071e8f9SHari Bathini 	 * boot memory regions are registered for MPIPL. In such
1416071e8f9SHari Bathini 	 * cases, warn that the vmcore may not be accurate and proceed
1426071e8f9SHari Bathini 	 * anyway as that is the best bet considering free pages, cache
1436071e8f9SHari Bathini 	 * pages, user pages, etc are usually filtered out.
1446071e8f9SHari Bathini 	 *
1456071e8f9SHari Bathini 	 * Hope the memory that could not be preserved only has pages
1466071e8f9SHari Bathini 	 * that are usually filtered out while saving the vmcore.
1476071e8f9SHari Bathini 	 */
1486071e8f9SHari Bathini 	if (fdm->region_cnt > fdm->registered_regions) {
1496071e8f9SHari Bathini 		pr_warn("Not all memory regions were saved!!!\n");
1506071e8f9SHari Bathini 		pr_warn("  Unsaved memory regions:\n");
1516071e8f9SHari Bathini 		i = fdm->registered_regions;
1526071e8f9SHari Bathini 		while (i < fdm->region_cnt) {
1536071e8f9SHari Bathini 			pr_warn("\t[%03d] base: 0x%llx, size: 0x%llx\n",
1546071e8f9SHari Bathini 				i, fdm->rgn[i].src, fdm->rgn[i].size);
1556071e8f9SHari Bathini 			i++;
1566071e8f9SHari Bathini 		}
1576071e8f9SHari Bathini 
1586071e8f9SHari Bathini 		pr_warn("If the unsaved regions only contain pages that are filtered out (eg. free/user pages), the vmcore should still be usable.\n");
1596071e8f9SHari Bathini 		pr_warn("WARNING: If the unsaved regions contain kernel pages, the vmcore will be corrupted.\n");
1606071e8f9SHari Bathini 	}
1616071e8f9SHari Bathini 
1622a1b06ddSHari Bathini 	opal_fadump_update_config(fadump_conf, fdm);
1632a1b06ddSHari Bathini }
1642a1b06ddSHari Bathini 
165742a265aSHari Bathini /* Initialize kernel metadata */
166742a265aSHari Bathini static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm)
167742a265aSHari Bathini {
168742a265aSHari Bathini 	fdm->version = OPAL_FADUMP_VERSION;
169742a265aSHari Bathini 	fdm->region_cnt = 0;
170742a265aSHari Bathini 	fdm->registered_regions = 0;
171742a265aSHari Bathini 	fdm->fadumphdr_addr = 0;
172742a265aSHari Bathini }
173742a265aSHari Bathini 
17441df5928SHari Bathini static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf)
17541df5928SHari Bathini {
17651bba8edSHari Bathini 	int max_copy_size, cur_size, size;
17751bba8edSHari Bathini 	u64 src_addr, dest_addr;
178742a265aSHari Bathini 
179742a265aSHari Bathini 	opal_fdm = __va(fadump_conf->kernel_metadata);
180742a265aSHari Bathini 	opal_fadump_init_metadata(opal_fdm);
181742a265aSHari Bathini 
18251bba8edSHari Bathini 	/*
18351bba8edSHari Bathini 	 * Firmware supports 32-bit field for size. Align it to PAGE_SIZE
18451bba8edSHari Bathini 	 * and request firmware to copy multiple kernel boot memory regions.
18551bba8edSHari Bathini 	 */
18651bba8edSHari Bathini 	max_copy_size = _ALIGN_DOWN(U32_MAX, PAGE_SIZE);
18751bba8edSHari Bathini 
18851bba8edSHari Bathini 	/* Boot memory regions */
18951bba8edSHari Bathini 	src_addr = 0;
19051bba8edSHari Bathini 	dest_addr = fadump_conf->reserve_dump_area_start;
19151bba8edSHari Bathini 	size = fadump_conf->boot_memory_size;
19251bba8edSHari Bathini 	while (size) {
19351bba8edSHari Bathini 		cur_size = size > max_copy_size ? max_copy_size : size;
19451bba8edSHari Bathini 
19551bba8edSHari Bathini 		opal_fdm->rgn[opal_fdm->region_cnt].src  = src_addr;
19651bba8edSHari Bathini 		opal_fdm->rgn[opal_fdm->region_cnt].dest = dest_addr;
19751bba8edSHari Bathini 		opal_fdm->rgn[opal_fdm->region_cnt].size = cur_size;
19851bba8edSHari Bathini 
19951bba8edSHari Bathini 		opal_fdm->region_cnt++;
20051bba8edSHari Bathini 		dest_addr	+= cur_size;
20151bba8edSHari Bathini 		src_addr	+= cur_size;
20251bba8edSHari Bathini 		size		-= cur_size;
20351bba8edSHari Bathini 	}
204742a265aSHari Bathini 
205742a265aSHari Bathini 	/*
206742a265aSHari Bathini 	 * Kernel metadata is passed to f/w and retrieved in capture kerenl.
207742a265aSHari Bathini 	 * So, use it to save fadump header address instead of calculating it.
208742a265aSHari Bathini 	 */
209742a265aSHari Bathini 	opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest +
210742a265aSHari Bathini 				    fadump_conf->boot_memory_size);
211742a265aSHari Bathini 
212a20a8fa4SHari Bathini 	opal_fadump_update_config(fadump_conf, opal_fdm);
213a20a8fa4SHari Bathini 
21451bba8edSHari Bathini 	return dest_addr;
215742a265aSHari Bathini }
216742a265aSHari Bathini 
217742a265aSHari Bathini static u64 opal_fadump_get_metadata_size(void)
218742a265aSHari Bathini {
219742a265aSHari Bathini 	return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct));
220742a265aSHari Bathini }
221742a265aSHari Bathini 
222742a265aSHari Bathini static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf)
223742a265aSHari Bathini {
224742a265aSHari Bathini 	int err = 0;
225742a265aSHari Bathini 	s64 ret;
226742a265aSHari Bathini 
227742a265aSHari Bathini 	/*
228742a265aSHari Bathini 	 * Use the last page(s) in FADump memory reservation for
229742a265aSHari Bathini 	 * kernel metadata.
230742a265aSHari Bathini 	 */
231742a265aSHari Bathini 	fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start +
232742a265aSHari Bathini 					fadump_conf->reserve_dump_area_size -
233742a265aSHari Bathini 					opal_fadump_get_metadata_size());
234742a265aSHari Bathini 	pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata);
235742a265aSHari Bathini 
236742a265aSHari Bathini 	/* Initialize kernel metadata before registering the address with f/w */
237742a265aSHari Bathini 	opal_fdm = __va(fadump_conf->kernel_metadata);
238742a265aSHari Bathini 	opal_fadump_init_metadata(opal_fdm);
239742a265aSHari Bathini 
240742a265aSHari Bathini 	/*
241742a265aSHari Bathini 	 * Register metadata address with f/w. Can be retrieved in
242742a265aSHari Bathini 	 * the capture kernel.
243742a265aSHari Bathini 	 */
244742a265aSHari Bathini 	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL,
245742a265aSHari Bathini 				      fadump_conf->kernel_metadata);
246742a265aSHari Bathini 	if (ret != OPAL_SUCCESS) {
247742a265aSHari Bathini 		pr_err("Failed to set kernel metadata tag!\n");
248742a265aSHari Bathini 		err = -EPERM;
249742a265aSHari Bathini 	}
250742a265aSHari Bathini 
251bec53196SHari Bathini 	/*
252bec53196SHari Bathini 	 * Register boot memory top address with f/w. Should be retrieved
253bec53196SHari Bathini 	 * by a kernel that intends to preserve crash'ed kernel's memory.
254bec53196SHari Bathini 	 */
255bec53196SHari Bathini 	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_BOOT_MEM,
256bec53196SHari Bathini 				      fadump_conf->boot_memory_size);
257bec53196SHari Bathini 	if (ret != OPAL_SUCCESS) {
258bec53196SHari Bathini 		pr_err("Failed to set boot memory tag!\n");
259bec53196SHari Bathini 		err = -EPERM;
260bec53196SHari Bathini 	}
261bec53196SHari Bathini 
262742a265aSHari Bathini 	return err;
26341df5928SHari Bathini }
26441df5928SHari Bathini 
26541df5928SHari Bathini static int opal_fadump_register(struct fw_dump *fadump_conf)
26641df5928SHari Bathini {
267a20a8fa4SHari Bathini 	s64 rc = OPAL_PARAMETER;
268a20a8fa4SHari Bathini 	int i, err = -EIO;
269a20a8fa4SHari Bathini 
270a20a8fa4SHari Bathini 	for (i = 0; i < opal_fdm->region_cnt; i++) {
271a20a8fa4SHari Bathini 		rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE,
272a20a8fa4SHari Bathini 				       opal_fdm->rgn[i].src,
273a20a8fa4SHari Bathini 				       opal_fdm->rgn[i].dest,
274a20a8fa4SHari Bathini 				       opal_fdm->rgn[i].size);
275a20a8fa4SHari Bathini 		if (rc != OPAL_SUCCESS)
276a20a8fa4SHari Bathini 			break;
277a20a8fa4SHari Bathini 
278a20a8fa4SHari Bathini 		opal_fdm->registered_regions++;
279a20a8fa4SHari Bathini 	}
280a20a8fa4SHari Bathini 
281a20a8fa4SHari Bathini 	switch (rc) {
282a20a8fa4SHari Bathini 	case OPAL_SUCCESS:
283a20a8fa4SHari Bathini 		pr_info("Registration is successful!\n");
284a20a8fa4SHari Bathini 		fadump_conf->dump_registered = 1;
285a20a8fa4SHari Bathini 		err = 0;
286a20a8fa4SHari Bathini 		break;
287a20a8fa4SHari Bathini 	case OPAL_RESOURCE:
288a20a8fa4SHari Bathini 		/* If MAX regions limit in f/w is hit, warn and proceed. */
289a20a8fa4SHari Bathini 		pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n",
290a20a8fa4SHari Bathini 			(opal_fdm->region_cnt - opal_fdm->registered_regions));
291a20a8fa4SHari Bathini 		fadump_conf->dump_registered = 1;
292a20a8fa4SHari Bathini 		err = 0;
293a20a8fa4SHari Bathini 		break;
294a20a8fa4SHari Bathini 	case OPAL_PARAMETER:
295a20a8fa4SHari Bathini 		pr_err("Failed to register. Parameter Error(%lld).\n", rc);
296a20a8fa4SHari Bathini 		break;
297a20a8fa4SHari Bathini 	case OPAL_HARDWARE:
298a20a8fa4SHari Bathini 		pr_err("Support not available.\n");
299a20a8fa4SHari Bathini 		fadump_conf->fadump_supported = 0;
300a20a8fa4SHari Bathini 		fadump_conf->fadump_enabled = 0;
301a20a8fa4SHari Bathini 		break;
302a20a8fa4SHari Bathini 	default:
303a20a8fa4SHari Bathini 		pr_err("Failed to register. Unknown Error(%lld).\n", rc);
304a20a8fa4SHari Bathini 		break;
305a20a8fa4SHari Bathini 	}
306a20a8fa4SHari Bathini 
307a20a8fa4SHari Bathini 	/*
308a20a8fa4SHari Bathini 	 * If some regions were registered before OPAL_MPIPL_ADD_RANGE
309a20a8fa4SHari Bathini 	 * OPAL call failed, unregister all regions.
310a20a8fa4SHari Bathini 	 */
311a20a8fa4SHari Bathini 	if ((err < 0) && (opal_fdm->registered_regions > 0))
312a20a8fa4SHari Bathini 		opal_fadump_unregister(fadump_conf);
313a20a8fa4SHari Bathini 
314a20a8fa4SHari Bathini 	return err;
31541df5928SHari Bathini }
31641df5928SHari Bathini 
31741df5928SHari Bathini static int opal_fadump_unregister(struct fw_dump *fadump_conf)
31841df5928SHari Bathini {
319a20a8fa4SHari Bathini 	s64 rc;
320a20a8fa4SHari Bathini 
321a20a8fa4SHari Bathini 	rc = opal_mpipl_update(OPAL_MPIPL_REMOVE_ALL, 0, 0, 0);
322a20a8fa4SHari Bathini 	if (rc) {
323a20a8fa4SHari Bathini 		pr_err("Failed to un-register - unexpected Error(%lld).\n", rc);
32441df5928SHari Bathini 		return -EIO;
32541df5928SHari Bathini 	}
32641df5928SHari Bathini 
327a20a8fa4SHari Bathini 	opal_fdm->registered_regions = 0;
328a20a8fa4SHari Bathini 	fadump_conf->dump_registered = 0;
329a20a8fa4SHari Bathini 	return 0;
330a20a8fa4SHari Bathini }
331a20a8fa4SHari Bathini 
33241df5928SHari Bathini static int opal_fadump_invalidate(struct fw_dump *fadump_conf)
33341df5928SHari Bathini {
334a4e2e2caSHari Bathini 	s64 rc;
335a4e2e2caSHari Bathini 
336a4e2e2caSHari Bathini 	rc = opal_mpipl_update(OPAL_MPIPL_FREE_PRESERVED_MEMORY, 0, 0, 0);
337a4e2e2caSHari Bathini 	if (rc) {
338a4e2e2caSHari Bathini 		pr_err("Failed to invalidate - unexpected Error(%lld).\n", rc);
33941df5928SHari Bathini 		return -EIO;
34041df5928SHari Bathini 	}
34141df5928SHari Bathini 
342a4e2e2caSHari Bathini 	fadump_conf->dump_active = 0;
343a4e2e2caSHari Bathini 	opal_fdm_active = NULL;
344a4e2e2caSHari Bathini 	return 0;
345a4e2e2caSHari Bathini }
346a4e2e2caSHari Bathini 
3472790d01dSHari Bathini static void opal_fadump_cleanup(struct fw_dump *fadump_conf)
3482790d01dSHari Bathini {
3492790d01dSHari Bathini 	s64 ret;
3502790d01dSHari Bathini 
3512790d01dSHari Bathini 	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0);
3522790d01dSHari Bathini 	if (ret != OPAL_SUCCESS)
3532790d01dSHari Bathini 		pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret);
3542790d01dSHari Bathini }
3552790d01dSHari Bathini 
3565000a17aSHari Bathini /*
3575000a17aSHari Bathini  * Verify if CPU state data is available. If available, do a bit of sanity
3585000a17aSHari Bathini  * checking before processing this data.
3595000a17aSHari Bathini  */
3605000a17aSHari Bathini static bool __init is_opal_fadump_cpu_data_valid(struct fw_dump *fadump_conf)
3615000a17aSHari Bathini {
3625000a17aSHari Bathini 	if (!opal_cpu_metadata)
3635000a17aSHari Bathini 		return false;
3645000a17aSHari Bathini 
3655000a17aSHari Bathini 	fadump_conf->cpu_state_data_version =
3665000a17aSHari Bathini 		be32_to_cpu(opal_cpu_metadata->cpu_data_version);
3675000a17aSHari Bathini 	fadump_conf->cpu_state_entry_size =
3685000a17aSHari Bathini 		be32_to_cpu(opal_cpu_metadata->cpu_data_size);
3695000a17aSHari Bathini 	fadump_conf->cpu_state_dest_vaddr =
3705000a17aSHari Bathini 		(u64)__va(be64_to_cpu(opal_cpu_metadata->region[0].dest));
3715000a17aSHari Bathini 	fadump_conf->cpu_state_data_size =
3725000a17aSHari Bathini 		be64_to_cpu(opal_cpu_metadata->region[0].size);
3735000a17aSHari Bathini 
3745000a17aSHari Bathini 	if (fadump_conf->cpu_state_data_version != HDAT_FADUMP_CPU_DATA_VER) {
3755000a17aSHari Bathini 		pr_warn("Supported CPU state data version: %u, found: %d!\n",
3765000a17aSHari Bathini 			HDAT_FADUMP_CPU_DATA_VER,
3775000a17aSHari Bathini 			fadump_conf->cpu_state_data_version);
3785000a17aSHari Bathini 		pr_warn("WARNING: F/W using newer CPU state data format!!\n");
3795000a17aSHari Bathini 	}
3805000a17aSHari Bathini 
3815000a17aSHari Bathini 	if ((fadump_conf->cpu_state_dest_vaddr == 0) ||
3825000a17aSHari Bathini 	    (fadump_conf->cpu_state_entry_size == 0) ||
3835000a17aSHari Bathini 	    (fadump_conf->cpu_state_entry_size >
3845000a17aSHari Bathini 	     fadump_conf->cpu_state_data_size)) {
3855000a17aSHari Bathini 		pr_err("CPU state data is invalid. Ignoring!\n");
3865000a17aSHari Bathini 		return false;
3875000a17aSHari Bathini 	}
3885000a17aSHari Bathini 
3895000a17aSHari Bathini 	return true;
3905000a17aSHari Bathini }
3915000a17aSHari Bathini 
3922a1b06ddSHari Bathini /*
3932a1b06ddSHari Bathini  * Convert CPU state data saved at the time of crash into ELF notes.
3942a1b06ddSHari Bathini  *
3955000a17aSHari Bathini  * While the crashing CPU's register data is saved by the kernel, CPU state
3965000a17aSHari Bathini  * data for all CPUs is saved by f/w. In CPU state data provided by f/w,
3975000a17aSHari Bathini  * each register entry is of 16 bytes, a numerical identifier along with
3985000a17aSHari Bathini  * a GPR/SPR flag in the first 8 bytes and the register value in the next
3995000a17aSHari Bathini  * 8 bytes. For more details refer to F/W documentation. If this data is
4005000a17aSHari Bathini  * missing or in unsupported format, append crashing CPU's register data
4015000a17aSHari Bathini  * saved by the kernel in the PT_NOTE, to have something to work with in
4025000a17aSHari Bathini  * the vmcore file.
4032a1b06ddSHari Bathini  */
4042a1b06ddSHari Bathini static int __init
4052a1b06ddSHari Bathini opal_fadump_build_cpu_notes(struct fw_dump *fadump_conf,
4062a1b06ddSHari Bathini 			    struct fadump_crash_info_header *fdh)
4072a1b06ddSHari Bathini {
4085000a17aSHari Bathini 	u32 thread_pir, size_per_thread, regs_offset, regs_cnt, reg_esize;
4095000a17aSHari Bathini 	struct hdat_fadump_thread_hdr *thdr;
4105000a17aSHari Bathini 	bool is_cpu_data_valid = false;
4112a1b06ddSHari Bathini 	u32 num_cpus = 1, *note_buf;
4125000a17aSHari Bathini 	struct pt_regs regs;
4135000a17aSHari Bathini 	char *bufp;
4145000a17aSHari Bathini 	int rc, i;
4152a1b06ddSHari Bathini 
4165000a17aSHari Bathini 	if (is_opal_fadump_cpu_data_valid(fadump_conf)) {
4175000a17aSHari Bathini 		size_per_thread = fadump_conf->cpu_state_entry_size;
4185000a17aSHari Bathini 		num_cpus = (fadump_conf->cpu_state_data_size / size_per_thread);
4195000a17aSHari Bathini 		bufp = __va(fadump_conf->cpu_state_dest_vaddr);
4205000a17aSHari Bathini 		is_cpu_data_valid = true;
4215000a17aSHari Bathini 	}
4222a1b06ddSHari Bathini 
4232a1b06ddSHari Bathini 	rc = fadump_setup_cpu_notes_buf(num_cpus);
4242a1b06ddSHari Bathini 	if (rc != 0)
4252a1b06ddSHari Bathini 		return rc;
4262a1b06ddSHari Bathini 
4272a1b06ddSHari Bathini 	note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr;
4285000a17aSHari Bathini 	if (!is_cpu_data_valid)
4295000a17aSHari Bathini 		goto out;
4305000a17aSHari Bathini 
4315000a17aSHari Bathini 	/*
4325000a17aSHari Bathini 	 * Offset for register entries, entry size and registers count is
4335000a17aSHari Bathini 	 * duplicated in every thread header in keeping with HDAT format.
4345000a17aSHari Bathini 	 * Use these values from the first thread header.
4355000a17aSHari Bathini 	 */
4365000a17aSHari Bathini 	thdr = (struct hdat_fadump_thread_hdr *)bufp;
4375000a17aSHari Bathini 	regs_offset = (offsetof(struct hdat_fadump_thread_hdr, offset) +
4385000a17aSHari Bathini 		       be32_to_cpu(thdr->offset));
4395000a17aSHari Bathini 	reg_esize = be32_to_cpu(thdr->esize);
4405000a17aSHari Bathini 	regs_cnt  = be32_to_cpu(thdr->ecnt);
4415000a17aSHari Bathini 
4425000a17aSHari Bathini 	pr_debug("--------CPU State Data------------\n");
4435000a17aSHari Bathini 	pr_debug("NumCpus     : %u\n", num_cpus);
4445000a17aSHari Bathini 	pr_debug("\tOffset: %u, Entry size: %u, Cnt: %u\n",
4455000a17aSHari Bathini 		 regs_offset, reg_esize, regs_cnt);
4465000a17aSHari Bathini 
4475000a17aSHari Bathini 	for (i = 0; i < num_cpus; i++, bufp += size_per_thread) {
4485000a17aSHari Bathini 		thdr = (struct hdat_fadump_thread_hdr *)bufp;
4495000a17aSHari Bathini 
4505000a17aSHari Bathini 		thread_pir = be32_to_cpu(thdr->pir);
4515000a17aSHari Bathini 		pr_debug("[%04d] PIR: 0x%x, core state: 0x%02x\n",
4525000a17aSHari Bathini 			 i, thread_pir, thdr->core_state);
4535000a17aSHari Bathini 
4545000a17aSHari Bathini 		/*
4555000a17aSHari Bathini 		 * If this is kernel initiated crash, crashing_cpu would be set
4565000a17aSHari Bathini 		 * appropriately and register data of the crashing CPU saved by
4575000a17aSHari Bathini 		 * crashing kernel. Add this saved register data of crashing CPU
4585000a17aSHari Bathini 		 * to elf notes and populate the pt_regs for the remaining CPUs
4595000a17aSHari Bathini 		 * from register state data provided by firmware.
4605000a17aSHari Bathini 		 */
4615000a17aSHari Bathini 		if (fdh->crashing_cpu == thread_pir) {
4625000a17aSHari Bathini 			note_buf = fadump_regs_to_elf_notes(note_buf,
4635000a17aSHari Bathini 							    &fdh->regs);
4645000a17aSHari Bathini 			pr_debug("Crashing CPU PIR: 0x%x - R1 : 0x%lx, NIP : 0x%lx\n",
4655000a17aSHari Bathini 				 fdh->crashing_cpu, fdh->regs.gpr[1],
4665000a17aSHari Bathini 				 fdh->regs.nip);
4675000a17aSHari Bathini 			continue;
4685000a17aSHari Bathini 		}
4695000a17aSHari Bathini 
4705000a17aSHari Bathini 		/*
4715000a17aSHari Bathini 		 * Register state data of MAX cores is provided by firmware,
4725000a17aSHari Bathini 		 * but some of this cores may not be active. So, while
4735000a17aSHari Bathini 		 * processing register state data, check core state and
4745000a17aSHari Bathini 		 * skip threads that belong to inactive cores.
4755000a17aSHari Bathini 		 */
4765000a17aSHari Bathini 		if (thdr->core_state == HDAT_FADUMP_CORE_INACTIVE)
4775000a17aSHari Bathini 			continue;
4785000a17aSHari Bathini 
4795000a17aSHari Bathini 		opal_fadump_read_regs((bufp + regs_offset), regs_cnt,
4806f713d18SHari Bathini 				      reg_esize, true, &regs);
4815000a17aSHari Bathini 		note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
4825000a17aSHari Bathini 		pr_debug("CPU PIR: 0x%x - R1 : 0x%lx, NIP : 0x%lx\n",
4835000a17aSHari Bathini 			 thread_pir, regs.gpr[1], regs.nip);
4845000a17aSHari Bathini 	}
4855000a17aSHari Bathini 
4865000a17aSHari Bathini out:
4875000a17aSHari Bathini 	/*
4885000a17aSHari Bathini 	 * CPU state data is invalid/unsupported. Try appending crashing CPU's
4895000a17aSHari Bathini 	 * register data, if it is saved by the kernel.
4905000a17aSHari Bathini 	 */
4915000a17aSHari Bathini 	if (fadump_conf->cpu_notes_buf_vaddr == (u64)note_buf) {
4925000a17aSHari Bathini 		if (fdh->crashing_cpu == FADUMP_CPU_UNKNOWN) {
4935000a17aSHari Bathini 			fadump_free_cpu_notes_buf();
4945000a17aSHari Bathini 			return -ENODEV;
4955000a17aSHari Bathini 		}
4965000a17aSHari Bathini 
4975000a17aSHari Bathini 		pr_warn("WARNING: appending only crashing CPU's register data\n");
4982a1b06ddSHari Bathini 		note_buf = fadump_regs_to_elf_notes(note_buf, &(fdh->regs));
4995000a17aSHari Bathini 	}
5005000a17aSHari Bathini 
5012a1b06ddSHari Bathini 	final_note(note_buf);
5022a1b06ddSHari Bathini 
5032a1b06ddSHari Bathini 	pr_debug("Updating elfcore header (%llx) with cpu notes\n",
5042a1b06ddSHari Bathini 		 fdh->elfcorehdr_addr);
5052a1b06ddSHari Bathini 	fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr));
5062a1b06ddSHari Bathini 	return 0;
5072a1b06ddSHari Bathini }
5082a1b06ddSHari Bathini 
50941df5928SHari Bathini static int __init opal_fadump_process(struct fw_dump *fadump_conf)
51041df5928SHari Bathini {
5112a1b06ddSHari Bathini 	struct fadump_crash_info_header *fdh;
5122a1b06ddSHari Bathini 	int rc = -EINVAL;
5132a1b06ddSHari Bathini 
5142a1b06ddSHari Bathini 	if (!opal_fdm_active || !fadump_conf->fadumphdr_addr)
5152a1b06ddSHari Bathini 		return rc;
5162a1b06ddSHari Bathini 
5172a1b06ddSHari Bathini 	/* Validate the fadump crash info header */
5182a1b06ddSHari Bathini 	fdh = __va(fadump_conf->fadumphdr_addr);
5192a1b06ddSHari Bathini 	if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
5202a1b06ddSHari Bathini 		pr_err("Crash info header is not valid.\n");
5212a1b06ddSHari Bathini 		return rc;
5222a1b06ddSHari Bathini 	}
5232a1b06ddSHari Bathini 
5246f713d18SHari Bathini #ifdef CONFIG_OPAL_CORE
5256f713d18SHari Bathini 	/*
5266f713d18SHari Bathini 	 * If this is a kernel initiated crash, crashing_cpu would be set
5276f713d18SHari Bathini 	 * appropriately and register data of the crashing CPU saved by
5286f713d18SHari Bathini 	 * crashing kernel. Add this saved register data of crashing CPU
5296f713d18SHari Bathini 	 * to elf notes and populate the pt_regs for the remaining CPUs
5306f713d18SHari Bathini 	 * from register state data provided by firmware.
5316f713d18SHari Bathini 	 */
5326f713d18SHari Bathini 	if (fdh->crashing_cpu != FADUMP_CPU_UNKNOWN)
5336f713d18SHari Bathini 		kernel_initiated = true;
5346f713d18SHari Bathini #endif
5356f713d18SHari Bathini 
5362a1b06ddSHari Bathini 	rc = opal_fadump_build_cpu_notes(fadump_conf, fdh);
5372a1b06ddSHari Bathini 	if (rc)
5382a1b06ddSHari Bathini 		return rc;
5392a1b06ddSHari Bathini 
5402a1b06ddSHari Bathini 	/*
5412a1b06ddSHari Bathini 	 * We are done validating dump info and elfcore header is now ready
5422a1b06ddSHari Bathini 	 * to be exported. set elfcorehdr_addr so that vmcore module will
5432a1b06ddSHari Bathini 	 * export the elfcore header through '/proc/vmcore'.
5442a1b06ddSHari Bathini 	 */
5452a1b06ddSHari Bathini 	elfcorehdr_addr = fdh->elfcorehdr_addr;
5462a1b06ddSHari Bathini 
5472a1b06ddSHari Bathini 	return rc;
54841df5928SHari Bathini }
54941df5928SHari Bathini 
55041df5928SHari Bathini static void opal_fadump_region_show(struct fw_dump *fadump_conf,
55141df5928SHari Bathini 				    struct seq_file *m)
55241df5928SHari Bathini {
5532a1b06ddSHari Bathini 	const struct opal_fadump_mem_struct *fdm_ptr;
554742a265aSHari Bathini 	u64 dumped_bytes = 0;
555742a265aSHari Bathini 	int i;
556742a265aSHari Bathini 
5572a1b06ddSHari Bathini 	if (fadump_conf->dump_active)
5582a1b06ddSHari Bathini 		fdm_ptr = opal_fdm_active;
5592a1b06ddSHari Bathini 	else
5602a1b06ddSHari Bathini 		fdm_ptr = opal_fdm;
5612a1b06ddSHari Bathini 
562742a265aSHari Bathini 	for (i = 0; i < fdm_ptr->region_cnt; i++) {
5632a1b06ddSHari Bathini 		/*
5642a1b06ddSHari Bathini 		 * Only regions that are registered for MPIPL
5652a1b06ddSHari Bathini 		 * would have dump data.
5662a1b06ddSHari Bathini 		 */
5672a1b06ddSHari Bathini 		if ((fadump_conf->dump_active) &&
5682a1b06ddSHari Bathini 		    (i < fdm_ptr->registered_regions))
5692a1b06ddSHari Bathini 			dumped_bytes = fdm_ptr->rgn[i].size;
5702a1b06ddSHari Bathini 
571742a265aSHari Bathini 		seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
572742a265aSHari Bathini 			   fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest);
573742a265aSHari Bathini 		seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
574742a265aSHari Bathini 			   fdm_ptr->rgn[i].size, dumped_bytes);
575742a265aSHari Bathini 	}
5762a1b06ddSHari Bathini 
5772a1b06ddSHari Bathini 	/* Dump is active. Show reserved area start address. */
5782a1b06ddSHari Bathini 	if (fadump_conf->dump_active) {
5792a1b06ddSHari Bathini 		seq_printf(m, "\nMemory above %#016lx is reserved for saving crash dump\n",
5802a1b06ddSHari Bathini 			   fadump_conf->reserve_dump_area_start);
5812a1b06ddSHari Bathini 	}
58241df5928SHari Bathini }
58341df5928SHari Bathini 
58441df5928SHari Bathini static void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
58541df5928SHari Bathini 				const char *msg)
58641df5928SHari Bathini {
58741df5928SHari Bathini 	int rc;
58841df5928SHari Bathini 
5895000a17aSHari Bathini 	/*
5905000a17aSHari Bathini 	 * Unlike on pSeries platform, logical CPU number is not provided
5915000a17aSHari Bathini 	 * with architected register state data. So, store the crashing
5925000a17aSHari Bathini 	 * CPU's PIR instead to plug the appropriate register data for
5935000a17aSHari Bathini 	 * crashing CPU in the vmcore file.
5945000a17aSHari Bathini 	 */
5955000a17aSHari Bathini 	fdh->crashing_cpu = (u32)mfspr(SPRN_PIR);
5965000a17aSHari Bathini 
59741df5928SHari Bathini 	rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg);
59841df5928SHari Bathini 	if (rc == OPAL_UNSUPPORTED) {
59941df5928SHari Bathini 		pr_emerg("Reboot type %d not supported.\n",
60041df5928SHari Bathini 			 OPAL_REBOOT_MPIPL);
60141df5928SHari Bathini 	} else if (rc == OPAL_HARDWARE)
60241df5928SHari Bathini 		pr_emerg("No backend support for MPIPL!\n");
60341df5928SHari Bathini }
60441df5928SHari Bathini 
60541df5928SHari Bathini static struct fadump_ops opal_fadump_ops = {
60641df5928SHari Bathini 	.fadump_init_mem_struct		= opal_fadump_init_mem_struct,
607742a265aSHari Bathini 	.fadump_get_metadata_size	= opal_fadump_get_metadata_size,
608742a265aSHari Bathini 	.fadump_setup_metadata		= opal_fadump_setup_metadata,
60941df5928SHari Bathini 	.fadump_register		= opal_fadump_register,
61041df5928SHari Bathini 	.fadump_unregister		= opal_fadump_unregister,
61141df5928SHari Bathini 	.fadump_invalidate		= opal_fadump_invalidate,
6122790d01dSHari Bathini 	.fadump_cleanup			= opal_fadump_cleanup,
61341df5928SHari Bathini 	.fadump_process			= opal_fadump_process,
61441df5928SHari Bathini 	.fadump_region_show		= opal_fadump_region_show,
61541df5928SHari Bathini 	.fadump_trigger			= opal_fadump_trigger,
61641df5928SHari Bathini };
61741df5928SHari Bathini 
61841df5928SHari Bathini void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
61941df5928SHari Bathini {
6202a1b06ddSHari Bathini 	const __be32 *prop;
62141df5928SHari Bathini 	unsigned long dn;
6222a1b06ddSHari Bathini 	u64 addr = 0;
6232a1b06ddSHari Bathini 	s64 ret;
6242a1b06ddSHari Bathini 
62541df5928SHari Bathini 
62641df5928SHari Bathini 	/*
62741df5928SHari Bathini 	 * Check if Firmware-Assisted Dump is supported. if yes, check
62841df5928SHari Bathini 	 * if dump has been initiated on last reboot.
62941df5928SHari Bathini 	 */
63041df5928SHari Bathini 	dn = of_get_flat_dt_subnode_by_name(node, "dump");
63141df5928SHari Bathini 	if (dn == -FDT_ERR_NOTFOUND) {
63241df5928SHari Bathini 		pr_debug("FADump support is missing!\n");
63341df5928SHari Bathini 		return;
63441df5928SHari Bathini 	}
63541df5928SHari Bathini 
63641df5928SHari Bathini 	if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) {
63741df5928SHari Bathini 		pr_err("Support missing for this f/w version!\n");
63841df5928SHari Bathini 		return;
63941df5928SHari Bathini 	}
64041df5928SHari Bathini 
64141df5928SHari Bathini 	fadump_conf->ops		= &opal_fadump_ops;
64241df5928SHari Bathini 	fadump_conf->fadump_supported	= 1;
6432a1b06ddSHari Bathini 
6442a1b06ddSHari Bathini 	/*
6452a1b06ddSHari Bathini 	 * Check if dump has been initiated on last reboot.
6462a1b06ddSHari Bathini 	 */
6472a1b06ddSHari Bathini 	prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL);
6482a1b06ddSHari Bathini 	if (!prop)
6492a1b06ddSHari Bathini 		return;
6502a1b06ddSHari Bathini 
6512a1b06ddSHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr);
6522a1b06ddSHari Bathini 	if ((ret != OPAL_SUCCESS) || !addr) {
6532a1b06ddSHari Bathini 		pr_err("Failed to get Kernel metadata (%lld)\n", ret);
6542a1b06ddSHari Bathini 		return;
6552a1b06ddSHari Bathini 	}
6562a1b06ddSHari Bathini 
6572a1b06ddSHari Bathini 	addr = be64_to_cpu(addr);
6582a1b06ddSHari Bathini 	pr_debug("Kernel metadata addr: %llx\n", addr);
6592a1b06ddSHari Bathini 
6602a1b06ddSHari Bathini 	opal_fdm_active = __va(addr);
6612a1b06ddSHari Bathini 	if (opal_fdm_active->version != OPAL_FADUMP_VERSION) {
6622a1b06ddSHari Bathini 		pr_warn("Supported kernel metadata version: %u, found: %d!\n",
6632a1b06ddSHari Bathini 			OPAL_FADUMP_VERSION, opal_fdm_active->version);
6642a1b06ddSHari Bathini 		pr_warn("WARNING: Kernel metadata format mismatch identified! Core file maybe corrupted..\n");
6652a1b06ddSHari Bathini 	}
6662a1b06ddSHari Bathini 
6672a1b06ddSHari Bathini 	/* Kernel regions not registered with f/w for MPIPL */
6682a1b06ddSHari Bathini 	if (opal_fdm_active->registered_regions == 0) {
6692a1b06ddSHari Bathini 		opal_fdm_active = NULL;
6702a1b06ddSHari Bathini 		return;
6712a1b06ddSHari Bathini 	}
6722a1b06ddSHari Bathini 
6735000a17aSHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &addr);
6745000a17aSHari Bathini 	if (addr) {
6755000a17aSHari Bathini 		addr = be64_to_cpu(addr);
6765000a17aSHari Bathini 		pr_debug("CPU metadata addr: %llx\n", addr);
6775000a17aSHari Bathini 		opal_cpu_metadata = __va(addr);
6785000a17aSHari Bathini 	}
6795000a17aSHari Bathini 
6802a1b06ddSHari Bathini 	pr_info("Firmware-assisted dump is active.\n");
6812a1b06ddSHari Bathini 	fadump_conf->dump_active = 1;
6822a1b06ddSHari Bathini 	opal_fadump_get_config(fadump_conf, opal_fdm_active);
68341df5928SHari Bathini }
684bec53196SHari Bathini #endif /* !CONFIG_PRESERVE_FA_DUMP */
685