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