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