1d3833a70SHari Bathini // SPDX-License-Identifier: GPL-2.0-or-later 2d3833a70SHari Bathini /* 3d3833a70SHari Bathini * Firmware-Assisted Dump support on POWERVM platform. 4d3833a70SHari Bathini * 5d3833a70SHari Bathini * Copyright 2011, Mahesh Salgaonkar, IBM Corporation. 6d3833a70SHari Bathini * Copyright 2019, Hari Bathini, IBM Corporation. 7d3833a70SHari Bathini */ 8d3833a70SHari Bathini 9d3833a70SHari Bathini #define pr_fmt(fmt) "rtas fadump: " fmt 10d3833a70SHari Bathini 11d3833a70SHari Bathini #include <linux/string.h> 12d3833a70SHari Bathini #include <linux/memblock.h> 13d3833a70SHari Bathini #include <linux/delay.h> 14d3833a70SHari Bathini #include <linux/seq_file.h> 15d3833a70SHari Bathini #include <linux/crash_dump.h> 16d3833a70SHari Bathini 17d3833a70SHari Bathini #include <asm/page.h> 18d3833a70SHari Bathini #include <asm/prom.h> 19d3833a70SHari Bathini #include <asm/rtas.h> 20d3833a70SHari Bathini #include <asm/fadump.h> 21d3833a70SHari Bathini #include <asm/fadump-internal.h> 22d3833a70SHari Bathini 23d3833a70SHari Bathini #include "rtas-fadump.h" 24d3833a70SHari Bathini 2541a65d16SHari Bathini static struct rtas_fadump_mem_struct fdm; 2641a65d16SHari Bathini 2741a65d16SHari Bathini static void rtas_fadump_update_config(struct fw_dump *fadump_conf, 2841a65d16SHari Bathini const struct rtas_fadump_mem_struct *fdm) 2941a65d16SHari Bathini { 3041a65d16SHari Bathini fadump_conf->boot_mem_dest_addr = 3141a65d16SHari Bathini be64_to_cpu(fdm->rmr_region.destination_address); 3241a65d16SHari Bathini 3341a65d16SHari Bathini fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr + 3441a65d16SHari Bathini fadump_conf->boot_memory_size); 3541a65d16SHari Bathini } 3641a65d16SHari Bathini 37d3833a70SHari Bathini static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf) 38d3833a70SHari Bathini { 3941a65d16SHari Bathini u64 addr = fadump_conf->reserve_dump_area_start; 4041a65d16SHari Bathini 4141a65d16SHari Bathini memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct)); 4241a65d16SHari Bathini addr = addr & PAGE_MASK; 4341a65d16SHari Bathini 4441a65d16SHari Bathini fdm.header.dump_format_version = cpu_to_be32(0x00000001); 4541a65d16SHari Bathini fdm.header.dump_num_sections = cpu_to_be16(3); 4641a65d16SHari Bathini fdm.header.dump_status_flag = 0; 4741a65d16SHari Bathini fdm.header.offset_first_dump_section = 4841a65d16SHari Bathini cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, 4941a65d16SHari Bathini cpu_state_data)); 5041a65d16SHari Bathini 5141a65d16SHari Bathini /* 5241a65d16SHari Bathini * Fields for disk dump option. 5341a65d16SHari Bathini * We are not using disk dump option, hence set these fields to 0. 5441a65d16SHari Bathini */ 5541a65d16SHari Bathini fdm.header.dd_block_size = 0; 5641a65d16SHari Bathini fdm.header.dd_block_offset = 0; 5741a65d16SHari Bathini fdm.header.dd_num_blocks = 0; 5841a65d16SHari Bathini fdm.header.dd_offset_disk_path = 0; 5941a65d16SHari Bathini 6041a65d16SHari Bathini /* set 0 to disable an automatic dump-reboot. */ 6141a65d16SHari Bathini fdm.header.max_time_auto = 0; 6241a65d16SHari Bathini 6341a65d16SHari Bathini /* Kernel dump sections */ 6441a65d16SHari Bathini /* cpu state data section. */ 6541a65d16SHari Bathini fdm.cpu_state_data.request_flag = 6641a65d16SHari Bathini cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 6741a65d16SHari Bathini fdm.cpu_state_data.source_data_type = 6841a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA); 6941a65d16SHari Bathini fdm.cpu_state_data.source_address = 0; 7041a65d16SHari Bathini fdm.cpu_state_data.source_len = 7141a65d16SHari Bathini cpu_to_be64(fadump_conf->cpu_state_data_size); 7241a65d16SHari Bathini fdm.cpu_state_data.destination_address = cpu_to_be64(addr); 7341a65d16SHari Bathini addr += fadump_conf->cpu_state_data_size; 7441a65d16SHari Bathini 7541a65d16SHari Bathini /* hpte region section */ 7641a65d16SHari Bathini fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 7741a65d16SHari Bathini fdm.hpte_region.source_data_type = 7841a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_HPTE_REGION); 7941a65d16SHari Bathini fdm.hpte_region.source_address = 0; 8041a65d16SHari Bathini fdm.hpte_region.source_len = 8141a65d16SHari Bathini cpu_to_be64(fadump_conf->hpte_region_size); 8241a65d16SHari Bathini fdm.hpte_region.destination_address = cpu_to_be64(addr); 8341a65d16SHari Bathini addr += fadump_conf->hpte_region_size; 8441a65d16SHari Bathini 8541a65d16SHari Bathini /* RMA region section */ 8641a65d16SHari Bathini fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 8741a65d16SHari Bathini fdm.rmr_region.source_data_type = 8841a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION); 8941a65d16SHari Bathini fdm.rmr_region.source_address = cpu_to_be64(RMA_START); 9041a65d16SHari Bathini fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size); 9141a65d16SHari Bathini fdm.rmr_region.destination_address = cpu_to_be64(addr); 9241a65d16SHari Bathini addr += fadump_conf->boot_memory_size; 9341a65d16SHari Bathini 9441a65d16SHari Bathini rtas_fadump_update_config(fadump_conf, &fdm); 9541a65d16SHari Bathini 9641a65d16SHari Bathini return addr; 97d3833a70SHari Bathini } 98d3833a70SHari Bathini 99d3833a70SHari Bathini static int rtas_fadump_register(struct fw_dump *fadump_conf) 100d3833a70SHari Bathini { 10141a65d16SHari Bathini unsigned int wait_time; 10241a65d16SHari Bathini int rc, err = -EIO; 10341a65d16SHari Bathini 10441a65d16SHari Bathini /* TODO: Add upper time limit for the delay */ 10541a65d16SHari Bathini do { 10641a65d16SHari Bathini rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 10741a65d16SHari Bathini NULL, FADUMP_REGISTER, &fdm, 10841a65d16SHari Bathini sizeof(struct rtas_fadump_mem_struct)); 10941a65d16SHari Bathini 11041a65d16SHari Bathini wait_time = rtas_busy_delay_time(rc); 11141a65d16SHari Bathini if (wait_time) 11241a65d16SHari Bathini mdelay(wait_time); 11341a65d16SHari Bathini 11441a65d16SHari Bathini } while (wait_time); 11541a65d16SHari Bathini 11641a65d16SHari Bathini switch (rc) { 11741a65d16SHari Bathini case 0: 11841a65d16SHari Bathini pr_info("Registration is successful!\n"); 11941a65d16SHari Bathini fadump_conf->dump_registered = 1; 12041a65d16SHari Bathini err = 0; 12141a65d16SHari Bathini break; 12241a65d16SHari Bathini case -1: 12341a65d16SHari Bathini pr_err("Failed to register. Hardware Error(%d).\n", rc); 12441a65d16SHari Bathini break; 12541a65d16SHari Bathini case -3: 12641a65d16SHari Bathini if (!is_fadump_boot_mem_contiguous()) 12741a65d16SHari Bathini pr_err("Can't have holes in boot memory area.\n"); 12841a65d16SHari Bathini else if (!is_fadump_reserved_mem_contiguous()) 12941a65d16SHari Bathini pr_err("Can't have holes in reserved memory area.\n"); 13041a65d16SHari Bathini 13141a65d16SHari Bathini pr_err("Failed to register. Parameter Error(%d).\n", rc); 13241a65d16SHari Bathini err = -EINVAL; 13341a65d16SHari Bathini break; 13441a65d16SHari Bathini case -9: 13541a65d16SHari Bathini pr_err("Already registered!\n"); 13641a65d16SHari Bathini fadump_conf->dump_registered = 1; 13741a65d16SHari Bathini err = -EEXIST; 13841a65d16SHari Bathini break; 13941a65d16SHari Bathini default: 14041a65d16SHari Bathini pr_err("Failed to register. Unknown Error(%d).\n", rc); 14141a65d16SHari Bathini break; 14241a65d16SHari Bathini } 14341a65d16SHari Bathini 14441a65d16SHari Bathini return err; 145d3833a70SHari Bathini } 146d3833a70SHari Bathini 147d3833a70SHari Bathini static int rtas_fadump_unregister(struct fw_dump *fadump_conf) 148d3833a70SHari Bathini { 14941a65d16SHari Bathini unsigned int wait_time; 15041a65d16SHari Bathini int rc; 15141a65d16SHari Bathini 15241a65d16SHari Bathini /* TODO: Add upper time limit for the delay */ 15341a65d16SHari Bathini do { 15441a65d16SHari Bathini rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 15541a65d16SHari Bathini NULL, FADUMP_UNREGISTER, &fdm, 15641a65d16SHari Bathini sizeof(struct rtas_fadump_mem_struct)); 15741a65d16SHari Bathini 15841a65d16SHari Bathini wait_time = rtas_busy_delay_time(rc); 15941a65d16SHari Bathini if (wait_time) 16041a65d16SHari Bathini mdelay(wait_time); 16141a65d16SHari Bathini } while (wait_time); 16241a65d16SHari Bathini 16341a65d16SHari Bathini if (rc) { 16441a65d16SHari Bathini pr_err("Failed to un-register - unexpected error(%d).\n", rc); 165d3833a70SHari Bathini return -EIO; 166d3833a70SHari Bathini } 167d3833a70SHari Bathini 16841a65d16SHari Bathini fadump_conf->dump_registered = 0; 16941a65d16SHari Bathini return 0; 17041a65d16SHari Bathini } 17141a65d16SHari Bathini 172d3833a70SHari Bathini static int rtas_fadump_invalidate(struct fw_dump *fadump_conf) 173d3833a70SHari Bathini { 174d3833a70SHari Bathini return -EIO; 175d3833a70SHari Bathini } 176d3833a70SHari Bathini 177d3833a70SHari Bathini /* 178d3833a70SHari Bathini * Validate and process the dump data stored by firmware before exporting 179d3833a70SHari Bathini * it through '/proc/vmcore'. 180d3833a70SHari Bathini */ 181d3833a70SHari Bathini static int __init rtas_fadump_process(struct fw_dump *fadump_conf) 182d3833a70SHari Bathini { 183d3833a70SHari Bathini return -EINVAL; 184d3833a70SHari Bathini } 185d3833a70SHari Bathini 186d3833a70SHari Bathini static void rtas_fadump_region_show(struct fw_dump *fadump_conf, 187d3833a70SHari Bathini struct seq_file *m) 188d3833a70SHari Bathini { 18941a65d16SHari Bathini const struct rtas_fadump_mem_struct *fdm_ptr = &fdm; 19041a65d16SHari Bathini const struct rtas_fadump_section *cpu_data_section; 19141a65d16SHari Bathini 19241a65d16SHari Bathini cpu_data_section = &(fdm_ptr->cpu_state_data); 19341a65d16SHari Bathini seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 19441a65d16SHari Bathini be64_to_cpu(cpu_data_section->destination_address), 19541a65d16SHari Bathini be64_to_cpu(cpu_data_section->destination_address) + 19641a65d16SHari Bathini be64_to_cpu(cpu_data_section->source_len) - 1, 19741a65d16SHari Bathini be64_to_cpu(cpu_data_section->source_len), 19841a65d16SHari Bathini be64_to_cpu(cpu_data_section->bytes_dumped)); 19941a65d16SHari Bathini 20041a65d16SHari Bathini seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 20141a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.destination_address), 20241a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.destination_address) + 20341a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1, 20441a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.source_len), 20541a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped)); 20641a65d16SHari Bathini 207*109f25ccSHari Bathini seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 208*109f25ccSHari Bathini be64_to_cpu(fdm_ptr->rmr_region.source_address), 209*109f25ccSHari Bathini be64_to_cpu(fdm_ptr->rmr_region.destination_address)); 210*109f25ccSHari Bathini seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 21141a65d16SHari Bathini be64_to_cpu(fdm_ptr->rmr_region.source_len), 21241a65d16SHari Bathini be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped)); 213d3833a70SHari Bathini } 214d3833a70SHari Bathini 215d3833a70SHari Bathini static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh, 216d3833a70SHari Bathini const char *msg) 217d3833a70SHari Bathini { 218d3833a70SHari Bathini /* Call ibm,os-term rtas call to trigger firmware assisted dump */ 219d3833a70SHari Bathini rtas_os_term((char *)msg); 220d3833a70SHari Bathini } 221d3833a70SHari Bathini 222d3833a70SHari Bathini static struct fadump_ops rtas_fadump_ops = { 223d3833a70SHari Bathini .fadump_init_mem_struct = rtas_fadump_init_mem_struct, 224d3833a70SHari Bathini .fadump_register = rtas_fadump_register, 225d3833a70SHari Bathini .fadump_unregister = rtas_fadump_unregister, 226d3833a70SHari Bathini .fadump_invalidate = rtas_fadump_invalidate, 227d3833a70SHari Bathini .fadump_process = rtas_fadump_process, 228d3833a70SHari Bathini .fadump_region_show = rtas_fadump_region_show, 229d3833a70SHari Bathini .fadump_trigger = rtas_fadump_trigger, 230d3833a70SHari Bathini }; 231d3833a70SHari Bathini 232d3833a70SHari Bathini void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 233d3833a70SHari Bathini { 234d3833a70SHari Bathini int i, size, num_sections; 235d3833a70SHari Bathini const __be32 *sections; 236d3833a70SHari Bathini const __be32 *token; 237d3833a70SHari Bathini 238d3833a70SHari Bathini /* 239d3833a70SHari Bathini * Check if Firmware Assisted dump is supported. if yes, check 240d3833a70SHari Bathini * if dump has been initiated on last reboot. 241d3833a70SHari Bathini */ 242d3833a70SHari Bathini token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); 243d3833a70SHari Bathini if (!token) 244d3833a70SHari Bathini return; 245d3833a70SHari Bathini 246d3833a70SHari Bathini fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token); 247d3833a70SHari Bathini fadump_conf->ops = &rtas_fadump_ops; 248d3833a70SHari Bathini fadump_conf->fadump_supported = 1; 249d3833a70SHari Bathini 250d3833a70SHari Bathini /* Get the sizes required to store dump data for the firmware provided 251d3833a70SHari Bathini * dump sections. 252d3833a70SHari Bathini * For each dump section type supported, a 32bit cell which defines 253d3833a70SHari Bathini * the ID of a supported section followed by two 32 bit cells which 254d3833a70SHari Bathini * gives the size of the section in bytes. 255d3833a70SHari Bathini */ 256d3833a70SHari Bathini sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", 257d3833a70SHari Bathini &size); 258d3833a70SHari Bathini 259d3833a70SHari Bathini if (!sections) 260d3833a70SHari Bathini return; 261d3833a70SHari Bathini 262d3833a70SHari Bathini num_sections = size / (3 * sizeof(u32)); 263d3833a70SHari Bathini 264d3833a70SHari Bathini for (i = 0; i < num_sections; i++, sections += 3) { 265d3833a70SHari Bathini u32 type = (u32)of_read_number(sections, 1); 266d3833a70SHari Bathini 267d3833a70SHari Bathini switch (type) { 268d3833a70SHari Bathini case RTAS_FADUMP_CPU_STATE_DATA: 269d3833a70SHari Bathini fadump_conf->cpu_state_data_size = 270d3833a70SHari Bathini of_read_ulong(§ions[1], 2); 271d3833a70SHari Bathini break; 272d3833a70SHari Bathini case RTAS_FADUMP_HPTE_REGION: 273d3833a70SHari Bathini fadump_conf->hpte_region_size = 274d3833a70SHari Bathini of_read_ulong(§ions[1], 2); 275d3833a70SHari Bathini break; 276d3833a70SHari Bathini } 277d3833a70SHari Bathini } 278d3833a70SHari Bathini } 279