18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Firmware-Assisted Dump support on POWERVM platform. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011, Mahesh Salgaonkar, IBM Corporation. 68c2ecf20Sopenharmony_ci * Copyright 2019, Hari Bathini, IBM Corporation. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "rtas fadump: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/memblock.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 158c2ecf20Sopenharmony_ci#include <linux/crash_dump.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/page.h> 188c2ecf20Sopenharmony_ci#include <asm/prom.h> 198c2ecf20Sopenharmony_ci#include <asm/rtas.h> 208c2ecf20Sopenharmony_ci#include <asm/fadump.h> 218c2ecf20Sopenharmony_ci#include <asm/fadump-internal.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include "rtas-fadump.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistatic struct rtas_fadump_mem_struct fdm; 268c2ecf20Sopenharmony_cistatic const struct rtas_fadump_mem_struct *fdm_active; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void rtas_fadump_update_config(struct fw_dump *fadump_conf, 298c2ecf20Sopenharmony_ci const struct rtas_fadump_mem_struct *fdm) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci fadump_conf->boot_mem_dest_addr = 328c2ecf20Sopenharmony_ci be64_to_cpu(fdm->rmr_region.destination_address); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr + 358c2ecf20Sopenharmony_ci fadump_conf->boot_memory_size); 368c2ecf20Sopenharmony_ci} 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * This function is called in the capture kernel to get configuration details 408c2ecf20Sopenharmony_ci * setup in the first kernel and passed to the f/w. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic void rtas_fadump_get_config(struct fw_dump *fadump_conf, 438c2ecf20Sopenharmony_ci const struct rtas_fadump_mem_struct *fdm) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci fadump_conf->boot_mem_addr[0] = 468c2ecf20Sopenharmony_ci be64_to_cpu(fdm->rmr_region.source_address); 478c2ecf20Sopenharmony_ci fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len); 488c2ecf20Sopenharmony_ci fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0]; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci fadump_conf->boot_mem_top = fadump_conf->boot_memory_size; 518c2ecf20Sopenharmony_ci fadump_conf->boot_mem_regs_cnt = 1; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * Start address of reserve dump area (permanent reservation) for 558c2ecf20Sopenharmony_ci * re-registering FADump after dump capture. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci fadump_conf->reserve_dump_area_start = 588c2ecf20Sopenharmony_ci be64_to_cpu(fdm->cpu_state_data.destination_address); 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci rtas_fadump_update_config(fadump_conf, fdm); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci u64 addr = fadump_conf->reserve_dump_area_start; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct)); 688c2ecf20Sopenharmony_ci addr = addr & PAGE_MASK; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci fdm.header.dump_format_version = cpu_to_be32(0x00000001); 718c2ecf20Sopenharmony_ci fdm.header.dump_num_sections = cpu_to_be16(3); 728c2ecf20Sopenharmony_ci fdm.header.dump_status_flag = 0; 738c2ecf20Sopenharmony_ci fdm.header.offset_first_dump_section = 748c2ecf20Sopenharmony_ci cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, 758c2ecf20Sopenharmony_ci cpu_state_data)); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Fields for disk dump option. 798c2ecf20Sopenharmony_ci * We are not using disk dump option, hence set these fields to 0. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci fdm.header.dd_block_size = 0; 828c2ecf20Sopenharmony_ci fdm.header.dd_block_offset = 0; 838c2ecf20Sopenharmony_ci fdm.header.dd_num_blocks = 0; 848c2ecf20Sopenharmony_ci fdm.header.dd_offset_disk_path = 0; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci /* set 0 to disable an automatic dump-reboot. */ 878c2ecf20Sopenharmony_ci fdm.header.max_time_auto = 0; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Kernel dump sections */ 908c2ecf20Sopenharmony_ci /* cpu state data section. */ 918c2ecf20Sopenharmony_ci fdm.cpu_state_data.request_flag = 928c2ecf20Sopenharmony_ci cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 938c2ecf20Sopenharmony_ci fdm.cpu_state_data.source_data_type = 948c2ecf20Sopenharmony_ci cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA); 958c2ecf20Sopenharmony_ci fdm.cpu_state_data.source_address = 0; 968c2ecf20Sopenharmony_ci fdm.cpu_state_data.source_len = 978c2ecf20Sopenharmony_ci cpu_to_be64(fadump_conf->cpu_state_data_size); 988c2ecf20Sopenharmony_ci fdm.cpu_state_data.destination_address = cpu_to_be64(addr); 998c2ecf20Sopenharmony_ci addr += fadump_conf->cpu_state_data_size; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci /* hpte region section */ 1028c2ecf20Sopenharmony_ci fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 1038c2ecf20Sopenharmony_ci fdm.hpte_region.source_data_type = 1048c2ecf20Sopenharmony_ci cpu_to_be16(RTAS_FADUMP_HPTE_REGION); 1058c2ecf20Sopenharmony_ci fdm.hpte_region.source_address = 0; 1068c2ecf20Sopenharmony_ci fdm.hpte_region.source_len = 1078c2ecf20Sopenharmony_ci cpu_to_be64(fadump_conf->hpte_region_size); 1088c2ecf20Sopenharmony_ci fdm.hpte_region.destination_address = cpu_to_be64(addr); 1098c2ecf20Sopenharmony_ci addr += fadump_conf->hpte_region_size; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci /* RMA region section */ 1128c2ecf20Sopenharmony_ci fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 1138c2ecf20Sopenharmony_ci fdm.rmr_region.source_data_type = 1148c2ecf20Sopenharmony_ci cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION); 1158c2ecf20Sopenharmony_ci fdm.rmr_region.source_address = cpu_to_be64(0); 1168c2ecf20Sopenharmony_ci fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size); 1178c2ecf20Sopenharmony_ci fdm.rmr_region.destination_address = cpu_to_be64(addr); 1188c2ecf20Sopenharmony_ci addr += fadump_conf->boot_memory_size; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci rtas_fadump_update_config(fadump_conf, &fdm); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return addr; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic u64 rtas_fadump_get_bootmem_min(void) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci return RTAS_FADUMP_MIN_BOOT_MEM; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int rtas_fadump_register(struct fw_dump *fadump_conf) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned int wait_time; 1338c2ecf20Sopenharmony_ci int rc, err = -EIO; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* TODO: Add upper time limit for the delay */ 1368c2ecf20Sopenharmony_ci do { 1378c2ecf20Sopenharmony_ci rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 1388c2ecf20Sopenharmony_ci NULL, FADUMP_REGISTER, &fdm, 1398c2ecf20Sopenharmony_ci sizeof(struct rtas_fadump_mem_struct)); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci wait_time = rtas_busy_delay_time(rc); 1428c2ecf20Sopenharmony_ci if (wait_time) 1438c2ecf20Sopenharmony_ci mdelay(wait_time); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci } while (wait_time); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci switch (rc) { 1488c2ecf20Sopenharmony_ci case 0: 1498c2ecf20Sopenharmony_ci pr_info("Registration is successful!\n"); 1508c2ecf20Sopenharmony_ci fadump_conf->dump_registered = 1; 1518c2ecf20Sopenharmony_ci err = 0; 1528c2ecf20Sopenharmony_ci break; 1538c2ecf20Sopenharmony_ci case -1: 1548c2ecf20Sopenharmony_ci pr_err("Failed to register. Hardware Error(%d).\n", rc); 1558c2ecf20Sopenharmony_ci break; 1568c2ecf20Sopenharmony_ci case -3: 1578c2ecf20Sopenharmony_ci if (!is_fadump_boot_mem_contiguous()) 1588c2ecf20Sopenharmony_ci pr_err("Can't have holes in boot memory area.\n"); 1598c2ecf20Sopenharmony_ci else if (!is_fadump_reserved_mem_contiguous()) 1608c2ecf20Sopenharmony_ci pr_err("Can't have holes in reserved memory area.\n"); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci pr_err("Failed to register. Parameter Error(%d).\n", rc); 1638c2ecf20Sopenharmony_ci err = -EINVAL; 1648c2ecf20Sopenharmony_ci break; 1658c2ecf20Sopenharmony_ci case -9: 1668c2ecf20Sopenharmony_ci pr_err("Already registered!\n"); 1678c2ecf20Sopenharmony_ci fadump_conf->dump_registered = 1; 1688c2ecf20Sopenharmony_ci err = -EEXIST; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci default: 1718c2ecf20Sopenharmony_ci pr_err("Failed to register. Unknown Error(%d).\n", rc); 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return err; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int rtas_fadump_unregister(struct fw_dump *fadump_conf) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci unsigned int wait_time; 1818c2ecf20Sopenharmony_ci int rc; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* TODO: Add upper time limit for the delay */ 1848c2ecf20Sopenharmony_ci do { 1858c2ecf20Sopenharmony_ci rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 1868c2ecf20Sopenharmony_ci NULL, FADUMP_UNREGISTER, &fdm, 1878c2ecf20Sopenharmony_ci sizeof(struct rtas_fadump_mem_struct)); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci wait_time = rtas_busy_delay_time(rc); 1908c2ecf20Sopenharmony_ci if (wait_time) 1918c2ecf20Sopenharmony_ci mdelay(wait_time); 1928c2ecf20Sopenharmony_ci } while (wait_time); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (rc) { 1958c2ecf20Sopenharmony_ci pr_err("Failed to un-register - unexpected error(%d).\n", rc); 1968c2ecf20Sopenharmony_ci return -EIO; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci fadump_conf->dump_registered = 0; 2008c2ecf20Sopenharmony_ci return 0; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int rtas_fadump_invalidate(struct fw_dump *fadump_conf) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci unsigned int wait_time; 2068c2ecf20Sopenharmony_ci int rc; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* TODO: Add upper time limit for the delay */ 2098c2ecf20Sopenharmony_ci do { 2108c2ecf20Sopenharmony_ci rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 2118c2ecf20Sopenharmony_ci NULL, FADUMP_INVALIDATE, fdm_active, 2128c2ecf20Sopenharmony_ci sizeof(struct rtas_fadump_mem_struct)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci wait_time = rtas_busy_delay_time(rc); 2158c2ecf20Sopenharmony_ci if (wait_time) 2168c2ecf20Sopenharmony_ci mdelay(wait_time); 2178c2ecf20Sopenharmony_ci } while (wait_time); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci if (rc) { 2208c2ecf20Sopenharmony_ci pr_err("Failed to invalidate - unexpected error (%d).\n", rc); 2218c2ecf20Sopenharmony_ci return -EIO; 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci fadump_conf->dump_active = 0; 2258c2ecf20Sopenharmony_ci fdm_active = NULL; 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#define RTAS_FADUMP_GPR_MASK 0xffffff0000000000 2308c2ecf20Sopenharmony_cistatic inline int rtas_fadump_gpr_index(u64 id) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci char str[3]; 2338c2ecf20Sopenharmony_ci int i = -1; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) { 2368c2ecf20Sopenharmony_ci /* get the digits at the end */ 2378c2ecf20Sopenharmony_ci id &= ~RTAS_FADUMP_GPR_MASK; 2388c2ecf20Sopenharmony_ci id >>= 24; 2398c2ecf20Sopenharmony_ci str[2] = '\0'; 2408c2ecf20Sopenharmony_ci str[1] = id & 0xff; 2418c2ecf20Sopenharmony_ci str[0] = (id >> 8) & 0xff; 2428c2ecf20Sopenharmony_ci if (kstrtoint(str, 10, &i)) 2438c2ecf20Sopenharmony_ci i = -EINVAL; 2448c2ecf20Sopenharmony_ci if (i > 31) 2458c2ecf20Sopenharmony_ci i = -1; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci return i; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_civoid rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci int i; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci i = rtas_fadump_gpr_index(reg_id); 2558c2ecf20Sopenharmony_ci if (i >= 0) 2568c2ecf20Sopenharmony_ci regs->gpr[i] = (unsigned long)reg_val; 2578c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("NIA")) 2588c2ecf20Sopenharmony_ci regs->nip = (unsigned long)reg_val; 2598c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("MSR")) 2608c2ecf20Sopenharmony_ci regs->msr = (unsigned long)reg_val; 2618c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("CTR")) 2628c2ecf20Sopenharmony_ci regs->ctr = (unsigned long)reg_val; 2638c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("LR")) 2648c2ecf20Sopenharmony_ci regs->link = (unsigned long)reg_val; 2658c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("XER")) 2668c2ecf20Sopenharmony_ci regs->xer = (unsigned long)reg_val; 2678c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("CR")) 2688c2ecf20Sopenharmony_ci regs->ccr = (unsigned long)reg_val; 2698c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("DAR")) 2708c2ecf20Sopenharmony_ci regs->dar = (unsigned long)reg_val; 2718c2ecf20Sopenharmony_ci else if (reg_id == fadump_str_to_u64("DSISR")) 2728c2ecf20Sopenharmony_ci regs->dsisr = (unsigned long)reg_val; 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct rtas_fadump_reg_entry* 2768c2ecf20Sopenharmony_cirtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry, 2778c2ecf20Sopenharmony_ci struct pt_regs *regs) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci memset(regs, 0, sizeof(struct pt_regs)); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) { 2828c2ecf20Sopenharmony_ci rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id), 2838c2ecf20Sopenharmony_ci be64_to_cpu(reg_entry->reg_value)); 2848c2ecf20Sopenharmony_ci reg_entry++; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci reg_entry++; 2878c2ecf20Sopenharmony_ci return reg_entry; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci/* 2918c2ecf20Sopenharmony_ci * Read CPU state dump data and convert it into ELF notes. 2928c2ecf20Sopenharmony_ci * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be 2938c2ecf20Sopenharmony_ci * used to access the data to allow for additional fields to be added without 2948c2ecf20Sopenharmony_ci * affecting compatibility. Each list of registers for a CPU starts with 2958c2ecf20Sopenharmony_ci * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes, 2968c2ecf20Sopenharmony_ci * 8 Byte ASCII identifier and 8 Byte register value. The register entry 2978c2ecf20Sopenharmony_ci * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part 2988c2ecf20Sopenharmony_ci * of register value. For more details refer to PAPR document. 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Only for the crashing cpu we ignore the CPU dump data and get exact 3018c2ecf20Sopenharmony_ci * state from fadump crash info structure populated by first kernel at the 3028c2ecf20Sopenharmony_ci * time of crash. 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct rtas_fadump_reg_save_area_header *reg_header; 3078c2ecf20Sopenharmony_ci struct fadump_crash_info_header *fdh = NULL; 3088c2ecf20Sopenharmony_ci struct rtas_fadump_reg_entry *reg_entry; 3098c2ecf20Sopenharmony_ci u32 num_cpus, *note_buf; 3108c2ecf20Sopenharmony_ci int i, rc = 0, cpu = 0; 3118c2ecf20Sopenharmony_ci struct pt_regs regs; 3128c2ecf20Sopenharmony_ci unsigned long addr; 3138c2ecf20Sopenharmony_ci void *vaddr; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address); 3168c2ecf20Sopenharmony_ci vaddr = __va(addr); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci reg_header = vaddr; 3198c2ecf20Sopenharmony_ci if (be64_to_cpu(reg_header->magic_number) != 3208c2ecf20Sopenharmony_ci fadump_str_to_u64("REGSAVE")) { 3218c2ecf20Sopenharmony_ci pr_err("Unable to read register save area.\n"); 3228c2ecf20Sopenharmony_ci return -ENOENT; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci pr_debug("--------CPU State Data------------\n"); 3268c2ecf20Sopenharmony_ci pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number)); 3278c2ecf20Sopenharmony_ci pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset)); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci vaddr += be32_to_cpu(reg_header->num_cpu_offset); 3308c2ecf20Sopenharmony_ci num_cpus = be32_to_cpu(*((__be32 *)(vaddr))); 3318c2ecf20Sopenharmony_ci pr_debug("NumCpus : %u\n", num_cpus); 3328c2ecf20Sopenharmony_ci vaddr += sizeof(u32); 3338c2ecf20Sopenharmony_ci reg_entry = (struct rtas_fadump_reg_entry *)vaddr; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci rc = fadump_setup_cpu_notes_buf(num_cpus); 3368c2ecf20Sopenharmony_ci if (rc != 0) 3378c2ecf20Sopenharmony_ci return rc; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (fadump_conf->fadumphdr_addr) 3428c2ecf20Sopenharmony_ci fdh = __va(fadump_conf->fadumphdr_addr); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci for (i = 0; i < num_cpus; i++) { 3458c2ecf20Sopenharmony_ci if (be64_to_cpu(reg_entry->reg_id) != 3468c2ecf20Sopenharmony_ci fadump_str_to_u64("CPUSTRT")) { 3478c2ecf20Sopenharmony_ci pr_err("Unable to read CPU state data\n"); 3488c2ecf20Sopenharmony_ci rc = -ENOENT; 3498c2ecf20Sopenharmony_ci goto error_out; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci /* Lower 4 bytes of reg_value contains logical cpu id */ 3528c2ecf20Sopenharmony_ci cpu = (be64_to_cpu(reg_entry->reg_value) & 3538c2ecf20Sopenharmony_ci RTAS_FADUMP_CPU_ID_MASK); 3548c2ecf20Sopenharmony_ci if (fdh && !cpumask_test_cpu(cpu, &fdh->online_mask)) { 3558c2ecf20Sopenharmony_ci RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); 3568c2ecf20Sopenharmony_ci continue; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci pr_debug("Reading register data for cpu %d...\n", cpu); 3598c2ecf20Sopenharmony_ci if (fdh && fdh->crashing_cpu == cpu) { 3608c2ecf20Sopenharmony_ci regs = fdh->regs; 3618c2ecf20Sopenharmony_ci note_buf = fadump_regs_to_elf_notes(note_buf, ®s); 3628c2ecf20Sopenharmony_ci RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); 3638c2ecf20Sopenharmony_ci } else { 3648c2ecf20Sopenharmony_ci reg_entry++; 3658c2ecf20Sopenharmony_ci reg_entry = rtas_fadump_read_regs(reg_entry, ®s); 3668c2ecf20Sopenharmony_ci note_buf = fadump_regs_to_elf_notes(note_buf, ®s); 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci final_note(note_buf); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci if (fdh) { 3728c2ecf20Sopenharmony_ci pr_debug("Updating elfcore header (%llx) with cpu notes\n", 3738c2ecf20Sopenharmony_ci fdh->elfcorehdr_addr); 3748c2ecf20Sopenharmony_ci fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr)); 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci return 0; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cierror_out: 3798c2ecf20Sopenharmony_ci fadump_free_cpu_notes_buf(); 3808c2ecf20Sopenharmony_ci return rc; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci/* 3858c2ecf20Sopenharmony_ci * Validate and process the dump data stored by firmware before exporting 3868c2ecf20Sopenharmony_ci * it through '/proc/vmcore'. 3878c2ecf20Sopenharmony_ci */ 3888c2ecf20Sopenharmony_cistatic int __init rtas_fadump_process(struct fw_dump *fadump_conf) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct fadump_crash_info_header *fdh; 3918c2ecf20Sopenharmony_ci int rc = 0; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (!fdm_active || !fadump_conf->fadumphdr_addr) 3948c2ecf20Sopenharmony_ci return -EINVAL; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* Check if the dump data is valid. */ 3978c2ecf20Sopenharmony_ci if ((be16_to_cpu(fdm_active->header.dump_status_flag) == 3988c2ecf20Sopenharmony_ci RTAS_FADUMP_ERROR_FLAG) || 3998c2ecf20Sopenharmony_ci (fdm_active->cpu_state_data.error_flags != 0) || 4008c2ecf20Sopenharmony_ci (fdm_active->rmr_region.error_flags != 0)) { 4018c2ecf20Sopenharmony_ci pr_err("Dump taken by platform is not valid\n"); 4028c2ecf20Sopenharmony_ci return -EINVAL; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci if ((fdm_active->rmr_region.bytes_dumped != 4058c2ecf20Sopenharmony_ci fdm_active->rmr_region.source_len) || 4068c2ecf20Sopenharmony_ci !fdm_active->cpu_state_data.bytes_dumped) { 4078c2ecf20Sopenharmony_ci pr_err("Dump taken by platform is incomplete\n"); 4088c2ecf20Sopenharmony_ci return -EINVAL; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci /* Validate the fadump crash info header */ 4128c2ecf20Sopenharmony_ci fdh = __va(fadump_conf->fadumphdr_addr); 4138c2ecf20Sopenharmony_ci if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) { 4148c2ecf20Sopenharmony_ci pr_err("Crash info header is not valid.\n"); 4158c2ecf20Sopenharmony_ci return -EINVAL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci rc = rtas_fadump_build_cpu_notes(fadump_conf); 4198c2ecf20Sopenharmony_ci if (rc) 4208c2ecf20Sopenharmony_ci return rc; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* 4238c2ecf20Sopenharmony_ci * We are done validating dump info and elfcore header is now ready 4248c2ecf20Sopenharmony_ci * to be exported. set elfcorehdr_addr so that vmcore module will 4258c2ecf20Sopenharmony_ci * export the elfcore header through '/proc/vmcore'. 4268c2ecf20Sopenharmony_ci */ 4278c2ecf20Sopenharmony_ci elfcorehdr_addr = fdh->elfcorehdr_addr; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void rtas_fadump_region_show(struct fw_dump *fadump_conf, 4338c2ecf20Sopenharmony_ci struct seq_file *m) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci const struct rtas_fadump_section *cpu_data_section; 4368c2ecf20Sopenharmony_ci const struct rtas_fadump_mem_struct *fdm_ptr; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (fdm_active) 4398c2ecf20Sopenharmony_ci fdm_ptr = fdm_active; 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci fdm_ptr = &fdm; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci cpu_data_section = &(fdm_ptr->cpu_state_data); 4448c2ecf20Sopenharmony_ci seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 4458c2ecf20Sopenharmony_ci be64_to_cpu(cpu_data_section->destination_address), 4468c2ecf20Sopenharmony_ci be64_to_cpu(cpu_data_section->destination_address) + 4478c2ecf20Sopenharmony_ci be64_to_cpu(cpu_data_section->source_len) - 1, 4488c2ecf20Sopenharmony_ci be64_to_cpu(cpu_data_section->source_len), 4498c2ecf20Sopenharmony_ci be64_to_cpu(cpu_data_section->bytes_dumped)); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 4528c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->hpte_region.destination_address), 4538c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->hpte_region.destination_address) + 4548c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1, 4558c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->hpte_region.source_len), 4568c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped)); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 4598c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->rmr_region.source_address), 4608c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->rmr_region.destination_address)); 4618c2ecf20Sopenharmony_ci seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 4628c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->rmr_region.source_len), 4638c2ecf20Sopenharmony_ci be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped)); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* Dump is active. Show reserved area start address. */ 4668c2ecf20Sopenharmony_ci if (fdm_active) { 4678c2ecf20Sopenharmony_ci seq_printf(m, "\nMemory above %#016lx is reserved for saving crash dump\n", 4688c2ecf20Sopenharmony_ci fadump_conf->reserve_dump_area_start); 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void rtas_fadump_trigger(struct fadump_crash_info_header *fdh, 4738c2ecf20Sopenharmony_ci const char *msg) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci /* Call ibm,os-term rtas call to trigger firmware assisted dump */ 4768c2ecf20Sopenharmony_ci rtas_os_term((char *)msg); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic struct fadump_ops rtas_fadump_ops = { 4808c2ecf20Sopenharmony_ci .fadump_init_mem_struct = rtas_fadump_init_mem_struct, 4818c2ecf20Sopenharmony_ci .fadump_get_bootmem_min = rtas_fadump_get_bootmem_min, 4828c2ecf20Sopenharmony_ci .fadump_register = rtas_fadump_register, 4838c2ecf20Sopenharmony_ci .fadump_unregister = rtas_fadump_unregister, 4848c2ecf20Sopenharmony_ci .fadump_invalidate = rtas_fadump_invalidate, 4858c2ecf20Sopenharmony_ci .fadump_process = rtas_fadump_process, 4868c2ecf20Sopenharmony_ci .fadump_region_show = rtas_fadump_region_show, 4878c2ecf20Sopenharmony_ci .fadump_trigger = rtas_fadump_trigger, 4888c2ecf20Sopenharmony_ci}; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_civoid __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci int i, size, num_sections; 4938c2ecf20Sopenharmony_ci const __be32 *sections; 4948c2ecf20Sopenharmony_ci const __be32 *token; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * Check if Firmware Assisted dump is supported. if yes, check 4988c2ecf20Sopenharmony_ci * if dump has been initiated on last reboot. 4998c2ecf20Sopenharmony_ci */ 5008c2ecf20Sopenharmony_ci token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); 5018c2ecf20Sopenharmony_ci if (!token) 5028c2ecf20Sopenharmony_ci return; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token); 5058c2ecf20Sopenharmony_ci fadump_conf->ops = &rtas_fadump_ops; 5068c2ecf20Sopenharmony_ci fadump_conf->fadump_supported = 1; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Firmware supports 64-bit value for size, align it to pagesize. */ 5098c2ecf20Sopenharmony_ci fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * The 'ibm,kernel-dump' rtas node is present only if there is 5138c2ecf20Sopenharmony_ci * dump data waiting for us. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); 5168c2ecf20Sopenharmony_ci if (fdm_active) { 5178c2ecf20Sopenharmony_ci pr_info("Firmware-assisted dump is active.\n"); 5188c2ecf20Sopenharmony_ci fadump_conf->dump_active = 1; 5198c2ecf20Sopenharmony_ci rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active)); 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* Get the sizes required to store dump data for the firmware provided 5238c2ecf20Sopenharmony_ci * dump sections. 5248c2ecf20Sopenharmony_ci * For each dump section type supported, a 32bit cell which defines 5258c2ecf20Sopenharmony_ci * the ID of a supported section followed by two 32 bit cells which 5268c2ecf20Sopenharmony_ci * gives the size of the section in bytes. 5278c2ecf20Sopenharmony_ci */ 5288c2ecf20Sopenharmony_ci sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", 5298c2ecf20Sopenharmony_ci &size); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!sections) 5328c2ecf20Sopenharmony_ci return; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci num_sections = size / (3 * sizeof(u32)); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci for (i = 0; i < num_sections; i++, sections += 3) { 5378c2ecf20Sopenharmony_ci u32 type = (u32)of_read_number(sections, 1); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci switch (type) { 5408c2ecf20Sopenharmony_ci case RTAS_FADUMP_CPU_STATE_DATA: 5418c2ecf20Sopenharmony_ci fadump_conf->cpu_state_data_size = 5428c2ecf20Sopenharmony_ci of_read_ulong(§ions[1], 2); 5438c2ecf20Sopenharmony_ci break; 5448c2ecf20Sopenharmony_ci case RTAS_FADUMP_HPTE_REGION: 5458c2ecf20Sopenharmony_ci fadump_conf->hpte_region_size = 5468c2ecf20Sopenharmony_ci of_read_ulong(§ions[1], 2); 5478c2ecf20Sopenharmony_ci break; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci} 551