From 6b42dfc890e1849e0c8502c830e5746df73b62dd Mon Sep 17 00:00:00 2001 From: Johannes Wikner Date: Tue, 29 Aug 2023 18:19:05 +0200 Subject: [PATCH] pagetables: allow user and kernel maps in same pgd KTF allows user mode to allocate high addresses, which normally are only available to kernel. However, to handle this somewhat gracefully, we need to check if already-existent page table protection bits conflicts with those of new maps. We downgrade the current flags if the most recent mapping's flags are more permissive than current flags. This is all a bit sketchy but useful, so print a big warning if this ever happens. Signed-off-by: Johannes Wikner --- arch/x86/pagetables.c | 17 +++++++++++++++++ tests/unittests.c | 13 ++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/arch/x86/pagetables.c b/arch/x86/pagetables.c index 289a907f..177f314a 100644 --- a/arch/x86/pagetables.c +++ b/arch/x86/pagetables.c @@ -147,6 +147,23 @@ static mfn_t get_pgentry_mfn(mfn_t tab_mfn, pt_index_t index, unsigned long flag tab = init_map_mfn(mfn); memset(tab, 0, PAGE_SIZE); } + else { + /* Valid page table already exists, but its current protection bits may conflict + * with our. Our new flags will take precedence over previous one. */ + unsigned long entry_new = *entry; + entry_new |= flags & _PAGE_USER; + entry_new |= flags & _PAGE_RW; + entry_new &= ~(flags & _PAGE_NX); + if (*entry != entry_new) { + char flags_str_old[16]; + char flags_str_new[16]; + printk("WARNING: Already-present page table protection conflicts with our.\n" + " Updating present page table protection: %s -> %s\n", + dump_pte_flags(flags_str_old, 16, (pte_t) *entry), + dump_pte_flags(flags_str_new, 16, (pte_t) entry_new)); + *entry = entry_new; + } + } return mfn; } diff --git a/tests/unittests.c b/tests/unittests.c index 954511f1..ea5634e8 100644 --- a/tests/unittests.c +++ b/tests/unittests.c @@ -33,6 +33,7 @@ #include #include +#include #include static char opt_string[4]; @@ -92,6 +93,12 @@ static unsigned long __user_text test_user_task_func2(void *arg) { return 0; } +#define HIGH_USER_PTR _ptr(0xffffffff80222000) +static unsigned long __user_text test_user_task_func3(void *arg) { + printf(USTR("access: %lx\n"), _ul(HIGH_USER_PTR)); + return *(unsigned long *) HIGH_USER_PTR; +} + int unit_tests(void *_unused) { printk("\nLet the UNITTESTs begin\n"); printk("Commandline parsing: %s\n", kernel_cmdline); @@ -160,18 +167,22 @@ int unit_tests(void *_unused) { cpu_freq_expect("Prototyp Amazing Foo One @ 1GHz", 1000000000); cpu_freq_expect("Prototyp Amazing Foo Two @ 1.00GHz", 1000000000); - task_t *task1, *task2, *task_user1, *task_user2; + task_t *task1, *task2, *task_user1, *task_user2, *task_user3; task1 = new_kernel_task("test1", test_kernel_task_func, _ptr(98)); task2 = new_kernel_task("test2", test_kernel_task_func, _ptr(-99)); task_user1 = new_user_task("test1 user", test_user_task_func1, NULL); task_user2 = new_user_task("test2 user", test_user_task_func2, NULL); + task_user3 = new_user_task("test3 user", test_user_task_func3, NULL); + + vmap_user_4k(HIGH_USER_PTR, get_free_frame()->mfn, L1_PROT_USER); set_task_repeat(task1, 10); schedule_task(task1, get_bsp_cpu()); schedule_task(task2, get_cpu(1)); schedule_task(task_user1, get_bsp_cpu()); schedule_task(task_user2, get_cpu(1)); + schedule_task(task_user3, get_bsp_cpu()); #ifdef UNITTEST_LONGMODE printk("Long mode to real mode transition:\n");