diff --git a/include/stratosphere.hpp b/include/stratosphere.hpp index 824f2405..e0ab6d67 100644 --- a/include/stratosphere.hpp +++ b/include/stratosphere.hpp @@ -37,4 +37,6 @@ #include "stratosphere/results.hpp" -#include "stratosphere/title_ids.hpp" \ No newline at end of file +#include "stratosphere/title_ids.hpp" + +#include "stratosphere/on_crash.hpp" \ No newline at end of file diff --git a/include/stratosphere/on_crash.hpp b/include/stratosphere/on_crash.hpp new file mode 100644 index 00000000..7482a7e8 --- /dev/null +++ b/include/stratosphere/on_crash.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2018-2019 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 +#include "services/bpc_ams.h" + +static constexpr size_t AtmosphereFatalErrorNumGprs = 29; + +static constexpr u32 AtmosphereFatalErrorMagic = 0x30454641; /* "AFE0" */ + +/* Will be called by libstratosphere on crash. */ +void StratosphereCrashHandler(ThreadExceptionDump *ctx); \ No newline at end of file diff --git a/include/stratosphere/services/bpc_ams.h b/include/stratosphere/services/bpc_ams.h new file mode 100644 index 00000000..256ce825 --- /dev/null +++ b/include/stratosphere/services/bpc_ams.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2018-2019 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 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + u32 magic; + u32 error_desc; + u64 title_id; + union { + u64 gprs[32]; + struct { + u64 _gprs[29]; + u64 fp; + u64 lr; + u64 sp; + }; + }; + u64 pc; + u64 padding; + u32 pstate; + u32 afsr0; + u32 afsr1; + u32 esr; + u64 far; + u64 report_identifier; /* Normally just system tick. */ +} AtmosphereFatalErrorContext; + +Result bpcAmsInitialize(void); +void bpcAmsExit(void); + +Result bpcAmsRebootToFatalError(AtmosphereFatalErrorContext *ctx); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/source/bpc_ams.c b/source/bpc_ams.c new file mode 100644 index 00000000..2336b6e9 --- /dev/null +++ b/source/bpc_ams.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +static Service g_bpcAmsSrv; +static u64 g_bpcAmsAmsRefcnt; + +Result bpcAmsInitialize(void) { + atomicIncrement64(&g_bpcAmsAmsRefcnt); + + if (serviceIsActive(&g_bpcAmsSrv)) { + return 0; + } + + Handle h; + Result rc = svcConnectToNamedPort(&h, "bpc:ams"); + if (R_SUCCEEDED(rc)) { + serviceCreate(&g_bpcAmsSrv, h); + } + + return rc; +} + +void bpcAmsExit(void) { + if (atomicDecrement64(&g_bpcAmsAmsRefcnt) == 0) + serviceClose(&g_bpcAmsSrv); +} + +Result bpcAmsRebootToFatalError(AtmosphereFatalErrorContext *ctx) { + IpcCommand c; + ipcInitialize(&c); + ipcAddSendBuffer(&c, ctx, sizeof(*ctx), BufferType_Normal); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&g_bpcAmsSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 65000; + + Result rc = serviceIpcDispatch(&g_bpcAmsSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_bpcAmsSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} diff --git a/source/on_crash.cpp b/source/on_crash.cpp new file mode 100644 index 00000000..da403b55 --- /dev/null +++ b/source/on_crash.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2018-2019 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 +#include +#include + +extern "C" { + __attribute__((weak)) u64 __stratosphere_title_id = 0; + void __attribute__((weak)) __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); + + /* Redefine abort, so that it triggers these handlers. */ + void abort(); +}; + +void StratosphereCrashHandler(ThreadExceptionDump *ctx) { + AtmosphereFatalErrorContext ams_ctx; + /* Convert thread dump to atmosphere dump. */ + { + ams_ctx.magic = AtmosphereFatalErrorMagic; + ams_ctx.error_desc = ctx->error_desc; + ams_ctx.title_id = __stratosphere_title_id; + for (size_t i = 0; i < AtmosphereFatalErrorNumGprs; i++) { + ams_ctx.gprs[i] = ctx->cpu_gprs[i].x; + } + ams_ctx.fp = ctx->fp.x; + ams_ctx.lr = ctx->lr.x; + ams_ctx.sp = ctx->sp.x; + ams_ctx.pc = ctx->pc.x; + ams_ctx.pstate = ctx->pstate; + ams_ctx.afsr0 = ctx->afsr0; + ams_ctx.afsr1 = ctx->afsr1; + ams_ctx.far = ctx->far.x; + ams_ctx.report_identifier = armGetSystemTick(); + } + + /* Just call the user exception handler. */ + __libstratosphere_exception_handler(&ams_ctx); +} + +/* Default exception handler behavior. */ +void __attribute__((weak)) __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx) { + Result rc = bpcAmsInitialize(); + if (R_SUCCEEDED(rc)) { + rc = bpcAmsRebootToFatalError(ctx); + bpcAmsExit(); + } + if (R_FAILED(rc)) { + std::abort(); + } + while (1) { } +} + +/* Custom abort handler, so that std::abort will trigger these. */ +void abort() { + /* Just perform a data abort. */ + while (true) { + *((volatile u64 *)0x8) = 0xCAFEBABE; + } +} \ No newline at end of file