diff options
Diffstat (limited to 'arch/arm64/kernel/elfcore.c')
-rw-r--r-- | arch/arm64/kernel/elfcore.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/arm64/kernel/elfcore.c b/arch/arm64/kernel/elfcore.c new file mode 100644 index 0000000000..2e94d20c4a --- /dev/null +++ b/arch/arm64/kernel/elfcore.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <linux/coredump.h> +#include <linux/elfcore.h> +#include <linux/kernel.h> +#include <linux/mm.h> + +#include <asm/cpufeature.h> +#include <asm/mte.h> + +#define for_each_mte_vma(cprm, i, m) \ + if (system_supports_mte()) \ + for (i = 0, m = cprm->vma_meta; \ + i < cprm->vma_count; \ + i++, m = cprm->vma_meta + i) \ + if (m->flags & VM_MTE) + +static unsigned long mte_vma_tag_dump_size(struct core_vma_metadata *m) +{ + return (m->dump_size >> PAGE_SHIFT) * MTE_PAGE_TAG_STORAGE; +} + +/* Derived from dump_user_range(); start/end must be page-aligned */ +static int mte_dump_tag_range(struct coredump_params *cprm, + unsigned long start, unsigned long len) +{ + int ret = 1; + unsigned long addr; + void *tags = NULL; + + for (addr = start; addr < start + len; addr += PAGE_SIZE) { + struct page *page = get_dump_page(addr); + + /* + * get_dump_page() returns NULL when encountering an empty + * page table entry that would otherwise have been filled with + * the zero page. Skip the equivalent tag dump which would + * have been all zeros. + */ + if (!page) { + dump_skip(cprm, MTE_PAGE_TAG_STORAGE); + continue; + } + + /* + * Pages mapped in user space as !pte_access_permitted() (e.g. + * PROT_EXEC only) may not have the PG_mte_tagged flag set. + */ + if (!page_mte_tagged(page)) { + put_page(page); + dump_skip(cprm, MTE_PAGE_TAG_STORAGE); + continue; + } + + if (!tags) { + tags = mte_allocate_tag_storage(); + if (!tags) { + put_page(page); + ret = 0; + break; + } + } + + mte_save_page_tags(page_address(page), tags); + put_page(page); + if (!dump_emit(cprm, tags, MTE_PAGE_TAG_STORAGE)) { + ret = 0; + break; + } + } + + if (tags) + mte_free_tag_storage(tags); + + return ret; +} + +Elf_Half elf_core_extra_phdrs(struct coredump_params *cprm) +{ + int i; + struct core_vma_metadata *m; + int vma_count = 0; + + for_each_mte_vma(cprm, i, m) + vma_count++; + + return vma_count; +} + +int elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset) +{ + int i; + struct core_vma_metadata *m; + + for_each_mte_vma(cprm, i, m) { + struct elf_phdr phdr; + + phdr.p_type = PT_AARCH64_MEMTAG_MTE; + phdr.p_offset = offset; + phdr.p_vaddr = m->start; + phdr.p_paddr = 0; + phdr.p_filesz = mte_vma_tag_dump_size(m); + phdr.p_memsz = m->end - m->start; + offset += phdr.p_filesz; + phdr.p_flags = 0; + phdr.p_align = 0; + + if (!dump_emit(cprm, &phdr, sizeof(phdr))) + return 0; + } + + return 1; +} + +size_t elf_core_extra_data_size(struct coredump_params *cprm) +{ + int i; + struct core_vma_metadata *m; + size_t data_size = 0; + + for_each_mte_vma(cprm, i, m) + data_size += mte_vma_tag_dump_size(m); + + return data_size; +} + +int elf_core_write_extra_data(struct coredump_params *cprm) +{ + int i; + struct core_vma_metadata *m; + + for_each_mte_vma(cprm, i, m) { + if (!mte_dump_tag_range(cprm, m->start, m->dump_size)) + return 0; + } + + return 1; +} |