|
|
|
@ -424,6 +424,68 @@ ResultCode KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, std |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
VAddr KPageTable::FindFreeArea(VAddr region_start, std::size_t region_num_pages, |
|
|
|
std::size_t num_pages, std::size_t alignment, std::size_t offset, |
|
|
|
std::size_t guard_pages) { |
|
|
|
VAddr address = 0; |
|
|
|
|
|
|
|
if (num_pages <= region_num_pages) { |
|
|
|
if (this->IsAslrEnabled()) { |
|
|
|
// Try to directly find a free area up to 8 times.
|
|
|
|
for (std::size_t i = 0; i < 8; i++) { |
|
|
|
const std::size_t random_offset = |
|
|
|
KSystemControl::GenerateRandomRange( |
|
|
|
0, (region_num_pages - num_pages - guard_pages) * PageSize / alignment) * |
|
|
|
alignment; |
|
|
|
const VAddr candidate = |
|
|
|
Common::AlignDown((region_start + random_offset), alignment) + offset; |
|
|
|
|
|
|
|
KMemoryInfo info = this->QueryInfoImpl(candidate); |
|
|
|
|
|
|
|
if (info.state != KMemoryState::Free) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (region_start > candidate) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (info.GetAddress() + guard_pages * PageSize > candidate) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
const VAddr candidate_end = candidate + (num_pages + guard_pages) * PageSize - 1; |
|
|
|
if (candidate_end > info.GetLastAddress()) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
if (candidate_end > region_start + region_num_pages * PageSize - 1) { |
|
|
|
continue; |
|
|
|
} |
|
|
|
|
|
|
|
address = candidate; |
|
|
|
break; |
|
|
|
} |
|
|
|
// Fall back to finding the first free area with a random offset.
|
|
|
|
if (address == 0) { |
|
|
|
// NOTE: Nintendo does not account for guard pages here.
|
|
|
|
// This may theoretically cause an offset to be chosen that cannot be mapped. We
|
|
|
|
// will account for guard pages.
|
|
|
|
const std::size_t offset_pages = KSystemControl::GenerateRandomRange( |
|
|
|
0, region_num_pages - num_pages - guard_pages); |
|
|
|
address = block_manager->FindFreeArea(region_start + offset_pages * PageSize, |
|
|
|
region_num_pages - offset_pages, num_pages, |
|
|
|
alignment, offset, guard_pages); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Find the first free area.
|
|
|
|
if (address == 0) { |
|
|
|
address = block_manager->FindFreeArea(region_start, region_num_pages, num_pages, |
|
|
|
alignment, offset, guard_pages); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return address; |
|
|
|
} |
|
|
|
|
|
|
|
ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size, |
|
|
|
KPageTable& src_page_table, VAddr src_addr) { |
|
|
|
KScopedLightLock lk(general_lock); |
|
|
|
@ -1055,6 +1117,46 @@ ResultCode KPageTable::MapPages(VAddr address, KPageLinkedList& page_linked_list |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
ResultCode KPageTable::MapPages(VAddr* out_addr, std::size_t num_pages, std::size_t alignment, |
|
|
|
PAddr phys_addr, bool is_pa_valid, VAddr region_start, |
|
|
|
std::size_t region_num_pages, KMemoryState state, |
|
|
|
KMemoryPermission perm) { |
|
|
|
ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); |
|
|
|
|
|
|
|
// Ensure this is a valid map request.
|
|
|
|
R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), |
|
|
|
ResultInvalidCurrentMemory); |
|
|
|
R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); |
|
|
|
|
|
|
|
// Lock the table.
|
|
|
|
KScopedLightLock lk(general_lock); |
|
|
|
|
|
|
|
// Find a random address to map at.
|
|
|
|
VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, |
|
|
|
this->GetNumGuardPages()); |
|
|
|
R_UNLESS(addr != 0, ResultOutOfMemory); |
|
|
|
ASSERT(Common::IsAligned(addr, alignment)); |
|
|
|
ASSERT(this->CanContain(addr, num_pages * PageSize, state)); |
|
|
|
ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, |
|
|
|
KMemoryPermission::None, KMemoryPermission::None, |
|
|
|
KMemoryAttribute::None, KMemoryAttribute::None) |
|
|
|
.IsSuccess()); |
|
|
|
|
|
|
|
// Perform mapping operation.
|
|
|
|
if (is_pa_valid) { |
|
|
|
R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); |
|
|
|
} else { |
|
|
|
UNIMPLEMENTED(); |
|
|
|
} |
|
|
|
|
|
|
|
// Update the blocks.
|
|
|
|
block_manager->Update(addr, num_pages, state, perm); |
|
|
|
|
|
|
|
// We successfully mapped the pages.
|
|
|
|
*out_addr = addr; |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
ResultCode KPageTable::UnmapPages(VAddr addr, const KPageLinkedList& page_linked_list) { |
|
|
|
ASSERT(this->IsLockedByCurrentThread()); |
|
|
|
|
|
|
|
@ -1097,6 +1199,30 @@ ResultCode KPageTable::UnmapPages(VAddr addr, KPageLinkedList& page_linked_list, |
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
ResultCode KPageTable::UnmapPages(VAddr address, std::size_t num_pages, KMemoryState state) { |
|
|
|
// Check that the unmap is in range.
|
|
|
|
const std::size_t size = num_pages * PageSize; |
|
|
|
R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); |
|
|
|
|
|
|
|
// Lock the table.
|
|
|
|
KScopedLightLock lk(general_lock); |
|
|
|
|
|
|
|
// Check the memory state.
|
|
|
|
std::size_t num_allocator_blocks{}; |
|
|
|
R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, |
|
|
|
KMemoryState::All, state, KMemoryPermission::None, |
|
|
|
KMemoryPermission::None, KMemoryAttribute::All, |
|
|
|
KMemoryAttribute::None)); |
|
|
|
|
|
|
|
// Perform the unmap.
|
|
|
|
R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); |
|
|
|
|
|
|
|
// Update the blocks.
|
|
|
|
block_manager->Update(address, num_pages, KMemoryState::Free, KMemoryPermission::None); |
|
|
|
|
|
|
|
return ResultSuccess; |
|
|
|
} |
|
|
|
|
|
|
|
ResultCode KPageTable::SetProcessMemoryPermission(VAddr addr, std::size_t size, |
|
|
|
Svc::MemoryPermission svc_perm) { |
|
|
|
const size_t num_pages = size / PageSize; |
|
|
|
|