From 4acdc899f574b4422bc9ffb69ef9e7bbe168cb38 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 16 Sep 2020 16:44:31 -0700 Subject: [PATCH] kern: generate fatal error on panic --- fusee/fusee-primary/src/panic.c | 2 + .../nintendo/nx/kern_k_system_control.hpp | 2 +- .../include/mesosphere/kern_build_config.hpp | 3 +- .../source/arch/arm64/kern_panic_asm.s | 88 +++++++++++++++++++ .../nintendo/nx/kern_k_system_control.cpp | 58 +++++++++++- .../board/nintendo/nx/kern_secure_monitor.cpp | 39 ++++++++ .../board/nintendo/nx/kern_secure_monitor.hpp | 8 ++ libraries/libmesosphere/source/kern_panic.cpp | 39 ++++++-- .../include/stratosphere/ams/ams_types.hpp | 42 +-------- libraries/libvapours/include/vapours.hpp | 2 + .../vapours/ams/ams_fatal_error_context.hpp | 66 ++++++++++++++ 11 files changed, 296 insertions(+), 53 deletions(-) create mode 100644 libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s create mode 100644 libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp diff --git a/fusee/fusee-primary/src/panic.c b/fusee/fusee-primary/src/panic.c index bd0520efb..5e8fd5c14 100644 --- a/fusee/fusee-primary/src/panic.c +++ b/fusee/fusee-primary/src/panic.c @@ -42,6 +42,8 @@ static const char *get_error_desc_str(uint32_t error_desc) { return "SError"; case 0x301: return "Bad SVC"; + case 0xF00: + return "Kernel Panic"; case 0xFFD: return "Stack overflow"; case 0xFFE: diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index fdf757df7..c72015d01 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -63,7 +63,7 @@ namespace ams::kern::board::nintendo::nx { /* Power management. */ static void SleepSystem(); - static NORETURN void StopSystem(); + static NORETURN void StopSystem(void *arg = nullptr); /* User access. */ static void CallSecureMonitorFromUser(ams::svc::lp64::SecureMonitorArguments *args); diff --git a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp index 84afc3e31..14c774182 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_build_config.hpp @@ -28,4 +28,5 @@ #define MESOSPHERE_ENABLE_DEBUG_PRINT #endif -#define MESOSPHERE_BUILD_FOR_TRACING +//#define MESOSPHERE_BUILD_FOR_TRACING +#define MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP diff --git a/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s new file mode 100644 index 000000000..7d317ac60 --- /dev/null +++ b/libraries/libmesosphere/source/arch/arm64/kern_panic_asm.s @@ -0,0 +1,88 @@ +/* + * 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 + +#if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP) + +#define MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT \ + /* Save x0/x1 to stack. */ \ + stp x0, x1, [sp, #-16]!; \ + \ + /* Get the exception context for the core. */ \ + adr x0, _ZN3ams4kern26g_panic_exception_contextsE; \ + mrs x1, mpidr_el1; \ + and x1, x1, #0xFF; \ + lsl x1, x1, #0x8; \ + add x0, x0, x1; \ + lsr x1, x1, #0x3; \ + add x0, x0, x1; \ + \ + /* Save x0/x1/sp to the context. */ \ + ldr x1, [sp, #(8 * 0)]; \ + str x1, [x0, #(8 * 0)]; \ + ldr x1, [sp, #(8 * 1)]; \ + str x1, [x0, #(8 * 1)]; \ + \ + /* Save all other registers to the context. */ \ + stp x2, x3, [x0, #(8 * 2)]; \ + stp x4, x5, [x0, #(8 * 4)]; \ + stp x6, x7, [x0, #(8 * 6)]; \ + stp x8, x9, [x0, #(8 * 8)]; \ + stp x10, x11, [x0, #(8 * 10)]; \ + stp x12, x13, [x0, #(8 * 12)]; \ + stp x14, x15, [x0, #(8 * 14)]; \ + stp x16, x17, [x0, #(8 * 16)]; \ + stp x18, x19, [x0, #(8 * 18)]; \ + stp x20, x21, [x0, #(8 * 20)]; \ + stp x22, x23, [x0, #(8 * 22)]; \ + stp x24, x25, [x0, #(8 * 24)]; \ + stp x26, x27, [x0, #(8 * 26)]; \ + stp x28, x29, [x0, #(8 * 28)]; \ + \ + add x1, sp, #16; \ + stp x30, x1, [x0, #(8 * 30)]; \ + \ + /* Restore x0/x1. */ \ + ldp x0, x1, [sp], #16; + +#else + +#define MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + +#endif + +/* ams::kern::Panic(const char *file, int line, const char *format, ...) */ +.section .text._ZN3ams4kern5PanicEPKciS2_z, "ax", %progbits +.global _ZN3ams4kern5PanicEPKciS2_z +.type _ZN3ams4kern5PanicEPKciS2_z, %function +_ZN3ams4kern5PanicEPKciS2_z: + /* Generate the exception context. */ + MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + + /* Jump to the architecturally-common implementation function. */ + b _ZN3ams4kern9PanicImplEPKciS2_z + + +/* ams::kern::Panic() */ +.section .text._ZN3ams4kern5PanicEv, "ax", %progbits +.global _ZN3ams4kern5PanicEv +.type _ZN3ams4kern5PanicEv, %function +_ZN3ams4kern5PanicEv: + /* Generate the exception context. */ + MESOSPHERE_GENERATE_PANIC_EXCEPTION_CONTEXT + + /* Jump to the architecturally-common implementation function. */ + b _ZN3ams4kern9PanicImplEv diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index 4c1763cac..8b717009f 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -532,13 +532,63 @@ namespace ams::kern::board::nintendo::nx { KSleepManager::SleepSystem(); } - void KSystemControl::StopSystem() { + void KSystemControl::StopSystem(void *arg) { + if (arg != nullptr) { + /* NOTE: Atmosphere extension; if we received an exception context from Panic(), */ + /* generate a fatal error report using it. */ + const KExceptionContext *e_ctx = static_cast(arg); + auto *f_ctx = GetPointer<::ams::impl::FatalErrorContext>(KMemoryLayout::GetDeviceVirtualAddress(KMemoryRegionType_LegacyLpsIram) + 0x3E000); + + /* Clear the fatal context. */ + std::memset(f_ctx, 0xCC, sizeof(*f_ctx)); + + /* Set metadata. */ + f_ctx->magic = ::ams::impl::FatalErrorContext::Magic; + f_ctx->error_desc = ::ams::impl::FatalErrorContext::KernelPanicDesc; + f_ctx->program_id = (static_cast(util::FourCC<'M', 'E', 'S', 'O'>::Code) << 0) | (static_cast(util::FourCC<'S', 'P', 'H', 'R'>::Code) << 32); + + /* Set identifier. */ + f_ctx->report_identifier = KHardwareTimer::GetTick(); + + /* Set module base. */ + f_ctx->module_base = KMemoryLayout::GetKernelCodeRegionExtents().GetAddress(); + + /* Copy registers. */ + for (size_t i = 0; i < util::size(e_ctx->x); ++i) { + f_ctx->gprs[i] = e_ctx->x[i]; + } + f_ctx->sp = e_ctx->sp; + + /* Dump stack trace. */ + { + uintptr_t fp = e_ctx->x[29]; + for (f_ctx->stack_trace_size = 0; f_ctx->stack_trace_size < ::ams::impl::FatalErrorContext::MaxStackTrace && fp != 0 && util::IsAligned(fp, 0x10) && cpu::GetPhysicalAddressWritable(nullptr, fp, true); ++(f_ctx->stack_trace_size)) { + struct { + uintptr_t fp; + uintptr_t lr; + } *stack_frame = reinterpret_cast(fp); + + f_ctx->stack_trace[f_ctx->stack_trace_size] = stack_frame->lr; + fp = stack_frame->fp; + } + } + + /* Dump stack. */ + { + uintptr_t sp = e_ctx->sp; + for (f_ctx->stack_dump_size = 0; f_ctx->stack_dump_size < ::ams::impl::FatalErrorContext::MaxStackDumpSize && cpu::GetPhysicalAddressWritable(nullptr, sp + f_ctx->stack_dump_size, true); f_ctx->stack_dump_size += sizeof(u64)) { + *reinterpret_cast(f_ctx->stack_dump + f_ctx->stack_dump_size) = *reinterpret_cast(sp + f_ctx->stack_dump_size); + } + } + + /* Trigger a reboot to rcm. */ + smc::init::SetConfig(smc::ConfigItem::ExosphereNeedsReboot, smc::UserRebootType_ToRcm); + } + if (g_call_smc_on_panic) { - /* Display a panic screen via secure monitor. */ + /* If we should, instruct the secure monitor to display a panic screen. */ smc::Panic(0xF00); } - u32 dummy; - smc::init::ReadWriteRegister(std::addressof(dummy), 0x7000E400, 0x10, 0x10); AMS_INFINITE_LOOP(); } diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp index 8c7414c0a..315f5072e 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.cpp @@ -24,6 +24,10 @@ namespace ams::kern::board::nintendo::nx::smc { u64 x[8]; }; + enum UserFunctionId : u32 { + UserFunctionId_SetConfig = 0xC3000401, + }; + enum FunctionId : u32 { FunctionId_CpuSuspend = 0xC4000001, FunctionId_CpuOff = 0x84000002, @@ -136,6 +140,35 @@ namespace ams::kern::board::nintendo::nx::smc { args.x[7] = x7; } + void CallUserSecureMonitorFunctionForInit(SecureMonitorArguments &args) { + /* Load arguments into registers. */ + register u64 x0 asm("x0") = args.x[0]; + register u64 x1 asm("x1") = args.x[1]; + register u64 x2 asm("x2") = args.x[2]; + register u64 x3 asm("x3") = args.x[3]; + register u64 x4 asm("x4") = args.x[4]; + register u64 x5 asm("x5") = args.x[5]; + register u64 x6 asm("x6") = args.x[6]; + register u64 x7 asm("x7") = args.x[7]; + + /* Actually make the call. */ + __asm__ __volatile__("smc #0" + : "+r"(x0), "+r"(x1), "+r"(x2), "+r"(x3), "+r"(x4), "+r"(x5), "+r"(x6), "+r"(x7) + : + : "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", "x16", "x17", "x18", "cc", "memory" + ); + + /* Store arguments to output. */ + args.x[0] = x0; + args.x[1] = x1; + args.x[2] = x2; + args.x[3] = x3; + args.x[4] = x4; + args.x[5] = x5; + args.x[6] = x6; + args.x[7] = x7; + } + /* Global lock for generate random bytes. */ KSpinLock g_generate_random_lock; @@ -177,6 +210,12 @@ namespace ams::kern::board::nintendo::nx::smc { return static_cast(args.x[0]) == SmcResult::Success; } + bool SetConfig(ConfigItem config_item, u64 value) { + SecureMonitorArguments args = { UserFunctionId_SetConfig, static_cast(config_item), 0, value }; + CallUserSecureMonitorFunctionForInit(args); + return static_cast(args.x[0]) == SmcResult::Success; + } + } void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item) { diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp index fe3e7634f..da63d29e6 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_secure_monitor.hpp @@ -83,6 +83,12 @@ namespace ams::kern::board::nintendo::nx::smc { using MemorySize = util::BitPack32::Field; }; + enum UserRebootType { + UserRebootType_None = 0, + UserRebootType_ToRcm = 1, + UserRebootType_ToPayload = 2, + }; + /* TODO: Rest of Secure Monitor API. */ void GenerateRandomBytes(void *dst, size_t size); void GetConfig(u64 *out, size_t num_qwords, ConfigItem config_item); @@ -102,6 +108,8 @@ namespace ams::kern::board::nintendo::nx::smc { void GenerateRandomBytes(void *dst, size_t size); bool ReadWriteRegister(u32 *out, u64 address, u32 mask, u32 value); + bool SetConfig(ConfigItem config_item, u64 value); + } } \ No newline at end of file diff --git a/libraries/libmesosphere/source/kern_panic.cpp b/libraries/libmesosphere/source/kern_panic.cpp index 92c765ad1..1dbbc54c9 100644 --- a/libraries/libmesosphere/source/kern_panic.cpp +++ b/libraries/libmesosphere/source/kern_panic.cpp @@ -27,6 +27,11 @@ namespace ams::result::impl { namespace ams::kern { + /* NOTE: This is not exposed via a header, but is referenced via assembly. */ + /* NOTE: Nintendo does not save register contents on panic; we use this */ + /* to generate an atmosphere fatal report on panic. */ + constinit KExceptionContext g_panic_exception_contexts[cpu::NumCores]; + namespace { constexpr std::array NegativeArray = [] { @@ -73,18 +78,38 @@ namespace ams::kern { } while (!g_current_ticket.compare_exchange_weak(compare, desired)); } + ALWAYS_INLINE KExceptionContext *GetPanicExceptionContext(int core_id) { + #if defined(MESOSPHERE_ENABLE_PANIC_REGISTER_DUMP) + return std::addressof(g_panic_exception_contexts[core_id]); + #else + return nullptr; + #endif + } + [[gnu::unused]] void PrintCurrentState() { /* Wait for it to be our turn to print. */ WaitCoreTicket(); - const s32 core_id = GetCurrentCoreId(); + /* Get the current exception context. */ + const s32 core_id = GetCurrentCoreId(); + const auto *core_ctx = GetPanicExceptionContext(core_id); + + /* Print the state. */ MESOSPHERE_RELEASE_LOG("Core[%d] Current State:\n", core_id); - /* TODO: Dump register state. */ - #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 backtrace. */ MESOSPHERE_RELEASE_LOG(" Backtrace:\n"); - uintptr_t fp = reinterpret_cast(__builtin_frame_address(0)); + 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; @@ -107,12 +132,12 @@ namespace ams::kern { PrintCurrentState(); #endif - KSystemControl::StopSystem(); + KSystemControl::StopSystem(GetPanicExceptionContext(GetCurrentCoreId())); } } - NORETURN WEAK_SYMBOL void Panic(const char *file, int line, const char *format, ...) { + NORETURN 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(); @@ -133,7 +158,7 @@ namespace ams::kern { StopSystem(); } - NORETURN WEAK_SYMBOL void Panic() { + NORETURN void PanicImpl() { StopSystem(); } diff --git a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp index 54b69ac4c..48789fd11 100644 --- a/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp +++ b/libraries/libstratosphere/include/stratosphere/ams/ams_types.hpp @@ -65,47 +65,9 @@ namespace ams::exosphere { namespace ams { - struct FatalErrorContext : sf::LargeData, sf::PrefersMapAliasTransferMode { - static constexpr size_t MaxStackTrace = 0x20; - static constexpr size_t MaxStackDumpSize = 0x100; - static constexpr size_t ThreadLocalSize = 0x100; - static constexpr size_t NumGprs = 29; - static constexpr uintptr_t StdAbortMagicAddress = 0x8; - static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul; - static constexpr u32 StdAbortErrorDesc = 0xFFE; - static constexpr u32 StackOverflowErrorDesc = 0xFFD; - static constexpr u32 DataAbortErrorDesc = 0x101; - static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code; + struct FatalErrorContext : ::ams::impl::FatalErrorContext, sf::LargeData, sf::PrefersMapAliasTransferMode {}; - u32 magic; - u32 error_desc; - u64 program_id; - union { - u64 gprs[32]; - struct { - u64 _gprs[29]; - u64 fp; - u64 lr; - u64 sp; - }; - }; - u64 pc; - u64 module_base; - u32 pstate; - u32 afsr0; - u32 afsr1; - u32 esr; - u64 far; - u64 report_identifier; /* Normally just system tick. */ - u64 stack_trace_size; - u64 stack_dump_size; - u64 stack_trace[MaxStackTrace]; - u8 stack_dump[MaxStackDumpSize]; - u8 tls[ThreadLocalSize]; - }; - - static_assert(sizeof(FatalErrorContext) == 0x450, "sizeof(FatalErrorContext)"); - static_assert(util::is_pod::value, "FatalErrorContext"); + static_assert(sizeof(FatalErrorContext) == sizeof(::ams::impl::FatalErrorContext)); #ifdef ATMOSPHERE_GIT_BRANCH NX_CONSTEXPR const char *GetGitBranch() { diff --git a/libraries/libvapours/include/vapours.hpp b/libraries/libvapours/include/vapours.hpp index 9da48965b..84820cb2b 100644 --- a/libraries/libvapours/include/vapours.hpp +++ b/libraries/libvapours/include/vapours.hpp @@ -27,3 +27,5 @@ #include #include #include + +#include diff --git a/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp b/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp new file mode 100644 index 000000000..986d6c48f --- /dev/null +++ b/libraries/libvapours/include/vapours/ams/ams_fatal_error_context.hpp @@ -0,0 +1,66 @@ +/* + * 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 . + */ +#pragma once +#include +#include + +namespace ams::impl { + + struct FatalErrorContext { + static constexpr size_t MaxStackTrace = 0x20; + static constexpr size_t MaxStackDumpSize = 0x100; + static constexpr size_t ThreadLocalSize = 0x100; + static constexpr size_t NumGprs = 29; + static constexpr uintptr_t StdAbortMagicAddress = 0x8; + static constexpr u64 StdAbortMagicValue = 0xA55AF00DDEADCAFEul; + static constexpr u32 StdAbortErrorDesc = 0xFFE; + static constexpr u32 StackOverflowErrorDesc = 0xFFD; + static constexpr u32 KernelPanicDesc = 0xF00; + static constexpr u32 DataAbortErrorDesc = 0x101; + static constexpr u32 Magic = util::FourCC<'A', 'F', 'E', '2'>::Code; + + u32 magic; + u32 error_desc; + u64 program_id; + union { + u64 gprs[32]; + struct { + u64 _gprs[29]; + u64 fp; + u64 lr; + u64 sp; + }; + }; + u64 pc; + u64 module_base; + u32 pstate; + u32 afsr0; + u32 afsr1; + u32 esr; + u64 far; + u64 report_identifier; /* Normally just system tick. */ + u64 stack_trace_size; + u64 stack_dump_size; + u64 stack_trace[MaxStackTrace]; + u8 stack_dump[MaxStackDumpSize]; + u8 tls[ThreadLocalSize]; + }; + + static_assert(sizeof(FatalErrorContext) == 0x450); + static_assert(std::is_standard_layout::value); + static_assert(std::is_trivial::value); + +}