From af259eabdad45d8de74d97e7a78bce2f395c2cf6 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 9 Dec 2020 22:49:04 -0800 Subject: [PATCH] kern: implement thread call stack debug --- .../mesosphere/arch/arm64/kern_k_debug.hpp | 5 +- .../arch/arm64/kern_k_process_page_table.hpp | 4 + .../include/mesosphere/kern_k_dump_object.hpp | 3 + .../include/mesosphere/kern_k_thread.hpp | 22 +- .../source/arch/arm64/kern_k_debug.cpp | 537 ++++++++++++++++++ .../source/kern_k_dump_object.cpp | 44 ++ libraries/libmesosphere/source/kern_panic.cpp | 23 +- .../source/svc/kern_svc_exception.cpp | 10 +- .../source/svc/kern_svc_kernel_debug.cpp | 7 + .../include/vapours/svc/svc_types_common.hpp | 3 +- .../kernel_ldr/source/kern_loader_panic.cpp | 31 + 11 files changed, 657 insertions(+), 32 deletions(-) create mode 100644 mesosphere/kernel_ldr/source/kern_loader_panic.cpp diff --git a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp index b9735db84..4dc3f9682 100644 --- a/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp +++ b/libraries/libmesosphere/include/mesosphere/arch/arm64/kern_k_debug.hpp @@ -44,6 +44,9 @@ namespace ams::kern::arch::arm64 { static uintptr_t GetProgramCounter(const KThread &thread); static void SetPreviousProgramCounter(); + static void PrintRegister(KThread *thread = nullptr); + static void PrintBacktrace(KThread *thread = nullptr); + static Result BreakIfAttached(ams::svc::BreakReason break_reason, uintptr_t address, size_t size); static Result SetHardwareBreakPoint(ams::svc::HardwareBreakPointRegisterName name, u64 flags, u64 value); @@ -61,8 +64,6 @@ namespace ams::kern::arch::arm64 { } } } - - /* TODO: This is a placeholder definition. */ }; } 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 52d56618b..48a9e8081 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 @@ -273,6 +273,10 @@ namespace ams::kern::arch::arm64 { return this->page_table.GetHeapPhysicalAddress(address); } + KVirtualAddress GetHeapVirtualAddress(KPhysicalAddress address) const { + return this->page_table.GetHeapVirtualAddress(address); + } + KBlockInfoManager *GetBlockInfoManager() { return this->page_table.GetBlockInfoManager(); } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp index ab7cf9a0a..6ef29e216 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_dump_object.hpp @@ -22,4 +22,7 @@ namespace ams::kern::KDumpObject { void DumpThread(); void DumpThread(u64 thread_id); + void DumpThreadCallStack(); + void DumpThreadCallStack(u64 thread_id); + } diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp index ce3791061..6f9fb4b9c 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_thread.hpp @@ -49,11 +49,11 @@ namespace ams::kern { }; enum SuspendType : u32 { - SuspendType_Process = 0, - SuspendType_Thread = 1, - SuspendType_Debug = 2, - SuspendType_Unk3 = 3, - SuspendType_Init = 4, + SuspendType_Process = 0, + SuspendType_Thread = 1, + SuspendType_Debug = 2, + SuspendType_Backtrace = 3, + SuspendType_Init = 4, SuspendType_Count, }; @@ -67,13 +67,13 @@ namespace ams::kern { ThreadState_SuspendShift = 4, ThreadState_Mask = (1 << ThreadState_SuspendShift) - 1, - ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)), - ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)), - ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)), - ThreadState_Unk3Suspended = (1 << (SuspendType_Unk3 + ThreadState_SuspendShift)), - ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)), + ThreadState_ProcessSuspended = (1 << (SuspendType_Process + ThreadState_SuspendShift)), + ThreadState_ThreadSuspended = (1 << (SuspendType_Thread + ThreadState_SuspendShift)), + ThreadState_DebugSuspended = (1 << (SuspendType_Debug + ThreadState_SuspendShift)), + ThreadState_BacktraceSuspended = (1 << (SuspendType_Backtrace + ThreadState_SuspendShift)), + ThreadState_InitSuspended = (1 << (SuspendType_Init + ThreadState_SuspendShift)), - ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift, + ThreadState_SuspendFlagMask = ((1 << SuspendType_Count) - 1) << ThreadState_SuspendShift, }; enum DpcFlag : u32 { diff --git a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp index b6286a02a..415e38c65 100644 --- a/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp +++ b/libraries/libmesosphere/source/arch/arm64/kern_k_debug.cpp @@ -384,4 +384,541 @@ namespace ams::kern::arch::arm64 { #undef MESOSPHERE_SET_HW_WATCH_POINT #undef MESOSPHERE_SET_HW_BREAK_POINT + void KDebug::PrintRegister(KThread *thread) { + #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) + { + /* Treat no thread as current thread. */ + if (thread == nullptr) { + thread = GetCurrentThreadPointer(); + } + + /* Get the exception context. */ + KExceptionContext *e_ctx = GetExceptionContext(thread); + + /* Get the owner process. */ + if (auto *process = thread->GetOwnerProcess(); process != nullptr) { + /* Lock the owner process. */ + KScopedLightLock state_lk(process->GetStateLock()); + KScopedLightLock list_lk(process->GetListLock()); + + /* Suspend all the process's threads. */ + { + KScopedSchedulerLock sl; + + auto end = process->GetThreadList().end(); + for (auto it = process->GetThreadList().begin(); it != end; ++it) { + if (std::addressof(*it) != GetCurrentThreadPointer()) { + it->RequestSuspend(KThread::SuspendType_Backtrace); + } + } + } + + /* Print the registers. */ + MESOSPHERE_RELEASE_LOG("Registers\n"); + if ((e_ctx->psr & 0x10) == 0) { + /* 64-bit thread. */ + for (auto i = 0; i < 31; ++i) { + MESOSPHERE_RELEASE_LOG(" X[%2d]: 0x%016lx\n", i, e_ctx->x[i]); + } + MESOSPHERE_RELEASE_LOG(" SP: 0x%016lx\n", e_ctx->sp); + MESOSPHERE_RELEASE_LOG(" PC: 0x%016lx\n", e_ctx->pc - sizeof(u32)); + MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr); + MESOSPHERE_RELEASE_LOG(" TPIDR_EL0: 0x%016lx\n", e_ctx->tpidr); + } else { + /* 32-bit thread. */ + for (auto i = 0; i < 13; ++i) { + MESOSPHERE_RELEASE_LOG(" R[%2d]: 0x%08x\n", i, static_cast(e_ctx->x[i])); + } + MESOSPHERE_RELEASE_LOG(" SP: 0x%08x\n", static_cast(e_ctx->x[13])); + MESOSPHERE_RELEASE_LOG(" LR: 0x%08x\n", static_cast(e_ctx->x[14])); + MESOSPHERE_RELEASE_LOG(" PC: 0x%08x\n", static_cast(e_ctx->pc) - static_cast((e_ctx->psr & 0x20) ? sizeof(u16) : sizeof(u32))); + MESOSPHERE_RELEASE_LOG(" PSR: 0x%08x\n", e_ctx->psr); + MESOSPHERE_RELEASE_LOG(" TPIDR: 0x%08x\n", static_cast(e_ctx->tpidr)); + } + + /* Resume the threads that we suspended. */ + { + KScopedSchedulerLock sl; + + auto end = process->GetThreadList().end(); + for (auto it = process->GetThreadList().begin(); it != end; ++it) { + if (std::addressof(*it) != GetCurrentThreadPointer()) { + it->Resume(KThread::SuspendType_Backtrace); + } + } + } + } + } + #else + MESOSPHERE_UNUSED(thread); + #endif + } + + #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) + namespace { + + bool IsHeapPhysicalAddress(KPhysicalAddress phys_addr) { + const KMemoryRegion *cached = nullptr; + return KMemoryLayout::IsHeapPhysicalAddress(cached, phys_addr); + } + + template + bool ReadValue(T *out, KProcess *process, uintptr_t address) { + KPhysicalAddress phys_addr; + KMemoryInfo mem_info; + ams::svc::PageInfo page_info; + + if (!util::IsAligned(address, sizeof(T))) { + return false; + } + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), address))) { + return false; + } + if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) { + return false; + } + if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), address)) { + return false; + } + if (!IsHeapPhysicalAddress(phys_addr)) { + return false; + } + + *out = *GetPointer(process->GetPageTable().GetHeapVirtualAddress(phys_addr)); + return true; + } + + bool GetModuleName(char *dst, size_t dst_size, KProcess *process, uintptr_t base_address) { + /* Locate .rodata. */ + KMemoryInfo mem_info; + ams::svc::PageInfo page_info; + KMemoryState mem_state = KMemoryState_None; + + while (true) { + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) { + return false; + } + if (mem_state == KMemoryState_None) { + mem_state = mem_info.GetState(); + if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) { + return false; + } + } + if (mem_info.GetState() != mem_state) { + return false; + } + if (mem_info.GetPermission() == KMemoryPermission_UserRead) { + break; + } + base_address = mem_info.GetEndAddress(); + } + + /* Check that first value is 0. */ + u32 val; + if (!ReadValue(std::addressof(val), process, base_address)) { + return false; + } + if (val != 0) { + return false; + } + + /* Read the name length. */ + if (!ReadValue(std::addressof(val), process, base_address + sizeof(u32))) { + return false; + } + if (!(0 < val && val < dst_size)) { + return false; + } + const size_t name_len = val; + + /* Read the name, one character at a time. */ + for (size_t i = 0; i < name_len; ++i) { + if (!ReadValue(dst + i, process, base_address + 2 * sizeof(u32) + i)) { + return false; + } + if (!(0 < dst[i] && dst[i] <= 0x7F)) { + return false; + } + } + + /* NULL-terminate. */ + dst[name_len] = 0; + + return true; + } + + void PrintAddress(uintptr_t address) { + MESOSPHERE_RELEASE_LOG(" %p\n", reinterpret_cast(address)); + } + + void PrintAddressWithModuleName(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address) { + if (has_module_name) { + MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx]\n", reinterpret_cast(address), module_name, address - base_address); + } else { + MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx]\n", reinterpret_cast(address), base_address, address - base_address); + } + } + + void PrintAddressWithSymbol(uintptr_t address, bool has_module_name, const char *module_name, uintptr_t base_address, const char *symbol_name, uintptr_t func_address) { + if (has_module_name) { + MESOSPHERE_RELEASE_LOG(" %p [%10s + %8lx] (%s + %lx)\n", reinterpret_cast(address), module_name, address - base_address, symbol_name, address - func_address); + } else { + MESOSPHERE_RELEASE_LOG(" %p [%10lx + %8lx] (%s + %lx)\n", reinterpret_cast(address), base_address, address - base_address, symbol_name, address - func_address); + } + } + + void PrintCodeAddress(KProcess *process, uintptr_t address, bool is_lr = true) { + /* Prepare to parse + print the address. */ + uintptr_t test_address = is_lr ? address - sizeof(u32) : address; + uintptr_t base_address = address; + uintptr_t dyn_address = 0; + uintptr_t sym_tab = 0; + uintptr_t str_tab = 0; + size_t num_sym = 0; + + u64 temp_64; + u32 temp_32; + + /* Locate the start of .text. */ + KMemoryInfo mem_info; + ams::svc::PageInfo page_info; + KMemoryState mem_state = KMemoryState_None; + while (true) { + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address))) { + return PrintAddress(address); + } + if (mem_state == KMemoryState_None) { + mem_state = mem_info.GetState(); + if (mem_state != KMemoryState_Code && mem_state != KMemoryState_AliasCode) { + return PrintAddress(address); + } + } else if (mem_info.GetState() != mem_state) { + return PrintAddress(address); + } + if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) { + return PrintAddress(address); + } + base_address = mem_info.GetAddress(); + + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), base_address - 1))) { + return PrintAddress(address); + } + if (mem_info.GetState() != mem_state) { + break; + } + if (mem_info.GetPermission() != KMemoryPermission_UserReadExecute) { + break; + } + } + + /* Read the first instruction. */ + if (!ReadValue(std::addressof(temp_32), process, base_address)) { + return PrintAddress(address); + } + + /* Get the module name. */ + char module_name[0x20]; + const bool has_module_name = GetModuleName(module_name, sizeof(module_name), process, base_address); + + /* If the process is 32-bit, just print the module. */ + if (!process->Is64Bit()) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + if (temp_32 == 0) { + /* Module is dynamically loaded by rtld. */ + u32 mod_offset; + if (!ReadValue(std::addressof(mod_offset), process, base_address + sizeof(u32))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (temp_32 != 0x30444F4D) { /* MOD0 */ + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (!ReadValue(std::addressof(temp_32), process, base_address + mod_offset + sizeof(u32))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + dyn_address = base_address + mod_offset + temp_32; + } else if (temp_32 == 0x14000002) { + /* Module embeds rtld. */ + if (!ReadValue(std::addressof(temp_32), process, base_address + 0x5C)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (temp_32 != 0x94000002) { /* MOD0 */ + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (!ReadValue(std::addressof(temp_32), process, base_address + 0x60)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + dyn_address = base_address + 0x60 + temp_32; + } else { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + /* Locate tables inside .dyn. */ + for (size_t ofs = 0; /* ... */; ofs += 0x10) { + /* Read the DynamicTag. */ + if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (temp_64 == 0) { + /* We're done parsing .dyn. */ + break; + } else if (temp_64 == 4) { + /* We found DT_HASH */ + if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + /* Read nchain, to get the number of symbols. */ + if (!ReadValue(std::addressof(temp_32), process, base_address + temp_64 + sizeof(u32))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + num_sym = temp_32; + } else if (temp_64 == 5) { + /* We found DT_STRTAB */ + if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + str_tab = base_address + temp_64; + } else if (temp_64 == 6) { + /* We found DT_SYMTAB */ + if (!ReadValue(std::addressof(temp_64), process, dyn_address + ofs + sizeof(u64))) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + sym_tab = base_address + temp_64; + } + } + + /* Check that we found all the tables. */ + if (!(sym_tab != 0 && str_tab != 0 && num_sym != 0)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + /* Try to locate an appropriate symbol. */ + for (size_t i = 0; i < num_sym; ++i) { + /* Read the symbol from userspace. */ + struct { + u32 st_name; + u8 st_info; + u8 st_other; + u16 st_shndx; + u64 st_value; + u64 st_size; + } sym; + { + u64 x[sizeof(sym) / sizeof(u64)]; + for (size_t j = 0; j < util::size(x); ++j) { + if (!ReadValue(x + j, process, sym_tab + sizeof(sym) * i + sizeof(u64) * j)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + } + std::memcpy(std::addressof(sym), x, sizeof(sym)); + } + + /* Check the symbol is valid/STT_FUNC. */ + if (sym.st_shndx == 0 || ((sym.st_shndx & 0xFF00) == 0xFF00)) { + continue; + } + if ((sym.st_info & 0xF) != 2) { + continue; + } + + /* Check the address. */ + const uintptr_t func_start = base_address + sym.st_value; + if (func_start <= test_address && test_address < func_start + sym.st_size) { + /* Read the symbol name. */ + const uintptr_t sym_address = str_tab + sym.st_name; + char sym_name[0x80]; + sym_name[util::size(sym_name) - 1] = 0; + for (size_t j = 0; j < util::size(sym_name) - 1; ++j) { + if (!ReadValue(sym_name + j, process, sym_address + j)) { + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + if (sym_name[j] == 0) { + break; + } + } + + /* Print the symbol. */ + return PrintAddressWithSymbol(address, has_module_name, module_name, base_address, sym_name, func_start); + } + } + + /* Fall back to printing the module. */ + return PrintAddressWithModuleName(address, has_module_name, module_name, base_address); + } + + } + #endif + + void KDebug::PrintBacktrace(KThread *thread) { + #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) + { + /* Treat no thread as current thread. */ + if (thread == nullptr) { + thread = GetCurrentThreadPointer(); + } + + /* Get the exception context. */ + KExceptionContext *e_ctx = GetExceptionContext(thread); + + /* Get the owner process. */ + if (auto *process = thread->GetOwnerProcess(); process != nullptr) { + /* Lock the owner process. */ + KScopedLightLock state_lk(process->GetStateLock()); + KScopedLightLock list_lk(process->GetListLock()); + + /* Suspend all the process's threads. */ + { + KScopedSchedulerLock sl; + + auto end = process->GetThreadList().end(); + for (auto it = process->GetThreadList().begin(); it != end; ++it) { + if (std::addressof(*it) != GetCurrentThreadPointer()) { + it->RequestSuspend(KThread::SuspendType_Backtrace); + } + } + } + + /* Print the backtrace. */ + MESOSPHERE_RELEASE_LOG("User Backtrace\n"); + if ((e_ctx->psr & 0x10) == 0) { + /* 64-bit thread. */ + PrintCodeAddress(process, e_ctx->pc, false); + PrintCodeAddress(process, e_ctx->x[30]); + + /* Walk the stack frames. */ + uintptr_t fp = static_cast(e_ctx->x[29]); + for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 0x10); ++i) { + /* Read the next frame. */ + struct { + u64 fp; + u64 lr; + } stack_frame; + { + KMemoryInfo mem_info; + ams::svc::PageInfo page_info; + KPhysicalAddress phys_addr; + + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) { + break; + } + if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) { + break; + } + if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) { + break; + } + if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) { + break; + } + if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) { + break; + } + if (!IsHeapPhysicalAddress(phys_addr)) { + break; + } + + u64 *frame_ptr = GetPointer(process->GetPageTable().GetHeapVirtualAddress(phys_addr)); + stack_frame.fp = frame_ptr[0]; + stack_frame.lr = frame_ptr[1]; + } + + /* Print and advance. */ + PrintCodeAddress(process, stack_frame.lr); + fp = stack_frame.fp; + } + } else { + /* 32-bit thread. */ + PrintCodeAddress(process, e_ctx->pc, false); + PrintCodeAddress(process, e_ctx->x[14]); + + /* Walk the stack frames. */ + uintptr_t fp = static_cast(e_ctx->x[11]); + for (auto i = 0; i < 0x20 && fp != 0 && util::IsAligned(fp, 4); ++i) { + /* Read the next frame. */ + struct { + u32 fp; + u32 lr; + } stack_frame; + { + KMemoryInfo mem_info; + ams::svc::PageInfo page_info; + KPhysicalAddress phys_addr; + + /* Read FP */ + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), fp))) { + break; + } + if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) { + break; + } + if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) { + break; + } + if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) { + break; + } + if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), fp)) { + break; + } + if (!IsHeapPhysicalAddress(phys_addr)) { + break; + } + + stack_frame.fp = *GetPointer(process->GetPageTable().GetHeapVirtualAddress(phys_addr)); + + /* Read LR. */ + uintptr_t lr_ptr = (e_ctx->x[13] <= stack_frame.fp && stack_frame.fp < e_ctx->x[13] + PageSize) ? fp + 4 : fp - 4; + if (R_FAILED(process->GetPageTable().QueryInfo(std::addressof(mem_info), std::addressof(page_info), lr_ptr))) { + break; + } + if ((mem_info.GetState() & KMemoryState_FlagReferenceCounted) == 0) { + break; + } + if ((mem_info.GetAttribute() & KMemoryAttribute_Uncached) != 0) { + break; + } + if ((mem_info.GetPermission() & KMemoryPermission_UserRead) != KMemoryPermission_UserRead) { + break; + } + if (!process->GetPageTable().GetPhysicalAddress(std::addressof(phys_addr), lr_ptr)) { + break; + } + if (!IsHeapPhysicalAddress(phys_addr)) { + break; + } + + stack_frame.lr = *GetPointer(process->GetPageTable().GetHeapVirtualAddress(phys_addr)); + } + + /* Print and advance. */ + PrintCodeAddress(process, stack_frame.lr); + fp = stack_frame.fp; + } + } + + /* Resume the threads that we suspended. */ + { + KScopedSchedulerLock sl; + + auto end = process->GetThreadList().end(); + for (auto it = process->GetThreadList().begin(); it != end; ++it) { + if (std::addressof(*it) != GetCurrentThreadPointer()) { + it->Resume(KThread::SuspendType_Backtrace); + } + } + } + } + } + #else + MESOSPHERE_UNUSED(thread); + #endif + } + } diff --git a/libraries/libmesosphere/source/kern_k_dump_object.cpp b/libraries/libmesosphere/source/kern_k_dump_object.cpp index 4aa24473b..551b3f374 100644 --- a/libraries/libmesosphere/source/kern_k_dump_object.cpp +++ b/libraries/libmesosphere/source/kern_k_dump_object.cpp @@ -44,6 +44,19 @@ namespace ams::kern::KDumpObject { } } + void DumpThreadCallStack(KThread *thread) { + if (KProcess *process = thread->GetOwnerProcess(); process != nullptr) { + MESOSPHERE_LOG("Thread ID=%5lu pid=%3lu %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n", + thread->GetId(), process->GetId(), process->GetName(), thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize); + + KDebug::PrintRegister(thread); + KDebug::PrintBacktrace(thread); + } else { + MESOSPHERE_LOG("Thread ID=%5lu pid=%3d %-11s Pri=%2d %-11s KernelStack=%4zu/%4zu\n", + thread->GetId(), -1, "(kernel)", thread->GetPriority(), ThreadStates[thread->GetState()], thread->GetKernelStackUsage(), PageSize); + } + } + } void DumpThread() { @@ -77,4 +90,35 @@ namespace ams::kern::KDumpObject { MESOSPHERE_LOG("\n"); } + void DumpThreadCallStack() { + MESOSPHERE_LOG("Dump Thread\n"); + + { + /* Lock the list. */ + KThread::ListAccessor accessor; + const auto end = accessor.end(); + + /* Dump each thread. */ + for (auto it = accessor.begin(); it != end; ++it) { + DumpThreadCallStack(static_cast(std::addressof(*it))); + } + } + + MESOSPHERE_LOG("\n"); + } + + void DumpThreadCallStack(u64 thread_id) { + MESOSPHERE_LOG("Dump Thread\n"); + + { + /* Find and dump the target thread. */ + if (KThread *thread = KThread::GetThreadFromId(thread_id); thread != nullptr) { + ON_SCOPE_EXIT { thread->Close(); }; + DumpThreadCallStack(thread); + } + } + + MESOSPHERE_LOG("\n"); + } + } diff --git a/libraries/libmesosphere/source/kern_panic.cpp b/libraries/libmesosphere/source/kern_panic.cpp index 1dbbc54c9..ce9e2cdd0 100644 --- a/libraries/libmesosphere/source/kern_panic.cpp +++ b/libraries/libmesosphere/source/kern_panic.cpp @@ -97,25 +97,20 @@ namespace ams::kern { /* Print the state. */ MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id); - #ifdef ATMOSPHERE_ARCH_ARM64 - /* Print registers. */ - if (core_ctx != nullptr) { - MESOSPHERE_RELEASE_LOG(" Registers:\n"); - for (size_t i = 0; i < util::size(core_ctx->x); ++i) { - MESOSPHERE_RELEASE_LOG(" X[%02zx]: %p\n", i, reinterpret_cast(core_ctx->x[i])); - } - MESOSPHERE_RELEASE_LOG(" SP: %p\n", reinterpret_cast(core_ctx->x[30])); - } + /* Print registers and user backtrace. */ + KDebug::PrintRegister(); + KDebug::PrintBacktrace(); - /* Print backtrace. */ - MESOSPHERE_RELEASE_LOG(" Backtrace:\n"); + #ifdef ATMOSPHERE_ARCH_ARM64 + /* Print kernel backtrace. */ + MESOSPHERE_RELEASE_LOG("Backtrace:\n"); uintptr_t fp = core_ctx != nullptr ? core_ctx->x[29] : reinterpret_cast(__builtin_frame_address(0)); for (size_t i = 0; i < 32 && fp && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); i++) { struct { uintptr_t fp; uintptr_t lr; } *stack_frame = reinterpret_cast(fp); - MESOSPHERE_RELEASE_LOG(" [%02zx]: %p\n", i, reinterpret_cast(stack_frame->lr)); + MESOSPHERE_RELEASE_LOG(" [%02zx]: %p\n", i, reinterpret_cast(stack_frame->lr)); fp = stack_frame->fp; } #endif @@ -137,7 +132,7 @@ namespace ams::kern { } - NORETURN void PanicImpl(const char *file, int line, const char *format, ...) { + NORETURN WEAK_SYMBOL void PanicImpl(const char *file, int line, const char *format, ...) { #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING /* Wait for it to be our turn to print. */ WaitCoreTicket(); @@ -158,7 +153,7 @@ namespace ams::kern { StopSystem(); } - NORETURN void PanicImpl() { + NORETURN WEAK_SYMBOL void PanicImpl() { StopSystem(); } diff --git a/libraries/libmesosphere/source/svc/kern_svc_exception.cpp b/libraries/libmesosphere/source/svc/kern_svc_exception.cpp index f5c859a88..3f9940008 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_exception.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_exception.cpp @@ -21,16 +21,18 @@ namespace ams::kern::svc { namespace { - [[maybe_unused]] void PrintBreak(ams::svc::BreakReason break_reason) { + #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) + void PrintBreak(ams::svc::BreakReason break_reason) { /* Print that break was called. */ MESOSPHERE_RELEASE_LOG("%s: svc::Break(%d) was called, pid=%ld, tid=%ld\n", GetCurrentProcess().GetName(), static_cast(break_reason), GetCurrentProcess().GetId(), GetCurrentThread().GetId()); /* Print the current thread's registers. */ - /* TODO: KDebug::PrintRegisters(); */ + KDebug::PrintRegister(); /* Print a backtrace. */ - /* TODO: KDebug::PrintBacktrace(); */ + KDebug::PrintBacktrace(); } + #endif void Break(ams::svc::BreakReason break_reason, uintptr_t address, size_t size) { /* Determine whether the break is only a notification. */ @@ -38,7 +40,7 @@ namespace ams::kern::svc { /* If the break isn't a notification, print it. */ if (!is_notification) { - #ifdef MESOSPHERE_BUILD_FOR_DEBUGGING + #if defined(MESOSPHERE_BUILD_FOR_DEBUGGING) PrintBreak(break_reason); #endif } diff --git a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp index c905f8017..18953df2f 100644 --- a/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp +++ b/libraries/libmesosphere/source/svc/kern_svc_kernel_debug.cpp @@ -34,6 +34,13 @@ namespace ams::kern::svc { KDumpObject::DumpThread(arg0); } break; + case ams::svc::KernelDebugType_ThreadCallStack: + if (arg0 == static_cast(-1)) { + KDumpObject::DumpThreadCallStack(); + } else { + KDumpObject::DumpThreadCallStack(arg0); + } + break; default: break; } diff --git a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp index c5aff199c..64070e9ce 100644 --- a/libraries/libvapours/include/vapours/svc/svc_types_common.hpp +++ b/libraries/libvapours/include/vapours/svc/svc_types_common.hpp @@ -470,7 +470,8 @@ namespace ams::svc { }; enum KernelDebugType : u32 { - KernelDebugType_Thread = 0, + KernelDebugType_Thread = 0, + KernelDebugType_ThreadCallStack = 1, }; enum KernelTraceState : u32 { diff --git a/mesosphere/kernel_ldr/source/kern_loader_panic.cpp b/mesosphere/kernel_ldr/source/kern_loader_panic.cpp new file mode 100644 index 000000000..37c3f39cd --- /dev/null +++ b/mesosphere/kernel_ldr/source/kern_loader_panic.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + /* This overrides the panic implementation from the kernel, to prevent linking debug print into kldr. */ + + NORETURN void PanicImpl(const char *file, int line, const char *format, ...) { + MESOSPHERE_UNUSED(file, line, format); + MESOSPHERE_INIT_ABORT(); + } + + NORETURN void PanicImpl() { + MESOSPHERE_INIT_ABORT(); + } + +}