From cdeeecf0807d0005356f30db0f7164c5891a9245 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Fri, 17 Jul 2015 23:19:16 -0300 Subject: Kernel: Properly implement ControlMemory FREE and COMMIT --- src/core/hle/kernel/vm_manager.cpp | 91 +++++++++++++++++++++++++++++++++----- 1 file changed, 81 insertions(+), 10 deletions(-) (limited to 'src/core/hle/kernel/vm_manager.cpp') diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 65395476b..2610acf76 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -60,7 +60,11 @@ void VMManager::Reset() { } VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { - return std::prev(vma_map.upper_bound(target)); + if (target >= MAX_ADDRESS) { + return vma_map.end(); + } else { + return std::prev(vma_map.upper_bound(target)); + } } ResultVal VMManager::MapMemoryBlock(VAddr target, @@ -115,10 +119,8 @@ ResultVal VMManager::MapMMIO(VAddr target, PAddr paddr, u3 return MakeResult(MergeAdjacent(vma_handle)); } -void VMManager::Unmap(VMAHandle vma_handle) { - VMAIter iter = StripIterConstness(vma_handle); - - VirtualMemoryArea& vma = iter->second; +VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { + VirtualMemoryArea& vma = vma_handle->second; vma.type = VMAType::Free; vma.permissions = VMAPermission::None; vma.meminfo_state = MemoryState::Free; @@ -130,17 +132,57 @@ void VMManager::Unmap(VMAHandle vma_handle) { UpdatePageTableForVMA(vma); - MergeAdjacent(iter); + return MergeAdjacent(vma_handle); +} + +ResultCode VMManager::UnmapRange(VAddr target, u32 size) { + CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); + VAddr target_end = target + size; + + VMAIter end = vma_map.end(); + // The comparison against the end of the range must be done using addresses since VMAs can be + // merged during this process, causing invalidation of the iterators. + while (vma != end && vma->second.base < target_end) { + vma = std::next(Unmap(vma)); + } + + ASSERT(FindVMA(target)->second.size >= size); + return RESULT_SUCCESS; } -void VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { +VMManager::VMAHandle VMManager::Reprotect(VMAHandle vma_handle, VMAPermission new_perms) { VMAIter iter = StripIterConstness(vma_handle); VirtualMemoryArea& vma = iter->second; vma.permissions = new_perms; UpdatePageTableForVMA(vma); - MergeAdjacent(iter); + return MergeAdjacent(iter); +} + +ResultCode VMManager::ReprotectRange(VAddr target, u32 size, VMAPermission new_perms) { + CASCADE_RESULT(VMAIter vma, CarveVMARange(target, size)); + VAddr target_end = target + size; + + VMAIter end = vma_map.end(); + // The comparison against the end of the range must be done using addresses since VMAs can be + // merged during this process, causing invalidation of the iterators. + while (vma != end && vma->second.base < target_end) { + vma = std::next(StripIterConstness(Reprotect(vma, new_perms))); + } + + return RESULT_SUCCESS; +} + +void VMManager::RefreshMemoryBlockMappings(const std::vector* block) { + // If this ever proves to have a noticeable performance impact, allow users of the function to + // specify a specific range of addresses to limit the scan to. + for (const auto& p : vma_map) { + const VirtualMemoryArea& vma = p.second; + if (block == vma.backing_block.get()) { + UpdatePageTableForVMA(vma); + } + } } void VMManager::LogLayout(Log::Level log_level) const { @@ -161,8 +203,8 @@ VMManager::VMAIter VMManager::StripIterConstness(const VMAHandle & iter) { } ResultVal VMManager::CarveVMA(VAddr base, u32 size) { - ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: %8X", size); - ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: %08X", base); + ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); + ASSERT_MSG((base & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", base); VMAIter vma_handle = StripIterConstness(FindVMA(base)); if (vma_handle == vma_map.end()) { @@ -196,6 +238,35 @@ ResultVal VMManager::CarveVMA(VAddr base, u32 size) { return MakeResult(vma_handle); } +ResultVal VMManager::CarveVMARange(VAddr target, u32 size) { + ASSERT_MSG((size & Memory::PAGE_MASK) == 0, "non-page aligned size: 0x%8X", size); + ASSERT_MSG((target & Memory::PAGE_MASK) == 0, "non-page aligned base: 0x%08X", target); + + VAddr target_end = target + size; + ASSERT(target_end >= target); + ASSERT(target_end <= MAX_ADDRESS); + ASSERT(size > 0); + + VMAIter begin_vma = StripIterConstness(FindVMA(target)); + VMAIter i_end = vma_map.lower_bound(target_end); + for (auto i = begin_vma; i != i_end; ++i) { + if (i->second.type == VMAType::Free) { + return ERR_INVALID_ADDRESS_STATE; + } + } + + if (target != begin_vma->second.base) { + begin_vma = SplitVMA(begin_vma, target - begin_vma->second.base); + } + + VMAIter end_vma = StripIterConstness(FindVMA(target_end)); + if (end_vma != vma_map.end() && target_end != end_vma->second.base) { + end_vma = SplitVMA(end_vma, target_end - end_vma->second.base); + } + + return MakeResult(begin_vma); +} + VMManager::VMAIter VMManager::SplitVMA(VMAIter vma_handle, u32 offset_in_vma) { VirtualMemoryArea& old_vma = vma_handle->second; VirtualMemoryArea new_vma = old_vma; // Make a copy of the VMA -- cgit v1.2.3