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
@ -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…
Reference in New Issue
Block a user