16f713d18SHari Bathini // SPDX-License-Identifier: GPL-2.0-only 26f713d18SHari Bathini /* 36f713d18SHari Bathini * Interface for exporting the OPAL ELF core. 46f713d18SHari Bathini * Heavily inspired from fs/proc/vmcore.c 56f713d18SHari Bathini * 66f713d18SHari Bathini * Copyright 2019, Hari Bathini, IBM Corporation. 76f713d18SHari Bathini */ 86f713d18SHari Bathini 96f713d18SHari Bathini #define pr_fmt(fmt) "opal core: " fmt 106f713d18SHari Bathini 116f713d18SHari Bathini #include <linux/memblock.h> 126f713d18SHari Bathini #include <linux/uaccess.h> 136f713d18SHari Bathini #include <linux/proc_fs.h> 146f713d18SHari Bathini #include <linux/elf.h> 156f713d18SHari Bathini #include <linux/elfcore.h> 16845426f3SHari Bathini #include <linux/kobject.h> 17845426f3SHari Bathini #include <linux/sysfs.h> 186f713d18SHari Bathini #include <linux/slab.h> 196f713d18SHari Bathini #include <linux/crash_core.h> 206f713d18SHari Bathini #include <linux/of.h> 216f713d18SHari Bathini 226f713d18SHari Bathini #include <asm/page.h> 236f713d18SHari Bathini #include <asm/opal.h> 246f713d18SHari Bathini #include <asm/fadump-internal.h> 256f713d18SHari Bathini 266f713d18SHari Bathini #include "opal-fadump.h" 276f713d18SHari Bathini 286f713d18SHari Bathini #define MAX_PT_LOAD_CNT 8 296f713d18SHari Bathini 306f713d18SHari Bathini /* NT_AUXV note related info */ 316f713d18SHari Bathini #define AUXV_CNT 1 326f713d18SHari Bathini #define AUXV_DESC_SZ (((2 * AUXV_CNT) + 1) * sizeof(Elf64_Off)) 336f713d18SHari Bathini 346f713d18SHari Bathini struct opalcore_config { 356f713d18SHari Bathini u32 num_cpus; 366f713d18SHari Bathini /* PIR value of crashing CPU */ 376f713d18SHari Bathini u32 crashing_cpu; 386f713d18SHari Bathini 396f713d18SHari Bathini /* CPU state data info from F/W */ 406f713d18SHari Bathini u64 cpu_state_destination_vaddr; 416f713d18SHari Bathini u64 cpu_state_data_size; 426f713d18SHari Bathini u64 cpu_state_entry_size; 436f713d18SHari Bathini 446f713d18SHari Bathini /* OPAL memory to be exported as PT_LOAD segments */ 456f713d18SHari Bathini u64 ptload_addr[MAX_PT_LOAD_CNT]; 466f713d18SHari Bathini u64 ptload_size[MAX_PT_LOAD_CNT]; 476f713d18SHari Bathini u64 ptload_cnt; 486f713d18SHari Bathini 496f713d18SHari Bathini /* Pointer to the first PT_LOAD in the ELF core file */ 506f713d18SHari Bathini Elf64_Phdr *ptload_phdr; 516f713d18SHari Bathini 526f713d18SHari Bathini /* Total size of opalcore file. */ 536f713d18SHari Bathini size_t opalcore_size; 546f713d18SHari Bathini 556f713d18SHari Bathini /* Buffer for all the ELF core headers and the PT_NOTE */ 566f713d18SHari Bathini size_t opalcorebuf_sz; 576f713d18SHari Bathini char *opalcorebuf; 586f713d18SHari Bathini 596f713d18SHari Bathini /* NT_AUXV buffer */ 606f713d18SHari Bathini char auxv_buf[AUXV_DESC_SZ]; 616f713d18SHari Bathini }; 626f713d18SHari Bathini 636f713d18SHari Bathini struct opalcore { 646f713d18SHari Bathini struct list_head list; 656f713d18SHari Bathini u64 paddr; 666f713d18SHari Bathini size_t size; 676f713d18SHari Bathini loff_t offset; 686f713d18SHari Bathini }; 696f713d18SHari Bathini 706f713d18SHari Bathini static LIST_HEAD(opalcore_list); 716f713d18SHari Bathini static struct opalcore_config *oc_conf; 726f713d18SHari Bathini static const struct opal_mpipl_fadump *opalc_metadata; 736f713d18SHari Bathini static const struct opal_mpipl_fadump *opalc_cpu_metadata; 74ff0b4155SBixuan Cui static struct kobject *mpipl_kobj; 756f713d18SHari Bathini 766f713d18SHari Bathini /* 776f713d18SHari Bathini * Set crashing CPU's signal to SIGUSR1. if the kernel is triggered 786f713d18SHari Bathini * by kernel, SIGTERM otherwise. 796f713d18SHari Bathini */ 806f713d18SHari Bathini bool kernel_initiated; 816f713d18SHari Bathini 826f713d18SHari Bathini static struct opalcore * __init get_new_element(void) 836f713d18SHari Bathini { 846f713d18SHari Bathini return kzalloc(sizeof(struct opalcore), GFP_KERNEL); 856f713d18SHari Bathini } 866f713d18SHari Bathini 876f713d18SHari Bathini static inline int is_opalcore_usable(void) 886f713d18SHari Bathini { 896f713d18SHari Bathini return (oc_conf && oc_conf->opalcorebuf != NULL) ? 1 : 0; 906f713d18SHari Bathini } 916f713d18SHari Bathini 92e5913db1SNick Child static Elf64_Word *__init append_elf64_note(Elf64_Word *buf, char *name, 936f713d18SHari Bathini u32 type, void *data, 946f713d18SHari Bathini size_t data_len) 956f713d18SHari Bathini { 966f713d18SHari Bathini Elf64_Nhdr *note = (Elf64_Nhdr *)buf; 976f713d18SHari Bathini Elf64_Word namesz = strlen(name) + 1; 986f713d18SHari Bathini 996f713d18SHari Bathini note->n_namesz = cpu_to_be32(namesz); 1006f713d18SHari Bathini note->n_descsz = cpu_to_be32(data_len); 1016f713d18SHari Bathini note->n_type = cpu_to_be32(type); 1026f713d18SHari Bathini buf += DIV_ROUND_UP(sizeof(*note), sizeof(Elf64_Word)); 1036f713d18SHari Bathini memcpy(buf, name, namesz); 1046f713d18SHari Bathini buf += DIV_ROUND_UP(namesz, sizeof(Elf64_Word)); 1056f713d18SHari Bathini memcpy(buf, data, data_len); 1066f713d18SHari Bathini buf += DIV_ROUND_UP(data_len, sizeof(Elf64_Word)); 1076f713d18SHari Bathini 1086f713d18SHari Bathini return buf; 1096f713d18SHari Bathini } 1106f713d18SHari Bathini 111e5913db1SNick Child static void __init fill_prstatus(struct elf_prstatus *prstatus, int pir, 1126f713d18SHari Bathini struct pt_regs *regs) 1136f713d18SHari Bathini { 1146f713d18SHari Bathini memset(prstatus, 0, sizeof(struct elf_prstatus)); 1156f713d18SHari Bathini elf_core_copy_kernel_regs(&(prstatus->pr_reg), regs); 1166f713d18SHari Bathini 1176f713d18SHari Bathini /* 1186f713d18SHari Bathini * Overload PID with PIR value. 1196f713d18SHari Bathini * As a PIR value could also be '0', add an offset of '100' 1206f713d18SHari Bathini * to every PIR to avoid misinterpretations in GDB. 1216f713d18SHari Bathini */ 122f2485a2dSAl Viro prstatus->common.pr_pid = cpu_to_be32(100 + pir); 123f2485a2dSAl Viro prstatus->common.pr_ppid = cpu_to_be32(1); 1246f713d18SHari Bathini 1256f713d18SHari Bathini /* 1266f713d18SHari Bathini * Indicate SIGUSR1 for crash initiated from kernel. 1276f713d18SHari Bathini * SIGTERM otherwise. 1286f713d18SHari Bathini */ 1296f713d18SHari Bathini if (pir == oc_conf->crashing_cpu) { 1306f713d18SHari Bathini short sig; 1316f713d18SHari Bathini 1326f713d18SHari Bathini sig = kernel_initiated ? SIGUSR1 : SIGTERM; 133f2485a2dSAl Viro prstatus->common.pr_cursig = cpu_to_be16(sig); 1346f713d18SHari Bathini } 1356f713d18SHari Bathini } 1366f713d18SHari Bathini 137e5913db1SNick Child static Elf64_Word *__init auxv_to_elf64_notes(Elf64_Word *buf, 1386f713d18SHari Bathini u64 opal_boot_entry) 1396f713d18SHari Bathini { 1406f713d18SHari Bathini Elf64_Off *bufp = (Elf64_Off *)oc_conf->auxv_buf; 1416f713d18SHari Bathini int idx = 0; 1426f713d18SHari Bathini 1436f713d18SHari Bathini memset(bufp, 0, AUXV_DESC_SZ); 1446f713d18SHari Bathini 1456f713d18SHari Bathini /* Entry point of OPAL */ 1466f713d18SHari Bathini bufp[idx++] = cpu_to_be64(AT_ENTRY); 1476f713d18SHari Bathini bufp[idx++] = cpu_to_be64(opal_boot_entry); 1486f713d18SHari Bathini 1496f713d18SHari Bathini /* end of vector */ 1506f713d18SHari Bathini bufp[idx++] = cpu_to_be64(AT_NULL); 1516f713d18SHari Bathini 1526f713d18SHari Bathini buf = append_elf64_note(buf, CRASH_CORE_NOTE_NAME, NT_AUXV, 1536f713d18SHari Bathini oc_conf->auxv_buf, AUXV_DESC_SZ); 1546f713d18SHari Bathini return buf; 1556f713d18SHari Bathini } 1566f713d18SHari Bathini 1576f713d18SHari Bathini /* 1586f713d18SHari Bathini * Read from the ELF header and then the crash dump. 1596f713d18SHari Bathini * Returns number of bytes read on success, -errno on failure. 1606f713d18SHari Bathini */ 1616f713d18SHari Bathini static ssize_t read_opalcore(struct file *file, struct kobject *kobj, 1626f713d18SHari Bathini struct bin_attribute *bin_attr, char *to, 1636f713d18SHari Bathini loff_t pos, size_t count) 1646f713d18SHari Bathini { 1656f713d18SHari Bathini struct opalcore *m; 1666f713d18SHari Bathini ssize_t tsz, avail; 1676f713d18SHari Bathini loff_t tpos = pos; 1686f713d18SHari Bathini 1696f713d18SHari Bathini if (pos >= oc_conf->opalcore_size) 1706f713d18SHari Bathini return 0; 1716f713d18SHari Bathini 1726f713d18SHari Bathini /* Adjust count if it goes beyond opalcore size */ 1736f713d18SHari Bathini avail = oc_conf->opalcore_size - pos; 1746f713d18SHari Bathini if (count > avail) 1756f713d18SHari Bathini count = avail; 1766f713d18SHari Bathini 1776f713d18SHari Bathini if (count == 0) 1786f713d18SHari Bathini return 0; 1796f713d18SHari Bathini 1806f713d18SHari Bathini /* Read ELF core header and/or PT_NOTE segment */ 1816f713d18SHari Bathini if (tpos < oc_conf->opalcorebuf_sz) { 1826f713d18SHari Bathini tsz = min_t(size_t, oc_conf->opalcorebuf_sz - tpos, count); 1836f713d18SHari Bathini memcpy(to, oc_conf->opalcorebuf + tpos, tsz); 1846f713d18SHari Bathini to += tsz; 1856f713d18SHari Bathini tpos += tsz; 1866f713d18SHari Bathini count -= tsz; 1876f713d18SHari Bathini } 1886f713d18SHari Bathini 1896f713d18SHari Bathini list_for_each_entry(m, &opalcore_list, list) { 1906f713d18SHari Bathini /* nothing more to read here */ 1916f713d18SHari Bathini if (count == 0) 1926f713d18SHari Bathini break; 1936f713d18SHari Bathini 1946f713d18SHari Bathini if (tpos < m->offset + m->size) { 1956f713d18SHari Bathini void *addr; 1966f713d18SHari Bathini 1976f713d18SHari Bathini tsz = min_t(size_t, m->offset + m->size - tpos, count); 1986f713d18SHari Bathini addr = (void *)(m->paddr + tpos - m->offset); 1996f713d18SHari Bathini memcpy(to, __va(addr), tsz); 2006f713d18SHari Bathini to += tsz; 2016f713d18SHari Bathini tpos += tsz; 2026f713d18SHari Bathini count -= tsz; 2036f713d18SHari Bathini } 2046f713d18SHari Bathini } 2056f713d18SHari Bathini 2066f713d18SHari Bathini return (tpos - pos); 2076f713d18SHari Bathini } 2086f713d18SHari Bathini 2096f713d18SHari Bathini static struct bin_attribute opal_core_attr = { 2106f713d18SHari Bathini .attr = {.name = "core", .mode = 0400}, 2116f713d18SHari Bathini .read = read_opalcore 2126f713d18SHari Bathini }; 2136f713d18SHari Bathini 2146f713d18SHari Bathini /* 2156f713d18SHari Bathini * Read CPU state dump data and convert it into ELF notes. 2166f713d18SHari Bathini * 2176f713d18SHari Bathini * Each register entry is of 16 bytes, A numerical identifier along with 2186f713d18SHari Bathini * a GPR/SPR flag in the first 8 bytes and the register value in the next 2196f713d18SHari Bathini * 8 bytes. For more details refer to F/W documentation. 2206f713d18SHari Bathini */ 2216f713d18SHari Bathini static Elf64_Word * __init opalcore_append_cpu_notes(Elf64_Word *buf) 2226f713d18SHari Bathini { 2236f713d18SHari Bathini u32 thread_pir, size_per_thread, regs_offset, regs_cnt, reg_esize; 2246f713d18SHari Bathini struct hdat_fadump_thread_hdr *thdr; 2256f713d18SHari Bathini struct elf_prstatus prstatus; 2266f713d18SHari Bathini Elf64_Word *first_cpu_note; 2276f713d18SHari Bathini struct pt_regs regs; 2286f713d18SHari Bathini char *bufp; 2296f713d18SHari Bathini int i; 2306f713d18SHari Bathini 2316f713d18SHari Bathini size_per_thread = oc_conf->cpu_state_entry_size; 2326f713d18SHari Bathini bufp = __va(oc_conf->cpu_state_destination_vaddr); 2336f713d18SHari Bathini 2346f713d18SHari Bathini /* 2356f713d18SHari Bathini * Offset for register entries, entry size and registers count is 2366f713d18SHari Bathini * duplicated in every thread header in keeping with HDAT format. 2376f713d18SHari Bathini * Use these values from the first thread header. 2386f713d18SHari Bathini */ 2396f713d18SHari Bathini thdr = (struct hdat_fadump_thread_hdr *)bufp; 2406f713d18SHari Bathini regs_offset = (offsetof(struct hdat_fadump_thread_hdr, offset) + 2416f713d18SHari Bathini be32_to_cpu(thdr->offset)); 2426f713d18SHari Bathini reg_esize = be32_to_cpu(thdr->esize); 2436f713d18SHari Bathini regs_cnt = be32_to_cpu(thdr->ecnt); 2446f713d18SHari Bathini 2456f713d18SHari Bathini pr_debug("--------CPU State Data------------\n"); 2466f713d18SHari Bathini pr_debug("NumCpus : %u\n", oc_conf->num_cpus); 2476f713d18SHari Bathini pr_debug("\tOffset: %u, Entry size: %u, Cnt: %u\n", 2486f713d18SHari Bathini regs_offset, reg_esize, regs_cnt); 2496f713d18SHari Bathini 2506f713d18SHari Bathini /* 2516f713d18SHari Bathini * Skip past the first CPU note. Fill this note with the 2526f713d18SHari Bathini * crashing CPU's prstatus. 2536f713d18SHari Bathini */ 2546f713d18SHari Bathini first_cpu_note = buf; 2556f713d18SHari Bathini buf = append_elf64_note(buf, CRASH_CORE_NOTE_NAME, NT_PRSTATUS, 2566f713d18SHari Bathini &prstatus, sizeof(prstatus)); 2576f713d18SHari Bathini 2586f713d18SHari Bathini for (i = 0; i < oc_conf->num_cpus; i++, bufp += size_per_thread) { 2596f713d18SHari Bathini thdr = (struct hdat_fadump_thread_hdr *)bufp; 2606f713d18SHari Bathini thread_pir = be32_to_cpu(thdr->pir); 2616f713d18SHari Bathini 2626f713d18SHari Bathini pr_debug("[%04d] PIR: 0x%x, core state: 0x%02x\n", 2636f713d18SHari Bathini i, thread_pir, thdr->core_state); 2646f713d18SHari Bathini 2656f713d18SHari Bathini /* 2666f713d18SHari Bathini * Register state data of MAX cores is provided by firmware, 2676f713d18SHari Bathini * but some of this cores may not be active. So, while 2686f713d18SHari Bathini * processing register state data, check core state and 2696f713d18SHari Bathini * skip threads that belong to inactive cores. 2706f713d18SHari Bathini */ 2716f713d18SHari Bathini if (thdr->core_state == HDAT_FADUMP_CORE_INACTIVE) 2726f713d18SHari Bathini continue; 2736f713d18SHari Bathini 2746f713d18SHari Bathini opal_fadump_read_regs((bufp + regs_offset), regs_cnt, 2756f713d18SHari Bathini reg_esize, false, ®s); 2766f713d18SHari Bathini 2776f713d18SHari Bathini pr_debug("PIR 0x%x - R1 : 0x%llx, NIP : 0x%llx\n", thread_pir, 2786f713d18SHari Bathini be64_to_cpu(regs.gpr[1]), be64_to_cpu(regs.nip)); 2796f713d18SHari Bathini fill_prstatus(&prstatus, thread_pir, ®s); 2806f713d18SHari Bathini 2816f713d18SHari Bathini if (thread_pir != oc_conf->crashing_cpu) { 2826f713d18SHari Bathini buf = append_elf64_note(buf, CRASH_CORE_NOTE_NAME, 2836f713d18SHari Bathini NT_PRSTATUS, &prstatus, 2846f713d18SHari Bathini sizeof(prstatus)); 2856f713d18SHari Bathini } else { 2866f713d18SHari Bathini /* 2876f713d18SHari Bathini * Add crashing CPU as the first NT_PRSTATUS note for 2886f713d18SHari Bathini * GDB to process the core file appropriately. 2896f713d18SHari Bathini */ 2906f713d18SHari Bathini append_elf64_note(first_cpu_note, CRASH_CORE_NOTE_NAME, 2916f713d18SHari Bathini NT_PRSTATUS, &prstatus, 2926f713d18SHari Bathini sizeof(prstatus)); 2936f713d18SHari Bathini } 2946f713d18SHari Bathini } 2956f713d18SHari Bathini 2966f713d18SHari Bathini return buf; 2976f713d18SHari Bathini } 2986f713d18SHari Bathini 2996f713d18SHari Bathini static int __init create_opalcore(void) 3006f713d18SHari Bathini { 3016f713d18SHari Bathini u64 opal_boot_entry, opal_base_addr, paddr; 3026f713d18SHari Bathini u32 hdr_size, cpu_notes_size, count; 3036f713d18SHari Bathini struct device_node *dn; 3046f713d18SHari Bathini struct opalcore *new; 3056f713d18SHari Bathini loff_t opalcore_off; 3066f713d18SHari Bathini struct page *page; 3076f713d18SHari Bathini Elf64_Phdr *phdr; 3086f713d18SHari Bathini Elf64_Ehdr *elf; 3096f713d18SHari Bathini int i, ret; 3106f713d18SHari Bathini char *bufp; 3116f713d18SHari Bathini 3126f713d18SHari Bathini /* Get size of header & CPU notes for OPAL core */ 3136f713d18SHari Bathini hdr_size = (sizeof(Elf64_Ehdr) + 3146f713d18SHari Bathini ((oc_conf->ptload_cnt + 1) * sizeof(Elf64_Phdr))); 3156f713d18SHari Bathini cpu_notes_size = ((oc_conf->num_cpus * (CRASH_CORE_NOTE_HEAD_BYTES + 3166f713d18SHari Bathini CRASH_CORE_NOTE_NAME_BYTES + 3176f713d18SHari Bathini CRASH_CORE_NOTE_DESC_BYTES)) + 3186f713d18SHari Bathini (CRASH_CORE_NOTE_HEAD_BYTES + 3196f713d18SHari Bathini CRASH_CORE_NOTE_NAME_BYTES + AUXV_DESC_SZ)); 3206f713d18SHari Bathini 3216f713d18SHari Bathini /* Allocate buffer to setup OPAL core */ 3226f713d18SHari Bathini oc_conf->opalcorebuf_sz = PAGE_ALIGN(hdr_size + cpu_notes_size); 3236f713d18SHari Bathini oc_conf->opalcorebuf = alloc_pages_exact(oc_conf->opalcorebuf_sz, 3246f713d18SHari Bathini GFP_KERNEL | __GFP_ZERO); 3256f713d18SHari Bathini if (!oc_conf->opalcorebuf) { 3266f713d18SHari Bathini pr_err("Not enough memory to setup OPAL core (size: %lu)\n", 3276f713d18SHari Bathini oc_conf->opalcorebuf_sz); 3286f713d18SHari Bathini oc_conf->opalcorebuf_sz = 0; 3296f713d18SHari Bathini return -ENOMEM; 3306f713d18SHari Bathini } 3316f713d18SHari Bathini count = oc_conf->opalcorebuf_sz / PAGE_SIZE; 3326f713d18SHari Bathini page = virt_to_page(oc_conf->opalcorebuf); 3336f713d18SHari Bathini for (i = 0; i < count; i++) 3346f713d18SHari Bathini mark_page_reserved(page + i); 3356f713d18SHari Bathini 3366f713d18SHari Bathini pr_debug("opalcorebuf = 0x%llx\n", (u64)oc_conf->opalcorebuf); 3376f713d18SHari Bathini 3386f713d18SHari Bathini /* Read OPAL related device-tree entries */ 3396f713d18SHari Bathini dn = of_find_node_by_name(NULL, "ibm,opal"); 3406f713d18SHari Bathini if (dn) { 3416f713d18SHari Bathini ret = of_property_read_u64(dn, "opal-base-address", 3426f713d18SHari Bathini &opal_base_addr); 3436f713d18SHari Bathini pr_debug("opal-base-address: %llx\n", opal_base_addr); 3446f713d18SHari Bathini ret |= of_property_read_u64(dn, "opal-boot-address", 3456f713d18SHari Bathini &opal_boot_entry); 3466f713d18SHari Bathini pr_debug("opal-boot-address: %llx\n", opal_boot_entry); 3476f713d18SHari Bathini } 3486f713d18SHari Bathini if (!dn || ret) 3496f713d18SHari Bathini pr_warn("WARNING: Failed to read OPAL base & entry values\n"); 3506f713d18SHari Bathini 3516f713d18SHari Bathini /* Use count to keep track of the program headers */ 3526f713d18SHari Bathini count = 0; 3536f713d18SHari Bathini 3546f713d18SHari Bathini bufp = oc_conf->opalcorebuf; 3556f713d18SHari Bathini elf = (Elf64_Ehdr *)bufp; 3566f713d18SHari Bathini bufp += sizeof(Elf64_Ehdr); 3576f713d18SHari Bathini memcpy(elf->e_ident, ELFMAG, SELFMAG); 3586f713d18SHari Bathini elf->e_ident[EI_CLASS] = ELF_CLASS; 3596f713d18SHari Bathini elf->e_ident[EI_DATA] = ELFDATA2MSB; 3606f713d18SHari Bathini elf->e_ident[EI_VERSION] = EV_CURRENT; 3616f713d18SHari Bathini elf->e_ident[EI_OSABI] = ELF_OSABI; 3626f713d18SHari Bathini memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD); 3636f713d18SHari Bathini elf->e_type = cpu_to_be16(ET_CORE); 3646f713d18SHari Bathini elf->e_machine = cpu_to_be16(ELF_ARCH); 3656f713d18SHari Bathini elf->e_version = cpu_to_be32(EV_CURRENT); 3666f713d18SHari Bathini elf->e_entry = 0; 3676f713d18SHari Bathini elf->e_phoff = cpu_to_be64(sizeof(Elf64_Ehdr)); 3686f713d18SHari Bathini elf->e_shoff = 0; 3696f713d18SHari Bathini elf->e_flags = 0; 3706f713d18SHari Bathini 3716f713d18SHari Bathini elf->e_ehsize = cpu_to_be16(sizeof(Elf64_Ehdr)); 3726f713d18SHari Bathini elf->e_phentsize = cpu_to_be16(sizeof(Elf64_Phdr)); 3736f713d18SHari Bathini elf->e_phnum = 0; 3746f713d18SHari Bathini elf->e_shentsize = 0; 3756f713d18SHari Bathini elf->e_shnum = 0; 3766f713d18SHari Bathini elf->e_shstrndx = 0; 3776f713d18SHari Bathini 3786f713d18SHari Bathini phdr = (Elf64_Phdr *)bufp; 3796f713d18SHari Bathini bufp += sizeof(Elf64_Phdr); 3806f713d18SHari Bathini phdr->p_type = cpu_to_be32(PT_NOTE); 3816f713d18SHari Bathini phdr->p_flags = 0; 3826f713d18SHari Bathini phdr->p_align = 0; 3836f713d18SHari Bathini phdr->p_paddr = phdr->p_vaddr = 0; 3846f713d18SHari Bathini phdr->p_offset = cpu_to_be64(hdr_size); 3856f713d18SHari Bathini phdr->p_filesz = phdr->p_memsz = cpu_to_be64(cpu_notes_size); 3866f713d18SHari Bathini count++; 3876f713d18SHari Bathini 3886f713d18SHari Bathini opalcore_off = oc_conf->opalcorebuf_sz; 3896f713d18SHari Bathini oc_conf->ptload_phdr = (Elf64_Phdr *)bufp; 3906f713d18SHari Bathini paddr = 0; 3916f713d18SHari Bathini for (i = 0; i < oc_conf->ptload_cnt; i++) { 3926f713d18SHari Bathini phdr = (Elf64_Phdr *)bufp; 3936f713d18SHari Bathini bufp += sizeof(Elf64_Phdr); 3946f713d18SHari Bathini phdr->p_type = cpu_to_be32(PT_LOAD); 3956f713d18SHari Bathini phdr->p_flags = cpu_to_be32(PF_R|PF_W|PF_X); 3966f713d18SHari Bathini phdr->p_align = 0; 3976f713d18SHari Bathini 3986f713d18SHari Bathini new = get_new_element(); 3996f713d18SHari Bathini if (!new) 4006f713d18SHari Bathini return -ENOMEM; 4016f713d18SHari Bathini new->paddr = oc_conf->ptload_addr[i]; 4026f713d18SHari Bathini new->size = oc_conf->ptload_size[i]; 4036f713d18SHari Bathini new->offset = opalcore_off; 4046f713d18SHari Bathini list_add_tail(&new->list, &opalcore_list); 4056f713d18SHari Bathini 4066f713d18SHari Bathini phdr->p_paddr = cpu_to_be64(paddr); 4076f713d18SHari Bathini phdr->p_vaddr = cpu_to_be64(opal_base_addr + paddr); 4086f713d18SHari Bathini phdr->p_filesz = phdr->p_memsz = 4096f713d18SHari Bathini cpu_to_be64(oc_conf->ptload_size[i]); 4106f713d18SHari Bathini phdr->p_offset = cpu_to_be64(opalcore_off); 4116f713d18SHari Bathini 4126f713d18SHari Bathini count++; 4136f713d18SHari Bathini opalcore_off += oc_conf->ptload_size[i]; 4146f713d18SHari Bathini paddr += oc_conf->ptload_size[i]; 4156f713d18SHari Bathini } 4166f713d18SHari Bathini 4176f713d18SHari Bathini elf->e_phnum = cpu_to_be16(count); 4186f713d18SHari Bathini 4196f713d18SHari Bathini bufp = (char *)opalcore_append_cpu_notes((Elf64_Word *)bufp); 4206f713d18SHari Bathini bufp = (char *)auxv_to_elf64_notes((Elf64_Word *)bufp, opal_boot_entry); 4216f713d18SHari Bathini 4226f713d18SHari Bathini oc_conf->opalcore_size = opalcore_off; 4236f713d18SHari Bathini return 0; 4246f713d18SHari Bathini } 4256f713d18SHari Bathini 4266f713d18SHari Bathini static void opalcore_cleanup(void) 4276f713d18SHari Bathini { 4286f713d18SHari Bathini if (oc_conf == NULL) 4296f713d18SHari Bathini return; 4306f713d18SHari Bathini 4316f713d18SHari Bathini /* Remove OPAL core sysfs file */ 4328852c07aSSourabh Jain sysfs_remove_bin_file(mpipl_kobj, &opal_core_attr); 4336f713d18SHari Bathini oc_conf->ptload_phdr = NULL; 4346f713d18SHari Bathini oc_conf->ptload_cnt = 0; 4356f713d18SHari Bathini 4366f713d18SHari Bathini /* free the buffer used for setting up OPAL core */ 4376f713d18SHari Bathini if (oc_conf->opalcorebuf) { 4386f713d18SHari Bathini void *end = (void *)((u64)oc_conf->opalcorebuf + 4396f713d18SHari Bathini oc_conf->opalcorebuf_sz); 4406f713d18SHari Bathini 4416f713d18SHari Bathini free_reserved_area(oc_conf->opalcorebuf, end, -1, NULL); 4426f713d18SHari Bathini oc_conf->opalcorebuf = NULL; 4436f713d18SHari Bathini oc_conf->opalcorebuf_sz = 0; 4446f713d18SHari Bathini } 4456f713d18SHari Bathini 4466f713d18SHari Bathini kfree(oc_conf); 4476f713d18SHari Bathini oc_conf = NULL; 4486f713d18SHari Bathini } 4496f713d18SHari Bathini __exitcall(opalcore_cleanup); 4506f713d18SHari Bathini 4516f713d18SHari Bathini static void __init opalcore_config_init(void) 4526f713d18SHari Bathini { 4536f713d18SHari Bathini u32 idx, cpu_data_version; 4546f713d18SHari Bathini struct device_node *np; 4556f713d18SHari Bathini const __be32 *prop; 4566f713d18SHari Bathini u64 addr = 0; 4576f713d18SHari Bathini int i, ret; 4586f713d18SHari Bathini 4596f713d18SHari Bathini np = of_find_node_by_path("/ibm,opal/dump"); 4606f713d18SHari Bathini if (np == NULL) 4616f713d18SHari Bathini return; 4626f713d18SHari Bathini 4636f713d18SHari Bathini if (!of_device_is_compatible(np, "ibm,opal-dump")) { 4646f713d18SHari Bathini pr_warn("Support missing for this f/w version!\n"); 4656f713d18SHari Bathini return; 4666f713d18SHari Bathini } 4676f713d18SHari Bathini 4686f713d18SHari Bathini /* Check if dump has been initiated on last reboot */ 4696f713d18SHari Bathini prop = of_get_property(np, "mpipl-boot", NULL); 4706f713d18SHari Bathini if (!prop) { 4716f713d18SHari Bathini of_node_put(np); 4726f713d18SHari Bathini return; 4736f713d18SHari Bathini } 4746f713d18SHari Bathini 4756f713d18SHari Bathini /* Get OPAL metadata */ 4766f713d18SHari Bathini ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_OPAL, &addr); 4776f713d18SHari Bathini if ((ret != OPAL_SUCCESS) || !addr) { 4786f713d18SHari Bathini pr_err("Failed to get OPAL metadata (%d)\n", ret); 4796f713d18SHari Bathini goto error_out; 4806f713d18SHari Bathini } 4816f713d18SHari Bathini 4826f713d18SHari Bathini addr = be64_to_cpu(addr); 4836f713d18SHari Bathini pr_debug("OPAL metadata addr: %llx\n", addr); 4846f713d18SHari Bathini opalc_metadata = __va(addr); 4856f713d18SHari Bathini 4866f713d18SHari Bathini /* Get OPAL CPU metadata */ 4876f713d18SHari Bathini ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &addr); 4886f713d18SHari Bathini if ((ret != OPAL_SUCCESS) || !addr) { 4896f713d18SHari Bathini pr_err("Failed to get OPAL CPU metadata (%d)\n", ret); 4906f713d18SHari Bathini goto error_out; 4916f713d18SHari Bathini } 4926f713d18SHari Bathini 4936f713d18SHari Bathini addr = be64_to_cpu(addr); 4946f713d18SHari Bathini pr_debug("CPU metadata addr: %llx\n", addr); 4956f713d18SHari Bathini opalc_cpu_metadata = __va(addr); 4966f713d18SHari Bathini 4976f713d18SHari Bathini /* Allocate memory for config buffer */ 4986f713d18SHari Bathini oc_conf = kzalloc(sizeof(struct opalcore_config), GFP_KERNEL); 4996f713d18SHari Bathini if (oc_conf == NULL) 5006f713d18SHari Bathini goto error_out; 5016f713d18SHari Bathini 5026f713d18SHari Bathini /* Parse OPAL metadata */ 5036f713d18SHari Bathini if (opalc_metadata->version != OPAL_MPIPL_VERSION) { 5046f713d18SHari Bathini pr_warn("Supported OPAL metadata version: %u, found: %u!\n", 5056f713d18SHari Bathini OPAL_MPIPL_VERSION, opalc_metadata->version); 5066f713d18SHari Bathini pr_warn("WARNING: F/W using newer OPAL metadata format!!\n"); 5076f713d18SHari Bathini } 5086f713d18SHari Bathini 5096f713d18SHari Bathini oc_conf->ptload_cnt = 0; 5106f713d18SHari Bathini idx = be32_to_cpu(opalc_metadata->region_cnt); 5116f713d18SHari Bathini if (idx > MAX_PT_LOAD_CNT) { 5126f713d18SHari Bathini pr_warn("WARNING: OPAL regions count (%d) adjusted to limit (%d)", 5138ec5cb12SQinglang Miao idx, MAX_PT_LOAD_CNT); 5146f713d18SHari Bathini idx = MAX_PT_LOAD_CNT; 5156f713d18SHari Bathini } 5166f713d18SHari Bathini for (i = 0; i < idx; i++) { 5176f713d18SHari Bathini oc_conf->ptload_addr[oc_conf->ptload_cnt] = 5186f713d18SHari Bathini be64_to_cpu(opalc_metadata->region[i].dest); 5196f713d18SHari Bathini oc_conf->ptload_size[oc_conf->ptload_cnt++] = 5206f713d18SHari Bathini be64_to_cpu(opalc_metadata->region[i].size); 5216f713d18SHari Bathini } 5226f713d18SHari Bathini oc_conf->ptload_cnt = i; 5236f713d18SHari Bathini oc_conf->crashing_cpu = be32_to_cpu(opalc_metadata->crashing_pir); 5246f713d18SHari Bathini 5256f713d18SHari Bathini if (!oc_conf->ptload_cnt) { 5266f713d18SHari Bathini pr_err("OPAL memory regions not found\n"); 5276f713d18SHari Bathini goto error_out; 5286f713d18SHari Bathini } 5296f713d18SHari Bathini 5306f713d18SHari Bathini /* Parse OPAL CPU metadata */ 5316f713d18SHari Bathini cpu_data_version = be32_to_cpu(opalc_cpu_metadata->cpu_data_version); 5326f713d18SHari Bathini if (cpu_data_version != HDAT_FADUMP_CPU_DATA_VER) { 5336f713d18SHari Bathini pr_warn("Supported CPU data version: %u, found: %u!\n", 5346f713d18SHari Bathini HDAT_FADUMP_CPU_DATA_VER, cpu_data_version); 5356f713d18SHari Bathini pr_warn("WARNING: F/W using newer CPU state data format!!\n"); 5366f713d18SHari Bathini } 5376f713d18SHari Bathini 5386f713d18SHari Bathini addr = be64_to_cpu(opalc_cpu_metadata->region[0].dest); 5396f713d18SHari Bathini if (!addr) { 5406f713d18SHari Bathini pr_err("CPU state data not found!\n"); 5416f713d18SHari Bathini goto error_out; 5426f713d18SHari Bathini } 5436f713d18SHari Bathini oc_conf->cpu_state_destination_vaddr = (u64)__va(addr); 5446f713d18SHari Bathini 5456f713d18SHari Bathini oc_conf->cpu_state_data_size = 5466f713d18SHari Bathini be64_to_cpu(opalc_cpu_metadata->region[0].size); 5476f713d18SHari Bathini oc_conf->cpu_state_entry_size = 5486f713d18SHari Bathini be32_to_cpu(opalc_cpu_metadata->cpu_data_size); 5496f713d18SHari Bathini 5506f713d18SHari Bathini if ((oc_conf->cpu_state_entry_size == 0) || 5516f713d18SHari Bathini (oc_conf->cpu_state_entry_size > oc_conf->cpu_state_data_size)) { 5526f713d18SHari Bathini pr_err("CPU state data is invalid.\n"); 5536f713d18SHari Bathini goto error_out; 5546f713d18SHari Bathini } 5556f713d18SHari Bathini oc_conf->num_cpus = (oc_conf->cpu_state_data_size / 5566f713d18SHari Bathini oc_conf->cpu_state_entry_size); 5576f713d18SHari Bathini 5586f713d18SHari Bathini of_node_put(np); 5596f713d18SHari Bathini return; 5606f713d18SHari Bathini 5616f713d18SHari Bathini error_out: 5626f713d18SHari Bathini pr_err("Could not export /sys/firmware/opal/core\n"); 5636f713d18SHari Bathini opalcore_cleanup(); 5646f713d18SHari Bathini of_node_put(np); 5656f713d18SHari Bathini } 5666f713d18SHari Bathini 5678852c07aSSourabh Jain static ssize_t release_core_store(struct kobject *kobj, 568845426f3SHari Bathini struct kobj_attribute *attr, 569845426f3SHari Bathini const char *buf, size_t count) 570845426f3SHari Bathini { 571845426f3SHari Bathini int input = -1; 572845426f3SHari Bathini 573845426f3SHari Bathini if (kstrtoint(buf, 0, &input)) 574845426f3SHari Bathini return -EINVAL; 575845426f3SHari Bathini 576845426f3SHari Bathini if (input == 1) { 577845426f3SHari Bathini if (oc_conf == NULL) { 578845426f3SHari Bathini pr_err("'/sys/firmware/opal/core' file not accessible!\n"); 579845426f3SHari Bathini return -EPERM; 580845426f3SHari Bathini } 581845426f3SHari Bathini 582845426f3SHari Bathini /* 583845426f3SHari Bathini * Take away '/sys/firmware/opal/core' and release all memory 584845426f3SHari Bathini * used for exporting this file. 585845426f3SHari Bathini */ 586845426f3SHari Bathini opalcore_cleanup(); 587845426f3SHari Bathini } else 588845426f3SHari Bathini return -EINVAL; 589845426f3SHari Bathini 590845426f3SHari Bathini return count; 591845426f3SHari Bathini } 592845426f3SHari Bathini 5938852c07aSSourabh Jain static struct kobj_attribute opalcore_rel_attr = __ATTR_WO(release_core); 5948852c07aSSourabh Jain 5958852c07aSSourabh Jain static struct attribute *mpipl_attr[] = { 5968852c07aSSourabh Jain &opalcore_rel_attr.attr, 5978852c07aSSourabh Jain NULL, 5988852c07aSSourabh Jain }; 5998852c07aSSourabh Jain 6008852c07aSSourabh Jain static struct bin_attribute *mpipl_bin_attr[] = { 6018852c07aSSourabh Jain &opal_core_attr, 6028852c07aSSourabh Jain NULL, 6038852c07aSSourabh Jain 6048852c07aSSourabh Jain }; 6058852c07aSSourabh Jain 606*6b3a3e12SRohan McLure static const struct attribute_group mpipl_group = { 6078852c07aSSourabh Jain .attrs = mpipl_attr, 6088852c07aSSourabh Jain .bin_attrs = mpipl_bin_attr, 6098852c07aSSourabh Jain }; 610845426f3SHari Bathini 6116f713d18SHari Bathini static int __init opalcore_init(void) 6126f713d18SHari Bathini { 6136f713d18SHari Bathini int rc = -1; 6146f713d18SHari Bathini 6156f713d18SHari Bathini opalcore_config_init(); 6166f713d18SHari Bathini 6176f713d18SHari Bathini if (oc_conf == NULL) 6186f713d18SHari Bathini return rc; 6196f713d18SHari Bathini 6206f713d18SHari Bathini create_opalcore(); 6216f713d18SHari Bathini 6226f713d18SHari Bathini /* 6236f713d18SHari Bathini * If oc_conf->opalcorebuf= is set in the 2nd kernel, 6246f713d18SHari Bathini * then capture the dump. 6256f713d18SHari Bathini */ 6266f713d18SHari Bathini if (!(is_opalcore_usable())) { 6278852c07aSSourabh Jain pr_err("Failed to export /sys/firmware/opal/mpipl/core\n"); 6286f713d18SHari Bathini opalcore_cleanup(); 6296f713d18SHari Bathini return rc; 6306f713d18SHari Bathini } 6316f713d18SHari Bathini 6326f713d18SHari Bathini /* Set OPAL core file size */ 6336f713d18SHari Bathini opal_core_attr.size = oc_conf->opalcore_size; 6346f713d18SHari Bathini 6358852c07aSSourabh Jain mpipl_kobj = kobject_create_and_add("mpipl", opal_kobj); 6368852c07aSSourabh Jain if (!mpipl_kobj) { 6378852c07aSSourabh Jain pr_err("unable to create mpipl kobject\n"); 6388852c07aSSourabh Jain return -ENOMEM; 6398852c07aSSourabh Jain } 6408852c07aSSourabh Jain 6416f713d18SHari Bathini /* Export OPAL core sysfs file */ 6428852c07aSSourabh Jain rc = sysfs_create_group(mpipl_kobj, &mpipl_group); 6438852c07aSSourabh Jain if (rc) { 6448852c07aSSourabh Jain pr_err("mpipl sysfs group creation failed (%d)", rc); 6456f713d18SHari Bathini opalcore_cleanup(); 6466f713d18SHari Bathini return rc; 6476f713d18SHari Bathini } 6488852c07aSSourabh Jain /* The /sys/firmware/opal/core is moved to /sys/firmware/opal/mpipl/ 6498852c07aSSourabh Jain * directory, need to create symlink at old location to maintain 6508852c07aSSourabh Jain * backward compatibility. 6518852c07aSSourabh Jain */ 6528852c07aSSourabh Jain rc = compat_only_sysfs_link_entry_to_kobj(opal_kobj, mpipl_kobj, 6538852c07aSSourabh Jain "core", NULL); 654845426f3SHari Bathini if (rc) { 6558852c07aSSourabh Jain pr_err("unable to create core symlink (%d)\n", rc); 6568852c07aSSourabh Jain return rc; 657845426f3SHari Bathini } 658845426f3SHari Bathini 6596f713d18SHari Bathini return 0; 6606f713d18SHari Bathini } 6616f713d18SHari Bathini fs_initcall(opalcore_init); 662