162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <linux/coredump.h> 462306a36Sopenharmony_ci#include <linux/elfcore.h> 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/mm.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/cpufeature.h> 962306a36Sopenharmony_ci#include <asm/mte.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define for_each_mte_vma(cprm, i, m) \ 1262306a36Sopenharmony_ci if (system_supports_mte()) \ 1362306a36Sopenharmony_ci for (i = 0, m = cprm->vma_meta; \ 1462306a36Sopenharmony_ci i < cprm->vma_count; \ 1562306a36Sopenharmony_ci i++, m = cprm->vma_meta + i) \ 1662306a36Sopenharmony_ci if (m->flags & VM_MTE) 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistatic unsigned long mte_vma_tag_dump_size(struct core_vma_metadata *m) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci return (m->dump_size >> PAGE_SHIFT) * MTE_PAGE_TAG_STORAGE; 2162306a36Sopenharmony_ci} 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* Derived from dump_user_range(); start/end must be page-aligned */ 2462306a36Sopenharmony_cistatic int mte_dump_tag_range(struct coredump_params *cprm, 2562306a36Sopenharmony_ci unsigned long start, unsigned long len) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci int ret = 1; 2862306a36Sopenharmony_ci unsigned long addr; 2962306a36Sopenharmony_ci void *tags = NULL; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci for (addr = start; addr < start + len; addr += PAGE_SIZE) { 3262306a36Sopenharmony_ci struct page *page = get_dump_page(addr); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci /* 3562306a36Sopenharmony_ci * get_dump_page() returns NULL when encountering an empty 3662306a36Sopenharmony_ci * page table entry that would otherwise have been filled with 3762306a36Sopenharmony_ci * the zero page. Skip the equivalent tag dump which would 3862306a36Sopenharmony_ci * have been all zeros. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci if (!page) { 4162306a36Sopenharmony_ci dump_skip(cprm, MTE_PAGE_TAG_STORAGE); 4262306a36Sopenharmony_ci continue; 4362306a36Sopenharmony_ci } 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci /* 4662306a36Sopenharmony_ci * Pages mapped in user space as !pte_access_permitted() (e.g. 4762306a36Sopenharmony_ci * PROT_EXEC only) may not have the PG_mte_tagged flag set. 4862306a36Sopenharmony_ci */ 4962306a36Sopenharmony_ci if (!page_mte_tagged(page)) { 5062306a36Sopenharmony_ci put_page(page); 5162306a36Sopenharmony_ci dump_skip(cprm, MTE_PAGE_TAG_STORAGE); 5262306a36Sopenharmony_ci continue; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (!tags) { 5662306a36Sopenharmony_ci tags = mte_allocate_tag_storage(); 5762306a36Sopenharmony_ci if (!tags) { 5862306a36Sopenharmony_ci put_page(page); 5962306a36Sopenharmony_ci ret = 0; 6062306a36Sopenharmony_ci break; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci mte_save_page_tags(page_address(page), tags); 6562306a36Sopenharmony_ci put_page(page); 6662306a36Sopenharmony_ci if (!dump_emit(cprm, tags, MTE_PAGE_TAG_STORAGE)) { 6762306a36Sopenharmony_ci ret = 0; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (tags) 7362306a36Sopenharmony_ci mte_free_tag_storage(tags); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return ret; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ciElf_Half elf_core_extra_phdrs(struct coredump_params *cprm) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci int i; 8162306a36Sopenharmony_ci struct core_vma_metadata *m; 8262306a36Sopenharmony_ci int vma_count = 0; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci for_each_mte_vma(cprm, i, m) 8562306a36Sopenharmony_ci vma_count++; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci return vma_count; 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ciint elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci int i; 9362306a36Sopenharmony_ci struct core_vma_metadata *m; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci for_each_mte_vma(cprm, i, m) { 9662306a36Sopenharmony_ci struct elf_phdr phdr; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci phdr.p_type = PT_AARCH64_MEMTAG_MTE; 9962306a36Sopenharmony_ci phdr.p_offset = offset; 10062306a36Sopenharmony_ci phdr.p_vaddr = m->start; 10162306a36Sopenharmony_ci phdr.p_paddr = 0; 10262306a36Sopenharmony_ci phdr.p_filesz = mte_vma_tag_dump_size(m); 10362306a36Sopenharmony_ci phdr.p_memsz = m->end - m->start; 10462306a36Sopenharmony_ci offset += phdr.p_filesz; 10562306a36Sopenharmony_ci phdr.p_flags = 0; 10662306a36Sopenharmony_ci phdr.p_align = 0; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (!dump_emit(cprm, &phdr, sizeof(phdr))) 10962306a36Sopenharmony_ci return 0; 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return 1; 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cisize_t elf_core_extra_data_size(struct coredump_params *cprm) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci int i; 11862306a36Sopenharmony_ci struct core_vma_metadata *m; 11962306a36Sopenharmony_ci size_t data_size = 0; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci for_each_mte_vma(cprm, i, m) 12262306a36Sopenharmony_ci data_size += mte_vma_tag_dump_size(m); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci return data_size; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint elf_core_write_extra_data(struct coredump_params *cprm) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci int i; 13062306a36Sopenharmony_ci struct core_vma_metadata *m; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci for_each_mte_vma(cprm, i, m) { 13362306a36Sopenharmony_ci if (!mte_dump_tag_range(cprm, m->start, m->dump_size)) 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return 1; 13862306a36Sopenharmony_ci} 139