From dea1235e128d6295caefdd3d8b50dbd5271a0785 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 21 Jul 2020 00:56:13 -0700 Subject: [PATCH] kern: dump page table on user exception --- .../arch/arm64/kern_k_page_table_impl.hpp | 2 + .../arch/arm64/kern_k_process_page_table.hpp | 4 + .../mesosphere/kern_k_page_table_base.hpp | 5 + .../arch/arm64/kern_exception_handlers.cpp | 5 +- .../arch/arm64/kern_k_page_table_impl.cpp | 130 ++++++++++++++++++ 5 files changed, 145 insertions(+), 1 deletion(-) diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp index c1597bf22..e88d5cd19 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_page_table_impl.hpp @@ -106,6 +106,8 @@ namespace ams::kern::arch::arm64 { NOINLINE void InitializeForProcess(void *tb, KVirtualAddress start, KVirtualAddress end); L1PageTableEntry *Finalize(); + void Dump(uintptr_t start, size_t size) const; + bool BeginTraversal(TraversalEntry *out_entry, TraversalContext *out_context, KProcessAddress address) const; bool ContinueTraversal(TraversalEntry *out_entry, TraversalContext *context) const; 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 e95bfa4b7..bcfbfe7ea 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 @@ -160,6 +160,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.CleanupForIpcClient(address, size, dst_state); } + void DumpTable() const { + return this->page_table.DumpTable(); + } + bool GetPhysicalAddress(KPhysicalAddress *out, KProcessAddress address) const { return this->page_table.GetPhysicalAddress(out, address); } 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 d2a9c60a3..dd9372713 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_page_table_base.hpp @@ -317,6 +317,11 @@ namespace ams::kern { Result SetupForIpc(KProcessAddress *out_dst_addr, size_t size, KProcessAddress src_addr, KPageTableBase &src_page_table, KMemoryPermission test_perm, KMemoryState dst_state, bool send); Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state, KProcess *server_process); Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); + + void DumpTable() const { + KScopedLightLock lk(this->general_lock); + this->GetImpl().Dump(GetInteger(this->address_space_start), this->address_space_end - this->address_space_start); + } public: KProcessAddress GetAddressSpaceStart() const { return this->address_space_start; } KProcessAddress GetHeapRegionStart() const { return this->heap_region_start; } diff --git a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp index dac08a64a..383b6029e 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_exception_handlers.cpp @@ -46,7 +46,10 @@ namespace ams::kern::arch::arm64 { MESOSPHERE_LOG("PC = %016lx\n", context->pc); MESOSPHERE_LOG("SP = %016lx\n", context->sp); - MESOSPHERE_PANIC("Unhandled Exception in Supervisor Mode\n"); + /* Dump the page tables. */ + GetCurrentProcess().GetPageTable().DumpTable(); + + MESOSPHERE_PANIC("Unhandled Exception in User Mode\n"); const u64 ec = (esr >> 26) & 0x3F; switch (ec) { diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp index e232942ee..1ab6f0e91 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_page_table_impl.cpp @@ -292,4 +292,134 @@ namespace ams::kern::arch::arm64 { return false; } + void KPageTableImpl::Dump(uintptr_t start, size_t size) const { + /* If zero size, there's nothing to dump. */ + if (size == 0) { + return; + } + + /* Define extents. */ + const uintptr_t end = start + size; + const uintptr_t last = end - 1; + + MESOSPHERE_LOG("==== PAGE TABLE DUMP START (%012lx - %012lx) ====\n", start, last); + ON_SCOPE_EXIT { MESOSPHERE_LOG("==== PAGE TABLE DUMP END ====\n"); }; + + /* Define tracking variables. */ + bool unmapped = false; + uintptr_t unmapped_start = 0; + + /* Walk the table. */ + uintptr_t cur = start; + while (cur < end) { + /* Validate that we can read the actual entry. */ + const size_t l0_index = GetL0Index(cur); + const size_t l1_index = GetL1Index(cur); + if (this->is_kernel) { + /* Kernel entries must be accessed via TTBR1. */ + if ((l0_index != MaxPageTableEntries - 1) || (l1_index < MaxPageTableEntries - this->num_entries)) { + return; + } + } else { + /* User entries must be accessed with TTBR0. */ + if ((l0_index != 0) || l1_index >= this->num_entries) { + return; + } + } + + /* Try to get from l1 table. */ + const L1PageTableEntry *l1_entry = this->GetL1Entry(cur); + if (l1_entry->IsBlock()) { + /* Update. */ + cur = util::AlignDown(cur, L1BlockSize); + if (unmapped) { + unmapped = false; + MESOSPHERE_LOG("%012lx - %012lx: ---\n", unmapped_start, cur - 1); + } + + /* Print. */ + MESOSPHERE_LOG("%012lx: %016lx\n", cur, *reinterpret_cast(l1_entry)); + + /* Advance. */ + cur += L1BlockSize; + continue; + } else if (!l1_entry->IsTable()) { + /* Update. */ + cur = util::AlignDown(cur, L1BlockSize); + if (!unmapped) { + unmapped_start = cur; + unmapped = true; + } + + /* Advance. */ + cur += L1BlockSize; + continue; + } + + /* Try to get from l2 table. */ + const L2PageTableEntry *l2_entry = this->GetL2Entry(l1_entry, cur); + if (l2_entry->IsBlock()) { + /* Update. */ + cur = util::AlignDown(cur, L2BlockSize); + if (unmapped) { + unmapped = false; + MESOSPHERE_LOG("%012lx - %012lx: ---\n", unmapped_start, cur - 1); + } + + /* Print. */ + MESOSPHERE_LOG("%012lx: %016lx\n", cur, *reinterpret_cast(l2_entry)); + + /* Advance. */ + cur += L2BlockSize; + continue; + } else if (!l2_entry->IsTable()) { + /* Update. */ + cur = util::AlignDown(cur, L2BlockSize); + if (!unmapped) { + unmapped_start = cur; + unmapped = true; + } + + /* Advance. */ + cur += L2BlockSize; + continue; + } + + /* Try to get from l3 table. */ + const L3PageTableEntry *l3_entry = this->GetL3Entry(l2_entry, cur); + if (l3_entry->IsBlock()) { + /* Update. */ + cur = util::AlignDown(cur, L3BlockSize); + if (unmapped) { + unmapped = false; + MESOSPHERE_LOG("%012lx - %012lx: ---\n", unmapped_start, cur - 1); + } + + /* Print. */ + MESOSPHERE_LOG("%012lx: %016lx\n", cur, *reinterpret_cast(l3_entry)); + + /* Advance. */ + cur += L3BlockSize; + continue; + } else { + /* Update. */ + cur = util::AlignDown(cur, L3BlockSize); + if (!unmapped) { + unmapped_start = cur; + unmapped = true; + } + + /* Advance. */ + cur += L3BlockSize; + continue; + } + } + + /* Print the last unmapped range if necessary. */ + if (unmapped) { + MESOSPHERE_LOG("%012lx - %012lx: ---\n", unmapped_start, last); + } + } + + }