1// SPDX-License-Identifier: GPL-2.0-only
2
3#include <linux/coredump.h>
4#include <linux/elfcore.h>
5#include <linux/kernel.h>
6#include <linux/mm.h>
7
8#include <asm/cpufeature.h>
9#include <asm/mte.h>
10
11#define for_each_mte_vma(cprm, i, m) \
12 if (system_supports_mte()) \
13 for (i = 0, m = cprm->vma_meta; \
14 i < cprm->vma_count; \
15 i++, m = cprm->vma_meta + i) \
16 if (m->flags & VM_MTE)
17
18static unsigned long mte_vma_tag_dump_size(struct core_vma_metadata *m)
19{
20 return (m->dump_size >> PAGE_SHIFT) * MTE_PAGE_TAG_STORAGE;
21}
22
23/* Derived from dump_user_range(); start/end must be page-aligned */
24static int mte_dump_tag_range(struct coredump_params *cprm,
25 unsigned long start, unsigned long len)
26{
27 int ret = 1;
28 unsigned long addr;
29 void *tags = NULL;
30
31 for (addr = start; addr < start + len; addr += PAGE_SIZE) {
32 struct page *page = get_dump_page(addr);
33
34 /*
35 * get_dump_page() returns NULL when encountering an empty
36 * page table entry that would otherwise have been filled with
37 * the zero page. Skip the equivalent tag dump which would
38 * have been all zeros.
39 */
40 if (!page) {
41 dump_skip(cprm, nr: MTE_PAGE_TAG_STORAGE);
42 continue;
43 }
44
45 /*
46 * Pages mapped in user space as !pte_access_permitted() (e.g.
47 * PROT_EXEC only) may not have the PG_mte_tagged flag set.
48 */
49 if (!page_mte_tagged(page)) {
50 put_page(page);
51 dump_skip(cprm, nr: MTE_PAGE_TAG_STORAGE);
52 continue;
53 }
54
55 if (!tags) {
56 tags = mte_allocate_tag_storage();
57 if (!tags) {
58 put_page(page);
59 ret = 0;
60 break;
61 }
62 }
63
64 mte_save_page_tags(page_address(page), tags);
65 put_page(page);
66 if (!dump_emit(cprm, addr: tags, nr: MTE_PAGE_TAG_STORAGE)) {
67 ret = 0;
68 break;
69 }
70 }
71
72 if (tags)
73 mte_free_tag_storage(tags);
74
75 return ret;
76}
77
78Elf_Half elf_core_extra_phdrs(struct coredump_params *cprm)
79{
80 int i;
81 struct core_vma_metadata *m;
82 int vma_count = 0;
83
84 for_each_mte_vma(cprm, i, m)
85 vma_count++;
86
87 return vma_count;
88}
89
90int elf_core_write_extra_phdrs(struct coredump_params *cprm, loff_t offset)
91{
92 int i;
93 struct core_vma_metadata *m;
94
95 for_each_mte_vma(cprm, i, m) {
96 struct elf_phdr phdr;
97
98 phdr.p_type = PT_AARCH64_MEMTAG_MTE;
99 phdr.p_offset = offset;
100 phdr.p_vaddr = m->start;
101 phdr.p_paddr = 0;
102 phdr.p_filesz = mte_vma_tag_dump_size(m);
103 phdr.p_memsz = m->end - m->start;
104 offset += phdr.p_filesz;
105 phdr.p_flags = 0;
106 phdr.p_align = 0;
107
108 if (!dump_emit(cprm, addr: &phdr, nr: sizeof(phdr)))
109 return 0;
110 }
111
112 return 1;
113}
114
115size_t elf_core_extra_data_size(struct coredump_params *cprm)
116{
117 int i;
118 struct core_vma_metadata *m;
119 size_t data_size = 0;
120
121 for_each_mte_vma(cprm, i, m)
122 data_size += mte_vma_tag_dump_size(m);
123
124 return data_size;
125}
126
127int elf_core_write_extra_data(struct coredump_params *cprm)
128{
129 int i;
130 struct core_vma_metadata *m;
131
132 for_each_mte_vma(cprm, i, m) {
133 if (!mte_dump_tag_range(cprm, start: m->start, len: m->dump_size))
134 return 0;
135 }
136
137 return 1;
138}
139

source code of linux/arch/arm64/kernel/elfcore.c