Linux 6.9-rc1
[linux-2.6-microblaze.git] / arch / arm64 / kernel / elfcore.c
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
18 static 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 */
24 static 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, 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, 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, tags, 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
78 Elf_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
90 int 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, &phdr, sizeof(phdr)))
109                         return 0;
110         }
111
112         return 1;
113 }
114
115 size_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
127 int 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, m->start, m->dump_size))
134                         return 0;
135         }
136
137         return 1;
138 }