From 9503aae522ef28ee1d5bceb5cf5f4b6c015e9052 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 9 Jul 2020 18:12:04 -0700 Subject: [PATCH] kern: implement SvcMapMemory --- .../arch/arm64/kern_k_process_page_table.hpp | 6 +- .../mesosphere/kern_k_page_table_base.hpp | 3 +- .../source/kern_k_page_table_base.cpp | 62 ++++++++++++++++++- .../source/svc/kern_svc_info.cpp | 8 +++ .../source/svc/kern_svc_memory.cpp | 31 +++++++++- 5 files changed, 105 insertions(+), 5 deletions(-) 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 cd9bb9c3c..a04e6d958 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 @@ -56,7 +56,11 @@ namespace ams::kern::arch::arm64 { return this->page_table.QueryInfo(out_info, out_page_info, addr); } - Result UnmapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) { + Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { + return this->page_table.MapMemory(dst_address, src_address, size); + } + + Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { return this->page_table.UnmapMemory(dst_address, src_address, size); } 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 65d434dba..f9e800dab 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -252,7 +252,8 @@ namespace ams::kern { Result SetHeapSize(KProcessAddress *out, size_t size); Result SetMaxHeapSize(size_t size); Result QueryInfo(KMemoryInfo *out_info, ams::svc::PageInfo *out_page_info, KProcessAddress addr) const; - Result UnmapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size); + Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); + Result UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size); Result MapIo(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); Result MapStatic(KPhysicalAddress phys_addr, size_t size, KMemoryPermission perm); Result MapRegion(KMemoryRegionType region_type, KMemoryPermission perm); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index e005b9c8d..9d4fc98fe 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -498,7 +498,67 @@ namespace ams::kern { return ResultSuccess(); } - Result KPageTableBase::UnmapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) { + Result KPageTableBase::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + /* Validate that the source address's state is valid. */ + KMemoryState src_state; + R_TRY(this->CheckMemoryState(std::addressof(src_state), nullptr, nullptr, src_address, size, KMemoryState_FlagCanAlias, KMemoryState_FlagCanAlias, KMemoryPermission_All, KMemoryPermission_UserReadWrite, KMemoryAttribute_All, KMemoryAttribute_None)); + + /* Validate that the dst address's state is valid. */ + R_TRY(this->CheckMemoryState(dst_address, size, KMemoryState_All, KMemoryState_Free, KMemoryPermission_None, KMemoryPermission_None, KMemoryAttribute_None, KMemoryAttribute_None)); + + /* Create an update allocator for the source. */ + KMemoryBlockManagerUpdateAllocator src_allocator(this->memory_block_slab_manager); + R_TRY(src_allocator.GetResult()); + + /* Create an update allocator for the destination. */ + KMemoryBlockManagerUpdateAllocator dst_allocator(this->memory_block_slab_manager); + R_TRY(dst_allocator.GetResult()); + + /* Map the memory. */ + { + /* 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(this->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. */ + const KMemoryPermission new_src_perm = static_cast(KMemoryPermission_KernelRead | KMemoryPermission_NotMapped); + const KMemoryAttribute new_src_attr = static_cast(KMemoryAttribute_AnyLocked | KMemoryAttribute_Locked); + const KPageProperties src_properties = { new_src_perm, false, false, false }; + R_TRY(this->Operate(updater.GetPageList(), src_address, num_pages, Null, false, src_properties, OperationType_ChangePermissions, false)); + + /* Ensure that we unprotect the source pages on failure. */ + auto unprot_guard = SCOPE_GUARD { + const KPageProperties unprotect_properties = { KMemoryPermission_UserReadWrite, false, false, false }; + MESOSPHERE_R_ABORT_UNLESS(this->Operate(updater.GetPageList(), src_address, num_pages, Null, false, unprotect_properties, OperationType_ChangePermissions, true)); + }; + + /* Map the alias pages. */ + const KPageProperties dst_map_properties = { KMemoryPermission_UserReadWrite, false, false, false }; + R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, false)); + + /* We successfully mapped the alias pages, so we don't need to unprotect the src pages on failure. */ + unprot_guard.Cancel(); + + /* Apply the memory block updates. */ + this->memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, new_src_perm, new_src_attr); + this->memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, KMemoryState_None, KMemoryPermission_UserReadWrite, KMemoryAttribute_None); + } + + return ResultSuccess(); + } + + Result KPageTableBase::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { /* Lock the table. */ KScopedLightLock lk(this->general_lock); diff --git a/libraries/libmesosphere/source/svc/kern_svc_info.cpp b/libraries/libmesosphere/source/svc/kern_svc_info.cpp index 8f7a560e6..dc27cc869 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_info.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_info.cpp @@ -26,6 +26,8 @@ namespace ams::kern::svc { ON_SCOPE_EXIT{ MESOSPHERE_LOG("GetInfo returned %016lx\n", *out); }; switch (info_type) { + case ams::svc::InfoType_CoreMask: + case ams::svc::InfoType_PriorityMask: case ams::svc::InfoType_AliasRegionAddress: case ams::svc::InfoType_AliasRegionSize: case ams::svc::InfoType_HeapRegionAddress: @@ -47,6 +49,12 @@ namespace ams::kern::svc { R_UNLESS(process.IsNotNull(), svc::ResultInvalidHandle()); switch (info_type) { + case ams::svc::InfoType_CoreMask: + *out = process->GetCoreMask(); + break; + case ams::svc::InfoType_PriorityMask: + *out = process->GetPriorityMask(); + break; case ams::svc::InfoType_AliasRegionAddress: *out = GetInteger(process->GetPageTable().GetAliasRegionStart()); break; diff --git a/libraries/libmesosphere/source/svc/kern_svc_memory.cpp b/libraries/libmesosphere/source/svc/kern_svc_memory.cpp index 3de08d0c8..dc6b643c1 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_memory.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_memory.cpp @@ -21,6 +21,33 @@ namespace ams::kern::svc { namespace { + Result MapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) { + /* Log the call parameters for debugging. */ + MESOSPHERE_LOG("MapMemory(%zx, %zx, %zx)\n", dst_address, src_address, size); + + /* Validate that addresses are page aligned. */ + R_UNLESS(util::IsAligned(dst_address, PageSize), svc::ResultInvalidAddress()); + R_UNLESS(util::IsAligned(src_address, PageSize), svc::ResultInvalidAddress()); + + /* Validate that size is positive and page aligned. */ + R_UNLESS(size > 0, svc::ResultInvalidSize()); + R_UNLESS(util::IsAligned(size, PageSize), svc::ResultInvalidSize()); + + /* Ensure that neither mapping overflows. */ + R_UNLESS(src_address < src_address + size, svc::ResultInvalidCurrentMemory()); + R_UNLESS(dst_address < dst_address + size, svc::ResultInvalidCurrentMemory()); + + /* Get the page table we're operating on. */ + auto &page_table = GetCurrentProcess().GetPageTable(); + + /* Ensure that the memory we're mapping is in range. */ + R_UNLESS(page_table.Contains(src_address, size), svc::ResultInvalidCurrentMemory()); + R_UNLESS(page_table.CanContain(dst_address, size, KMemoryState_Stack), svc::ResultInvalidMemoryRegion()); + + /* Map the memory. */ + return page_table.MapMemory(dst_address, src_address, size); + } + Result UnmapMemory(uintptr_t dst_address, uintptr_t src_address, size_t size) { /* Log the call parameters for debugging. */ MESOSPHERE_LOG("UnmapMemory(%zx, %zx, %zx)\n", dst_address, src_address, size); @@ -61,7 +88,7 @@ namespace ams::kern::svc { } Result MapMemory64(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcMapMemory64 was called."); + return MapMemory(dst_address, src_address, size); } Result UnmapMemory64(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) { @@ -79,7 +106,7 @@ namespace ams::kern::svc { } Result MapMemory64From32(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcMapMemory64From32 was called."); + return MapMemory(dst_address, src_address, size); } Result UnmapMemory64From32(ams::svc::Address dst_address, ams::svc::Address src_address, ams::svc::Size size) {