From 1fe4b9b074f965031545f1f6644881e39fd9cee5 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Thu, 4 Jan 2024 05:26:22 +0000 Subject: [PATCH] mm: Add mpagemap Add mpagemap, a system call to fetch PTE data from a range of address space. It's more or less mincore-like, but doesn't take into account the page cache, it just dumps PTEs. This is useful for userspace testing, etc. Signed-off-by: Pedro Falcato --- kernel/arch/arm64/syscall_table.json | 20 ++++++ kernel/arch/riscv64/syscall_table.json | 20 ++++++ kernel/arch/x86_64/syscall_table.json | 20 ++++++ kernel/include/uapi/mincore.h | 24 +++++++ kernel/kernel/mm/Makefile | 2 +- kernel/kernel/mm/mincore.cpp | 94 ++++++++++++++++++++++++++ musl | 2 +- 7 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 kernel/include/uapi/mincore.h create mode 100644 kernel/kernel/mm/mincore.cpp diff --git a/kernel/arch/arm64/syscall_table.json b/kernel/arch/arm64/syscall_table.json index f6ea19e3d..d84b14ba3 100644 --- a/kernel/arch/arm64/syscall_table.json +++ b/kernel/arch/arm64/syscall_table.json @@ -2614,5 +2614,25 @@ ] ], "return_type": "int" + }, + { + "name": "mpagemap", + "nr": 153, + "nr_args": 3, + "args": [ + [ + "void *", + "addr" + ], + [ + "size_t", + "length" + ], + [ + "u64 *", + "pagemap" + ] + ], + "return_type": "int" } ] diff --git a/kernel/arch/riscv64/syscall_table.json b/kernel/arch/riscv64/syscall_table.json index 8694d9111..24ed628e6 100644 --- a/kernel/arch/riscv64/syscall_table.json +++ b/kernel/arch/riscv64/syscall_table.json @@ -2686,5 +2686,25 @@ ] ], "return_type": "int" + }, + { + "name": "mpagemap", + "nr": 153, + "nr_args": 3, + "args": [ + [ + "void *", + "addr" + ], + [ + "size_t", + "length" + ], + [ + "u64 *", + "pagemap" + ] + ], + "return_type": "int" } ] diff --git a/kernel/arch/x86_64/syscall_table.json b/kernel/arch/x86_64/syscall_table.json index bd1e3aa3b..b6d869698 100644 --- a/kernel/arch/x86_64/syscall_table.json +++ b/kernel/arch/x86_64/syscall_table.json @@ -2730,5 +2730,25 @@ ] ], "return_type": "int" + }, + { + "name": "mpagemap", + "nr": 153, + "nr_args": 3, + "args": [ + [ + "void *", + "addr" + ], + [ + "size_t", + "length" + ], + [ + "u64 *", + "pagemap" + ] + ], + "return_type": "int" } ] diff --git a/kernel/include/uapi/mincore.h b/kernel/include/uapi/mincore.h new file mode 100644 index 000000000..698387450 --- /dev/null +++ b/kernel/include/uapi/mincore.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 Pedro Falcato + * This file is part of Onyx, and is released under the terms of the MIT License + * check LICENSE at the root directory for more information + * + * SPDX-License-Identifier: MIT + */ +#ifndef _UAPI_MINCORE_H +#define _UAPI_MINCORE_H + +#define PAGE_PRESENT (1 << 0) +#define PAGE_GLOBAL (1 << 1) +#define PAGE_WRITABLE (1 << 2) +#define PAGE_EXECUTABLE (1 << 3) +#define PAGE_DIRTY (1 << 4) +#define PAGE_ACCESSED (1 << 5) +#define PAGE_USER (1 << 6) +#define PAGE_HUGE (1 << 7) + +/* Reserve the 12 bottom bits for flags, this lines up nicely with 4K pages */ + +#define MAPPING_INFO_PADDR(x) ((x) & -4096UL) + +#endif diff --git a/kernel/kernel/mm/Makefile b/kernel/kernel/mm/Makefile index d1de81865..8881a1b3c 100644 --- a/kernel/kernel/mm/Makefile +++ b/kernel/kernel/mm/Makefile @@ -1,4 +1,4 @@ -mm-y:= bootmem.o page.o pagealloc.o vm_object.o vm.o vmalloc.o reclaim.o amap.o anon.o +mm-y:= bootmem.o page.o pagealloc.o vm_object.o vm.o vmalloc.o reclaim.o amap.o anon.o mincore.o mm-$(CONFIG_KUNIT)+= vm_tests.o ifeq ($(CONFIG_KASAN), y) diff --git a/kernel/kernel/mm/mincore.cpp b/kernel/kernel/mm/mincore.cpp new file mode 100644 index 000000000..6eba1ee16 --- /dev/null +++ b/kernel/kernel/mm/mincore.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 Pedro Falcato + * This file is part of Onyx, and is released under the terms of the MIT License + * check LICENSE at the root directory for more information + * + * SPDX-License-Identifier: MIT + */ + +#include +#include + +// TODO: Export this stuff in some header, and avoid sticking everything into vm.cpp +vm_area_struct *vm_search(struct mm_address_space *mm, void *addr, size_t length) + REQUIRES_SHARED(mm->vm_lock); + +static long do_pagemap(struct mm_address_space *as, unsigned long start, unsigned long end, + u64 *pagemap) +{ + + scoped_mutex g{as->vm_lock}; + long pfns_processed = 0; + struct vm_area_struct *vma = vm_search(as, (void *) start, 1); + if (!vma) + return -ENOMEM; + + while (vma) + { + if (vma->vm_start > end) + break; + + spin_lock(&as->page_table_lock); + + /* Note: This is bad and slow(er). Nonetheless, it does the job for now */ + for (; start < end; start += PAGE_SIZE) + pagemap[pfns_processed++] = __get_mapping_info((void *) start, as); + + spin_unlock(&as->page_table_lock); + + if (start == end) + break; + + vma = containerof_null_safe(bst_next(&as->region_tree, &vma->vm_tree_node), + struct vm_area_struct, vm_tree_node); + } + + return pfns_processed; +} + +int sys_mpagemap(void *addr, size_t length, u64 *pagemap) +{ + unsigned long start = (unsigned long) addr & -PAGE_SIZE; + length += (unsigned long) addr & (PAGE_SIZE - 1); + unsigned long end = ALIGN_TO(start + length, PAGE_SIZE); + struct mm_address_space *as = get_current_address_space(); + struct page *buffer; + int ret = 0; + + if (start < as->start || end > as->end) + return -EINVAL; + + buffer = alloc_page(GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + while (start < end) + { + /* Limit runs to PAGE_SIZE / sizeof(u64) */ + u64 *kbuffer = (u64 *) PAGE_TO_VIRT(buffer); + unsigned long this_run_end = cul::min(end, start + (PAGE_SIZE / sizeof(u64))); + long processed = do_pagemap(as, start, this_run_end, kbuffer); + + if (processed == 0) + break; + + if (processed < 0) + { + ret = processed; + break; + } + + if (copy_to_user(pagemap, kbuffer, processed * sizeof(u64)) < 0) + { + ret = -EFAULT; + break; + } + + pagemap += processed; + start += processed << PAGE_SHIFT; + } + + free_page(buffer); + + return ret; +} diff --git a/musl b/musl index 7bf332833..c8931f26e 160000 --- a/musl +++ b/musl @@ -1 +1 @@ -Subproject commit 7bf332833998706944b4d994abe202d0658a0504 +Subproject commit c8931f26e2f954334bedc746ca3da4314af19399