diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 175276354..1e9f0248b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -418,7 +418,6 @@ add_library(core STATIC loader/smdh.h memory.cpp memory.h - memory_setup.h mmio.h movie.cpp movie.h diff --git a/src/core/core.cpp b/src/core/core.cpp index c5c56d11a..95fa69f43 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -25,7 +25,6 @@ #include "core/hle/service/sm/sm.h" #include "core/hw/hw.h" #include "core/loader/loader.h" -#include "core/memory_setup.h" #include "core/movie.h" #ifdef ENABLE_SCRIPTING #include "core/rpc/rpc_server.h" diff --git a/src/core/core.h b/src/core/core.h index 81d0dfffd..365b8063c 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -276,12 +276,10 @@ private: public: // HACK: this is temporary exposed for tests, // due to WIP kernel refactor causing desync state in memory + std::unique_ptr memory; std::unique_ptr kernel; std::unique_ptr timing; - /// Memory system - std::unique_ptr memory; - private: static System s_instance; diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index c2d4d3053..16d497b7a 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -19,7 +19,6 @@ #include "core/hle/kernel/vm_manager.h" #include "core/hle/result.h" #include "core/memory.h" -#include "core/memory_setup.h" //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index a3b8b0d9c..7dd775550 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -402,8 +402,13 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe } Kernel::Process::Process(KernelSystem& kernel) - : Object(kernel), handle_table(kernel), kernel(kernel) {} -Kernel::Process::~Process() {} + : Object(kernel), handle_table(kernel), kernel(kernel), vm_manager(kernel.memory) { + + kernel.memory.RegisterPageTable(&vm_manager.page_table); +} +Kernel::Process::~Process() { + kernel.memory.UnregisterPageTable(&vm_manager.page_table); +} SharedPtr KernelSystem::GetProcessById(u32 process_id) const { auto itr = std::find_if( diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index bf6d36cd7..c7cc36a3c 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -8,7 +8,6 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" -#include "core/memory_setup.h" #include "core/mmio.h" namespace Kernel { @@ -37,7 +36,7 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { return true; } -VMManager::VMManager() { +VMManager::VMManager(Memory::MemorySystem& memory) : memory(memory) { Reset(); } @@ -351,13 +350,13 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: - Memory::UnmapRegion(page_table, vma.base, vma.size); + memory.UnmapRegion(page_table, vma.base, vma.size); break; case VMAType::BackingMemory: - Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); + memory.MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - Memory::MapIoRegion(page_table, 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 db48d7aed..fbd9bf09b 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -113,7 +113,7 @@ public: std::map vma_map; using VMAHandle = decltype(vma_map)::const_iterator; - VMManager(); + explicit VMManager(Memory::MemorySystem& memory); ~VMManager(); /// Clears the address space map, re-initializing with a single free area. @@ -227,5 +227,7 @@ private: /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); + + Memory::MemorySystem& memory; }; } // namespace Kernel diff --git a/src/core/hle/service/apt/applet_manager.cpp b/src/core/hle/service/apt/applet_manager.cpp index 076f5f6cf..8d7d671da 100644 --- a/src/core/hle/service/apt/applet_manager.cpp +++ b/src/core/hle/service/apt/applet_manager.cpp @@ -327,10 +327,13 @@ ResultCode AppletManager::PrepareToStartLibraryApplet(AppletId applet_id) { // There are some problems with LLE applets. The rasterizer cache gets out of sync // when the applet is closed. To avoid breaking applications because of the issue, // we are going to disable loading LLE applets before further fixes are done. - // auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id, - // region_value)); if (process) { - // return RESULT_SUCCESS; - // } + auto cfg = Service::CFG::GetModule(system); + u32 region_value = cfg->GetRegionValue(); + auto process = + NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id, region_value)); + if (process) { + return RESULT_SUCCESS; + } // If we weren't able to load the native applet title, try to fallback to an HLE implementation. auto applet = HLE::Applets::Applet::Get(applet_id); @@ -354,10 +357,13 @@ ResultCode AppletManager::PreloadLibraryApplet(AppletId applet_id) { // There are some problems with LLE applets. The rasterizer cache gets out of sync // when the applet is closed. To avoid breaking applications because of the issue, // we are going to disable loading LLE applets before further fixes are done. - // auto process = NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id, - // region_value)); if (process) { - // return RESULT_SUCCESS; - // } + auto cfg = Service::CFG::GetModule(system); + u32 region_value = cfg->GetRegionValue(); + auto process = + NS::LaunchTitle(FS::MediaType::NAND, GetTitleIdForApplet(applet_id, region_value)); + if (process) { + return RESULT_SUCCESS; + } // If we weren't able to load the native applet title, try to fallback to an HLE implementation. auto applet = HLE::Applets::Applet::Get(applet_id); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 396a00e39..7693cdd9b 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -15,12 +15,45 @@ #include "core/hle/kernel/process.h" #include "core/hle/lock.h" #include "core/memory.h" -#include "core/memory_setup.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Memory { +class RasterizerCacheMarker { +public: + void Mark(VAddr addr, bool cached) { + bool* p = At(addr); + if (p) + *p = cached; + } + + bool IsCached(VAddr addr) { + bool* p = At(addr); + if (p) + return *p; + return false; + } + +private: + bool* At(VAddr addr) { + if (addr >= VRAM_VADDR && addr < VRAM_VADDR_END) { + return &vram[(addr - VRAM_VADDR) / PAGE_SIZE]; + } + if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { + return &linear_heap[(addr - LINEAR_HEAP_VADDR) / PAGE_SIZE]; + } + if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { + return &new_linear_heap[(addr - NEW_LINEAR_HEAP_VADDR) / PAGE_SIZE]; + } + return nullptr; + } + + std::array vram{}; + std::array linear_heap{}; + std::array new_linear_heap{}; +}; + class MemorySystem::Impl { public: Impl() { @@ -36,6 +69,8 @@ public: std::unique_ptr n3ds_extra_ram = std::make_unique(Memory::N3DS_EXTRA_RAM_SIZE); PageTable* current_page_table = nullptr; + RasterizerCacheMarker cache_marker; + std::vector page_table_list; }; MemorySystem::MemorySystem() : impl(std::make_unique()) {} @@ -52,7 +87,7 @@ PageTable* MemorySystem::GetCurrentPageTable() const { return impl->current_page_table; } -static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { +void MemorySystem::MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping {} onto {:08X}-{:08X}", (void*)memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -66,19 +101,26 @@ static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, Page page_table.attributes[base] = type; page_table.pointers[base] = memory; + // If the memory to map is already rasterizer-cached, mark the page + if (type == PageType::Memory && impl->cache_marker.IsCached(base * PAGE_SIZE)) { + page_table.attributes[base] = PageType::RasterizerCachedMemory; + page_table.pointers[base] = nullptr; + } + base += 1; if (memory != nullptr) memory += PAGE_SIZE; } } -void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { +void MemorySystem::MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); } -void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) { +void MemorySystem::MapIoRegion(PageTable& page_table, VAddr base, u32 size, + MMIORegionPointer mmio_handler) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); @@ -86,7 +128,7 @@ void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); } -void UnmapRegion(PageTable& page_table, VAddr base, u32 size) { +void MemorySystem::UnmapRegion(PageTable& page_table, VAddr base, u32 size) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: {:08X}", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: {:08X}", base); MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); @@ -105,6 +147,15 @@ u8* MemorySystem::GetPointerForRasterizerCache(VAddr addr) { UNREACHABLE(); } +void MemorySystem::RegisterPageTable(PageTable* page_table) { + impl->page_table_list.push_back(page_table); +} + +void MemorySystem::UnregisterPageTable(PageTable* page_table) { + impl->page_table_list.erase( + std::find(impl->page_table_list.begin(), impl->page_table_list.end(), page_table)); +} + /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ @@ -324,37 +375,40 @@ void MemorySystem::RasterizerMarkRegionCached(PAddr start, u32 size, bool cached for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { for (VAddr vaddr : PhysicalToVirtualAddressForRasterizer(paddr)) { - PageType& page_type = impl->current_page_table->attributes[vaddr >> PAGE_BITS]; + impl->cache_marker.Mark(vaddr, cached); + for (PageTable* page_table : impl->page_table_list) { + PageType& page_type = page_table->attributes[vaddr >> PAGE_BITS]; - if (cached) { - // Switch page type to cached if now cached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::Memory: - page_type = PageType::RasterizerCachedMemory; - impl->current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; - break; - default: - UNREACHABLE(); - } - } else { - // Switch page type to uncached if now uncached - switch (page_type) { - case PageType::Unmapped: - // It is not necessary for a process to have this region mapped into its address - // space, for example, a system module need not have a VRAM mapping. - break; - case PageType::RasterizerCachedMemory: { - page_type = PageType::Memory; - impl->current_page_table->pointers[vaddr >> PAGE_BITS] = - GetPointerForRasterizerCache(vaddr & ~PAGE_MASK); - break; - } - default: - UNREACHABLE(); + if (cached) { + // Switch page type to cached if now cached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its + // address space, for example, a system module need not have a VRAM mapping. + break; + case PageType::Memory: + page_type = PageType::RasterizerCachedMemory; + page_table->pointers[vaddr >> PAGE_BITS] = nullptr; + break; + default: + UNREACHABLE(); + } + } else { + // Switch page type to uncached if now uncached + switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its + // address space, for example, a system module need not have a VRAM mapping. + break; + case PageType::RasterizerCachedMemory: { + page_type = PageType::Memory; + page_table->pointers[vaddr >> PAGE_BITS] = + GetPointerForRasterizerCache(vaddr & ~PAGE_MASK); + break; + } + default: + UNREACHABLE(); + } } } } diff --git a/src/core/memory.h b/src/core/memory.h index b6f0fb619..f154affc2 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -214,6 +214,27 @@ public: MemorySystem(); ~MemorySystem(); + /** + * Maps an allocated buffer onto a region of the emulated process address space. + * + * @param page_table The page table of the emulated process. + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. + */ + void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); + + /** + * Maps a region of the emulated process address space as a IO region. + * @param page_table The page table of the emulated process. + * @param base The address to start mapping at. Must be page-aligned. + * @param size The amount of bytes to map. Must be page-aligned. + * @param mmio_handler The handler that backs the mapping. + */ + void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); + + void UnmapRegion(PageTable& page_table, VAddr base, u32 size); + /// Currently active page table void SetCurrentPageTable(PageTable* page_table); PageTable* GetCurrentPageTable() const; @@ -260,6 +281,12 @@ public: */ void RasterizerMarkRegionCached(PAddr start, u32 size, bool cached); + /// Registers page table for rasterizer cache marking + void RegisterPageTable(PageTable* page_table); + + /// Unregisters page table for rasterizer cache marking + void UnregisterPageTable(PageTable* page_table); + private: template T Read(const VAddr vaddr); @@ -275,6 +302,8 @@ private: */ u8* GetPointerForRasterizerCache(VAddr addr); + void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type); + class Impl; std::unique_ptr impl; diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h deleted file mode 100644 index 92d2abfdb..000000000 --- a/src/core/memory_setup.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" -#include "core/mmio.h" - -namespace Memory { - -/** - * Maps an allocated buffer onto a region of the emulated process address space. - * - * @param page_table The page table of the emulated process. - * @param base The address to start mapping at. Must be page-aligned. - * @param size The amount of bytes to map. Must be page-aligned. - * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. - */ -void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); - -/** - * Maps a region of the emulated process address space as a IO region. - * @param page_table The page table of the emulated process. - * @param base The address to start mapping at. Must be page-aligned. - * @param size The amount of bytes to map. Must be page-aligned. - * @param mmio_handler The handler that backs the mapping. - */ -void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); - -void UnmapRegion(PageTable& page_table, VAddr base, u32 size); -} // namespace Memory diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index c316f8f36..acadf4d53 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -6,7 +6,6 @@ #include "core/core_timing.h" #include "core/hle/kernel/process.h" #include "core/memory.h" -#include "core/memory_setup.h" #include "tests/core/arm/arm_test_common.h" namespace ArmTests { @@ -31,15 +30,17 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) page_table->pointers.fill(nullptr); page_table->attributes.fill(Memory::PageType::Unmapped); - Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); - Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); + memory.MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); + memory.MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); memory.SetCurrentPageTable(page_table); } TestEnvironment::~TestEnvironment() { - Memory::UnmapRegion(*page_table, 0x80000000, 0x80000000); - Memory::UnmapRegion(*page_table, 0x00000000, 0x80000000); + Memory::MemorySystem& memory = *Core::System::GetInstance().memory; + memory.UnmapRegion(*page_table, 0x80000000, 0x80000000); + memory.UnmapRegion(*page_table, 0x00000000, 0x80000000); + Core::System::GetInstance().kernel.reset(); } void TestEnvironment::SetMemory64(VAddr vaddr, u64 value) { diff --git a/src/tests/core/memory/vm_manager.cpp b/src/tests/core/memory/vm_manager.cpp index 775a254a7..bd510864a 100644 --- a/src/tests/core/memory/vm_manager.cpp +++ b/src/tests/core/memory/vm_manager.cpp @@ -11,9 +11,10 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { auto block = std::make_shared>(Memory::PAGE_SIZE); + Memory::MemorySystem memory; SECTION("mapping memory") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. - auto manager = std::make_unique(); + auto manager = std::make_unique(memory); auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); @@ -28,7 +29,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("unmapping memory") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. - auto manager = std::make_unique(); + auto manager = std::make_unique(memory); auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); @@ -44,7 +45,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("changing memory permissions") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. - auto manager = std::make_unique(); + auto manager = std::make_unique(memory); auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS); @@ -63,7 +64,7 @@ TEST_CASE("Memory Basics", "[kernel][memory]") { SECTION("changing memory state") { // Because of the PageTable, Kernel::VMManager is too big to be created on the stack. - auto manager = std::make_unique(); + auto manager = std::make_unique(memory); auto result = manager->MapBackingMemory(Memory::HEAP_VADDR, block->data(), block->size(), Kernel::MemoryState::Private); REQUIRE(result.Code() == RESULT_SUCCESS);