|
|
@@ -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;
|