From 31ea030c7fd6d17d6bf87b9c58a582620c6b0888 Mon Sep 17 00:00:00 2001 From: apio Date: Thu, 5 Jan 2023 21:50:26 +0100 Subject: [PATCH] MMU: Add functions to create and delete userspace page directories --- kernel/src/arch/MMU.h | 1 + kernel/src/arch/x86_64/MMU.cpp | 118 +++++++++++++++++++++++++++++++-- 2 files changed, 113 insertions(+), 6 deletions(-) diff --git a/kernel/src/arch/MMU.h b/kernel/src/arch/MMU.h index c0620071..031c038e 100644 --- a/kernel/src/arch/MMU.h +++ b/kernel/src/arch/MMU.h @@ -31,6 +31,7 @@ namespace MMU void flush_all(); Result create_page_directory_for_userspace(); + Result delete_userspace_page_directory(PageDirectory* directory); void setup_initial_page_directory(); PageDirectory* kernel_page_directory(); diff --git a/kernel/src/arch/x86_64/MMU.cpp b/kernel/src/arch/x86_64/MMU.cpp index a51dced4..f6081f2a 100644 --- a/kernel/src/arch/x86_64/MMU.cpp +++ b/kernel/src/arch/x86_64/MMU.cpp @@ -2,6 +2,7 @@ #include "memory/MemoryManager.h" #include #include +#include #include #pragma GCC push_options @@ -50,11 +51,16 @@ namespace MMU return l4_table()->entries[l4_index(addr)]; } + constexpr PageDirectory* raw_l3_table(u64 l4) + { + const u64 l3 = sign | (rindex << 39) | (rindex << 30) | (rindex << 21) | (l4 << 12); + return (PageDirectory*)l3; + } + constexpr PageDirectory* l3_table(u64 addr) { const u64 l4 = l4_index(addr); - const u64 l3 = sign | (rindex << 39) | (rindex << 30) | (rindex << 21) | (l4 << 12); - return (PageDirectory*)l3; + return raw_l3_table(l4); } constexpr u64 l3_index(u64 addr) @@ -67,12 +73,17 @@ namespace MMU return l3_table(addr)->entries[l3_index(addr)]; } + constexpr PageDirectory* raw_l2_table(u64 l4, u64 l3) + { + const u64 l2 = sign | (rindex << 39) | (rindex << 30) | (l4 << 21) | (l3 << 12); + return (PageDirectory*)l2; + } + constexpr PageDirectory* l2_table(u64 addr) { const u64 l4 = l4_index(addr); const u64 l3 = l3_index(addr); - const u64 l2 = sign | (rindex << 39) | (rindex << 30) | (l4 << 21) | (l3 << 12); - return (PageDirectory*)l2; + return raw_l2_table(l4, l3); } constexpr u64 l2_index(u64 addr) @@ -85,13 +96,18 @@ namespace MMU return l2_table(addr)->entries[l2_index(addr)]; } + constexpr PageDirectory* raw_l1_table(u64 l4, u64 l3, u64 l2) + { + const u64 l1 = sign | (rindex << 39) | (l4 << 30) | (l3 << 21) | (l2 << 12); + return (PageDirectory*)l1; + } + constexpr PageDirectory* l1_table(u64 addr) { const u64 l4 = l4_index(addr); const u64 l3 = l3_index(addr); const u64 l2 = l2_index(addr); - const u64 l1 = sign | (rindex << 39) | (l4 << 30) | (l3 << 21) | (l2 << 12); - return (PageDirectory*)l1; + return raw_l1_table(l4, l3, l2); } constexpr u64 l1_index(u64 addr) @@ -270,6 +286,96 @@ namespace MMU flush_all(); } + Result create_page_directory_for_userspace() + { + u64 directory_virt = TRY(MemoryManager::alloc_for_kernel(1, MMU::ReadWrite | MMU::NoExecute)); + u64 directory_phys = MMU::get_physical(directory_virt).value(); + + PageDirectory* directory = (PageDirectory*)directory_virt; + memset(directory, 0, ARCH_PAGE_SIZE); + PageTableEntry& recursive_entry = directory->entries[rindex]; + recursive_entry.read_write = true; + recursive_entry.present = true; + recursive_entry.set_address(directory_phys); + + directory->entries[511] = g_kernel_directory->entries[511]; + + // From now on, we're only going to use the physical address, since accessing the PageDirectory will be dealt + // with using recursive mapping. So let's make sure we don't leak any VM. + MemoryManager::unmap_weak_and_free_vm(directory_virt, 1); + + return (PageDirectory*)directory_phys; + } + + Result delete_userspace_page_directory(PageDirectory* directory) + { + check(directory); + + // Needed in order to access page tables using the recursive mapping system. + switch_page_directory(directory); + + auto guard = make_scope_guard([&] { + check(g_kernel_directory); + switch_page_directory(g_kernel_directory); + MemoryManager::free_frame((u64)directory); + }); + + PageDirectory* table = l4_table(); + + // Let's iterate over every top-level entry, skipping the last two entries (recursive mapping and kernel pages) + for (u64 i = 0; i < 510; i++) + { + PageTableEntry& l4 = table->entries[i]; + if (!l4.present) continue; + + PageDirectory* pdp = raw_l3_table(i); + + for (u64 j = 0; j < 512; j++) + { + PageTableEntry& l3 = pdp->entries[j]; + if (!l3.present) continue; + if (l3.larger_pages) + { + // FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory. + TRY(MemoryManager::free_frame(l3.get_address())); + } + + PageDirectory* pd = raw_l2_table(i, j); + + for (u64 k = 0; k < 512; k++) + { + PageTableEntry& l2 = pd->entries[k]; + if (!l2.present) continue; + if (l2.larger_pages) + { + // FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory. + TRY(MemoryManager::free_frame(l2.get_address())); + } + + PageDirectory* pt = raw_l1_table(i, j, k); + + for (u64 l = 0; l < 512; l++) + { + PageTableEntry& l1 = pt->entries[l]; + if (!l1.present) continue; + + // FIXME: Maybe we shouldn't delete some pages in an address space, such as shared memory. + TRY(MemoryManager::free_frame(l1.get_address())); + } + + TRY(MemoryManager::free_frame(l2.get_address())); + } + + TRY(MemoryManager::free_frame(l3.get_address())); + } + + TRY(MemoryManager::free_frame(l4.get_address())); + } + + // No need to clean up manually, the ScopeGuard we set up earlier will do that for us. + return {}; + } + PageDirectory* kernel_page_directory() { return g_kernel_directory;