18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *	fs/proc/vmcore.c Interface for accessing the crash
48c2ecf20Sopenharmony_ci * 				 dump from the system's previous life.
58c2ecf20Sopenharmony_ci * 	Heavily borrowed from fs/proc/kcore.c
68c2ecf20Sopenharmony_ci *	Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
78c2ecf20Sopenharmony_ci *	Copyright (C) IBM Corporation, 2004. All rights reserved
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/kcore.h>
138c2ecf20Sopenharmony_ci#include <linux/user.h>
148c2ecf20Sopenharmony_ci#include <linux/elf.h>
158c2ecf20Sopenharmony_ci#include <linux/elfcore.h>
168c2ecf20Sopenharmony_ci#include <linux/export.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <linux/printk.h>
208c2ecf20Sopenharmony_ci#include <linux/memblock.h>
218c2ecf20Sopenharmony_ci#include <linux/init.h>
228c2ecf20Sopenharmony_ci#include <linux/crash_dump.h>
238c2ecf20Sopenharmony_ci#include <linux/list.h>
248c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
258c2ecf20Sopenharmony_ci#include <linux/mutex.h>
268c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
278c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
288c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
298c2ecf20Sopenharmony_ci#include <linux/mem_encrypt.h>
308c2ecf20Sopenharmony_ci#include <asm/io.h>
318c2ecf20Sopenharmony_ci#include "internal.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* List representing chunks of contiguous memory areas and their offsets in
348c2ecf20Sopenharmony_ci * vmcore file.
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_cistatic LIST_HEAD(vmcore_list);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/* Stores the pointer to the buffer containing kernel elf core headers. */
398c2ecf20Sopenharmony_cistatic char *elfcorebuf;
408c2ecf20Sopenharmony_cistatic size_t elfcorebuf_sz;
418c2ecf20Sopenharmony_cistatic size_t elfcorebuf_sz_orig;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic char *elfnotes_buf;
448c2ecf20Sopenharmony_cistatic size_t elfnotes_sz;
458c2ecf20Sopenharmony_ci/* Size of all notes minus the device dump notes */
468c2ecf20Sopenharmony_cistatic size_t elfnotes_orig_sz;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* Total size of vmcore file. */
498c2ecf20Sopenharmony_cistatic u64 vmcore_size;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct proc_dir_entry *proc_vmcore;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
548c2ecf20Sopenharmony_ci/* Device Dump list and mutex to synchronize access to list */
558c2ecf20Sopenharmony_cistatic LIST_HEAD(vmcoredd_list);
568c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(vmcoredd_mutex);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic bool vmcoredd_disabled;
598c2ecf20Sopenharmony_cicore_param(novmcoredd, vmcoredd_disabled, bool, 0);
608c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* Device Dump Size */
638c2ecf20Sopenharmony_cistatic size_t vmcoredd_orig_sz;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error
678c2ecf20Sopenharmony_ci * The called function has to take care of module refcounting.
688c2ecf20Sopenharmony_ci */
698c2ecf20Sopenharmony_cistatic int (*oldmem_pfn_is_ram)(unsigned long pfn);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ciint register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn))
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	if (oldmem_pfn_is_ram)
748c2ecf20Sopenharmony_ci		return -EBUSY;
758c2ecf20Sopenharmony_ci	oldmem_pfn_is_ram = fn;
768c2ecf20Sopenharmony_ci	return 0;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_civoid unregister_oldmem_pfn_is_ram(void)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	oldmem_pfn_is_ram = NULL;
838c2ecf20Sopenharmony_ci	wmb();
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int pfn_is_ram(unsigned long pfn)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	int (*fn)(unsigned long pfn);
908c2ecf20Sopenharmony_ci	/* pfn is ram unless fn() checks pagetype */
918c2ecf20Sopenharmony_ci	int ret = 1;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	/*
948c2ecf20Sopenharmony_ci	 * Ask hypervisor if the pfn is really ram.
958c2ecf20Sopenharmony_ci	 * A ballooned page contains no data and reading from such a page
968c2ecf20Sopenharmony_ci	 * will cause high load in the hypervisor.
978c2ecf20Sopenharmony_ci	 */
988c2ecf20Sopenharmony_ci	fn = oldmem_pfn_is_ram;
998c2ecf20Sopenharmony_ci	if (fn)
1008c2ecf20Sopenharmony_ci		ret = fn(pfn);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	return ret;
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci/* Reads a page from the oldmem device from given offset. */
1068c2ecf20Sopenharmony_cissize_t read_from_oldmem(char *buf, size_t count,
1078c2ecf20Sopenharmony_ci			 u64 *ppos, int userbuf,
1088c2ecf20Sopenharmony_ci			 bool encrypted)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	unsigned long pfn, offset;
1118c2ecf20Sopenharmony_ci	size_t nr_bytes;
1128c2ecf20Sopenharmony_ci	ssize_t read = 0, tmp;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (!count)
1158c2ecf20Sopenharmony_ci		return 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	offset = (unsigned long)(*ppos % PAGE_SIZE);
1188c2ecf20Sopenharmony_ci	pfn = (unsigned long)(*ppos / PAGE_SIZE);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	do {
1218c2ecf20Sopenharmony_ci		if (count > (PAGE_SIZE - offset))
1228c2ecf20Sopenharmony_ci			nr_bytes = PAGE_SIZE - offset;
1238c2ecf20Sopenharmony_ci		else
1248c2ecf20Sopenharmony_ci			nr_bytes = count;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		/* If pfn is not ram, return zeros for sparse dump files */
1278c2ecf20Sopenharmony_ci		if (pfn_is_ram(pfn) == 0) {
1288c2ecf20Sopenharmony_ci			tmp = 0;
1298c2ecf20Sopenharmony_ci			if (!userbuf)
1308c2ecf20Sopenharmony_ci				memset(buf, 0, nr_bytes);
1318c2ecf20Sopenharmony_ci			else if (clear_user(buf, nr_bytes))
1328c2ecf20Sopenharmony_ci				tmp = -EFAULT;
1338c2ecf20Sopenharmony_ci		} else {
1348c2ecf20Sopenharmony_ci			if (encrypted)
1358c2ecf20Sopenharmony_ci				tmp = copy_oldmem_page_encrypted(pfn, buf,
1368c2ecf20Sopenharmony_ci								 nr_bytes,
1378c2ecf20Sopenharmony_ci								 offset,
1388c2ecf20Sopenharmony_ci								 userbuf);
1398c2ecf20Sopenharmony_ci			else
1408c2ecf20Sopenharmony_ci				tmp = copy_oldmem_page(pfn, buf, nr_bytes,
1418c2ecf20Sopenharmony_ci						       offset, userbuf);
1428c2ecf20Sopenharmony_ci		}
1438c2ecf20Sopenharmony_ci		if (tmp < 0)
1448c2ecf20Sopenharmony_ci			return tmp;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		*ppos += nr_bytes;
1478c2ecf20Sopenharmony_ci		count -= nr_bytes;
1488c2ecf20Sopenharmony_ci		buf += nr_bytes;
1498c2ecf20Sopenharmony_ci		read += nr_bytes;
1508c2ecf20Sopenharmony_ci		++pfn;
1518c2ecf20Sopenharmony_ci		offset = 0;
1528c2ecf20Sopenharmony_ci	} while (count);
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	return read;
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci/*
1588c2ecf20Sopenharmony_ci * Architectures may override this function to allocate ELF header in 2nd kernel
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_ciint __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return 0;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci/*
1668c2ecf20Sopenharmony_ci * Architectures may override this function to free header
1678c2ecf20Sopenharmony_ci */
1688c2ecf20Sopenharmony_civoid __weak elfcorehdr_free(unsigned long long addr)
1698c2ecf20Sopenharmony_ci{}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/*
1728c2ecf20Sopenharmony_ci * Architectures may override this function to read from ELF header
1738c2ecf20Sopenharmony_ci */
1748c2ecf20Sopenharmony_cissize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	return read_from_oldmem(buf, count, ppos, 0, false);
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/*
1808c2ecf20Sopenharmony_ci * Architectures may override this function to read from notes sections
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_cissize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	return read_from_oldmem(buf, count, ppos, 0, mem_encrypt_active());
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci/*
1888c2ecf20Sopenharmony_ci * Architectures may override this function to map oldmem
1898c2ecf20Sopenharmony_ci */
1908c2ecf20Sopenharmony_ciint __weak remap_oldmem_pfn_range(struct vm_area_struct *vma,
1918c2ecf20Sopenharmony_ci				  unsigned long from, unsigned long pfn,
1928c2ecf20Sopenharmony_ci				  unsigned long size, pgprot_t prot)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	prot = pgprot_encrypted(prot);
1958c2ecf20Sopenharmony_ci	return remap_pfn_range(vma, from, pfn, size, prot);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci/*
1998c2ecf20Sopenharmony_ci * Architectures which support memory encryption override this.
2008c2ecf20Sopenharmony_ci */
2018c2ecf20Sopenharmony_cissize_t __weak
2028c2ecf20Sopenharmony_cicopy_oldmem_page_encrypted(unsigned long pfn, char *buf, size_t csize,
2038c2ecf20Sopenharmony_ci			   unsigned long offset, int userbuf)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	return copy_oldmem_page(pfn, buf, csize, offset, userbuf);
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci/*
2098c2ecf20Sopenharmony_ci * Copy to either kernel or user space
2108c2ecf20Sopenharmony_ci */
2118c2ecf20Sopenharmony_cistatic int copy_to(void *target, void *src, size_t size, int userbuf)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	if (userbuf) {
2148c2ecf20Sopenharmony_ci		if (copy_to_user((char __user *) target, src, size))
2158c2ecf20Sopenharmony_ci			return -EFAULT;
2168c2ecf20Sopenharmony_ci	} else {
2178c2ecf20Sopenharmony_ci		memcpy(target, src, size);
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
2238c2ecf20Sopenharmony_cistatic int vmcoredd_copy_dumps(void *dst, u64 start, size_t size, int userbuf)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct vmcoredd_node *dump;
2268c2ecf20Sopenharmony_ci	u64 offset = 0;
2278c2ecf20Sopenharmony_ci	int ret = 0;
2288c2ecf20Sopenharmony_ci	size_t tsz;
2298c2ecf20Sopenharmony_ci	char *buf;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
2328c2ecf20Sopenharmony_ci	list_for_each_entry(dump, &vmcoredd_list, list) {
2338c2ecf20Sopenharmony_ci		if (start < offset + dump->size) {
2348c2ecf20Sopenharmony_ci			tsz = min(offset + (u64)dump->size - start, (u64)size);
2358c2ecf20Sopenharmony_ci			buf = dump->buf + start - offset;
2368c2ecf20Sopenharmony_ci			if (copy_to(dst, buf, tsz, userbuf)) {
2378c2ecf20Sopenharmony_ci				ret = -EFAULT;
2388c2ecf20Sopenharmony_ci				goto out_unlock;
2398c2ecf20Sopenharmony_ci			}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci			size -= tsz;
2428c2ecf20Sopenharmony_ci			start += tsz;
2438c2ecf20Sopenharmony_ci			dst += tsz;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci			/* Leave now if buffer filled already */
2468c2ecf20Sopenharmony_ci			if (!size)
2478c2ecf20Sopenharmony_ci				goto out_unlock;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci		offset += dump->size;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ciout_unlock:
2538c2ecf20Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
2548c2ecf20Sopenharmony_ci	return ret;
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU
2588c2ecf20Sopenharmony_cistatic int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst,
2598c2ecf20Sopenharmony_ci			       u64 start, size_t size)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct vmcoredd_node *dump;
2628c2ecf20Sopenharmony_ci	u64 offset = 0;
2638c2ecf20Sopenharmony_ci	int ret = 0;
2648c2ecf20Sopenharmony_ci	size_t tsz;
2658c2ecf20Sopenharmony_ci	char *buf;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
2688c2ecf20Sopenharmony_ci	list_for_each_entry(dump, &vmcoredd_list, list) {
2698c2ecf20Sopenharmony_ci		if (start < offset + dump->size) {
2708c2ecf20Sopenharmony_ci			tsz = min(offset + (u64)dump->size - start, (u64)size);
2718c2ecf20Sopenharmony_ci			buf = dump->buf + start - offset;
2728c2ecf20Sopenharmony_ci			if (remap_vmalloc_range_partial(vma, dst, buf, 0,
2738c2ecf20Sopenharmony_ci							tsz)) {
2748c2ecf20Sopenharmony_ci				ret = -EFAULT;
2758c2ecf20Sopenharmony_ci				goto out_unlock;
2768c2ecf20Sopenharmony_ci			}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci			size -= tsz;
2798c2ecf20Sopenharmony_ci			start += tsz;
2808c2ecf20Sopenharmony_ci			dst += tsz;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci			/* Leave now if buffer filled already */
2838c2ecf20Sopenharmony_ci			if (!size)
2848c2ecf20Sopenharmony_ci				goto out_unlock;
2858c2ecf20Sopenharmony_ci		}
2868c2ecf20Sopenharmony_ci		offset += dump->size;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ciout_unlock:
2908c2ecf20Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
2918c2ecf20Sopenharmony_ci	return ret;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci#endif /* CONFIG_MMU */
2948c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci/* Read from the ELF header and then the crash dump. On error, negative value is
2978c2ecf20Sopenharmony_ci * returned otherwise number of bytes read are returned.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic ssize_t __read_vmcore(char *buffer, size_t buflen, loff_t *fpos,
3008c2ecf20Sopenharmony_ci			     int userbuf)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	ssize_t acc = 0, tmp;
3038c2ecf20Sopenharmony_ci	size_t tsz;
3048c2ecf20Sopenharmony_ci	u64 start;
3058c2ecf20Sopenharmony_ci	struct vmcore *m = NULL;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	if (buflen == 0 || *fpos >= vmcore_size)
3088c2ecf20Sopenharmony_ci		return 0;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	/* trim buflen to not go beyond EOF */
3118c2ecf20Sopenharmony_ci	if (buflen > vmcore_size - *fpos)
3128c2ecf20Sopenharmony_ci		buflen = vmcore_size - *fpos;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* Read ELF core header */
3158c2ecf20Sopenharmony_ci	if (*fpos < elfcorebuf_sz) {
3168c2ecf20Sopenharmony_ci		tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen);
3178c2ecf20Sopenharmony_ci		if (copy_to(buffer, elfcorebuf + *fpos, tsz, userbuf))
3188c2ecf20Sopenharmony_ci			return -EFAULT;
3198c2ecf20Sopenharmony_ci		buflen -= tsz;
3208c2ecf20Sopenharmony_ci		*fpos += tsz;
3218c2ecf20Sopenharmony_ci		buffer += tsz;
3228c2ecf20Sopenharmony_ci		acc += tsz;
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci		/* leave now if filled buffer already */
3258c2ecf20Sopenharmony_ci		if (buflen == 0)
3268c2ecf20Sopenharmony_ci			return acc;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	/* Read Elf note segment */
3308c2ecf20Sopenharmony_ci	if (*fpos < elfcorebuf_sz + elfnotes_sz) {
3318c2ecf20Sopenharmony_ci		void *kaddr;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		/* We add device dumps before other elf notes because the
3348c2ecf20Sopenharmony_ci		 * other elf notes may not fill the elf notes buffer
3358c2ecf20Sopenharmony_ci		 * completely and we will end up with zero-filled data
3368c2ecf20Sopenharmony_ci		 * between the elf notes and the device dumps. Tools will
3378c2ecf20Sopenharmony_ci		 * then try to decode this zero-filled data as valid notes
3388c2ecf20Sopenharmony_ci		 * and we don't want that. Hence, adding device dumps before
3398c2ecf20Sopenharmony_ci		 * the other elf notes ensure that zero-filled data can be
3408c2ecf20Sopenharmony_ci		 * avoided.
3418c2ecf20Sopenharmony_ci		 */
3428c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
3438c2ecf20Sopenharmony_ci		/* Read device dumps */
3448c2ecf20Sopenharmony_ci		if (*fpos < elfcorebuf_sz + vmcoredd_orig_sz) {
3458c2ecf20Sopenharmony_ci			tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
3468c2ecf20Sopenharmony_ci				  (size_t)*fpos, buflen);
3478c2ecf20Sopenharmony_ci			start = *fpos - elfcorebuf_sz;
3488c2ecf20Sopenharmony_ci			if (vmcoredd_copy_dumps(buffer, start, tsz, userbuf))
3498c2ecf20Sopenharmony_ci				return -EFAULT;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci			buflen -= tsz;
3528c2ecf20Sopenharmony_ci			*fpos += tsz;
3538c2ecf20Sopenharmony_ci			buffer += tsz;
3548c2ecf20Sopenharmony_ci			acc += tsz;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci			/* leave now if filled buffer already */
3578c2ecf20Sopenharmony_ci			if (!buflen)
3588c2ecf20Sopenharmony_ci				return acc;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci		/* Read remaining elf notes */
3638c2ecf20Sopenharmony_ci		tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen);
3648c2ecf20Sopenharmony_ci		kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz;
3658c2ecf20Sopenharmony_ci		if (copy_to(buffer, kaddr, tsz, userbuf))
3668c2ecf20Sopenharmony_ci			return -EFAULT;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci		buflen -= tsz;
3698c2ecf20Sopenharmony_ci		*fpos += tsz;
3708c2ecf20Sopenharmony_ci		buffer += tsz;
3718c2ecf20Sopenharmony_ci		acc += tsz;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci		/* leave now if filled buffer already */
3748c2ecf20Sopenharmony_ci		if (buflen == 0)
3758c2ecf20Sopenharmony_ci			return acc;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	list_for_each_entry(m, &vmcore_list, list) {
3798c2ecf20Sopenharmony_ci		if (*fpos < m->offset + m->size) {
3808c2ecf20Sopenharmony_ci			tsz = (size_t)min_t(unsigned long long,
3818c2ecf20Sopenharmony_ci					    m->offset + m->size - *fpos,
3828c2ecf20Sopenharmony_ci					    buflen);
3838c2ecf20Sopenharmony_ci			start = m->paddr + *fpos - m->offset;
3848c2ecf20Sopenharmony_ci			tmp = read_from_oldmem(buffer, tsz, &start,
3858c2ecf20Sopenharmony_ci					       userbuf, mem_encrypt_active());
3868c2ecf20Sopenharmony_ci			if (tmp < 0)
3878c2ecf20Sopenharmony_ci				return tmp;
3888c2ecf20Sopenharmony_ci			buflen -= tsz;
3898c2ecf20Sopenharmony_ci			*fpos += tsz;
3908c2ecf20Sopenharmony_ci			buffer += tsz;
3918c2ecf20Sopenharmony_ci			acc += tsz;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci			/* leave now if filled buffer already */
3948c2ecf20Sopenharmony_ci			if (buflen == 0)
3958c2ecf20Sopenharmony_ci				return acc;
3968c2ecf20Sopenharmony_ci		}
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return acc;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic ssize_t read_vmcore(struct file *file, char __user *buffer,
4038c2ecf20Sopenharmony_ci			   size_t buflen, loff_t *fpos)
4048c2ecf20Sopenharmony_ci{
4058c2ecf20Sopenharmony_ci	return __read_vmcore((__force char *) buffer, buflen, fpos, 1);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/*
4098c2ecf20Sopenharmony_ci * The vmcore fault handler uses the page cache and fills data using the
4108c2ecf20Sopenharmony_ci * standard __vmcore_read() function.
4118c2ecf20Sopenharmony_ci *
4128c2ecf20Sopenharmony_ci * On s390 the fault handler is used for memory regions that can't be mapped
4138c2ecf20Sopenharmony_ci * directly with remap_pfn_range().
4148c2ecf20Sopenharmony_ci */
4158c2ecf20Sopenharmony_cistatic vm_fault_t mmap_vmcore_fault(struct vm_fault *vmf)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci#ifdef CONFIG_S390
4188c2ecf20Sopenharmony_ci	struct address_space *mapping = vmf->vma->vm_file->f_mapping;
4198c2ecf20Sopenharmony_ci	pgoff_t index = vmf->pgoff;
4208c2ecf20Sopenharmony_ci	struct page *page;
4218c2ecf20Sopenharmony_ci	loff_t offset;
4228c2ecf20Sopenharmony_ci	char *buf;
4238c2ecf20Sopenharmony_ci	int rc;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	page = find_or_create_page(mapping, index, GFP_KERNEL);
4268c2ecf20Sopenharmony_ci	if (!page)
4278c2ecf20Sopenharmony_ci		return VM_FAULT_OOM;
4288c2ecf20Sopenharmony_ci	if (!PageUptodate(page)) {
4298c2ecf20Sopenharmony_ci		offset = (loff_t) index << PAGE_SHIFT;
4308c2ecf20Sopenharmony_ci		buf = __va((page_to_pfn(page) << PAGE_SHIFT));
4318c2ecf20Sopenharmony_ci		rc = __read_vmcore(buf, PAGE_SIZE, &offset, 0);
4328c2ecf20Sopenharmony_ci		if (rc < 0) {
4338c2ecf20Sopenharmony_ci			unlock_page(page);
4348c2ecf20Sopenharmony_ci			put_page(page);
4358c2ecf20Sopenharmony_ci			return vmf_error(rc);
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		SetPageUptodate(page);
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci	unlock_page(page);
4408c2ecf20Sopenharmony_ci	vmf->page = page;
4418c2ecf20Sopenharmony_ci	return 0;
4428c2ecf20Sopenharmony_ci#else
4438c2ecf20Sopenharmony_ci	return VM_FAULT_SIGBUS;
4448c2ecf20Sopenharmony_ci#endif
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic const struct vm_operations_struct vmcore_mmap_ops = {
4488c2ecf20Sopenharmony_ci	.fault = mmap_vmcore_fault,
4498c2ecf20Sopenharmony_ci};
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/**
4528c2ecf20Sopenharmony_ci * vmcore_alloc_buf - allocate buffer in vmalloc memory
4538c2ecf20Sopenharmony_ci * @sizez: size of buffer
4548c2ecf20Sopenharmony_ci *
4558c2ecf20Sopenharmony_ci * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap
4568c2ecf20Sopenharmony_ci * the buffer to user-space by means of remap_vmalloc_range().
4578c2ecf20Sopenharmony_ci *
4588c2ecf20Sopenharmony_ci * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is
4598c2ecf20Sopenharmony_ci * disabled and there's no need to allow users to mmap the buffer.
4608c2ecf20Sopenharmony_ci */
4618c2ecf20Sopenharmony_cistatic inline char *vmcore_alloc_buf(size_t size)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU
4648c2ecf20Sopenharmony_ci	return vmalloc_user(size);
4658c2ecf20Sopenharmony_ci#else
4668c2ecf20Sopenharmony_ci	return vzalloc(size);
4678c2ecf20Sopenharmony_ci#endif
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/*
4718c2ecf20Sopenharmony_ci * Disable mmap_vmcore() if CONFIG_MMU is not defined. MMU is
4728c2ecf20Sopenharmony_ci * essential for mmap_vmcore() in order to map physically
4738c2ecf20Sopenharmony_ci * non-contiguous objects (ELF header, ELF note segment and memory
4748c2ecf20Sopenharmony_ci * regions in the 1st kernel pointed to by PT_LOAD entries) into
4758c2ecf20Sopenharmony_ci * virtually contiguous user-space in ELF layout.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_ci#ifdef CONFIG_MMU
4788c2ecf20Sopenharmony_ci/*
4798c2ecf20Sopenharmony_ci * remap_oldmem_pfn_checked - do remap_oldmem_pfn_range replacing all pages
4808c2ecf20Sopenharmony_ci * reported as not being ram with the zero page.
4818c2ecf20Sopenharmony_ci *
4828c2ecf20Sopenharmony_ci * @vma: vm_area_struct describing requested mapping
4838c2ecf20Sopenharmony_ci * @from: start remapping from
4848c2ecf20Sopenharmony_ci * @pfn: page frame number to start remapping to
4858c2ecf20Sopenharmony_ci * @size: remapping size
4868c2ecf20Sopenharmony_ci * @prot: protection bits
4878c2ecf20Sopenharmony_ci *
4888c2ecf20Sopenharmony_ci * Returns zero on success, -EAGAIN on failure.
4898c2ecf20Sopenharmony_ci */
4908c2ecf20Sopenharmony_cistatic int remap_oldmem_pfn_checked(struct vm_area_struct *vma,
4918c2ecf20Sopenharmony_ci				    unsigned long from, unsigned long pfn,
4928c2ecf20Sopenharmony_ci				    unsigned long size, pgprot_t prot)
4938c2ecf20Sopenharmony_ci{
4948c2ecf20Sopenharmony_ci	unsigned long map_size;
4958c2ecf20Sopenharmony_ci	unsigned long pos_start, pos_end, pos;
4968c2ecf20Sopenharmony_ci	unsigned long zeropage_pfn = my_zero_pfn(0);
4978c2ecf20Sopenharmony_ci	size_t len = 0;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	pos_start = pfn;
5008c2ecf20Sopenharmony_ci	pos_end = pfn + (size >> PAGE_SHIFT);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	for (pos = pos_start; pos < pos_end; ++pos) {
5038c2ecf20Sopenharmony_ci		if (!pfn_is_ram(pos)) {
5048c2ecf20Sopenharmony_ci			/*
5058c2ecf20Sopenharmony_ci			 * We hit a page which is not ram. Remap the continuous
5068c2ecf20Sopenharmony_ci			 * region between pos_start and pos-1 and replace
5078c2ecf20Sopenharmony_ci			 * the non-ram page at pos with the zero page.
5088c2ecf20Sopenharmony_ci			 */
5098c2ecf20Sopenharmony_ci			if (pos > pos_start) {
5108c2ecf20Sopenharmony_ci				/* Remap continuous region */
5118c2ecf20Sopenharmony_ci				map_size = (pos - pos_start) << PAGE_SHIFT;
5128c2ecf20Sopenharmony_ci				if (remap_oldmem_pfn_range(vma, from + len,
5138c2ecf20Sopenharmony_ci							   pos_start, map_size,
5148c2ecf20Sopenharmony_ci							   prot))
5158c2ecf20Sopenharmony_ci					goto fail;
5168c2ecf20Sopenharmony_ci				len += map_size;
5178c2ecf20Sopenharmony_ci			}
5188c2ecf20Sopenharmony_ci			/* Remap the zero page */
5198c2ecf20Sopenharmony_ci			if (remap_oldmem_pfn_range(vma, from + len,
5208c2ecf20Sopenharmony_ci						   zeropage_pfn,
5218c2ecf20Sopenharmony_ci						   PAGE_SIZE, prot))
5228c2ecf20Sopenharmony_ci				goto fail;
5238c2ecf20Sopenharmony_ci			len += PAGE_SIZE;
5248c2ecf20Sopenharmony_ci			pos_start = pos + 1;
5258c2ecf20Sopenharmony_ci		}
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci	if (pos > pos_start) {
5288c2ecf20Sopenharmony_ci		/* Remap the rest */
5298c2ecf20Sopenharmony_ci		map_size = (pos - pos_start) << PAGE_SHIFT;
5308c2ecf20Sopenharmony_ci		if (remap_oldmem_pfn_range(vma, from + len, pos_start,
5318c2ecf20Sopenharmony_ci					   map_size, prot))
5328c2ecf20Sopenharmony_ci			goto fail;
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci	return 0;
5358c2ecf20Sopenharmony_cifail:
5368c2ecf20Sopenharmony_ci	do_munmap(vma->vm_mm, from, len, NULL);
5378c2ecf20Sopenharmony_ci	return -EAGAIN;
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic int vmcore_remap_oldmem_pfn(struct vm_area_struct *vma,
5418c2ecf20Sopenharmony_ci			    unsigned long from, unsigned long pfn,
5428c2ecf20Sopenharmony_ci			    unsigned long size, pgprot_t prot)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	/*
5458c2ecf20Sopenharmony_ci	 * Check if oldmem_pfn_is_ram was registered to avoid
5468c2ecf20Sopenharmony_ci	 * looping over all pages without a reason.
5478c2ecf20Sopenharmony_ci	 */
5488c2ecf20Sopenharmony_ci	if (oldmem_pfn_is_ram)
5498c2ecf20Sopenharmony_ci		return remap_oldmem_pfn_checked(vma, from, pfn, size, prot);
5508c2ecf20Sopenharmony_ci	else
5518c2ecf20Sopenharmony_ci		return remap_oldmem_pfn_range(vma, from, pfn, size, prot);
5528c2ecf20Sopenharmony_ci}
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_cistatic int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	size_t size = vma->vm_end - vma->vm_start;
5578c2ecf20Sopenharmony_ci	u64 start, end, len, tsz;
5588c2ecf20Sopenharmony_ci	struct vmcore *m;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	start = (u64)vma->vm_pgoff << PAGE_SHIFT;
5618c2ecf20Sopenharmony_ci	end = start + size;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	if (size > vmcore_size || end > vmcore_size)
5648c2ecf20Sopenharmony_ci		return -EINVAL;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	if (vma->vm_flags & (VM_WRITE | VM_EXEC))
5678c2ecf20Sopenharmony_ci		return -EPERM;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	vma->vm_flags &= ~(VM_MAYWRITE | VM_MAYEXEC);
5708c2ecf20Sopenharmony_ci	vma->vm_flags |= VM_MIXEDMAP;
5718c2ecf20Sopenharmony_ci	vma->vm_ops = &vmcore_mmap_ops;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	len = 0;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	if (start < elfcorebuf_sz) {
5768c2ecf20Sopenharmony_ci		u64 pfn;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		tsz = min(elfcorebuf_sz - (size_t)start, size);
5798c2ecf20Sopenharmony_ci		pfn = __pa(elfcorebuf + start) >> PAGE_SHIFT;
5808c2ecf20Sopenharmony_ci		if (remap_pfn_range(vma, vma->vm_start, pfn, tsz,
5818c2ecf20Sopenharmony_ci				    vma->vm_page_prot))
5828c2ecf20Sopenharmony_ci			return -EAGAIN;
5838c2ecf20Sopenharmony_ci		size -= tsz;
5848c2ecf20Sopenharmony_ci		start += tsz;
5858c2ecf20Sopenharmony_ci		len += tsz;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci		if (size == 0)
5888c2ecf20Sopenharmony_ci			return 0;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	if (start < elfcorebuf_sz + elfnotes_sz) {
5928c2ecf20Sopenharmony_ci		void *kaddr;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		/* We add device dumps before other elf notes because the
5958c2ecf20Sopenharmony_ci		 * other elf notes may not fill the elf notes buffer
5968c2ecf20Sopenharmony_ci		 * completely and we will end up with zero-filled data
5978c2ecf20Sopenharmony_ci		 * between the elf notes and the device dumps. Tools will
5988c2ecf20Sopenharmony_ci		 * then try to decode this zero-filled data as valid notes
5998c2ecf20Sopenharmony_ci		 * and we don't want that. Hence, adding device dumps before
6008c2ecf20Sopenharmony_ci		 * the other elf notes ensure that zero-filled data can be
6018c2ecf20Sopenharmony_ci		 * avoided. This also ensures that the device dumps and
6028c2ecf20Sopenharmony_ci		 * other elf notes can be properly mmaped at page aligned
6038c2ecf20Sopenharmony_ci		 * address.
6048c2ecf20Sopenharmony_ci		 */
6058c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
6068c2ecf20Sopenharmony_ci		/* Read device dumps */
6078c2ecf20Sopenharmony_ci		if (start < elfcorebuf_sz + vmcoredd_orig_sz) {
6088c2ecf20Sopenharmony_ci			u64 start_off;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci			tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
6118c2ecf20Sopenharmony_ci				  (size_t)start, size);
6128c2ecf20Sopenharmony_ci			start_off = start - elfcorebuf_sz;
6138c2ecf20Sopenharmony_ci			if (vmcoredd_mmap_dumps(vma, vma->vm_start + len,
6148c2ecf20Sopenharmony_ci						start_off, tsz))
6158c2ecf20Sopenharmony_ci				goto fail;
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci			size -= tsz;
6188c2ecf20Sopenharmony_ci			start += tsz;
6198c2ecf20Sopenharmony_ci			len += tsz;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci			/* leave now if filled buffer already */
6228c2ecf20Sopenharmony_ci			if (!size)
6238c2ecf20Sopenharmony_ci				return 0;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci		/* Read remaining elf notes */
6288c2ecf20Sopenharmony_ci		tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size);
6298c2ecf20Sopenharmony_ci		kaddr = elfnotes_buf + start - elfcorebuf_sz - vmcoredd_orig_sz;
6308c2ecf20Sopenharmony_ci		if (remap_vmalloc_range_partial(vma, vma->vm_start + len,
6318c2ecf20Sopenharmony_ci						kaddr, 0, tsz))
6328c2ecf20Sopenharmony_ci			goto fail;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci		size -= tsz;
6358c2ecf20Sopenharmony_ci		start += tsz;
6368c2ecf20Sopenharmony_ci		len += tsz;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci		if (size == 0)
6398c2ecf20Sopenharmony_ci			return 0;
6408c2ecf20Sopenharmony_ci	}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	list_for_each_entry(m, &vmcore_list, list) {
6438c2ecf20Sopenharmony_ci		if (start < m->offset + m->size) {
6448c2ecf20Sopenharmony_ci			u64 paddr = 0;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci			tsz = (size_t)min_t(unsigned long long,
6478c2ecf20Sopenharmony_ci					    m->offset + m->size - start, size);
6488c2ecf20Sopenharmony_ci			paddr = m->paddr + start - m->offset;
6498c2ecf20Sopenharmony_ci			if (vmcore_remap_oldmem_pfn(vma, vma->vm_start + len,
6508c2ecf20Sopenharmony_ci						    paddr >> PAGE_SHIFT, tsz,
6518c2ecf20Sopenharmony_ci						    vma->vm_page_prot))
6528c2ecf20Sopenharmony_ci				goto fail;
6538c2ecf20Sopenharmony_ci			size -= tsz;
6548c2ecf20Sopenharmony_ci			start += tsz;
6558c2ecf20Sopenharmony_ci			len += tsz;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci			if (size == 0)
6588c2ecf20Sopenharmony_ci				return 0;
6598c2ecf20Sopenharmony_ci		}
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	return 0;
6638c2ecf20Sopenharmony_cifail:
6648c2ecf20Sopenharmony_ci	do_munmap(vma->vm_mm, vma->vm_start, len, NULL);
6658c2ecf20Sopenharmony_ci	return -EAGAIN;
6668c2ecf20Sopenharmony_ci}
6678c2ecf20Sopenharmony_ci#else
6688c2ecf20Sopenharmony_cistatic int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	return -ENOSYS;
6718c2ecf20Sopenharmony_ci}
6728c2ecf20Sopenharmony_ci#endif
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cistatic const struct proc_ops vmcore_proc_ops = {
6758c2ecf20Sopenharmony_ci	.proc_read	= read_vmcore,
6768c2ecf20Sopenharmony_ci	.proc_lseek	= default_llseek,
6778c2ecf20Sopenharmony_ci	.proc_mmap	= mmap_vmcore,
6788c2ecf20Sopenharmony_ci};
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_cistatic struct vmcore* __init get_new_element(void)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	return kzalloc(sizeof(struct vmcore), GFP_KERNEL);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic u64 get_vmcore_size(size_t elfsz, size_t elfnotesegsz,
6868c2ecf20Sopenharmony_ci			   struct list_head *vc_list)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	u64 size;
6898c2ecf20Sopenharmony_ci	struct vmcore *m;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	size = elfsz + elfnotesegsz;
6928c2ecf20Sopenharmony_ci	list_for_each_entry(m, vc_list, list) {
6938c2ecf20Sopenharmony_ci		size += m->size;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	return size;
6968c2ecf20Sopenharmony_ci}
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci/**
6998c2ecf20Sopenharmony_ci * update_note_header_size_elf64 - update p_memsz member of each PT_NOTE entry
7008c2ecf20Sopenharmony_ci *
7018c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
7028c2ecf20Sopenharmony_ci *
7038c2ecf20Sopenharmony_ci * This function updates p_memsz member of each PT_NOTE entry in the
7048c2ecf20Sopenharmony_ci * program header table pointed to by @ehdr_ptr to real size of ELF
7058c2ecf20Sopenharmony_ci * note segment.
7068c2ecf20Sopenharmony_ci */
7078c2ecf20Sopenharmony_cistatic int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	int i, rc=0;
7108c2ecf20Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
7118c2ecf20Sopenharmony_ci	Elf64_Nhdr *nhdr_ptr;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1);
7148c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
7158c2ecf20Sopenharmony_ci		void *notes_section;
7168c2ecf20Sopenharmony_ci		u64 offset, max_sz, sz, real_sz = 0;
7178c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
7188c2ecf20Sopenharmony_ci			continue;
7198c2ecf20Sopenharmony_ci		max_sz = phdr_ptr->p_memsz;
7208c2ecf20Sopenharmony_ci		offset = phdr_ptr->p_offset;
7218c2ecf20Sopenharmony_ci		notes_section = kmalloc(max_sz, GFP_KERNEL);
7228c2ecf20Sopenharmony_ci		if (!notes_section)
7238c2ecf20Sopenharmony_ci			return -ENOMEM;
7248c2ecf20Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
7258c2ecf20Sopenharmony_ci		if (rc < 0) {
7268c2ecf20Sopenharmony_ci			kfree(notes_section);
7278c2ecf20Sopenharmony_ci			return rc;
7288c2ecf20Sopenharmony_ci		}
7298c2ecf20Sopenharmony_ci		nhdr_ptr = notes_section;
7308c2ecf20Sopenharmony_ci		while (nhdr_ptr->n_namesz != 0) {
7318c2ecf20Sopenharmony_ci			sz = sizeof(Elf64_Nhdr) +
7328c2ecf20Sopenharmony_ci				(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
7338c2ecf20Sopenharmony_ci				(((u64)nhdr_ptr->n_descsz + 3) & ~3);
7348c2ecf20Sopenharmony_ci			if ((real_sz + sz) > max_sz) {
7358c2ecf20Sopenharmony_ci				pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
7368c2ecf20Sopenharmony_ci					nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
7378c2ecf20Sopenharmony_ci				break;
7388c2ecf20Sopenharmony_ci			}
7398c2ecf20Sopenharmony_ci			real_sz += sz;
7408c2ecf20Sopenharmony_ci			nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz);
7418c2ecf20Sopenharmony_ci		}
7428c2ecf20Sopenharmony_ci		kfree(notes_section);
7438c2ecf20Sopenharmony_ci		phdr_ptr->p_memsz = real_sz;
7448c2ecf20Sopenharmony_ci		if (real_sz == 0) {
7458c2ecf20Sopenharmony_ci			pr_warn("Warning: Zero PT_NOTE entries found\n");
7468c2ecf20Sopenharmony_ci		}
7478c2ecf20Sopenharmony_ci	}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	return 0;
7508c2ecf20Sopenharmony_ci}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci/**
7538c2ecf20Sopenharmony_ci * get_note_number_and_size_elf64 - get the number of PT_NOTE program
7548c2ecf20Sopenharmony_ci * headers and sum of real size of their ELF note segment headers and
7558c2ecf20Sopenharmony_ci * data.
7568c2ecf20Sopenharmony_ci *
7578c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
7588c2ecf20Sopenharmony_ci * @nr_ptnote: buffer for the number of PT_NOTE program headers
7598c2ecf20Sopenharmony_ci * @sz_ptnote: buffer for size of unique PT_NOTE program header
7608c2ecf20Sopenharmony_ci *
7618c2ecf20Sopenharmony_ci * This function is used to merge multiple PT_NOTE program headers
7628c2ecf20Sopenharmony_ci * into a unique single one. The resulting unique entry will have
7638c2ecf20Sopenharmony_ci * @sz_ptnote in its phdr->p_mem.
7648c2ecf20Sopenharmony_ci *
7658c2ecf20Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
7668c2ecf20Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf64
7678c2ecf20Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
7688c2ecf20Sopenharmony_ci * size in its p_memsz member.
7698c2ecf20Sopenharmony_ci */
7708c2ecf20Sopenharmony_cistatic int __init get_note_number_and_size_elf64(const Elf64_Ehdr *ehdr_ptr,
7718c2ecf20Sopenharmony_ci						 int *nr_ptnote, u64 *sz_ptnote)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	int i;
7748c2ecf20Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	*nr_ptnote = *sz_ptnote = 0;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1);
7798c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
7808c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
7818c2ecf20Sopenharmony_ci			continue;
7828c2ecf20Sopenharmony_ci		*nr_ptnote += 1;
7838c2ecf20Sopenharmony_ci		*sz_ptnote += phdr_ptr->p_memsz;
7848c2ecf20Sopenharmony_ci	}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	return 0;
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci/**
7908c2ecf20Sopenharmony_ci * copy_notes_elf64 - copy ELF note segments in a given buffer
7918c2ecf20Sopenharmony_ci *
7928c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
7938c2ecf20Sopenharmony_ci * @notes_buf: buffer into which ELF note segments are copied
7948c2ecf20Sopenharmony_ci *
7958c2ecf20Sopenharmony_ci * This function is used to copy ELF note segment in the 1st kernel
7968c2ecf20Sopenharmony_ci * into the buffer @notes_buf in the 2nd kernel. It is assumed that
7978c2ecf20Sopenharmony_ci * size of the buffer @notes_buf is equal to or larger than sum of the
7988c2ecf20Sopenharmony_ci * real ELF note segment headers and data.
7998c2ecf20Sopenharmony_ci *
8008c2ecf20Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
8018c2ecf20Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf64
8028c2ecf20Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
8038c2ecf20Sopenharmony_ci * size in its p_memsz member.
8048c2ecf20Sopenharmony_ci */
8058c2ecf20Sopenharmony_cistatic int __init copy_notes_elf64(const Elf64_Ehdr *ehdr_ptr, char *notes_buf)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	int i, rc=0;
8088c2ecf20Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	phdr_ptr = (Elf64_Phdr*)(ehdr_ptr + 1);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
8138c2ecf20Sopenharmony_ci		u64 offset;
8148c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
8158c2ecf20Sopenharmony_ci			continue;
8168c2ecf20Sopenharmony_ci		offset = phdr_ptr->p_offset;
8178c2ecf20Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
8188c2ecf20Sopenharmony_ci					   &offset);
8198c2ecf20Sopenharmony_ci		if (rc < 0)
8208c2ecf20Sopenharmony_ci			return rc;
8218c2ecf20Sopenharmony_ci		notes_buf += phdr_ptr->p_memsz;
8228c2ecf20Sopenharmony_ci	}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_ci	return 0;
8258c2ecf20Sopenharmony_ci}
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci/* Merges all the PT_NOTE headers into one. */
8288c2ecf20Sopenharmony_cistatic int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
8298c2ecf20Sopenharmony_ci					   char **notes_buf, size_t *notes_sz)
8308c2ecf20Sopenharmony_ci{
8318c2ecf20Sopenharmony_ci	int i, nr_ptnote=0, rc=0;
8328c2ecf20Sopenharmony_ci	char *tmp;
8338c2ecf20Sopenharmony_ci	Elf64_Ehdr *ehdr_ptr;
8348c2ecf20Sopenharmony_ci	Elf64_Phdr phdr;
8358c2ecf20Sopenharmony_ci	u64 phdr_sz = 0, note_off;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	ehdr_ptr = (Elf64_Ehdr *)elfptr;
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci	rc = update_note_header_size_elf64(ehdr_ptr);
8408c2ecf20Sopenharmony_ci	if (rc < 0)
8418c2ecf20Sopenharmony_ci		return rc;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	rc = get_note_number_and_size_elf64(ehdr_ptr, &nr_ptnote, &phdr_sz);
8448c2ecf20Sopenharmony_ci	if (rc < 0)
8458c2ecf20Sopenharmony_ci		return rc;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	*notes_sz = roundup(phdr_sz, PAGE_SIZE);
8488c2ecf20Sopenharmony_ci	*notes_buf = vmcore_alloc_buf(*notes_sz);
8498c2ecf20Sopenharmony_ci	if (!*notes_buf)
8508c2ecf20Sopenharmony_ci		return -ENOMEM;
8518c2ecf20Sopenharmony_ci
8528c2ecf20Sopenharmony_ci	rc = copy_notes_elf64(ehdr_ptr, *notes_buf);
8538c2ecf20Sopenharmony_ci	if (rc < 0)
8548c2ecf20Sopenharmony_ci		return rc;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/* Prepare merged PT_NOTE program header. */
8578c2ecf20Sopenharmony_ci	phdr.p_type    = PT_NOTE;
8588c2ecf20Sopenharmony_ci	phdr.p_flags   = 0;
8598c2ecf20Sopenharmony_ci	note_off = sizeof(Elf64_Ehdr) +
8608c2ecf20Sopenharmony_ci			(ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf64_Phdr);
8618c2ecf20Sopenharmony_ci	phdr.p_offset  = roundup(note_off, PAGE_SIZE);
8628c2ecf20Sopenharmony_ci	phdr.p_vaddr   = phdr.p_paddr = 0;
8638c2ecf20Sopenharmony_ci	phdr.p_filesz  = phdr.p_memsz = phdr_sz;
8648c2ecf20Sopenharmony_ci	phdr.p_align   = 0;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	/* Add merged PT_NOTE program header*/
8678c2ecf20Sopenharmony_ci	tmp = elfptr + sizeof(Elf64_Ehdr);
8688c2ecf20Sopenharmony_ci	memcpy(tmp, &phdr, sizeof(phdr));
8698c2ecf20Sopenharmony_ci	tmp += sizeof(phdr);
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	/* Remove unwanted PT_NOTE program headers. */
8728c2ecf20Sopenharmony_ci	i = (nr_ptnote - 1) * sizeof(Elf64_Phdr);
8738c2ecf20Sopenharmony_ci	*elfsz = *elfsz - i;
8748c2ecf20Sopenharmony_ci	memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr)));
8758c2ecf20Sopenharmony_ci	memset(elfptr + *elfsz, 0, i);
8768c2ecf20Sopenharmony_ci	*elfsz = roundup(*elfsz, PAGE_SIZE);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	/* Modify e_phnum to reflect merged headers. */
8798c2ecf20Sopenharmony_ci	ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	/* Store the size of all notes.  We need this to update the note
8828c2ecf20Sopenharmony_ci	 * header when the device dumps will be added.
8838c2ecf20Sopenharmony_ci	 */
8848c2ecf20Sopenharmony_ci	elfnotes_orig_sz = phdr.p_memsz;
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	return 0;
8878c2ecf20Sopenharmony_ci}
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci/**
8908c2ecf20Sopenharmony_ci * update_note_header_size_elf32 - update p_memsz member of each PT_NOTE entry
8918c2ecf20Sopenharmony_ci *
8928c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
8938c2ecf20Sopenharmony_ci *
8948c2ecf20Sopenharmony_ci * This function updates p_memsz member of each PT_NOTE entry in the
8958c2ecf20Sopenharmony_ci * program header table pointed to by @ehdr_ptr to real size of ELF
8968c2ecf20Sopenharmony_ci * note segment.
8978c2ecf20Sopenharmony_ci */
8988c2ecf20Sopenharmony_cistatic int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	int i, rc=0;
9018c2ecf20Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
9028c2ecf20Sopenharmony_ci	Elf32_Nhdr *nhdr_ptr;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1);
9058c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
9068c2ecf20Sopenharmony_ci		void *notes_section;
9078c2ecf20Sopenharmony_ci		u64 offset, max_sz, sz, real_sz = 0;
9088c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
9098c2ecf20Sopenharmony_ci			continue;
9108c2ecf20Sopenharmony_ci		max_sz = phdr_ptr->p_memsz;
9118c2ecf20Sopenharmony_ci		offset = phdr_ptr->p_offset;
9128c2ecf20Sopenharmony_ci		notes_section = kmalloc(max_sz, GFP_KERNEL);
9138c2ecf20Sopenharmony_ci		if (!notes_section)
9148c2ecf20Sopenharmony_ci			return -ENOMEM;
9158c2ecf20Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
9168c2ecf20Sopenharmony_ci		if (rc < 0) {
9178c2ecf20Sopenharmony_ci			kfree(notes_section);
9188c2ecf20Sopenharmony_ci			return rc;
9198c2ecf20Sopenharmony_ci		}
9208c2ecf20Sopenharmony_ci		nhdr_ptr = notes_section;
9218c2ecf20Sopenharmony_ci		while (nhdr_ptr->n_namesz != 0) {
9228c2ecf20Sopenharmony_ci			sz = sizeof(Elf32_Nhdr) +
9238c2ecf20Sopenharmony_ci				(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
9248c2ecf20Sopenharmony_ci				(((u64)nhdr_ptr->n_descsz + 3) & ~3);
9258c2ecf20Sopenharmony_ci			if ((real_sz + sz) > max_sz) {
9268c2ecf20Sopenharmony_ci				pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
9278c2ecf20Sopenharmony_ci					nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
9288c2ecf20Sopenharmony_ci				break;
9298c2ecf20Sopenharmony_ci			}
9308c2ecf20Sopenharmony_ci			real_sz += sz;
9318c2ecf20Sopenharmony_ci			nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz);
9328c2ecf20Sopenharmony_ci		}
9338c2ecf20Sopenharmony_ci		kfree(notes_section);
9348c2ecf20Sopenharmony_ci		phdr_ptr->p_memsz = real_sz;
9358c2ecf20Sopenharmony_ci		if (real_sz == 0) {
9368c2ecf20Sopenharmony_ci			pr_warn("Warning: Zero PT_NOTE entries found\n");
9378c2ecf20Sopenharmony_ci		}
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	return 0;
9418c2ecf20Sopenharmony_ci}
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci/**
9448c2ecf20Sopenharmony_ci * get_note_number_and_size_elf32 - get the number of PT_NOTE program
9458c2ecf20Sopenharmony_ci * headers and sum of real size of their ELF note segment headers and
9468c2ecf20Sopenharmony_ci * data.
9478c2ecf20Sopenharmony_ci *
9488c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
9498c2ecf20Sopenharmony_ci * @nr_ptnote: buffer for the number of PT_NOTE program headers
9508c2ecf20Sopenharmony_ci * @sz_ptnote: buffer for size of unique PT_NOTE program header
9518c2ecf20Sopenharmony_ci *
9528c2ecf20Sopenharmony_ci * This function is used to merge multiple PT_NOTE program headers
9538c2ecf20Sopenharmony_ci * into a unique single one. The resulting unique entry will have
9548c2ecf20Sopenharmony_ci * @sz_ptnote in its phdr->p_mem.
9558c2ecf20Sopenharmony_ci *
9568c2ecf20Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
9578c2ecf20Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf32
9588c2ecf20Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
9598c2ecf20Sopenharmony_ci * size in its p_memsz member.
9608c2ecf20Sopenharmony_ci */
9618c2ecf20Sopenharmony_cistatic int __init get_note_number_and_size_elf32(const Elf32_Ehdr *ehdr_ptr,
9628c2ecf20Sopenharmony_ci						 int *nr_ptnote, u64 *sz_ptnote)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	int i;
9658c2ecf20Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	*nr_ptnote = *sz_ptnote = 0;
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1);
9708c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
9718c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
9728c2ecf20Sopenharmony_ci			continue;
9738c2ecf20Sopenharmony_ci		*nr_ptnote += 1;
9748c2ecf20Sopenharmony_ci		*sz_ptnote += phdr_ptr->p_memsz;
9758c2ecf20Sopenharmony_ci	}
9768c2ecf20Sopenharmony_ci
9778c2ecf20Sopenharmony_ci	return 0;
9788c2ecf20Sopenharmony_ci}
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci/**
9818c2ecf20Sopenharmony_ci * copy_notes_elf32 - copy ELF note segments in a given buffer
9828c2ecf20Sopenharmony_ci *
9838c2ecf20Sopenharmony_ci * @ehdr_ptr: ELF header
9848c2ecf20Sopenharmony_ci * @notes_buf: buffer into which ELF note segments are copied
9858c2ecf20Sopenharmony_ci *
9868c2ecf20Sopenharmony_ci * This function is used to copy ELF note segment in the 1st kernel
9878c2ecf20Sopenharmony_ci * into the buffer @notes_buf in the 2nd kernel. It is assumed that
9888c2ecf20Sopenharmony_ci * size of the buffer @notes_buf is equal to or larger than sum of the
9898c2ecf20Sopenharmony_ci * real ELF note segment headers and data.
9908c2ecf20Sopenharmony_ci *
9918c2ecf20Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
9928c2ecf20Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf32
9938c2ecf20Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
9948c2ecf20Sopenharmony_ci * size in its p_memsz member.
9958c2ecf20Sopenharmony_ci */
9968c2ecf20Sopenharmony_cistatic int __init copy_notes_elf32(const Elf32_Ehdr *ehdr_ptr, char *notes_buf)
9978c2ecf20Sopenharmony_ci{
9988c2ecf20Sopenharmony_ci	int i, rc=0;
9998c2ecf20Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	phdr_ptr = (Elf32_Phdr*)(ehdr_ptr + 1);
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
10048c2ecf20Sopenharmony_ci		u64 offset;
10058c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
10068c2ecf20Sopenharmony_ci			continue;
10078c2ecf20Sopenharmony_ci		offset = phdr_ptr->p_offset;
10088c2ecf20Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
10098c2ecf20Sopenharmony_ci					   &offset);
10108c2ecf20Sopenharmony_ci		if (rc < 0)
10118c2ecf20Sopenharmony_ci			return rc;
10128c2ecf20Sopenharmony_ci		notes_buf += phdr_ptr->p_memsz;
10138c2ecf20Sopenharmony_ci	}
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci	return 0;
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci/* Merges all the PT_NOTE headers into one. */
10198c2ecf20Sopenharmony_cistatic int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz,
10208c2ecf20Sopenharmony_ci					   char **notes_buf, size_t *notes_sz)
10218c2ecf20Sopenharmony_ci{
10228c2ecf20Sopenharmony_ci	int i, nr_ptnote=0, rc=0;
10238c2ecf20Sopenharmony_ci	char *tmp;
10248c2ecf20Sopenharmony_ci	Elf32_Ehdr *ehdr_ptr;
10258c2ecf20Sopenharmony_ci	Elf32_Phdr phdr;
10268c2ecf20Sopenharmony_ci	u64 phdr_sz = 0, note_off;
10278c2ecf20Sopenharmony_ci
10288c2ecf20Sopenharmony_ci	ehdr_ptr = (Elf32_Ehdr *)elfptr;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	rc = update_note_header_size_elf32(ehdr_ptr);
10318c2ecf20Sopenharmony_ci	if (rc < 0)
10328c2ecf20Sopenharmony_ci		return rc;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	rc = get_note_number_and_size_elf32(ehdr_ptr, &nr_ptnote, &phdr_sz);
10358c2ecf20Sopenharmony_ci	if (rc < 0)
10368c2ecf20Sopenharmony_ci		return rc;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	*notes_sz = roundup(phdr_sz, PAGE_SIZE);
10398c2ecf20Sopenharmony_ci	*notes_buf = vmcore_alloc_buf(*notes_sz);
10408c2ecf20Sopenharmony_ci	if (!*notes_buf)
10418c2ecf20Sopenharmony_ci		return -ENOMEM;
10428c2ecf20Sopenharmony_ci
10438c2ecf20Sopenharmony_ci	rc = copy_notes_elf32(ehdr_ptr, *notes_buf);
10448c2ecf20Sopenharmony_ci	if (rc < 0)
10458c2ecf20Sopenharmony_ci		return rc;
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	/* Prepare merged PT_NOTE program header. */
10488c2ecf20Sopenharmony_ci	phdr.p_type    = PT_NOTE;
10498c2ecf20Sopenharmony_ci	phdr.p_flags   = 0;
10508c2ecf20Sopenharmony_ci	note_off = sizeof(Elf32_Ehdr) +
10518c2ecf20Sopenharmony_ci			(ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr);
10528c2ecf20Sopenharmony_ci	phdr.p_offset  = roundup(note_off, PAGE_SIZE);
10538c2ecf20Sopenharmony_ci	phdr.p_vaddr   = phdr.p_paddr = 0;
10548c2ecf20Sopenharmony_ci	phdr.p_filesz  = phdr.p_memsz = phdr_sz;
10558c2ecf20Sopenharmony_ci	phdr.p_align   = 0;
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	/* Add merged PT_NOTE program header*/
10588c2ecf20Sopenharmony_ci	tmp = elfptr + sizeof(Elf32_Ehdr);
10598c2ecf20Sopenharmony_ci	memcpy(tmp, &phdr, sizeof(phdr));
10608c2ecf20Sopenharmony_ci	tmp += sizeof(phdr);
10618c2ecf20Sopenharmony_ci
10628c2ecf20Sopenharmony_ci	/* Remove unwanted PT_NOTE program headers. */
10638c2ecf20Sopenharmony_ci	i = (nr_ptnote - 1) * sizeof(Elf32_Phdr);
10648c2ecf20Sopenharmony_ci	*elfsz = *elfsz - i;
10658c2ecf20Sopenharmony_ci	memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr)));
10668c2ecf20Sopenharmony_ci	memset(elfptr + *elfsz, 0, i);
10678c2ecf20Sopenharmony_ci	*elfsz = roundup(*elfsz, PAGE_SIZE);
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	/* Modify e_phnum to reflect merged headers. */
10708c2ecf20Sopenharmony_ci	ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/* Store the size of all notes.  We need this to update the note
10738c2ecf20Sopenharmony_ci	 * header when the device dumps will be added.
10748c2ecf20Sopenharmony_ci	 */
10758c2ecf20Sopenharmony_ci	elfnotes_orig_sz = phdr.p_memsz;
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	return 0;
10788c2ecf20Sopenharmony_ci}
10798c2ecf20Sopenharmony_ci
10808c2ecf20Sopenharmony_ci/* Add memory chunks represented by program headers to vmcore list. Also update
10818c2ecf20Sopenharmony_ci * the new offset fields of exported program headers. */
10828c2ecf20Sopenharmony_cistatic int __init process_ptload_program_headers_elf64(char *elfptr,
10838c2ecf20Sopenharmony_ci						size_t elfsz,
10848c2ecf20Sopenharmony_ci						size_t elfnotes_sz,
10858c2ecf20Sopenharmony_ci						struct list_head *vc_list)
10868c2ecf20Sopenharmony_ci{
10878c2ecf20Sopenharmony_ci	int i;
10888c2ecf20Sopenharmony_ci	Elf64_Ehdr *ehdr_ptr;
10898c2ecf20Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
10908c2ecf20Sopenharmony_ci	loff_t vmcore_off;
10918c2ecf20Sopenharmony_ci	struct vmcore *new;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	ehdr_ptr = (Elf64_Ehdr *)elfptr;
10948c2ecf20Sopenharmony_ci	phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/* Skip Elf header, program headers and Elf note segment. */
10978c2ecf20Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
10988c2ecf20Sopenharmony_ci
10998c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
11008c2ecf20Sopenharmony_ci		u64 paddr, start, end, size;
11018c2ecf20Sopenharmony_ci
11028c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_LOAD)
11038c2ecf20Sopenharmony_ci			continue;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		paddr = phdr_ptr->p_offset;
11068c2ecf20Sopenharmony_ci		start = rounddown(paddr, PAGE_SIZE);
11078c2ecf20Sopenharmony_ci		end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE);
11088c2ecf20Sopenharmony_ci		size = end - start;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		/* Add this contiguous chunk of memory to vmcore list.*/
11118c2ecf20Sopenharmony_ci		new = get_new_element();
11128c2ecf20Sopenharmony_ci		if (!new)
11138c2ecf20Sopenharmony_ci			return -ENOMEM;
11148c2ecf20Sopenharmony_ci		new->paddr = start;
11158c2ecf20Sopenharmony_ci		new->size = size;
11168c2ecf20Sopenharmony_ci		list_add_tail(&new->list, vc_list);
11178c2ecf20Sopenharmony_ci
11188c2ecf20Sopenharmony_ci		/* Update the program header offset. */
11198c2ecf20Sopenharmony_ci		phdr_ptr->p_offset = vmcore_off + (paddr - start);
11208c2ecf20Sopenharmony_ci		vmcore_off = vmcore_off + size;
11218c2ecf20Sopenharmony_ci	}
11228c2ecf20Sopenharmony_ci	return 0;
11238c2ecf20Sopenharmony_ci}
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_cistatic int __init process_ptload_program_headers_elf32(char *elfptr,
11268c2ecf20Sopenharmony_ci						size_t elfsz,
11278c2ecf20Sopenharmony_ci						size_t elfnotes_sz,
11288c2ecf20Sopenharmony_ci						struct list_head *vc_list)
11298c2ecf20Sopenharmony_ci{
11308c2ecf20Sopenharmony_ci	int i;
11318c2ecf20Sopenharmony_ci	Elf32_Ehdr *ehdr_ptr;
11328c2ecf20Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
11338c2ecf20Sopenharmony_ci	loff_t vmcore_off;
11348c2ecf20Sopenharmony_ci	struct vmcore *new;
11358c2ecf20Sopenharmony_ci
11368c2ecf20Sopenharmony_ci	ehdr_ptr = (Elf32_Ehdr *)elfptr;
11378c2ecf20Sopenharmony_ci	phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Skip Elf header, program headers and Elf note segment. */
11408c2ecf20Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
11438c2ecf20Sopenharmony_ci		u64 paddr, start, end, size;
11448c2ecf20Sopenharmony_ci
11458c2ecf20Sopenharmony_ci		if (phdr_ptr->p_type != PT_LOAD)
11468c2ecf20Sopenharmony_ci			continue;
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_ci		paddr = phdr_ptr->p_offset;
11498c2ecf20Sopenharmony_ci		start = rounddown(paddr, PAGE_SIZE);
11508c2ecf20Sopenharmony_ci		end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE);
11518c2ecf20Sopenharmony_ci		size = end - start;
11528c2ecf20Sopenharmony_ci
11538c2ecf20Sopenharmony_ci		/* Add this contiguous chunk of memory to vmcore list.*/
11548c2ecf20Sopenharmony_ci		new = get_new_element();
11558c2ecf20Sopenharmony_ci		if (!new)
11568c2ecf20Sopenharmony_ci			return -ENOMEM;
11578c2ecf20Sopenharmony_ci		new->paddr = start;
11588c2ecf20Sopenharmony_ci		new->size = size;
11598c2ecf20Sopenharmony_ci		list_add_tail(&new->list, vc_list);
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ci		/* Update the program header offset */
11628c2ecf20Sopenharmony_ci		phdr_ptr->p_offset = vmcore_off + (paddr - start);
11638c2ecf20Sopenharmony_ci		vmcore_off = vmcore_off + size;
11648c2ecf20Sopenharmony_ci	}
11658c2ecf20Sopenharmony_ci	return 0;
11668c2ecf20Sopenharmony_ci}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci/* Sets offset fields of vmcore elements. */
11698c2ecf20Sopenharmony_cistatic void set_vmcore_list_offsets(size_t elfsz, size_t elfnotes_sz,
11708c2ecf20Sopenharmony_ci				    struct list_head *vc_list)
11718c2ecf20Sopenharmony_ci{
11728c2ecf20Sopenharmony_ci	loff_t vmcore_off;
11738c2ecf20Sopenharmony_ci	struct vmcore *m;
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci	/* Skip Elf header, program headers and Elf note segment. */
11768c2ecf20Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci	list_for_each_entry(m, vc_list, list) {
11798c2ecf20Sopenharmony_ci		m->offset = vmcore_off;
11808c2ecf20Sopenharmony_ci		vmcore_off += m->size;
11818c2ecf20Sopenharmony_ci	}
11828c2ecf20Sopenharmony_ci}
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_cistatic void free_elfcorebuf(void)
11858c2ecf20Sopenharmony_ci{
11868c2ecf20Sopenharmony_ci	free_pages((unsigned long)elfcorebuf, get_order(elfcorebuf_sz_orig));
11878c2ecf20Sopenharmony_ci	elfcorebuf = NULL;
11888c2ecf20Sopenharmony_ci	vfree(elfnotes_buf);
11898c2ecf20Sopenharmony_ci	elfnotes_buf = NULL;
11908c2ecf20Sopenharmony_ci}
11918c2ecf20Sopenharmony_ci
11928c2ecf20Sopenharmony_cistatic int __init parse_crash_elf64_headers(void)
11938c2ecf20Sopenharmony_ci{
11948c2ecf20Sopenharmony_ci	int rc=0;
11958c2ecf20Sopenharmony_ci	Elf64_Ehdr ehdr;
11968c2ecf20Sopenharmony_ci	u64 addr;
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	addr = elfcorehdr_addr;
11998c2ecf20Sopenharmony_ci
12008c2ecf20Sopenharmony_ci	/* Read Elf header */
12018c2ecf20Sopenharmony_ci	rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf64_Ehdr), &addr);
12028c2ecf20Sopenharmony_ci	if (rc < 0)
12038c2ecf20Sopenharmony_ci		return rc;
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci	/* Do some basic Verification. */
12068c2ecf20Sopenharmony_ci	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
12078c2ecf20Sopenharmony_ci		(ehdr.e_type != ET_CORE) ||
12088c2ecf20Sopenharmony_ci		!vmcore_elf64_check_arch(&ehdr) ||
12098c2ecf20Sopenharmony_ci		ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
12108c2ecf20Sopenharmony_ci		ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
12118c2ecf20Sopenharmony_ci		ehdr.e_version != EV_CURRENT ||
12128c2ecf20Sopenharmony_ci		ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
12138c2ecf20Sopenharmony_ci		ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
12148c2ecf20Sopenharmony_ci		ehdr.e_phnum == 0) {
12158c2ecf20Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
12168c2ecf20Sopenharmony_ci		return -EINVAL;
12178c2ecf20Sopenharmony_ci	}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	/* Read in all elf headers. */
12208c2ecf20Sopenharmony_ci	elfcorebuf_sz_orig = sizeof(Elf64_Ehdr) +
12218c2ecf20Sopenharmony_ci				ehdr.e_phnum * sizeof(Elf64_Phdr);
12228c2ecf20Sopenharmony_ci	elfcorebuf_sz = elfcorebuf_sz_orig;
12238c2ecf20Sopenharmony_ci	elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
12248c2ecf20Sopenharmony_ci					      get_order(elfcorebuf_sz_orig));
12258c2ecf20Sopenharmony_ci	if (!elfcorebuf)
12268c2ecf20Sopenharmony_ci		return -ENOMEM;
12278c2ecf20Sopenharmony_ci	addr = elfcorehdr_addr;
12288c2ecf20Sopenharmony_ci	rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
12298c2ecf20Sopenharmony_ci	if (rc < 0)
12308c2ecf20Sopenharmony_ci		goto fail;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci	/* Merge all PT_NOTE headers into one. */
12338c2ecf20Sopenharmony_ci	rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz,
12348c2ecf20Sopenharmony_ci				      &elfnotes_buf, &elfnotes_sz);
12358c2ecf20Sopenharmony_ci	if (rc)
12368c2ecf20Sopenharmony_ci		goto fail;
12378c2ecf20Sopenharmony_ci	rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz,
12388c2ecf20Sopenharmony_ci						  elfnotes_sz, &vmcore_list);
12398c2ecf20Sopenharmony_ci	if (rc)
12408c2ecf20Sopenharmony_ci		goto fail;
12418c2ecf20Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_cifail:
12448c2ecf20Sopenharmony_ci	free_elfcorebuf();
12458c2ecf20Sopenharmony_ci	return rc;
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_cistatic int __init parse_crash_elf32_headers(void)
12498c2ecf20Sopenharmony_ci{
12508c2ecf20Sopenharmony_ci	int rc=0;
12518c2ecf20Sopenharmony_ci	Elf32_Ehdr ehdr;
12528c2ecf20Sopenharmony_ci	u64 addr;
12538c2ecf20Sopenharmony_ci
12548c2ecf20Sopenharmony_ci	addr = elfcorehdr_addr;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	/* Read Elf header */
12578c2ecf20Sopenharmony_ci	rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf32_Ehdr), &addr);
12588c2ecf20Sopenharmony_ci	if (rc < 0)
12598c2ecf20Sopenharmony_ci		return rc;
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_ci	/* Do some basic Verification. */
12628c2ecf20Sopenharmony_ci	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
12638c2ecf20Sopenharmony_ci		(ehdr.e_type != ET_CORE) ||
12648c2ecf20Sopenharmony_ci		!vmcore_elf32_check_arch(&ehdr) ||
12658c2ecf20Sopenharmony_ci		ehdr.e_ident[EI_CLASS] != ELFCLASS32||
12668c2ecf20Sopenharmony_ci		ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
12678c2ecf20Sopenharmony_ci		ehdr.e_version != EV_CURRENT ||
12688c2ecf20Sopenharmony_ci		ehdr.e_ehsize != sizeof(Elf32_Ehdr) ||
12698c2ecf20Sopenharmony_ci		ehdr.e_phentsize != sizeof(Elf32_Phdr) ||
12708c2ecf20Sopenharmony_ci		ehdr.e_phnum == 0) {
12718c2ecf20Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
12728c2ecf20Sopenharmony_ci		return -EINVAL;
12738c2ecf20Sopenharmony_ci	}
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	/* Read in all elf headers. */
12768c2ecf20Sopenharmony_ci	elfcorebuf_sz_orig = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr);
12778c2ecf20Sopenharmony_ci	elfcorebuf_sz = elfcorebuf_sz_orig;
12788c2ecf20Sopenharmony_ci	elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
12798c2ecf20Sopenharmony_ci					      get_order(elfcorebuf_sz_orig));
12808c2ecf20Sopenharmony_ci	if (!elfcorebuf)
12818c2ecf20Sopenharmony_ci		return -ENOMEM;
12828c2ecf20Sopenharmony_ci	addr = elfcorehdr_addr;
12838c2ecf20Sopenharmony_ci	rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
12848c2ecf20Sopenharmony_ci	if (rc < 0)
12858c2ecf20Sopenharmony_ci		goto fail;
12868c2ecf20Sopenharmony_ci
12878c2ecf20Sopenharmony_ci	/* Merge all PT_NOTE headers into one. */
12888c2ecf20Sopenharmony_ci	rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz,
12898c2ecf20Sopenharmony_ci				      &elfnotes_buf, &elfnotes_sz);
12908c2ecf20Sopenharmony_ci	if (rc)
12918c2ecf20Sopenharmony_ci		goto fail;
12928c2ecf20Sopenharmony_ci	rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz,
12938c2ecf20Sopenharmony_ci						  elfnotes_sz, &vmcore_list);
12948c2ecf20Sopenharmony_ci	if (rc)
12958c2ecf20Sopenharmony_ci		goto fail;
12968c2ecf20Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
12978c2ecf20Sopenharmony_ci	return 0;
12988c2ecf20Sopenharmony_cifail:
12998c2ecf20Sopenharmony_ci	free_elfcorebuf();
13008c2ecf20Sopenharmony_ci	return rc;
13018c2ecf20Sopenharmony_ci}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_cistatic int __init parse_crash_elf_headers(void)
13048c2ecf20Sopenharmony_ci{
13058c2ecf20Sopenharmony_ci	unsigned char e_ident[EI_NIDENT];
13068c2ecf20Sopenharmony_ci	u64 addr;
13078c2ecf20Sopenharmony_ci	int rc=0;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	addr = elfcorehdr_addr;
13108c2ecf20Sopenharmony_ci	rc = elfcorehdr_read(e_ident, EI_NIDENT, &addr);
13118c2ecf20Sopenharmony_ci	if (rc < 0)
13128c2ecf20Sopenharmony_ci		return rc;
13138c2ecf20Sopenharmony_ci	if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
13148c2ecf20Sopenharmony_ci		pr_warn("Warning: Core image elf header not found\n");
13158c2ecf20Sopenharmony_ci		return -EINVAL;
13168c2ecf20Sopenharmony_ci	}
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	if (e_ident[EI_CLASS] == ELFCLASS64) {
13198c2ecf20Sopenharmony_ci		rc = parse_crash_elf64_headers();
13208c2ecf20Sopenharmony_ci		if (rc)
13218c2ecf20Sopenharmony_ci			return rc;
13228c2ecf20Sopenharmony_ci	} else if (e_ident[EI_CLASS] == ELFCLASS32) {
13238c2ecf20Sopenharmony_ci		rc = parse_crash_elf32_headers();
13248c2ecf20Sopenharmony_ci		if (rc)
13258c2ecf20Sopenharmony_ci			return rc;
13268c2ecf20Sopenharmony_ci	} else {
13278c2ecf20Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
13288c2ecf20Sopenharmony_ci		return -EINVAL;
13298c2ecf20Sopenharmony_ci	}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	/* Determine vmcore size. */
13328c2ecf20Sopenharmony_ci	vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz,
13338c2ecf20Sopenharmony_ci				      &vmcore_list);
13348c2ecf20Sopenharmony_ci
13358c2ecf20Sopenharmony_ci	return 0;
13368c2ecf20Sopenharmony_ci}
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
13398c2ecf20Sopenharmony_ci/**
13408c2ecf20Sopenharmony_ci * vmcoredd_write_header - Write vmcore device dump header at the
13418c2ecf20Sopenharmony_ci * beginning of the dump's buffer.
13428c2ecf20Sopenharmony_ci * @buf: Output buffer where the note is written
13438c2ecf20Sopenharmony_ci * @data: Dump info
13448c2ecf20Sopenharmony_ci * @size: Size of the dump
13458c2ecf20Sopenharmony_ci *
13468c2ecf20Sopenharmony_ci * Fills beginning of the dump's buffer with vmcore device dump header.
13478c2ecf20Sopenharmony_ci */
13488c2ecf20Sopenharmony_cistatic void vmcoredd_write_header(void *buf, struct vmcoredd_data *data,
13498c2ecf20Sopenharmony_ci				  u32 size)
13508c2ecf20Sopenharmony_ci{
13518c2ecf20Sopenharmony_ci	struct vmcoredd_header *vdd_hdr = (struct vmcoredd_header *)buf;
13528c2ecf20Sopenharmony_ci
13538c2ecf20Sopenharmony_ci	vdd_hdr->n_namesz = sizeof(vdd_hdr->name);
13548c2ecf20Sopenharmony_ci	vdd_hdr->n_descsz = size + sizeof(vdd_hdr->dump_name);
13558c2ecf20Sopenharmony_ci	vdd_hdr->n_type = NT_VMCOREDD;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	strncpy((char *)vdd_hdr->name, VMCOREDD_NOTE_NAME,
13588c2ecf20Sopenharmony_ci		sizeof(vdd_hdr->name));
13598c2ecf20Sopenharmony_ci	memcpy(vdd_hdr->dump_name, data->dump_name, sizeof(vdd_hdr->dump_name));
13608c2ecf20Sopenharmony_ci}
13618c2ecf20Sopenharmony_ci
13628c2ecf20Sopenharmony_ci/**
13638c2ecf20Sopenharmony_ci * vmcoredd_update_program_headers - Update all Elf program headers
13648c2ecf20Sopenharmony_ci * @elfptr: Pointer to elf header
13658c2ecf20Sopenharmony_ci * @elfnotesz: Size of elf notes aligned to page size
13668c2ecf20Sopenharmony_ci * @vmcoreddsz: Size of device dumps to be added to elf note header
13678c2ecf20Sopenharmony_ci *
13688c2ecf20Sopenharmony_ci * Determine type of Elf header (Elf64 or Elf32) and update the elf note size.
13698c2ecf20Sopenharmony_ci * Also update the offsets of all the program headers after the elf note header.
13708c2ecf20Sopenharmony_ci */
13718c2ecf20Sopenharmony_cistatic void vmcoredd_update_program_headers(char *elfptr, size_t elfnotesz,
13728c2ecf20Sopenharmony_ci					    size_t vmcoreddsz)
13738c2ecf20Sopenharmony_ci{
13748c2ecf20Sopenharmony_ci	unsigned char *e_ident = (unsigned char *)elfptr;
13758c2ecf20Sopenharmony_ci	u64 start, end, size;
13768c2ecf20Sopenharmony_ci	loff_t vmcore_off;
13778c2ecf20Sopenharmony_ci	u32 i;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	vmcore_off = elfcorebuf_sz + elfnotesz;
13808c2ecf20Sopenharmony_ci
13818c2ecf20Sopenharmony_ci	if (e_ident[EI_CLASS] == ELFCLASS64) {
13828c2ecf20Sopenharmony_ci		Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfptr;
13838c2ecf20Sopenharmony_ci		Elf64_Phdr *phdr = (Elf64_Phdr *)(elfptr + sizeof(Elf64_Ehdr));
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci		/* Update all program headers */
13868c2ecf20Sopenharmony_ci		for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
13878c2ecf20Sopenharmony_ci			if (phdr->p_type == PT_NOTE) {
13888c2ecf20Sopenharmony_ci				/* Update note size */
13898c2ecf20Sopenharmony_ci				phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
13908c2ecf20Sopenharmony_ci				phdr->p_filesz = phdr->p_memsz;
13918c2ecf20Sopenharmony_ci				continue;
13928c2ecf20Sopenharmony_ci			}
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci			start = rounddown(phdr->p_offset, PAGE_SIZE);
13958c2ecf20Sopenharmony_ci			end = roundup(phdr->p_offset + phdr->p_memsz,
13968c2ecf20Sopenharmony_ci				      PAGE_SIZE);
13978c2ecf20Sopenharmony_ci			size = end - start;
13988c2ecf20Sopenharmony_ci			phdr->p_offset = vmcore_off + (phdr->p_offset - start);
13998c2ecf20Sopenharmony_ci			vmcore_off += size;
14008c2ecf20Sopenharmony_ci		}
14018c2ecf20Sopenharmony_ci	} else {
14028c2ecf20Sopenharmony_ci		Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfptr;
14038c2ecf20Sopenharmony_ci		Elf32_Phdr *phdr = (Elf32_Phdr *)(elfptr + sizeof(Elf32_Ehdr));
14048c2ecf20Sopenharmony_ci
14058c2ecf20Sopenharmony_ci		/* Update all program headers */
14068c2ecf20Sopenharmony_ci		for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
14078c2ecf20Sopenharmony_ci			if (phdr->p_type == PT_NOTE) {
14088c2ecf20Sopenharmony_ci				/* Update note size */
14098c2ecf20Sopenharmony_ci				phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
14108c2ecf20Sopenharmony_ci				phdr->p_filesz = phdr->p_memsz;
14118c2ecf20Sopenharmony_ci				continue;
14128c2ecf20Sopenharmony_ci			}
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci			start = rounddown(phdr->p_offset, PAGE_SIZE);
14158c2ecf20Sopenharmony_ci			end = roundup(phdr->p_offset + phdr->p_memsz,
14168c2ecf20Sopenharmony_ci				      PAGE_SIZE);
14178c2ecf20Sopenharmony_ci			size = end - start;
14188c2ecf20Sopenharmony_ci			phdr->p_offset = vmcore_off + (phdr->p_offset - start);
14198c2ecf20Sopenharmony_ci			vmcore_off += size;
14208c2ecf20Sopenharmony_ci		}
14218c2ecf20Sopenharmony_ci	}
14228c2ecf20Sopenharmony_ci}
14238c2ecf20Sopenharmony_ci
14248c2ecf20Sopenharmony_ci/**
14258c2ecf20Sopenharmony_ci * vmcoredd_update_size - Update the total size of the device dumps and update
14268c2ecf20Sopenharmony_ci * Elf header
14278c2ecf20Sopenharmony_ci * @dump_size: Size of the current device dump to be added to total size
14288c2ecf20Sopenharmony_ci *
14298c2ecf20Sopenharmony_ci * Update the total size of all the device dumps and update the Elf program
14308c2ecf20Sopenharmony_ci * headers. Calculate the new offsets for the vmcore list and update the
14318c2ecf20Sopenharmony_ci * total vmcore size.
14328c2ecf20Sopenharmony_ci */
14338c2ecf20Sopenharmony_cistatic void vmcoredd_update_size(size_t dump_size)
14348c2ecf20Sopenharmony_ci{
14358c2ecf20Sopenharmony_ci	vmcoredd_orig_sz += dump_size;
14368c2ecf20Sopenharmony_ci	elfnotes_sz = roundup(elfnotes_orig_sz, PAGE_SIZE) + vmcoredd_orig_sz;
14378c2ecf20Sopenharmony_ci	vmcoredd_update_program_headers(elfcorebuf, elfnotes_sz,
14388c2ecf20Sopenharmony_ci					vmcoredd_orig_sz);
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci	/* Update vmcore list offsets */
14418c2ecf20Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz,
14448c2ecf20Sopenharmony_ci				      &vmcore_list);
14458c2ecf20Sopenharmony_ci	proc_vmcore->size = vmcore_size;
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci/**
14498c2ecf20Sopenharmony_ci * vmcore_add_device_dump - Add a buffer containing device dump to vmcore
14508c2ecf20Sopenharmony_ci * @data: dump info.
14518c2ecf20Sopenharmony_ci *
14528c2ecf20Sopenharmony_ci * Allocate a buffer and invoke the calling driver's dump collect routine.
14538c2ecf20Sopenharmony_ci * Write Elf note at the beginning of the buffer to indicate vmcore device
14548c2ecf20Sopenharmony_ci * dump and add the dump to global list.
14558c2ecf20Sopenharmony_ci */
14568c2ecf20Sopenharmony_ciint vmcore_add_device_dump(struct vmcoredd_data *data)
14578c2ecf20Sopenharmony_ci{
14588c2ecf20Sopenharmony_ci	struct vmcoredd_node *dump;
14598c2ecf20Sopenharmony_ci	void *buf = NULL;
14608c2ecf20Sopenharmony_ci	size_t data_size;
14618c2ecf20Sopenharmony_ci	int ret;
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (vmcoredd_disabled) {
14648c2ecf20Sopenharmony_ci		pr_err_once("Device dump is disabled\n");
14658c2ecf20Sopenharmony_ci		return -EINVAL;
14668c2ecf20Sopenharmony_ci	}
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	if (!data || !strlen(data->dump_name) ||
14698c2ecf20Sopenharmony_ci	    !data->vmcoredd_callback || !data->size)
14708c2ecf20Sopenharmony_ci		return -EINVAL;
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci	dump = vzalloc(sizeof(*dump));
14738c2ecf20Sopenharmony_ci	if (!dump) {
14748c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14758c2ecf20Sopenharmony_ci		goto out_err;
14768c2ecf20Sopenharmony_ci	}
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	/* Keep size of the buffer page aligned so that it can be mmaped */
14798c2ecf20Sopenharmony_ci	data_size = roundup(sizeof(struct vmcoredd_header) + data->size,
14808c2ecf20Sopenharmony_ci			    PAGE_SIZE);
14818c2ecf20Sopenharmony_ci
14828c2ecf20Sopenharmony_ci	/* Allocate buffer for driver's to write their dumps */
14838c2ecf20Sopenharmony_ci	buf = vmcore_alloc_buf(data_size);
14848c2ecf20Sopenharmony_ci	if (!buf) {
14858c2ecf20Sopenharmony_ci		ret = -ENOMEM;
14868c2ecf20Sopenharmony_ci		goto out_err;
14878c2ecf20Sopenharmony_ci	}
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	vmcoredd_write_header(buf, data, data_size -
14908c2ecf20Sopenharmony_ci			      sizeof(struct vmcoredd_header));
14918c2ecf20Sopenharmony_ci
14928c2ecf20Sopenharmony_ci	/* Invoke the driver's dump collection routing */
14938c2ecf20Sopenharmony_ci	ret = data->vmcoredd_callback(data, buf +
14948c2ecf20Sopenharmony_ci				      sizeof(struct vmcoredd_header));
14958c2ecf20Sopenharmony_ci	if (ret)
14968c2ecf20Sopenharmony_ci		goto out_err;
14978c2ecf20Sopenharmony_ci
14988c2ecf20Sopenharmony_ci	dump->buf = buf;
14998c2ecf20Sopenharmony_ci	dump->size = data_size;
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci	/* Add the dump to driver sysfs list */
15028c2ecf20Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
15038c2ecf20Sopenharmony_ci	list_add_tail(&dump->list, &vmcoredd_list);
15048c2ecf20Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci	vmcoredd_update_size(data_size);
15078c2ecf20Sopenharmony_ci	return 0;
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ciout_err:
15108c2ecf20Sopenharmony_ci	if (buf)
15118c2ecf20Sopenharmony_ci		vfree(buf);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci	if (dump)
15148c2ecf20Sopenharmony_ci		vfree(dump);
15158c2ecf20Sopenharmony_ci
15168c2ecf20Sopenharmony_ci	return ret;
15178c2ecf20Sopenharmony_ci}
15188c2ecf20Sopenharmony_ciEXPORT_SYMBOL(vmcore_add_device_dump);
15198c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
15208c2ecf20Sopenharmony_ci
15218c2ecf20Sopenharmony_ci/* Free all dumps in vmcore device dump list */
15228c2ecf20Sopenharmony_cistatic void vmcore_free_device_dumps(void)
15238c2ecf20Sopenharmony_ci{
15248c2ecf20Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
15258c2ecf20Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
15268c2ecf20Sopenharmony_ci	while (!list_empty(&vmcoredd_list)) {
15278c2ecf20Sopenharmony_ci		struct vmcoredd_node *dump;
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci		dump = list_first_entry(&vmcoredd_list, struct vmcoredd_node,
15308c2ecf20Sopenharmony_ci					list);
15318c2ecf20Sopenharmony_ci		list_del(&dump->list);
15328c2ecf20Sopenharmony_ci		vfree(dump->buf);
15338c2ecf20Sopenharmony_ci		vfree(dump);
15348c2ecf20Sopenharmony_ci	}
15358c2ecf20Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
15368c2ecf20Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
15378c2ecf20Sopenharmony_ci}
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci/* Init function for vmcore module. */
15408c2ecf20Sopenharmony_cistatic int __init vmcore_init(void)
15418c2ecf20Sopenharmony_ci{
15428c2ecf20Sopenharmony_ci	int rc = 0;
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci	/* Allow architectures to allocate ELF header in 2nd kernel */
15458c2ecf20Sopenharmony_ci	rc = elfcorehdr_alloc(&elfcorehdr_addr, &elfcorehdr_size);
15468c2ecf20Sopenharmony_ci	if (rc)
15478c2ecf20Sopenharmony_ci		return rc;
15488c2ecf20Sopenharmony_ci	/*
15498c2ecf20Sopenharmony_ci	 * If elfcorehdr= has been passed in cmdline or created in 2nd kernel,
15508c2ecf20Sopenharmony_ci	 * then capture the dump.
15518c2ecf20Sopenharmony_ci	 */
15528c2ecf20Sopenharmony_ci	if (!(is_vmcore_usable()))
15538c2ecf20Sopenharmony_ci		return rc;
15548c2ecf20Sopenharmony_ci	rc = parse_crash_elf_headers();
15558c2ecf20Sopenharmony_ci	if (rc) {
15568c2ecf20Sopenharmony_ci		pr_warn("Kdump: vmcore not initialized\n");
15578c2ecf20Sopenharmony_ci		return rc;
15588c2ecf20Sopenharmony_ci	}
15598c2ecf20Sopenharmony_ci	elfcorehdr_free(elfcorehdr_addr);
15608c2ecf20Sopenharmony_ci	elfcorehdr_addr = ELFCORE_ADDR_ERR;
15618c2ecf20Sopenharmony_ci
15628c2ecf20Sopenharmony_ci	proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &vmcore_proc_ops);
15638c2ecf20Sopenharmony_ci	if (proc_vmcore)
15648c2ecf20Sopenharmony_ci		proc_vmcore->size = vmcore_size;
15658c2ecf20Sopenharmony_ci	return 0;
15668c2ecf20Sopenharmony_ci}
15678c2ecf20Sopenharmony_cifs_initcall(vmcore_init);
15688c2ecf20Sopenharmony_ci
15698c2ecf20Sopenharmony_ci/* Cleanup function for vmcore module. */
15708c2ecf20Sopenharmony_civoid vmcore_cleanup(void)
15718c2ecf20Sopenharmony_ci{
15728c2ecf20Sopenharmony_ci	if (proc_vmcore) {
15738c2ecf20Sopenharmony_ci		proc_remove(proc_vmcore);
15748c2ecf20Sopenharmony_ci		proc_vmcore = NULL;
15758c2ecf20Sopenharmony_ci	}
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci	/* clear the vmcore list. */
15788c2ecf20Sopenharmony_ci	while (!list_empty(&vmcore_list)) {
15798c2ecf20Sopenharmony_ci		struct vmcore *m;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci		m = list_first_entry(&vmcore_list, struct vmcore, list);
15828c2ecf20Sopenharmony_ci		list_del(&m->list);
15838c2ecf20Sopenharmony_ci		kfree(m);
15848c2ecf20Sopenharmony_ci	}
15858c2ecf20Sopenharmony_ci	free_elfcorebuf();
15868c2ecf20Sopenharmony_ci
15878c2ecf20Sopenharmony_ci	/* clear vmcore device dump list */
15888c2ecf20Sopenharmony_ci	vmcore_free_device_dumps();
15898c2ecf20Sopenharmony_ci}
1590