diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index a331522487..352b68fdb7 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -44,12 +45,22 @@ namespace Memory { MemoryManager::MemoryManager(Core::System& system) - : m_page_size(m_arena.GetPageSize()), m_system(system) + : m_page_size(static_cast(m_arena.GetPageSize())), + m_guest_pages_per_host_page(m_page_size / PowerPC::HW_PAGE_SIZE), + m_host_page_type(GetHostPageTypeForPageSize(m_page_size)), m_system(system) { } MemoryManager::~MemoryManager() = default; +MemoryManager::HostPageType MemoryManager::GetHostPageTypeForPageSize(u32 page_size) +{ + if (!std::has_single_bit(page_size)) + return HostPageType::Unsupported; + + return page_size > PowerPC::HW_PAGE_SIZE ? HostPageType::LargePages : HostPageType::SmallPages; +} + void MemoryManager::InitMMIO(bool is_wii) { m_mmio_mapping = std::make_unique(); @@ -312,10 +323,69 @@ void MemoryManager::UpdateDBATMappings(const PowerPC::BatTable& dbat_table) void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_address, bool writeable) { - if (!m_is_fastmem_arena_initialized || m_page_size > PowerPC::HW_PAGE_SIZE) + if (!m_is_fastmem_arena_initialized) return; - constexpr u32 logical_size = PowerPC::HW_PAGE_SIZE; + switch (m_host_page_type) + { + case HostPageType::SmallPages: + return AddHostPageTableMapping(logical_address, translated_address, writeable, + PowerPC::HW_PAGE_SIZE); + case HostPageType::LargePages: + return TryAddLargePageTableMapping(logical_address, translated_address, writeable); + default: + return; + } +} + +void MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address, + bool writeable) +{ + const bool add_readable = + TryAddLargePageTableMapping(logical_address, translated_address, m_large_readable_pages); + + const bool add_writeable = + writeable && + TryAddLargePageTableMapping(logical_address, translated_address, m_large_writeable_pages); + + if (add_readable || add_writeable) + { + AddHostPageTableMapping(logical_address & ~(m_page_size - 1), + translated_address & ~(m_page_size - 1), add_writeable, m_page_size); + } +} + +bool MemoryManager::TryAddLargePageTableMapping(u32 logical_address, u32 translated_address, + std::map>& map) +{ + std::vector& entries = map[logical_address & ~(m_page_size - 1)]; + + if (entries.empty()) + entries = std::vector(m_guest_pages_per_host_page, INVALID_MAPPING); + + entries[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = translated_address; + + return CanCreateHostMappingForGuestPages(entries); +} + +bool MemoryManager::CanCreateHostMappingForGuestPages(const std::vector& entries) const +{ + const u32 translated_address = entries[0]; + if ((translated_address & (m_page_size - 1)) != 0) + return false; + + for (size_t i = 1; i < m_guest_pages_per_host_page; ++i) + { + if (entries[i] != translated_address + i * PowerPC::HW_PAGE_SIZE) + return false; + } + + return true; +} + +void MemoryManager::AddHostPageTableMapping(u32 logical_address, u32 translated_address, + bool writeable, u32 logical_size) +{ for (const auto& physical_region : m_physical_regions) { if (!physical_region.active) @@ -367,9 +437,45 @@ void MemoryManager::AddPageTableMapping(u32 logical_address, u32 translated_addr void MemoryManager::RemovePageTableMappings(const std::set& mappings) { - if (m_page_size > PowerPC::HW_PAGE_SIZE) + switch (m_host_page_type) + { + case HostPageType::SmallPages: + return RemoveHostPageTableMappings(mappings); + case HostPageType::LargePages: + for (u32 logical_address : mappings) + RemoveLargePageTableMapping(logical_address); return; + default: + return; + } +} +void MemoryManager::RemoveLargePageTableMapping(u32 logical_address) +{ + RemoveLargePageTableMapping(logical_address, m_large_readable_pages); + RemoveLargePageTableMapping(logical_address, m_large_writeable_pages); + + const u32 aligned_logical_address = logical_address & ~(m_page_size - 1); + const auto it = m_page_table_mapped_entries.find(aligned_logical_address); + if (it != m_page_table_mapped_entries.end()) + { + const LogicalMemoryView& entry = it->second; + m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size); + + m_page_table_mapped_entries.erase(it); + } +} + +void MemoryManager::RemoveLargePageTableMapping(u32 logical_address, + std::map>& map) +{ + const auto it = map.find(logical_address & ~(m_page_size - 1)); + if (it != map.end()) + it->second[(logical_address & (m_page_size - 1)) / PowerPC::HW_PAGE_SIZE] = INVALID_MAPPING; +} + +void MemoryManager::RemoveHostPageTableMappings(const std::set& mappings) +{ if (mappings.empty()) return; @@ -389,6 +495,8 @@ void MemoryManager::RemoveAllPageTableMappings() m_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size); } m_page_table_mapped_entries.clear(); + m_large_readable_pages.clear(); + m_large_writeable_pages.clear(); } void MemoryManager::DoState(PointerWrap& p) @@ -486,6 +594,9 @@ void MemoryManager::ShutdownFastmemArena() m_arena.ReleaseMemoryRegion(); + m_large_readable_pages.clear(); + m_large_writeable_pages.clear(); + m_fastmem_arena = nullptr; m_fastmem_arena_size = 0; m_physical_base = nullptr; diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index e78e3d1c62..afe74bf747 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" #include "Common/MathUtil.h" @@ -161,6 +162,16 @@ public: } private: + enum class HostPageType + { + // 4K or smaller + SmallPages, + // 8K or larger + LargePages, + // Required APIs aren't available, or the page size isn't a power of 2 + Unsupported, + }; + // Base is a pointer to the base of the memory map. Yes, some MMU tricks // are used to set up a full GC or Wii memory map in process memory. // In 64-bit, this might point to "high memory" (above the 32-bit limit), @@ -212,7 +223,9 @@ private: // The MemArena class Common::MemArena m_arena; - const size_t m_page_size; + const u32 m_page_size; + const u32 m_guest_pages_per_host_page; + const HostPageType m_host_page_type; // Dolphin allocates memory to represent four regions: // - 32MB RAM (actually 24MB on hardware), available on GameCube and Wii @@ -261,8 +274,26 @@ private: std::array m_physical_page_mappings{}; std::array m_logical_page_mappings{}; + // If the host page size is larger than the guest page size, these two maps are used + // to keep track of which guest pages can be combined and mapped as one host page. + static constexpr u32 INVALID_MAPPING = 0xFFFFFFFF; + std::map> m_large_readable_pages; + std::map> m_large_writeable_pages; + Core::System& m_system; + static HostPageType GetHostPageTypeForPageSize(u32 page_size); + void InitMMIO(bool is_wii); + + void TryAddLargePageTableMapping(u32 logical_address, u32 translated_address, bool writeable); + bool TryAddLargePageTableMapping(u32 logical_address, u32 translated_address, + std::map>& map); + bool CanCreateHostMappingForGuestPages(const std::vector& entries) const; + void AddHostPageTableMapping(u32 logical_address, u32 translated_address, bool writeable, + u32 logical_size); + void RemoveLargePageTableMapping(u32 logical_address); + void RemoveLargePageTableMapping(u32 logical_address, std::map>& map); + void RemoveHostPageTableMappings(const std::set& mappings); }; } // namespace Memory