diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 7a007c065..3f1ae2c4a 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -129,6 +129,39 @@ ResultVal VMManager::MapMMIO(VAddr target, PAddr paddr, u3 return MakeResult(MergeAdjacent(vma_handle)); } +ResultCode VMManager::ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state, + VMAPermission expected_perms, MemoryState new_state, + VMAPermission new_perms) { + VAddr target_end = target + size; + VMAIter begin_vma = StripIterConstness(FindVMA(target)); + VMAIter i_end = vma_map.lower_bound(target_end); + + if (begin_vma == vma_map.end()) + return ERR_INVALID_ADDRESS; + + for (auto i = begin_vma; i != i_end; ++i) { + auto& vma = i->second; + if (vma.meminfo_state != expected_state) { + return ERR_INVALID_ADDRESS_STATE; + } + u32 perms = static_cast(expected_perms); + if ((static_cast(vma.permissions) & perms) != perms) { + return ERR_INVALID_ADDRESS_STATE; + } + } + + CASCADE_RESULT(auto vma, CarveVMARange(target, size)); + ASSERT(vma->second.size == size); + + vma->second.permissions = new_perms; + vma->second.meminfo_state = new_state; + UpdatePageTableForVMA(vma->second); + + MergeAdjacent(vma); + + return RESULT_SUCCESS; +} + VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) { VirtualMemoryArea& vma = vma_handle->second; vma.type = VMAType::Free; diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 1302527bb..92fe987e2 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -166,6 +166,21 @@ public: ResultVal MapMMIO(VAddr target, PAddr paddr, u32 size, MemoryState state, Memory::MMIORegionPointer mmio_handler); + /** + * Updates the memory state and permissions of the specified range. The range's original memory + * state and permissions must match the `expected` parameters. + * + * @param target The guest address of the beginning of the range. + * @param size The size of the range + * @param expected_state Expected MemoryState of the range. + * @param expected_perms Expected VMAPermission of the range. + * @param new_state New MemoryState for the range. + * @param new_perms New VMAPermission for the range. + */ + ResultCode ChangeMemoryState(VAddr target, u32 size, MemoryState expected_state, + VMAPermission expected_perms, MemoryState new_state, + VMAPermission new_perms); + /// Unmaps a range of addresses, splitting VMAs as necessary. ResultCode UnmapRange(VAddr target, u32 size); @@ -224,4 +239,4 @@ private: /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); }; -} +} // namespace Kernel