// SPDX-License-Identifier: GPL-2.0 /* * fs/proc/kcore.c kernel ELF core dumper * * Modelled on fs/exec.c:aout_core_dump() * Jeremy Fitzhardinge <jeremy@sw.oz.au> * ELF version written by David Howells <David.Howells@nexor.co.uk> * Modified and incorporated into 2.3.x by Tigran Aivazian <tigran@veritas.com> * Support to dump vmalloc'd areas (ELF only), Tigran Aivazian <tigran@veritas.com> * Safe accesses to vmalloc/direct-mapped discontiguous areas, Kanoj Sarcar <kanoj@sgi.com>
*/
/* * Returns > 0 for RAM pages, 0 for non-RAM pages, < 0 on error * Same as oldmem_pfn_is_ram in vmcore
*/ staticint (*mem_pfn_is_ram)(unsignedlong pfn);
int __init register_mem_pfn_is_ram(int (*fn)(unsignedlong pfn))
{ if (mem_pfn_is_ram) return -EBUSY;
mem_pfn_is_ram = fn; return 0;
}
/* This doesn't grab kclist_lock, so it should only be used at init time. */ void __init kclist_add(struct kcore_list *new, void *addr, size_t size, int type)
{
new->addr = (unsignedlong)addr;
new->size = size;
new->type = type;
#ifdef CONFIG_HIGHMEM /* * If no highmem, we can assume [0...max_low_pfn) continuous range of memory * because memory hole is not as big as !HIGHMEM case. * (HIGHMEM is special because part of memory is _invisible_ from the kernel.)
*/ staticint kcore_ram_list(struct list_head *head)
{ struct kcore_list *ent;
/* * We've already checked virt_addr_valid so we know this address * is a valid pointer, therefore we can check against it to determine * if we need to trim
*/ if (VMALLOC_START > ent->addr) { if (VMALLOC_START - ent->addr < ent->size)
ent->size = VMALLOC_START - ent->addr;
}
if (!get_sparsemem_vmemmap_info(ent, head)) {
list_del(&ent->list); goto free_out;
}
return 0;
free_out:
kfree(ent); return 1;
}
staticint kcore_ram_list(struct list_head *list)
{ int nid, ret; unsignedlong end_pfn;
/* Not initialized....update now */ /* find out "max pfn" */
end_pfn = 0;
for_each_node_state(nid, N_MEMORY) { unsignedlong node_end;
node_end = node_end_pfn(nid); if (end_pfn < node_end)
end_pfn = node_end;
} /* scan 0 to max_pfn */
ret = walk_system_ram_range(0, end_pfn, list, kclist_add_private); if (ret) return -ENOMEM; return 0;
} #endif/* CONFIG_HIGHMEM */
staticint kcore_update_ram(void)
{
LIST_HEAD(list);
LIST_HEAD(garbage); struct kcore_list *tmp, *pos; int ret = 0;
percpu_down_write(&kclist_lock); if (!xchg(&kcore_need_update, 0)) goto out;
ret = kcore_ram_list(&list); if (ret) { /* Couldn't get the RAM list, try again next time. */
WRITE_ONCE(kcore_need_update, 1);
list_splice_tail(&list, &garbage); goto out;
}
notes = kzalloc(kcore_notes_len, GFP_KERNEL); if (!notes) {
ret = -ENOMEM; goto out;
}
append_kcore_note(notes, &i, NN_PRSTATUS, NT_PRSTATUS, &prstatus, sizeof(prstatus));
append_kcore_note(notes, &i, NN_PRPSINFO, NT_PRPSINFO, &prpsinfo, sizeof(prpsinfo));
append_kcore_note(notes, &i, NN_TASKSTRUCT, NT_TASKSTRUCT, current,
arch_task_struct_size); /* * vmcoreinfo_size is mostly constant after init time, but it * can be changed by crash_save_vmcoreinfo(). Racing here with a * panic on another CPU before the machine goes down is insanely * unlikely, but it's better to not leave potential buffer * overflows lying around, regardless.
*/
append_kcore_note(notes, &i, VMCOREINFO_NOTE_NAME, 0,
vmcoreinfo_data,
min(vmcoreinfo_size, kcore_notes_len - i));
/* * Check to see if our file offset matches with any of * the addresses in the elf_phdr on our list.
*/
start = kc_offset_to_vaddr(*fpos - kcore_data_offset); if ((tsz = (PAGE_SIZE - (start & ~PAGE_MASK))) > buflen)
tsz = buflen;
m = NULL; while (buflen) { struct page *page; unsignedlong pfn;
phys_addr_t phys; void *__start;
/* * If this is the first iteration or the address is not within * the previous entry, search for a matching entry.
*/ if (!m || start < m->addr || start >= m->addr + m->size) { struct kcore_list *pos;
m = NULL;
list_for_each_entry(pos, &kclist_head, list) { if (start >= pos->addr &&
start < pos->addr + pos->size) {
m = pos; break;
}
}
}
if (!m) { if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT; goto out;
} goto skip;
}
switch (m->type) { case KCORE_VMALLOC:
{ constchar *src = (char *)start;
size_t read = 0, left = tsz;
/* * vmalloc uses spinlocks, so we optimistically try to * read memory. If this fails, fault pages in and try * again until we are done.
*/ while (true) {
read += vread_iter(iter, src, left); if (read == tsz) break;
src += read;
left -= read;
if (fault_in_iov_iter_writeable(iter, left)) {
ret = -EFAULT; goto out;
}
} break;
} case KCORE_USER: /* User page is handled prior to normal kernel page: */ if (copy_to_iter((char *)start, tsz, iter) != tsz) {
ret = -EFAULT; goto out;
} break; case KCORE_RAM:
phys = __pa(start);
pfn = phys >> PAGE_SHIFT;
page = pfn_to_online_page(pfn);
/* * Don't read offline sections, logically offline pages * (e.g., inflated in a balloon), hwpoisoned pages, * and explicitly excluded physical ranges.
*/ if (!page || PageOffline(page) ||
is_page_hwpoison(page) || !pfn_is_ram(pfn) ||
pfn_is_unaccepted_memory(pfn)) { if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT; goto out;
} break;
}
fallthrough; case KCORE_VMEMMAP: case KCORE_TEXT: if (m->type == KCORE_RAM) {
__start = kc_xlate_dev_mem_ptr(phys); if (!__start) {
ret = -ENOMEM; if (iov_iter_zero(tsz, iter) != tsz)
ret = -EFAULT; goto out;
}
} else {
__start = (void *)start;
}
/* * Sadly we must use a bounce buffer here to be able to * make use of copy_from_kernel_nofault(), as these * memory regions might not always be mapped on all * architectures.
*/
ret = copy_from_kernel_nofault(buf, __start, tsz); if (m->type == KCORE_RAM)
kc_unxlate_dev_mem_ptr(phys, __start); if (ret) { if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT; goto out;
}
ret = 0; /* * We know the bounce buffer is safe to copy from, so * use _copy_to_iter() directly.
*/
} elseif (_copy_to_iter(buf, tsz, iter) != tsz) {
ret = -EFAULT; goto out;
} break; default:
pr_warn_once("Unhandled KCORE type: %d\n", m->type); if (iov_iter_zero(tsz, iter) != tsz) {
ret = -EFAULT; goto out;
}
}
skip:
buflen -= tsz;
*fpos += tsz;
start += tsz;
tsz = (buflen > PAGE_SIZE ? PAGE_SIZE : buflen);
}
/* just remember that we have to update kcore */ staticint __meminit kcore_callback(struct notifier_block *self, unsignedlong action, void *arg)
{ switch (action) { case MEM_ONLINE: case MEM_OFFLINE:
kcore_need_update = 1; break;
} return NOTIFY_OK;
}
staticstruct kcore_list kcore_vmalloc;
#ifdef CONFIG_ARCH_PROC_KCORE_TEXT staticstruct kcore_list kcore_text; /* * If defined, special segment is used for mapping kernel text instead of * direct-map area. We need to create special TEXT section.
*/ staticvoid __init proc_kcore_text_init(void)
{
kclist_add(&kcore_text, _text, _end - _text, KCORE_TEXT);
} #else staticvoid __init proc_kcore_text_init(void)
{
} #endif
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.