diff options
Diffstat (limited to '')
-rw-r--r-- | src/core/hle/kernel/k_page_table.cpp | 944 |
1 files changed, 588 insertions, 356 deletions
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 9c7ac22dc..02b5cada4 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -106,9 +106,10 @@ KPageTable::~KPageTable() = default; Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type, bool enable_aslr, bool enable_das_merge, bool from_back, - KMemoryManager::Pool pool, VAddr code_addr, + KMemoryManager::Pool pool, KProcessAddress code_addr, size_t code_size, KSystemResource* system_resource, - KResourceLimit* resource_limit) { + KResourceLimit* resource_limit, + Core::Memory::Memory& memory) { const auto GetSpaceStart = [this](KAddressSpaceInfo::Type type) { return KAddressSpaceInfo::GetAddressSpaceStart(m_address_space_width, type); @@ -117,10 +118,13 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type return KAddressSpaceInfo::GetAddressSpaceSize(m_address_space_width, type); }; + // Set the tracking memory + m_memory = std::addressof(memory); + // Set our width and heap/alias sizes m_address_space_width = GetAddressSpaceWidthFromType(as_type); - const VAddr start = 0; - const VAddr end{1ULL << m_address_space_width}; + const KProcessAddress start = 0; + const KProcessAddress end{1ULL << m_address_space_width}; size_t alias_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Alias)}; size_t heap_region_size{GetSpaceSize(KAddressSpaceInfo::Type::Heap)}; @@ -135,8 +139,8 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type // Set code regions and determine remaining constexpr size_t RegionAlignment{2_MiB}; - VAddr process_code_start{}; - VAddr process_code_end{}; + KProcessAddress process_code_start{}; + KProcessAddress process_code_end{}; size_t stack_region_size{}; size_t kernel_map_region_size{}; @@ -149,8 +153,8 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type m_code_region_end = m_code_region_start + GetSpaceSize(KAddressSpaceInfo::Type::Map39Bit); m_alias_code_region_start = m_code_region_start; m_alias_code_region_end = m_code_region_end; - process_code_start = Common::AlignDown(code_addr, RegionAlignment); - process_code_end = Common::AlignUp(code_addr + code_size, RegionAlignment); + process_code_start = Common::AlignDown(GetInteger(code_addr), RegionAlignment); + process_code_end = Common::AlignUp(GetInteger(code_addr) + code_size, RegionAlignment); } else { stack_region_size = 0; kernel_map_region_size = 0; @@ -178,7 +182,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type m_resource_limit = resource_limit; // Determine the region we can place our undetermineds in - VAddr alloc_start{}; + KProcessAddress alloc_start{}; size_t alloc_size{}; if ((process_code_start - m_code_region_start) >= (end - process_code_end)) { alloc_start = m_code_region_start; @@ -292,7 +296,7 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type : KMemoryManager::Direction::FromFront); // Ensure that we regions inside our address space - auto IsInAddressSpace = [&](VAddr addr) { + auto IsInAddressSpace = [&](KProcessAddress addr) { return m_address_space_start <= addr && addr <= m_address_space_end; }; ASSERT(IsInAddressSpace(m_alias_region_start)); @@ -305,14 +309,14 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type ASSERT(IsInAddressSpace(m_kernel_map_region_end)); // Ensure that we selected regions that don't overlap - const VAddr alias_start{m_alias_region_start}; - const VAddr alias_last{m_alias_region_end - 1}; - const VAddr heap_start{m_heap_region_start}; - const VAddr heap_last{m_heap_region_end - 1}; - const VAddr stack_start{m_stack_region_start}; - const VAddr stack_last{m_stack_region_end - 1}; - const VAddr kmap_start{m_kernel_map_region_start}; - const VAddr kmap_last{m_kernel_map_region_end - 1}; + const KProcessAddress alias_start{m_alias_region_start}; + const KProcessAddress alias_last{m_alias_region_end - 1}; + const KProcessAddress heap_start{m_heap_region_start}; + const KProcessAddress heap_last{m_heap_region_end - 1}; + const KProcessAddress stack_start{m_stack_region_start}; + const KProcessAddress stack_last{m_stack_region_end - 1}; + const KProcessAddress kmap_start{m_kernel_map_region_start}; + const KProcessAddress kmap_last{m_kernel_map_region_end - 1}; ASSERT(alias_last < heap_start || heap_last < alias_start); ASSERT(alias_last < stack_start || stack_last < alias_start); ASSERT(alias_last < kmap_start || kmap_last < alias_start); @@ -334,9 +338,10 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type void KPageTable::Finalize() { // Finalize memory blocks. - m_memory_block_manager.Finalize(m_memory_block_slab_manager, [&](VAddr addr, u64 size) { - m_system.Memory().UnmapRegion(*m_page_table_impl, addr, size); - }); + m_memory_block_manager.Finalize(m_memory_block_slab_manager, + [&](KProcessAddress addr, u64 size) { + m_memory->UnmapRegion(*m_page_table_impl, addr, size); + }); // Release any insecure mapped memory. if (m_mapped_insecure_memory) { @@ -352,7 +357,7 @@ void KPageTable::Finalize() { m_page_table_impl.reset(); } -Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState state, +Result KPageTable::MapProcessCode(KProcessAddress addr, size_t num_pages, KMemoryState state, KMemoryPermission perm) { const u64 size{num_pages * PageSize}; @@ -388,7 +393,8 @@ Result KPageTable::MapProcessCode(VAddr addr, size_t num_pages, KMemoryState sta R_SUCCEED(); } -Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t size) { +Result KPageTable::MapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, + size_t size) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), ResultInvalidMemoryRegion); @@ -435,6 +441,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si KPageGroup pg{m_kernel, m_block_info_manager}; AddRegionToPages(src_address, num_pages, pg); + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + // Reprotect the source as kernel-read/not mapped. const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); @@ -447,7 +456,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si }); // Map the alias pages. - R_TRY(MapPages(dst_address, pg, new_perm)); + const KPageProperties dst_properties = {new_perm, false, false, + DisableMergeAttribute::DisableHead}; + R_TRY( + this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false)); // We successfully mapped the alias pages, so we don't need to unprotect the src pages on // failure. @@ -467,7 +479,8 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si R_SUCCEED(); } -Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t size, +Result KPageTable::UnmapCodeMemory(KProcessAddress dst_address, KProcessAddress src_address, + size_t size, ICacheInvalidationStrategy icache_invalidation_strategy) { // Validate the mapping request. R_UNLESS(this->CanContain(dst_address, size, KMemoryState::AliasCode), @@ -519,7 +532,7 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t SCOPE_EXIT({ if (reprotected_pages && any_code_pages) { if (icache_invalidation_strategy == ICacheInvalidationStrategy::InvalidateRange) { - m_system.InvalidateCpuInstructionCacheRange(dst_address, size); + m_system.InvalidateCpuInstructionCacheRange(GetInteger(dst_address), size); } else { m_system.InvalidateCpuInstructionCaches(); } @@ -569,9 +582,10 @@ Result KPageTable::UnmapCodeMemory(VAddr dst_address, VAddr src_address, size_t R_SUCCEED(); } -VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size_t num_pages, - size_t alignment, size_t offset, size_t guard_pages) { - VAddr address = 0; +KProcessAddress KPageTable::FindFreeArea(KProcessAddress region_start, size_t region_num_pages, + size_t num_pages, size_t alignment, size_t offset, + size_t guard_pages) { + KProcessAddress address = 0; if (num_pages <= region_num_pages) { if (this->IsAslrEnabled()) { @@ -587,7 +601,7 @@ VAddr KPageTable::FindFreeArea(VAddr region_start, size_t region_num_pages, size return address; } -Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { +Result KPageTable::MakePageGroup(KPageGroup& pg, KProcessAddress addr, size_t num_pages) { ASSERT(this->IsLockedByCurrentThread()); const size_t size = num_pages * PageSize; @@ -598,11 +612,11 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, addr), + R_UNLESS(m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr)), ResultInvalidCurrentMemory); // Prepare tracking variables. - PAddr cur_addr = next_entry.phys_addr; + KPhysicalAddress cur_addr = next_entry.phys_addr; size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); size_t tot_size = cur_size; @@ -640,7 +654,7 @@ Result KPageTable::MakePageGroup(KPageGroup& pg, VAddr addr, size_t num_pages) { R_SUCCEED(); } -bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_pages) { +bool KPageTable::IsValidPageGroup(const KPageGroup& pg, KProcessAddress addr, size_t num_pages) { ASSERT(this->IsLockedByCurrentThread()); const size_t size = num_pages * PageSize; @@ -653,7 +667,7 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_p // We're going to validate that the group we'd expect is the group we see. auto cur_it = pg.begin(); - PAddr cur_block_address = cur_it->GetAddress(); + KPhysicalAddress cur_block_address = cur_it->GetAddress(); size_t cur_block_pages = cur_it->GetNumPages(); auto UpdateCurrentIterator = [&]() { @@ -671,12 +685,12 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_p // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - if (!m_page_table_impl->BeginTraversal(next_entry, context, addr)) { + if (!m_page_table_impl->BeginTraversal(next_entry, context, GetInteger(addr))) { return false; } // Prepare tracking variables. - PAddr cur_addr = next_entry.phys_addr; + KPhysicalAddress cur_addr = next_entry.phys_addr; size_t cur_size = next_entry.block_size - (cur_addr & (next_entry.block_size - 1)); size_t tot_size = cur_size; @@ -728,8 +742,8 @@ bool KPageTable::IsValidPageGroup(const KPageGroup& pg, VAddr addr, size_t num_p return cur_block_address == cur_addr && cur_block_pages == (cur_size / PageSize); } -Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& src_page_table, - VAddr src_addr) { +Result KPageTable::UnmapProcessMemory(KProcessAddress dst_addr, size_t size, + KPageTable& src_page_table, KProcessAddress src_addr) { // Acquire the table locks. KScopedLightLockPair lk(src_page_table.m_general_lock, m_general_lock); @@ -768,8 +782,8 @@ Result KPageTable::UnmapProcessMemory(VAddr dst_addr, size_t size, KPageTable& s } Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_blocks_needed, - VAddr address, size_t size, KMemoryPermission test_perm, - KMemoryState dst_state) { + KProcessAddress address, size_t size, + KMemoryPermission test_perm, KMemoryState dst_state) { // Validate pre-conditions. ASSERT(this->IsLockedByCurrentThread()); ASSERT(test_perm == KMemoryPermission::UserReadWrite || @@ -784,10 +798,10 @@ Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_bloc : KMemoryPermission::UserRead; // Get aligned extents. - const VAddr aligned_src_start = Common::AlignDown((address), PageSize); - const VAddr aligned_src_end = Common::AlignUp((address) + size, PageSize); - const VAddr mapping_src_start = Common::AlignUp((address), PageSize); - const VAddr mapping_src_end = Common::AlignDown((address) + size, PageSize); + const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(address), PageSize); + const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(address) + size, PageSize); + const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_src_end = Common::AlignDown(GetInteger(address) + size, PageSize); const auto aligned_src_last = (aligned_src_end)-1; const auto mapping_src_last = (mapping_src_end)-1; @@ -834,14 +848,15 @@ Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_bloc test_attr_mask, KMemoryAttribute::None)); if (mapping_src_start < mapping_src_end && (mapping_src_start) < info.GetEndAddress() && - info.GetAddress() < (mapping_src_end)) { - const auto cur_start = - info.GetAddress() >= (mapping_src_start) ? info.GetAddress() : (mapping_src_start); + info.GetAddress() < GetInteger(mapping_src_end)) { + const auto cur_start = info.GetAddress() >= GetInteger(mapping_src_start) + ? info.GetAddress() + : (mapping_src_start); const auto cur_end = mapping_src_last >= info.GetLastAddress() ? info.GetEndAddress() : (mapping_src_end); const size_t cur_size = cur_end - cur_start; - if (info.GetAddress() < (mapping_src_start)) { + if (info.GetAddress() < GetInteger(mapping_src_start)) { ++blocks_needed; } if (mapping_src_last < info.GetLastAddress()) { @@ -876,30 +891,32 @@ Result KPageTable::SetupForIpcClient(PageLinkedList* page_list, size_t* out_bloc R_SUCCEED(); } -Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_addr, - KMemoryPermission test_perm, KMemoryState dst_state, - KPageTable& src_page_table, bool send) { +Result KPageTable::SetupForIpcServer(KProcessAddress* out_addr, size_t size, + KProcessAddress src_addr, KMemoryPermission test_perm, + KMemoryState dst_state, KPageTable& src_page_table, + bool send) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(src_page_table.IsLockedByCurrentThread()); // Check that we can theoretically map. - const VAddr region_start = m_alias_region_start; + const KProcessAddress region_start = m_alias_region_start; const size_t region_size = m_alias_region_end - m_alias_region_start; R_UNLESS(size < region_size, ResultOutOfAddressSpace); // Get aligned source extents. - const VAddr src_start = src_addr; - const VAddr src_end = src_addr + size; - const VAddr aligned_src_start = Common::AlignDown((src_start), PageSize); - const VAddr aligned_src_end = Common::AlignUp((src_start) + size, PageSize); - const VAddr mapping_src_start = Common::AlignUp((src_start), PageSize); - const VAddr mapping_src_end = Common::AlignDown((src_start) + size, PageSize); + const KProcessAddress src_start = src_addr; + const KProcessAddress src_end = src_addr + size; + const KProcessAddress aligned_src_start = Common::AlignDown(GetInteger(src_start), PageSize); + const KProcessAddress aligned_src_end = Common::AlignUp(GetInteger(src_start) + size, PageSize); + const KProcessAddress mapping_src_start = Common::AlignUp(GetInteger(src_start), PageSize); + const KProcessAddress mapping_src_end = + Common::AlignDown(GetInteger(src_start) + size, PageSize); const size_t aligned_src_size = aligned_src_end - aligned_src_start; const size_t mapping_src_size = (mapping_src_start < mapping_src_end) ? (mapping_src_end - mapping_src_start) : 0; // Select a random address to map at. - VAddr dst_addr = + KProcessAddress dst_addr = this->FindFreeArea(region_start, region_size / PageSize, aligned_src_size / PageSize, PageSize, 0, this->GetNumGuardPages()); @@ -924,9 +941,9 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add R_UNLESS(memory_reservation.Succeeded(), ResultLimitReached); // Ensure that we manage page references correctly. - PAddr start_partial_page = 0; - PAddr end_partial_page = 0; - VAddr cur_mapped_addr = dst_addr; + KPhysicalAddress start_partial_page = 0; + KPhysicalAddress end_partial_page = 0; + KProcessAddress cur_mapped_addr = dst_addr; // If the partial pages are mapped, an extra reference will have been opened. Otherwise, they'll // free on scope exit. @@ -971,11 +988,12 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add // Begin traversal. Common::PageTable::TraversalContext context; Common::PageTable::TraversalEntry next_entry; - bool traverse_valid = src_impl.BeginTraversal(next_entry, context, aligned_src_start); + bool traverse_valid = + src_impl.BeginTraversal(next_entry, context, GetInteger(aligned_src_start)); ASSERT(traverse_valid); // Prepare tracking variables. - PAddr cur_block_addr = next_entry.phys_addr; + KPhysicalAddress cur_block_addr = next_entry.phys_addr; size_t cur_block_size = next_entry.block_size - ((cur_block_addr) & (next_entry.block_size - 1)); size_t tot_block_size = cur_block_size; @@ -983,7 +1001,7 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add // Map the start page, if we have one. if (start_partial_page != 0) { // Ensure the page holds correct data. - const VAddr start_partial_virt = + const KVirtualAddress start_partial_virt = GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), start_partial_page); if (send) { const size_t partial_offset = src_start - aligned_src_start; @@ -996,21 +1014,22 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add clear_size = 0; } - std::memset(m_system.Memory().GetPointer<void>(start_partial_virt), fill_val, + std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val, partial_offset); std::memcpy( - m_system.Memory().GetPointer<void>(start_partial_virt + partial_offset), - m_system.Memory().GetPointer<void>( - GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), cur_block_addr) + - partial_offset), + m_memory->GetPointer<void>(GetInteger(start_partial_virt) + partial_offset), + m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress( + m_system.Kernel().MemoryLayout(), cur_block_addr)) + + partial_offset), copy_size); if (clear_size > 0) { - std::memset(m_system.Memory().GetPointer<void>(start_partial_virt + partial_offset + - copy_size), + std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt) + + partial_offset + copy_size), fill_val, clear_size); } } else { - std::memset(m_system.Memory().GetPointer<void>(start_partial_virt), fill_val, PageSize); + std::memset(m_memory->GetPointer<void>(GetInteger(start_partial_virt)), fill_val, + PageSize); } // Map the page. @@ -1055,7 +1074,8 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add } // Handle the last direct-mapped page. - if (const VAddr mapped_block_end = aligned_src_start + tot_block_size - cur_block_size; + if (const KProcessAddress mapped_block_end = + aligned_src_start + tot_block_size - cur_block_size; mapped_block_end < mapping_src_end) { const size_t last_block_size = mapping_src_end - mapped_block_end; @@ -1078,18 +1098,19 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add // Map the end page, if we have one. if (end_partial_page != 0) { // Ensure the page holds correct data. - const VAddr end_partial_virt = + const KVirtualAddress end_partial_virt = GetHeapVirtualAddress(m_system.Kernel().MemoryLayout(), end_partial_page); if (send) { const size_t copy_size = src_end - mapping_src_end; - std::memcpy(m_system.Memory().GetPointer<void>(end_partial_virt), - m_system.Memory().GetPointer<void>(GetHeapVirtualAddress( - m_system.Kernel().MemoryLayout(), cur_block_addr)), + std::memcpy(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), + m_memory->GetPointer<void>(GetInteger(GetHeapVirtualAddress( + m_system.Kernel().MemoryLayout(), cur_block_addr))), copy_size); - std::memset(m_system.Memory().GetPointer<void>(end_partial_virt + copy_size), fill_val, - PageSize - copy_size); + std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt) + copy_size), + fill_val, PageSize - copy_size); } else { - std::memset(m_system.Memory().GetPointer<void>(end_partial_virt), fill_val, PageSize); + std::memset(m_memory->GetPointer<void>(GetInteger(end_partial_virt)), fill_val, + PageSize); } // Map the page. @@ -1110,7 +1131,7 @@ Result KPageTable::SetupForIpcServer(VAddr* out_addr, size_t size, VAddr src_add R_SUCCEED(); } -Result KPageTable::SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, +Result KPageTable::SetupForIpc(KProcessAddress* out_dst_addr, size_t size, KProcessAddress src_addr, KPageTable& src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send) { // For convenience, alias this. @@ -1136,8 +1157,8 @@ Result KPageTable::SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, R_TRY(allocator_result); // Get the mapped extents. - const VAddr src_map_start = Common::AlignUp((src_addr), PageSize); - const VAddr src_map_end = Common::AlignDown((src_addr) + size, PageSize); + const KProcessAddress src_map_start = Common::AlignUp(GetInteger(src_addr), PageSize); + const KProcessAddress src_map_end = Common::AlignDown(GetInteger(src_addr) + size, PageSize); const size_t src_map_size = src_map_end - src_map_start; // Ensure that we clean up appropriately if we fail after this. @@ -1166,7 +1187,8 @@ Result KPageTable::SetupForIpc(VAddr* out_dst_addr, size_t size, VAddr src_addr, R_SUCCEED(); } -Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState dst_state) { +Result KPageTable::CleanupForIpcServer(KProcessAddress address, size_t size, + KMemoryState dst_state) { // Validate the address. R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); @@ -1190,8 +1212,8 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState KScopedPageTableUpdater updater(this); // Get aligned extents. - const VAddr aligned_start = Common::AlignDown((address), PageSize); - const VAddr aligned_end = Common::AlignUp((address) + size, PageSize); + const KProcessAddress aligned_start = Common::AlignDown(GetInteger(address), PageSize); + const KProcessAddress aligned_end = Common::AlignUp(GetInteger(address) + size, PageSize); const size_t aligned_size = aligned_end - aligned_start; const size_t aligned_num_pages = aligned_size / PageSize; @@ -1205,22 +1227,23 @@ Result KPageTable::CleanupForIpcServer(VAddr address, size_t size, KMemoryState KMemoryBlockDisableMergeAttribute::Normal); // Release from the resource limit as relevant. - const VAddr mapping_start = Common::AlignUp((address), PageSize); - const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); + const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); const size_t mapping_size = (mapping_start < mapping_end) ? mapping_end - mapping_start : 0; m_resource_limit->Release(LimitableResource::PhysicalMemoryMax, aligned_size - mapping_size); R_SUCCEED(); } -Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState dst_state) { +Result KPageTable::CleanupForIpcClient(KProcessAddress address, size_t size, + KMemoryState dst_state) { // Validate the address. R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Get aligned source extents. - const VAddr mapping_start = Common::AlignUp((address), PageSize); - const VAddr mapping_end = Common::AlignDown((address) + size, PageSize); - const VAddr mapping_last = mapping_end - 1; + const KProcessAddress mapping_start = Common::AlignUp(GetInteger(address), PageSize); + const KProcessAddress mapping_end = Common::AlignDown(GetInteger(address) + size, PageSize); + const KProcessAddress mapping_last = mapping_end - 1; const size_t mapping_size = (mapping_start < mapping_end) ? (mapping_end - mapping_start) : 0; // If nothing was mapped, we're actually done immediately. @@ -1273,7 +1296,7 @@ Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState KMemoryInfo cur_info = start_it->GetMemoryInfo(); // Create tracking variables. - VAddr cur_address = cur_info.GetAddress(); + KProcessAddress cur_address = cur_info.GetAddress(); size_t cur_size = cur_info.GetSize(); bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; @@ -1346,7 +1369,7 @@ Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState .IsSuccess()); // Create tracking variables. - VAddr cur_address = cur_info.GetAddress(); + KProcessAddress cur_address = cur_info.GetAddress(); size_t cur_size = cur_info.GetSize(); bool cur_perm_eq = cur_info.GetPermission() == cur_info.GetOriginalPermission(); bool cur_needs_set_perm = !cur_perm_eq && cur_info.GetIpcLockCount() == 1; @@ -1433,16 +1456,16 @@ Result KPageTable::CleanupForIpcClient(VAddr address, size_t size, KMemoryState } void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLinkedList* page_list, - VAddr address, size_t size, + KProcessAddress address, size_t size, KMemoryPermission prot_perm) { ASSERT(this->IsLockedByCurrentThread()); - ASSERT(Common::IsAligned(address, PageSize)); + ASSERT(Common::IsAligned(GetInteger(address), PageSize)); ASSERT(Common::IsAligned(size, PageSize)); // Get the mapped extents. - const VAddr src_map_start = address; - const VAddr src_map_end = address + size; - const VAddr src_map_last = src_map_end - 1; + const KProcessAddress src_map_start = address; + const KProcessAddress src_map_end = address + size; + const KProcessAddress src_map_last = src_map_end - 1; // This function is only invoked when there's something to do. ASSERT(src_map_end > src_map_start); @@ -1452,8 +1475,9 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi while (true) { const KMemoryInfo info = it->GetMemoryInfo(); - const auto cur_start = - info.GetAddress() >= src_map_start ? info.GetAddress() : src_map_start; + const auto cur_start = info.GetAddress() >= GetInteger(src_map_start) + ? info.GetAddress() + : GetInteger(src_map_start); const auto cur_end = src_map_last <= info.GetLastAddress() ? src_map_end : info.GetEndAddress(); @@ -1463,7 +1487,7 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi (info.GetIpcLockCount() != 0 && (info.GetOriginalPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm)) { // Check if we actually need to fix the protections on the block. - if (cur_end == src_map_end || info.GetAddress() <= src_map_start || + if (cur_end == src_map_end || info.GetAddress() <= GetInteger(src_map_start) || (info.GetPermission() & KMemoryPermission::IpcLockChangeMask) != prot_perm) { ASSERT(Operate(cur_start, (cur_end - cur_start) / PageSize, info.GetPermission(), OperationType::ChangePermissions) @@ -1482,15 +1506,15 @@ void KPageTable::CleanupForIpcClientOnServerSetupFailure([[maybe_unused]] PageLi } } -Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { +Result KPageTable::MapPhysicalMemory(KProcessAddress address, size_t size) { // Lock the physical memory lock. KScopedLightLock phys_lk(m_map_physical_memory_lock); // Calculate the last address for convenience. - const VAddr last_address = address + size - 1; + const KProcessAddress last_address = address + size - 1; // Define iteration variables. - VAddr cur_address; + KProcessAddress cur_address; size_t mapped_size; // The entire mapping process can be retried. @@ -1522,7 +1546,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Track the memory if it's mapped. if (info.GetState() != KMemoryState::Free) { - mapped_size += VAddr(info.GetEndAddress()) - cur_address; + mapped_size += KProcessAddress(info.GetEndAddress()) - cur_address; } // Advance. @@ -1575,7 +1599,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { const bool is_free = info.GetState() == KMemoryState::Free; if (is_free) { - if (info.GetAddress() < address) { + if (info.GetAddress() < GetInteger(address)) { ++num_allocator_blocks; } if (last_address < info.GetLastAddress()) { @@ -1593,7 +1617,8 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Track the memory if it's mapped. if (!is_free) { - checked_mapped_size += VAddr(info.GetEndAddress()) - cur_address; + checked_mapped_size += + KProcessAddress(info.GetEndAddress()) - cur_address; } // Advance. @@ -1621,7 +1646,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // Prepare to iterate over the memory. auto pg_it = pg.begin(); - PAddr pg_phys_addr = pg_it->GetAddress(); + KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); size_t pg_pages = pg_it->GetNumPages(); // Reset the current tracking address, and make sure we clean up on failure. @@ -1629,7 +1654,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { cur_address = address; ON_RESULT_FAILURE { if (cur_address > address) { - const VAddr last_unmap_address = cur_address - 1; + const KProcessAddress last_unmap_address = cur_address - 1; // Iterate, unmapping the pages. cur_address = address; @@ -1646,7 +1671,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { if (info.GetState() == KMemoryState::Free) { // Determine the range to unmap. const size_t cur_pages = - std::min(VAddr(info.GetEndAddress()) - cur_address, + std::min(KProcessAddress(info.GetEndAddress()) - cur_address, last_unmap_address + 1 - cur_address) / PageSize; @@ -1689,9 +1714,10 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { // If it's unmapped, we need to map it. if (info.GetState() == KMemoryState::Free) { // Determine the range to map. - size_t map_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, - last_address + 1 - cur_address) / - PageSize; + size_t map_pages = + std::min(KProcessAddress(info.GetEndAddress()) - cur_address, + last_address + 1 - cur_address) / + PageSize; // While we have pages to map, map them. while (map_pages > 0) { @@ -1748,7 +1774,7 @@ Result KPageTable::MapPhysicalMemory(VAddr address, size_t size) { } } -Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { +Result KPageTable::UnmapPhysicalMemory(KProcessAddress address, size_t size) { // Lock the physical memory lock. KScopedLightLock phys_lk(m_map_physical_memory_lock); @@ -1756,13 +1782,13 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { KScopedLightLock lk(m_general_lock); // Calculate the last address for convenience. - const VAddr last_address = address + size - 1; + const KProcessAddress last_address = address + size - 1; // Define iteration variables. - VAddr map_start_address = 0; - VAddr map_last_address = 0; + KProcessAddress map_start_address = 0; + KProcessAddress map_last_address = 0; - VAddr cur_address; + KProcessAddress cur_address; size_t mapped_size; size_t num_allocator_blocks = 0; @@ -1795,7 +1821,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { map_last_address = (last_address >= info.GetLastAddress()) ? info.GetLastAddress() : last_address; - if (info.GetAddress() < address) { + if (info.GetAddress() < GetInteger(address)) { ++num_allocator_blocks; } if (last_address < info.GetLastAddress()) { @@ -1848,7 +1874,7 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { // If the memory state is normal, we need to unmap it. if (info.GetState() == KMemoryState::Normal) { // Determine the range to unmap. - const size_t cur_pages = std::min(VAddr(info.GetEndAddress()) - cur_address, + const size_t cur_pages = std::min(KProcessAddress(info.GetEndAddress()) - cur_address, last_address + 1 - cur_address) / PageSize; @@ -1881,7 +1907,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) { R_SUCCEED(); } -Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { +Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, + size_t size) { // Lock the table. KScopedLightLock lk(m_general_lock); @@ -1902,53 +1929,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) KMemoryAttribute::None)); // Create an update allocator for the source. - Result src_allocator_result{ResultSuccess}; + Result src_allocator_result; KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. - Result dst_allocator_result{ResultSuccess}; + Result dst_allocator_result; KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); R_TRY(dst_allocator_result); // Map the memory. - KPageGroup page_linked_list{m_kernel, m_block_info_manager}; - const size_t num_pages{size / PageSize}; - const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( - KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); - const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; - - AddRegionToPages(src_address, num_pages, page_linked_list); { + // Determine the number of pages being operated on. + const size_t num_pages = size / PageSize; + + // Create page groups for the memory being unmapped. + KPageGroup pg{m_kernel, m_block_info_manager}; + + // Create the page group representing the source. + R_TRY(this->MakePageGroup(pg, src_address, num_pages)); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + // Reprotect the source as kernel-read/not mapped. - auto block_guard = detail::ScopeExit([&] { - Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, - OperationType::ChangePermissions); - }); - R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); - R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); + const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( + KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); + const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; + const KPageProperties src_properties = {new_src_perm, false, false, + DisableMergeAttribute::DisableHeadBodyTail}; + R_TRY(this->Operate(src_address, num_pages, src_properties.perm, + OperationType::ChangePermissions)); - block_guard.Cancel(); - } + // Ensure that we unprotect the source pages on failure. + ON_RESULT_FAILURE { + const KPageProperties unprotect_properties = { + KMemoryPermission::UserReadWrite, false, false, + DisableMergeAttribute::EnableHeadBodyTail}; + ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm, + OperationType::ChangePermissions) == ResultSuccess); + }; - // Apply the memory block updates. - m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, - new_src_perm, new_src_attr, - KMemoryBlockDisableMergeAttribute::Locked, - KMemoryBlockDisableMergeAttribute::None); - m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::Stack, KMemoryPermission::UserReadWrite, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + // Map the alias pages. + const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false, + DisableMergeAttribute::DisableHead}; + R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, + false)); + + // Apply the memory block updates. + m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, + src_state, new_src_perm, new_src_attr, + KMemoryBlockDisableMergeAttribute::Locked, + KMemoryBlockDisableMergeAttribute::None); + m_memory_block_manager.Update( + std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); + } R_SUCCEED(); } -Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { +Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, + size_t size) { // Lock the table. KScopedLightLock lk(m_general_lock); @@ -1970,108 +2017,209 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None)); // Create an update allocator for the source. - Result src_allocator_result{ResultSuccess}; + Result src_allocator_result; KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result), m_memory_block_slab_manager, num_src_allocator_blocks); R_TRY(src_allocator_result); // Create an update allocator for the destination. - Result dst_allocator_result{ResultSuccess}; + Result dst_allocator_result; KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result), m_memory_block_slab_manager, num_dst_allocator_blocks); R_TRY(dst_allocator_result); - KPageGroup src_pages{m_kernel, m_block_info_manager}; - KPageGroup dst_pages{m_kernel, m_block_info_manager}; - const size_t num_pages{size / PageSize}; + // Unmap the memory. + { + // Determine the number of pages being operated on. + const size_t num_pages = size / PageSize; - AddRegionToPages(src_address, num_pages, src_pages); - AddRegionToPages(dst_address, num_pages, dst_pages); + // Create page groups for the memory being unmapped. + KPageGroup pg{m_kernel, m_block_info_manager}; - R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); + // Create the page group representing the destination. + R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); - { - auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); + // Ensure the page group is the valid for the source. + R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); - R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); - R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, - OperationType::ChangePermissions)); + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); - block_guard.Cancel(); - } + // Unmap the aliased copy of the pages. + const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, + DisableMergeAttribute::None}; + R_TRY( + this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap)); + + // Ensure that we re-map the aliased pages on failure. + ON_RESULT_FAILURE { + this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); + }; - // Apply the memory block updates. - m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, - KMemoryPermission::UserReadWrite, KMemoryAttribute::None, - KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Locked); - m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, - KMemoryState::None, KMemoryPermission::None, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, - KMemoryBlockDisableMergeAttribute::Normal); + // Try to set the permissions for the source pages back to what they should be. + const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, + DisableMergeAttribute::EnableAndMergeHeadBodyTail}; + R_TRY(this->Operate(src_address, num_pages, src_properties.perm, + OperationType::ChangePermissions)); + + // Apply the memory block updates. + m_memory_block_manager.Update( + std::addressof(src_allocator), src_address, num_pages, src_state, + KMemoryPermission::UserReadWrite, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); + m_memory_block_manager.Update( + std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); + } R_SUCCEED(); } -Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, - KMemoryPermission perm) { +Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, + size_t num_pages, KMemoryPermission perm) { ASSERT(this->IsLockedByCurrentThread()); - VAddr cur_addr{addr}; + // Create a page group to hold the pages we allocate. + KPageGroup pg{m_kernel, m_block_info_manager}; - for (const auto& node : page_linked_list) { - if (const auto result{ - Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; - result.IsError()) { - const size_t num_pages{(addr - cur_addr) / PageSize}; + // Allocate the pages. + R_TRY( + m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); - ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) - .IsSuccess()); + // Ensure that the page group is closed when we're done working with it. + SCOPE_EXIT({ pg.Close(); }); - R_RETURN(result); + // Clear all pages. + for (const auto& it : pg) { + std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, + it.GetSize()); + } + + // Map the pages. + R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup)); +} + +Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, + const KPageGroup& pg, const KPageProperties properties, + bool reuse_ll) { + ASSERT(this->IsLockedByCurrentThread()); + + // Note the current address, so that we can iterate. + const KProcessAddress start_address = address; + KProcessAddress cur_address = address; + + // Ensure that we clean up on failure. + ON_RESULT_FAILURE { + ASSERT(!reuse_ll); + if (cur_address != start_address) { + const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, + DisableMergeAttribute::None}; + ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize, + unmap_properties.perm, OperationType::Unmap) == ResultSuccess); } + }; - cur_addr += node.GetNumPages() * PageSize; + // Iterate, mapping all pages in the group. + for (const auto& block : pg) { + // Map and advance. + const KPageProperties cur_properties = + (cur_address == start_address) + ? properties + : KPageProperties{properties.perm, properties.io, properties.uncached, + DisableMergeAttribute::None}; + this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map, + block.GetAddress()); + cur_address += block.GetSize(); } + // We succeeded! R_SUCCEED(); } -Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, - KMemoryPermission perm) { - // Check that the map is in range. - const size_t num_pages{page_linked_list.GetNumPages()}; - const size_t size{num_pages * PageSize}; - R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); +void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, + const KPageGroup& pg) { + ASSERT(this->IsLockedByCurrentThread()); - // Lock the table. - KScopedLightLock lk(m_general_lock); + // Note the current address, so that we can iterate. + const KProcessAddress start_address = address; + const KProcessAddress last_address = start_address + size - 1; + const KProcessAddress end_address = last_address + 1; - // Check the memory state. - R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, - KMemoryPermission::None, KMemoryPermission::None, - KMemoryAttribute::None, KMemoryAttribute::None)); + // Iterate over the memory. + auto pg_it = pg.begin(); + ASSERT(pg_it != pg.end()); - // Create an update allocator. - Result allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - m_memory_block_slab_manager); + KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); + size_t pg_pages = pg_it->GetNumPages(); - // Map the pages. - R_TRY(MapPages(address, page_linked_list, perm)); + auto it = m_memory_block_manager.FindIterator(start_address); + while (true) { + // Check that the iterator is valid. + ASSERT(it != m_memory_block_manager.end()); - // Update the blocks. - m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); + // Get the memory info. + const KMemoryInfo info = it->GetMemoryInfo(); - R_SUCCEED(); + // Determine the range to map. + KProcessAddress map_address = std::max<KProcessAddress>(info.GetAddress(), start_address); + const KProcessAddress map_end_address = + std::min<KProcessAddress>(info.GetEndAddress(), end_address); + ASSERT(map_end_address != map_address); + + // Determine if we should disable head merge. + const bool disable_head_merge = + info.GetAddress() >= GetInteger(start_address) && + True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal); + const KPageProperties map_properties = { + info.GetPermission(), false, false, + disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None}; + + // While we have pages to map, map them. + size_t map_pages = (map_end_address - map_address) / PageSize; + while (map_pages > 0) { + // Check if we're at the end of the physical block. + if (pg_pages == 0) { + // Ensure there are more pages to map. + ASSERT(pg_it != pg.end()); + + // Advance our physical block. + ++pg_it; + pg_phys_addr = pg_it->GetAddress(); + pg_pages = pg_it->GetNumPages(); + } + + // Map whatever we can. + const size_t cur_pages = std::min(pg_pages, map_pages); + ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map, + pg_phys_addr) == ResultSuccess); + + // Advance. + map_address += cur_pages * PageSize; + map_pages -= cur_pages; + + pg_phys_addr += cur_pages * PageSize; + pg_pages -= cur_pages; + } + + // Check if we're done. + if (last_address <= info.GetLastAddress()) { + break; + } + + // Advance. + ++it; + } + + // Check that we re-mapped precisely the page group. + ASSERT((++pg_it) == pg.end()); } -Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, - bool is_pa_valid, VAddr region_start, size_t region_num_pages, +Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, + KPhysicalAddress phys_addr, bool is_pa_valid, + KProcessAddress region_start, size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); @@ -2084,26 +2232,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, KScopedLightLock lk(m_general_lock); // Find a random address to map at. - VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, - this->GetNumGuardPages()); + KProcessAddress 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(Common::IsAligned(GetInteger(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()); + KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); // Create an update allocator. - Result allocator_result{ResultSuccess}; + Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); // Perform mapping operation. if (is_pa_valid) { - R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); + const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; + R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr)); } else { - UNIMPLEMENTED(); + R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm)); } // Update the blocks. @@ -2116,28 +2268,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, R_SUCCEED(); } -Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { - ASSERT(this->IsLockedByCurrentThread()); +Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, + KMemoryPermission perm) { + // Check that the map is in range. + const size_t size = num_pages * PageSize; + R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); - VAddr cur_addr{addr}; + // Lock the table. + KScopedLightLock lk(m_general_lock); - for (const auto& node : page_linked_list) { - if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, - OperationType::Unmap)}; - result.IsError()) { - R_RETURN(result); - } + // Check the memory state. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, + KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); - cur_addr += node.GetNumPages() * PageSize; - } + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Map the pages. + R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm)); + + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); R_SUCCEED(); } -Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { +Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) { // Check that the unmap is in range. - const size_t num_pages{page_linked_list.GetNumPages()}; - const size_t size{num_pages * PageSize}; + const size_t size = num_pages * PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); // Lock the table. @@ -2151,13 +2320,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo KMemoryAttribute::None)); // Create an update allocator. - Result allocator_result{ResultSuccess}; + Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + // Perform the unmap. - R_TRY(UnmapPages(address, page_linked_list)); + const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, + DisableMergeAttribute::None}; + R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap)); // Update the blocks. m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, @@ -2168,29 +2342,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo R_SUCCEED(); } -Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { - // Check that the unmap is in range. +Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, + KProcessAddress region_start, size_t region_num_pages, + KMemoryState state, KMemoryPermission perm) { + ASSERT(!this->IsLockedByCurrentThread()); + + // Ensure this is a valid map request. + const size_t num_pages = pg.GetNumPages(); + 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(m_general_lock); + + // Find a random address to map at. + KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, + 0, this->GetNumGuardPages()); + R_UNLESS(addr != 0, ResultOutOfMemory); + 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) == ResultSuccess); + + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Perform mapping operation. + const KPageProperties properties = {perm, state == KMemoryState::Io, false, + DisableMergeAttribute::DisableHead}; + R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); + + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); + + // We successfully mapped the pages. + *out_addr = addr; + R_SUCCEED(); +} + +Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, + KMemoryPermission perm) { + ASSERT(!this->IsLockedByCurrentThread()); + + // Ensure this is a valid map request. + const size_t num_pages = pg.GetNumPages(); const size_t size = num_pages * PageSize; - R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); + R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory); // Lock the table. KScopedLightLock lk(m_general_lock); - // Check the memory state. - size_t num_allocator_blocks{}; + // Check if state allows us to map. + size_t num_allocator_blocks; + R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, + KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, + KMemoryPermission::None, KMemoryAttribute::None, + KMemoryAttribute::None)); + + // Create an update allocator. + Result allocator_result; + KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), + m_memory_block_slab_manager, num_allocator_blocks); + R_TRY(allocator_result); + + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Perform mapping operation. + const KPageProperties properties = {perm, state == KMemoryState::Io, false, + DisableMergeAttribute::DisableHead}; + R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); + + // Update the blocks. + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, + KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, + KMemoryBlockDisableMergeAttribute::None); + + // We successfully mapped the pages. + R_SUCCEED(); +} + +Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, + KMemoryState state) { + ASSERT(!this->IsLockedByCurrentThread()); + + // Ensure this is a valid unmap request. + const size_t num_pages = pg.GetNumPages(); + const size_t size = num_pages * PageSize; + R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); + + // Lock the table. + KScopedLightLock lk(m_general_lock); + + // Check if state allows us to unmap. + 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)); + // Check that the page group is valid. + R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory); + // Create an update allocator. - Result allocator_result{ResultSuccess}; + Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager, num_allocator_blocks); R_TRY(allocator_result); - // Perform the unmap. - R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); + // We're going to perform an update, so create a helper. + KScopedPageTableUpdater updater(this); + + // Perform unmapping operation. + const KPageProperties properties = {KMemoryPermission::None, false, false, + DisableMergeAttribute::None}; + R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap)); // Update the blocks. m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, @@ -2201,7 +2476,7 @@ Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState stat R_SUCCEED(); } -Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t num_pages, +Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) { @@ -2226,7 +2501,7 @@ Result KPageTable::MakeAndOpenPageGroup(KPageGroup* out, VAddr address, size_t n R_SUCCEED(); } -Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, +Result KPageTable::SetProcessMemoryPermission(KProcessAddress addr, size_t size, Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; @@ -2287,23 +2562,23 @@ Result KPageTable::SetProcessMemoryPermission(VAddr addr, size_t size, // Ensure cache coherency, if we're setting pages as executable. if (is_x) { - m_system.InvalidateCpuInstructionCacheRange(addr, size); + m_system.InvalidateCpuInstructionCacheRange(GetInteger(addr), size); } R_SUCCEED(); } -KMemoryInfo KPageTable::QueryInfoImpl(VAddr addr) { +KMemoryInfo KPageTable::QueryInfoImpl(KProcessAddress addr) { KScopedLightLock lk(m_general_lock); return m_memory_block_manager.FindBlock(addr)->GetMemoryInfo(); } -KMemoryInfo KPageTable::QueryInfo(VAddr addr) { +KMemoryInfo KPageTable::QueryInfo(KProcessAddress addr) { if (!Contains(addr, 1)) { return { - .m_address = m_address_space_end, - .m_size = 0 - m_address_space_end, + .m_address = GetInteger(m_address_space_end), + .m_size = 0 - GetInteger(m_address_space_end), .m_state = static_cast<KMemoryState>(Svc::MemoryState::Inaccessible), .m_device_disable_merge_left_count = 0, .m_device_disable_merge_right_count = 0, @@ -2320,7 +2595,8 @@ KMemoryInfo KPageTable::QueryInfo(VAddr addr) { return QueryInfoImpl(addr); } -Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm) { +Result KPageTable::SetMemoryPermission(KProcessAddress addr, size_t size, + Svc::MemoryPermission svc_perm) { const size_t num_pages = size / PageSize; // Lock the table. @@ -2357,7 +2633,7 @@ Result KPageTable::SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermi R_SUCCEED(); } -Result KPageTable::SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr) { +Result KPageTable::SetMemoryAttribute(KProcessAddress addr, size_t size, u32 mask, u32 attr) { const size_t num_pages = size / PageSize; ASSERT((static_cast<KMemoryAttribute>(mask) | KMemoryAttribute::SetMask) == KMemoryAttribute::SetMask); @@ -2412,12 +2688,12 @@ Result KPageTable::SetMaxHeapSize(size_t size) { R_SUCCEED(); } -Result KPageTable::SetHeapSize(VAddr* out, size_t size) { +Result KPageTable::SetHeapSize(u64* out, size_t size) { // Lock the physical memory mutex. KScopedLightLock map_phys_mem_lk(m_map_physical_memory_lock); // Try to perform a reduction in heap, instead of an extension. - VAddr cur_address{}; + KProcessAddress cur_address{}; size_t allocation_size{}; { // Lock the table. @@ -2468,11 +2744,11 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = m_heap_region_start; + *out = GetInteger(m_heap_region_start); R_SUCCEED(); } else if (size == GetHeapSize()) { // The size requested is exactly the current size. - *out = m_heap_region_start; + *out = GetInteger(m_heap_region_start); R_SUCCEED(); } else { // We have to allocate memory. Determine how much to allocate and where while the table @@ -2526,7 +2802,7 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { // Clear all the newly allocated pages. for (size_t cur_page = 0; cur_page < num_pages; ++cur_page) { - std::memset(m_system.Memory().GetPointer(m_current_heap_end + (cur_page * PageSize)), 0, + std::memset(m_memory->GetPointer(m_current_heap_end + (cur_page * PageSize)), 0, PageSize); } @@ -2545,62 +2821,14 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) { m_current_heap_end = m_heap_region_start + size; // Set the output. - *out = m_heap_region_start; + *out = GetInteger(m_heap_region_start); R_SUCCEED(); } } -ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align, - bool is_map_only, VAddr region_start, - size_t region_num_pages, KMemoryState state, - KMemoryPermission perm, PAddr map_addr) { - KScopedLightLock lk(m_general_lock); - - R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state), - ResultInvalidCurrentMemory); - R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory); - const VAddr addr{ - AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; - R_UNLESS(addr, ResultOutOfMemory); - - // Create an update allocator. - Result allocator_result{ResultSuccess}; - KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), - m_memory_block_slab_manager); - - if (is_map_only) { - R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); - } else { - // Create a page group tohold the pages we allocate. - KPageGroup pg{m_kernel, m_block_info_manager}; - - R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( - &pg, needed_num_pages, - KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); - - // Ensure that the page group is closed when we're done working with it. - SCOPE_EXIT({ pg.Close(); }); - - // Clear all pages. - for (const auto& it : pg) { - std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), - m_heap_fill_value, it.GetSize()); - } - - R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup)); - } - - // Update the blocks. - m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, - KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, - KMemoryBlockDisableMergeAttribute::None); - - return addr; -} - -Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size, - KMemoryPermission perm, bool is_aligned, - bool check_heap) { +Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, KProcessAddress address, + size_t size, KMemoryPermission perm, + bool is_aligned, bool check_heap) { // Lightly validate the range before doing anything else. const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); @@ -2636,7 +2864,8 @@ Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, R_SUCCEED(); } -Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap) { +Result KPageTable::LockForUnmapDeviceAddressSpace(KProcessAddress address, size_t size, + bool check_heap) { // Lightly validate the range before doing anything else. const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); @@ -2670,7 +2899,7 @@ Result KPageTable::LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bo R_SUCCEED(); } -Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { +Result KPageTable::UnlockForDeviceAddressSpace(KProcessAddress address, size_t size) { // Lightly validate the range before doing anything else. const size_t num_pages = size / PageSize; R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); @@ -2698,7 +2927,8 @@ Result KPageTable::UnlockForDeviceAddressSpace(VAddr address, size_t size) { R_SUCCEED(); } -Result KPageTable::LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size) { +Result KPageTable::LockForIpcUserBuffer(KPhysicalAddress* out, KProcessAddress address, + size_t size) { R_RETURN(this->LockMemoryAndOpen( nullptr, out, address, size, KMemoryState::FlagCanIpcUserBuffer, KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::All, @@ -2707,7 +2937,7 @@ Result KPageTable::LockForIpcUserBuffer(PAddr* out, VAddr address, size_t size) KMemoryAttribute::Locked)); } -Result KPageTable::UnlockForIpcUserBuffer(VAddr address, size_t size) { +Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) { R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanIpcUserBuffer, KMemoryState::FlagCanIpcUserBuffer, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, @@ -2715,7 +2945,7 @@ Result KPageTable::UnlockForIpcUserBuffer(VAddr address, size_t size) { KMemoryAttribute::Locked, nullptr)); } -Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { +Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { R_RETURN(this->LockMemoryAndOpen( out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::All, KMemoryPermission::UserReadWrite, KMemoryAttribute::All, @@ -2723,17 +2953,17 @@ Result KPageTable::LockForCodeMemory(KPageGroup* out, VAddr addr, size_t size) { KMemoryAttribute::Locked)); } -Result KPageTable::UnlockForCodeMemory(VAddr addr, size_t size, const KPageGroup& pg) { +Result KPageTable::UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg) { R_RETURN(this->UnlockMemory( addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, KMemoryAttribute::Locked, &pg)); } -bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { - auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(addr); +bool KPageTable::IsRegionContiguous(KProcessAddress addr, u64 size) const { + auto start_ptr = m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr)); for (u64 offset{}; offset < size; offset += PageSize) { - if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(addr + offset)) { + if (start_ptr != m_system.DeviceMemory().GetPointer<u8>(GetInteger(addr) + offset)) { return false; } start_ptr += PageSize; @@ -2741,18 +2971,19 @@ bool KPageTable::IsRegionContiguous(VAddr addr, u64 size) const { return true; } -void KPageTable::AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list) { - VAddr addr{start}; +void KPageTable::AddRegionToPages(KProcessAddress start, size_t num_pages, + KPageGroup& page_linked_list) { + KProcessAddress addr{start}; while (addr < start + (num_pages * PageSize)) { - const PAddr paddr{GetPhysicalAddr(addr)}; + const KPhysicalAddress paddr{GetPhysicalAddr(addr)}; ASSERT(paddr != 0); page_linked_list.AddBlock(paddr, 1); addr += PageSize; } } -VAddr KPageTable::AllocateVirtualMemory(VAddr start, size_t region_num_pages, u64 needed_num_pages, - size_t align) { +KProcessAddress KPageTable::AllocateVirtualMemory(KProcessAddress start, size_t region_num_pages, + u64 needed_num_pages, size_t align) { if (m_enable_aslr) { UNIMPLEMENTED(); } @@ -2760,11 +2991,11 @@ VAddr KPageTable::AllocateVirtualMemory(VAddr start, size_t region_num_pages, u6 IsKernel() ? 1 : 4); } -Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_group, +Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, const KPageGroup& page_group, OperationType operation) { ASSERT(this->IsLockedByCurrentThread()); - ASSERT(Common::IsAligned(addr, PageSize)); + ASSERT(Common::IsAligned(GetInteger(addr), PageSize)); ASSERT(num_pages > 0); ASSERT(num_pages == page_group.GetNumPages()); @@ -2777,7 +3008,7 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ const size_t size{node.GetNumPages() * PageSize}; // Map the pages. - m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); + m_memory->MapMemoryRegion(*m_page_table_impl, addr, size, node.GetAddress()); addr += size; } @@ -2795,12 +3026,12 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, const KPageGroup& page_ R_SUCCEED(); } -Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, - OperationType operation, PAddr map_addr) { +Result KPageTable::Operate(KProcessAddress addr, size_t num_pages, KMemoryPermission perm, + OperationType operation, KPhysicalAddress map_addr) { ASSERT(this->IsLockedByCurrentThread()); ASSERT(num_pages > 0); - ASSERT(Common::IsAligned(addr, PageSize)); + ASSERT(Common::IsAligned(GetInteger(addr), PageSize)); ASSERT(ContainsPages(addr, num_pages)); switch (operation) { @@ -2810,14 +3041,14 @@ Result KPageTable::Operate(VAddr addr, size_t num_pages, KMemoryPermission perm, SCOPE_EXIT({ pages_to_close.CloseAndReset(); }); this->AddRegionToPages(addr, num_pages, pages_to_close); - m_system.Memory().UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); + m_memory->UnmapRegion(*m_page_table_impl, addr, num_pages * PageSize); break; } case OperationType::MapFirst: case OperationType::Map: { ASSERT(map_addr); - ASSERT(Common::IsAligned(map_addr, PageSize)); - m_system.Memory().MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); + ASSERT(Common::IsAligned(GetInteger(map_addr), PageSize)); + m_memory->MapMemoryRegion(*m_page_table_impl, addr, num_pages * PageSize, map_addr); // Open references to pages, if we should. if (IsHeapPhysicalAddress(m_kernel.MemoryLayout(), map_addr)) { @@ -2854,7 +3085,7 @@ void KPageTable::FinalizeUpdate(PageLinkedList* page_list) { } } -VAddr KPageTable::GetRegionAddress(KMemoryState state) const { +KProcessAddress KPageTable::GetRegionAddress(KMemoryState state) const { switch (state) { case KMemoryState::Free: case KMemoryState::Kernel: @@ -2926,11 +3157,11 @@ size_t KPageTable::GetRegionSize(KMemoryState state) const { } } -bool KPageTable::CanContain(VAddr addr, size_t size, KMemoryState state) const { - const VAddr end = addr + size; - const VAddr last = end - 1; +bool KPageTable::CanContain(KProcessAddress addr, size_t size, KMemoryState state) const { + const KProcessAddress end = addr + size; + const KProcessAddress last = end - 1; - const VAddr region_start = this->GetRegionAddress(state); + const KProcessAddress region_start = this->GetRegionAddress(state); const size_t region_size = this->GetRegionSize(state); const bool is_in_region = @@ -2985,21 +3216,21 @@ Result KPageTable::CheckMemoryState(const KMemoryInfo& info, KMemoryState state_ R_SUCCEED(); } -Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr addr, size_t size, - KMemoryState state_mask, KMemoryState state, - KMemoryPermission perm_mask, KMemoryPermission perm, - KMemoryAttribute attr_mask, +Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, KProcessAddress addr, + size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. - const VAddr last_addr = addr + size - 1; + const KProcessAddress last_addr = addr + size - 1; KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. const size_t blocks_for_start_align = - (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; + (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0; while (true) { // Validate against the provided masks. @@ -3018,7 +3249,7 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr a // If the end address isn't aligned, we need a block. const size_t blocks_for_end_align = - (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; + (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0; if (out_blocks_needed != nullptr) { *out_blocks_needed = blocks_for_start_align + blocks_for_end_align; @@ -3029,20 +3260,20 @@ Result KPageTable::CheckMemoryStateContiguous(size_t* out_blocks_needed, VAddr a Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* out_perm, KMemoryAttribute* out_attr, size_t* out_blocks_needed, - VAddr addr, size_t size, KMemoryState state_mask, + KProcessAddress addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryAttribute ignore_attr) const { ASSERT(this->IsLockedByCurrentThread()); // Get information about the first block. - const VAddr last_addr = addr + size - 1; + const KProcessAddress last_addr = addr + size - 1; KMemoryBlockManager::const_iterator it = m_memory_block_manager.FindIterator(addr); KMemoryInfo info = it->GetMemoryInfo(); // If the start address isn't aligned, we need a block. const size_t blocks_for_start_align = - (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0; + (Common::AlignDown(GetInteger(addr), PageSize) != info.GetAddress()) ? 1 : 0; // Validate all blocks in the range have correct state. const KMemoryState first_state = info.m_state; @@ -3071,7 +3302,7 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* // If the end address isn't aligned, we need a block. const size_t blocks_for_end_align = - (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0; + (Common::AlignUp(GetInteger(addr) + size, PageSize) != info.GetEndAddress()) ? 1 : 0; // Write output state. if (out_state != nullptr) { @@ -3089,11 +3320,12 @@ Result KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermission* R_SUCCEED(); } -Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr addr, size_t size, - KMemoryState state_mask, KMemoryState state, - KMemoryPermission perm_mask, KMemoryPermission perm, - KMemoryAttribute attr_mask, KMemoryAttribute attr, - KMemoryPermission new_perm, KMemoryAttribute lock_attr) { +Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_KPhysicalAddress, + KProcessAddress addr, size_t size, KMemoryState state_mask, + KMemoryState state, KMemoryPermission perm_mask, + KMemoryPermission perm, KMemoryAttribute attr_mask, + KMemoryAttribute attr, KMemoryPermission new_perm, + KMemoryAttribute lock_attr) { // Validate basic preconditions. ASSERT((lock_attr & attr) == KMemoryAttribute::None); ASSERT((lock_attr & (KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared)) == @@ -3123,8 +3355,8 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr attr_mask, attr)); // Get the physical address, if we're supposed to. - if (out_paddr != nullptr) { - ASSERT(this->GetPhysicalAddressLocked(out_paddr, addr)); + if (out_KPhysicalAddress != nullptr) { + ASSERT(this->GetPhysicalAddressLocked(out_KPhysicalAddress, addr)); } // Make the page group, if we're supposed to. @@ -3155,7 +3387,7 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, PAddr* out_paddr, VAddr R_SUCCEED(); } -Result KPageTable::UnlockMemory(VAddr addr, size_t size, KMemoryState state_mask, +Result KPageTable::UnlockMemory(KProcessAddress addr, size_t size, KMemoryState state_mask, KMemoryState state, KMemoryPermission perm_mask, KMemoryPermission perm, KMemoryAttribute attr_mask, KMemoryAttribute attr, KMemoryPermission new_perm, |