From 6d2734a074f44a24129db850339677d8d7b436aa Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 21:17:57 -0500 Subject: Kernel/Memory: Give each Process its own page table. The loader is in charge of setting the newly created process's page table as the main one during the loading process. --- src/core/hle/kernel/vm_manager.cpp | 13 +++++++++---- src/core/hle/kernel/vm_manager.h | 6 +++++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index cef1f7fa8..7a007c065 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -56,6 +56,10 @@ void VMManager::Reset() { initial_vma.size = MAX_ADDRESS; vma_map.emplace(initial_vma.base, initial_vma); + page_table.pointers.fill(nullptr); + page_table.attributes.fill(Memory::PageType::Unmapped); + page_table.cached_res_count.fill(0); + UpdatePageTableForVMA(initial_vma); } @@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: - Memory::UnmapRegion(vma.base, vma.size); + Memory::UnmapRegion(page_table, vma.base, vma.size); break; case VMAType::AllocatedMemoryBlock: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, + vma.backing_block->data() + vma.offset); break; case VMAType::BackingMemory: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); + Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); break; } } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 38e0d74d0..1302527bb 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -9,6 +9,7 @@ #include #include "common/common_types.h" #include "core/hle/result.h" +#include "core/memory.h" #include "core/mmio.h" namespace Kernel { @@ -102,7 +103,6 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ class VMManager final { - // TODO(yuriks): Make page tables switchable to support multiple VMManagers public: /** * The maximum amount of address space managed by the kernel. Addresses above this are never @@ -184,6 +184,10 @@ public: /// Dumps the address space layout to the log, for debugging void LogLayout(Log::Level log_level) const; + /// Each VMManager has its own page table, which is set as the main one when the owning process + /// is scheduled. + Memory::PageTable page_table; + private: using VMAIter = decltype(vma_map)::iterator; -- cgit v1.2.3 From c34ec5e77cd9e83fcf5c929f3951557d5269b7a6 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 21:28:03 -0500 Subject: Kernel/Memory: Switch the current page table when a new process is scheduled. --- src/core/hle/kernel/thread.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f5f2eb2f7..b7f094f46 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -178,8 +178,18 @@ static void SwitchContext(Thread* new_thread) { Core::CPU().LoadContext(new_thread->context); Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); + + if (!previous_thread || previous_thread->owner_process != current_thread->owner_process) { + Kernel::g_current_process = current_thread->owner_process; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; + // We have switched processes and thus, page tables, clear the instruction cache so we + // don't keep stale data from the previous process. + Core::CPU().ClearInstructionCache(); + } } else { current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. } } -- cgit v1.2.3 From 214150f00c77474927cbdfb1598dbdb2cb4fcf32 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 22:22:59 -0500 Subject: Kernel/Memory: Changed GetPhysicalPointer so that it doesn't go through the current process' page table to obtain a pointer. --- src/core/hle/kernel/memory.cpp | 30 +++++------------------------- src/core/hle/kernel/memory.h | 2 ++ 2 files changed, 7 insertions(+), 25 deletions(-) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 496d07cb5..7f27e9655 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -8,7 +8,6 @@ #include #include #include -#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -24,7 +23,7 @@ namespace Kernel { -static MemoryRegionInfo memory_regions[3]; +MemoryRegionInfo memory_regions[3]; /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system /// memory configuration type. @@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { } } -std::array vram; -std::array n3ds_extra_ram; - void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { using namespace Memory; @@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin return; } - // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual - // mappings. - u8* target_pointer = nullptr; - switch (area->paddr_base) { - case VRAM_PADDR: - target_pointer = vram.data(); - break; - case DSP_RAM_PADDR: - target_pointer = AudioCore::GetDspMemory().data(); - break; - case N3DS_EXTRA_RAM_PADDR: - target_pointer = n3ds_extra_ram.data(); - break; - default: - UNREACHABLE(); - } + u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region); // TODO(yuriks): This flag seems to have some other effect, but it's unknown what MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; - auto vma = address_space - .MapBackingMemory(mapping.address, target_pointer + offset_into_region, - mapping.size, memory_state) - .Unwrap(); + auto vma = + address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state) + .Unwrap(); address_space.Reprotect(vma, mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); } diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index 08c1a9989..da6bb3563 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h @@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); void MapSharedPages(VMManager& address_space); + +extern MemoryRegionInfo memory_regions[3]; } // namespace Kernel -- cgit v1.2.3 From b178089251200bd0309afcbcb06b43e7c82dc3bc Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 22 Jul 2017 19:37:26 -0500 Subject: Kernel/Threads: Don't clear the CPU instruction cache when performing a context switch from an idle thread into a thread in the same process. We were unnecessarily clearing the cache when going from Process A -> Idle -> Process A, this caused extreme performance regressions. --- src/core/hle/kernel/thread.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/core/hle/kernel') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b7f094f46..f77c39d18 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -171,6 +171,8 @@ static void SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); + auto previous_process = Kernel::g_current_process; + current_thread = new_thread; ready_queue.remove(new_thread->current_priority, new_thread); @@ -179,7 +181,7 @@ static void SwitchContext(Thread* new_thread) { Core::CPU().LoadContext(new_thread->context); Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); - if (!previous_thread || previous_thread->owner_process != current_thread->owner_process) { + if (previous_process != current_thread->owner_process) { Kernel::g_current_process = current_thread->owner_process; Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // We have switched processes and thus, page tables, clear the instruction cache so we -- cgit v1.2.3