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 232a1b06ddSHari Bathini static const struct opal_fadump_mem_struct *opal_fdm_active; 24742a265aSHari Bathini static struct opal_fadump_mem_struct *opal_fdm; 25742a265aSHari Bathini 26a20a8fa4SHari Bathini static int opal_fadump_unregister(struct fw_dump *fadump_conf); 27a20a8fa4SHari Bathini 28a20a8fa4SHari Bathini static void opal_fadump_update_config(struct fw_dump *fadump_conf, 29a20a8fa4SHari Bathini const struct opal_fadump_mem_struct *fdm) 30a20a8fa4SHari Bathini { 3151bba8edSHari Bathini pr_debug("Boot memory regions count: %d\n", fdm->region_cnt); 3251bba8edSHari Bathini 33a20a8fa4SHari Bathini /* 34a20a8fa4SHari Bathini * The destination address of the first boot memory region is the 35a20a8fa4SHari Bathini * destination address of boot memory regions. 36a20a8fa4SHari Bathini */ 37a20a8fa4SHari Bathini fadump_conf->boot_mem_dest_addr = fdm->rgn[0].dest; 38a20a8fa4SHari Bathini pr_debug("Destination address of boot memory regions: %#016llx\n", 39a20a8fa4SHari Bathini fadump_conf->boot_mem_dest_addr); 40a20a8fa4SHari Bathini 41a20a8fa4SHari Bathini fadump_conf->fadumphdr_addr = fdm->fadumphdr_addr; 42a20a8fa4SHari Bathini } 43a20a8fa4SHari Bathini 442a1b06ddSHari Bathini /* 452a1b06ddSHari Bathini * This function is called in the capture kernel to get configuration details 462a1b06ddSHari Bathini * from metadata setup by the first kernel. 472a1b06ddSHari Bathini */ 482a1b06ddSHari Bathini static void opal_fadump_get_config(struct fw_dump *fadump_conf, 492a1b06ddSHari Bathini const struct opal_fadump_mem_struct *fdm) 502a1b06ddSHari Bathini { 512a1b06ddSHari Bathini int i; 522a1b06ddSHari Bathini 532a1b06ddSHari Bathini if (!fadump_conf->dump_active) 542a1b06ddSHari Bathini return; 552a1b06ddSHari Bathini 562a1b06ddSHari Bathini fadump_conf->boot_memory_size = 0; 572a1b06ddSHari Bathini 582a1b06ddSHari Bathini pr_debug("Boot memory regions:\n"); 592a1b06ddSHari Bathini for (i = 0; i < fdm->region_cnt; i++) { 602a1b06ddSHari Bathini pr_debug("\t%d. base: 0x%llx, size: 0x%llx\n", 612a1b06ddSHari Bathini (i + 1), fdm->rgn[i].src, fdm->rgn[i].size); 622a1b06ddSHari Bathini 632a1b06ddSHari Bathini fadump_conf->boot_memory_size += fdm->rgn[i].size; 642a1b06ddSHari Bathini } 652a1b06ddSHari Bathini 662a1b06ddSHari Bathini /* 672a1b06ddSHari Bathini * Start address of reserve dump area (permanent reservation) for 682a1b06ddSHari Bathini * re-registering FADump after dump capture. 692a1b06ddSHari Bathini */ 702a1b06ddSHari Bathini fadump_conf->reserve_dump_area_start = fdm->rgn[0].dest; 712a1b06ddSHari Bathini 726071e8f9SHari Bathini /* 736071e8f9SHari Bathini * Rarely, but it can so happen that system crashes before all 746071e8f9SHari Bathini * boot memory regions are registered for MPIPL. In such 756071e8f9SHari Bathini * cases, warn that the vmcore may not be accurate and proceed 766071e8f9SHari Bathini * anyway as that is the best bet considering free pages, cache 776071e8f9SHari Bathini * pages, user pages, etc are usually filtered out. 786071e8f9SHari Bathini * 796071e8f9SHari Bathini * Hope the memory that could not be preserved only has pages 806071e8f9SHari Bathini * that are usually filtered out while saving the vmcore. 816071e8f9SHari Bathini */ 826071e8f9SHari Bathini if (fdm->region_cnt > fdm->registered_regions) { 836071e8f9SHari Bathini pr_warn("Not all memory regions were saved!!!\n"); 846071e8f9SHari Bathini pr_warn(" Unsaved memory regions:\n"); 856071e8f9SHari Bathini i = fdm->registered_regions; 866071e8f9SHari Bathini while (i < fdm->region_cnt) { 876071e8f9SHari Bathini pr_warn("\t[%03d] base: 0x%llx, size: 0x%llx\n", 886071e8f9SHari Bathini i, fdm->rgn[i].src, fdm->rgn[i].size); 896071e8f9SHari Bathini i++; 906071e8f9SHari Bathini } 916071e8f9SHari Bathini 926071e8f9SHari 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"); 936071e8f9SHari Bathini pr_warn("WARNING: If the unsaved regions contain kernel pages, the vmcore will be corrupted.\n"); 946071e8f9SHari Bathini } 956071e8f9SHari Bathini 962a1b06ddSHari Bathini opal_fadump_update_config(fadump_conf, fdm); 972a1b06ddSHari Bathini } 982a1b06ddSHari Bathini 99742a265aSHari Bathini /* Initialize kernel metadata */ 100742a265aSHari Bathini static void opal_fadump_init_metadata(struct opal_fadump_mem_struct *fdm) 101742a265aSHari Bathini { 102742a265aSHari Bathini fdm->version = OPAL_FADUMP_VERSION; 103742a265aSHari Bathini fdm->region_cnt = 0; 104742a265aSHari Bathini fdm->registered_regions = 0; 105742a265aSHari Bathini fdm->fadumphdr_addr = 0; 106742a265aSHari Bathini } 107742a265aSHari Bathini 10841df5928SHari Bathini static u64 opal_fadump_init_mem_struct(struct fw_dump *fadump_conf) 10941df5928SHari Bathini { 11051bba8edSHari Bathini int max_copy_size, cur_size, size; 11151bba8edSHari Bathini u64 src_addr, dest_addr; 112742a265aSHari Bathini 113742a265aSHari Bathini opal_fdm = __va(fadump_conf->kernel_metadata); 114742a265aSHari Bathini opal_fadump_init_metadata(opal_fdm); 115742a265aSHari Bathini 11651bba8edSHari Bathini /* 11751bba8edSHari Bathini * Firmware supports 32-bit field for size. Align it to PAGE_SIZE 11851bba8edSHari Bathini * and request firmware to copy multiple kernel boot memory regions. 11951bba8edSHari Bathini */ 12051bba8edSHari Bathini max_copy_size = _ALIGN_DOWN(U32_MAX, PAGE_SIZE); 12151bba8edSHari Bathini 12251bba8edSHari Bathini /* Boot memory regions */ 12351bba8edSHari Bathini src_addr = 0; 12451bba8edSHari Bathini dest_addr = fadump_conf->reserve_dump_area_start; 12551bba8edSHari Bathini size = fadump_conf->boot_memory_size; 12651bba8edSHari Bathini while (size) { 12751bba8edSHari Bathini cur_size = size > max_copy_size ? max_copy_size : size; 12851bba8edSHari Bathini 12951bba8edSHari Bathini opal_fdm->rgn[opal_fdm->region_cnt].src = src_addr; 13051bba8edSHari Bathini opal_fdm->rgn[opal_fdm->region_cnt].dest = dest_addr; 13151bba8edSHari Bathini opal_fdm->rgn[opal_fdm->region_cnt].size = cur_size; 13251bba8edSHari Bathini 13351bba8edSHari Bathini opal_fdm->region_cnt++; 13451bba8edSHari Bathini dest_addr += cur_size; 13551bba8edSHari Bathini src_addr += cur_size; 13651bba8edSHari Bathini size -= cur_size; 13751bba8edSHari Bathini } 138742a265aSHari Bathini 139742a265aSHari Bathini /* 140742a265aSHari Bathini * Kernel metadata is passed to f/w and retrieved in capture kerenl. 141742a265aSHari Bathini * So, use it to save fadump header address instead of calculating it. 142742a265aSHari Bathini */ 143742a265aSHari Bathini opal_fdm->fadumphdr_addr = (opal_fdm->rgn[0].dest + 144742a265aSHari Bathini fadump_conf->boot_memory_size); 145742a265aSHari Bathini 146a20a8fa4SHari Bathini opal_fadump_update_config(fadump_conf, opal_fdm); 147a20a8fa4SHari Bathini 14851bba8edSHari Bathini return dest_addr; 149742a265aSHari Bathini } 150742a265aSHari Bathini 151742a265aSHari Bathini static u64 opal_fadump_get_metadata_size(void) 152742a265aSHari Bathini { 153742a265aSHari Bathini return PAGE_ALIGN(sizeof(struct opal_fadump_mem_struct)); 154742a265aSHari Bathini } 155742a265aSHari Bathini 156742a265aSHari Bathini static int opal_fadump_setup_metadata(struct fw_dump *fadump_conf) 157742a265aSHari Bathini { 158742a265aSHari Bathini int err = 0; 159742a265aSHari Bathini s64 ret; 160742a265aSHari Bathini 161742a265aSHari Bathini /* 162742a265aSHari Bathini * Use the last page(s) in FADump memory reservation for 163742a265aSHari Bathini * kernel metadata. 164742a265aSHari Bathini */ 165742a265aSHari Bathini fadump_conf->kernel_metadata = (fadump_conf->reserve_dump_area_start + 166742a265aSHari Bathini fadump_conf->reserve_dump_area_size - 167742a265aSHari Bathini opal_fadump_get_metadata_size()); 168742a265aSHari Bathini pr_info("Kernel metadata addr: %llx\n", fadump_conf->kernel_metadata); 169742a265aSHari Bathini 170742a265aSHari Bathini /* Initialize kernel metadata before registering the address with f/w */ 171742a265aSHari Bathini opal_fdm = __va(fadump_conf->kernel_metadata); 172742a265aSHari Bathini opal_fadump_init_metadata(opal_fdm); 173742a265aSHari Bathini 174742a265aSHari Bathini /* 175742a265aSHari Bathini * Register metadata address with f/w. Can be retrieved in 176742a265aSHari Bathini * the capture kernel. 177742a265aSHari Bathini */ 178742a265aSHari Bathini ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 179742a265aSHari Bathini fadump_conf->kernel_metadata); 180742a265aSHari Bathini if (ret != OPAL_SUCCESS) { 181742a265aSHari Bathini pr_err("Failed to set kernel metadata tag!\n"); 182742a265aSHari Bathini err = -EPERM; 183742a265aSHari Bathini } 184742a265aSHari Bathini 185742a265aSHari Bathini return err; 18641df5928SHari Bathini } 18741df5928SHari Bathini 18841df5928SHari Bathini static int opal_fadump_register(struct fw_dump *fadump_conf) 18941df5928SHari Bathini { 190a20a8fa4SHari Bathini s64 rc = OPAL_PARAMETER; 191a20a8fa4SHari Bathini int i, err = -EIO; 192a20a8fa4SHari Bathini 193a20a8fa4SHari Bathini for (i = 0; i < opal_fdm->region_cnt; i++) { 194a20a8fa4SHari Bathini rc = opal_mpipl_update(OPAL_MPIPL_ADD_RANGE, 195a20a8fa4SHari Bathini opal_fdm->rgn[i].src, 196a20a8fa4SHari Bathini opal_fdm->rgn[i].dest, 197a20a8fa4SHari Bathini opal_fdm->rgn[i].size); 198a20a8fa4SHari Bathini if (rc != OPAL_SUCCESS) 199a20a8fa4SHari Bathini break; 200a20a8fa4SHari Bathini 201a20a8fa4SHari Bathini opal_fdm->registered_regions++; 202a20a8fa4SHari Bathini } 203a20a8fa4SHari Bathini 204a20a8fa4SHari Bathini switch (rc) { 205a20a8fa4SHari Bathini case OPAL_SUCCESS: 206a20a8fa4SHari Bathini pr_info("Registration is successful!\n"); 207a20a8fa4SHari Bathini fadump_conf->dump_registered = 1; 208a20a8fa4SHari Bathini err = 0; 209a20a8fa4SHari Bathini break; 210a20a8fa4SHari Bathini case OPAL_RESOURCE: 211a20a8fa4SHari Bathini /* If MAX regions limit in f/w is hit, warn and proceed. */ 212a20a8fa4SHari Bathini pr_warn("%d regions could not be registered for MPIPL as MAX limit is reached!\n", 213a20a8fa4SHari Bathini (opal_fdm->region_cnt - opal_fdm->registered_regions)); 214a20a8fa4SHari Bathini fadump_conf->dump_registered = 1; 215a20a8fa4SHari Bathini err = 0; 216a20a8fa4SHari Bathini break; 217a20a8fa4SHari Bathini case OPAL_PARAMETER: 218a20a8fa4SHari Bathini pr_err("Failed to register. Parameter Error(%lld).\n", rc); 219a20a8fa4SHari Bathini break; 220a20a8fa4SHari Bathini case OPAL_HARDWARE: 221a20a8fa4SHari Bathini pr_err("Support not available.\n"); 222a20a8fa4SHari Bathini fadump_conf->fadump_supported = 0; 223a20a8fa4SHari Bathini fadump_conf->fadump_enabled = 0; 224a20a8fa4SHari Bathini break; 225a20a8fa4SHari Bathini default: 226a20a8fa4SHari Bathini pr_err("Failed to register. Unknown Error(%lld).\n", rc); 227a20a8fa4SHari Bathini break; 228a20a8fa4SHari Bathini } 229a20a8fa4SHari Bathini 230a20a8fa4SHari Bathini /* 231a20a8fa4SHari Bathini * If some regions were registered before OPAL_MPIPL_ADD_RANGE 232a20a8fa4SHari Bathini * OPAL call failed, unregister all regions. 233a20a8fa4SHari Bathini */ 234a20a8fa4SHari Bathini if ((err < 0) && (opal_fdm->registered_regions > 0)) 235a20a8fa4SHari Bathini opal_fadump_unregister(fadump_conf); 236a20a8fa4SHari Bathini 237a20a8fa4SHari Bathini return err; 23841df5928SHari Bathini } 23941df5928SHari Bathini 24041df5928SHari Bathini static int opal_fadump_unregister(struct fw_dump *fadump_conf) 24141df5928SHari Bathini { 242a20a8fa4SHari Bathini s64 rc; 243a20a8fa4SHari Bathini 244a20a8fa4SHari Bathini rc = opal_mpipl_update(OPAL_MPIPL_REMOVE_ALL, 0, 0, 0); 245a20a8fa4SHari Bathini if (rc) { 246a20a8fa4SHari Bathini pr_err("Failed to un-register - unexpected Error(%lld).\n", rc); 24741df5928SHari Bathini return -EIO; 24841df5928SHari Bathini } 24941df5928SHari Bathini 250a20a8fa4SHari Bathini opal_fdm->registered_regions = 0; 251a20a8fa4SHari Bathini fadump_conf->dump_registered = 0; 252a20a8fa4SHari Bathini return 0; 253a20a8fa4SHari Bathini } 254a20a8fa4SHari Bathini 25541df5928SHari Bathini static int opal_fadump_invalidate(struct fw_dump *fadump_conf) 25641df5928SHari Bathini { 257a4e2e2caSHari Bathini s64 rc; 258a4e2e2caSHari Bathini 259a4e2e2caSHari Bathini rc = opal_mpipl_update(OPAL_MPIPL_FREE_PRESERVED_MEMORY, 0, 0, 0); 260a4e2e2caSHari Bathini if (rc) { 261a4e2e2caSHari Bathini pr_err("Failed to invalidate - unexpected Error(%lld).\n", rc); 26241df5928SHari Bathini return -EIO; 26341df5928SHari Bathini } 26441df5928SHari Bathini 265a4e2e2caSHari Bathini fadump_conf->dump_active = 0; 266a4e2e2caSHari Bathini opal_fdm_active = NULL; 267a4e2e2caSHari Bathini return 0; 268a4e2e2caSHari Bathini } 269a4e2e2caSHari Bathini 2702790d01dSHari Bathini static void opal_fadump_cleanup(struct fw_dump *fadump_conf) 2712790d01dSHari Bathini { 2722790d01dSHari Bathini s64 ret; 2732790d01dSHari Bathini 2742790d01dSHari Bathini ret = opal_mpipl_register_tag(OPAL_MPIPL_TAG_KERNEL, 0); 2752790d01dSHari Bathini if (ret != OPAL_SUCCESS) 2762790d01dSHari Bathini pr_warn("Could not reset (%llu) kernel metadata tag!\n", ret); 2772790d01dSHari Bathini } 2782790d01dSHari Bathini 2792a1b06ddSHari Bathini /* 2802a1b06ddSHari Bathini * Convert CPU state data saved at the time of crash into ELF notes. 2812a1b06ddSHari Bathini * 2822a1b06ddSHari Bathini * Append crashing CPU's register data saved by the kernel in the PT_NOTE. 2832a1b06ddSHari Bathini */ 2842a1b06ddSHari Bathini static int __init 2852a1b06ddSHari Bathini opal_fadump_build_cpu_notes(struct fw_dump *fadump_conf, 2862a1b06ddSHari Bathini struct fadump_crash_info_header *fdh) 2872a1b06ddSHari Bathini { 2882a1b06ddSHari Bathini u32 num_cpus = 1, *note_buf; 2892a1b06ddSHari Bathini int rc; 2902a1b06ddSHari Bathini 2912a1b06ddSHari Bathini if (fdh->crashing_cpu == FADUMP_CPU_UNKNOWN) 2922a1b06ddSHari Bathini return -ENODEV; 2932a1b06ddSHari Bathini 2942a1b06ddSHari Bathini /* Allocate CPU notes buffer to hold crashing cpu notes. */ 2952a1b06ddSHari Bathini rc = fadump_setup_cpu_notes_buf(num_cpus); 2962a1b06ddSHari Bathini if (rc != 0) 2972a1b06ddSHari Bathini return rc; 2982a1b06ddSHari Bathini 2992a1b06ddSHari Bathini note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr; 3002a1b06ddSHari Bathini note_buf = fadump_regs_to_elf_notes(note_buf, &(fdh->regs)); 3012a1b06ddSHari Bathini final_note(note_buf); 3022a1b06ddSHari Bathini 3032a1b06ddSHari Bathini pr_debug("Updating elfcore header (%llx) with cpu notes\n", 3042a1b06ddSHari Bathini fdh->elfcorehdr_addr); 3052a1b06ddSHari Bathini fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr)); 3062a1b06ddSHari Bathini return 0; 3072a1b06ddSHari Bathini } 3082a1b06ddSHari Bathini 30941df5928SHari Bathini static int __init opal_fadump_process(struct fw_dump *fadump_conf) 31041df5928SHari Bathini { 3112a1b06ddSHari Bathini struct fadump_crash_info_header *fdh; 3122a1b06ddSHari Bathini int rc = -EINVAL; 3132a1b06ddSHari Bathini 3142a1b06ddSHari Bathini if (!opal_fdm_active || !fadump_conf->fadumphdr_addr) 3152a1b06ddSHari Bathini return rc; 3162a1b06ddSHari Bathini 3172a1b06ddSHari Bathini /* Validate the fadump crash info header */ 3182a1b06ddSHari Bathini fdh = __va(fadump_conf->fadumphdr_addr); 3192a1b06ddSHari Bathini if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) { 3202a1b06ddSHari Bathini pr_err("Crash info header is not valid.\n"); 3212a1b06ddSHari Bathini return rc; 3222a1b06ddSHari Bathini } 3232a1b06ddSHari Bathini 3242a1b06ddSHari Bathini rc = opal_fadump_build_cpu_notes(fadump_conf, fdh); 3252a1b06ddSHari Bathini if (rc) 3262a1b06ddSHari Bathini return rc; 3272a1b06ddSHari Bathini 3282a1b06ddSHari Bathini /* 3292a1b06ddSHari Bathini * We are done validating dump info and elfcore header is now ready 3302a1b06ddSHari Bathini * to be exported. set elfcorehdr_addr so that vmcore module will 3312a1b06ddSHari Bathini * export the elfcore header through '/proc/vmcore'. 3322a1b06ddSHari Bathini */ 3332a1b06ddSHari Bathini elfcorehdr_addr = fdh->elfcorehdr_addr; 3342a1b06ddSHari Bathini 3352a1b06ddSHari Bathini return rc; 33641df5928SHari Bathini } 33741df5928SHari Bathini 33841df5928SHari Bathini static void opal_fadump_region_show(struct fw_dump *fadump_conf, 33941df5928SHari Bathini struct seq_file *m) 34041df5928SHari Bathini { 3412a1b06ddSHari Bathini const struct opal_fadump_mem_struct *fdm_ptr; 342742a265aSHari Bathini u64 dumped_bytes = 0; 343742a265aSHari Bathini int i; 344742a265aSHari Bathini 3452a1b06ddSHari Bathini if (fadump_conf->dump_active) 3462a1b06ddSHari Bathini fdm_ptr = opal_fdm_active; 3472a1b06ddSHari Bathini else 3482a1b06ddSHari Bathini fdm_ptr = opal_fdm; 3492a1b06ddSHari Bathini 350742a265aSHari Bathini for (i = 0; i < fdm_ptr->region_cnt; i++) { 3512a1b06ddSHari Bathini /* 3522a1b06ddSHari Bathini * Only regions that are registered for MPIPL 3532a1b06ddSHari Bathini * would have dump data. 3542a1b06ddSHari Bathini */ 3552a1b06ddSHari Bathini if ((fadump_conf->dump_active) && 3562a1b06ddSHari Bathini (i < fdm_ptr->registered_regions)) 3572a1b06ddSHari Bathini dumped_bytes = fdm_ptr->rgn[i].size; 3582a1b06ddSHari Bathini 359742a265aSHari Bathini seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 360742a265aSHari Bathini fdm_ptr->rgn[i].src, fdm_ptr->rgn[i].dest); 361742a265aSHari Bathini seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 362742a265aSHari Bathini fdm_ptr->rgn[i].size, dumped_bytes); 363742a265aSHari Bathini } 3642a1b06ddSHari Bathini 3652a1b06ddSHari Bathini /* Dump is active. Show reserved area start address. */ 3662a1b06ddSHari Bathini if (fadump_conf->dump_active) { 3672a1b06ddSHari Bathini seq_printf(m, "\nMemory above %#016lx is reserved for saving crash dump\n", 3682a1b06ddSHari Bathini fadump_conf->reserve_dump_area_start); 3692a1b06ddSHari Bathini } 37041df5928SHari Bathini } 37141df5928SHari Bathini 37241df5928SHari Bathini static void opal_fadump_trigger(struct fadump_crash_info_header *fdh, 37341df5928SHari Bathini const char *msg) 37441df5928SHari Bathini { 37541df5928SHari Bathini int rc; 37641df5928SHari Bathini 37741df5928SHari Bathini rc = opal_cec_reboot2(OPAL_REBOOT_MPIPL, msg); 37841df5928SHari Bathini if (rc == OPAL_UNSUPPORTED) { 37941df5928SHari Bathini pr_emerg("Reboot type %d not supported.\n", 38041df5928SHari Bathini OPAL_REBOOT_MPIPL); 38141df5928SHari Bathini } else if (rc == OPAL_HARDWARE) 38241df5928SHari Bathini pr_emerg("No backend support for MPIPL!\n"); 38341df5928SHari Bathini } 38441df5928SHari Bathini 38541df5928SHari Bathini static struct fadump_ops opal_fadump_ops = { 38641df5928SHari Bathini .fadump_init_mem_struct = opal_fadump_init_mem_struct, 387742a265aSHari Bathini .fadump_get_metadata_size = opal_fadump_get_metadata_size, 388742a265aSHari Bathini .fadump_setup_metadata = opal_fadump_setup_metadata, 38941df5928SHari Bathini .fadump_register = opal_fadump_register, 39041df5928SHari Bathini .fadump_unregister = opal_fadump_unregister, 39141df5928SHari Bathini .fadump_invalidate = opal_fadump_invalidate, 3922790d01dSHari Bathini .fadump_cleanup = opal_fadump_cleanup, 39341df5928SHari Bathini .fadump_process = opal_fadump_process, 39441df5928SHari Bathini .fadump_region_show = opal_fadump_region_show, 39541df5928SHari Bathini .fadump_trigger = opal_fadump_trigger, 39641df5928SHari Bathini }; 39741df5928SHari Bathini 39841df5928SHari Bathini void __init opal_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 39941df5928SHari Bathini { 4002a1b06ddSHari Bathini const __be32 *prop; 40141df5928SHari Bathini unsigned long dn; 4022a1b06ddSHari Bathini u64 addr = 0; 4032a1b06ddSHari Bathini s64 ret; 4042a1b06ddSHari Bathini 40541df5928SHari Bathini 40641df5928SHari Bathini /* 40741df5928SHari Bathini * Check if Firmware-Assisted Dump is supported. if yes, check 40841df5928SHari Bathini * if dump has been initiated on last reboot. 40941df5928SHari Bathini */ 41041df5928SHari Bathini dn = of_get_flat_dt_subnode_by_name(node, "dump"); 41141df5928SHari Bathini if (dn == -FDT_ERR_NOTFOUND) { 41241df5928SHari Bathini pr_debug("FADump support is missing!\n"); 41341df5928SHari Bathini return; 41441df5928SHari Bathini } 41541df5928SHari Bathini 41641df5928SHari Bathini if (!of_flat_dt_is_compatible(dn, "ibm,opal-dump")) { 41741df5928SHari Bathini pr_err("Support missing for this f/w version!\n"); 41841df5928SHari Bathini return; 41941df5928SHari Bathini } 42041df5928SHari Bathini 42141df5928SHari Bathini fadump_conf->ops = &opal_fadump_ops; 42241df5928SHari Bathini fadump_conf->fadump_supported = 1; 4232a1b06ddSHari Bathini 4242a1b06ddSHari Bathini /* 4252a1b06ddSHari Bathini * Check if dump has been initiated on last reboot. 4262a1b06ddSHari Bathini */ 4272a1b06ddSHari Bathini prop = of_get_flat_dt_prop(dn, "mpipl-boot", NULL); 4282a1b06ddSHari Bathini if (!prop) 4292a1b06ddSHari Bathini return; 4302a1b06ddSHari Bathini 4312a1b06ddSHari Bathini ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_KERNEL, &addr); 4322a1b06ddSHari Bathini if ((ret != OPAL_SUCCESS) || !addr) { 4332a1b06ddSHari Bathini pr_err("Failed to get Kernel metadata (%lld)\n", ret); 4342a1b06ddSHari Bathini return; 4352a1b06ddSHari Bathini } 4362a1b06ddSHari Bathini 4372a1b06ddSHari Bathini addr = be64_to_cpu(addr); 4382a1b06ddSHari Bathini pr_debug("Kernel metadata addr: %llx\n", addr); 4392a1b06ddSHari Bathini 4402a1b06ddSHari Bathini opal_fdm_active = __va(addr); 4412a1b06ddSHari Bathini if (opal_fdm_active->version != OPAL_FADUMP_VERSION) { 4422a1b06ddSHari Bathini pr_warn("Supported kernel metadata version: %u, found: %d!\n", 4432a1b06ddSHari Bathini OPAL_FADUMP_VERSION, opal_fdm_active->version); 4442a1b06ddSHari Bathini pr_warn("WARNING: Kernel metadata format mismatch identified! Core file maybe corrupted..\n"); 4452a1b06ddSHari Bathini } 4462a1b06ddSHari Bathini 4472a1b06ddSHari Bathini /* Kernel regions not registered with f/w for MPIPL */ 4482a1b06ddSHari Bathini if (opal_fdm_active->registered_regions == 0) { 4492a1b06ddSHari Bathini opal_fdm_active = NULL; 4502a1b06ddSHari Bathini return; 4512a1b06ddSHari Bathini } 4522a1b06ddSHari Bathini 4532a1b06ddSHari Bathini pr_info("Firmware-assisted dump is active.\n"); 4542a1b06ddSHari Bathini fadump_conf->dump_active = 1; 4552a1b06ddSHari Bathini opal_fadump_get_config(fadump_conf, opal_fdm_active); 45641df5928SHari Bathini } 457