From 18698bf1d39f07354220754254fd49318a951b84 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 13 Jul 2020 13:24:32 -0700 Subject: [PATCH] kern: add SvcQueryIoMapping (NOTE: pre-10.x, ABI needs update) --- .../arch/arm64/kern_k_process_page_table.hpp | 8 +++ .../mesosphere/kern_k_memory_layout.hpp | 47 +++++++++++++ .../mesosphere/kern_k_page_table_base.hpp | 5 ++ .../source/kern_k_page_table_base.cpp | 70 +++++++++++++++++++ .../svc/kern_svc_address_translation.cpp | 68 +++++++++++++++++- .../include/vapours/svc/svc_types_common.hpp | 8 +++ 6 files changed, 204 insertions(+), 2 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 8d73dda1b..e3162be12 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,6 +56,14 @@ namespace ams::kern::arch::arm64 { return this->page_table.QueryInfo(out_info, out_page_info, addr); } + Result QueryStaticMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { + return this->page_table.QueryStaticMapping(out, address, size); + } + + Result QueryIoMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { + return this->page_table.QueryIoMapping(out, address, size); + } + Result MapMemory(KProcessAddress dst_address, KProcessAddress src_address, size_t size) { return this->page_table.MapMemory(dst_address, src_address, size); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp index 721d575ba..690679a3b 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_memory_layout.hpp @@ -268,6 +268,16 @@ namespace ams::kern { MESOSPHERE_INIT_ABORT(); } + iterator TryFindFirstRegionByType(u32 type_id) { + for (auto it = this->begin(); it != this->end(); it++) { + if (it->GetType() == type_id) { + return it; + } + } + + return this->end(); + } + iterator FindFirstDerivedRegion(u32 type_id) { for (auto it = this->begin(); it != this->end(); it++) { if (it->IsDerivedFrom(type_id)) { @@ -277,6 +287,16 @@ namespace ams::kern { MESOSPHERE_INIT_ABORT(); } + iterator TryFindFirstDerivedRegion(u32 type_id) { + for (auto it = this->begin(); it != this->end(); it++) { + if (it->IsDerivedFrom(type_id)) { + return it; + } + } + + return this->end(); + } + DerivedRegionExtents GetDerivedRegionExtents(u32 type_id) const { DerivedRegionExtents extents; @@ -504,6 +524,33 @@ namespace ams::kern { return *GetVirtualLinearMemoryRegionTree().FindContainingRegion(GetInteger(address)); } + static NOINLINE const KMemoryRegion *TryGetKernelTraceBufferRegion() { + auto &tree = GetPhysicalMemoryRegionTree(); + if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_KernelTraceBuffer); it != tree.end()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + static NOINLINE const KMemoryRegion *TryGetOnMemoryBootImageRegion() { + auto &tree = GetPhysicalMemoryRegionTree(); + if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_OnMemoryBootImage); it != tree.end()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + + static NOINLINE const KMemoryRegion *TryGetDTBRegion() { + auto &tree = GetPhysicalMemoryRegionTree(); + if (KMemoryRegionTree::const_iterator it = tree.TryFindFirstDerivedRegion(KMemoryRegionType_DTB); it != tree.end()) { + return std::addressof(*it); + } else { + return nullptr; + } + } + static NOINLINE bool IsHeapPhysicalAddress(const KMemoryRegion **out, KPhysicalAddress address, const KMemoryRegion *hint = nullptr) { auto &tree = GetPhysicalLinearMemoryRegionTree(); KMemoryRegionTree::const_iterator it = tree.end(); 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 744541e3a..de6f50e3f 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -248,6 +248,9 @@ namespace ams::kern { Result UnlockMemory(KProcessAddress addr, size_t size, u32 state_mask, u32 state, u32 perm_mask, u32 perm, u32 attr_mask, u32 attr, KMemoryPermission new_perm, u32 lock_attr, const KPageGroup *pg); Result QueryInfoImpl(KMemoryInfo *out_info, ams::svc::PageInfo *out_page, KProcessAddress address) const; + + Result QueryMappingImpl(KProcessAddress *out, KPhysicalAddress address, size_t size, KMemoryState state) const; + Result AllocateAndMapPagesImpl(PageLinkedList *page_list, KProcessAddress address, size_t num_pages, const KPageProperties properties); Result MapPageGroupImpl(PageLinkedList *page_list, KProcessAddress address, const KPageGroup &pg, const KPageProperties properties, bool reuse_ll); @@ -271,6 +274,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 QueryStaticMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { return this->QueryMappingImpl(out, address, size, KMemoryState_Static); } + Result QueryIoMapping(KProcessAddress *out, KPhysicalAddress address, size_t size) const { return this->QueryMappingImpl(out, address, size, KMemoryState_Io); } 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); diff --git a/libraries/libmesosphere/source/kern_k_page_table_base.cpp b/libraries/libmesosphere/source/kern_k_page_table_base.cpp index 3b59727a7..535e5ef56 100644 --- a/libraries/libmesosphere/source/kern_k_page_table_base.cpp +++ b/libraries/libmesosphere/source/kern_k_page_table_base.cpp @@ -585,6 +585,76 @@ namespace ams::kern { return ResultSuccess(); } + Result KPageTableBase::QueryMappingImpl(KProcessAddress *out, KPhysicalAddress address, size_t size, KMemoryState state) const { + MESOSPHERE_ASSERT(!this->IsLockedByCurrentThread()); + MESOSPHERE_ASSERT(out != nullptr); + + const KProcessAddress region_start = this->GetRegionAddress(state); + const size_t region_size = this->GetRegionSize(state); + + /* Check that the address/size are potentially valid. */ + R_UNLESS((address < address + size), svc::ResultNotFound()); + + /* Lock the table. */ + KScopedLightLock lk(this->general_lock); + + auto &impl = this->GetImpl(); + + /* Begin traversal. */ + TraversalContext context; + TraversalEntry cur_entry = {}; + bool cur_valid = false; + TraversalEntry next_entry; + bool next_valid; + size_t tot_size = false; + + next_valid = impl.BeginTraversal(std::addressof(next_entry), std::addressof(context), region_start); + next_entry.block_size = (next_entry.block_size - (GetInteger(address) & (next_entry.block_size - 1))); + + /* Iterate, looking for entry. */ + while (true) { + if ((!next_valid && !cur_valid) || (next_valid && cur_valid && next_entry.phys_addr == cur_entry.phys_addr + cur_entry.block_size)) { + cur_entry.block_size += next_entry.block_size; + } else { + if (cur_valid && cur_entry.phys_addr <= address && address + size <= cur_entry.phys_addr + cur_entry.block_size) { + /* Check if this region is valid. */ + const KProcessAddress mapped_address = (region_start + tot_size) + (address - cur_entry.phys_addr); + if (R_SUCCEEDED(this->CheckMemoryState(mapped_address, size, KMemoryState_All, state, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None))) { + /* It is! */ + *out = mapped_address; + return ResultSuccess(); + } + } + + /* Update tracking variables. */ + tot_size += cur_entry.block_size; + cur_entry = next_entry; + cur_valid = next_valid; + } + + if (cur_entry.block_size + tot_size >= region_size) { + break; + } + + next_valid = impl.ContinueTraversal(std::addressof(next_entry), std::addressof(context)); + } + + /* Check the last entry. */ + R_UNLESS(cur_valid, svc::ResultNotFound()); + R_UNLESS(cur_entry.phys_addr <= address, svc::ResultNotFound()); + R_UNLESS(address + size <= cur_entry.phys_addr + cur_entry.block_size, svc::ResultNotFound()); + + /* Check if the last region is valid. */ + const KProcessAddress mapped_address = (region_start + tot_size) + (address - cur_entry.phys_addr); + R_TRY_CATCH(this->CheckMemoryState(mapped_address, size, KMemoryState_All, state, KMemoryPermission_UserRead, KMemoryPermission_UserRead, KMemoryAttribute_None, KMemoryAttribute_None)) { + R_CONVERT_ALL(svc::ResultNotFound()); + } R_END_TRY_CATCH; + + /* We found the region. */ + *out = mapped_address; + return ResultSuccess(); + } + Result KPageTableBase::MapMemory(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_address_translation.cpp b/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp index ce2eb87ea..c68eaa39b 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_address_translation.cpp @@ -21,7 +21,69 @@ namespace ams::kern::svc { namespace { + Result QueryIoMapping(uintptr_t *out_address, size_t *out_size, uint64_t phys_addr, size_t size) { + /* Declare variables we'll populate. */ + KProcessAddress found_address = Null; + size_t found_size = 0; + /* Get reference to page table. */ + auto &pt = GetCurrentProcess().GetPageTable(); + + /* Check whether the address is aligned. */ + const bool aligned = util::IsAligned(phys_addr, PageSize); + + if (aligned) { + /* The size must be non-zero. */ + R_UNLESS(size > 0, svc::ResultInvalidSize()); + + /* The request must not overflow. */ + R_UNLESS((phys_addr < phys_addr + size), svc::ResultNotFound()); + + /* Query the mapping. */ + R_TRY(pt.QueryIoMapping(std::addressof(found_address), phys_addr, size)); + + /* Use the size as the found size. */ + found_size = size; + } else { + /* TODO: Older kernel ABI compatibility. */ + /* Newer kernel only allows unaligned addresses when they're special enum members. */ + R_UNLESS(phys_addr < PageSize, svc::ResultNotFound()); + + /* Try to find the memory region. */ + const KMemoryRegion *region; + switch (static_cast(phys_addr)) { + case ams::svc::MemoryRegionType_KernelTraceBuffer: + region = KMemoryLayout::TryGetKernelTraceBufferRegion(); + break; + case ams::svc::MemoryRegionType_OnMemoryBootImage: + region = KMemoryLayout::TryGetOnMemoryBootImageRegion(); + break; + case ams::svc::MemoryRegionType_DTB: + region = KMemoryLayout::TryGetDTBRegion(); + break; + default: + region = nullptr; + break; + } + + /* Ensure that we found the region. */ + R_UNLESS(region != nullptr, svc::ResultNotFound()); + + R_TRY(pt.QueryStaticMapping(std::addressof(found_address), region->GetAddress(), region->GetSize())); + found_size = region->GetSize(); + } + + /* We succeeded. */ + MESOSPHERE_ASSERT(found_address != Null); + MESOSPHERE_ASSERT(found_size != 0); + if (out_address != nullptr) { + *out_address = GetInteger(found_address); + } + if (out_size != nullptr) { + *out_size = found_size; + } + return ResultSuccess(); + } } @@ -32,7 +94,8 @@ namespace ams::kern::svc { } Result QueryIoMapping64(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcQueryIoMapping64 was called."); + static_assert(sizeof(*out_address) == sizeof(uintptr_t)); + return QueryIoMapping(reinterpret_cast(out_address), nullptr, physical_address, size); } /* ============================= 64From32 ABI ============================= */ @@ -42,7 +105,8 @@ namespace ams::kern::svc { } Result QueryIoMapping64From32(ams::svc::Address *out_address, ams::svc::PhysicalAddress physical_address, ams::svc::Size size) { - MESOSPHERE_PANIC("Stubbed SvcQueryIoMapping64From32 was called."); + static_assert(sizeof(*out_address) == sizeof(uintptr_t)); + return QueryIoMapping(reinterpret_cast(out_address), nullptr, physical_address, size); } } diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index 8e7a5049e..48e7941de 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -120,6 +120,14 @@ namespace ams::svc { u32 flags; }; + enum MemoryRegionType { + MemoryRegionType_None = 0, + MemoryRegionType_KernelTraceBuffer = 1, + MemoryRegionType_OnMemoryBootImage = 2, + MemoryRegionType_DTB = 3, + MemoryRegionType_Count, + }; + /* Info Types. */ enum InfoType : u32 { InfoType_CoreMask = 0,