diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json index 2d4388785..9dc17d9a9 100644 --- a/stratosphere/fatal/fatal.json +++ b/stratosphere/fatal/fatal.json @@ -4,7 +4,7 @@ "title_id_range_min": "0x0100000000000034", "title_id_range_max": "0x0100000000000034", "main_thread_stack_size": "0x00010000", - "main_thread_priority": 15, + "main_thread_priority": 37, "default_cpu_id": 3, "process_category": 0, "is_retail": true, diff --git a/stratosphere/fatal/source/fatal_event_manager.cpp b/stratosphere/fatal/source/fatal_event_manager.cpp index faed8969b..4414941ce 100644 --- a/stratosphere/fatal/source/fatal_event_manager.cpp +++ b/stratosphere/fatal/source/fatal_event_manager.cpp @@ -15,8 +15,15 @@ */ #include +#include "fatal_types.hpp" #include "fatal_event_manager.hpp" +static FatalEventManager g_event_manager; + +FatalEventManager *GetEventManager() { + return &g_event_manager; +} + FatalEventManager::FatalEventManager() { /* Just create all the events. */ for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { @@ -31,7 +38,7 @@ Result FatalEventManager::GetEvent(Handle *out) { /* Only allow GetEvent to succeed NumFatalEvents times. */ if (this->events_gotten >= FatalEventManager::NumFatalEvents) { - return 0x8A3; + return FatalResult_TooManyEvents; } *out = this->events[this->events_gotten++].revent; diff --git a/stratosphere/fatal/source/fatal_event_manager.hpp b/stratosphere/fatal/source/fatal_event_manager.hpp index 83e4e46dd..e96ab0f63 100644 --- a/stratosphere/fatal/source/fatal_event_manager.hpp +++ b/stratosphere/fatal/source/fatal_event_manager.hpp @@ -30,3 +30,5 @@ class FatalEventManager { Result GetEvent(Handle *out); void SignalEvents(); }; + +FatalEventManager *GetEventManager(); \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index 2573eb7ad..72b006d31 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -67,11 +67,17 @@ void __appInit(void) { fatalSimple(rc); } + rc = pminfoInitialize(); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + CheckAtmosphereVersion(CURRENT_ATMOSPHERE_VERSION); } void __appExit(void) { /* Cleanup services. */ + pminfoExit(); setsysExit(); smExit(); } diff --git a/stratosphere/fatal/source/fatal_private.cpp b/stratosphere/fatal/source/fatal_private.cpp index b1e57322a..afa384662 100644 --- a/stratosphere/fatal/source/fatal_private.cpp +++ b/stratosphere/fatal/source/fatal_private.cpp @@ -18,8 +18,6 @@ #include "fatal_private.hpp" #include "fatal_event_manager.hpp" -static FatalEventManager g_EventManager; - Result PrivateService::GetFatalEvent(Out out_h) { - return g_EventManager.GetEvent(out_h.GetHandlePointer()); + return GetEventManager()->GetEvent(out_h.GetHandlePointer()); } diff --git a/stratosphere/fatal/source/fatal_task.cpp b/stratosphere/fatal/source/fatal_task.cpp new file mode 100644 index 000000000..eb10add15 --- /dev/null +++ b/stratosphere/fatal/source/fatal_task.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2018 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 "fatal_types.hpp" +#include "fatal_task.hpp" + +static constexpr size_t MaxTasks = 8; +static HosThread g_task_threads[MaxTasks]; +static size_t g_num_threads = 0; + + +static void RunTaskThreadFunc(void *arg) { + IFatalTask *task = reinterpret_cast(arg); + + Result rc = task->Run(); + if (R_FAILED(rc)) { + /* TODO: Log task failure, somehow? */ + } +} + +static void RunTask(IFatalTask *task) { + if (g_num_threads >= MaxTasks) { + std::abort(); + } + + HosThread *cur_thread = &g_task_threads[g_num_threads++]; + + cur_thread->Initialize(&RunTaskThreadFunc, task, 0x4000, 15); + cur_thread->Start(); +} + +void RunFatalTasks(FatalContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event) { + /* TODO: RunTask(new ErrorReportTask(ctx, title_id, error_report, erpt_event); */ + /* TODO: RunTask(new PowerControlTask(ctx, title_id, battery_event); */ + /* TODO: RunTask(new ShowFatalTask(ctx, title_id, battery_event); */ + /* TODO: RunTask(new StopSoundTask(); */ + /* TODO: RunTask(new BacklightControlTask(); */ + /* TODO: RunTask(new AdjustClockTask(); */ + /* TODO: RunTask(new PowerButtonTask(erpt_event); */ + /* TODO: RunTask(new StateTransitionStop(); */ +} diff --git a/stratosphere/fatal/source/fatal_task.hpp b/stratosphere/fatal/source/fatal_task.hpp new file mode 100644 index 000000000..63d1ca43e --- /dev/null +++ b/stratosphere/fatal/source/fatal_task.hpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2018 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 "fatal_types.hpp" + +class IFatalTask { + protected: + FatalContext *ctx; + u64 title_id; + public: + virtual Result Run() = 0; + virtual const char *GetName() const = 0; +}; + +void RunFatalTasks(FatalContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event); diff --git a/stratosphere/fatal/source/fatal_types.hpp b/stratosphere/fatal/source/fatal_types.hpp index 3ca5b85fc..46fafeec4 100644 --- a/stratosphere/fatal/source/fatal_types.hpp +++ b/stratosphere/fatal/source/fatal_types.hpp @@ -19,6 +19,7 @@ #include enum FatalResult : Result { + FatalResult_AlreadyThrown = 0x6A3, FatalResult_TooManyEvents = 0x8A3, FatalResult_InRepairWithoutVolHeld = 0xAA3, FatalResult_InRepairWithoutTimeReviserCartridge = 0xCA3, @@ -91,6 +92,11 @@ struct FatalCpuContext { u32 type; }; +struct FatalContext { + u32 error_code; + FatalCpuContext cpu_ctx; +}; + static_assert(sizeof(Aarch64CpuContext) == 0x248, "Aarch64CpuContext definition!"); static_assert(sizeof(Aarch32CpuContext) == 0xE0, "Aarch32CpuContext definition!"); static_assert(sizeof(FatalCpuContext) == 0x250, "FatalCpuContext definition!"); diff --git a/stratosphere/fatal/source/fatal_user.cpp b/stratosphere/fatal/source/fatal_user.cpp index b97e2bb25..f683de4cc 100644 --- a/stratosphere/fatal/source/fatal_user.cpp +++ b/stratosphere/fatal/source/fatal_user.cpp @@ -16,9 +16,61 @@ #include #include "fatal_user.hpp" +#include "fatal_event_manager.hpp" +#include "fatal_task.hpp" -Result UserService::ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *context) { - /* TODO */ +static bool g_thrown = false; + +static Result SetThrown() { + /* This should be fine, since fatal only has a single IPC thread. */ + if (g_thrown) { + return FatalResult_AlreadyThrown; + } + + g_thrown = true; + return 0; +} + +Result UserService::ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu_ctx) { + Result rc = 0; + FatalContext ctx; + ctx.error_code = error; + ctx.cpu_ctx = *cpu_ctx; + + /* Get title id. On failure, it'll be zero. */ + u64 title_id = 0; + pminfoGetTitleId(&title_id, pid); + + switch (policy) { + case FatalType_ErrorReport: + /* TODO: Don't write an error report. */ + case FatalType_ErrorReportAndErrorScreen: + case FatalType_ErrorScreen: + { + /* Ensure we only throw once. */ + if (R_FAILED((rc = SetThrown()))) { + return rc; + } + + /* Signal that fatal is about to happen. */ + GetEventManager()->SignalEvents(); + + /* Create events. */ + Event erpt_event; + Event battery_event; + if (R_FAILED(eventCreate(&erpt_event, true)) || R_FAILED(eventCreate(&battery_event, true))) { + std::abort(); + } + + /* Run tasks. */ + RunFatalTasks(&ctx, title_id, policy == FatalType_ErrorReportAndErrorScreen, &erpt_event, &battery_event); + } + break; + default: + /* N aborts here. Should we just return an error code? */ + std::abort(); + } + return 0; }