diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp index 3bf4d6cee..9ca342116 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_process_page_table.hpp @@ -136,10 +136,18 @@ namespace ams::kern::arch::arm64 { return m_page_table.ReadDebugMemory(buffer, address, size); } + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + return m_page_table.ReadDebugIoMemory(buffer, address, size); + } + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size) { return m_page_table.WriteDebugMemory(address, buffer, size); } + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + return m_page_table.WriteDebugIoMemory(address, buffer, size); + } + Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { return m_page_table.LockForDeviceAddressSpace(out, address, size, perm, is_aligned); } @@ -290,6 +298,10 @@ namespace ams::kern::arch::arm64 { KBlockInfoManager *GetBlockInfoManager() { return m_page_table.GetBlockInfoManager(); } + + KPageTableBase &GetBasePageTable() { + return m_page_table; + } }; } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp index 2eafc4c49..b63a9a4ee 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -288,6 +288,10 @@ namespace ams::kern { NOINLINE Result 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); + Result MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); + Result ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size); + Result WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size); + Result SetupForIpcClient(PageLinkedList *page_list, size_t *out_blocks_needed, KProcessAddress address, size_t size, KMemoryPermission test_perm, KMemoryState dst_state); Result SetupForIpcServer(KProcessAddress *out_addr, size_t size, KProcessAddress src_addr, KMemoryPermission test_perm, KMemoryState dst_state, KPageTableBase &src_page_table, bool send); void CleanupForIpcClientOnServerSetupFailure(PageLinkedList *page_list, KProcessAddress address, size_t size, KMemoryPermission prot_perm); @@ -341,7 +345,10 @@ namespace ams::kern { Result InvalidateProcessDataCache(KProcessAddress address, size_t size); Result ReadDebugMemory(void *buffer, KProcessAddress address, size_t size); + Result ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size); + Result WriteDebugMemory(KProcessAddress address, const void *buffer, size_t size); + Result WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size); Result LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned); Result UnlockForDeviceAddressSpace(KProcessAddress address, size_t size); diff --git a/libraries/libmesosphere/source/kern_k_debug_base.cpp b/libraries/libmesosphere/source/kern_k_debug_base.cpp index 7e9d61557..bd49cab2c 100644 --- a/libraries/libmesosphere/source/kern_k_debug_base.cpp +++ b/libraries/libmesosphere/source/kern_k_debug_base.cpp @@ -92,51 +92,7 @@ namespace ams::kern { R_TRY(target_pt.ReadDebugMemory(GetVoidPointer(buffer), cur_address, cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is readable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserRead) == KMemoryPermission_UserRead, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserRead)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(GetVoidPointer(buffer), GetVoidPointer(io_address), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.ReadDebugIoMemory(GetVoidPointer(buffer), cur_address, cur_size)); } /* Advance. */ @@ -185,51 +141,7 @@ namespace ams::kern { R_TRY(target_pt.WriteDebugMemory(cur_address, GetVoidPointer(buffer), cur_size)); } else { /* The memory is IO memory. */ - - /* Verify that the memory is writable. */ - R_UNLESS((info.GetPermission() & KMemoryPermission_UserReadWrite) == KMemoryPermission_UserReadWrite, svc::ResultInvalidAddress()); - - /* Get the physical address of the memory. */ - /* NOTE: Nintendo does not verify the result of this call. */ - KPhysicalAddress phys_addr; - target_pt.GetPhysicalAddress(std::addressof(phys_addr), cur_address); - - /* Map the address as IO in the current process. */ - R_TRY(debugger_pt.MapIo(util::AlignDown(GetInteger(phys_addr), PageSize), PageSize, KMemoryPermission_UserReadWrite)); - - /* Get the address of the newly mapped IO. */ - KProcessAddress io_address; - Result query_result = debugger_pt.QueryIoMapping(std::addressof(io_address), util::AlignDown(GetInteger(phys_addr), PageSize), PageSize); - MESOSPHERE_R_ASSERT(query_result); - R_TRY(query_result); - - /* Ensure we clean up the new mapping on scope exit. */ - ON_SCOPE_EXIT { MESOSPHERE_R_ABORT_UNLESS(debugger_pt.UnmapPages(util::AlignDown(GetInteger(io_address), PageSize), 1, KMemoryState_Io)); }; - - /* Adjust the io address for alignment. */ - io_address += (GetInteger(cur_address) & (PageSize - 1)); - - /* Get the readable size. */ - const size_t readable_size = std::min(cur_size, util::AlignDown(GetInteger(cur_address) + PageSize, PageSize) - GetInteger(cur_address)); - - /* Read the memory. */ - switch ((GetInteger(cur_address) | readable_size) & 3) { - case 0: - { - R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - case 2: - { - R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - default: - { - R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(io_address), GetVoidPointer(buffer), readable_size), svc::ResultInvalidPointer()); - } - break; - } + R_TRY(target_pt.WriteDebugIoMemory(cur_address, GetVoidPointer(buffer), cur_size)); } /* Advance. */ diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 5e5ff59d1..e503a3a37 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -997,7 +997,7 @@ namespace ams::kern { KMemoryInfo info; ams::svc::PageInfo page_info; - MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(&info, &page_info, candidate)); + MESOSPHERE_R_ABORT_UNLESS(this->QueryInfoImpl(std::addressof(info), std::addressof(page_info), candidate)); if (info.m_state != KMemoryState_Free) { continue; } if (!(region_start <= candidate)) { continue; } @@ -1355,7 +1355,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissions, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1413,7 +1413,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, operation, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, new_state, new_perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); /* Ensure cache coherency, if we're setting pages as executable. */ if (is_x) { @@ -1461,7 +1461,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, Null, false, properties, OperationType_ChangePermissionsAndRefresh, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, old_state, old_perm, new_attr, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_None); return ResultSuccess(); } @@ -1701,10 +1701,13 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + Result KPageTableBase::MapIoImpl(KProcessAddress *out, PageLinkedList *page_list, KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); MESOSPHERE_ASSERT(util::IsAligned(GetInteger(phys_addr), PageSize)); MESOSPHERE_ASSERT(util::IsAligned(size, PageSize)); MESOSPHERE_ASSERT(size > 0); + R_UNLESS(phys_addr < phys_addr + size, svc::ResultInvalidAddress()); const size_t num_pages = size / PageSize; const KPhysicalAddress last = phys_addr + size - 1; @@ -1740,9 +1743,6 @@ namespace ams::kern { region = region->GetNext(); }; - /* Lock the table. */ - KScopedLightLock lk(m_general_lock); - /* Select an address to map at. */ KProcessAddress addr = Null; const size_t phys_alignment = std::min(std::min(GetInteger(phys_addr) & -GetInteger(phys_addr), size & -size), MaxPhysicalMapAlignment); @@ -1763,6 +1763,20 @@ namespace ams::kern { MESOSPHERE_ASSERT(this->CanContain(addr, size, KMemoryState_Io)); MESOSPHERE_R_ASSERT(this->CheckMemoryState(addr, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); + /* Perform mapping operation. */ + const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; + R_TRY(this->Operate(page_list, addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + + /* Set the output address. */ + *out = addr; + + return ResultSuccess(); + } + + Result KPageTableBase::MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm) { + /* Lock the table. */ + KScopedLightLock lk(m_general_lock); + /* Create an update allocator. */ Result allocator_result; KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), m_memory_block_slab_manager); @@ -1771,12 +1785,12 @@ namespace ams::kern { /* We're going to perform an update, so create a helper. */ KScopedPageTableUpdater updater(this); - /* Perform mapping operation. */ - const KPageProperties properties = { perm, true, false, DisableMergeAttribute_DisableHead }; - R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); + /* Map the io memory. */ + KProcessAddress addr; + R_TRY(this->MapIoImpl(std::addressof(addr), updater.GetPageList(), phys_addr, size, perm)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Io, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, size / PageSize, KMemoryState_Io, perm, KMemoryAttribute_Locked, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1844,7 +1858,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), addr, num_pages, phys_addr, true, properties, OperationType_Map, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, KMemoryState_Static, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -1961,7 +1975,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, unmap_properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -1996,7 +2010,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + 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; @@ -2031,7 +2045,7 @@ namespace ams::kern { R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); + m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_Normal, KMemoryBlockDisableMergeAttribute_None); /* We successfully mapped the pages. */ return ResultSuccess(); @@ -2068,7 +2082,7 @@ namespace ams::kern { R_TRY(this->Operate(updater.GetPageList(), address, num_pages, Null, false, properties, OperationType_Unmap, false)); /* Update the blocks. */ - m_memory_block_manager.Update(&allocator, address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); + m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState_Free, KMemoryPermission_None, KMemoryAttribute_None, KMemoryBlockDisableMergeAttribute_None, KMemoryBlockDisableMergeAttribute_Normal); return ResultSuccess(); } @@ -2337,6 +2351,188 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::ReadIoMemoryImpl(void *buffer, KPhysicalAddress phys_addr, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserRead)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress read_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(read_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::ReadIoMemory32Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::ReadIoMemory16Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::ReadIoMemory8Bit(buffer, GetVoidPointer(read_addr), size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteIoMemoryImpl(KPhysicalAddress phys_addr, const void *buffer, size_t size) { + /* Check pre-conditions. */ + MESOSPHERE_ASSERT(this->IsLockedByCurrentThread()); + + /* Determine the mapping extents. */ + const KPhysicalAddress map_start = util::AlignDown(GetInteger(phys_addr), PageSize); + const KPhysicalAddress map_end = util::AlignUp(GetInteger(phys_addr) + size, PageSize); + const size_t map_size = map_end - map_start; + + /* We're going to perform an update, so create a helper. */ + KScopedPageTableUpdater updater(this); + + /* Temporarily map the io memory. */ + KProcessAddress io_addr; + R_TRY(this->MapIoImpl(std::addressof(io_addr), updater.GetPageList(), map_start, map_size, KMemoryPermission_UserReadWrite)); + + /* Ensure we unmap the io memory when we're done with it. */ + ON_SCOPE_EXIT { + const KPageProperties unmap_properties = { KMemoryPermission_None, false, false, DisableMergeAttribute_None }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), io_addr, map_size / PageSize, Null, false, unmap_properties, OperationType_Unmap, true)); + }; + + /* Read the memory. */ + const KProcessAddress write_addr = io_addr + (GetInteger(phys_addr) & (PageSize - 1)); + switch ((GetInteger(write_addr) | size) & 3) { + case 0: + { + R_UNLESS(UserspaceAccess::WriteIoMemory32Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + case 2: + { + R_UNLESS(UserspaceAccess::WriteIoMemory16Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + default: + { + R_UNLESS(UserspaceAccess::WriteIoMemory8Bit(GetVoidPointer(write_addr), buffer, size), svc::ResultInvalidPointer()); + } + break; + } + + return ResultSuccess(); + } + + Result KPageTableBase::ReadDebugIoMemory(void *buffer, KProcessAddress address, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Get the table locks. */ + KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; + KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; + + /* Lock the first lock. */ + KScopedLightLock lk0(lock_0); + + /* If necessary, lock the second lock. */ + std::optional lk1; + if (std::addressof(lock_0) != std::addressof(lock_1)) { + lk1.emplace(lock_1); + } + + /* Check that the desired range is readable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + u8 *dst = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.ReadIoMemoryImpl(dst, phys_addr, cur_size)); + + /* Advance. */ + address += cur_size; + dst += cur_size; + } + + return ResultSuccess(); + } + + Result KPageTableBase::WriteDebugIoMemory(KProcessAddress address, const void *buffer, size_t size) { + /* Lightly validate the range before doing anything else. */ + R_UNLESS(this->Contains(address, size), svc::ResultInvalidCurrentMemory()); + + /* We need to lock both this table, and the current process's table, so set up some aliases. */ + KPageTableBase &src_page_table = *this; + KPageTableBase &dst_page_table = GetCurrentProcess().GetPageTable().GetBasePageTable(); + + /* Get the table locks. */ + KLightLock &lock_0 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? src_page_table.m_general_lock : dst_page_table.m_general_lock; + KLightLock &lock_1 = (reinterpret_cast(std::addressof(src_page_table)) <= reinterpret_cast(std::addressof(dst_page_table))) ? dst_page_table.m_general_lock : src_page_table.m_general_lock; + + /* Lock the first lock. */ + KScopedLightLock lk0(lock_0); + + /* If necessary, lock the second lock. */ + std::optional lk1; + if (std::addressof(lock_0) != std::addressof(lock_1)) { + lk1.emplace(lock_1); + } + + /* Check that the desired range is writable io memory. */ + R_TRY(this->CheckMemoryStateContiguous(address, size, KMemoryState_All, KMemoryState_Io, KMemoryPermission_UserReadWrite, KMemoryPermission_UserReadWrite, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Read the memory. */ + const u8 *src = static_cast(buffer); + const KProcessAddress last_address = address + size - 1; + while (address <= last_address) { + /* Get the current physical address. */ + KPhysicalAddress phys_addr; + MESOSPHERE_ABORT_UNLESS(src_page_table.GetPhysicalAddress(std::addressof(phys_addr), address)); + + /* Determine the current read size. */ + const size_t cur_size = std::min(last_address - address + 1, util::AlignDown(GetInteger(address) + PageSize, PageSize) - GetInteger(address)); + + /* Read. */ + R_TRY(dst_page_table.WriteIoMemoryImpl(phys_addr, src, cur_size)); + + /* Advance. */ + address += cur_size; + src += cur_size; + } + + return ResultSuccess(); + } + Result KPageTableBase::LockForDeviceAddressSpace(KPageGroup *out, KProcessAddress address, size_t size, KMemoryPermission perm, bool is_aligned) { /* Lightly validate the range before doing anything else. */ const size_t num_pages = size / PageSize;