18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Firmware-Assisted Dump support on POWER platform (OPAL).
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2019, Hari Bathini, IBM Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "opal fadump: " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/string.h>
118c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_fdt.h>
148c2ecf20Sopenharmony_ci#include <linux/libfdt.h>
158c2ecf20Sopenharmony_ci#include <linux/mm.h>
168c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <asm/page.h>
198c2ecf20Sopenharmony_ci#include <asm/opal.h>
208c2ecf20Sopenharmony_ci#include <asm/fadump-internal.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "opal-fadump.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#ifdef CONFIG_PRESERVE_FA_DUMP
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci * When dump is active but PRESERVE_FA_DUMP is enabled on the kernel,
288c2ecf20Sopenharmony_ci * ensure crash data is preserved in hope that the subsequent memory
298c2ecf20Sopenharmony_ci * preserving kernel boot is going to process this crash data.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_civoid __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	const struct opal_fadump_mem_struct *opal_fdm_active;
348c2ecf20Sopenharmony_ci	const __be32 *prop;
358c2ecf20Sopenharmony_ci	unsigned long dn;
368c2ecf20Sopenharmony_ci	u64 addr = 0;
378c2ecf20Sopenharmony_ci	s64 ret;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	dn = of_get_flat_dt_subnode_by_name(node, "dump");
408c2ecf20Sopenharmony_ci	if (dn == -FDT_ERR_NOTFOUND)
418c2ecf20Sopenharmony_ci		return;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/*
448c2ecf20Sopenharmony_ci	 * Check if dump has been initiated on last reboot.
458c2ecf20Sopenharmony_ci	 */
468c2ecf20Sopenharmony_ci	prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL);
478c2ecf20Sopenharmony_ci	if (!prop)
488c2ecf20Sopenharmony_ci		return;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr);
518c2ecf20Sopenharmony_ci	if ((ret != OPAL_SUCCESS) || !addr) {
528c2ecf20Sopenharmony_ci		pr_debug("Could not get Kernel metadata (%lld)\n", ret);
538c2ecf20Sopenharmony_ci		return;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/*
578c2ecf20Sopenharmony_ci	 * Preserve memory only if kernel memory regions are registered
588c2ecf20Sopenharmony_ci	 * with f/w for MPIPL.
598c2ecf20Sopenharmony_ci	 */
608c2ecf20Sopenharmony_ci	addr = be64_to_cpu(addr);
618c2ecf20Sopenharmony_ci	pr_debug("Kernel metadata addr: %llx\n", addr);
628c2ecf20Sopenharmony_ci	opal_fdm_active = (void *)addr;
638c2ecf20Sopenharmony_ci	if (be16_to_cpu(opal_fdm_active->registered_regions) == 0)
648c2ecf20Sopenharmony_ci		return;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_BOOT_MEM, &addr);
678c2ecf20Sopenharmony_ci	if ((ret != OPAL_SUCCESS) || !addr) {
688c2ecf20Sopenharmony_ci		pr_err("Failed to get boot memory tag (%lld)\n", ret);
698c2ecf20Sopenharmony_ci		return;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	/*
738c2ecf20Sopenharmony_ci	 * Memory below this address can be used for booting a
748c2ecf20Sopenharmony_ci	 * capture kernel or petitboot kernel. Preserve everything
758c2ecf20Sopenharmony_ci	 * above this address for processing crashdump.
768c2ecf20Sopenharmony_ci	 */
778c2ecf20Sopenharmony_ci	fadump_conf->boot_mem_top = be64_to_cpu(addr);
788c2ecf20Sopenharmony_ci	pr_debug("Preserve everything above %llx\n", fadump_conf->boot_mem_top);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	pr_info("Firmware-assisted dump is active.\n");
818c2ecf20Sopenharmony_ci	fadump_conf->dump_active = 1;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#else /* CONFIG_PRESERVE_FA_DUMP */
858c2ecf20Sopenharmony_cistatic const struct opal_fadump_mem_struct *opal_fdm_active;
868c2ecf20Sopenharmony_cistatic const struct opal_mpipl_fadump *opal_cpu_metadata;
878c2ecf20Sopenharmony_cistatic struct opal_fadump_mem_struct *opal_fdm;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci#ifdef CONFIG_OPAL_CORE
908c2ecf20Sopenharmony_ciextern bool kernel_initiated;
918c2ecf20Sopenharmony_ci#endif
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_cistatic int opal_fadump_unregister(struct fw_dump *fadump_conf);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void opal_fadump_update_config(struct fw_dump *fadump_conf,
968c2ecf20Sopenharmony_ci				      const struct opal_fadump_mem_struct *fdm)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	pr_debug("Boot memory regions count: %d\n", be16_to_cpu(fdm->region_cnt));
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/*
1018c2ecf20Sopenharmony_ci	 * The destination address of the first boot memory region is the
1028c2ecf20Sopenharmony_ci	 * destination address of boot memory regions.
1038c2ecf20Sopenharmony_ci	 */
1048c2ecf20Sopenharmony_ci	fadump_conf->boot_mem_dest_addr = be64_to_cpu(fdm->rgn[0].dest);
1058c2ecf20Sopenharmony_ci	pr_debug("Destination address of boot memory regions: %#016llx\n",
1068c2ecf20Sopenharmony_ci		 fadump_conf->boot_mem_dest_addr);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	fadump_conf->fadumphdr_addr = be64_to_cpu(fdm->fadumphdr_addr);
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * This function is called in the capture kernel to get configuration details
1138c2ecf20Sopenharmony_ci * from metadata setup by the first kernel.
1148c2ecf20Sopenharmony_ci */
1158c2ecf20Sopenharmony_cistatic void opal_fadump_get_config(struct fw_dump *fadump_conf,
1168c2ecf20Sopenharmony_ci				   const struct opal_fadump_mem_struct *fdm)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	unsigned long base, size, last_end, hole_size;
1198c2ecf20Sopenharmony_ci	int i;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (!fadump_conf->dump_active)
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	last_end = 0;
1258c2ecf20Sopenharmony_ci	hole_size = 0;
1268c2ecf20Sopenharmony_ci	fadump_conf->boot_memory_size = 0;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	pr_debug("Boot memory regions:\n");
1298c2ecf20Sopenharmony_ci	for (i = 0; i < be16_to_cpu(fdm->region_cnt); i++) {
1308c2ecf20Sopenharmony_ci		base = be64_to_cpu(fdm->rgn[i].src);
1318c2ecf20Sopenharmony_ci		size = be64_to_cpu(fdm->rgn[i].size);
1328c2ecf20Sopenharmony_ci		pr_debug("\t[%03d] base: 0x%lx, size: 0x%lx\n", i, base, size);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		fadump_conf->boot_mem_addr[i] = base;
1358c2ecf20Sopenharmony_ci		fadump_conf->boot_mem_sz[i] = size;
1368c2ecf20Sopenharmony_ci		fadump_conf->boot_memory_size += size;
1378c2ecf20Sopenharmony_ci		hole_size += (base - last_end);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci		last_end = base + size;
1408c2ecf20Sopenharmony_ci	}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	/*
1438c2ecf20Sopenharmony_ci	 * Start address of reserve dump area (permanent reservation) for
1448c2ecf20Sopenharmony_ci	 * re-registering FADump after dump capture.
1458c2ecf20Sopenharmony_ci	 */
1468c2ecf20Sopenharmony_ci	fadump_conf->reserve_dump_area_start = be64_to_cpu(fdm->rgn[0].dest);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	/*
1498c2ecf20Sopenharmony_ci	 * Rarely, but it can so happen that system crashes before all
1508c2ecf20Sopenharmony_ci	 * boot memory regions are registered for MPIPL. In such
1518c2ecf20Sopenharmony_ci	 * cases, warn that the vmcore may not be accurate and proceed
1528c2ecf20Sopenharmony_ci	 * anyway as that is the best bet considering free pages, cache
1538c2ecf20Sopenharmony_ci	 * pages, user pages, etc are usually filtered out.
1548c2ecf20Sopenharmony_ci	 *
1558c2ecf20Sopenharmony_ci	 * Hope the memory that could not be preserved only has pages
1568c2ecf20Sopenharmony_ci	 * that are usually filtered out while saving the vmcore.
1578c2ecf20Sopenharmony_ci	 */
1588c2ecf20Sopenharmony_ci	if (be16_to_cpu(fdm->region_cnt) > be16_to_cpu(fdm->registered_regions)) {
1598c2ecf20Sopenharmony_ci		pr_warn("Not all memory regions were saved!!!\n");
1608c2ecf20Sopenharmony_ci		pr_warn("  Unsaved memory regions:\n");
1618c2ecf20Sopenharmony_ci		i = be16_to_cpu(fdm->registered_regions);
1628c2ecf20Sopenharmony_ci		while (i < be16_to_cpu(fdm->region_cnt)) {
1638c2ecf20Sopenharmony_ci			pr_warn("\t[%03d] base: 0x%llx, size: 0x%llx\n",
1648c2ecf20Sopenharmony_ci				i, be64_to_cpu(fdm->rgn[i].src),
1658c2ecf20Sopenharmony_ci				be64_to_cpu(fdm->rgn[i].size));
1668c2ecf20Sopenharmony_ci			i++;
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci		pr_warn("If the unsaved regions only contain pages that are filtered out (eg. free/user pages), the vmcore should still be usable.\n");
1708c2ecf20Sopenharmony_ci		pr_warn("WARNING: If the unsaved regions contain kernel pages, the vmcore will be corrupted.\n");
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	fadump_conf->boot_mem_top = (fadump_conf->boot_memory_size + hole_size);
1748c2ecf20Sopenharmony_ci	fadump_conf->boot_mem_regs_cnt = be16_to_cpu(fdm->region_cnt);
1758c2ecf20Sopenharmony_ci	opal_fadump_update_config(fadump_conf, fdm);
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/* Initialize kernel metadata */
1798c2ecf20Sopenharmony_cistatic void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	fdm->version = OPAL_FADUMP_VERSION;
1828c2ecf20Sopenharmony_ci	fdm->region_cnt = cpu_to_be16(0);
1838c2ecf20Sopenharmony_ci	fdm->registered_regions = cpu_to_be16(0);
1848c2ecf20Sopenharmony_ci	fdm->fadumphdr_addr = cpu_to_be64(0);
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	u64 addr = fadump_conf->reserve_dump_area_start;
1908c2ecf20Sopenharmony_ci	u16 reg_cnt;
1918c2ecf20Sopenharmony_ci	int i;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	opal_fdm = __va(fadump_conf->kernel_metadata);
1948c2ecf20Sopenharmony_ci	opal_fadump_init_metadata(opal_fdm);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* Boot memory regions */
1978c2ecf20Sopenharmony_ci	reg_cnt = be16_to_cpu(opal_fdm->region_cnt);
1988c2ecf20Sopenharmony_ci	for (i = 0; i < fadump_conf->boot_mem_regs_cnt; i++) {
1998c2ecf20Sopenharmony_ci		opal_fdm->rgn[i].src	= cpu_to_be64(fadump_conf->boot_mem_addr[i]);
2008c2ecf20Sopenharmony_ci		opal_fdm->rgn[i].dest	= cpu_to_be64(addr);
2018c2ecf20Sopenharmony_ci		opal_fdm->rgn[i].size	= cpu_to_be64(fadump_conf->boot_mem_sz[i]);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		reg_cnt++;
2048c2ecf20Sopenharmony_ci		addr += fadump_conf->boot_mem_sz[i];
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci	opal_fdm->region_cnt = cpu_to_be16(reg_cnt);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * Kernel metadata is passed to f/w and retrieved in capture kerenl.
2108c2ecf20Sopenharmony_ci	 * So, use it to save fadump header address instead of calculating it.
2118c2ecf20Sopenharmony_ci	 */
2128c2ecf20Sopenharmony_ci	opal_fdm->fadumphdr_addr = cpu_to_be64(be64_to_cpu(opal_fdm->rgn[0].dest) +
2138c2ecf20Sopenharmony_ci					       fadump_conf->boot_memory_size);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	opal_fadump_update_config(fadump_conf, opal_fdm);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	return addr;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic u64 opal_fadump_get_metadata_size(void)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct));
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int opal_fadump_setup_metadata(struct fw_dump *fadump_conf)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int err = 0;
2288c2ecf20Sopenharmony_ci	s64 ret;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/*
2318c2ecf20Sopenharmony_ci	 * Use the last page(s) in FADump memory reservation for
2328c2ecf20Sopenharmony_ci	 * kernel metadata.
2338c2ecf20Sopenharmony_ci	 */
2348c2ecf20Sopenharmony_ci	fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start +
2358c2ecf20Sopenharmony_ci					fadump_conf->reserve_dump_area_size -
2368c2ecf20Sopenharmony_ci					opal_fadump_get_metadata_size());
2378c2ecf20Sopenharmony_ci	pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Initialize kernel metadata before registering the address with f/w */
2408c2ecf20Sopenharmony_ci	opal_fdm = __va(fadump_conf->kernel_metadata);
2418c2ecf20Sopenharmony_ci	opal_fadump_init_metadata(opal_fdm);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/*
2448c2ecf20Sopenharmony_ci	 * Register metadata address with f/w. Can be retrieved in
2458c2ecf20Sopenharmony_ci	 * the capture kernel.
2468c2ecf20Sopenharmony_ci	 */
2478c2ecf20Sopenharmony_ci	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL,
2488c2ecf20Sopenharmony_ci				      fadump_conf->kernel_metadata);
2498c2ecf20Sopenharmony_ci	if (ret != OPAL_SUCCESS) {
2508c2ecf20Sopenharmony_ci		pr_err("Failed to set kernel metadata tag!\n");
2518c2ecf20Sopenharmony_ci		err = -EPERM;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci	/*
2558c2ecf20Sopenharmony_ci	 * Register boot memory top address with f/w. Should be retrieved
2568c2ecf20Sopenharmony_ci	 * by a kernel that intends to preserve crash'ed kernel's memory.
2578c2ecf20Sopenharmony_ci	 */
2588c2ecf20Sopenharmony_ci	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_BOOT_MEM,
2598c2ecf20Sopenharmony_ci				      fadump_conf->boot_mem_top);
2608c2ecf20Sopenharmony_ci	if (ret != OPAL_SUCCESS) {
2618c2ecf20Sopenharmony_ci		pr_err("Failed to set boot memory tag!\n");
2628c2ecf20Sopenharmony_ci		err = -EPERM;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return err;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic u64 opal_fadump_get_bootmem_min(void)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	return OPAL_FADUMP_MIN_BOOT_MEM;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_cistatic int opal_fadump_register(struct fw_dump *fadump_conf)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	s64 rc = OPAL_PARAMETER;
2768c2ecf20Sopenharmony_ci	u16 registered_regs;
2778c2ecf20Sopenharmony_ci	int i, err = -EIO;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	registered_regs = be16_to_cpu(opal_fdm->registered_regions);
2808c2ecf20Sopenharmony_ci	for (i = 0; i < be16_to_cpu(opal_fdm->region_cnt); i++) {
2818c2ecf20Sopenharmony_ci		rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE,
2828c2ecf20Sopenharmony_ci				       be64_to_cpu(opal_fdm->rgn[i].src),
2838c2ecf20Sopenharmony_ci				       be64_to_cpu(opal_fdm->rgn[i].dest),
2848c2ecf20Sopenharmony_ci				       be64_to_cpu(opal_fdm->rgn[i].size));
2858c2ecf20Sopenharmony_ci		if (rc != OPAL_SUCCESS)
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		registered_regs++;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci	opal_fdm->registered_regions = cpu_to_be16(registered_regs);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	switch (rc) {
2938c2ecf20Sopenharmony_ci	case OPAL_SUCCESS:
2948c2ecf20Sopenharmony_ci		pr_info("Registration is successful!\n");
2958c2ecf20Sopenharmony_ci		fadump_conf->dump_registered = 1;
2968c2ecf20Sopenharmony_ci		err = 0;
2978c2ecf20Sopenharmony_ci		break;
2988c2ecf20Sopenharmony_ci	case OPAL_RESOURCE:
2998c2ecf20Sopenharmony_ci		/* If MAX regions limit in f/w is hit, warn and proceed. */
3008c2ecf20Sopenharmony_ci		pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n",
3018c2ecf20Sopenharmony_ci			(be16_to_cpu(opal_fdm->region_cnt) -
3028c2ecf20Sopenharmony_ci			 be16_to_cpu(opal_fdm->registered_regions)));
3038c2ecf20Sopenharmony_ci		fadump_conf->dump_registered = 1;
3048c2ecf20Sopenharmony_ci		err = 0;
3058c2ecf20Sopenharmony_ci		break;
3068c2ecf20Sopenharmony_ci	case OPAL_PARAMETER:
3078c2ecf20Sopenharmony_ci		pr_err("Failed to register. Parameter Error(%lld).\n", rc);
3088c2ecf20Sopenharmony_ci		break;
3098c2ecf20Sopenharmony_ci	case OPAL_HARDWARE:
3108c2ecf20Sopenharmony_ci		pr_err("Support not available.\n");
3118c2ecf20Sopenharmony_ci		fadump_conf->fadump_supported = 0;
3128c2ecf20Sopenharmony_ci		fadump_conf->fadump_enabled = 0;
3138c2ecf20Sopenharmony_ci		break;
3148c2ecf20Sopenharmony_ci	default:
3158c2ecf20Sopenharmony_ci		pr_err("Failed to register. Unknown Error(%lld).\n", rc);
3168c2ecf20Sopenharmony_ci		break;
3178c2ecf20Sopenharmony_ci	}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	/*
3208c2ecf20Sopenharmony_ci	 * If some regions were registered before OPAL_MPIPL_ADD_RANGE
3218c2ecf20Sopenharmony_ci	 * OPAL call failed, unregister all regions.
3228c2ecf20Sopenharmony_ci	 */
3238c2ecf20Sopenharmony_ci	if ((err < 0) && (be16_to_cpu(opal_fdm->registered_regions) > 0))
3248c2ecf20Sopenharmony_ci		opal_fadump_unregister(fadump_conf);
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return err;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int opal_fadump_unregister(struct fw_dump *fadump_conf)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	s64 rc;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	rc = opal_mpipl_update(OPAL_MPIPL_REMOVE_ALL, 0, 0, 0);
3348c2ecf20Sopenharmony_ci	if (rc) {
3358c2ecf20Sopenharmony_ci		pr_err("Failed to un-register - unexpected Error(%lld).\n", rc);
3368c2ecf20Sopenharmony_ci		return -EIO;
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	opal_fdm->registered_regions = cpu_to_be16(0);
3408c2ecf20Sopenharmony_ci	fadump_conf->dump_registered = 0;
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int opal_fadump_invalidate(struct fw_dump *fadump_conf)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	s64 rc;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	rc = opal_mpipl_update(OPAL_MPIPL_FREE_PRESERVED_MEMORY, 0, 0, 0);
3498c2ecf20Sopenharmony_ci	if (rc) {
3508c2ecf20Sopenharmony_ci		pr_err("Failed to invalidate - unexpected Error(%lld).\n", rc);
3518c2ecf20Sopenharmony_ci		return -EIO;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	fadump_conf->dump_active = 0;
3558c2ecf20Sopenharmony_ci	opal_fdm_active = NULL;
3568c2ecf20Sopenharmony_ci	return 0;
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic void opal_fadump_cleanup(struct fw_dump *fadump_conf)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	s64 ret;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0);
3648c2ecf20Sopenharmony_ci	if (ret != OPAL_SUCCESS)
3658c2ecf20Sopenharmony_ci		pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci/*
3698c2ecf20Sopenharmony_ci * Verify if CPU state data is available. If available, do a bit of sanity
3708c2ecf20Sopenharmony_ci * checking before processing this data.
3718c2ecf20Sopenharmony_ci */
3728c2ecf20Sopenharmony_cistatic bool __init is_opal_fadump_cpu_data_valid(struct fw_dump *fadump_conf)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	if (!opal_cpu_metadata)
3758c2ecf20Sopenharmony_ci		return false;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	fadump_conf->cpu_state_data_version =
3788c2ecf20Sopenharmony_ci		be32_to_cpu(opal_cpu_metadata->cpu_data_version);
3798c2ecf20Sopenharmony_ci	fadump_conf->cpu_state_entry_size =
3808c2ecf20Sopenharmony_ci		be32_to_cpu(opal_cpu_metadata->cpu_data_size);
3818c2ecf20Sopenharmony_ci	fadump_conf->cpu_state_dest_vaddr =
3828c2ecf20Sopenharmony_ci		(u64)__va(be64_to_cpu(opal_cpu_metadata->region[0].dest));
3838c2ecf20Sopenharmony_ci	fadump_conf->cpu_state_data_size =
3848c2ecf20Sopenharmony_ci		be64_to_cpu(opal_cpu_metadata->region[0].size);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (fadump_conf->cpu_state_data_version != HDAT_FADUMP_CPU_DATA_VER) {
3878c2ecf20Sopenharmony_ci		pr_warn("Supported CPU state data version: %u, found: %d!\n",
3888c2ecf20Sopenharmony_ci			HDAT_FADUMP_CPU_DATA_VER,
3898c2ecf20Sopenharmony_ci			fadump_conf->cpu_state_data_version);
3908c2ecf20Sopenharmony_ci		pr_warn("WARNING: F/W using newer CPU state data format!!\n");
3918c2ecf20Sopenharmony_ci	}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	if ((fadump_conf->cpu_state_dest_vaddr == 0) ||
3948c2ecf20Sopenharmony_ci	    (fadump_conf->cpu_state_entry_size == 0) ||
3958c2ecf20Sopenharmony_ci	    (fadump_conf->cpu_state_entry_size >
3968c2ecf20Sopenharmony_ci	     fadump_conf->cpu_state_data_size)) {
3978c2ecf20Sopenharmony_ci		pr_err("CPU state data is invalid. Ignoring!\n");
3988c2ecf20Sopenharmony_ci		return false;
3998c2ecf20Sopenharmony_ci	}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return true;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/*
4058c2ecf20Sopenharmony_ci * Convert CPU state data saved at the time of crash into ELF notes.
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci * While the crashing CPU's register data is saved by the kernel, CPU state
4088c2ecf20Sopenharmony_ci * data for all CPUs is saved by f/w. In CPU state data provided by f/w,
4098c2ecf20Sopenharmony_ci * each register entry is of 16 bytes, a numerical identifier along with
4108c2ecf20Sopenharmony_ci * a GPR/SPR flag in the first 8 bytes and the register value in the next
4118c2ecf20Sopenharmony_ci * 8 bytes. For more details refer to F/W documentation. If this data is
4128c2ecf20Sopenharmony_ci * missing or in unsupported format, append crashing CPU's register data
4138c2ecf20Sopenharmony_ci * saved by the kernel in the PT_NOTE, to have something to work with in
4148c2ecf20Sopenharmony_ci * the vmcore file.
4158c2ecf20Sopenharmony_ci */
4168c2ecf20Sopenharmony_cistatic int __init
4178c2ecf20Sopenharmony_ciopal_fadump_build_cpu_notes(struct fw_dump *fadump_conf,
4188c2ecf20Sopenharmony_ci			    struct fadump_crash_info_header *fdh)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	u32 thread_pir, size_per_thread, regs_offset, regs_cnt, reg_esize;
4218c2ecf20Sopenharmony_ci	struct hdat_fadump_thread_hdr *thdr;
4228c2ecf20Sopenharmony_ci	bool is_cpu_data_valid = false;
4238c2ecf20Sopenharmony_ci	u32 num_cpus = 1, *note_buf;
4248c2ecf20Sopenharmony_ci	struct pt_regs regs;
4258c2ecf20Sopenharmony_ci	char *bufp;
4268c2ecf20Sopenharmony_ci	int rc, i;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	if (is_opal_fadump_cpu_data_valid(fadump_conf)) {
4298c2ecf20Sopenharmony_ci		size_per_thread = fadump_conf->cpu_state_entry_size;
4308c2ecf20Sopenharmony_ci		num_cpus = (fadump_conf->cpu_state_data_size / size_per_thread);
4318c2ecf20Sopenharmony_ci		bufp = __va(fadump_conf->cpu_state_dest_vaddr);
4328c2ecf20Sopenharmony_ci		is_cpu_data_valid = true;
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	rc = fadump_setup_cpu_notes_buf(num_cpus);
4368c2ecf20Sopenharmony_ci	if (rc != 0)
4378c2ecf20Sopenharmony_ci		return rc;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr;
4408c2ecf20Sopenharmony_ci	if (!is_cpu_data_valid)
4418c2ecf20Sopenharmony_ci		goto out;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	/*
4448c2ecf20Sopenharmony_ci	 * Offset for register entries, entry size and registers count is
4458c2ecf20Sopenharmony_ci	 * duplicated in every thread header in keeping with HDAT format.
4468c2ecf20Sopenharmony_ci	 * Use these values from the first thread header.
4478c2ecf20Sopenharmony_ci	 */
4488c2ecf20Sopenharmony_ci	thdr = (struct hdat_fadump_thread_hdr *)bufp;
4498c2ecf20Sopenharmony_ci	regs_offset = (offsetof(struct hdat_fadump_thread_hdr, offset) +
4508c2ecf20Sopenharmony_ci		       be32_to_cpu(thdr->offset));
4518c2ecf20Sopenharmony_ci	reg_esize = be32_to_cpu(thdr->esize);
4528c2ecf20Sopenharmony_ci	regs_cnt  = be32_to_cpu(thdr->ecnt);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	pr_debug("--------CPU State Data------------\n");
4558c2ecf20Sopenharmony_ci	pr_debug("NumCpus     : %u\n", num_cpus);
4568c2ecf20Sopenharmony_ci	pr_debug("\tOffset: %u, Entry size: %u, Cnt: %u\n",
4578c2ecf20Sopenharmony_ci		 regs_offset, reg_esize, regs_cnt);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	for (i = 0; i < num_cpus; i++, bufp += size_per_thread) {
4608c2ecf20Sopenharmony_ci		thdr = (struct hdat_fadump_thread_hdr *)bufp;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci		thread_pir = be32_to_cpu(thdr->pir);
4638c2ecf20Sopenharmony_ci		pr_debug("[%04d] PIR: 0x%x, core state: 0x%02x\n",
4648c2ecf20Sopenharmony_ci			 i, thread_pir, thdr->core_state);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci		/*
4678c2ecf20Sopenharmony_ci		 * If this is kernel initiated crash, crashing_cpu would be set
4688c2ecf20Sopenharmony_ci		 * appropriately and register data of the crashing CPU saved by
4698c2ecf20Sopenharmony_ci		 * crashing kernel. Add this saved register data of crashing CPU
4708c2ecf20Sopenharmony_ci		 * to elf notes and populate the pt_regs for the remaining CPUs
4718c2ecf20Sopenharmony_ci		 * from register state data provided by firmware.
4728c2ecf20Sopenharmony_ci		 */
4738c2ecf20Sopenharmony_ci		if (fdh->crashing_cpu == thread_pir) {
4748c2ecf20Sopenharmony_ci			note_buf = fadump_regs_to_elf_notes(note_buf,
4758c2ecf20Sopenharmony_ci							    &fdh->regs);
4768c2ecf20Sopenharmony_ci			pr_debug("Crashing CPU PIR: 0x%x - R1 : 0x%lx, NIP : 0x%lx\n",
4778c2ecf20Sopenharmony_ci				 fdh->crashing_cpu, fdh->regs.gpr[1],
4788c2ecf20Sopenharmony_ci				 fdh->regs.nip);
4798c2ecf20Sopenharmony_ci			continue;
4808c2ecf20Sopenharmony_ci		}
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci		/*
4838c2ecf20Sopenharmony_ci		 * Register state data of MAX cores is provided by firmware,
4848c2ecf20Sopenharmony_ci		 * but some of this cores may not be active. So, while
4858c2ecf20Sopenharmony_ci		 * processing register state data, check core state and
4868c2ecf20Sopenharmony_ci		 * skip threads that belong to inactive cores.
4878c2ecf20Sopenharmony_ci		 */
4888c2ecf20Sopenharmony_ci		if (thdr->core_state == HDAT_FADUMP_CORE_INACTIVE)
4898c2ecf20Sopenharmony_ci			continue;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci		opal_fadump_read_regs((bufp + regs_offset), regs_cnt,
4928c2ecf20Sopenharmony_ci				      reg_esize, true, &regs);
4938c2ecf20Sopenharmony_ci		note_buf = fadump_regs_to_elf_notes(note_buf, &regs);
4948c2ecf20Sopenharmony_ci		pr_debug("CPU PIR: 0x%x - R1 : 0x%lx, NIP : 0x%lx\n",
4958c2ecf20Sopenharmony_ci			 thread_pir, regs.gpr[1], regs.nip);
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ciout:
4998c2ecf20Sopenharmony_ci	/*
5008c2ecf20Sopenharmony_ci	 * CPU state data is invalid/unsupported. Try appending crashing CPU's
5018c2ecf20Sopenharmony_ci	 * register data, if it is saved by the kernel.
5028c2ecf20Sopenharmony_ci	 */
5038c2ecf20Sopenharmony_ci	if (fadump_conf->cpu_notes_buf_vaddr == (u64)note_buf) {
5048c2ecf20Sopenharmony_ci		if (fdh->crashing_cpu == FADUMP_CPU_UNKNOWN) {
5058c2ecf20Sopenharmony_ci			fadump_free_cpu_notes_buf();
5068c2ecf20Sopenharmony_ci			return -ENODEV;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		pr_warn("WARNING: appending only crashing CPU's register data\n");
5108c2ecf20Sopenharmony_ci		note_buf = fadump_regs_to_elf_notes(note_buf, &(fdh->regs));
5118c2ecf20Sopenharmony_ci	}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	final_note(note_buf);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	pr_debug("Updating elfcore header (%llx) with cpu notes\n",
5168c2ecf20Sopenharmony_ci		 fdh->elfcorehdr_addr);
5178c2ecf20Sopenharmony_ci	fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr));
5188c2ecf20Sopenharmony_ci	return 0;
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_cistatic int __init opal_fadump_process(struct fw_dump *fadump_conf)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct fadump_crash_info_header *fdh;
5248c2ecf20Sopenharmony_ci	int rc = -EINVAL;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	if (!opal_fdm_active || !fadump_conf->fadumphdr_addr)
5278c2ecf20Sopenharmony_ci		return rc;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* Validate the fadump crash info header */
5308c2ecf20Sopenharmony_ci	fdh = __va(fadump_conf->fadumphdr_addr);
5318c2ecf20Sopenharmony_ci	if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) {
5328c2ecf20Sopenharmony_ci		pr_err("Crash info header is not valid.\n");
5338c2ecf20Sopenharmony_ci		return rc;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci#ifdef CONFIG_OPAL_CORE
5378c2ecf20Sopenharmony_ci	/*
5388c2ecf20Sopenharmony_ci	 * If this is a kernel initiated crash, crashing_cpu would be set
5398c2ecf20Sopenharmony_ci	 * appropriately and register data of the crashing CPU saved by
5408c2ecf20Sopenharmony_ci	 * crashing kernel. Add this saved register data of crashing CPU
5418c2ecf20Sopenharmony_ci	 * to elf notes and populate the pt_regs for the remaining CPUs
5428c2ecf20Sopenharmony_ci	 * from register state data provided by firmware.
5438c2ecf20Sopenharmony_ci	 */
5448c2ecf20Sopenharmony_ci	if (fdh->crashing_cpu != FADUMP_CPU_UNKNOWN)
5458c2ecf20Sopenharmony_ci		kernel_initiated = true;
5468c2ecf20Sopenharmony_ci#endif
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	rc = opal_fadump_build_cpu_notes(fadump_conf, fdh);
5498c2ecf20Sopenharmony_ci	if (rc)
5508c2ecf20Sopenharmony_ci		return rc;
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/*
5538c2ecf20Sopenharmony_ci	 * We are done validating dump info and elfcore header is now ready
5548c2ecf20Sopenharmony_ci	 * to be exported. set elfcorehdr_addr so that vmcore module will
5558c2ecf20Sopenharmony_ci	 * export the elfcore header through '/proc/vmcore'.
5568c2ecf20Sopenharmony_ci	 */
5578c2ecf20Sopenharmony_ci	elfcorehdr_addr = fdh->elfcorehdr_addr;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return rc;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic void opal_fadump_region_show(struct fw_dump *fadump_conf,
5638c2ecf20Sopenharmony_ci				    struct seq_file *m)
5648c2ecf20Sopenharmony_ci{
5658c2ecf20Sopenharmony_ci	const struct opal_fadump_mem_struct *fdm_ptr;
5668c2ecf20Sopenharmony_ci	u64 dumped_bytes = 0;
5678c2ecf20Sopenharmony_ci	int i;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	if (fadump_conf->dump_active)
5708c2ecf20Sopenharmony_ci		fdm_ptr = opal_fdm_active;
5718c2ecf20Sopenharmony_ci	else
5728c2ecf20Sopenharmony_ci		fdm_ptr = opal_fdm;
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	for (i = 0; i < be16_to_cpu(fdm_ptr->region_cnt); i++) {
5758c2ecf20Sopenharmony_ci		/*
5768c2ecf20Sopenharmony_ci		 * Only regions that are registered for MPIPL
5778c2ecf20Sopenharmony_ci		 * would have dump data.
5788c2ecf20Sopenharmony_ci		 */
5798c2ecf20Sopenharmony_ci		if ((fadump_conf->dump_active) &&
5808c2ecf20Sopenharmony_ci		    (i < be16_to_cpu(fdm_ptr->registered_regions)))
5818c2ecf20Sopenharmony_ci			dumped_bytes = be64_to_cpu(fdm_ptr->rgn[i].size);
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ci		seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ",
5848c2ecf20Sopenharmony_ci			   be64_to_cpu(fdm_ptr->rgn[i].src),
5858c2ecf20Sopenharmony_ci			   be64_to_cpu(fdm_ptr->rgn[i].dest));
5868c2ecf20Sopenharmony_ci		seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n",
5878c2ecf20Sopenharmony_ci			   be64_to_cpu(fdm_ptr->rgn[i].size), dumped_bytes);
5888c2ecf20Sopenharmony_ci	}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	/* Dump is active. Show reserved area start address. */
5918c2ecf20Sopenharmony_ci	if (fadump_conf->dump_active) {
5928c2ecf20Sopenharmony_ci		seq_printf(m, "\nMemory above %#016lx is reserved for saving crash dump\n",
5938c2ecf20Sopenharmony_ci			   fadump_conf->reserve_dump_area_start);
5948c2ecf20Sopenharmony_ci	}
5958c2ecf20Sopenharmony_ci}
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cistatic void opal_fadump_trigger(struct fadump_crash_info_header *fdh,
5988c2ecf20Sopenharmony_ci				const char *msg)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci	int rc;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	/*
6038c2ecf20Sopenharmony_ci	 * Unlike on pSeries platform, logical CPU number is not provided
6048c2ecf20Sopenharmony_ci	 * with architected register state data. So, store the crashing
6058c2ecf20Sopenharmony_ci	 * CPU's PIR instead to plug the appropriate register data for
6068c2ecf20Sopenharmony_ci	 * crashing CPU in the vmcore file.
6078c2ecf20Sopenharmony_ci	 */
6088c2ecf20Sopenharmony_ci	fdh->crashing_cpu = (u32)mfspr(SPRN_PIR);
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg);
6118c2ecf20Sopenharmony_ci	if (rc == OPAL_UNSUPPORTED) {
6128c2ecf20Sopenharmony_ci		pr_emerg("Reboot type %d not supported.\n",
6138c2ecf20Sopenharmony_ci			 OPAL_REBOOT_MPIPL);
6148c2ecf20Sopenharmony_ci	} else if (rc == OPAL_HARDWARE)
6158c2ecf20Sopenharmony_ci		pr_emerg("No backend support for MPIPL!\n");
6168c2ecf20Sopenharmony_ci}
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_cistatic struct fadump_ops opal_fadump_ops = {
6198c2ecf20Sopenharmony_ci	.fadump_init_mem_struct		= opal_fadump_init_mem_struct,
6208c2ecf20Sopenharmony_ci	.fadump_get_metadata_size	= opal_fadump_get_metadata_size,
6218c2ecf20Sopenharmony_ci	.fadump_setup_metadata		= opal_fadump_setup_metadata,
6228c2ecf20Sopenharmony_ci	.fadump_get_bootmem_min		= opal_fadump_get_bootmem_min,
6238c2ecf20Sopenharmony_ci	.fadump_register		= opal_fadump_register,
6248c2ecf20Sopenharmony_ci	.fadump_unregister		= opal_fadump_unregister,
6258c2ecf20Sopenharmony_ci	.fadump_invalidate		= opal_fadump_invalidate,
6268c2ecf20Sopenharmony_ci	.fadump_cleanup			= opal_fadump_cleanup,
6278c2ecf20Sopenharmony_ci	.fadump_process			= opal_fadump_process,
6288c2ecf20Sopenharmony_ci	.fadump_region_show		= opal_fadump_region_show,
6298c2ecf20Sopenharmony_ci	.fadump_trigger			= opal_fadump_trigger,
6308c2ecf20Sopenharmony_ci};
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_civoid __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	const __be32 *prop;
6358c2ecf20Sopenharmony_ci	unsigned long dn;
6368c2ecf20Sopenharmony_ci	__be64 be_addr;
6378c2ecf20Sopenharmony_ci	u64 addr = 0;
6388c2ecf20Sopenharmony_ci	int i, len;
6398c2ecf20Sopenharmony_ci	s64 ret;
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci	/*
6428c2ecf20Sopenharmony_ci	 * Check if Firmware-Assisted Dump is supported. if yes, check
6438c2ecf20Sopenharmony_ci	 * if dump has been initiated on last reboot.
6448c2ecf20Sopenharmony_ci	 */
6458c2ecf20Sopenharmony_ci	dn = of_get_flat_dt_subnode_by_name(node, "dump");
6468c2ecf20Sopenharmony_ci	if (dn == -FDT_ERR_NOTFOUND) {
6478c2ecf20Sopenharmony_ci		pr_debug("FADump support is missing!\n");
6488c2ecf20Sopenharmony_ci		return;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) {
6528c2ecf20Sopenharmony_ci		pr_err("Support missing for this f/w version!\n");
6538c2ecf20Sopenharmony_ci		return;
6548c2ecf20Sopenharmony_ci	}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	prop = of_get_flat_dt_prop(dn, "fw-load-area", &len);
6578c2ecf20Sopenharmony_ci	if (prop) {
6588c2ecf20Sopenharmony_ci		/*
6598c2ecf20Sopenharmony_ci		 * Each f/w load area is an (address,size) pair,
6608c2ecf20Sopenharmony_ci		 * 2 cells each, totalling 4 cells per range.
6618c2ecf20Sopenharmony_ci		 */
6628c2ecf20Sopenharmony_ci		for (i = 0; i < len / (sizeof(*prop) * 4); i++) {
6638c2ecf20Sopenharmony_ci			u64 base, end;
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci			base = of_read_number(prop + (i * 4) + 0, 2);
6668c2ecf20Sopenharmony_ci			end = base;
6678c2ecf20Sopenharmony_ci			end += of_read_number(prop + (i * 4) + 2, 2);
6688c2ecf20Sopenharmony_ci			if (end > OPAL_FADUMP_MIN_BOOT_MEM) {
6698c2ecf20Sopenharmony_ci				pr_err("F/W load area: 0x%llx-0x%llx\n",
6708c2ecf20Sopenharmony_ci				       base, end);
6718c2ecf20Sopenharmony_ci				pr_err("F/W version not supported!\n");
6728c2ecf20Sopenharmony_ci				return;
6738c2ecf20Sopenharmony_ci			}
6748c2ecf20Sopenharmony_ci		}
6758c2ecf20Sopenharmony_ci	}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	fadump_conf->ops		= &opal_fadump_ops;
6788c2ecf20Sopenharmony_ci	fadump_conf->fadump_supported	= 1;
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	/*
6818c2ecf20Sopenharmony_ci	 * Firmware supports 32-bit field for size. Align it to PAGE_SIZE
6828c2ecf20Sopenharmony_ci	 * and request firmware to copy multiple kernel boot memory regions.
6838c2ecf20Sopenharmony_ci	 */
6848c2ecf20Sopenharmony_ci	fadump_conf->max_copy_size = ALIGN_DOWN(U32_MAX, PAGE_SIZE);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/*
6878c2ecf20Sopenharmony_ci	 * Check if dump has been initiated on last reboot.
6888c2ecf20Sopenharmony_ci	 */
6898c2ecf20Sopenharmony_ci	prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL);
6908c2ecf20Sopenharmony_ci	if (!prop)
6918c2ecf20Sopenharmony_ci		return;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &be_addr);
6948c2ecf20Sopenharmony_ci	if ((ret != OPAL_SUCCESS) || !be_addr) {
6958c2ecf20Sopenharmony_ci		pr_err("Failed to get Kernel metadata (%lld)\n", ret);
6968c2ecf20Sopenharmony_ci		return;
6978c2ecf20Sopenharmony_ci	}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	addr = be64_to_cpu(be_addr);
7008c2ecf20Sopenharmony_ci	pr_debug("Kernel metadata addr: %llx\n", addr);
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	opal_fdm_active = __va(addr);
7038c2ecf20Sopenharmony_ci	if (opal_fdm_active->version != OPAL_FADUMP_VERSION) {
7048c2ecf20Sopenharmony_ci		pr_warn("Supported kernel metadata version: %u, found: %d!\n",
7058c2ecf20Sopenharmony_ci			OPAL_FADUMP_VERSION, opal_fdm_active->version);
7068c2ecf20Sopenharmony_ci		pr_warn("WARNING: Kernel metadata format mismatch identified! Core file maybe corrupted..\n");
7078c2ecf20Sopenharmony_ci	}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	/* Kernel regions not registered with f/w for MPIPL */
7108c2ecf20Sopenharmony_ci	if (be16_to_cpu(opal_fdm_active->registered_regions) == 0) {
7118c2ecf20Sopenharmony_ci		opal_fdm_active = NULL;
7128c2ecf20Sopenharmony_ci		return;
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &be_addr);
7168c2ecf20Sopenharmony_ci	if (be_addr) {
7178c2ecf20Sopenharmony_ci		addr = be64_to_cpu(be_addr);
7188c2ecf20Sopenharmony_ci		pr_debug("CPU metadata addr: %llx\n", addr);
7198c2ecf20Sopenharmony_ci		opal_cpu_metadata = __va(addr);
7208c2ecf20Sopenharmony_ci	}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci	pr_info("Firmware-assisted dump is active.\n");
7238c2ecf20Sopenharmony_ci	fadump_conf->dump_active = 1;
7248c2ecf20Sopenharmony_ci	opal_fadump_get_config(fadump_conf, opal_fdm_active);
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci#endif /* !CONFIG_PRESERVE_FA_DUMP */
727