diff --git a/src/core/hle/kernel/k_memory_manager.cpp b/src/core/hle/kernel/k_memory_manager.cpp index 743e290541..90bc419481 100644 --- a/src/core/hle/kernel/k_memory_manager.cpp +++ b/src/core/hle/kernel/k_memory_manager.cpp @@ -247,11 +247,26 @@ KPhysicalAddress KMemoryManager::AllocateAndOpenContinuous(size_t num_pages, siz } Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, - Direction dir, bool unoptimized, bool random) { - // Choose a heap based on our page size request. - const s32 heap_index = KPageHeap::GetBlockIndex(num_pages); + Direction dir, bool unoptimized, bool random, KProcessAddress expected_vaddr) { + // Choose a heap based on our page size request + s32 heap_index = KPageHeap::GetAlignedBlockIndex(num_pages, Common::GuestHostAlignment); R_UNLESS(0 <= heap_index, ResultOutOfMemory); + // Every block we use has to have the same or greater alignment than the host page size, + // so we have to determine the smallest heap index we can use and use that. + s32 min_index = 0; + for (s32 i = 0; i < static_cast(KPageHeap::NumMemoryBlockPageShifts); ++i) { + if (KPageHeap::GetBlockNumPages(i) >= Common::GuestHostAlignment) { + min_index = i; + break; + } + } + R_UNLESS(0 <= min_index, ResultOutOfMemory); + + if (heap_index < min_index) { + heap_index = min_index; + } + // Ensure that we don't leave anything un-freed. ON_RESULT_FAILURE { for (const auto& it : *out) { @@ -264,32 +279,54 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, }; // Keep allocating until we've allocated all our pages. - for (s32 index = heap_index; index >= 0 && num_pages > 0; index--) { + for (s32 index = heap_index; index >= min_index && num_pages > 0; index--) { const size_t pages_per_alloc = KPageHeap::GetBlockNumPages(index); for (Impl* cur_manager = this->GetFirstManager(pool, dir); cur_manager != nullptr; cur_manager = this->GetNextManager(cur_manager, dir)) { - while (num_pages >= pages_per_alloc) { + while (num_pages > 0 && (num_pages >= pages_per_alloc || index == min_index)) { // Allocate a block. KPhysicalAddress allocated_block = cur_manager->AllocateBlock(index, random); if (allocated_block == 0) { break; } + const size_t host_page_off = GetInteger(expected_vaddr) % Common::HostPageSize; + + // Cut off the start of the page to match expected_vaddr. + const size_t cut_pages = host_page_off >> PageBits; + const size_t remainder = pages_per_alloc - cut_pages; + const size_t used_pages = std::min(num_pages, remainder); + // Cut off end of block if needed + const size_t tail_pages = remainder - used_pages; + + const KPhysicalAddress start = allocated_block + (cut_pages << PageBits); + + // Free the unused blocks. + if (cut_pages > 0) { + cur_manager->Free(allocated_block, cut_pages); + } + if (tail_pages > 0) { + cur_manager->Free(start + (used_pages << PageBits), tail_pages); + } + + ASSERT(GetInteger(start) % Common::HostPageSize == + GetInteger(expected_vaddr) % Common::HostPageSize); + // Ensure we don't leak the block if we fail. ON_RESULT_FAILURE_2 { - cur_manager->Free(allocated_block, pages_per_alloc); + cur_manager->Free(start, used_pages); }; // Add the block to our group. - R_TRY(out->AddBlock(allocated_block, pages_per_alloc)); + R_TRY(out->AddBlock(start, used_pages)); // Maintain the optimized memory bitmap, if we should. if (unoptimized) { - cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), allocated_block, - pages_per_alloc); + cur_manager->TrackUnoptimizedAllocation(m_system.Kernel(), start, used_pages); } - num_pages -= pages_per_alloc; + num_pages -= used_pages; + expected_vaddr += used_pages << PageBits; } } } @@ -301,7 +338,7 @@ Result KMemoryManager::AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, R_SUCCEED(); } -Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option) { +Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option, KProcessAddress expected_vaddr) { ASSERT(out != nullptr); ASSERT(out->GetNumPages() == 0); @@ -314,7 +351,8 @@ Result KMemoryManager::AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 op // Allocate the page group. R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, - m_has_optimized_process[static_cast(pool)], true)); + m_has_optimized_process[static_cast(pool)], true, + expected_vaddr)); // Open the first reference to the pages. for (const auto& block : *out) { @@ -358,7 +396,7 @@ Result KMemoryManager::AllocateForProcess(KPageGroup* out, size_t num_pages, u32 // Allocate the page group. R_TRY(this->AllocatePageGroupImpl(out, num_pages, pool, dir, has_optimized && !is_optimized, - false)); + false, 0)); // Set whether we should optimize. optimized = has_optimized && is_optimized; diff --git a/src/core/hle/kernel/k_memory_manager.h b/src/core/hle/kernel/k_memory_manager.h index 2f4ae7f04f..1828b4e4d1 100644 --- a/src/core/hle/kernel/k_memory_manager.h +++ b/src/core/hle/kernel/k_memory_manager.h @@ -59,7 +59,7 @@ public: void FinalizeOptimizedMemory(u64 process_id, Pool pool); KPhysicalAddress AllocateAndOpenContinuous(size_t num_pages, size_t align_pages, u32 option); - Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option); + Result AllocateAndOpen(KPageGroup* out, size_t num_pages, u32 option, KProcessAddress expected_vaddr = 0); Result AllocateForProcess(KPageGroup* out, size_t num_pages, u32 option, u64 process_id, u8 fill_pattern); @@ -355,7 +355,7 @@ private: } Result AllocatePageGroupImpl(KPageGroup* out, size_t num_pages, Pool pool, Direction dir, - bool unoptimized, bool random); + bool unoptimized, bool random, KProcessAddress expected_vaddr); private: template diff --git a/src/core/hle/kernel/k_page_heap.h b/src/core/hle/kernel/k_page_heap.h index 7d34509983..c52109261c 100644 --- a/src/core/hle/kernel/k_page_heap.h +++ b/src/core/hle/kernel/k_page_heap.h @@ -104,6 +104,11 @@ public: return GetBlockSize(index) / PageSize; } + static constexpr size_t NumMemoryBlockPageShifts{7}; + static constexpr std::array MemoryBlockPageShifts{ + 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, + }; + private: class Block { public: @@ -205,11 +210,6 @@ private: void FreeBlock(KPhysicalAddress block, s32 index); - static constexpr size_t NumMemoryBlockPageShifts{7}; - static constexpr std::array MemoryBlockPageShifts{ - 0xC, 0x10, 0x15, 0x16, 0x19, 0x1D, 0x1E, - }; - private: KPhysicalAddress AllocateByLinearSearch(s32 index); KPhysicalAddress AllocateByRandom(s32 index, size_t num_pages, size_t align_pages); diff --git a/src/core/hle/kernel/k_page_table_base.cpp b/src/core/hle/kernel/k_page_table_base.cpp index a727355a9f..a4b287074e 100644 --- a/src/core/hle/kernel/k_page_table_base.cpp +++ b/src/core/hle/kernel/k_page_table_base.cpp @@ -1397,7 +1397,7 @@ Result KPageTableBase::MapInsecureMemory(KProcessAddress address, size_t size) { KPageGroup pg(m_system.Kernel(), m_block_info_manager); R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( std::addressof(pg), size / PageSize, - KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront))); + KMemoryManager::EncodeOption(insecure_pool, KMemoryManager::Direction::FromFront), GetInteger(address))); // Close the opened pages when we're done with them. // If the mapping succeeds, each page will gain an extra reference, otherwise they will be freed @@ -1607,7 +1607,7 @@ Result KPageTableBase::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProce // Allocate the pages. R_TRY( - m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); + m_system.Kernel().MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option, GetInteger(address))); // Ensure that the page group is closed when we're done working with it. SCOPE_EXIT { @@ -2735,8 +2735,10 @@ Result KPageTableBase::MapPages(KProcessAddress* out_addr, size_t num_pages, siz KScopedLightLock lk(m_general_lock); // Find a random address to map at. + // Note: on non-4KiB paged systems this function no longer mimics Horizon (offset should be 0) and instead + // will attempt to align to host page size to support fastmem KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, - 0, this->GetNumGuardPages()); + GetInteger(phys_addr) % Common::HostPageSize, this->GetNumGuardPages()); R_UNLESS(addr != 0, ResultOutOfMemory); ASSERT(Common::IsAligned(GetInteger(addr), alignment)); ASSERT(this->CanContain(addr, num_pages * PageSize, state)); diff --git a/src/core/hle/kernel/memory_types.h b/src/core/hle/kernel/memory_types.h index 18de675cc1..213f97b675 100644 --- a/src/core/hle/kernel/memory_types.h +++ b/src/core/hle/kernel/memory_types.h @@ -10,7 +10,7 @@ namespace Kernel { -constexpr std::size_t PageBits{12}; +constexpr u8 PageBits{12}; constexpr std::size_t PageSize{1 << PageBits}; using Page = std::array;