mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Merge pull request #3136 from Subv/mem_aliasing1
Kernel/Memory: Added a function to change the memory state of an address range
This commit is contained in:
		
						commit
						33b0b5163f
					
				
					 4 changed files with 187 additions and 0 deletions
				
			
		|  | @ -159,6 +159,39 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u3 | |||
|     return MakeResult<VMAHandle>(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<u32>(expected_perms); | ||||
|         if ((static_cast<u32>(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; | ||||
|  |  | |||
|  | @ -180,6 +180,21 @@ public: | |||
|     ResultVal<VMAHandle> 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); | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ add_executable(tests | |||
|     core/file_sys/path_parser.cpp | ||||
|     core/hle/kernel/hle_ipc.cpp | ||||
|     core/memory/memory.cpp | ||||
|     core/memory/vm_manager.cpp | ||||
|     glad.cpp | ||||
|     tests.cpp | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										138
									
								
								src/tests/core/memory/vm_manager.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/tests/core/memory/vm_manager.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,138 @@ | |||
| // Copyright 2017 Citra Emulator Project
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <vector> | ||||
| #include <catch.hpp> | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/memory.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/memory.h" | ||||
| 
 | ||||
| TEST_CASE("Memory Basics", "[kernel][memory]") { | ||||
|     auto block = std::make_shared<std::vector<u8>>(Memory::PAGE_SIZE); | ||||
|     SECTION("mapping memory") { | ||||
|         // Because of the PageTable, Kernel::VMManager is too big to be created on the stack.
 | ||||
|         auto manager = std::make_unique<Kernel::VMManager>(); | ||||
|         auto result = manager->MapMemoryBlock(Memory::HEAP_VADDR, block, 0, block->size(), | ||||
|                                               Kernel::MemoryState::Private); | ||||
|         REQUIRE(result.Code() == RESULT_SUCCESS); | ||||
| 
 | ||||
|         auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|         CHECK(vma != manager->vma_map.end()); | ||||
|         CHECK(vma->second.size == block->size()); | ||||
|         CHECK(vma->second.type == Kernel::VMAType::AllocatedMemoryBlock); | ||||
|         CHECK(vma->second.backing_block == block); | ||||
|         CHECK(vma->second.meminfo_state == Kernel::MemoryState::Private); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("unmapping memory") { | ||||
|         // Because of the PageTable, Kernel::VMManager is too big to be created on the stack.
 | ||||
|         auto manager = std::make_unique<Kernel::VMManager>(); | ||||
|         auto result = manager->MapMemoryBlock(Memory::HEAP_VADDR, block, 0, block->size(), | ||||
|                                               Kernel::MemoryState::Private); | ||||
|         REQUIRE(result.Code() == RESULT_SUCCESS); | ||||
| 
 | ||||
|         ResultCode code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); | ||||
|         REQUIRE(code == RESULT_SUCCESS); | ||||
| 
 | ||||
|         auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|         CHECK(vma != manager->vma_map.end()); | ||||
|         CHECK(vma->second.type == Kernel::VMAType::Free); | ||||
|         CHECK(vma->second.backing_block == nullptr); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("changing memory permissions") { | ||||
|         // Because of the PageTable, Kernel::VMManager is too big to be created on the stack.
 | ||||
|         auto manager = std::make_unique<Kernel::VMManager>(); | ||||
|         auto result = manager->MapMemoryBlock(Memory::HEAP_VADDR, block, 0, block->size(), | ||||
|                                               Kernel::MemoryState::Private); | ||||
|         REQUIRE(result.Code() == RESULT_SUCCESS); | ||||
| 
 | ||||
|         ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block->size(), | ||||
|                                                   Kernel::VMAPermission::Execute); | ||||
|         CHECK(code == RESULT_SUCCESS); | ||||
| 
 | ||||
|         auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|         CHECK(vma != manager->vma_map.end()); | ||||
|         CHECK(vma->second.permissions == Kernel::VMAPermission::Execute); | ||||
| 
 | ||||
|         code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); | ||||
|         REQUIRE(code == RESULT_SUCCESS); | ||||
|     } | ||||
| 
 | ||||
|     SECTION("changing memory state") { | ||||
|         // Because of the PageTable, Kernel::VMManager is too big to be created on the stack.
 | ||||
|         auto manager = std::make_unique<Kernel::VMManager>(); | ||||
|         auto result = manager->MapMemoryBlock(Memory::HEAP_VADDR, block, 0, block->size(), | ||||
|                                               Kernel::MemoryState::Private); | ||||
|         REQUIRE(result.Code() == RESULT_SUCCESS); | ||||
| 
 | ||||
|         ResultCode code = manager->ReprotectRange(Memory::HEAP_VADDR, block->size(), | ||||
|                                                   Kernel::VMAPermission::ReadWrite); | ||||
|         REQUIRE(code == RESULT_SUCCESS); | ||||
| 
 | ||||
|         SECTION("with invalid address") { | ||||
|             ResultCode code = manager->ChangeMemoryState( | ||||
|                 0xFFFFFFFF, block->size(), Kernel::MemoryState::Locked, | ||||
|                 Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, | ||||
|                 Kernel::VMAPermission::Execute); | ||||
|             CHECK(code == Kernel::ERR_INVALID_ADDRESS); | ||||
|         } | ||||
| 
 | ||||
|         SECTION("ignoring the original permissions") { | ||||
|             ResultCode code = manager->ChangeMemoryState( | ||||
|                 Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, | ||||
|                 Kernel::VMAPermission::None, Kernel::MemoryState::Locked, | ||||
|                 Kernel::VMAPermission::Write); | ||||
|             CHECK(code == RESULT_SUCCESS); | ||||
| 
 | ||||
|             auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|             CHECK(vma != manager->vma_map.end()); | ||||
|             CHECK(vma->second.permissions == Kernel::VMAPermission::Write); | ||||
|             CHECK(vma->second.meminfo_state == Kernel::MemoryState::Locked); | ||||
|         } | ||||
| 
 | ||||
|         SECTION("enforcing the original permissions with correct expectations") { | ||||
|             ResultCode code = manager->ChangeMemoryState( | ||||
|                 Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, | ||||
|                 Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, | ||||
|                 Kernel::VMAPermission::Execute); | ||||
|             CHECK(code == RESULT_SUCCESS); | ||||
| 
 | ||||
|             auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|             CHECK(vma != manager->vma_map.end()); | ||||
|             CHECK(vma->second.permissions == Kernel::VMAPermission::Execute); | ||||
|             CHECK(vma->second.meminfo_state == Kernel::MemoryState::Aliased); | ||||
|         } | ||||
| 
 | ||||
|         SECTION("with incorrect permission expectations") { | ||||
|             ResultCode code = manager->ChangeMemoryState( | ||||
|                 Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Private, | ||||
|                 Kernel::VMAPermission::Execute, Kernel::MemoryState::Aliased, | ||||
|                 Kernel::VMAPermission::Execute); | ||||
|             CHECK(code == Kernel::ERR_INVALID_ADDRESS_STATE); | ||||
| 
 | ||||
|             auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|             CHECK(vma != manager->vma_map.end()); | ||||
|             CHECK(vma->second.permissions == Kernel::VMAPermission::ReadWrite); | ||||
|             CHECK(vma->second.meminfo_state == Kernel::MemoryState::Private); | ||||
|         } | ||||
| 
 | ||||
|         SECTION("with incorrect state expectations") { | ||||
|             ResultCode code = manager->ChangeMemoryState( | ||||
|                 Memory::HEAP_VADDR, block->size(), Kernel::MemoryState::Locked, | ||||
|                 Kernel::VMAPermission::ReadWrite, Kernel::MemoryState::Aliased, | ||||
|                 Kernel::VMAPermission::Execute); | ||||
|             CHECK(code == Kernel::ERR_INVALID_ADDRESS_STATE); | ||||
| 
 | ||||
|             auto vma = manager->FindVMA(Memory::HEAP_VADDR); | ||||
|             CHECK(vma != manager->vma_map.end()); | ||||
|             CHECK(vma->second.permissions == Kernel::VMAPermission::ReadWrite); | ||||
|             CHECK(vma->second.meminfo_state == Kernel::MemoryState::Private); | ||||
|         } | ||||
| 
 | ||||
|         code = manager->UnmapRange(Memory::HEAP_VADDR, block->size()); | ||||
|         REQUIRE(code == RESULT_SUCCESS); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue