// SPDX-License-Identifier: GPL-2.0-only/* * fs/proc/vmcore.c Interface for accessing the crash * dump from the system's previous life. * Heavily borrowed from fs/proc/kcore.c * Created by: Hariprasad Nellitheertha (hari@in.ibm.com) * Copyright (C) IBM Corporation, 2004. All rights reserved * */ #define pr_fmt(fmt) "vmcore: " fmt #include <linux/mm.h>#include <linux/kcore.h>#include <linux/user.h>#include <linux/elf.h>#include <linux/elfcore.h>#include <linux/export.h>#include <linux/slab.h>#include <linux/highmem.h>#include <linux/printk.h>#include <linux/memblock.h>#include <linux/init.h>#include <linux/crash_dump.h>#include <linux/list.h>#include <linux/moduleparam.h>#include <linux/mutex.h>#include <linux/vmalloc.h>#include <linux/pagemap.h>#include <linux/uio.h>#include <linux/cc_platform.h>#include <asm/io.h>#include "internal.h" /* List representing chunks of contiguous memory areas and their offsets in * vmcore file. */static LIST_HEAD(vmcore_list); /* Stores the pointer to the buffer containing kernel elf core headers. */static char *elfcorebuf;static size_t elfcorebuf_sz;static size_t elfcorebuf_sz_orig; static char *elfnotes_buf;static size_t elfnotes_sz;/* Size of all notes minus the device dump notes */static size_t elfnotes_orig_sz; /* Total size of vmcore file. */static u64 vmcore_size; static struct proc_dir_entry *proc_vmcore; #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMPstruct vmcoredd_node { struct list_head list; /* List of dumps */ void *buf; /* Buffer containing device's dump */ unsigned int size; /* Size of the buffer */}; /* Device Dump list and mutex to synchronize access to list */static LIST_HEAD(vmcoredd_list); static bool vmcoredd_disabled;core_param(novmcoredd, vmcoredd_disabled, bool, 0);#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Device Dump Size */static size_t vmcoredd_orig_sz; static DEFINE_MUTEX(vmcore_mutex); DEFINE_STATIC_SRCU(vmcore_cb_srcu);/* List of registered vmcore callbacks. */static LIST_HEAD(vmcore_cb_list);/* Whether the vmcore has been opened once. */static bool vmcore_opened;/* Whether the vmcore is currently open. */static unsigned int vmcore_open; static void vmcore_process_device_ram(struct vmcore_cb *cb); void register_vmcore_cb(struct vmcore_cb *cb){ INIT_LIST_HEAD(&cb->next); mutex_lock(&vmcore_mutex); list_add_tail(&cb->next, &vmcore_cb_list); /* * Registering a vmcore callback after the vmcore was opened is * very unusual (e.g., manual driver loading). */ if (vmcore_opened) pr_warn_once("Unexpected vmcore callback registration\n"); if (!vmcore_open && cb->get_device_ram) vmcore_process_device_ram(cb); mutex_unlock(&vmcore_mutex);}EXPORT_SYMBOL_GPL(register_vmcore_cb); void unregister_vmcore_cb(struct vmcore_cb *cb){ mutex_lock(&vmcore_mutex); list_del_rcu(&cb->next); /* * Unregistering a vmcore callback after the vmcore was opened is * very unusual (e.g., forced driver removal), but we cannot stop * unregistering. */ if (vmcore_opened) pr_warn_once("Unexpected vmcore callback unregistration\n"); mutex_unlock(&vmcore_mutex); synchronize_srcu(&vmcore_cb_srcu);}EXPORT_SYMBOL_GPL(unregister_vmcore_cb); static bool pfn_is_ram(unsigned long pfn){ struct vmcore_cb *cb; bool ret = true; list_for_each_entry_srcu(cb, &vmcore_cb_list, next, srcu_read_lock_held(&vmcore_cb_srcu)) { if (unlikely(!cb->pfn_is_ram)) continue; ret = cb->pfn_is_ram(cb, pfn); if (!ret) break; } return ret;} static int open_vmcore(struct inode *inode, struct file *file){ mutex_lock(&vmcore_mutex); vmcore_opened = true; if (vmcore_open + 1 == 0) { mutex_unlock(&vmcore_mutex); return -EBUSY; } vmcore_open++; mutex_unlock(&vmcore_mutex); return 0;} static int release_vmcore(struct inode *inode, struct file *file){ mutex_lock(&vmcore_mutex); vmcore_open--; mutex_unlock(&vmcore_mutex); return 0;} /* Reads a page from the oldmem device from given offset. */ssize_t read_from_oldmem(struct iov_iter *iter, size_t count, u64 *ppos, bool encrypted){ unsigned long pfn, offset; ssize_t nr_bytes; ssize_t read = 0, tmp; int idx; if (!count) return 0; offset = (unsigned long)(*ppos % PAGE_SIZE); pfn = (unsigned long)(*ppos / PAGE_SIZE); idx = srcu_read_lock(&vmcore_cb_srcu); do { if (count > (PAGE_SIZE - offset)) nr_bytes = PAGE_SIZE - offset; else nr_bytes = count; /* If pfn is not ram, return zeros for sparse dump files */ if (!pfn_is_ram(pfn)) { tmp = iov_iter_zero(nr_bytes, iter); } else { if (encrypted) tmp = copy_oldmem_page_encrypted(iter, pfn, nr_bytes, offset); else tmp = copy_oldmem_page(iter, pfn, nr_bytes, offset); } if (tmp < nr_bytes) { srcu_read_unlock(&vmcore_cb_srcu, idx); return -EFAULT; } *ppos += nr_bytes; count -= nr_bytes; read += nr_bytes; ++pfn; offset = 0; } while (count); srcu_read_unlock(&vmcore_cb_srcu, idx); return read;} /* * Architectures may override this function to allocate ELF header in 2nd kernel */int __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size){ return 0;} /* * Architectures may override this function to free header */void __weak elfcorehdr_free(unsigned long long addr){} /* * Architectures may override this function to read from ELF header */ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos){ struct kvec kvec = { .iov_base = buf, .iov_len = count }; struct iov_iter iter; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count); return read_from_oldmem(&iter, count, ppos, false);} /* * Architectures may override this function to read from notes sections */ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos){ struct kvec kvec = { .iov_base = buf, .iov_len = count }; struct iov_iter iter; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count); return read_from_oldmem(&iter, count, ppos, cc_platform_has(CC_ATTR_MEM_ENCRYPT));} /* * Architectures may override this function to map oldmem */int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot){ prot = pgprot_encrypted(prot); return remap_pfn_range(vma, from, pfn, size, prot);} /* * Architectures which support memory encryption override this. */ssize_t __weak copy_oldmem_page_encrypted(struct iov_iter *iter, unsigned long pfn, size_t csize, unsigned long offset){ return copy_oldmem_page(iter, pfn, csize, offset);} #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMPstatic int vmcoredd_copy_dumps(struct iov_iter *iter, u64 start, size_t size){ struct vmcoredd_node *dump; u64 offset = 0; size_t tsz; char *buf; list_for_each_entry(dump, &vmcoredd_list, list) { if (start < offset + dump->size) { tsz = min(offset + (u64)dump->size - start, (u64)size); buf = dump->buf + start - offset; if (copy_to_iter(buf, tsz, iter) < tsz) return -EFAULT; size -= tsz; start += tsz; /* Leave now if buffer filled already */ if (!size) return 0; } offset += dump->size; } return 0;} #ifdef CONFIG_MMUstatic int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst, u64 start, size_t size){ struct vmcoredd_node *dump; u64 offset = 0; size_t tsz; char *buf; list_for_each_entry(dump, &vmcoredd_list, list) { if (start < offset + dump->size) { tsz = min(offset + (u64)dump->size - start, (u64)size); buf = dump->buf + start - offset; if (remap_vmalloc_range_partial(vma, dst, buf, 0, tsz)) return -EFAULT; size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) return 0; } offset += dump->size; } return 0;}#endif /* CONFIG_MMU */#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read from the ELF header and then the crash dump. On error, negative value is * returned otherwise number of bytes read are returned. */static ssize_t __read_vmcore(struct iov_iter *iter, loff_t *fpos){ struct vmcore_range *m = NULL; ssize_t acc = 0, tmp; size_t tsz; u64 start; if (!iov_iter_count(iter) || *fpos >= vmcore_size) return 0; iov_iter_truncate(iter, vmcore_size - *fpos); /* Read ELF core header */ if (*fpos < elfcorebuf_sz) { tsz = min(elfcorebuf_sz - (size_t)*fpos, iov_iter_count(iter)); if (copy_to_iter(elfcorebuf + *fpos, tsz, iter) < tsz) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; } /* Read ELF note segment */ if (*fpos < elfcorebuf_sz + elfnotes_sz) { void *kaddr; /* We add device dumps before other elf notes because the * other elf notes may not fill the elf notes buffer * completely and we will end up with zero-filled data * between the elf notes and the device dumps. Tools will * then try to decode this zero-filled data as valid notes * and we don't want that. Hence, adding device dumps before * the other elf notes ensure that zero-filled data can be * avoided. */#ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP /* Read device dumps */ if (*fpos < elfcorebuf_sz + vmcoredd_orig_sz) { tsz = min(elfcorebuf_sz + vmcoredd_orig_sz - (size_t)*fpos, iov_iter_count(iter)); start = *fpos - elfcorebuf_sz; if (vmcoredd_copy_dumps(iter, start, tsz)) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; }#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read remaining elf notes */ tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, iov_iter_count(iter)); kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz; if (copy_to_iter(kaddr, tsz, iter) < tsz) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; cond_resched(); } list_for_each_entry(m, &vmcore_list, list) { if (*fpos < m->offset + m->size) { tsz = (size_t)min_t(unsigned long long, m->offset + m->size - *fpos, iov_iter_count(iter)); start = m->paddr + *fpos - m->offset; tmp = read_from_oldmem(iter, tsz, &start, cc_platform_has(CC_ATTR_MEM_ENCRYPT)); if (tmp < 0) return tmp; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; } cond_resched(); } return acc;} static ssize_t read_vmcore(struct kiocb *iocb, struct iov_iter *iter){ return __read_vmcore(iter, &iocb->ki_pos);} /** * vmcore_alloc_buf - allocate buffer in vmalloc memory * @size: size of buffer * * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap * the buffer to user-space by means of remap_vmalloc_range(). * * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is * disabled and there's no need to allow users to mmap the buffer. */static inline char *vmcore_alloc_buf(size_t size){#ifdef CONFIG_MMU return vmalloc_user(size);#else return vzalloc(size);#endif} /* * Disable mmap_vmcore() if CONFIG_MMU is not defined. MMU is * essential for mmap_vmcore() in order to map physically * non-contiguous objects (ELF header, ELF note segment and memory * regions in the 1st kernel pointed to by PT_LOAD entries) into * virtually contiguous user-space in ELF layout. */#ifdef CONFIG_MMU /* * The vmcore fault handler uses the page cache and fills data using the * standard __read_vmcore() function. * * On s390 the fault handler is used for memory regions that can't be mapped * directly with remap_pfn_range(). */static vm_fault_t mmap_vmcore_fault(struct vm_fault *vmf){#ifdef CONFIG_S390 struct address_space *mapping = vmf->vma->vm_file->f_mapping; pgoff_t index = vmf->pgoff; struct iov_iter iter; struct kvec kvec; struct page *page; loff_t offset; int rc; page = find_or_create_page(mapping, index, GFP_KERNEL); if (!page) return VM_FAULT_OOM; if (!PageUptodate(page)) { offset = (loff_t) index << PAGE_SHIFT; kvec.iov_base = page_address(page); kvec.iov_len = PAGE_SIZE; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, PAGE_SIZE); rc = __read_vmcore(&iter, &offset); if (rc < 0) { unlock_page(page); put_page(page); return vmf_error(rc); } SetPageUptodate(page); } unlock_page(page); vmf->page = page; return 0;Lines 1–500 of 1756. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * fs/proc/vmcore.c Interface for accessing the crash * dump from the system's previous life. * Heavily borrowed from fs/proc/kcore.c * Created by: Hariprasad Nellitheertha (hari@in.ibm.com) * Copyright (C) IBM Corporation, 2004. All rights reserved * */ #define pr_fmt(fmt) "vmcore: " fmt #include <linux/mm.h> #include <linux/kcore.h> #include <linux/user.h> #include <linux/elf.h> #include <linux/elfcore.h> #include <linux/export.h> #include <linux/slab.h> #include <linux/highmem.h> #include <linux/printk.h> #include <linux/memblock.h> #include <linux/init.h> #include <linux/crash_dump.h> #include <linux/list.h> #include <linux/moduleparam.h> #include <linux/mutex.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/uio.h> #include <linux/cc_platform.h> #include <asm/io.h> #include "internal.h" /* List representing chunks of contiguous memory areas and their offsets in * vmcore file. */ static LIST_HEAD(vmcore_list); /* Stores the pointer to the buffer containing kernel elf core headers. */ static char *elfcorebuf; static size_t elfcorebuf_sz; static size_t elfcorebuf_sz_orig; static char *elfnotes_buf; static size_t elfnotes_sz; /* Size of all notes minus the device dump notes */ static size_t elfnotes_orig_sz; /* Total size of vmcore file. */ static u64 vmcore_size; static struct proc_dir_entry *proc_vmcore; #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP struct vmcoredd_node { struct list_head list; /* List of dumps */ void *buf; /* Buffer containing device's dump */ unsigned int size; /* Size of the buffer */ }; /* Device Dump list and mutex to synchronize access to list */ static LIST_HEAD(vmcoredd_list); static bool vmcoredd_disabled; core_param(novmcoredd, vmcoredd_disabled, bool, 0); #endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Device Dump Size */ static size_t vmcoredd_orig_sz; static DEFINE_MUTEX(vmcore_mutex); DEFINE_STATIC_SRCU(vmcore_cb_srcu); /* List of registered vmcore callbacks. */ static LIST_HEAD(vmcore_cb_list); /* Whether the vmcore has been opened once. */ static bool vmcore_opened; /* Whether the vmcore is currently open. */ static unsigned int vmcore_open; static void vmcore_process_device_ram(struct vmcore_cb *cb); void register_vmcore_cb(struct vmcore_cb *cb) { INIT_LIST_HEAD(&cb->next); mutex_lock(&vmcore_mutex); list_add_tail(&cb->next, &vmcore_cb_list); /* * Registering a vmcore callback after the vmcore was opened is * very unusual (e.g., manual driver loading). */ if (vmcore_opened) pr_warn_once("Unexpected vmcore callback registration\n"); if (!vmcore_open && cb->get_device_ram) vmcore_process_device_ram(cb); mutex_unlock(&vmcore_mutex); } EXPORT_SYMBOL_GPL(register_vmcore_cb); void unregister_vmcore_cb(struct vmcore_cb *cb) { mutex_lock(&vmcore_mutex); list_del_rcu(&cb->next); /* * Unregistering a vmcore callback after the vmcore was opened is * very unusual (e.g., forced driver removal), but we cannot stop * unregistering. */ if (vmcore_opened) pr_warn_once("Unexpected vmcore callback unregistration\n"); mutex_unlock(&vmcore_mutex); synchronize_srcu(&vmcore_cb_srcu); } EXPORT_SYMBOL_GPL(unregister_vmcore_cb); static bool pfn_is_ram(unsigned long pfn) { struct vmcore_cb *cb; bool ret = true; list_for_each_entry_srcu(cb, &vmcore_cb_list, next, srcu_read_lock_held(&vmcore_cb_srcu)) { if (unlikely(!cb->pfn_is_ram)) continue; ret = cb->pfn_is_ram(cb, pfn); if (!ret) break; } return ret; } static int open_vmcore(struct inode *inode, struct file *file) { mutex_lock(&vmcore_mutex); vmcore_opened = true; if (vmcore_open + 1 == 0) { mutex_unlock(&vmcore_mutex); return -EBUSY; } vmcore_open++; mutex_unlock(&vmcore_mutex); return 0; } static int release_vmcore(struct inode *inode, struct file *file) { mutex_lock(&vmcore_mutex); vmcore_open--; mutex_unlock(&vmcore_mutex); return 0; } /* Reads a page from the oldmem device from given offset. */ ssize_t read_from_oldmem(struct iov_iter *iter, size_t count, u64 *ppos, bool encrypted) { unsigned long pfn, offset; ssize_t nr_bytes; ssize_t read = 0, tmp; int idx; if (!count) return 0; offset = (unsigned long)(*ppos % PAGE_SIZE); pfn = (unsigned long)(*ppos / PAGE_SIZE); idx = srcu_read_lock(&vmcore_cb_srcu); do { if (count > (PAGE_SIZE - offset)) nr_bytes = PAGE_SIZE - offset; else nr_bytes = count; /* If pfn is not ram, return zeros for sparse dump files */ if (!pfn_is_ram(pfn)) { tmp = iov_iter_zero(nr_bytes, iter); } else { if (encrypted) tmp = copy_oldmem_page_encrypted(iter, pfn, nr_bytes, offset); else tmp = copy_oldmem_page(iter, pfn, nr_bytes, offset); } if (tmp < nr_bytes) { srcu_read_unlock(&vmcore_cb_srcu, idx); return -EFAULT; } *ppos += nr_bytes; count -= nr_bytes; read += nr_bytes; ++pfn; offset = 0; } while (count); srcu_read_unlock(&vmcore_cb_srcu, idx); return read; } /* * Architectures may override this function to allocate ELF header in 2nd kernel */ int __weak elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) { return 0; } /* * Architectures may override this function to free header */ void __weak elfcorehdr_free(unsigned long long addr) {} /* * Architectures may override this function to read from ELF header */ ssize_t __weak elfcorehdr_read(char *buf, size_t count, u64 *ppos) { struct kvec kvec = { .iov_base = buf, .iov_len = count }; struct iov_iter iter; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count); return read_from_oldmem(&iter, count, ppos, false); } /* * Architectures may override this function to read from notes sections */ ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos) { struct kvec kvec = { .iov_base = buf, .iov_len = count }; struct iov_iter iter; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, count); return read_from_oldmem(&iter, count, ppos, cc_platform_has(CC_ATTR_MEM_ENCRYPT)); } /* * Architectures may override this function to map oldmem */ int __weak remap_oldmem_pfn_range(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot) { prot = pgprot_encrypted(prot); return remap_pfn_range(vma, from, pfn, size, prot); } /* * Architectures which support memory encryption override this. */ ssize_t __weak copy_oldmem_page_encrypted(struct iov_iter *iter, unsigned long pfn, size_t csize, unsigned long offset) { return copy_oldmem_page(iter, pfn, csize, offset); } #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP static int vmcoredd_copy_dumps(struct iov_iter *iter, u64 start, size_t size) { struct vmcoredd_node *dump; u64 offset = 0; size_t tsz; char *buf; list_for_each_entry(dump, &vmcoredd_list, list) { if (start < offset + dump->size) { tsz = min(offset + (u64)dump->size - start, (u64)size); buf = dump->buf + start - offset; if (copy_to_iter(buf, tsz, iter) < tsz) return -EFAULT; size -= tsz; start += tsz; /* Leave now if buffer filled already */ if (!size) return 0; } offset += dump->size; } return 0; } #ifdef CONFIG_MMU static int vmcoredd_mmap_dumps(struct vm_area_struct *vma, unsigned long dst, u64 start, size_t size) { struct vmcoredd_node *dump; u64 offset = 0; size_t tsz; char *buf; list_for_each_entry(dump, &vmcoredd_list, list) { if (start < offset + dump->size) { tsz = min(offset + (u64)dump->size - start, (u64)size); buf = dump->buf + start - offset; if (remap_vmalloc_range_partial(vma, dst, buf, 0, tsz)) return -EFAULT; size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) return 0; } offset += dump->size; } return 0; } #endif /* CONFIG_MMU */ #endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read from the ELF header and then the crash dump. On error, negative value is * returned otherwise number of bytes read are returned. */ static ssize_t __read_vmcore(struct iov_iter *iter, loff_t *fpos) { struct vmcore_range *m = NULL; ssize_t acc = 0, tmp; size_t tsz; u64 start; if (!iov_iter_count(iter) || *fpos >= vmcore_size) return 0; iov_iter_truncate(iter, vmcore_size - *fpos); /* Read ELF core header */ if (*fpos < elfcorebuf_sz) { tsz = min(elfcorebuf_sz - (size_t)*fpos, iov_iter_count(iter)); if (copy_to_iter(elfcorebuf + *fpos, tsz, iter) < tsz) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; } /* Read ELF note segment */ if (*fpos < elfcorebuf_sz + elfnotes_sz) { void *kaddr; /* We add device dumps before other elf notes because the * other elf notes may not fill the elf notes buffer * completely and we will end up with zero-filled data * between the elf notes and the device dumps. Tools will * then try to decode this zero-filled data as valid notes * and we don't want that. Hence, adding device dumps before * the other elf notes ensure that zero-filled data can be * avoided. */ #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP /* Read device dumps */ if (*fpos < elfcorebuf_sz + vmcoredd_orig_sz) { tsz = min(elfcorebuf_sz + vmcoredd_orig_sz - (size_t)*fpos, iov_iter_count(iter)); start = *fpos - elfcorebuf_sz; if (vmcoredd_copy_dumps(iter, start, tsz)) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; } #endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read remaining elf notes */ tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, iov_iter_count(iter)); kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz; if (copy_to_iter(kaddr, tsz, iter) < tsz) return -EFAULT; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; cond_resched(); } list_for_each_entry(m, &vmcore_list, list) { if (*fpos < m->offset + m->size) { tsz = (size_t)min_t(unsigned long long, m->offset + m->size - *fpos, iov_iter_count(iter)); start = m->paddr + *fpos - m->offset; tmp = read_from_oldmem(iter, tsz, &start, cc_platform_has(CC_ATTR_MEM_ENCRYPT)); if (tmp < 0) return tmp; *fpos += tsz; acc += tsz; /* leave now if filled buffer already */ if (!iov_iter_count(iter)) return acc; } cond_resched(); } return acc; } static ssize_t read_vmcore(struct kiocb *iocb, struct iov_iter *iter) { return __read_vmcore(iter, &iocb->ki_pos); } /** * vmcore_alloc_buf - allocate buffer in vmalloc memory * @size: size of buffer * * If CONFIG_MMU is defined, use vmalloc_user() to allow users to mmap * the buffer to user-space by means of remap_vmalloc_range(). * * If CONFIG_MMU is not defined, use vzalloc() since mmap_vmcore() is * disabled and there's no need to allow users to mmap the buffer. */ static inline char *vmcore_alloc_buf(size_t size) { #ifdef CONFIG_MMU return vmalloc_user(size); #else return vzalloc(size); #endif } /* * Disable mmap_vmcore() if CONFIG_MMU is not defined. MMU is * essential for mmap_vmcore() in order to map physically * non-contiguous objects (ELF header, ELF note segment and memory * regions in the 1st kernel pointed to by PT_LOAD entries) into * virtually contiguous user-space in ELF layout. */ #ifdef CONFIG_MMU /* * The vmcore fault handler uses the page cache and fills data using the * standard __read_vmcore() function. * * On s390 the fault handler is used for memory regions that can't be mapped * directly with remap_pfn_range(). */ static vm_fault_t mmap_vmcore_fault(struct vm_fault *vmf) { #ifdef CONFIG_S390 struct address_space *mapping = vmf->vma->vm_file->f_mapping; pgoff_t index = vmf->pgoff; struct iov_iter iter; struct kvec kvec; struct page *page; loff_t offset; int rc; page = find_or_create_page(mapping, index, GFP_KERNEL); if (!page) return VM_FAULT_OOM; if (!PageUptodate(page)) { offset = (loff_t) index << PAGE_SHIFT; kvec.iov_base = page_address(page); kvec.iov_len = PAGE_SIZE; iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, PAGE_SIZE); rc = __read_vmcore(&iter, &offset); if (rc < 0) { unlock_page(page); put_page(page); return vmf_error(rc); } SetPageUptodate(page); } unlock_page(page); vmf->page = page; return 0;