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; 26f3512011SHari Bathini static const struct rtas_fadump_mem_struct *fdm_active; 2741a65d16SHari Bathini 2841a65d16SHari Bathini static void rtas_fadump_update_config(struct fw_dump *fadump_conf, 2941a65d16SHari Bathini const struct rtas_fadump_mem_struct *fdm) 3041a65d16SHari Bathini { 3141a65d16SHari Bathini fadump_conf->boot_mem_dest_addr = 3241a65d16SHari Bathini be64_to_cpu(fdm->rmr_region.destination_address); 3341a65d16SHari Bathini 3441a65d16SHari Bathini fadump_conf->fadumphdr_addr = (fadump_conf->boot_mem_dest_addr + 3541a65d16SHari Bathini fadump_conf->boot_memory_size); 3641a65d16SHari Bathini } 3741a65d16SHari Bathini 38f3512011SHari Bathini /* 39f3512011SHari Bathini * This function is called in the capture kernel to get configuration details 40f3512011SHari Bathini * setup in the first kernel and passed to the f/w. 41f3512011SHari Bathini */ 42e14ff96dSNick Child static void __init rtas_fadump_get_config(struct fw_dump *fadump_conf, 43f3512011SHari Bathini const struct rtas_fadump_mem_struct *fdm) 44f3512011SHari Bathini { 457dee93a9SHari Bathini fadump_conf->boot_mem_addr[0] = 467dee93a9SHari Bathini be64_to_cpu(fdm->rmr_region.source_address); 477dee93a9SHari Bathini fadump_conf->boot_mem_sz[0] = be64_to_cpu(fdm->rmr_region.source_len); 487dee93a9SHari Bathini fadump_conf->boot_memory_size = fadump_conf->boot_mem_sz[0]; 497dee93a9SHari Bathini 507dee93a9SHari Bathini fadump_conf->boot_mem_top = fadump_conf->boot_memory_size; 517dee93a9SHari Bathini fadump_conf->boot_mem_regs_cnt = 1; 52f3512011SHari Bathini 53f3512011SHari Bathini /* 54f3512011SHari Bathini * Start address of reserve dump area (permanent reservation) for 55f3512011SHari Bathini * re-registering FADump after dump capture. 56f3512011SHari Bathini */ 57f3512011SHari Bathini fadump_conf->reserve_dump_area_start = 58f3512011SHari Bathini be64_to_cpu(fdm->cpu_state_data.destination_address); 59f3512011SHari Bathini 60f3512011SHari Bathini rtas_fadump_update_config(fadump_conf, fdm); 61f3512011SHari Bathini } 62f3512011SHari Bathini 63d3833a70SHari Bathini static u64 rtas_fadump_init_mem_struct(struct fw_dump *fadump_conf) 64d3833a70SHari Bathini { 6541a65d16SHari Bathini u64 addr = fadump_conf->reserve_dump_area_start; 6641a65d16SHari Bathini 6741a65d16SHari Bathini memset(&fdm, 0, sizeof(struct rtas_fadump_mem_struct)); 6841a65d16SHari Bathini addr = addr & PAGE_MASK; 6941a65d16SHari Bathini 7041a65d16SHari Bathini fdm.header.dump_format_version = cpu_to_be32(0x00000001); 7141a65d16SHari Bathini fdm.header.dump_num_sections = cpu_to_be16(3); 7241a65d16SHari Bathini fdm.header.dump_status_flag = 0; 7341a65d16SHari Bathini fdm.header.offset_first_dump_section = 7441a65d16SHari Bathini cpu_to_be32((u32)offsetof(struct rtas_fadump_mem_struct, 7541a65d16SHari Bathini cpu_state_data)); 7641a65d16SHari Bathini 7741a65d16SHari Bathini /* 7841a65d16SHari Bathini * Fields for disk dump option. 7941a65d16SHari Bathini * We are not using disk dump option, hence set these fields to 0. 8041a65d16SHari Bathini */ 8141a65d16SHari Bathini fdm.header.dd_block_size = 0; 8241a65d16SHari Bathini fdm.header.dd_block_offset = 0; 8341a65d16SHari Bathini fdm.header.dd_num_blocks = 0; 8441a65d16SHari Bathini fdm.header.dd_offset_disk_path = 0; 8541a65d16SHari Bathini 8641a65d16SHari Bathini /* set 0 to disable an automatic dump-reboot. */ 8741a65d16SHari Bathini fdm.header.max_time_auto = 0; 8841a65d16SHari Bathini 8941a65d16SHari Bathini /* Kernel dump sections */ 9041a65d16SHari Bathini /* cpu state data section. */ 9141a65d16SHari Bathini fdm.cpu_state_data.request_flag = 9241a65d16SHari Bathini cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 9341a65d16SHari Bathini fdm.cpu_state_data.source_data_type = 9441a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_CPU_STATE_DATA); 9541a65d16SHari Bathini fdm.cpu_state_data.source_address = 0; 9641a65d16SHari Bathini fdm.cpu_state_data.source_len = 9741a65d16SHari Bathini cpu_to_be64(fadump_conf->cpu_state_data_size); 9841a65d16SHari Bathini fdm.cpu_state_data.destination_address = cpu_to_be64(addr); 9941a65d16SHari Bathini addr += fadump_conf->cpu_state_data_size; 10041a65d16SHari Bathini 10141a65d16SHari Bathini /* hpte region section */ 10241a65d16SHari Bathini fdm.hpte_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 10341a65d16SHari Bathini fdm.hpte_region.source_data_type = 10441a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_HPTE_REGION); 10541a65d16SHari Bathini fdm.hpte_region.source_address = 0; 10641a65d16SHari Bathini fdm.hpte_region.source_len = 10741a65d16SHari Bathini cpu_to_be64(fadump_conf->hpte_region_size); 10841a65d16SHari Bathini fdm.hpte_region.destination_address = cpu_to_be64(addr); 10941a65d16SHari Bathini addr += fadump_conf->hpte_region_size; 11041a65d16SHari Bathini 1119cf3b3a3SHari Bathini /* 1129cf3b3a3SHari Bathini * Align boot memory area destination address to page boundary to 1139cf3b3a3SHari Bathini * be able to mmap read this area in the vmcore. 1149cf3b3a3SHari Bathini */ 1159cf3b3a3SHari Bathini addr = PAGE_ALIGN(addr); 1169cf3b3a3SHari Bathini 11741a65d16SHari Bathini /* RMA region section */ 11841a65d16SHari Bathini fdm.rmr_region.request_flag = cpu_to_be32(RTAS_FADUMP_REQUEST_FLAG); 11941a65d16SHari Bathini fdm.rmr_region.source_data_type = 12041a65d16SHari Bathini cpu_to_be16(RTAS_FADUMP_REAL_MODE_REGION); 121becd91d9SHari Bathini fdm.rmr_region.source_address = cpu_to_be64(0); 12241a65d16SHari Bathini fdm.rmr_region.source_len = cpu_to_be64(fadump_conf->boot_memory_size); 12341a65d16SHari Bathini fdm.rmr_region.destination_address = cpu_to_be64(addr); 12441a65d16SHari Bathini addr += fadump_conf->boot_memory_size; 12541a65d16SHari Bathini 12641a65d16SHari Bathini rtas_fadump_update_config(fadump_conf, &fdm); 12741a65d16SHari Bathini 12841a65d16SHari Bathini return addr; 129d3833a70SHari Bathini } 130d3833a70SHari Bathini 1317b1b3b48SHari Bathini static u64 rtas_fadump_get_bootmem_min(void) 1327b1b3b48SHari Bathini { 1337b1b3b48SHari Bathini return RTAS_FADUMP_MIN_BOOT_MEM; 1347b1b3b48SHari Bathini } 1357b1b3b48SHari Bathini 136d3833a70SHari Bathini static int rtas_fadump_register(struct fw_dump *fadump_conf) 137d3833a70SHari Bathini { 13841a65d16SHari Bathini unsigned int wait_time; 13941a65d16SHari Bathini int rc, err = -EIO; 14041a65d16SHari Bathini 14141a65d16SHari Bathini /* TODO: Add upper time limit for the delay */ 14241a65d16SHari Bathini do { 14341a65d16SHari Bathini rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 14441a65d16SHari Bathini NULL, FADUMP_REGISTER, &fdm, 14541a65d16SHari Bathini sizeof(struct rtas_fadump_mem_struct)); 14641a65d16SHari Bathini 14741a65d16SHari Bathini wait_time = rtas_busy_delay_time(rc); 14841a65d16SHari Bathini if (wait_time) 14941a65d16SHari Bathini mdelay(wait_time); 15041a65d16SHari Bathini 15141a65d16SHari Bathini } while (wait_time); 15241a65d16SHari Bathini 15341a65d16SHari Bathini switch (rc) { 15441a65d16SHari Bathini case 0: 15541a65d16SHari Bathini pr_info("Registration is successful!\n"); 15641a65d16SHari Bathini fadump_conf->dump_registered = 1; 15741a65d16SHari Bathini err = 0; 15841a65d16SHari Bathini break; 15941a65d16SHari Bathini case -1: 16041a65d16SHari Bathini pr_err("Failed to register. Hardware Error(%d).\n", rc); 16141a65d16SHari Bathini break; 16241a65d16SHari Bathini case -3: 16341a65d16SHari Bathini if (!is_fadump_boot_mem_contiguous()) 16441a65d16SHari Bathini pr_err("Can't have holes in boot memory area.\n"); 16541a65d16SHari Bathini else if (!is_fadump_reserved_mem_contiguous()) 16641a65d16SHari Bathini pr_err("Can't have holes in reserved memory area.\n"); 16741a65d16SHari Bathini 16841a65d16SHari Bathini pr_err("Failed to register. Parameter Error(%d).\n", rc); 16941a65d16SHari Bathini err = -EINVAL; 17041a65d16SHari Bathini break; 17141a65d16SHari Bathini case -9: 17241a65d16SHari Bathini pr_err("Already registered!\n"); 17341a65d16SHari Bathini fadump_conf->dump_registered = 1; 17441a65d16SHari Bathini err = -EEXIST; 17541a65d16SHari Bathini break; 17641a65d16SHari Bathini default: 17741a65d16SHari Bathini pr_err("Failed to register. Unknown Error(%d).\n", rc); 17841a65d16SHari Bathini break; 17941a65d16SHari Bathini } 18041a65d16SHari Bathini 18141a65d16SHari Bathini return err; 182d3833a70SHari Bathini } 183d3833a70SHari Bathini 184d3833a70SHari Bathini static int rtas_fadump_unregister(struct fw_dump *fadump_conf) 185d3833a70SHari Bathini { 18641a65d16SHari Bathini unsigned int wait_time; 18741a65d16SHari Bathini int rc; 18841a65d16SHari Bathini 18941a65d16SHari Bathini /* TODO: Add upper time limit for the delay */ 19041a65d16SHari Bathini do { 19141a65d16SHari Bathini rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 19241a65d16SHari Bathini NULL, FADUMP_UNREGISTER, &fdm, 19341a65d16SHari Bathini sizeof(struct rtas_fadump_mem_struct)); 19441a65d16SHari Bathini 19541a65d16SHari Bathini wait_time = rtas_busy_delay_time(rc); 19641a65d16SHari Bathini if (wait_time) 19741a65d16SHari Bathini mdelay(wait_time); 19841a65d16SHari Bathini } while (wait_time); 19941a65d16SHari Bathini 20041a65d16SHari Bathini if (rc) { 20141a65d16SHari Bathini pr_err("Failed to un-register - unexpected error(%d).\n", rc); 202d3833a70SHari Bathini return -EIO; 203d3833a70SHari Bathini } 204d3833a70SHari Bathini 20541a65d16SHari Bathini fadump_conf->dump_registered = 0; 20641a65d16SHari Bathini return 0; 20741a65d16SHari Bathini } 20841a65d16SHari Bathini 209d3833a70SHari Bathini static int rtas_fadump_invalidate(struct fw_dump *fadump_conf) 210d3833a70SHari Bathini { 211f3512011SHari Bathini unsigned int wait_time; 212f3512011SHari Bathini int rc; 213f3512011SHari Bathini 214f3512011SHari Bathini /* TODO: Add upper time limit for the delay */ 215f3512011SHari Bathini do { 216f3512011SHari Bathini rc = rtas_call(fadump_conf->ibm_configure_kernel_dump, 3, 1, 217f3512011SHari Bathini NULL, FADUMP_INVALIDATE, fdm_active, 218f3512011SHari Bathini sizeof(struct rtas_fadump_mem_struct)); 219f3512011SHari Bathini 220f3512011SHari Bathini wait_time = rtas_busy_delay_time(rc); 221f3512011SHari Bathini if (wait_time) 222f3512011SHari Bathini mdelay(wait_time); 223f3512011SHari Bathini } while (wait_time); 224f3512011SHari Bathini 225f3512011SHari Bathini if (rc) { 226f3512011SHari Bathini pr_err("Failed to invalidate - unexpected error (%d).\n", rc); 227d3833a70SHari Bathini return -EIO; 228d3833a70SHari Bathini } 229d3833a70SHari Bathini 230f3512011SHari Bathini fadump_conf->dump_active = 0; 231f3512011SHari Bathini fdm_active = NULL; 232f3512011SHari Bathini return 0; 233f3512011SHari Bathini } 234f3512011SHari Bathini 235f3512011SHari Bathini #define RTAS_FADUMP_GPR_MASK 0xffffff0000000000 236f3512011SHari Bathini static inline int rtas_fadump_gpr_index(u64 id) 237f3512011SHari Bathini { 238f3512011SHari Bathini char str[3]; 239f3512011SHari Bathini int i = -1; 240f3512011SHari Bathini 241f3512011SHari Bathini if ((id & RTAS_FADUMP_GPR_MASK) == fadump_str_to_u64("GPR")) { 242f3512011SHari Bathini /* get the digits at the end */ 243f3512011SHari Bathini id &= ~RTAS_FADUMP_GPR_MASK; 244f3512011SHari Bathini id >>= 24; 245f3512011SHari Bathini str[2] = '\0'; 246f3512011SHari Bathini str[1] = id & 0xff; 247f3512011SHari Bathini str[0] = (id >> 8) & 0xff; 248f3512011SHari Bathini if (kstrtoint(str, 10, &i)) 249f3512011SHari Bathini i = -EINVAL; 250f3512011SHari Bathini if (i > 31) 251f3512011SHari Bathini i = -1; 252f3512011SHari Bathini } 253f3512011SHari Bathini return i; 254f3512011SHari Bathini } 255f3512011SHari Bathini 256e14ff96dSNick Child static void __init rtas_fadump_set_regval(struct pt_regs *regs, u64 reg_id, u64 reg_val) 257f3512011SHari Bathini { 258f3512011SHari Bathini int i; 259f3512011SHari Bathini 260f3512011SHari Bathini i = rtas_fadump_gpr_index(reg_id); 261f3512011SHari Bathini if (i >= 0) 262f3512011SHari Bathini regs->gpr[i] = (unsigned long)reg_val; 263f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("NIA")) 264f3512011SHari Bathini regs->nip = (unsigned long)reg_val; 265f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("MSR")) 266f3512011SHari Bathini regs->msr = (unsigned long)reg_val; 267f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("CTR")) 268f3512011SHari Bathini regs->ctr = (unsigned long)reg_val; 269f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("LR")) 270f3512011SHari Bathini regs->link = (unsigned long)reg_val; 271f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("XER")) 272f3512011SHari Bathini regs->xer = (unsigned long)reg_val; 273f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("CR")) 274f3512011SHari Bathini regs->ccr = (unsigned long)reg_val; 275f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("DAR")) 276f3512011SHari Bathini regs->dar = (unsigned long)reg_val; 277f3512011SHari Bathini else if (reg_id == fadump_str_to_u64("DSISR")) 278f3512011SHari Bathini regs->dsisr = (unsigned long)reg_val; 279f3512011SHari Bathini } 280f3512011SHari Bathini 281e14ff96dSNick Child static struct rtas_fadump_reg_entry* __init 282f3512011SHari Bathini rtas_fadump_read_regs(struct rtas_fadump_reg_entry *reg_entry, 283f3512011SHari Bathini struct pt_regs *regs) 284f3512011SHari Bathini { 285f3512011SHari Bathini memset(regs, 0, sizeof(struct pt_regs)); 286f3512011SHari Bathini 287f3512011SHari Bathini while (be64_to_cpu(reg_entry->reg_id) != fadump_str_to_u64("CPUEND")) { 288f3512011SHari Bathini rtas_fadump_set_regval(regs, be64_to_cpu(reg_entry->reg_id), 289f3512011SHari Bathini be64_to_cpu(reg_entry->reg_value)); 290f3512011SHari Bathini reg_entry++; 291f3512011SHari Bathini } 292f3512011SHari Bathini reg_entry++; 293f3512011SHari Bathini return reg_entry; 294f3512011SHari Bathini } 295f3512011SHari Bathini 296f3512011SHari Bathini /* 297f3512011SHari Bathini * Read CPU state dump data and convert it into ELF notes. 298f3512011SHari Bathini * The CPU dump starts with magic number "REGSAVE". NumCpusOffset should be 299f3512011SHari Bathini * used to access the data to allow for additional fields to be added without 300f3512011SHari Bathini * affecting compatibility. Each list of registers for a CPU starts with 301f3512011SHari Bathini * "CPUSTRT" and ends with "CPUEND". Each register entry is of 16 bytes, 302f3512011SHari Bathini * 8 Byte ASCII identifier and 8 Byte register value. The register entry 303f3512011SHari Bathini * with identifier "CPUSTRT" and "CPUEND" contains 4 byte cpu id as part 304f3512011SHari Bathini * of register value. For more details refer to PAPR document. 305f3512011SHari Bathini * 306f3512011SHari Bathini * Only for the crashing cpu we ignore the CPU dump data and get exact 307f3512011SHari Bathini * state from fadump crash info structure populated by first kernel at the 308f3512011SHari Bathini * time of crash. 309f3512011SHari Bathini */ 310f3512011SHari Bathini static int __init rtas_fadump_build_cpu_notes(struct fw_dump *fadump_conf) 311f3512011SHari Bathini { 312f3512011SHari Bathini struct rtas_fadump_reg_save_area_header *reg_header; 313f3512011SHari Bathini struct fadump_crash_info_header *fdh = NULL; 314f3512011SHari Bathini struct rtas_fadump_reg_entry *reg_entry; 315f3512011SHari Bathini u32 num_cpus, *note_buf; 316f3512011SHari Bathini int i, rc = 0, cpu = 0; 317f3512011SHari Bathini struct pt_regs regs; 318f3512011SHari Bathini unsigned long addr; 319f3512011SHari Bathini void *vaddr; 320f3512011SHari Bathini 321f3512011SHari Bathini addr = be64_to_cpu(fdm_active->cpu_state_data.destination_address); 322f3512011SHari Bathini vaddr = __va(addr); 323f3512011SHari Bathini 324f3512011SHari Bathini reg_header = vaddr; 325f3512011SHari Bathini if (be64_to_cpu(reg_header->magic_number) != 326f3512011SHari Bathini fadump_str_to_u64("REGSAVE")) { 327f3512011SHari Bathini pr_err("Unable to read register save area.\n"); 328f3512011SHari Bathini return -ENOENT; 329f3512011SHari Bathini } 330f3512011SHari Bathini 331f3512011SHari Bathini pr_debug("--------CPU State Data------------\n"); 332f3512011SHari Bathini pr_debug("Magic Number: %llx\n", be64_to_cpu(reg_header->magic_number)); 333f3512011SHari Bathini pr_debug("NumCpuOffset: %x\n", be32_to_cpu(reg_header->num_cpu_offset)); 334f3512011SHari Bathini 335f3512011SHari Bathini vaddr += be32_to_cpu(reg_header->num_cpu_offset); 336f3512011SHari Bathini num_cpus = be32_to_cpu(*((__be32 *)(vaddr))); 337f3512011SHari Bathini pr_debug("NumCpus : %u\n", num_cpus); 338f3512011SHari Bathini vaddr += sizeof(u32); 339f3512011SHari Bathini reg_entry = (struct rtas_fadump_reg_entry *)vaddr; 340f3512011SHari Bathini 341f3512011SHari Bathini rc = fadump_setup_cpu_notes_buf(num_cpus); 342f3512011SHari Bathini if (rc != 0) 343f3512011SHari Bathini return rc; 344f3512011SHari Bathini 345f3512011SHari Bathini note_buf = (u32 *)fadump_conf->cpu_notes_buf_vaddr; 346f3512011SHari Bathini 347f3512011SHari Bathini if (fadump_conf->fadumphdr_addr) 348f3512011SHari Bathini fdh = __va(fadump_conf->fadumphdr_addr); 349f3512011SHari Bathini 350f3512011SHari Bathini for (i = 0; i < num_cpus; i++) { 351f3512011SHari Bathini if (be64_to_cpu(reg_entry->reg_id) != 352f3512011SHari Bathini fadump_str_to_u64("CPUSTRT")) { 353f3512011SHari Bathini pr_err("Unable to read CPU state data\n"); 354f3512011SHari Bathini rc = -ENOENT; 355f3512011SHari Bathini goto error_out; 356f3512011SHari Bathini } 357f3512011SHari Bathini /* Lower 4 bytes of reg_value contains logical cpu id */ 358f3512011SHari Bathini cpu = (be64_to_cpu(reg_entry->reg_value) & 359f3512011SHari Bathini RTAS_FADUMP_CPU_ID_MASK); 3606584cec0SHari Bathini if (fdh && !cpumask_test_cpu(cpu, &fdh->cpu_mask)) { 361f3512011SHari Bathini RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); 362f3512011SHari Bathini continue; 363f3512011SHari Bathini } 364f3512011SHari Bathini pr_debug("Reading register data for cpu %d...\n", cpu); 365f3512011SHari Bathini if (fdh && fdh->crashing_cpu == cpu) { 366f3512011SHari Bathini regs = fdh->regs; 367f3512011SHari Bathini note_buf = fadump_regs_to_elf_notes(note_buf, ®s); 368f3512011SHari Bathini RTAS_FADUMP_SKIP_TO_NEXT_CPU(reg_entry); 369f3512011SHari Bathini } else { 370f3512011SHari Bathini reg_entry++; 371f3512011SHari Bathini reg_entry = rtas_fadump_read_regs(reg_entry, ®s); 372f3512011SHari Bathini note_buf = fadump_regs_to_elf_notes(note_buf, ®s); 373f3512011SHari Bathini } 374f3512011SHari Bathini } 375f3512011SHari Bathini final_note(note_buf); 376f3512011SHari Bathini 377f3512011SHari Bathini if (fdh) { 378f3512011SHari Bathini pr_debug("Updating elfcore header (%llx) with cpu notes\n", 379f3512011SHari Bathini fdh->elfcorehdr_addr); 380f3512011SHari Bathini fadump_update_elfcore_header(__va(fdh->elfcorehdr_addr)); 381f3512011SHari Bathini } 382f3512011SHari Bathini return 0; 383f3512011SHari Bathini 384f3512011SHari Bathini error_out: 385f3512011SHari Bathini fadump_free_cpu_notes_buf(); 386f3512011SHari Bathini return rc; 387f3512011SHari Bathini 388f3512011SHari Bathini } 389f3512011SHari Bathini 390d3833a70SHari Bathini /* 391d3833a70SHari Bathini * Validate and process the dump data stored by firmware before exporting 392d3833a70SHari Bathini * it through '/proc/vmcore'. 393d3833a70SHari Bathini */ 394d3833a70SHari Bathini static int __init rtas_fadump_process(struct fw_dump *fadump_conf) 395d3833a70SHari Bathini { 396f3512011SHari Bathini struct fadump_crash_info_header *fdh; 397f3512011SHari Bathini int rc = 0; 398f3512011SHari Bathini 399f3512011SHari Bathini if (!fdm_active || !fadump_conf->fadumphdr_addr) 400d3833a70SHari Bathini return -EINVAL; 401f3512011SHari Bathini 402f3512011SHari Bathini /* Check if the dump data is valid. */ 403f3512011SHari Bathini if ((be16_to_cpu(fdm_active->header.dump_status_flag) == 404f3512011SHari Bathini RTAS_FADUMP_ERROR_FLAG) || 405f3512011SHari Bathini (fdm_active->cpu_state_data.error_flags != 0) || 406f3512011SHari Bathini (fdm_active->rmr_region.error_flags != 0)) { 407f3512011SHari Bathini pr_err("Dump taken by platform is not valid\n"); 408f3512011SHari Bathini return -EINVAL; 409f3512011SHari Bathini } 410f3512011SHari Bathini if ((fdm_active->rmr_region.bytes_dumped != 411f3512011SHari Bathini fdm_active->rmr_region.source_len) || 412f3512011SHari Bathini !fdm_active->cpu_state_data.bytes_dumped) { 413f3512011SHari Bathini pr_err("Dump taken by platform is incomplete\n"); 414f3512011SHari Bathini return -EINVAL; 415f3512011SHari Bathini } 416f3512011SHari Bathini 417f3512011SHari Bathini /* Validate the fadump crash info header */ 418f3512011SHari Bathini fdh = __va(fadump_conf->fadumphdr_addr); 419f3512011SHari Bathini if (fdh->magic_number != FADUMP_CRASH_INFO_MAGIC) { 420f3512011SHari Bathini pr_err("Crash info header is not valid.\n"); 421f3512011SHari Bathini return -EINVAL; 422f3512011SHari Bathini } 423f3512011SHari Bathini 424f3512011SHari Bathini rc = rtas_fadump_build_cpu_notes(fadump_conf); 425f3512011SHari Bathini if (rc) 426f3512011SHari Bathini return rc; 427f3512011SHari Bathini 428f3512011SHari Bathini /* 429f3512011SHari Bathini * We are done validating dump info and elfcore header is now ready 430f3512011SHari Bathini * to be exported. set elfcorehdr_addr so that vmcore module will 431f3512011SHari Bathini * export the elfcore header through '/proc/vmcore'. 432f3512011SHari Bathini */ 433f3512011SHari Bathini elfcorehdr_addr = fdh->elfcorehdr_addr; 434f3512011SHari Bathini 435f3512011SHari Bathini return 0; 436d3833a70SHari Bathini } 437d3833a70SHari Bathini 438d3833a70SHari Bathini static void rtas_fadump_region_show(struct fw_dump *fadump_conf, 439d3833a70SHari Bathini struct seq_file *m) 440d3833a70SHari Bathini { 44141a65d16SHari Bathini const struct rtas_fadump_section *cpu_data_section; 442f3512011SHari Bathini const struct rtas_fadump_mem_struct *fdm_ptr; 443f3512011SHari Bathini 444f3512011SHari Bathini if (fdm_active) 445f3512011SHari Bathini fdm_ptr = fdm_active; 446f3512011SHari Bathini else 447f3512011SHari Bathini fdm_ptr = &fdm; 44841a65d16SHari Bathini 44941a65d16SHari Bathini cpu_data_section = &(fdm_ptr->cpu_state_data); 45041a65d16SHari Bathini seq_printf(m, "CPU :[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 45141a65d16SHari Bathini be64_to_cpu(cpu_data_section->destination_address), 45241a65d16SHari Bathini be64_to_cpu(cpu_data_section->destination_address) + 45341a65d16SHari Bathini be64_to_cpu(cpu_data_section->source_len) - 1, 45441a65d16SHari Bathini be64_to_cpu(cpu_data_section->source_len), 45541a65d16SHari Bathini be64_to_cpu(cpu_data_section->bytes_dumped)); 45641a65d16SHari Bathini 45741a65d16SHari Bathini seq_printf(m, "HPTE:[%#016llx-%#016llx] %#llx bytes, Dumped: %#llx\n", 45841a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.destination_address), 45941a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.destination_address) + 46041a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.source_len) - 1, 46141a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.source_len), 46241a65d16SHari Bathini be64_to_cpu(fdm_ptr->hpte_region.bytes_dumped)); 46341a65d16SHari Bathini 464109f25ccSHari Bathini seq_printf(m, "DUMP: Src: %#016llx, Dest: %#016llx, ", 465109f25ccSHari Bathini be64_to_cpu(fdm_ptr->rmr_region.source_address), 466109f25ccSHari Bathini be64_to_cpu(fdm_ptr->rmr_region.destination_address)); 467109f25ccSHari Bathini seq_printf(m, "Size: %#llx, Dumped: %#llx bytes\n", 46841a65d16SHari Bathini be64_to_cpu(fdm_ptr->rmr_region.source_len), 46941a65d16SHari Bathini be64_to_cpu(fdm_ptr->rmr_region.bytes_dumped)); 470f3512011SHari Bathini 471*a3ceb588SHari Bathini /* Dump is active. Show preserved area start address. */ 472f3512011SHari Bathini if (fdm_active) { 473*a3ceb588SHari Bathini seq_printf(m, "\nMemory above %#016llx is reserved for saving crash dump\n", 474*a3ceb588SHari Bathini fadump_conf->boot_mem_top); 475f3512011SHari Bathini } 476d3833a70SHari Bathini } 477d3833a70SHari Bathini 478d3833a70SHari Bathini static void rtas_fadump_trigger(struct fadump_crash_info_header *fdh, 479d3833a70SHari Bathini const char *msg) 480d3833a70SHari Bathini { 481d3833a70SHari Bathini /* Call ibm,os-term rtas call to trigger firmware assisted dump */ 482d3833a70SHari Bathini rtas_os_term((char *)msg); 483d3833a70SHari Bathini } 484d3833a70SHari Bathini 485d3833a70SHari Bathini static struct fadump_ops rtas_fadump_ops = { 486d3833a70SHari Bathini .fadump_init_mem_struct = rtas_fadump_init_mem_struct, 4877b1b3b48SHari Bathini .fadump_get_bootmem_min = rtas_fadump_get_bootmem_min, 488d3833a70SHari Bathini .fadump_register = rtas_fadump_register, 489d3833a70SHari Bathini .fadump_unregister = rtas_fadump_unregister, 490d3833a70SHari Bathini .fadump_invalidate = rtas_fadump_invalidate, 491d3833a70SHari Bathini .fadump_process = rtas_fadump_process, 492d3833a70SHari Bathini .fadump_region_show = rtas_fadump_region_show, 493d3833a70SHari Bathini .fadump_trigger = rtas_fadump_trigger, 494d3833a70SHari Bathini }; 495d3833a70SHari Bathini 496d3833a70SHari Bathini void __init rtas_fadump_dt_scan(struct fw_dump *fadump_conf, u64 node) 497d3833a70SHari Bathini { 498d3833a70SHari Bathini int i, size, num_sections; 499d3833a70SHari Bathini const __be32 *sections; 500d3833a70SHari Bathini const __be32 *token; 501d3833a70SHari Bathini 502d3833a70SHari Bathini /* 503d3833a70SHari Bathini * Check if Firmware Assisted dump is supported. if yes, check 504d3833a70SHari Bathini * if dump has been initiated on last reboot. 505d3833a70SHari Bathini */ 506d3833a70SHari Bathini token = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump", NULL); 507d3833a70SHari Bathini if (!token) 508d3833a70SHari Bathini return; 509d3833a70SHari Bathini 510d3833a70SHari Bathini fadump_conf->ibm_configure_kernel_dump = be32_to_cpu(*token); 511d3833a70SHari Bathini fadump_conf->ops = &rtas_fadump_ops; 512d3833a70SHari Bathini fadump_conf->fadump_supported = 1; 513d3833a70SHari Bathini 5147dee93a9SHari Bathini /* Firmware supports 64-bit value for size, align it to pagesize. */ 515e96d904eSChristophe Leroy fadump_conf->max_copy_size = ALIGN_DOWN(U64_MAX, PAGE_SIZE); 5167dee93a9SHari Bathini 517f3512011SHari Bathini /* 518f3512011SHari Bathini * The 'ibm,kernel-dump' rtas node is present only if there is 519f3512011SHari Bathini * dump data waiting for us. 520f3512011SHari Bathini */ 521f3512011SHari Bathini fdm_active = of_get_flat_dt_prop(node, "ibm,kernel-dump", NULL); 522f3512011SHari Bathini if (fdm_active) { 523f3512011SHari Bathini pr_info("Firmware-assisted dump is active.\n"); 524f3512011SHari Bathini fadump_conf->dump_active = 1; 525f3512011SHari Bathini rtas_fadump_get_config(fadump_conf, (void *)__pa(fdm_active)); 526f3512011SHari Bathini } 527f3512011SHari Bathini 528d3833a70SHari Bathini /* Get the sizes required to store dump data for the firmware provided 529d3833a70SHari Bathini * dump sections. 530d3833a70SHari Bathini * For each dump section type supported, a 32bit cell which defines 531d3833a70SHari Bathini * the ID of a supported section followed by two 32 bit cells which 532d3833a70SHari Bathini * gives the size of the section in bytes. 533d3833a70SHari Bathini */ 534d3833a70SHari Bathini sections = of_get_flat_dt_prop(node, "ibm,configure-kernel-dump-sizes", 535d3833a70SHari Bathini &size); 536d3833a70SHari Bathini 537d3833a70SHari Bathini if (!sections) 538d3833a70SHari Bathini return; 539d3833a70SHari Bathini 540d3833a70SHari Bathini num_sections = size / (3 * sizeof(u32)); 541d3833a70SHari Bathini 542d3833a70SHari Bathini for (i = 0; i < num_sections; i++, sections += 3) { 543d3833a70SHari Bathini u32 type = (u32)of_read_number(sections, 1); 544d3833a70SHari Bathini 545d3833a70SHari Bathini switch (type) { 546d3833a70SHari Bathini case RTAS_FADUMP_CPU_STATE_DATA: 547d3833a70SHari Bathini fadump_conf->cpu_state_data_size = 548d3833a70SHari Bathini of_read_ulong(§ions[1], 2); 549d3833a70SHari Bathini break; 550d3833a70SHari Bathini case RTAS_FADUMP_HPTE_REGION: 551d3833a70SHari Bathini fadump_conf->hpte_region_size = 552d3833a70SHari Bathini of_read_ulong(§ions[1], 2); 553d3833a70SHari Bathini break; 554d3833a70SHari Bathini } 555d3833a70SHari Bathini } 556d3833a70SHari Bathini } 557