Kernel/Process: implement prvileged Map/Unmap

This is used by svcControlProcessMemory and maps memory as Locked/AliasCode pair.

Also fixed a bug where map didn't apply specified permissions to the alias memory
This commit is contained in:
Weiyi Wang 2018-11-07 12:13:00 -05:00
parent f43524fff1
commit 617b388354
2 changed files with 42 additions and 10 deletions

View file

@ -303,7 +303,8 @@ ResultCode Process::LinearFree(VAddr target, u32 size) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms) { ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perms,
bool privileged) {
LOG_DEBUG(Kernel, "Map memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", target, LOG_DEBUG(Kernel, "Map memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", target,
source, size, static_cast<u8>(perms)); source, size, static_cast<u8>(perms));
if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END ||
@ -320,22 +321,39 @@ ResultCode Process::Map(VAddr target, VAddr source, u32 size, VMAPermission perm
return ERR_INVALID_ADDRESS_STATE; return ERR_INVALID_ADDRESS_STATE;
} }
if (source == target) {
if (privileged) {
// privileged Map allows identical source and target address, which simply changes the
// state and the permission of the memory
return vm_manager.ChangeMemoryState(source, size, MemoryState::Private,
VMAPermission::ReadWrite, MemoryState::AliasCode,
perms);
}
return ERR_INVALID_ADDRESS_STATE;
}
MemoryState source_state = privileged ? MemoryState::Locked : MemoryState::Aliased;
MemoryState target_state = privileged ? MemoryState::AliasCode : MemoryState::Alias;
VMAPermission source_perm = privileged ? VMAPermission::None : VMAPermission::ReadWrite;
// Mark source region as Aliased // Mark source region as Aliased
CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Private, CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Private,
VMAPermission::ReadWrite, MemoryState::Aliased, VMAPermission::ReadWrite, source_state, source_perm));
VMAPermission::ReadWrite));
CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size)); CASCADE_RESULT(auto backing_blocks, vm_manager.GetBackingBlocksForRange(source, size));
VAddr interval_target = target; VAddr interval_target = target;
for (const auto [backing_memory, block_size] : backing_blocks) { for (const auto [backing_memory, block_size] : backing_blocks) {
auto target_vma = vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, auto target_vma =
MemoryState::Alias); vm_manager.MapBackingMemory(interval_target, backing_memory, block_size, target_state);
ASSERT(target_vma.Succeeded());
vm_manager.Reprotect(target_vma.Unwrap(), perms);
interval_target += block_size; interval_target += block_size;
} }
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms) { ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms,
bool privileged) {
LOG_DEBUG(Kernel, "Unmap memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}", LOG_DEBUG(Kernel, "Unmap memory target={:08X}, source={:08X}, size={:08X}, perms={:08X}",
target, source, size, static_cast<u8>(perms)); target, source, size, static_cast<u8>(perms));
if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END || if (source < Memory::HEAP_VADDR || source + size > Memory::HEAP_VADDR_END ||
@ -349,11 +367,23 @@ ResultCode Process::Unmap(VAddr target, VAddr source, u32 size, VMAPermission pe
// TODO(wwylele): check that the source and the target are actually a pair created by Map // TODO(wwylele): check that the source and the target are actually a pair created by Map
// Should return error 0xD8E007F5 in this case // Should return error 0xD8E007F5 in this case
if (source == target) {
if (privileged) {
// privileged Unmap allows identical source and target address, which simply changes
// the state and the permission of the memory
return vm_manager.ChangeMemoryState(source, size, MemoryState::AliasCode,
VMAPermission::None, MemoryState::Private, perms);
}
return ERR_INVALID_ADDRESS_STATE;
}
MemoryState source_state = privileged ? MemoryState::Locked : MemoryState::Aliased;
CASCADE_CODE(vm_manager.UnmapRange(target, size)); CASCADE_CODE(vm_manager.UnmapRange(target, size));
// Change back source region state. Note that the permission is reprotected according to param // Change back source region state. Note that the permission is reprotected according to param
CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, MemoryState::Aliased, CASCADE_CODE(vm_manager.ChangeMemoryState(source, size, source_state, VMAPermission::None,
VMAPermission::None, MemoryState::Private, perms)); MemoryState::Private, perms));
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }

View file

@ -188,8 +188,10 @@ public:
ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms); ResultVal<VAddr> LinearAllocate(VAddr target, u32 size, VMAPermission perms);
ResultCode LinearFree(VAddr target, u32 size); ResultCode LinearFree(VAddr target, u32 size);
ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms); ResultCode Map(VAddr target, VAddr source, u32 size, VMAPermission perms,
ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms); bool privileged = false);
ResultCode Unmap(VAddr target, VAddr source, u32 size, VMAPermission perms,
bool privileged = false);
private: private:
explicit Process(Kernel::KernelSystem& kernel); explicit Process(Kernel::KernelSystem& kernel);