162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *	fs/proc/vmcore.c Interface for accessing the crash
462306a36Sopenharmony_ci * 				 dump from the system's previous life.
562306a36Sopenharmony_ci * 	Heavily borrowed from fs/proc/kcore.c
662306a36Sopenharmony_ci *	Created by: Hariprasad Nellitheertha (hari@in.ibm.com)
762306a36Sopenharmony_ci *	Copyright (C) IBM Corporation, 2004. All rights reserved
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/mm.h>
1262306a36Sopenharmony_ci#include <linux/kcore.h>
1362306a36Sopenharmony_ci#include <linux/user.h>
1462306a36Sopenharmony_ci#include <linux/elf.h>
1562306a36Sopenharmony_ci#include <linux/elfcore.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci#include <linux/slab.h>
1862306a36Sopenharmony_ci#include <linux/highmem.h>
1962306a36Sopenharmony_ci#include <linux/printk.h>
2062306a36Sopenharmony_ci#include <linux/memblock.h>
2162306a36Sopenharmony_ci#include <linux/init.h>
2262306a36Sopenharmony_ci#include <linux/crash_dump.h>
2362306a36Sopenharmony_ci#include <linux/list.h>
2462306a36Sopenharmony_ci#include <linux/moduleparam.h>
2562306a36Sopenharmony_ci#include <linux/mutex.h>
2662306a36Sopenharmony_ci#include <linux/vmalloc.h>
2762306a36Sopenharmony_ci#include <linux/pagemap.h>
2862306a36Sopenharmony_ci#include <linux/uio.h>
2962306a36Sopenharmony_ci#include <linux/cc_platform.h>
3062306a36Sopenharmony_ci#include <asm/io.h>
3162306a36Sopenharmony_ci#include "internal.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci/* List representing chunks of contiguous memory areas and their offsets in
3462306a36Sopenharmony_ci * vmcore file.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_cistatic LIST_HEAD(vmcore_list);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* Stores the pointer to the buffer containing kernel elf core headers. */
3962306a36Sopenharmony_cistatic char *elfcorebuf;
4062306a36Sopenharmony_cistatic size_t elfcorebuf_sz;
4162306a36Sopenharmony_cistatic size_t elfcorebuf_sz_orig;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic char *elfnotes_buf;
4462306a36Sopenharmony_cistatic size_t elfnotes_sz;
4562306a36Sopenharmony_ci/* Size of all notes minus the device dump notes */
4662306a36Sopenharmony_cistatic size_t elfnotes_orig_sz;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* Total size of vmcore file. */
4962306a36Sopenharmony_cistatic u64 vmcore_size;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct proc_dir_entry *proc_vmcore;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
5462306a36Sopenharmony_ci/* Device Dump list and mutex to synchronize access to list */
5562306a36Sopenharmony_cistatic LIST_HEAD(vmcoredd_list);
5662306a36Sopenharmony_cistatic DEFINE_MUTEX(vmcoredd_mutex);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic bool vmcoredd_disabled;
5962306a36Sopenharmony_cicore_param(novmcoredd, vmcoredd_disabled, bool, 0);
6062306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Device Dump Size */
6362306a36Sopenharmony_cistatic size_t vmcoredd_orig_sz;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic DEFINE_SPINLOCK(vmcore_cb_lock);
6662306a36Sopenharmony_ciDEFINE_STATIC_SRCU(vmcore_cb_srcu);
6762306a36Sopenharmony_ci/* List of registered vmcore callbacks. */
6862306a36Sopenharmony_cistatic LIST_HEAD(vmcore_cb_list);
6962306a36Sopenharmony_ci/* Whether the vmcore has been opened once. */
7062306a36Sopenharmony_cistatic bool vmcore_opened;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid register_vmcore_cb(struct vmcore_cb *cb)
7362306a36Sopenharmony_ci{
7462306a36Sopenharmony_ci	INIT_LIST_HEAD(&cb->next);
7562306a36Sopenharmony_ci	spin_lock(&vmcore_cb_lock);
7662306a36Sopenharmony_ci	list_add_tail(&cb->next, &vmcore_cb_list);
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * Registering a vmcore callback after the vmcore was opened is
7962306a36Sopenharmony_ci	 * very unusual (e.g., manual driver loading).
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (vmcore_opened)
8262306a36Sopenharmony_ci		pr_warn_once("Unexpected vmcore callback registration\n");
8362306a36Sopenharmony_ci	spin_unlock(&vmcore_cb_lock);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(register_vmcore_cb);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_civoid unregister_vmcore_cb(struct vmcore_cb *cb)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	spin_lock(&vmcore_cb_lock);
9062306a36Sopenharmony_ci	list_del_rcu(&cb->next);
9162306a36Sopenharmony_ci	/*
9262306a36Sopenharmony_ci	 * Unregistering a vmcore callback after the vmcore was opened is
9362306a36Sopenharmony_ci	 * very unusual (e.g., forced driver removal), but we cannot stop
9462306a36Sopenharmony_ci	 * unregistering.
9562306a36Sopenharmony_ci	 */
9662306a36Sopenharmony_ci	if (vmcore_opened)
9762306a36Sopenharmony_ci		pr_warn_once("Unexpected vmcore callback unregistration\n");
9862306a36Sopenharmony_ci	spin_unlock(&vmcore_cb_lock);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	synchronize_srcu(&vmcore_cb_srcu);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(unregister_vmcore_cb);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic bool pfn_is_ram(unsigned long pfn)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	struct vmcore_cb *cb;
10762306a36Sopenharmony_ci	bool ret = true;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	list_for_each_entry_srcu(cb, &vmcore_cb_list, next,
11062306a36Sopenharmony_ci				 srcu_read_lock_held(&vmcore_cb_srcu)) {
11162306a36Sopenharmony_ci		if (unlikely(!cb->pfn_is_ram))
11262306a36Sopenharmony_ci			continue;
11362306a36Sopenharmony_ci		ret = cb->pfn_is_ram(cb, pfn);
11462306a36Sopenharmony_ci		if (!ret)
11562306a36Sopenharmony_ci			break;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	return ret;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic int open_vmcore(struct inode *inode, struct file *file)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	spin_lock(&vmcore_cb_lock);
12462306a36Sopenharmony_ci	vmcore_opened = true;
12562306a36Sopenharmony_ci	spin_unlock(&vmcore_cb_lock);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* Reads a page from the oldmem device from given offset. */
13162306a36Sopenharmony_cissize_t read_from_oldmem(struct iov_iter *iter, size_t count,
13262306a36Sopenharmony_ci			 u64 *ppos, bool encrypted)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	unsigned long pfn, offset;
13562306a36Sopenharmony_ci	ssize_t nr_bytes;
13662306a36Sopenharmony_ci	ssize_t read = 0, tmp;
13762306a36Sopenharmony_ci	int idx;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	if (!count)
14062306a36Sopenharmony_ci		return 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	offset = (unsigned long)(*ppos % PAGE_SIZE);
14362306a36Sopenharmony_ci	pfn = (unsigned long)(*ppos / PAGE_SIZE);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	idx = srcu_read_lock(&vmcore_cb_srcu);
14662306a36Sopenharmony_ci	do {
14762306a36Sopenharmony_ci		if (count > (PAGE_SIZE - offset))
14862306a36Sopenharmony_ci			nr_bytes = PAGE_SIZE - offset;
14962306a36Sopenharmony_ci		else
15062306a36Sopenharmony_ci			nr_bytes = count;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		/* If pfn is not ram, return zeros for sparse dump files */
15362306a36Sopenharmony_ci		if (!pfn_is_ram(pfn)) {
15462306a36Sopenharmony_ci			tmp = iov_iter_zero(nr_bytes, iter);
15562306a36Sopenharmony_ci		} else {
15662306a36Sopenharmony_ci			if (encrypted)
15762306a36Sopenharmony_ci				tmp = copy_oldmem_page_encrypted(iter, pfn,
15862306a36Sopenharmony_ci								 nr_bytes,
15962306a36Sopenharmony_ci								 offset);
16062306a36Sopenharmony_ci			else
16162306a36Sopenharmony_ci				tmp = copy_oldmem_page(iter, pfn, nr_bytes,
16262306a36Sopenharmony_ci						       offset);
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		if (tmp < nr_bytes) {
16562306a36Sopenharmony_ci			srcu_read_unlock(&vmcore_cb_srcu, idx);
16662306a36Sopenharmony_ci			return -EFAULT;
16762306a36Sopenharmony_ci		}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci		*ppos += nr_bytes;
17062306a36Sopenharmony_ci		count -= nr_bytes;
17162306a36Sopenharmony_ci		read += nr_bytes;
17262306a36Sopenharmony_ci		++pfn;
17362306a36Sopenharmony_ci		offset = 0;
17462306a36Sopenharmony_ci	} while (count);
17562306a36Sopenharmony_ci	srcu_read_unlock(&vmcore_cb_srcu, idx);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return read;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/*
18162306a36Sopenharmony_ci * Architectures may override this function to allocate ELF header in 2nd kernel
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ciint __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	return 0;
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Architectures may override this function to free header
19062306a36Sopenharmony_ci */
19162306a36Sopenharmony_civoid __weak elfcorehdr_free(unsigned long long addr)
19262306a36Sopenharmony_ci{}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/*
19562306a36Sopenharmony_ci * Architectures may override this function to read from ELF header
19662306a36Sopenharmony_ci */
19762306a36Sopenharmony_cissize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct kvec kvec = { .iov_base = buf, .iov_len = count };
20062306a36Sopenharmony_ci	struct iov_iter iter;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count);
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return read_from_oldmem(&iter, count, ppos, false);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/*
20862306a36Sopenharmony_ci * Architectures may override this function to read from notes sections
20962306a36Sopenharmony_ci */
21062306a36Sopenharmony_cissize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	struct kvec kvec = { .iov_base = buf, .iov_len = count };
21362306a36Sopenharmony_ci	struct iov_iter iter;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return read_from_oldmem(&iter, count, ppos,
21862306a36Sopenharmony_ci			cc_platform_has(CC_ATTR_MEM_ENCRYPT));
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci/*
22262306a36Sopenharmony_ci * Architectures may override this function to map oldmem
22362306a36Sopenharmony_ci */
22462306a36Sopenharmony_ciint __weak remap_oldmem_pfn_range(struct vm_area_struct *vma,
22562306a36Sopenharmony_ci				  unsigned long from, unsigned long pfn,
22662306a36Sopenharmony_ci				  unsigned long size, pgprot_t prot)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	prot = pgprot_encrypted(prot);
22962306a36Sopenharmony_ci	return remap_pfn_range(vma, from, pfn, size, prot);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/*
23362306a36Sopenharmony_ci * Architectures which support memory encryption override this.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cissize_t __weak copy_oldmem_page_encrypted(struct iov_iter *iter,
23662306a36Sopenharmony_ci		unsigned long pfn, size_t csize, unsigned long offset)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	return copy_oldmem_page(iter, pfn, csize, offset);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
24262306a36Sopenharmony_cistatic int vmcoredd_copy_dumps(struct iov_iter *iter, u64 start, size_t size)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct vmcoredd_node *dump;
24562306a36Sopenharmony_ci	u64 offset = 0;
24662306a36Sopenharmony_ci	int ret = 0;
24762306a36Sopenharmony_ci	size_t tsz;
24862306a36Sopenharmony_ci	char *buf;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
25162306a36Sopenharmony_ci	list_for_each_entry(dump, &vmcoredd_list, list) {
25262306a36Sopenharmony_ci		if (start < offset + dump->size) {
25362306a36Sopenharmony_ci			tsz = min(offset + (u64)dump->size - start, (u64)size);
25462306a36Sopenharmony_ci			buf = dump->buf + start - offset;
25562306a36Sopenharmony_ci			if (copy_to_iter(buf, tsz, iter) < tsz) {
25662306a36Sopenharmony_ci				ret = -EFAULT;
25762306a36Sopenharmony_ci				goto out_unlock;
25862306a36Sopenharmony_ci			}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci			size -= tsz;
26162306a36Sopenharmony_ci			start += tsz;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci			/* Leave now if buffer filled already */
26462306a36Sopenharmony_ci			if (!size)
26562306a36Sopenharmony_ci				goto out_unlock;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci		offset += dump->size;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ciout_unlock:
27162306a36Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
27262306a36Sopenharmony_ci	return ret;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci#ifdef CONFIG_MMU
27662306a36Sopenharmony_cistatic int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst,
27762306a36Sopenharmony_ci			       u64 start, size_t size)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	struct vmcoredd_node *dump;
28062306a36Sopenharmony_ci	u64 offset = 0;
28162306a36Sopenharmony_ci	int ret = 0;
28262306a36Sopenharmony_ci	size_t tsz;
28362306a36Sopenharmony_ci	char *buf;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
28662306a36Sopenharmony_ci	list_for_each_entry(dump, &vmcoredd_list, list) {
28762306a36Sopenharmony_ci		if (start < offset + dump->size) {
28862306a36Sopenharmony_ci			tsz = min(offset + (u64)dump->size - start, (u64)size);
28962306a36Sopenharmony_ci			buf = dump->buf + start - offset;
29062306a36Sopenharmony_ci			if (remap_vmalloc_range_partial(vma, dst, buf, 0,
29162306a36Sopenharmony_ci							tsz)) {
29262306a36Sopenharmony_ci				ret = -EFAULT;
29362306a36Sopenharmony_ci				goto out_unlock;
29462306a36Sopenharmony_ci			}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci			size -= tsz;
29762306a36Sopenharmony_ci			start += tsz;
29862306a36Sopenharmony_ci			dst += tsz;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci			/* Leave now if buffer filled already */
30162306a36Sopenharmony_ci			if (!size)
30262306a36Sopenharmony_ci				goto out_unlock;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci		offset += dump->size;
30562306a36Sopenharmony_ci	}
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ciout_unlock:
30862306a36Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
30962306a36Sopenharmony_ci	return ret;
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci#endif /* CONFIG_MMU */
31262306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci/* Read from the ELF header and then the crash dump. On error, negative value is
31562306a36Sopenharmony_ci * returned otherwise number of bytes read are returned.
31662306a36Sopenharmony_ci */
31762306a36Sopenharmony_cistatic ssize_t __read_vmcore(struct iov_iter *iter, loff_t *fpos)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	ssize_t acc = 0, tmp;
32062306a36Sopenharmony_ci	size_t tsz;
32162306a36Sopenharmony_ci	u64 start;
32262306a36Sopenharmony_ci	struct vmcore *m = NULL;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	if (!iov_iter_count(iter) || *fpos >= vmcore_size)
32562306a36Sopenharmony_ci		return 0;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	iov_iter_truncate(iter, vmcore_size - *fpos);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	/* Read ELF core header */
33062306a36Sopenharmony_ci	if (*fpos < elfcorebuf_sz) {
33162306a36Sopenharmony_ci		tsz = min(elfcorebuf_sz - (size_t)*fpos, iov_iter_count(iter));
33262306a36Sopenharmony_ci		if (copy_to_iter(elfcorebuf + *fpos, tsz, iter) < tsz)
33362306a36Sopenharmony_ci			return -EFAULT;
33462306a36Sopenharmony_ci		*fpos += tsz;
33562306a36Sopenharmony_ci		acc += tsz;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci		/* leave now if filled buffer already */
33862306a36Sopenharmony_ci		if (!iov_iter_count(iter))
33962306a36Sopenharmony_ci			return acc;
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/* Read ELF note segment */
34362306a36Sopenharmony_ci	if (*fpos < elfcorebuf_sz + elfnotes_sz) {
34462306a36Sopenharmony_ci		void *kaddr;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		/* We add device dumps before other elf notes because the
34762306a36Sopenharmony_ci		 * other elf notes may not fill the elf notes buffer
34862306a36Sopenharmony_ci		 * completely and we will end up with zero-filled data
34962306a36Sopenharmony_ci		 * between the elf notes and the device dumps. Tools will
35062306a36Sopenharmony_ci		 * then try to decode this zero-filled data as valid notes
35162306a36Sopenharmony_ci		 * and we don't want that. Hence, adding device dumps before
35262306a36Sopenharmony_ci		 * the other elf notes ensure that zero-filled data can be
35362306a36Sopenharmony_ci		 * avoided.
35462306a36Sopenharmony_ci		 */
35562306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
35662306a36Sopenharmony_ci		/* Read device dumps */
35762306a36Sopenharmony_ci		if (*fpos < elfcorebuf_sz + vmcoredd_orig_sz) {
35862306a36Sopenharmony_ci			tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
35962306a36Sopenharmony_ci				  (size_t)*fpos, iov_iter_count(iter));
36062306a36Sopenharmony_ci			start = *fpos - elfcorebuf_sz;
36162306a36Sopenharmony_ci			if (vmcoredd_copy_dumps(iter, start, tsz))
36262306a36Sopenharmony_ci				return -EFAULT;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci			*fpos += tsz;
36562306a36Sopenharmony_ci			acc += tsz;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci			/* leave now if filled buffer already */
36862306a36Sopenharmony_ci			if (!iov_iter_count(iter))
36962306a36Sopenharmony_ci				return acc;
37062306a36Sopenharmony_ci		}
37162306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		/* Read remaining elf notes */
37462306a36Sopenharmony_ci		tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos,
37562306a36Sopenharmony_ci			  iov_iter_count(iter));
37662306a36Sopenharmony_ci		kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz;
37762306a36Sopenharmony_ci		if (copy_to_iter(kaddr, tsz, iter) < tsz)
37862306a36Sopenharmony_ci			return -EFAULT;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		*fpos += tsz;
38162306a36Sopenharmony_ci		acc += tsz;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		/* leave now if filled buffer already */
38462306a36Sopenharmony_ci		if (!iov_iter_count(iter))
38562306a36Sopenharmony_ci			return acc;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	list_for_each_entry(m, &vmcore_list, list) {
38962306a36Sopenharmony_ci		if (*fpos < m->offset + m->size) {
39062306a36Sopenharmony_ci			tsz = (size_t)min_t(unsigned long long,
39162306a36Sopenharmony_ci					    m->offset + m->size - *fpos,
39262306a36Sopenharmony_ci					    iov_iter_count(iter));
39362306a36Sopenharmony_ci			start = m->paddr + *fpos - m->offset;
39462306a36Sopenharmony_ci			tmp = read_from_oldmem(iter, tsz, &start,
39562306a36Sopenharmony_ci					cc_platform_has(CC_ATTR_MEM_ENCRYPT));
39662306a36Sopenharmony_ci			if (tmp < 0)
39762306a36Sopenharmony_ci				return tmp;
39862306a36Sopenharmony_ci			*fpos += tsz;
39962306a36Sopenharmony_ci			acc += tsz;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci			/* leave now if filled buffer already */
40262306a36Sopenharmony_ci			if (!iov_iter_count(iter))
40362306a36Sopenharmony_ci				return acc;
40462306a36Sopenharmony_ci		}
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	return acc;
40862306a36Sopenharmony_ci}
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_cistatic ssize_t read_vmcore(struct kiocb *iocb, struct iov_iter *iter)
41162306a36Sopenharmony_ci{
41262306a36Sopenharmony_ci	return __read_vmcore(iter, &iocb->ki_pos);
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci/*
41662306a36Sopenharmony_ci * The vmcore fault handler uses the page cache and fills data using the
41762306a36Sopenharmony_ci * standard __read_vmcore() function.
41862306a36Sopenharmony_ci *
41962306a36Sopenharmony_ci * On s390 the fault handler is used for memory regions that can't be mapped
42062306a36Sopenharmony_ci * directly with remap_pfn_range().
42162306a36Sopenharmony_ci */
42262306a36Sopenharmony_cistatic vm_fault_t mmap_vmcore_fault(struct vm_fault *vmf)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci#ifdef CONFIG_S390
42562306a36Sopenharmony_ci	struct address_space *mapping = vmf->vma->vm_file->f_mapping;
42662306a36Sopenharmony_ci	pgoff_t index = vmf->pgoff;
42762306a36Sopenharmony_ci	struct iov_iter iter;
42862306a36Sopenharmony_ci	struct kvec kvec;
42962306a36Sopenharmony_ci	struct page *page;
43062306a36Sopenharmony_ci	loff_t offset;
43162306a36Sopenharmony_ci	int rc;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	page = find_or_create_page(mapping, index, GFP_KERNEL);
43462306a36Sopenharmony_ci	if (!page)
43562306a36Sopenharmony_ci		return VM_FAULT_OOM;
43662306a36Sopenharmony_ci	if (!PageUptodate(page)) {
43762306a36Sopenharmony_ci		offset = (loff_t) index << PAGE_SHIFT;
43862306a36Sopenharmony_ci		kvec.iov_base = page_address(page);
43962306a36Sopenharmony_ci		kvec.iov_len = PAGE_SIZE;
44062306a36Sopenharmony_ci		iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, PAGE_SIZE);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		rc = __read_vmcore(&iter, &offset);
44362306a36Sopenharmony_ci		if (rc < 0) {
44462306a36Sopenharmony_ci			unlock_page(page);
44562306a36Sopenharmony_ci			put_page(page);
44662306a36Sopenharmony_ci			return vmf_error(rc);
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci		SetPageUptodate(page);
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	unlock_page(page);
45162306a36Sopenharmony_ci	vmf->page = page;
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci#else
45462306a36Sopenharmony_ci	return VM_FAULT_SIGBUS;
45562306a36Sopenharmony_ci#endif
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic const struct vm_operations_struct vmcore_mmap_ops = {
45962306a36Sopenharmony_ci	.fault = mmap_vmcore_fault,
46062306a36Sopenharmony_ci};
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci/**
46362306a36Sopenharmony_ci * vmcore_alloc_buf - allocate buffer in vmalloc memory
46462306a36Sopenharmony_ci * @size: size of buffer
46562306a36Sopenharmony_ci *
46662306a36Sopenharmony_ci * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap
46762306a36Sopenharmony_ci * the buffer to user-space by means of remap_vmalloc_range().
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is
47062306a36Sopenharmony_ci * disabled and there's no need to allow users to mmap the buffer.
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic inline char *vmcore_alloc_buf(size_t size)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci#ifdef CONFIG_MMU
47562306a36Sopenharmony_ci	return vmalloc_user(size);
47662306a36Sopenharmony_ci#else
47762306a36Sopenharmony_ci	return vzalloc(size);
47862306a36Sopenharmony_ci#endif
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci/*
48262306a36Sopenharmony_ci * Disable mmap_vmcore() if CONFIG_MMU is not defined. MMU is
48362306a36Sopenharmony_ci * essential for mmap_vmcore() in order to map physically
48462306a36Sopenharmony_ci * non-contiguous objects (ELF header, ELF note segment and memory
48562306a36Sopenharmony_ci * regions in the 1st kernel pointed to by PT_LOAD entries) into
48662306a36Sopenharmony_ci * virtually contiguous user-space in ELF layout.
48762306a36Sopenharmony_ci */
48862306a36Sopenharmony_ci#ifdef CONFIG_MMU
48962306a36Sopenharmony_ci/*
49062306a36Sopenharmony_ci * remap_oldmem_pfn_checked - do remap_oldmem_pfn_range replacing all pages
49162306a36Sopenharmony_ci * reported as not being ram with the zero page.
49262306a36Sopenharmony_ci *
49362306a36Sopenharmony_ci * @vma: vm_area_struct describing requested mapping
49462306a36Sopenharmony_ci * @from: start remapping from
49562306a36Sopenharmony_ci * @pfn: page frame number to start remapping to
49662306a36Sopenharmony_ci * @size: remapping size
49762306a36Sopenharmony_ci * @prot: protection bits
49862306a36Sopenharmony_ci *
49962306a36Sopenharmony_ci * Returns zero on success, -EAGAIN on failure.
50062306a36Sopenharmony_ci */
50162306a36Sopenharmony_cistatic int remap_oldmem_pfn_checked(struct vm_area_struct *vma,
50262306a36Sopenharmony_ci				    unsigned long from, unsigned long pfn,
50362306a36Sopenharmony_ci				    unsigned long size, pgprot_t prot)
50462306a36Sopenharmony_ci{
50562306a36Sopenharmony_ci	unsigned long map_size;
50662306a36Sopenharmony_ci	unsigned long pos_start, pos_end, pos;
50762306a36Sopenharmony_ci	unsigned long zeropage_pfn = my_zero_pfn(0);
50862306a36Sopenharmony_ci	size_t len = 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	pos_start = pfn;
51162306a36Sopenharmony_ci	pos_end = pfn + (size >> PAGE_SHIFT);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	for (pos = pos_start; pos < pos_end; ++pos) {
51462306a36Sopenharmony_ci		if (!pfn_is_ram(pos)) {
51562306a36Sopenharmony_ci			/*
51662306a36Sopenharmony_ci			 * We hit a page which is not ram. Remap the continuous
51762306a36Sopenharmony_ci			 * region between pos_start and pos-1 and replace
51862306a36Sopenharmony_ci			 * the non-ram page at pos with the zero page.
51962306a36Sopenharmony_ci			 */
52062306a36Sopenharmony_ci			if (pos > pos_start) {
52162306a36Sopenharmony_ci				/* Remap continuous region */
52262306a36Sopenharmony_ci				map_size = (pos - pos_start) << PAGE_SHIFT;
52362306a36Sopenharmony_ci				if (remap_oldmem_pfn_range(vma, from + len,
52462306a36Sopenharmony_ci							   pos_start, map_size,
52562306a36Sopenharmony_ci							   prot))
52662306a36Sopenharmony_ci					goto fail;
52762306a36Sopenharmony_ci				len += map_size;
52862306a36Sopenharmony_ci			}
52962306a36Sopenharmony_ci			/* Remap the zero page */
53062306a36Sopenharmony_ci			if (remap_oldmem_pfn_range(vma, from + len,
53162306a36Sopenharmony_ci						   zeropage_pfn,
53262306a36Sopenharmony_ci						   PAGE_SIZE, prot))
53362306a36Sopenharmony_ci				goto fail;
53462306a36Sopenharmony_ci			len += PAGE_SIZE;
53562306a36Sopenharmony_ci			pos_start = pos + 1;
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci	if (pos > pos_start) {
53962306a36Sopenharmony_ci		/* Remap the rest */
54062306a36Sopenharmony_ci		map_size = (pos - pos_start) << PAGE_SHIFT;
54162306a36Sopenharmony_ci		if (remap_oldmem_pfn_range(vma, from + len, pos_start,
54262306a36Sopenharmony_ci					   map_size, prot))
54362306a36Sopenharmony_ci			goto fail;
54462306a36Sopenharmony_ci	}
54562306a36Sopenharmony_ci	return 0;
54662306a36Sopenharmony_cifail:
54762306a36Sopenharmony_ci	do_munmap(vma->vm_mm, from, len, NULL);
54862306a36Sopenharmony_ci	return -EAGAIN;
54962306a36Sopenharmony_ci}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_cistatic int vmcore_remap_oldmem_pfn(struct vm_area_struct *vma,
55262306a36Sopenharmony_ci			    unsigned long from, unsigned long pfn,
55362306a36Sopenharmony_ci			    unsigned long size, pgprot_t prot)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	int ret, idx;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/*
55862306a36Sopenharmony_ci	 * Check if a callback was registered to avoid looping over all
55962306a36Sopenharmony_ci	 * pages without a reason.
56062306a36Sopenharmony_ci	 */
56162306a36Sopenharmony_ci	idx = srcu_read_lock(&vmcore_cb_srcu);
56262306a36Sopenharmony_ci	if (!list_empty(&vmcore_cb_list))
56362306a36Sopenharmony_ci		ret = remap_oldmem_pfn_checked(vma, from, pfn, size, prot);
56462306a36Sopenharmony_ci	else
56562306a36Sopenharmony_ci		ret = remap_oldmem_pfn_range(vma, from, pfn, size, prot);
56662306a36Sopenharmony_ci	srcu_read_unlock(&vmcore_cb_srcu, idx);
56762306a36Sopenharmony_ci	return ret;
56862306a36Sopenharmony_ci}
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_cistatic int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
57162306a36Sopenharmony_ci{
57262306a36Sopenharmony_ci	size_t size = vma->vm_end - vma->vm_start;
57362306a36Sopenharmony_ci	u64 start, end, len, tsz;
57462306a36Sopenharmony_ci	struct vmcore *m;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	start = (u64)vma->vm_pgoff << PAGE_SHIFT;
57762306a36Sopenharmony_ci	end = start + size;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	if (size > vmcore_size || end > vmcore_size)
58062306a36Sopenharmony_ci		return -EINVAL;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	if (vma->vm_flags & (VM_WRITE | VM_EXEC))
58362306a36Sopenharmony_ci		return -EPERM;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	vm_flags_mod(vma, VM_MIXEDMAP, VM_MAYWRITE | VM_MAYEXEC);
58662306a36Sopenharmony_ci	vma->vm_ops = &vmcore_mmap_ops;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	len = 0;
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_ci	if (start < elfcorebuf_sz) {
59162306a36Sopenharmony_ci		u64 pfn;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		tsz = min(elfcorebuf_sz - (size_t)start, size);
59462306a36Sopenharmony_ci		pfn = __pa(elfcorebuf + start) >> PAGE_SHIFT;
59562306a36Sopenharmony_ci		if (remap_pfn_range(vma, vma->vm_start, pfn, tsz,
59662306a36Sopenharmony_ci				    vma->vm_page_prot))
59762306a36Sopenharmony_ci			return -EAGAIN;
59862306a36Sopenharmony_ci		size -= tsz;
59962306a36Sopenharmony_ci		start += tsz;
60062306a36Sopenharmony_ci		len += tsz;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		if (size == 0)
60362306a36Sopenharmony_ci			return 0;
60462306a36Sopenharmony_ci	}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (start < elfcorebuf_sz + elfnotes_sz) {
60762306a36Sopenharmony_ci		void *kaddr;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		/* We add device dumps before other elf notes because the
61062306a36Sopenharmony_ci		 * other elf notes may not fill the elf notes buffer
61162306a36Sopenharmony_ci		 * completely and we will end up with zero-filled data
61262306a36Sopenharmony_ci		 * between the elf notes and the device dumps. Tools will
61362306a36Sopenharmony_ci		 * then try to decode this zero-filled data as valid notes
61462306a36Sopenharmony_ci		 * and we don't want that. Hence, adding device dumps before
61562306a36Sopenharmony_ci		 * the other elf notes ensure that zero-filled data can be
61662306a36Sopenharmony_ci		 * avoided. This also ensures that the device dumps and
61762306a36Sopenharmony_ci		 * other elf notes can be properly mmaped at page aligned
61862306a36Sopenharmony_ci		 * address.
61962306a36Sopenharmony_ci		 */
62062306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
62162306a36Sopenharmony_ci		/* Read device dumps */
62262306a36Sopenharmony_ci		if (start < elfcorebuf_sz + vmcoredd_orig_sz) {
62362306a36Sopenharmony_ci			u64 start_off;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci			tsz = min(elfcorebuf_sz + vmcoredd_orig_sz -
62662306a36Sopenharmony_ci				  (size_t)start, size);
62762306a36Sopenharmony_ci			start_off = start - elfcorebuf_sz;
62862306a36Sopenharmony_ci			if (vmcoredd_mmap_dumps(vma, vma->vm_start + len,
62962306a36Sopenharmony_ci						start_off, tsz))
63062306a36Sopenharmony_ci				goto fail;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci			size -= tsz;
63362306a36Sopenharmony_ci			start += tsz;
63462306a36Sopenharmony_ci			len += tsz;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci			/* leave now if filled buffer already */
63762306a36Sopenharmony_ci			if (!size)
63862306a36Sopenharmony_ci				return 0;
63962306a36Sopenharmony_ci		}
64062306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci		/* Read remaining elf notes */
64362306a36Sopenharmony_ci		tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)start, size);
64462306a36Sopenharmony_ci		kaddr = elfnotes_buf + start - elfcorebuf_sz - vmcoredd_orig_sz;
64562306a36Sopenharmony_ci		if (remap_vmalloc_range_partial(vma, vma->vm_start + len,
64662306a36Sopenharmony_ci						kaddr, 0, tsz))
64762306a36Sopenharmony_ci			goto fail;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		size -= tsz;
65062306a36Sopenharmony_ci		start += tsz;
65162306a36Sopenharmony_ci		len += tsz;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci		if (size == 0)
65462306a36Sopenharmony_ci			return 0;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	list_for_each_entry(m, &vmcore_list, list) {
65862306a36Sopenharmony_ci		if (start < m->offset + m->size) {
65962306a36Sopenharmony_ci			u64 paddr = 0;
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci			tsz = (size_t)min_t(unsigned long long,
66262306a36Sopenharmony_ci					    m->offset + m->size - start, size);
66362306a36Sopenharmony_ci			paddr = m->paddr + start - m->offset;
66462306a36Sopenharmony_ci			if (vmcore_remap_oldmem_pfn(vma, vma->vm_start + len,
66562306a36Sopenharmony_ci						    paddr >> PAGE_SHIFT, tsz,
66662306a36Sopenharmony_ci						    vma->vm_page_prot))
66762306a36Sopenharmony_ci				goto fail;
66862306a36Sopenharmony_ci			size -= tsz;
66962306a36Sopenharmony_ci			start += tsz;
67062306a36Sopenharmony_ci			len += tsz;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci			if (size == 0)
67362306a36Sopenharmony_ci				return 0;
67462306a36Sopenharmony_ci		}
67562306a36Sopenharmony_ci	}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	return 0;
67862306a36Sopenharmony_cifail:
67962306a36Sopenharmony_ci	do_munmap(vma->vm_mm, vma->vm_start, len, NULL);
68062306a36Sopenharmony_ci	return -EAGAIN;
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci#else
68362306a36Sopenharmony_cistatic int mmap_vmcore(struct file *file, struct vm_area_struct *vma)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	return -ENOSYS;
68662306a36Sopenharmony_ci}
68762306a36Sopenharmony_ci#endif
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_cistatic const struct proc_ops vmcore_proc_ops = {
69062306a36Sopenharmony_ci	.proc_open	= open_vmcore,
69162306a36Sopenharmony_ci	.proc_read_iter	= read_vmcore,
69262306a36Sopenharmony_ci	.proc_lseek	= default_llseek,
69362306a36Sopenharmony_ci	.proc_mmap	= mmap_vmcore,
69462306a36Sopenharmony_ci};
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_cistatic struct vmcore* __init get_new_element(void)
69762306a36Sopenharmony_ci{
69862306a36Sopenharmony_ci	return kzalloc(sizeof(struct vmcore), GFP_KERNEL);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic u64 get_vmcore_size(size_t elfsz, size_t elfnotesegsz,
70262306a36Sopenharmony_ci			   struct list_head *vc_list)
70362306a36Sopenharmony_ci{
70462306a36Sopenharmony_ci	u64 size;
70562306a36Sopenharmony_ci	struct vmcore *m;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	size = elfsz + elfnotesegsz;
70862306a36Sopenharmony_ci	list_for_each_entry(m, vc_list, list) {
70962306a36Sopenharmony_ci		size += m->size;
71062306a36Sopenharmony_ci	}
71162306a36Sopenharmony_ci	return size;
71262306a36Sopenharmony_ci}
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci/**
71562306a36Sopenharmony_ci * update_note_header_size_elf64 - update p_memsz member of each PT_NOTE entry
71662306a36Sopenharmony_ci *
71762306a36Sopenharmony_ci * @ehdr_ptr: ELF header
71862306a36Sopenharmony_ci *
71962306a36Sopenharmony_ci * This function updates p_memsz member of each PT_NOTE entry in the
72062306a36Sopenharmony_ci * program header table pointed to by @ehdr_ptr to real size of ELF
72162306a36Sopenharmony_ci * note segment.
72262306a36Sopenharmony_ci */
72362306a36Sopenharmony_cistatic int __init update_note_header_size_elf64(const Elf64_Ehdr *ehdr_ptr)
72462306a36Sopenharmony_ci{
72562306a36Sopenharmony_ci	int i, rc=0;
72662306a36Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
72762306a36Sopenharmony_ci	Elf64_Nhdr *nhdr_ptr;
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1);
73062306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
73162306a36Sopenharmony_ci		void *notes_section;
73262306a36Sopenharmony_ci		u64 offset, max_sz, sz, real_sz = 0;
73362306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
73462306a36Sopenharmony_ci			continue;
73562306a36Sopenharmony_ci		max_sz = phdr_ptr->p_memsz;
73662306a36Sopenharmony_ci		offset = phdr_ptr->p_offset;
73762306a36Sopenharmony_ci		notes_section = kmalloc(max_sz, GFP_KERNEL);
73862306a36Sopenharmony_ci		if (!notes_section)
73962306a36Sopenharmony_ci			return -ENOMEM;
74062306a36Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
74162306a36Sopenharmony_ci		if (rc < 0) {
74262306a36Sopenharmony_ci			kfree(notes_section);
74362306a36Sopenharmony_ci			return rc;
74462306a36Sopenharmony_ci		}
74562306a36Sopenharmony_ci		nhdr_ptr = notes_section;
74662306a36Sopenharmony_ci		while (nhdr_ptr->n_namesz != 0) {
74762306a36Sopenharmony_ci			sz = sizeof(Elf64_Nhdr) +
74862306a36Sopenharmony_ci				(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
74962306a36Sopenharmony_ci				(((u64)nhdr_ptr->n_descsz + 3) & ~3);
75062306a36Sopenharmony_ci			if ((real_sz + sz) > max_sz) {
75162306a36Sopenharmony_ci				pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
75262306a36Sopenharmony_ci					nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
75362306a36Sopenharmony_ci				break;
75462306a36Sopenharmony_ci			}
75562306a36Sopenharmony_ci			real_sz += sz;
75662306a36Sopenharmony_ci			nhdr_ptr = (Elf64_Nhdr*)((char*)nhdr_ptr + sz);
75762306a36Sopenharmony_ci		}
75862306a36Sopenharmony_ci		kfree(notes_section);
75962306a36Sopenharmony_ci		phdr_ptr->p_memsz = real_sz;
76062306a36Sopenharmony_ci		if (real_sz == 0) {
76162306a36Sopenharmony_ci			pr_warn("Warning: Zero PT_NOTE entries found\n");
76262306a36Sopenharmony_ci		}
76362306a36Sopenharmony_ci	}
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	return 0;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci/**
76962306a36Sopenharmony_ci * get_note_number_and_size_elf64 - get the number of PT_NOTE program
77062306a36Sopenharmony_ci * headers and sum of real size of their ELF note segment headers and
77162306a36Sopenharmony_ci * data.
77262306a36Sopenharmony_ci *
77362306a36Sopenharmony_ci * @ehdr_ptr: ELF header
77462306a36Sopenharmony_ci * @nr_ptnote: buffer for the number of PT_NOTE program headers
77562306a36Sopenharmony_ci * @sz_ptnote: buffer for size of unique PT_NOTE program header
77662306a36Sopenharmony_ci *
77762306a36Sopenharmony_ci * This function is used to merge multiple PT_NOTE program headers
77862306a36Sopenharmony_ci * into a unique single one. The resulting unique entry will have
77962306a36Sopenharmony_ci * @sz_ptnote in its phdr->p_mem.
78062306a36Sopenharmony_ci *
78162306a36Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
78262306a36Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf64
78362306a36Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
78462306a36Sopenharmony_ci * size in its p_memsz member.
78562306a36Sopenharmony_ci */
78662306a36Sopenharmony_cistatic int __init get_note_number_and_size_elf64(const Elf64_Ehdr *ehdr_ptr,
78762306a36Sopenharmony_ci						 int *nr_ptnote, u64 *sz_ptnote)
78862306a36Sopenharmony_ci{
78962306a36Sopenharmony_ci	int i;
79062306a36Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	*nr_ptnote = *sz_ptnote = 0;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	phdr_ptr = (Elf64_Phdr *)(ehdr_ptr + 1);
79562306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
79662306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
79762306a36Sopenharmony_ci			continue;
79862306a36Sopenharmony_ci		*nr_ptnote += 1;
79962306a36Sopenharmony_ci		*sz_ptnote += phdr_ptr->p_memsz;
80062306a36Sopenharmony_ci	}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	return 0;
80362306a36Sopenharmony_ci}
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci/**
80662306a36Sopenharmony_ci * copy_notes_elf64 - copy ELF note segments in a given buffer
80762306a36Sopenharmony_ci *
80862306a36Sopenharmony_ci * @ehdr_ptr: ELF header
80962306a36Sopenharmony_ci * @notes_buf: buffer into which ELF note segments are copied
81062306a36Sopenharmony_ci *
81162306a36Sopenharmony_ci * This function is used to copy ELF note segment in the 1st kernel
81262306a36Sopenharmony_ci * into the buffer @notes_buf in the 2nd kernel. It is assumed that
81362306a36Sopenharmony_ci * size of the buffer @notes_buf is equal to or larger than sum of the
81462306a36Sopenharmony_ci * real ELF note segment headers and data.
81562306a36Sopenharmony_ci *
81662306a36Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
81762306a36Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf64
81862306a36Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
81962306a36Sopenharmony_ci * size in its p_memsz member.
82062306a36Sopenharmony_ci */
82162306a36Sopenharmony_cistatic int __init copy_notes_elf64(const Elf64_Ehdr *ehdr_ptr, char *notes_buf)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int i, rc=0;
82462306a36Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	phdr_ptr = (Elf64_Phdr*)(ehdr_ptr + 1);
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
82962306a36Sopenharmony_ci		u64 offset;
83062306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
83162306a36Sopenharmony_ci			continue;
83262306a36Sopenharmony_ci		offset = phdr_ptr->p_offset;
83362306a36Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
83462306a36Sopenharmony_ci					   &offset);
83562306a36Sopenharmony_ci		if (rc < 0)
83662306a36Sopenharmony_ci			return rc;
83762306a36Sopenharmony_ci		notes_buf += phdr_ptr->p_memsz;
83862306a36Sopenharmony_ci	}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_ci	return 0;
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/* Merges all the PT_NOTE headers into one. */
84462306a36Sopenharmony_cistatic int __init merge_note_headers_elf64(char *elfptr, size_t *elfsz,
84562306a36Sopenharmony_ci					   char **notes_buf, size_t *notes_sz)
84662306a36Sopenharmony_ci{
84762306a36Sopenharmony_ci	int i, nr_ptnote=0, rc=0;
84862306a36Sopenharmony_ci	char *tmp;
84962306a36Sopenharmony_ci	Elf64_Ehdr *ehdr_ptr;
85062306a36Sopenharmony_ci	Elf64_Phdr phdr;
85162306a36Sopenharmony_ci	u64 phdr_sz = 0, note_off;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	ehdr_ptr = (Elf64_Ehdr *)elfptr;
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	rc = update_note_header_size_elf64(ehdr_ptr);
85662306a36Sopenharmony_ci	if (rc < 0)
85762306a36Sopenharmony_ci		return rc;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	rc = get_note_number_and_size_elf64(ehdr_ptr, &nr_ptnote, &phdr_sz);
86062306a36Sopenharmony_ci	if (rc < 0)
86162306a36Sopenharmony_ci		return rc;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	*notes_sz = roundup(phdr_sz, PAGE_SIZE);
86462306a36Sopenharmony_ci	*notes_buf = vmcore_alloc_buf(*notes_sz);
86562306a36Sopenharmony_ci	if (!*notes_buf)
86662306a36Sopenharmony_ci		return -ENOMEM;
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci	rc = copy_notes_elf64(ehdr_ptr, *notes_buf);
86962306a36Sopenharmony_ci	if (rc < 0)
87062306a36Sopenharmony_ci		return rc;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	/* Prepare merged PT_NOTE program header. */
87362306a36Sopenharmony_ci	phdr.p_type    = PT_NOTE;
87462306a36Sopenharmony_ci	phdr.p_flags   = 0;
87562306a36Sopenharmony_ci	note_off = sizeof(Elf64_Ehdr) +
87662306a36Sopenharmony_ci			(ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf64_Phdr);
87762306a36Sopenharmony_ci	phdr.p_offset  = roundup(note_off, PAGE_SIZE);
87862306a36Sopenharmony_ci	phdr.p_vaddr   = phdr.p_paddr = 0;
87962306a36Sopenharmony_ci	phdr.p_filesz  = phdr.p_memsz = phdr_sz;
88062306a36Sopenharmony_ci	phdr.p_align   = 4;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	/* Add merged PT_NOTE program header*/
88362306a36Sopenharmony_ci	tmp = elfptr + sizeof(Elf64_Ehdr);
88462306a36Sopenharmony_ci	memcpy(tmp, &phdr, sizeof(phdr));
88562306a36Sopenharmony_ci	tmp += sizeof(phdr);
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	/* Remove unwanted PT_NOTE program headers. */
88862306a36Sopenharmony_ci	i = (nr_ptnote - 1) * sizeof(Elf64_Phdr);
88962306a36Sopenharmony_ci	*elfsz = *elfsz - i;
89062306a36Sopenharmony_ci	memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf64_Ehdr)-sizeof(Elf64_Phdr)));
89162306a36Sopenharmony_ci	memset(elfptr + *elfsz, 0, i);
89262306a36Sopenharmony_ci	*elfsz = roundup(*elfsz, PAGE_SIZE);
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	/* Modify e_phnum to reflect merged headers. */
89562306a36Sopenharmony_ci	ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/* Store the size of all notes.  We need this to update the note
89862306a36Sopenharmony_ci	 * header when the device dumps will be added.
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	elfnotes_orig_sz = phdr.p_memsz;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci/**
90662306a36Sopenharmony_ci * update_note_header_size_elf32 - update p_memsz member of each PT_NOTE entry
90762306a36Sopenharmony_ci *
90862306a36Sopenharmony_ci * @ehdr_ptr: ELF header
90962306a36Sopenharmony_ci *
91062306a36Sopenharmony_ci * This function updates p_memsz member of each PT_NOTE entry in the
91162306a36Sopenharmony_ci * program header table pointed to by @ehdr_ptr to real size of ELF
91262306a36Sopenharmony_ci * note segment.
91362306a36Sopenharmony_ci */
91462306a36Sopenharmony_cistatic int __init update_note_header_size_elf32(const Elf32_Ehdr *ehdr_ptr)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	int i, rc=0;
91762306a36Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
91862306a36Sopenharmony_ci	Elf32_Nhdr *nhdr_ptr;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1);
92162306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
92262306a36Sopenharmony_ci		void *notes_section;
92362306a36Sopenharmony_ci		u64 offset, max_sz, sz, real_sz = 0;
92462306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
92562306a36Sopenharmony_ci			continue;
92662306a36Sopenharmony_ci		max_sz = phdr_ptr->p_memsz;
92762306a36Sopenharmony_ci		offset = phdr_ptr->p_offset;
92862306a36Sopenharmony_ci		notes_section = kmalloc(max_sz, GFP_KERNEL);
92962306a36Sopenharmony_ci		if (!notes_section)
93062306a36Sopenharmony_ci			return -ENOMEM;
93162306a36Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_section, max_sz, &offset);
93262306a36Sopenharmony_ci		if (rc < 0) {
93362306a36Sopenharmony_ci			kfree(notes_section);
93462306a36Sopenharmony_ci			return rc;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci		nhdr_ptr = notes_section;
93762306a36Sopenharmony_ci		while (nhdr_ptr->n_namesz != 0) {
93862306a36Sopenharmony_ci			sz = sizeof(Elf32_Nhdr) +
93962306a36Sopenharmony_ci				(((u64)nhdr_ptr->n_namesz + 3) & ~3) +
94062306a36Sopenharmony_ci				(((u64)nhdr_ptr->n_descsz + 3) & ~3);
94162306a36Sopenharmony_ci			if ((real_sz + sz) > max_sz) {
94262306a36Sopenharmony_ci				pr_warn("Warning: Exceeded p_memsz, dropping PT_NOTE entry n_namesz=0x%x, n_descsz=0x%x\n",
94362306a36Sopenharmony_ci					nhdr_ptr->n_namesz, nhdr_ptr->n_descsz);
94462306a36Sopenharmony_ci				break;
94562306a36Sopenharmony_ci			}
94662306a36Sopenharmony_ci			real_sz += sz;
94762306a36Sopenharmony_ci			nhdr_ptr = (Elf32_Nhdr*)((char*)nhdr_ptr + sz);
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci		kfree(notes_section);
95062306a36Sopenharmony_ci		phdr_ptr->p_memsz = real_sz;
95162306a36Sopenharmony_ci		if (real_sz == 0) {
95262306a36Sopenharmony_ci			pr_warn("Warning: Zero PT_NOTE entries found\n");
95362306a36Sopenharmony_ci		}
95462306a36Sopenharmony_ci	}
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	return 0;
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_ci/**
96062306a36Sopenharmony_ci * get_note_number_and_size_elf32 - get the number of PT_NOTE program
96162306a36Sopenharmony_ci * headers and sum of real size of their ELF note segment headers and
96262306a36Sopenharmony_ci * data.
96362306a36Sopenharmony_ci *
96462306a36Sopenharmony_ci * @ehdr_ptr: ELF header
96562306a36Sopenharmony_ci * @nr_ptnote: buffer for the number of PT_NOTE program headers
96662306a36Sopenharmony_ci * @sz_ptnote: buffer for size of unique PT_NOTE program header
96762306a36Sopenharmony_ci *
96862306a36Sopenharmony_ci * This function is used to merge multiple PT_NOTE program headers
96962306a36Sopenharmony_ci * into a unique single one. The resulting unique entry will have
97062306a36Sopenharmony_ci * @sz_ptnote in its phdr->p_mem.
97162306a36Sopenharmony_ci *
97262306a36Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
97362306a36Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf32
97462306a36Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
97562306a36Sopenharmony_ci * size in its p_memsz member.
97662306a36Sopenharmony_ci */
97762306a36Sopenharmony_cistatic int __init get_note_number_and_size_elf32(const Elf32_Ehdr *ehdr_ptr,
97862306a36Sopenharmony_ci						 int *nr_ptnote, u64 *sz_ptnote)
97962306a36Sopenharmony_ci{
98062306a36Sopenharmony_ci	int i;
98162306a36Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	*nr_ptnote = *sz_ptnote = 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	phdr_ptr = (Elf32_Phdr *)(ehdr_ptr + 1);
98662306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
98762306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
98862306a36Sopenharmony_ci			continue;
98962306a36Sopenharmony_ci		*nr_ptnote += 1;
99062306a36Sopenharmony_ci		*sz_ptnote += phdr_ptr->p_memsz;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return 0;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci/**
99762306a36Sopenharmony_ci * copy_notes_elf32 - copy ELF note segments in a given buffer
99862306a36Sopenharmony_ci *
99962306a36Sopenharmony_ci * @ehdr_ptr: ELF header
100062306a36Sopenharmony_ci * @notes_buf: buffer into which ELF note segments are copied
100162306a36Sopenharmony_ci *
100262306a36Sopenharmony_ci * This function is used to copy ELF note segment in the 1st kernel
100362306a36Sopenharmony_ci * into the buffer @notes_buf in the 2nd kernel. It is assumed that
100462306a36Sopenharmony_ci * size of the buffer @notes_buf is equal to or larger than sum of the
100562306a36Sopenharmony_ci * real ELF note segment headers and data.
100662306a36Sopenharmony_ci *
100762306a36Sopenharmony_ci * It is assumed that program headers with PT_NOTE type pointed to by
100862306a36Sopenharmony_ci * @ehdr_ptr has already been updated by update_note_header_size_elf32
100962306a36Sopenharmony_ci * and each of PT_NOTE program headers has actual ELF note segment
101062306a36Sopenharmony_ci * size in its p_memsz member.
101162306a36Sopenharmony_ci */
101262306a36Sopenharmony_cistatic int __init copy_notes_elf32(const Elf32_Ehdr *ehdr_ptr, char *notes_buf)
101362306a36Sopenharmony_ci{
101462306a36Sopenharmony_ci	int i, rc=0;
101562306a36Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	phdr_ptr = (Elf32_Phdr*)(ehdr_ptr + 1);
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
102062306a36Sopenharmony_ci		u64 offset;
102162306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_NOTE)
102262306a36Sopenharmony_ci			continue;
102362306a36Sopenharmony_ci		offset = phdr_ptr->p_offset;
102462306a36Sopenharmony_ci		rc = elfcorehdr_read_notes(notes_buf, phdr_ptr->p_memsz,
102562306a36Sopenharmony_ci					   &offset);
102662306a36Sopenharmony_ci		if (rc < 0)
102762306a36Sopenharmony_ci			return rc;
102862306a36Sopenharmony_ci		notes_buf += phdr_ptr->p_memsz;
102962306a36Sopenharmony_ci	}
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci	return 0;
103262306a36Sopenharmony_ci}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci/* Merges all the PT_NOTE headers into one. */
103562306a36Sopenharmony_cistatic int __init merge_note_headers_elf32(char *elfptr, size_t *elfsz,
103662306a36Sopenharmony_ci					   char **notes_buf, size_t *notes_sz)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	int i, nr_ptnote=0, rc=0;
103962306a36Sopenharmony_ci	char *tmp;
104062306a36Sopenharmony_ci	Elf32_Ehdr *ehdr_ptr;
104162306a36Sopenharmony_ci	Elf32_Phdr phdr;
104262306a36Sopenharmony_ci	u64 phdr_sz = 0, note_off;
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	ehdr_ptr = (Elf32_Ehdr *)elfptr;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	rc = update_note_header_size_elf32(ehdr_ptr);
104762306a36Sopenharmony_ci	if (rc < 0)
104862306a36Sopenharmony_ci		return rc;
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	rc = get_note_number_and_size_elf32(ehdr_ptr, &nr_ptnote, &phdr_sz);
105162306a36Sopenharmony_ci	if (rc < 0)
105262306a36Sopenharmony_ci		return rc;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	*notes_sz = roundup(phdr_sz, PAGE_SIZE);
105562306a36Sopenharmony_ci	*notes_buf = vmcore_alloc_buf(*notes_sz);
105662306a36Sopenharmony_ci	if (!*notes_buf)
105762306a36Sopenharmony_ci		return -ENOMEM;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	rc = copy_notes_elf32(ehdr_ptr, *notes_buf);
106062306a36Sopenharmony_ci	if (rc < 0)
106162306a36Sopenharmony_ci		return rc;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* Prepare merged PT_NOTE program header. */
106462306a36Sopenharmony_ci	phdr.p_type    = PT_NOTE;
106562306a36Sopenharmony_ci	phdr.p_flags   = 0;
106662306a36Sopenharmony_ci	note_off = sizeof(Elf32_Ehdr) +
106762306a36Sopenharmony_ci			(ehdr_ptr->e_phnum - nr_ptnote +1) * sizeof(Elf32_Phdr);
106862306a36Sopenharmony_ci	phdr.p_offset  = roundup(note_off, PAGE_SIZE);
106962306a36Sopenharmony_ci	phdr.p_vaddr   = phdr.p_paddr = 0;
107062306a36Sopenharmony_ci	phdr.p_filesz  = phdr.p_memsz = phdr_sz;
107162306a36Sopenharmony_ci	phdr.p_align   = 4;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* Add merged PT_NOTE program header*/
107462306a36Sopenharmony_ci	tmp = elfptr + sizeof(Elf32_Ehdr);
107562306a36Sopenharmony_ci	memcpy(tmp, &phdr, sizeof(phdr));
107662306a36Sopenharmony_ci	tmp += sizeof(phdr);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	/* Remove unwanted PT_NOTE program headers. */
107962306a36Sopenharmony_ci	i = (nr_ptnote - 1) * sizeof(Elf32_Phdr);
108062306a36Sopenharmony_ci	*elfsz = *elfsz - i;
108162306a36Sopenharmony_ci	memmove(tmp, tmp+i, ((*elfsz)-sizeof(Elf32_Ehdr)-sizeof(Elf32_Phdr)));
108262306a36Sopenharmony_ci	memset(elfptr + *elfsz, 0, i);
108362306a36Sopenharmony_ci	*elfsz = roundup(*elfsz, PAGE_SIZE);
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_ci	/* Modify e_phnum to reflect merged headers. */
108662306a36Sopenharmony_ci	ehdr_ptr->e_phnum = ehdr_ptr->e_phnum - nr_ptnote + 1;
108762306a36Sopenharmony_ci
108862306a36Sopenharmony_ci	/* Store the size of all notes.  We need this to update the note
108962306a36Sopenharmony_ci	 * header when the device dumps will be added.
109062306a36Sopenharmony_ci	 */
109162306a36Sopenharmony_ci	elfnotes_orig_sz = phdr.p_memsz;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci	return 0;
109462306a36Sopenharmony_ci}
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci/* Add memory chunks represented by program headers to vmcore list. Also update
109762306a36Sopenharmony_ci * the new offset fields of exported program headers. */
109862306a36Sopenharmony_cistatic int __init process_ptload_program_headers_elf64(char *elfptr,
109962306a36Sopenharmony_ci						size_t elfsz,
110062306a36Sopenharmony_ci						size_t elfnotes_sz,
110162306a36Sopenharmony_ci						struct list_head *vc_list)
110262306a36Sopenharmony_ci{
110362306a36Sopenharmony_ci	int i;
110462306a36Sopenharmony_ci	Elf64_Ehdr *ehdr_ptr;
110562306a36Sopenharmony_ci	Elf64_Phdr *phdr_ptr;
110662306a36Sopenharmony_ci	loff_t vmcore_off;
110762306a36Sopenharmony_ci	struct vmcore *new;
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_ci	ehdr_ptr = (Elf64_Ehdr *)elfptr;
111062306a36Sopenharmony_ci	phdr_ptr = (Elf64_Phdr*)(elfptr + sizeof(Elf64_Ehdr)); /* PT_NOTE hdr */
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	/* Skip ELF header, program headers and ELF note segment. */
111362306a36Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
111662306a36Sopenharmony_ci		u64 paddr, start, end, size;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_LOAD)
111962306a36Sopenharmony_ci			continue;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		paddr = phdr_ptr->p_offset;
112262306a36Sopenharmony_ci		start = rounddown(paddr, PAGE_SIZE);
112362306a36Sopenharmony_ci		end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE);
112462306a36Sopenharmony_ci		size = end - start;
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		/* Add this contiguous chunk of memory to vmcore list.*/
112762306a36Sopenharmony_ci		new = get_new_element();
112862306a36Sopenharmony_ci		if (!new)
112962306a36Sopenharmony_ci			return -ENOMEM;
113062306a36Sopenharmony_ci		new->paddr = start;
113162306a36Sopenharmony_ci		new->size = size;
113262306a36Sopenharmony_ci		list_add_tail(&new->list, vc_list);
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		/* Update the program header offset. */
113562306a36Sopenharmony_ci		phdr_ptr->p_offset = vmcore_off + (paddr - start);
113662306a36Sopenharmony_ci		vmcore_off = vmcore_off + size;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	return 0;
113962306a36Sopenharmony_ci}
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_cistatic int __init process_ptload_program_headers_elf32(char *elfptr,
114262306a36Sopenharmony_ci						size_t elfsz,
114362306a36Sopenharmony_ci						size_t elfnotes_sz,
114462306a36Sopenharmony_ci						struct list_head *vc_list)
114562306a36Sopenharmony_ci{
114662306a36Sopenharmony_ci	int i;
114762306a36Sopenharmony_ci	Elf32_Ehdr *ehdr_ptr;
114862306a36Sopenharmony_ci	Elf32_Phdr *phdr_ptr;
114962306a36Sopenharmony_ci	loff_t vmcore_off;
115062306a36Sopenharmony_ci	struct vmcore *new;
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	ehdr_ptr = (Elf32_Ehdr *)elfptr;
115362306a36Sopenharmony_ci	phdr_ptr = (Elf32_Phdr*)(elfptr + sizeof(Elf32_Ehdr)); /* PT_NOTE hdr */
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	/* Skip ELF header, program headers and ELF note segment. */
115662306a36Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	for (i = 0; i < ehdr_ptr->e_phnum; i++, phdr_ptr++) {
115962306a36Sopenharmony_ci		u64 paddr, start, end, size;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci		if (phdr_ptr->p_type != PT_LOAD)
116262306a36Sopenharmony_ci			continue;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci		paddr = phdr_ptr->p_offset;
116562306a36Sopenharmony_ci		start = rounddown(paddr, PAGE_SIZE);
116662306a36Sopenharmony_ci		end = roundup(paddr + phdr_ptr->p_memsz, PAGE_SIZE);
116762306a36Sopenharmony_ci		size = end - start;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		/* Add this contiguous chunk of memory to vmcore list.*/
117062306a36Sopenharmony_ci		new = get_new_element();
117162306a36Sopenharmony_ci		if (!new)
117262306a36Sopenharmony_ci			return -ENOMEM;
117362306a36Sopenharmony_ci		new->paddr = start;
117462306a36Sopenharmony_ci		new->size = size;
117562306a36Sopenharmony_ci		list_add_tail(&new->list, vc_list);
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		/* Update the program header offset */
117862306a36Sopenharmony_ci		phdr_ptr->p_offset = vmcore_off + (paddr - start);
117962306a36Sopenharmony_ci		vmcore_off = vmcore_off + size;
118062306a36Sopenharmony_ci	}
118162306a36Sopenharmony_ci	return 0;
118262306a36Sopenharmony_ci}
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci/* Sets offset fields of vmcore elements. */
118562306a36Sopenharmony_cistatic void set_vmcore_list_offsets(size_t elfsz, size_t elfnotes_sz,
118662306a36Sopenharmony_ci				    struct list_head *vc_list)
118762306a36Sopenharmony_ci{
118862306a36Sopenharmony_ci	loff_t vmcore_off;
118962306a36Sopenharmony_ci	struct vmcore *m;
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	/* Skip ELF header, program headers and ELF note segment. */
119262306a36Sopenharmony_ci	vmcore_off = elfsz + elfnotes_sz;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci	list_for_each_entry(m, vc_list, list) {
119562306a36Sopenharmony_ci		m->offset = vmcore_off;
119662306a36Sopenharmony_ci		vmcore_off += m->size;
119762306a36Sopenharmony_ci	}
119862306a36Sopenharmony_ci}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_cistatic void free_elfcorebuf(void)
120162306a36Sopenharmony_ci{
120262306a36Sopenharmony_ci	free_pages((unsigned long)elfcorebuf, get_order(elfcorebuf_sz_orig));
120362306a36Sopenharmony_ci	elfcorebuf = NULL;
120462306a36Sopenharmony_ci	vfree(elfnotes_buf);
120562306a36Sopenharmony_ci	elfnotes_buf = NULL;
120662306a36Sopenharmony_ci}
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_cistatic int __init parse_crash_elf64_headers(void)
120962306a36Sopenharmony_ci{
121062306a36Sopenharmony_ci	int rc=0;
121162306a36Sopenharmony_ci	Elf64_Ehdr ehdr;
121262306a36Sopenharmony_ci	u64 addr;
121362306a36Sopenharmony_ci
121462306a36Sopenharmony_ci	addr = elfcorehdr_addr;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci	/* Read ELF header */
121762306a36Sopenharmony_ci	rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf64_Ehdr), &addr);
121862306a36Sopenharmony_ci	if (rc < 0)
121962306a36Sopenharmony_ci		return rc;
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci	/* Do some basic Verification. */
122262306a36Sopenharmony_ci	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
122362306a36Sopenharmony_ci		(ehdr.e_type != ET_CORE) ||
122462306a36Sopenharmony_ci		!vmcore_elf64_check_arch(&ehdr) ||
122562306a36Sopenharmony_ci		ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
122662306a36Sopenharmony_ci		ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
122762306a36Sopenharmony_ci		ehdr.e_version != EV_CURRENT ||
122862306a36Sopenharmony_ci		ehdr.e_ehsize != sizeof(Elf64_Ehdr) ||
122962306a36Sopenharmony_ci		ehdr.e_phentsize != sizeof(Elf64_Phdr) ||
123062306a36Sopenharmony_ci		ehdr.e_phnum == 0) {
123162306a36Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
123262306a36Sopenharmony_ci		return -EINVAL;
123362306a36Sopenharmony_ci	}
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	/* Read in all elf headers. */
123662306a36Sopenharmony_ci	elfcorebuf_sz_orig = sizeof(Elf64_Ehdr) +
123762306a36Sopenharmony_ci				ehdr.e_phnum * sizeof(Elf64_Phdr);
123862306a36Sopenharmony_ci	elfcorebuf_sz = elfcorebuf_sz_orig;
123962306a36Sopenharmony_ci	elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
124062306a36Sopenharmony_ci					      get_order(elfcorebuf_sz_orig));
124162306a36Sopenharmony_ci	if (!elfcorebuf)
124262306a36Sopenharmony_ci		return -ENOMEM;
124362306a36Sopenharmony_ci	addr = elfcorehdr_addr;
124462306a36Sopenharmony_ci	rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
124562306a36Sopenharmony_ci	if (rc < 0)
124662306a36Sopenharmony_ci		goto fail;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	/* Merge all PT_NOTE headers into one. */
124962306a36Sopenharmony_ci	rc = merge_note_headers_elf64(elfcorebuf, &elfcorebuf_sz,
125062306a36Sopenharmony_ci				      &elfnotes_buf, &elfnotes_sz);
125162306a36Sopenharmony_ci	if (rc)
125262306a36Sopenharmony_ci		goto fail;
125362306a36Sopenharmony_ci	rc = process_ptload_program_headers_elf64(elfcorebuf, elfcorebuf_sz,
125462306a36Sopenharmony_ci						  elfnotes_sz, &vmcore_list);
125562306a36Sopenharmony_ci	if (rc)
125662306a36Sopenharmony_ci		goto fail;
125762306a36Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
125862306a36Sopenharmony_ci	return 0;
125962306a36Sopenharmony_cifail:
126062306a36Sopenharmony_ci	free_elfcorebuf();
126162306a36Sopenharmony_ci	return rc;
126262306a36Sopenharmony_ci}
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_cistatic int __init parse_crash_elf32_headers(void)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	int rc=0;
126762306a36Sopenharmony_ci	Elf32_Ehdr ehdr;
126862306a36Sopenharmony_ci	u64 addr;
126962306a36Sopenharmony_ci
127062306a36Sopenharmony_ci	addr = elfcorehdr_addr;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	/* Read ELF header */
127362306a36Sopenharmony_ci	rc = elfcorehdr_read((char *)&ehdr, sizeof(Elf32_Ehdr), &addr);
127462306a36Sopenharmony_ci	if (rc < 0)
127562306a36Sopenharmony_ci		return rc;
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci	/* Do some basic Verification. */
127862306a36Sopenharmony_ci	if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0 ||
127962306a36Sopenharmony_ci		(ehdr.e_type != ET_CORE) ||
128062306a36Sopenharmony_ci		!vmcore_elf32_check_arch(&ehdr) ||
128162306a36Sopenharmony_ci		ehdr.e_ident[EI_CLASS] != ELFCLASS32||
128262306a36Sopenharmony_ci		ehdr.e_ident[EI_VERSION] != EV_CURRENT ||
128362306a36Sopenharmony_ci		ehdr.e_version != EV_CURRENT ||
128462306a36Sopenharmony_ci		ehdr.e_ehsize != sizeof(Elf32_Ehdr) ||
128562306a36Sopenharmony_ci		ehdr.e_phentsize != sizeof(Elf32_Phdr) ||
128662306a36Sopenharmony_ci		ehdr.e_phnum == 0) {
128762306a36Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
128862306a36Sopenharmony_ci		return -EINVAL;
128962306a36Sopenharmony_ci	}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	/* Read in all elf headers. */
129262306a36Sopenharmony_ci	elfcorebuf_sz_orig = sizeof(Elf32_Ehdr) + ehdr.e_phnum * sizeof(Elf32_Phdr);
129362306a36Sopenharmony_ci	elfcorebuf_sz = elfcorebuf_sz_orig;
129462306a36Sopenharmony_ci	elfcorebuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
129562306a36Sopenharmony_ci					      get_order(elfcorebuf_sz_orig));
129662306a36Sopenharmony_ci	if (!elfcorebuf)
129762306a36Sopenharmony_ci		return -ENOMEM;
129862306a36Sopenharmony_ci	addr = elfcorehdr_addr;
129962306a36Sopenharmony_ci	rc = elfcorehdr_read(elfcorebuf, elfcorebuf_sz_orig, &addr);
130062306a36Sopenharmony_ci	if (rc < 0)
130162306a36Sopenharmony_ci		goto fail;
130262306a36Sopenharmony_ci
130362306a36Sopenharmony_ci	/* Merge all PT_NOTE headers into one. */
130462306a36Sopenharmony_ci	rc = merge_note_headers_elf32(elfcorebuf, &elfcorebuf_sz,
130562306a36Sopenharmony_ci				      &elfnotes_buf, &elfnotes_sz);
130662306a36Sopenharmony_ci	if (rc)
130762306a36Sopenharmony_ci		goto fail;
130862306a36Sopenharmony_ci	rc = process_ptload_program_headers_elf32(elfcorebuf, elfcorebuf_sz,
130962306a36Sopenharmony_ci						  elfnotes_sz, &vmcore_list);
131062306a36Sopenharmony_ci	if (rc)
131162306a36Sopenharmony_ci		goto fail;
131262306a36Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
131362306a36Sopenharmony_ci	return 0;
131462306a36Sopenharmony_cifail:
131562306a36Sopenharmony_ci	free_elfcorebuf();
131662306a36Sopenharmony_ci	return rc;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic int __init parse_crash_elf_headers(void)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	unsigned char e_ident[EI_NIDENT];
132262306a36Sopenharmony_ci	u64 addr;
132362306a36Sopenharmony_ci	int rc=0;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	addr = elfcorehdr_addr;
132662306a36Sopenharmony_ci	rc = elfcorehdr_read(e_ident, EI_NIDENT, &addr);
132762306a36Sopenharmony_ci	if (rc < 0)
132862306a36Sopenharmony_ci		return rc;
132962306a36Sopenharmony_ci	if (memcmp(e_ident, ELFMAG, SELFMAG) != 0) {
133062306a36Sopenharmony_ci		pr_warn("Warning: Core image elf header not found\n");
133162306a36Sopenharmony_ci		return -EINVAL;
133262306a36Sopenharmony_ci	}
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci	if (e_ident[EI_CLASS] == ELFCLASS64) {
133562306a36Sopenharmony_ci		rc = parse_crash_elf64_headers();
133662306a36Sopenharmony_ci		if (rc)
133762306a36Sopenharmony_ci			return rc;
133862306a36Sopenharmony_ci	} else if (e_ident[EI_CLASS] == ELFCLASS32) {
133962306a36Sopenharmony_ci		rc = parse_crash_elf32_headers();
134062306a36Sopenharmony_ci		if (rc)
134162306a36Sopenharmony_ci			return rc;
134262306a36Sopenharmony_ci	} else {
134362306a36Sopenharmony_ci		pr_warn("Warning: Core image elf header is not sane\n");
134462306a36Sopenharmony_ci		return -EINVAL;
134562306a36Sopenharmony_ci	}
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	/* Determine vmcore size. */
134862306a36Sopenharmony_ci	vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz,
134962306a36Sopenharmony_ci				      &vmcore_list);
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	return 0;
135262306a36Sopenharmony_ci}
135362306a36Sopenharmony_ci
135462306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
135562306a36Sopenharmony_ci/**
135662306a36Sopenharmony_ci * vmcoredd_write_header - Write vmcore device dump header at the
135762306a36Sopenharmony_ci * beginning of the dump's buffer.
135862306a36Sopenharmony_ci * @buf: Output buffer where the note is written
135962306a36Sopenharmony_ci * @data: Dump info
136062306a36Sopenharmony_ci * @size: Size of the dump
136162306a36Sopenharmony_ci *
136262306a36Sopenharmony_ci * Fills beginning of the dump's buffer with vmcore device dump header.
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cistatic void vmcoredd_write_header(void *buf, struct vmcoredd_data *data,
136562306a36Sopenharmony_ci				  u32 size)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct vmcoredd_header *vdd_hdr = (struct vmcoredd_header *)buf;
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	vdd_hdr->n_namesz = sizeof(vdd_hdr->name);
137062306a36Sopenharmony_ci	vdd_hdr->n_descsz = size + sizeof(vdd_hdr->dump_name);
137162306a36Sopenharmony_ci	vdd_hdr->n_type = NT_VMCOREDD;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	strncpy((char *)vdd_hdr->name, VMCOREDD_NOTE_NAME,
137462306a36Sopenharmony_ci		sizeof(vdd_hdr->name));
137562306a36Sopenharmony_ci	memcpy(vdd_hdr->dump_name, data->dump_name, sizeof(vdd_hdr->dump_name));
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci/**
137962306a36Sopenharmony_ci * vmcoredd_update_program_headers - Update all ELF program headers
138062306a36Sopenharmony_ci * @elfptr: Pointer to elf header
138162306a36Sopenharmony_ci * @elfnotesz: Size of elf notes aligned to page size
138262306a36Sopenharmony_ci * @vmcoreddsz: Size of device dumps to be added to elf note header
138362306a36Sopenharmony_ci *
138462306a36Sopenharmony_ci * Determine type of ELF header (Elf64 or Elf32) and update the elf note size.
138562306a36Sopenharmony_ci * Also update the offsets of all the program headers after the elf note header.
138662306a36Sopenharmony_ci */
138762306a36Sopenharmony_cistatic void vmcoredd_update_program_headers(char *elfptr, size_t elfnotesz,
138862306a36Sopenharmony_ci					    size_t vmcoreddsz)
138962306a36Sopenharmony_ci{
139062306a36Sopenharmony_ci	unsigned char *e_ident = (unsigned char *)elfptr;
139162306a36Sopenharmony_ci	u64 start, end, size;
139262306a36Sopenharmony_ci	loff_t vmcore_off;
139362306a36Sopenharmony_ci	u32 i;
139462306a36Sopenharmony_ci
139562306a36Sopenharmony_ci	vmcore_off = elfcorebuf_sz + elfnotesz;
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci	if (e_ident[EI_CLASS] == ELFCLASS64) {
139862306a36Sopenharmony_ci		Elf64_Ehdr *ehdr = (Elf64_Ehdr *)elfptr;
139962306a36Sopenharmony_ci		Elf64_Phdr *phdr = (Elf64_Phdr *)(elfptr + sizeof(Elf64_Ehdr));
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci		/* Update all program headers */
140262306a36Sopenharmony_ci		for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
140362306a36Sopenharmony_ci			if (phdr->p_type == PT_NOTE) {
140462306a36Sopenharmony_ci				/* Update note size */
140562306a36Sopenharmony_ci				phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
140662306a36Sopenharmony_ci				phdr->p_filesz = phdr->p_memsz;
140762306a36Sopenharmony_ci				continue;
140862306a36Sopenharmony_ci			}
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci			start = rounddown(phdr->p_offset, PAGE_SIZE);
141162306a36Sopenharmony_ci			end = roundup(phdr->p_offset + phdr->p_memsz,
141262306a36Sopenharmony_ci				      PAGE_SIZE);
141362306a36Sopenharmony_ci			size = end - start;
141462306a36Sopenharmony_ci			phdr->p_offset = vmcore_off + (phdr->p_offset - start);
141562306a36Sopenharmony_ci			vmcore_off += size;
141662306a36Sopenharmony_ci		}
141762306a36Sopenharmony_ci	} else {
141862306a36Sopenharmony_ci		Elf32_Ehdr *ehdr = (Elf32_Ehdr *)elfptr;
141962306a36Sopenharmony_ci		Elf32_Phdr *phdr = (Elf32_Phdr *)(elfptr + sizeof(Elf32_Ehdr));
142062306a36Sopenharmony_ci
142162306a36Sopenharmony_ci		/* Update all program headers */
142262306a36Sopenharmony_ci		for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
142362306a36Sopenharmony_ci			if (phdr->p_type == PT_NOTE) {
142462306a36Sopenharmony_ci				/* Update note size */
142562306a36Sopenharmony_ci				phdr->p_memsz = elfnotes_orig_sz + vmcoreddsz;
142662306a36Sopenharmony_ci				phdr->p_filesz = phdr->p_memsz;
142762306a36Sopenharmony_ci				continue;
142862306a36Sopenharmony_ci			}
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci			start = rounddown(phdr->p_offset, PAGE_SIZE);
143162306a36Sopenharmony_ci			end = roundup(phdr->p_offset + phdr->p_memsz,
143262306a36Sopenharmony_ci				      PAGE_SIZE);
143362306a36Sopenharmony_ci			size = end - start;
143462306a36Sopenharmony_ci			phdr->p_offset = vmcore_off + (phdr->p_offset - start);
143562306a36Sopenharmony_ci			vmcore_off += size;
143662306a36Sopenharmony_ci		}
143762306a36Sopenharmony_ci	}
143862306a36Sopenharmony_ci}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci/**
144162306a36Sopenharmony_ci * vmcoredd_update_size - Update the total size of the device dumps and update
144262306a36Sopenharmony_ci * ELF header
144362306a36Sopenharmony_ci * @dump_size: Size of the current device dump to be added to total size
144462306a36Sopenharmony_ci *
144562306a36Sopenharmony_ci * Update the total size of all the device dumps and update the ELF program
144662306a36Sopenharmony_ci * headers. Calculate the new offsets for the vmcore list and update the
144762306a36Sopenharmony_ci * total vmcore size.
144862306a36Sopenharmony_ci */
144962306a36Sopenharmony_cistatic void vmcoredd_update_size(size_t dump_size)
145062306a36Sopenharmony_ci{
145162306a36Sopenharmony_ci	vmcoredd_orig_sz += dump_size;
145262306a36Sopenharmony_ci	elfnotes_sz = roundup(elfnotes_orig_sz, PAGE_SIZE) + vmcoredd_orig_sz;
145362306a36Sopenharmony_ci	vmcoredd_update_program_headers(elfcorebuf, elfnotes_sz,
145462306a36Sopenharmony_ci					vmcoredd_orig_sz);
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	/* Update vmcore list offsets */
145762306a36Sopenharmony_ci	set_vmcore_list_offsets(elfcorebuf_sz, elfnotes_sz, &vmcore_list);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci	vmcore_size = get_vmcore_size(elfcorebuf_sz, elfnotes_sz,
146062306a36Sopenharmony_ci				      &vmcore_list);
146162306a36Sopenharmony_ci	proc_vmcore->size = vmcore_size;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci/**
146562306a36Sopenharmony_ci * vmcore_add_device_dump - Add a buffer containing device dump to vmcore
146662306a36Sopenharmony_ci * @data: dump info.
146762306a36Sopenharmony_ci *
146862306a36Sopenharmony_ci * Allocate a buffer and invoke the calling driver's dump collect routine.
146962306a36Sopenharmony_ci * Write ELF note at the beginning of the buffer to indicate vmcore device
147062306a36Sopenharmony_ci * dump and add the dump to global list.
147162306a36Sopenharmony_ci */
147262306a36Sopenharmony_ciint vmcore_add_device_dump(struct vmcoredd_data *data)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct vmcoredd_node *dump;
147562306a36Sopenharmony_ci	void *buf = NULL;
147662306a36Sopenharmony_ci	size_t data_size;
147762306a36Sopenharmony_ci	int ret;
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	if (vmcoredd_disabled) {
148062306a36Sopenharmony_ci		pr_err_once("Device dump is disabled\n");
148162306a36Sopenharmony_ci		return -EINVAL;
148262306a36Sopenharmony_ci	}
148362306a36Sopenharmony_ci
148462306a36Sopenharmony_ci	if (!data || !strlen(data->dump_name) ||
148562306a36Sopenharmony_ci	    !data->vmcoredd_callback || !data->size)
148662306a36Sopenharmony_ci		return -EINVAL;
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	dump = vzalloc(sizeof(*dump));
148962306a36Sopenharmony_ci	if (!dump) {
149062306a36Sopenharmony_ci		ret = -ENOMEM;
149162306a36Sopenharmony_ci		goto out_err;
149262306a36Sopenharmony_ci	}
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* Keep size of the buffer page aligned so that it can be mmaped */
149562306a36Sopenharmony_ci	data_size = roundup(sizeof(struct vmcoredd_header) + data->size,
149662306a36Sopenharmony_ci			    PAGE_SIZE);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	/* Allocate buffer for driver's to write their dumps */
149962306a36Sopenharmony_ci	buf = vmcore_alloc_buf(data_size);
150062306a36Sopenharmony_ci	if (!buf) {
150162306a36Sopenharmony_ci		ret = -ENOMEM;
150262306a36Sopenharmony_ci		goto out_err;
150362306a36Sopenharmony_ci	}
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	vmcoredd_write_header(buf, data, data_size -
150662306a36Sopenharmony_ci			      sizeof(struct vmcoredd_header));
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* Invoke the driver's dump collection routing */
150962306a36Sopenharmony_ci	ret = data->vmcoredd_callback(data, buf +
151062306a36Sopenharmony_ci				      sizeof(struct vmcoredd_header));
151162306a36Sopenharmony_ci	if (ret)
151262306a36Sopenharmony_ci		goto out_err;
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	dump->buf = buf;
151562306a36Sopenharmony_ci	dump->size = data_size;
151662306a36Sopenharmony_ci
151762306a36Sopenharmony_ci	/* Add the dump to driver sysfs list */
151862306a36Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
151962306a36Sopenharmony_ci	list_add_tail(&dump->list, &vmcoredd_list);
152062306a36Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	vmcoredd_update_size(data_size);
152362306a36Sopenharmony_ci	return 0;
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ciout_err:
152662306a36Sopenharmony_ci	vfree(buf);
152762306a36Sopenharmony_ci	vfree(dump);
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	return ret;
153062306a36Sopenharmony_ci}
153162306a36Sopenharmony_ciEXPORT_SYMBOL(vmcore_add_device_dump);
153262306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci/* Free all dumps in vmcore device dump list */
153562306a36Sopenharmony_cistatic void vmcore_free_device_dumps(void)
153662306a36Sopenharmony_ci{
153762306a36Sopenharmony_ci#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP
153862306a36Sopenharmony_ci	mutex_lock(&vmcoredd_mutex);
153962306a36Sopenharmony_ci	while (!list_empty(&vmcoredd_list)) {
154062306a36Sopenharmony_ci		struct vmcoredd_node *dump;
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci		dump = list_first_entry(&vmcoredd_list, struct vmcoredd_node,
154362306a36Sopenharmony_ci					list);
154462306a36Sopenharmony_ci		list_del(&dump->list);
154562306a36Sopenharmony_ci		vfree(dump->buf);
154662306a36Sopenharmony_ci		vfree(dump);
154762306a36Sopenharmony_ci	}
154862306a36Sopenharmony_ci	mutex_unlock(&vmcoredd_mutex);
154962306a36Sopenharmony_ci#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_ci/* Init function for vmcore module. */
155362306a36Sopenharmony_cistatic int __init vmcore_init(void)
155462306a36Sopenharmony_ci{
155562306a36Sopenharmony_ci	int rc = 0;
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci	/* Allow architectures to allocate ELF header in 2nd kernel */
155862306a36Sopenharmony_ci	rc = elfcorehdr_alloc(&elfcorehdr_addr, &elfcorehdr_size);
155962306a36Sopenharmony_ci	if (rc)
156062306a36Sopenharmony_ci		return rc;
156162306a36Sopenharmony_ci	/*
156262306a36Sopenharmony_ci	 * If elfcorehdr= has been passed in cmdline or created in 2nd kernel,
156362306a36Sopenharmony_ci	 * then capture the dump.
156462306a36Sopenharmony_ci	 */
156562306a36Sopenharmony_ci	if (!(is_vmcore_usable()))
156662306a36Sopenharmony_ci		return rc;
156762306a36Sopenharmony_ci	rc = parse_crash_elf_headers();
156862306a36Sopenharmony_ci	if (rc) {
156962306a36Sopenharmony_ci		elfcorehdr_free(elfcorehdr_addr);
157062306a36Sopenharmony_ci		pr_warn("Kdump: vmcore not initialized\n");
157162306a36Sopenharmony_ci		return rc;
157262306a36Sopenharmony_ci	}
157362306a36Sopenharmony_ci	elfcorehdr_free(elfcorehdr_addr);
157462306a36Sopenharmony_ci	elfcorehdr_addr = ELFCORE_ADDR_ERR;
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci	proc_vmcore = proc_create("vmcore", S_IRUSR, NULL, &vmcore_proc_ops);
157762306a36Sopenharmony_ci	if (proc_vmcore)
157862306a36Sopenharmony_ci		proc_vmcore->size = vmcore_size;
157962306a36Sopenharmony_ci	return 0;
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_cifs_initcall(vmcore_init);
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci/* Cleanup function for vmcore module. */
158462306a36Sopenharmony_civoid vmcore_cleanup(void)
158562306a36Sopenharmony_ci{
158662306a36Sopenharmony_ci	if (proc_vmcore) {
158762306a36Sopenharmony_ci		proc_remove(proc_vmcore);
158862306a36Sopenharmony_ci		proc_vmcore = NULL;
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	/* clear the vmcore list. */
159262306a36Sopenharmony_ci	while (!list_empty(&vmcore_list)) {
159362306a36Sopenharmony_ci		struct vmcore *m;
159462306a36Sopenharmony_ci
159562306a36Sopenharmony_ci		m = list_first_entry(&vmcore_list, struct vmcore, list);
159662306a36Sopenharmony_ci		list_del(&m->list);
159762306a36Sopenharmony_ci		kfree(m);
159862306a36Sopenharmony_ci	}
159962306a36Sopenharmony_ci	free_elfcorebuf();
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	/* clear vmcore device dump list */
160262306a36Sopenharmony_ci	vmcore_free_device_dumps();
160362306a36Sopenharmony_ci}
1604