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 
get_new_element(void)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 
is_opalcore_usable(void)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 
append_elf64_note(Elf64_Word * buf,char * name,u32 type,void * data,size_t data_len)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 
fill_prstatus(struct elf_prstatus * prstatus,int pir,struct pt_regs * regs)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));
1159554e908SBrian Gerst 	elf_core_copy_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 
auxv_to_elf64_notes(Elf64_Word * buf,u64 opal_boot_entry)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  */
read_opalcore(struct file * file,struct kobject * kobj,struct bin_attribute * bin_attr,char * to,loff_t pos,size_t count)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  */
opalcore_append_cpu_notes(Elf64_Word * buf)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, &regs);
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, &regs);
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 
create_opalcore(void)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 
351*605c27f3SLiang He 	of_node_put(dn);
352*605c27f3SLiang He 
3536f713d18SHari Bathini 	/* Use count to keep track of the program headers */
3546f713d18SHari Bathini 	count = 0;
3556f713d18SHari Bathini 
3566f713d18SHari Bathini 	bufp = oc_conf->opalcorebuf;
3576f713d18SHari Bathini 	elf = (Elf64_Ehdr *)bufp;
3586f713d18SHari Bathini 	bufp += sizeof(Elf64_Ehdr);
3596f713d18SHari Bathini 	memcpy(elf->e_ident, ELFMAG, SELFMAG);
3606f713d18SHari Bathini 	elf->e_ident[EI_CLASS] = ELF_CLASS;
3616f713d18SHari Bathini 	elf->e_ident[EI_DATA] = ELFDATA2MSB;
3626f713d18SHari Bathini 	elf->e_ident[EI_VERSION] = EV_CURRENT;
3636f713d18SHari Bathini 	elf->e_ident[EI_OSABI] = ELF_OSABI;
3646f713d18SHari Bathini 	memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
3656f713d18SHari Bathini 	elf->e_type = cpu_to_be16(ET_CORE);
3666f713d18SHari Bathini 	elf->e_machine = cpu_to_be16(ELF_ARCH);
3676f713d18SHari Bathini 	elf->e_version = cpu_to_be32(EV_CURRENT);
3686f713d18SHari Bathini 	elf->e_entry = 0;
3696f713d18SHari Bathini 	elf->e_phoff = cpu_to_be64(sizeof(Elf64_Ehdr));
3706f713d18SHari Bathini 	elf->e_shoff = 0;
3716f713d18SHari Bathini 	elf->e_flags = 0;
3726f713d18SHari Bathini 
3736f713d18SHari Bathini 	elf->e_ehsize = cpu_to_be16(sizeof(Elf64_Ehdr));
3746f713d18SHari Bathini 	elf->e_phentsize = cpu_to_be16(sizeof(Elf64_Phdr));
3756f713d18SHari Bathini 	elf->e_phnum = 0;
3766f713d18SHari Bathini 	elf->e_shentsize = 0;
3776f713d18SHari Bathini 	elf->e_shnum = 0;
3786f713d18SHari Bathini 	elf->e_shstrndx = 0;
3796f713d18SHari Bathini 
3806f713d18SHari Bathini 	phdr = (Elf64_Phdr *)bufp;
3816f713d18SHari Bathini 	bufp += sizeof(Elf64_Phdr);
3826f713d18SHari Bathini 	phdr->p_type	= cpu_to_be32(PT_NOTE);
3836f713d18SHari Bathini 	phdr->p_flags	= 0;
3846f713d18SHari Bathini 	phdr->p_align	= 0;
3856f713d18SHari Bathini 	phdr->p_paddr	= phdr->p_vaddr = 0;
3866f713d18SHari Bathini 	phdr->p_offset	= cpu_to_be64(hdr_size);
3876f713d18SHari Bathini 	phdr->p_filesz	= phdr->p_memsz = cpu_to_be64(cpu_notes_size);
3886f713d18SHari Bathini 	count++;
3896f713d18SHari Bathini 
3906f713d18SHari Bathini 	opalcore_off = oc_conf->opalcorebuf_sz;
3916f713d18SHari Bathini 	oc_conf->ptload_phdr  = (Elf64_Phdr *)bufp;
3926f713d18SHari Bathini 	paddr = 0;
3936f713d18SHari Bathini 	for (i = 0; i < oc_conf->ptload_cnt; i++) {
3946f713d18SHari Bathini 		phdr = (Elf64_Phdr *)bufp;
3956f713d18SHari Bathini 		bufp += sizeof(Elf64_Phdr);
3966f713d18SHari Bathini 		phdr->p_type	= cpu_to_be32(PT_LOAD);
3976f713d18SHari Bathini 		phdr->p_flags	= cpu_to_be32(PF_R|PF_W|PF_X);
3986f713d18SHari Bathini 		phdr->p_align	= 0;
3996f713d18SHari Bathini 
4006f713d18SHari Bathini 		new = get_new_element();
4016f713d18SHari Bathini 		if (!new)
4026f713d18SHari Bathini 			return -ENOMEM;
4036f713d18SHari Bathini 		new->paddr  = oc_conf->ptload_addr[i];
4046f713d18SHari Bathini 		new->size   = oc_conf->ptload_size[i];
4056f713d18SHari Bathini 		new->offset = opalcore_off;
4066f713d18SHari Bathini 		list_add_tail(&new->list, &opalcore_list);
4076f713d18SHari Bathini 
4086f713d18SHari Bathini 		phdr->p_paddr	= cpu_to_be64(paddr);
4096f713d18SHari Bathini 		phdr->p_vaddr	= cpu_to_be64(opal_base_addr + paddr);
4106f713d18SHari Bathini 		phdr->p_filesz	= phdr->p_memsz  =
4116f713d18SHari Bathini 			cpu_to_be64(oc_conf->ptload_size[i]);
4126f713d18SHari Bathini 		phdr->p_offset	= cpu_to_be64(opalcore_off);
4136f713d18SHari Bathini 
4146f713d18SHari Bathini 		count++;
4156f713d18SHari Bathini 		opalcore_off += oc_conf->ptload_size[i];
4166f713d18SHari Bathini 		paddr += oc_conf->ptload_size[i];
4176f713d18SHari Bathini 	}
4186f713d18SHari Bathini 
4196f713d18SHari Bathini 	elf->e_phnum = cpu_to_be16(count);
4206f713d18SHari Bathini 
4216f713d18SHari Bathini 	bufp = (char *)opalcore_append_cpu_notes((Elf64_Word *)bufp);
4226f713d18SHari Bathini 	bufp = (char *)auxv_to_elf64_notes((Elf64_Word *)bufp, opal_boot_entry);
4236f713d18SHari Bathini 
4246f713d18SHari Bathini 	oc_conf->opalcore_size = opalcore_off;
4256f713d18SHari Bathini 	return 0;
4266f713d18SHari Bathini }
4276f713d18SHari Bathini 
opalcore_cleanup(void)4286f713d18SHari Bathini static void opalcore_cleanup(void)
4296f713d18SHari Bathini {
4306f713d18SHari Bathini 	if (oc_conf == NULL)
4316f713d18SHari Bathini 		return;
4326f713d18SHari Bathini 
4336f713d18SHari Bathini 	/* Remove OPAL core sysfs file */
4348852c07aSSourabh Jain 	sysfs_remove_bin_file(mpipl_kobj, &opal_core_attr);
4356f713d18SHari Bathini 	oc_conf->ptload_phdr = NULL;
4366f713d18SHari Bathini 	oc_conf->ptload_cnt = 0;
4376f713d18SHari Bathini 
4386f713d18SHari Bathini 	/* free the buffer used for setting up OPAL core */
4396f713d18SHari Bathini 	if (oc_conf->opalcorebuf) {
4406f713d18SHari Bathini 		void *end = (void *)((u64)oc_conf->opalcorebuf +
4416f713d18SHari Bathini 				     oc_conf->opalcorebuf_sz);
4426f713d18SHari Bathini 
4436f713d18SHari Bathini 		free_reserved_area(oc_conf->opalcorebuf, end, -1, NULL);
4446f713d18SHari Bathini 		oc_conf->opalcorebuf = NULL;
4456f713d18SHari Bathini 		oc_conf->opalcorebuf_sz = 0;
4466f713d18SHari Bathini 	}
4476f713d18SHari Bathini 
4486f713d18SHari Bathini 	kfree(oc_conf);
4496f713d18SHari Bathini 	oc_conf = NULL;
4506f713d18SHari Bathini }
4516f713d18SHari Bathini __exitcall(opalcore_cleanup);
4526f713d18SHari Bathini 
opalcore_config_init(void)4536f713d18SHari Bathini static void __init opalcore_config_init(void)
4546f713d18SHari Bathini {
4556f713d18SHari Bathini 	u32 idx, cpu_data_version;
4566f713d18SHari Bathini 	struct device_node *np;
4576f713d18SHari Bathini 	const __be32 *prop;
4586f713d18SHari Bathini 	u64 addr = 0;
4596f713d18SHari Bathini 	int i, ret;
4606f713d18SHari Bathini 
4616f713d18SHari Bathini 	np = of_find_node_by_path("/ibm,opal/dump");
4626f713d18SHari Bathini 	if (np == NULL)
4636f713d18SHari Bathini 		return;
4646f713d18SHari Bathini 
4656f713d18SHari Bathini 	if (!of_device_is_compatible(np, "ibm,opal-dump")) {
4666f713d18SHari Bathini 		pr_warn("Support missing for this f/w version!\n");
4676f713d18SHari Bathini 		return;
4686f713d18SHari Bathini 	}
4696f713d18SHari Bathini 
4706f713d18SHari Bathini 	/* Check if dump has been initiated on last reboot */
4716f713d18SHari Bathini 	prop = of_get_property(np, "mpipl-boot", NULL);
4726f713d18SHari Bathini 	if (!prop) {
4736f713d18SHari Bathini 		of_node_put(np);
4746f713d18SHari Bathini 		return;
4756f713d18SHari Bathini 	}
4766f713d18SHari Bathini 
4776f713d18SHari Bathini 	/* Get OPAL metadata */
4786f713d18SHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_OPAL, &addr);
4796f713d18SHari Bathini 	if ((ret != OPAL_SUCCESS) || !addr) {
4806f713d18SHari Bathini 		pr_err("Failed to get OPAL metadata (%d)\n", ret);
4816f713d18SHari Bathini 		goto error_out;
4826f713d18SHari Bathini 	}
4836f713d18SHari Bathini 
4846f713d18SHari Bathini 	addr = be64_to_cpu(addr);
4856f713d18SHari Bathini 	pr_debug("OPAL metadata addr: %llx\n", addr);
4866f713d18SHari Bathini 	opalc_metadata = __va(addr);
4876f713d18SHari Bathini 
4886f713d18SHari Bathini 	/* Get OPAL CPU metadata */
4896f713d18SHari Bathini 	ret = opal_mpipl_query_tag(OPAL_MPIPL_TAG_CPU, &addr);
4906f713d18SHari Bathini 	if ((ret != OPAL_SUCCESS) || !addr) {
4916f713d18SHari Bathini 		pr_err("Failed to get OPAL CPU metadata (%d)\n", ret);
4926f713d18SHari Bathini 		goto error_out;
4936f713d18SHari Bathini 	}
4946f713d18SHari Bathini 
4956f713d18SHari Bathini 	addr = be64_to_cpu(addr);
4966f713d18SHari Bathini 	pr_debug("CPU metadata addr: %llx\n", addr);
4976f713d18SHari Bathini 	opalc_cpu_metadata = __va(addr);
4986f713d18SHari Bathini 
4996f713d18SHari Bathini 	/* Allocate memory for config buffer */
5006f713d18SHari Bathini 	oc_conf = kzalloc(sizeof(struct opalcore_config), GFP_KERNEL);
5016f713d18SHari Bathini 	if (oc_conf == NULL)
5026f713d18SHari Bathini 		goto error_out;
5036f713d18SHari Bathini 
5046f713d18SHari Bathini 	/* Parse OPAL metadata */
5056f713d18SHari Bathini 	if (opalc_metadata->version != OPAL_MPIPL_VERSION) {
5066f713d18SHari Bathini 		pr_warn("Supported OPAL metadata version: %u, found: %u!\n",
5076f713d18SHari Bathini 			OPAL_MPIPL_VERSION, opalc_metadata->version);
5086f713d18SHari Bathini 		pr_warn("WARNING: F/W using newer OPAL metadata format!!\n");
5096f713d18SHari Bathini 	}
5106f713d18SHari Bathini 
5116f713d18SHari Bathini 	oc_conf->ptload_cnt = 0;
5126f713d18SHari Bathini 	idx = be32_to_cpu(opalc_metadata->region_cnt);
5136f713d18SHari Bathini 	if (idx > MAX_PT_LOAD_CNT) {
5146f713d18SHari Bathini 		pr_warn("WARNING: OPAL regions count (%d) adjusted to limit (%d)",
5158ec5cb12SQinglang Miao 			idx, MAX_PT_LOAD_CNT);
5166f713d18SHari Bathini 		idx = MAX_PT_LOAD_CNT;
5176f713d18SHari Bathini 	}
5186f713d18SHari Bathini 	for (i = 0; i < idx; i++) {
5196f713d18SHari Bathini 		oc_conf->ptload_addr[oc_conf->ptload_cnt] =
5206f713d18SHari Bathini 				be64_to_cpu(opalc_metadata->region[i].dest);
5216f713d18SHari Bathini 		oc_conf->ptload_size[oc_conf->ptload_cnt++] =
5226f713d18SHari Bathini 				be64_to_cpu(opalc_metadata->region[i].size);
5236f713d18SHari Bathini 	}
5246f713d18SHari Bathini 	oc_conf->ptload_cnt = i;
5256f713d18SHari Bathini 	oc_conf->crashing_cpu = be32_to_cpu(opalc_metadata->crashing_pir);
5266f713d18SHari Bathini 
5276f713d18SHari Bathini 	if (!oc_conf->ptload_cnt) {
5286f713d18SHari Bathini 		pr_err("OPAL memory regions not found\n");
5296f713d18SHari Bathini 		goto error_out;
5306f713d18SHari Bathini 	}
5316f713d18SHari Bathini 
5326f713d18SHari Bathini 	/* Parse OPAL CPU metadata */
5336f713d18SHari Bathini 	cpu_data_version = be32_to_cpu(opalc_cpu_metadata->cpu_data_version);
5346f713d18SHari Bathini 	if (cpu_data_version != HDAT_FADUMP_CPU_DATA_VER) {
5356f713d18SHari Bathini 		pr_warn("Supported CPU data version: %u, found: %u!\n",
5366f713d18SHari Bathini 			HDAT_FADUMP_CPU_DATA_VER, cpu_data_version);
5376f713d18SHari Bathini 		pr_warn("WARNING: F/W using newer CPU state data format!!\n");
5386f713d18SHari Bathini 	}
5396f713d18SHari Bathini 
5406f713d18SHari Bathini 	addr = be64_to_cpu(opalc_cpu_metadata->region[0].dest);
5416f713d18SHari Bathini 	if (!addr) {
5426f713d18SHari Bathini 		pr_err("CPU state data not found!\n");
5436f713d18SHari Bathini 		goto error_out;
5446f713d18SHari Bathini 	}
5456f713d18SHari Bathini 	oc_conf->cpu_state_destination_vaddr = (u64)__va(addr);
5466f713d18SHari Bathini 
5476f713d18SHari Bathini 	oc_conf->cpu_state_data_size =
5486f713d18SHari Bathini 			be64_to_cpu(opalc_cpu_metadata->region[0].size);
5496f713d18SHari Bathini 	oc_conf->cpu_state_entry_size =
5506f713d18SHari Bathini 			be32_to_cpu(opalc_cpu_metadata->cpu_data_size);
5516f713d18SHari Bathini 
5526f713d18SHari Bathini 	if ((oc_conf->cpu_state_entry_size == 0) ||
5536f713d18SHari Bathini 	    (oc_conf->cpu_state_entry_size > oc_conf->cpu_state_data_size)) {
5546f713d18SHari Bathini 		pr_err("CPU state data is invalid.\n");
5556f713d18SHari Bathini 		goto error_out;
5566f713d18SHari Bathini 	}
5576f713d18SHari Bathini 	oc_conf->num_cpus = (oc_conf->cpu_state_data_size /
5586f713d18SHari Bathini 			     oc_conf->cpu_state_entry_size);
5596f713d18SHari Bathini 
5606f713d18SHari Bathini 	of_node_put(np);
5616f713d18SHari Bathini 	return;
5626f713d18SHari Bathini 
5636f713d18SHari Bathini error_out:
5646f713d18SHari Bathini 	pr_err("Could not export /sys/firmware/opal/core\n");
5656f713d18SHari Bathini 	opalcore_cleanup();
5666f713d18SHari Bathini 	of_node_put(np);
5676f713d18SHari Bathini }
5686f713d18SHari Bathini 
release_core_store(struct kobject * kobj,struct kobj_attribute * attr,const char * buf,size_t count)5698852c07aSSourabh Jain static ssize_t release_core_store(struct kobject *kobj,
570845426f3SHari Bathini 				  struct kobj_attribute *attr,
571845426f3SHari Bathini 				  const char *buf, size_t count)
572845426f3SHari Bathini {
573845426f3SHari Bathini 	int input = -1;
574845426f3SHari Bathini 
575845426f3SHari Bathini 	if (kstrtoint(buf, 0, &input))
576845426f3SHari Bathini 		return -EINVAL;
577845426f3SHari Bathini 
578845426f3SHari Bathini 	if (input == 1) {
579845426f3SHari Bathini 		if (oc_conf == NULL) {
580845426f3SHari Bathini 			pr_err("'/sys/firmware/opal/core' file not accessible!\n");
581845426f3SHari Bathini 			return -EPERM;
582845426f3SHari Bathini 		}
583845426f3SHari Bathini 
584845426f3SHari Bathini 		/*
585845426f3SHari Bathini 		 * Take away '/sys/firmware/opal/core' and release all memory
586845426f3SHari Bathini 		 * used for exporting this file.
587845426f3SHari Bathini 		 */
588845426f3SHari Bathini 		opalcore_cleanup();
589845426f3SHari Bathini 	} else
590845426f3SHari Bathini 		return -EINVAL;
591845426f3SHari Bathini 
592845426f3SHari Bathini 	return count;
593845426f3SHari Bathini }
594845426f3SHari Bathini 
5958852c07aSSourabh Jain static struct kobj_attribute opalcore_rel_attr = __ATTR_WO(release_core);
5968852c07aSSourabh Jain 
5978852c07aSSourabh Jain static struct attribute *mpipl_attr[] = {
5988852c07aSSourabh Jain 	&opalcore_rel_attr.attr,
5998852c07aSSourabh Jain 	NULL,
6008852c07aSSourabh Jain };
6018852c07aSSourabh Jain 
6028852c07aSSourabh Jain static struct bin_attribute *mpipl_bin_attr[] = {
6038852c07aSSourabh Jain 	&opal_core_attr,
6048852c07aSSourabh Jain 	NULL,
6058852c07aSSourabh Jain 
6068852c07aSSourabh Jain };
6078852c07aSSourabh Jain 
6086b3a3e12SRohan McLure static const struct attribute_group mpipl_group = {
6098852c07aSSourabh Jain 	.attrs = mpipl_attr,
6108852c07aSSourabh Jain 	.bin_attrs =  mpipl_bin_attr,
6118852c07aSSourabh Jain };
612845426f3SHari Bathini 
opalcore_init(void)6136f713d18SHari Bathini static int __init opalcore_init(void)
6146f713d18SHari Bathini {
6156f713d18SHari Bathini 	int rc = -1;
6166f713d18SHari Bathini 
6176f713d18SHari Bathini 	opalcore_config_init();
6186f713d18SHari Bathini 
6196f713d18SHari Bathini 	if (oc_conf == NULL)
6206f713d18SHari Bathini 		return rc;
6216f713d18SHari Bathini 
6226f713d18SHari Bathini 	create_opalcore();
6236f713d18SHari Bathini 
6246f713d18SHari Bathini 	/*
6256f713d18SHari Bathini 	 * If oc_conf->opalcorebuf= is set in the 2nd kernel,
6266f713d18SHari Bathini 	 * then capture the dump.
6276f713d18SHari Bathini 	 */
6286f713d18SHari Bathini 	if (!(is_opalcore_usable())) {
6298852c07aSSourabh Jain 		pr_err("Failed to export /sys/firmware/opal/mpipl/core\n");
6306f713d18SHari Bathini 		opalcore_cleanup();
6316f713d18SHari Bathini 		return rc;
6326f713d18SHari Bathini 	}
6336f713d18SHari Bathini 
6346f713d18SHari Bathini 	/* Set OPAL core file size */
6356f713d18SHari Bathini 	opal_core_attr.size = oc_conf->opalcore_size;
6366f713d18SHari Bathini 
6378852c07aSSourabh Jain 	mpipl_kobj = kobject_create_and_add("mpipl", opal_kobj);
6388852c07aSSourabh Jain 	if (!mpipl_kobj) {
6398852c07aSSourabh Jain 		pr_err("unable to create mpipl kobject\n");
6408852c07aSSourabh Jain 		return -ENOMEM;
6418852c07aSSourabh Jain 	}
6428852c07aSSourabh Jain 
6436f713d18SHari Bathini 	/* Export OPAL core sysfs file */
6448852c07aSSourabh Jain 	rc = sysfs_create_group(mpipl_kobj, &mpipl_group);
6458852c07aSSourabh Jain 	if (rc) {
6468852c07aSSourabh Jain 		pr_err("mpipl sysfs group creation failed (%d)", rc);
6476f713d18SHari Bathini 		opalcore_cleanup();
6486f713d18SHari Bathini 		return rc;
6496f713d18SHari Bathini 	}
6508852c07aSSourabh Jain 	/* The /sys/firmware/opal/core is moved to /sys/firmware/opal/mpipl/
6518852c07aSSourabh Jain 	 * directory, need to create symlink at old location to maintain
6528852c07aSSourabh Jain 	 * backward compatibility.
6538852c07aSSourabh Jain 	 */
6548852c07aSSourabh Jain 	rc = compat_only_sysfs_link_entry_to_kobj(opal_kobj, mpipl_kobj,
6558852c07aSSourabh Jain 						  "core", NULL);
656845426f3SHari Bathini 	if (rc) {
6578852c07aSSourabh Jain 		pr_err("unable to create core symlink (%d)\n", rc);
6588852c07aSSourabh Jain 		return rc;
659845426f3SHari Bathini 	}
660845426f3SHari Bathini 
6616f713d18SHari Bathini 	return 0;
6626f713d18SHari Bathini }
6636f713d18SHari Bathini fs_initcall(opalcore_init);
664