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