// 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 * */ #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/uaccess.h>#include <linux/mem_encrypt.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/* Device Dump list and mutex to synchronize access to list */static LIST_HEAD(vmcoredd_list);static DEFINE_MUTEX(vmcoredd_mutex); 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; /* * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error * The called function has to take care of module refcounting. */static int (*oldmem_pfn_is_ram)(unsigned long pfn); int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn)){ if (oldmem_pfn_is_ram) return -EBUSY; oldmem_pfn_is_ram = fn; return 0;}EXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram); void unregister_oldmem_pfn_is_ram(void){ oldmem_pfn_is_ram = NULL; wmb();}EXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram); static int pfn_is_ram(unsigned long pfn){ int (*fn)(unsigned long pfn); /* pfn is ram unless fn() checks pagetype */ int ret = 1; /* * Ask hypervisor if the pfn is really ram. * A ballooned page contains no data and reading from such a page * will cause high load in the hypervisor. */ fn = oldmem_pfn_is_ram; if (fn) ret = fn(pfn); return ret;} /* Reads a page from the oldmem device from given offset. */ssize_t read_from_oldmem(char *buf, size_t count, u64 *ppos, int userbuf, bool encrypted){ unsigned long pfn, offset; size_t nr_bytes; ssize_t read = 0, tmp; if (!count) return 0; offset = (unsigned long)(*ppos % PAGE_SIZE); pfn = (unsigned long)(*ppos / PAGE_SIZE); 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) == 0) memset(buf, 0, nr_bytes); else { if (encrypted) tmp = copy_oldmem_page_encrypted(pfn, buf, nr_bytes, offset, userbuf); else tmp = copy_oldmem_page(pfn, buf, nr_bytes, offset, userbuf); if (tmp < 0) return tmp; } *ppos += nr_bytes; count -= nr_bytes; buf += nr_bytes; read += nr_bytes; ++pfn; offset = 0; } while (count); 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){ return read_from_oldmem(buf, count, ppos, 0, false);} /* * Architectures may override this function to read from notes sections */ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos){ return read_from_oldmem(buf, count, ppos, 0, mem_encrypt_active());} /* * 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 __weakcopy_oldmem_page_encrypted(unsigned long pfn, char *buf, size_t csize, unsigned long offset, int userbuf){ return copy_oldmem_page(pfn, buf, csize, offset, userbuf);} /* * Copy to either kernel or user space */static int copy_to(void *target, void *src, size_t size, int userbuf){ if (userbuf) { if (copy_to_user((char __user *) target, src, size)) return -EFAULT; } else { memcpy(target, src, size); } return 0;} #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMPstatic int vmcoredd_copy_dumps(void *dst, u64 start, size_t size, int userbuf){ struct vmcoredd_node *dump; u64 offset = 0; int ret = 0; size_t tsz; char *buf; mutex_lock(&vmcoredd_mutex); 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(dst, buf, tsz, userbuf)) { ret = -EFAULT; goto out_unlock; } size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) goto out_unlock; } offset += dump->size; } out_unlock: mutex_unlock(&vmcoredd_mutex); return ret;} #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; int ret = 0; size_t tsz; char *buf; mutex_lock(&vmcoredd_mutex); 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)) { ret = -EFAULT; goto out_unlock; } size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) goto out_unlock; } offset += dump->size; } out_unlock: mutex_unlock(&vmcoredd_mutex); return ret;}#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(char *buffer, size_t buflen, loff_t *fpos, int userbuf){ ssize_t acc = 0, tmp; size_t tsz; u64 start; struct vmcore *m = NULL; if (buflen == 0 || *fpos >= vmcore_size) return 0; /* trim buflen to not go beyond EOF */ if (buflen > vmcore_size - *fpos) buflen = vmcore_size - *fpos; /* Read ELF core header */ if (*fpos < elfcorebuf_sz) { tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen); if (copy_to(buffer, elfcorebuf + *fpos, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) 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, buflen); start = *fpos - elfcorebuf_sz; if (vmcoredd_copy_dumps(buffer, start, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (!buflen) return acc; }#endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read remaining elf notes */ tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen); kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz; if (copy_to(buffer, kaddr, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) return acc; } 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, buflen); start = m->paddr + *fpos - m->offset; tmp = read_from_oldmem(buffer, tsz, &start, userbuf, mem_encrypt_active()); if (tmp < 0) return tmp; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) return acc; } } return acc;} static ssize_t read_vmcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos){ return __read_vmcore((__force char *) buffer, buflen, fpos, 1);} /* * The vmcore fault handler uses the page cache and fills data using the * standard __vmcore_read() 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 page *page; loff_t offset; char *buf; 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; buf = __va((page_to_pfn(page) << PAGE_SHIFT)); rc = __read_vmcore(buf, PAGE_SIZE, &offset, 0); if (rc < 0) { unlock_page(page); put_page(page); return vmf_error(rc); } SetPageUptodate(page); } unlock_page(page); vmf->page = page; return 0;#else return VM_FAULT_SIGBUS;#endif} static const struct vm_operations_struct vmcore_mmap_ops = { .fault = mmap_vmcore_fault,}; /** * vmcore_alloc_buf - allocate buffer in vmalloc memory * @sizez: 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/* * remap_oldmem_pfn_checked - do remap_oldmem_pfn_range replacing all pages * reported as not being ram with the zero page. * * @vma: vm_area_struct describing requested mapping * @from: start remapping from * @pfn: page frame number to start remapping to * @size: remapping size * @prot: protection bits * * Returns zero on success, -EAGAIN on failure. */static int remap_oldmem_pfn_checked(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot){ unsigned long map_size; unsigned long pos_start, pos_end, pos; unsigned long zeropage_pfn = my_zero_pfn(0); size_t len = 0; pos_start = pfn; pos_end = pfn + (size >> PAGE_SHIFT); for (pos = pos_start; pos < pos_end; ++pos) { if (!pfn_is_ram(pos)) { /*Lines 1–500 of 1585. 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 * */ #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/uaccess.h> #include <linux/mem_encrypt.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 /* Device Dump list and mutex to synchronize access to list */ static LIST_HEAD(vmcoredd_list); static DEFINE_MUTEX(vmcoredd_mutex); 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; /* * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error * The called function has to take care of module refcounting. */ static int (*oldmem_pfn_is_ram)(unsigned long pfn); int register_oldmem_pfn_is_ram(int (*fn)(unsigned long pfn)) { if (oldmem_pfn_is_ram) return -EBUSY; oldmem_pfn_is_ram = fn; return 0; } EXPORT_SYMBOL_GPL(register_oldmem_pfn_is_ram); void unregister_oldmem_pfn_is_ram(void) { oldmem_pfn_is_ram = NULL; wmb(); } EXPORT_SYMBOL_GPL(unregister_oldmem_pfn_is_ram); static int pfn_is_ram(unsigned long pfn) { int (*fn)(unsigned long pfn); /* pfn is ram unless fn() checks pagetype */ int ret = 1; /* * Ask hypervisor if the pfn is really ram. * A ballooned page contains no data and reading from such a page * will cause high load in the hypervisor. */ fn = oldmem_pfn_is_ram; if (fn) ret = fn(pfn); return ret; } /* Reads a page from the oldmem device from given offset. */ ssize_t read_from_oldmem(char *buf, size_t count, u64 *ppos, int userbuf, bool encrypted) { unsigned long pfn, offset; size_t nr_bytes; ssize_t read = 0, tmp; if (!count) return 0; offset = (unsigned long)(*ppos % PAGE_SIZE); pfn = (unsigned long)(*ppos / PAGE_SIZE); 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) == 0) memset(buf, 0, nr_bytes); else { if (encrypted) tmp = copy_oldmem_page_encrypted(pfn, buf, nr_bytes, offset, userbuf); else tmp = copy_oldmem_page(pfn, buf, nr_bytes, offset, userbuf); if (tmp < 0) return tmp; } *ppos += nr_bytes; count -= nr_bytes; buf += nr_bytes; read += nr_bytes; ++pfn; offset = 0; } while (count); 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) { return read_from_oldmem(buf, count, ppos, 0, false); } /* * Architectures may override this function to read from notes sections */ ssize_t __weak elfcorehdr_read_notes(char *buf, size_t count, u64 *ppos) { return read_from_oldmem(buf, count, ppos, 0, mem_encrypt_active()); } /* * 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(unsigned long pfn, char *buf, size_t csize, unsigned long offset, int userbuf) { return copy_oldmem_page(pfn, buf, csize, offset, userbuf); } /* * Copy to either kernel or user space */ static int copy_to(void *target, void *src, size_t size, int userbuf) { if (userbuf) { if (copy_to_user((char __user *) target, src, size)) return -EFAULT; } else { memcpy(target, src, size); } return 0; } #ifdef CONFIG_PROC_VMCORE_DEVICE_DUMP static int vmcoredd_copy_dumps(void *dst, u64 start, size_t size, int userbuf) { struct vmcoredd_node *dump; u64 offset = 0; int ret = 0; size_t tsz; char *buf; mutex_lock(&vmcoredd_mutex); 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(dst, buf, tsz, userbuf)) { ret = -EFAULT; goto out_unlock; } size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) goto out_unlock; } offset += dump->size; } out_unlock: mutex_unlock(&vmcoredd_mutex); return ret; } #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; int ret = 0; size_t tsz; char *buf; mutex_lock(&vmcoredd_mutex); 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)) { ret = -EFAULT; goto out_unlock; } size -= tsz; start += tsz; dst += tsz; /* Leave now if buffer filled already */ if (!size) goto out_unlock; } offset += dump->size; } out_unlock: mutex_unlock(&vmcoredd_mutex); return ret; } #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(char *buffer, size_t buflen, loff_t *fpos, int userbuf) { ssize_t acc = 0, tmp; size_t tsz; u64 start; struct vmcore *m = NULL; if (buflen == 0 || *fpos >= vmcore_size) return 0; /* trim buflen to not go beyond EOF */ if (buflen > vmcore_size - *fpos) buflen = vmcore_size - *fpos; /* Read ELF core header */ if (*fpos < elfcorebuf_sz) { tsz = min(elfcorebuf_sz - (size_t)*fpos, buflen); if (copy_to(buffer, elfcorebuf + *fpos, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) 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, buflen); start = *fpos - elfcorebuf_sz; if (vmcoredd_copy_dumps(buffer, start, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (!buflen) return acc; } #endif /* CONFIG_PROC_VMCORE_DEVICE_DUMP */ /* Read remaining elf notes */ tsz = min(elfcorebuf_sz + elfnotes_sz - (size_t)*fpos, buflen); kaddr = elfnotes_buf + *fpos - elfcorebuf_sz - vmcoredd_orig_sz; if (copy_to(buffer, kaddr, tsz, userbuf)) return -EFAULT; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) return acc; } 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, buflen); start = m->paddr + *fpos - m->offset; tmp = read_from_oldmem(buffer, tsz, &start, userbuf, mem_encrypt_active()); if (tmp < 0) return tmp; buflen -= tsz; *fpos += tsz; buffer += tsz; acc += tsz; /* leave now if filled buffer already */ if (buflen == 0) return acc; } } return acc; } static ssize_t read_vmcore(struct file *file, char __user *buffer, size_t buflen, loff_t *fpos) { return __read_vmcore((__force char *) buffer, buflen, fpos, 1); } /* * The vmcore fault handler uses the page cache and fills data using the * standard __vmcore_read() 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 page *page; loff_t offset; char *buf; 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; buf = __va((page_to_pfn(page) << PAGE_SHIFT)); rc = __read_vmcore(buf, PAGE_SIZE, &offset, 0); if (rc < 0) { unlock_page(page); put_page(page); return vmf_error(rc); } SetPageUptodate(page); } unlock_page(page); vmf->page = page; return 0; #else return VM_FAULT_SIGBUS; #endif } static const struct vm_operations_struct vmcore_mmap_ops = { .fault = mmap_vmcore_fault, }; /** * vmcore_alloc_buf - allocate buffer in vmalloc memory * @sizez: 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 /* * remap_oldmem_pfn_checked - do remap_oldmem_pfn_range replacing all pages * reported as not being ram with the zero page. * * @vma: vm_area_struct describing requested mapping * @from: start remapping from * @pfn: page frame number to start remapping to * @size: remapping size * @prot: protection bits * * Returns zero on success, -EAGAIN on failure. */ static int remap_oldmem_pfn_checked(struct vm_area_struct *vma, unsigned long from, unsigned long pfn, unsigned long size, pgprot_t prot) { unsigned long map_size; unsigned long pos_start, pos_end, pos; unsigned long zeropage_pfn = my_zero_pfn(0); size_t len = 0; pos_start = pfn; pos_end = pfn + (size >> PAGE_SHIFT); for (pos = pos_start; pos < pos_end; ++pos) { if (!pfn_is_ram(pos)) { /*