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, ®s); 4938c2ecf20Sopenharmony_ci note_buf = fadump_regs_to_elf_notes(note_buf, ®s); 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