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);
+
+}