summaryrefslogtreecommitdiffstats
path: root/arch/arm64/kernel/elfcore.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/kernel/elfcore.c')
-rw-r--r--arch/arm64/kernel/elfcore.c138
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 000000000..2e94d20c4
--- /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;
+}