From 2ad963d261abf8452a5dea959c0366aaee6fa6c5 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 6 Aug 2019 11:19:16 +0200 Subject: [PATCH] Kernel: Add mapping from page directory base (PDB) to PageDirectory This allows the page fault code to find the owning PageDirectory and corresponding process for faulting regions. The mapping is implemented as a global hash map right now, which is definitely not optimal. We can come up with something better when it becomes necessary. --- Kernel/Process.cpp | 4 ++-- Kernel/VM/MemoryManager.cpp | 27 ++++++++++++++++++--------- Kernel/VM/MemoryManager.h | 2 ++ Kernel/VM/PageDirectory.cpp | 26 ++++++++++++++++++++++++-- Kernel/VM/PageDirectory.h | 17 ++++++++++++++--- 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 6e7f0f73bad..df9f5649f1a 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -333,7 +333,7 @@ int Process::do_exec(String path, Vector arguments, Vector envir u32 entry_eip = 0; // FIXME: Is there a race here? auto old_page_directory = move(m_page_directory); - m_page_directory = PageDirectory::create_for_userspace(); + m_page_directory = PageDirectory::create_for_userspace(*this); #ifdef MM_DEBUG dbgprintf("Process %u exec: PD=%x created\n", pid(), m_page_directory.ptr()); #endif @@ -590,7 +590,7 @@ Process::Process(String&& name, uid_t uid, gid_t gid, pid_t ppid, RingLevel ring { dbgprintf("Process: New process PID=%u with name=%s\n", m_pid, m_name.characters()); - m_page_directory = PageDirectory::create_for_userspace(fork_parent ? &fork_parent->page_directory().range_allocator() : nullptr); + m_page_directory = PageDirectory::create_for_userspace(*this, fork_parent ? &fork_parent->page_directory().range_allocator() : nullptr); #ifdef MM_DEBUG dbgprintf("Process %u ctor: PD=%x created\n", pid(), m_page_directory.ptr()); #endif diff --git a/Kernel/VM/MemoryManager.cpp b/Kernel/VM/MemoryManager.cpp index 51e75a2f94f..fd0ff993ac4 100644 --- a/Kernel/VM/MemoryManager.cpp +++ b/Kernel/VM/MemoryManager.cpp @@ -257,7 +257,8 @@ void MemoryManager::initialize() Region* MemoryManager::kernel_region_from_vaddr(VirtualAddress vaddr) { - ASSERT(vaddr.get() >= 0xc0000000); + if (vaddr.get() < 0xc0000000) + return nullptr; for (auto& region : MM.m_kernel_regions) { if (region->contains(vaddr)) return region; @@ -279,18 +280,15 @@ Region* MemoryManager::user_region_from_vaddr(Process& process, VirtualAddress v Region* MemoryManager::region_from_vaddr(Process& process, VirtualAddress vaddr) { ASSERT_INTERRUPTS_DISABLED(); - - if (vaddr.get() >= 0xc0000000) - return kernel_region_from_vaddr(vaddr); - + if (auto* region = kernel_region_from_vaddr(vaddr)) + return region; return user_region_from_vaddr(process, vaddr); } const Region* MemoryManager::region_from_vaddr(const Process& process, VirtualAddress vaddr) { - if (vaddr.get() >= 0xc0000000) - return kernel_region_from_vaddr(vaddr); - + if (auto* region = kernel_region_from_vaddr(vaddr)) + return region; return user_region_from_vaddr(const_cast(process), vaddr); } @@ -398,6 +396,17 @@ bool MemoryManager::page_in_from_inode(Region& region, unsigned page_index_in_re return true; } +Region* MemoryManager::region_from_vaddr(VirtualAddress vaddr) +{ + if (auto* region = kernel_region_from_vaddr(vaddr)) + return region; + auto page_directory = PageDirectory::find_by_pdb(cpu_cr3()); + if (!page_directory) + return nullptr; + ASSERT(page_directory->process()); + return user_region_from_vaddr(*page_directory->process(), vaddr); +} + PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault) { ASSERT_INTERRUPTS_DISABLED(); @@ -417,7 +426,7 @@ PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault) return PageFaultResponse::Continue; } } - auto* region = region_from_vaddr(current->process(), fault.vaddr()); + auto* region = region_from_vaddr(fault.vaddr()); if (!region) { kprintf("NP(error) fault at invalid address L%x\n", fault.vaddr().get()); return PageFaultResponse::ShouldCrash; diff --git a/Kernel/VM/MemoryManager.h b/Kernel/VM/MemoryManager.h index 6c4884a3199..255c1a39ee4 100644 --- a/Kernel/VM/MemoryManager.h +++ b/Kernel/VM/MemoryManager.h @@ -108,6 +108,8 @@ private: static Region* user_region_from_vaddr(Process&, VirtualAddress); static Region* kernel_region_from_vaddr(VirtualAddress); + static Region* region_from_vaddr(VirtualAddress); + bool copy_on_write(Region&, unsigned page_index_in_region); bool page_in_from_inode(Region&, unsigned page_index_in_region); bool zero_page(Region& region, unsigned page_index_in_region); diff --git a/Kernel/VM/PageDirectory.cpp b/Kernel/VM/PageDirectory.cpp index f6d401a38f5..8283883fadb 100644 --- a/Kernel/VM/PageDirectory.cpp +++ b/Kernel/VM/PageDirectory.cpp @@ -6,16 +6,36 @@ static const u32 userspace_range_base = 0x01000000; static const u32 kernelspace_range_base = 0xc0000000; +static HashMap& pdb_map() +{ + ASSERT_INTERRUPTS_DISABLED(); + static HashMap* map; + if (!map) + map = new HashMap; + return *map; +} + +RefPtr PageDirectory::find_by_pdb(u32 pdb) +{ + InterruptDisabler disabler; + return pdb_map().get(pdb).value_or({}); +} + PageDirectory::PageDirectory(PhysicalAddress paddr) : m_range_allocator(VirtualAddress(0xc0000000), 0x3f000000) { m_directory_page = PhysicalPage::create(paddr, true, false); + InterruptDisabler disabler; + pdb_map().set(m_directory_page->paddr().get(), this); } -PageDirectory::PageDirectory(const RangeAllocator* parent_range_allocator) - : m_range_allocator(parent_range_allocator ? RangeAllocator(*parent_range_allocator) : RangeAllocator(VirtualAddress(userspace_range_base), kernelspace_range_base - userspace_range_base)) +PageDirectory::PageDirectory(Process& process, const RangeAllocator* parent_range_allocator) + : m_process(&process) + , m_range_allocator(parent_range_allocator ? RangeAllocator(*parent_range_allocator) : RangeAllocator(VirtualAddress(userspace_range_base), kernelspace_range_base - userspace_range_base)) { MM.populate_page_directory(*this); + InterruptDisabler disabler; + pdb_map().set(m_directory_page->paddr().get(), this); } PageDirectory::~PageDirectory() @@ -23,6 +43,8 @@ PageDirectory::~PageDirectory() #ifdef MM_DEBUG dbgprintf("MM: ~PageDirectory K%x\n", this); #endif + InterruptDisabler disabler; + pdb_map().remove(m_directory_page->paddr().get()); } void PageDirectory::flush(VirtualAddress vaddr) diff --git a/Kernel/VM/PageDirectory.h b/Kernel/VM/PageDirectory.h index 7eecd07d162..b6458c1055e 100644 --- a/Kernel/VM/PageDirectory.h +++ b/Kernel/VM/PageDirectory.h @@ -1,17 +1,24 @@ #pragma once #include -#include #include +#include #include #include +class Process; + class PageDirectory : public RefCounted { friend class MemoryManager; public: - static NonnullRefPtr create_for_userspace(const RangeAllocator* parent_range_allocator = nullptr) { return adopt(*new PageDirectory(parent_range_allocator)); } + static NonnullRefPtr create_for_userspace(Process& process, const RangeAllocator* parent_range_allocator = nullptr) + { + return adopt(*new PageDirectory(process, parent_range_allocator)); + } static NonnullRefPtr create_at_fixed_address(PhysicalAddress paddr) { return adopt(*new PageDirectory(paddr)); } + static RefPtr find_by_pdb(u32); + ~PageDirectory(); u32 cr3() const { return m_directory_page->paddr().get(); } @@ -21,10 +28,14 @@ public: RangeAllocator& range_allocator() { return m_range_allocator; } + Process* process() { return m_process; } + const Process* process() const { return m_process; } + private: - explicit PageDirectory(const RangeAllocator* parent_range_allocator); + PageDirectory(Process&, const RangeAllocator* parent_range_allocator); explicit PageDirectory(PhysicalAddress); + Process* m_process { nullptr }; RangeAllocator m_range_allocator; RefPtr m_directory_page; HashMap> m_physical_pages;