From 39d041466d4b5f78abcb1f99edd8c36f187b738d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 18 Jul 2019 19:09:35 -0700 Subject: [PATCH] fatal: refactor into sts namespace --- .../creport/source/creport_crash_report.cpp | 21 +- stratosphere/fatal/fatal.json | 9 +- .../{ams_logo.hpp => fatal_ams_logo.inc} | 9 +- stratosphere/fatal/source/fatal_config.cpp | 126 ++-- stratosphere/fatal/source/fatal_config.hpp | 91 ++- stratosphere/fatal/source/fatal_debug.cpp | 428 ++++++----- stratosphere/fatal/source/fatal_debug.hpp | 128 +--- .../fatal/source/fatal_event_manager.cpp | 44 +- .../fatal/source/fatal_event_manager.hpp | 26 +- stratosphere/fatal/source/fatal_font.cpp | 374 +++++----- stratosphere/fatal/source/fatal_font.hpp | 40 +- stratosphere/fatal/source/fatal_main.cpp | 21 +- stratosphere/fatal/source/fatal_repair.cpp | 171 +++-- stratosphere/fatal/source/fatal_repair.hpp | 6 +- stratosphere/fatal/source/fatal_service.cpp | 161 ++++ stratosphere/fatal/source/fatal_service.hpp | 58 ++ ...l_throw.hpp => fatal_service_for_self.hpp} | 8 +- stratosphere/fatal/source/fatal_task.cpp | 82 ++- stratosphere/fatal/source/fatal_task.hpp | 35 +- .../fatal/source/fatal_task_clock.cpp | 91 ++- .../fatal/source/fatal_task_clock.hpp | 18 +- .../fatal/source/fatal_task_error_report.cpp | 226 +++--- .../fatal/source/fatal_task_error_report.hpp | 22 +- .../fatal/source/fatal_task_power.cpp | 292 +++++--- .../fatal/source/fatal_task_power.hpp | 41 +- .../fatal/source/fatal_task_screen.cpp | 686 ++++++++++-------- .../fatal/source/fatal_task_screen.hpp | 36 +- .../fatal/source/fatal_task_sound.cpp | 110 +-- .../fatal/source/fatal_task_sound.hpp | 17 +- stratosphere/fatal/source/fatal_throw.cpp | 121 --- stratosphere/fatal/source/fatal_types.hpp | 159 ---- stratosphere/fatal/source/fatal_user.cpp | 37 - stratosphere/fatal/source/fatal_user.hpp | 41 -- .../libstratosphere/include/stratosphere.hpp | 1 + .../include/stratosphere/fatal.hpp} | 7 +- .../stratosphere/fatal/fatal_types.hpp | 340 +++++++++ .../include/stratosphere/results.hpp | 1 + .../stratosphere/results/err_results.hpp} | 18 +- 38 files changed, 2176 insertions(+), 1926 deletions(-) rename stratosphere/fatal/source/{ams_logo.hpp => fatal_ams_logo.inc} (99%) create mode 100644 stratosphere/fatal/source/fatal_service.cpp create mode 100644 stratosphere/fatal/source/fatal_service.hpp rename stratosphere/fatal/source/{fatal_throw.hpp => fatal_service_for_self.hpp} (79%) delete mode 100644 stratosphere/fatal/source/fatal_throw.cpp delete mode 100644 stratosphere/fatal/source/fatal_types.hpp delete mode 100644 stratosphere/fatal/source/fatal_user.cpp delete mode 100644 stratosphere/fatal/source/fatal_user.hpp rename stratosphere/{fatal/source/fatal_private.cpp => libstratosphere/include/stratosphere/fatal.hpp} (77%) create mode 100644 stratosphere/libstratosphere/include/stratosphere/fatal/fatal_types.hpp rename stratosphere/{fatal/source/fatal_private.hpp => libstratosphere/include/stratosphere/results/err_results.hpp} (62%) diff --git a/stratosphere/creport/source/creport_crash_report.cpp b/stratosphere/creport/source/creport_crash_report.cpp index fe2cfdd82..f15f9ab57 100644 --- a/stratosphere/creport/source/creport_crash_report.cpp +++ b/stratosphere/creport/source/creport_crash_report.cpp @@ -115,19 +115,22 @@ namespace sts::creport { } } - void CrashReport::GetFatalContext(FatalContext *out) const { + void CrashReport::GetFatalContext(FatalContext *_out) const { + static_assert(sizeof(*_out) == sizeof(sts::fatal::CpuContext)); + sts::fatal::CpuContext *out = reinterpret_cast(_out); std::memset(out, 0, sizeof(*out)); /* TODO: Support generating 32-bit fatal contexts? */ - out->is_aarch32 = false; + out->architecture = fatal::CpuContext::Architecture_Aarch64; out->type = static_cast(this->exception_info.type); - for (size_t i = 0; i < 29; i++) { - out->aarch64_ctx.x[i] = this->crashed_thread.GetGeneralPurposeRegister(i); + for (size_t i = 0; i < fatal::aarch64::RegisterName_FP; i++) { + out->aarch64_ctx.SetRegisterValue(static_cast(i), this->crashed_thread.GetGeneralPurposeRegister(i)); } - out->aarch64_ctx.fp = this->crashed_thread.GetFP(); - out->aarch64_ctx.lr = this->crashed_thread.GetLR(); - out->aarch64_ctx.pc = this->crashed_thread.GetPC(); + out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_FP, this->crashed_thread.GetFP()); + out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_LR, this->crashed_thread.GetLR()); + out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_SP, this->crashed_thread.GetSP()); + out->aarch64_ctx.SetRegisterValue(fatal::aarch64::RegisterName_PC, this->crashed_thread.GetPC()); out->aarch64_ctx.stack_trace_size = this->crashed_thread.GetStackTraceSize(); for (size_t i = 0; i < out->aarch64_ctx.stack_trace_size; i++) { @@ -135,11 +138,11 @@ namespace sts::creport { } if (this->module_list.GetModuleCount()) { - out->aarch64_ctx.start_address = this->module_list.GetModuleStartAddress(0); + out->aarch64_ctx.SetBaseAddress(this->module_list.GetModuleStartAddress(0)); } /* For ams fatal, which doesn't use afsr0, pass title_id instead. */ - out->aarch64_ctx.afsr0 = this->process_info.title_id; + out->aarch64_ctx.SetTitleIdForAtmosphere(ncm::TitleId{this->process_info.title_id}); } void CrashReport::ProcessExceptions() { diff --git a/stratosphere/fatal/fatal.json b/stratosphere/fatal/fatal.json index f2365d850..affd6c151 100644 --- a/stratosphere/fatal/fatal.json +++ b/stratosphere/fatal/fatal.json @@ -92,5 +92,12 @@ }, { "type": "handle_table_size", "value": 128 - }] + }, + { + "type": "debug_flags", + "value": { + "allow_debug": false, + "force_debug": true + } + }] } \ No newline at end of file diff --git a/stratosphere/fatal/source/ams_logo.hpp b/stratosphere/fatal/source/fatal_ams_logo.inc similarity index 99% rename from stratosphere/fatal/source/ams_logo.hpp rename to stratosphere/fatal/source/fatal_ams_logo.inc index b7e76b355..ac2ccfe0a 100644 --- a/stratosphere/fatal/source/ams_logo.hpp +++ b/stratosphere/fatal/source/fatal_ams_logo.inc @@ -14,11 +14,10 @@ * along with this program. If not, see . */ -#pragma once -#define AMS_LOGO_WIDTH 0xA0 -#define AMS_LOGO_HEIGHT 0x80 +constexpr size_t AtmosphereLogoWidth = 0xA0; +constexpr size_t AtmosphereLogoHeight = 0x80; -static constexpr u16 AMS_LOGO_BIN[] = { +static constexpr u16 AtmosphereLogoData[] = { 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, @@ -1301,4 +1300,4 @@ static constexpr u16 AMS_LOGO_BIN[] = { 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9, 0x39C9 }; -static_assert(sizeof(AMS_LOGO_BIN) == AMS_LOGO_WIDTH * AMS_LOGO_HEIGHT * sizeof(*AMS_LOGO_BIN), "Logo definition!"); \ No newline at end of file +static_assert(util::size(AtmosphereLogoData) == AtmosphereLogoWidth * AtmosphereLogoHeight, "Logo definition!"); \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_config.cpp b/stratosphere/fatal/source/fatal_config.cpp index adb997008..eaf0ec8f3 100644 --- a/stratosphere/fatal/source/fatal_config.cpp +++ b/stratosphere/fatal/source/fatal_config.cpp @@ -14,78 +14,82 @@ * along with this program. If not, see . */ -#include -#include "fatal_types.hpp" #include "fatal_config.hpp" -static FatalConfig g_fatal_config = {}; +namespace sts::fatal::srv { -static IEvent *g_fatal_settings_event = nullptr; + namespace { -FatalConfig *GetFatalConfig() { - return &g_fatal_config; -} + /* Global config. */ + FatalConfig g_config; -static void UpdateLanguageCode() { - setGetLanguageCode(&GetFatalConfig()->language_code); -} - -IEvent *GetFatalSettingsEvent() { - if (g_fatal_settings_event == nullptr) { - Event evt; - if (R_FAILED(setsysBindFatalDirtyFlagEvent(&evt))) { - std::abort(); + /* Event creator. */ + IEvent *CreateFatalDirtyEvent() { + Event evt; + R_ASSERT(setsysBindFatalDirtyFlagEvent(&evt)); + return LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) { + u64 flags_0, flags_1; + if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) { + g_config.UpdateLanguageCode(); + } + return ResultSuccess; + }, true); } - g_fatal_settings_event = LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) { - u64 flags_0, flags_1; - if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) { - UpdateLanguageCode(); - } - return ResultSuccess; - }, true); + + /* Global event. */ + IEvent *g_fatal_dirty_event = CreateFatalDirtyEvent(); } - return g_fatal_settings_event; -} + FatalConfig::FatalConfig() { + /* Clear this. */ + std::memset(this, 0, sizeof(*this)); -static void SetupConfigLanguages() { - FatalConfig *config = GetFatalConfig(); + /* Get information from set. */ + setsysGetSerialNumber(this->serial_number); + setsysGetFirmwareVersion(&this->firmware_version); + setsysGetFlag(SetSysFlag_Quest, &this->quest_flag); + this->UpdateLanguageCode(); - /* Defaults. */ - config->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n"; + /* Read information from settings. */ + setsysGetSettingsItemValue("fatal", "transition_to_fatal", &this->transition_to_fatal, sizeof(this->transition_to_fatal)); + setsysGetSettingsItemValue("fatal", "show_extra_info", &this->show_extra_info, sizeof(this->show_extra_info)); + setsysGetSettingsItemValue("fatal", "quest_reboot_interval_second", &this->quest_reboot_interval_second, sizeof(this->quest_reboot_interval_second)); - if (config->quest_flag) { - config->error_desc = u8"Please call 1-800-875-1852 for service.\n"; - } else { - config->error_desc = u8"An error has occured.\n\n" - u8"Please press the POWER Button to restart the console normally, or a VOL button\n" - u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" - u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" - u8"If the problem persists, refer to the Nintendo Support Website.\n" - u8"support.nintendo.com/switch/error\n"; + /* Atmosphere extension for automatic reboot. */ + if (R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "fatal_auto_reboot_interval", &this->fatal_auto_reboot_interval, sizeof(this->fatal_auto_reboot_interval)))) { + this->fatal_auto_reboot_enabled = this->fatal_auto_reboot_interval != 0; + } + + /* Setup messages. */ + { + this->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n"; + + this->error_desc = u8"An error has occured.\n\n" + u8"Please press the POWER Button to restart the console normally, or a VOL button\n" + u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" + u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" + u8"If the problem persists, refer to the Nintendo Support Website.\n" + u8"support.nintendo.com/switch/error\n"; + + /* If you're running Atmosphere on a quest unit for some reason, talk to me on discord. */ + this->quest_desc = u8"Please call 1-800-875-1852 for service.\n\n" + u8"Also, please be aware that running Atmosphere on a Quest device is not fully\n" + u8"supported. Perhaps try booting your device without Atmosphere before calling\n" + u8"an official Nintendo service hotline. If you encounter further issues, please\n" + u8"contact SciresM#0524 on Discord, or via some other means.\n"; + + /* TODO: Try to load dynamically? */ + /* FsStorage message_storage; */ + /* TODO: if (R_SUCCEEDED(fsOpenDataStorageByDataId(0x010000000000081D, "fatal_msg"))) { ... } */ + } + } + + IEvent *GetFatalDirtyEvent() { + return g_fatal_dirty_event; + } + + const FatalConfig &GetFatalConfig() { + return g_config; } - /* TODO: Try to load dynamically. */ - /* FsStorage message_storage; */ - /* TODO: if (R_SUCCEEDED(fsOpenDataStorageByDataId(0x010000000000081D, "fatal_msg"))) { ... } */ -} - -void InitializeFatalConfig() { - FatalConfig *config = GetFatalConfig(); - - memset(config, 0, sizeof(*config)); - setsysGetSerialNumber(config->serial_number); - setsysGetFirmwareVersion(&config->firmware_version); - UpdateLanguageCode(); - - setsysGetSettingsItemValue("fatal", "transition_to_fatal", &config->transition_to_fatal, sizeof(config->transition_to_fatal)); - setsysGetSettingsItemValue("fatal", "show_extra_info", &config->show_extra_info, sizeof(config->show_extra_info)); - setsysGetSettingsItemValue("fatal", "quest_reboot_interval_second", &config->quest_reboot_interval_second, sizeof(config->quest_reboot_interval_second)); - - setsysGetFlag(SetSysFlag_Quest, &config->quest_flag); - - config->is_auto_reboot_enabled = R_SUCCEEDED(setsysGetSettingsItemValue("atmosphere", "fatal_auto_reboot_interval", &config->fatal_auto_reboot_interval, sizeof(config->fatal_auto_reboot_interval))); - config->is_auto_reboot_enabled &= (config->fatal_auto_reboot_interval != 0); - - SetupConfigLanguages(); } diff --git a/stratosphere/fatal/source/fatal_config.hpp b/stratosphere/fatal/source/fatal_config.hpp index 2a4eba2d6..5f805a088 100644 --- a/stratosphere/fatal/source/fatal_config.hpp +++ b/stratosphere/fatal/source/fatal_config.hpp @@ -18,22 +18,79 @@ #include #include -struct FatalConfig { - char serial_number[0x18]; - SetSysFirmwareVersion firmware_version; - u64 language_code; - u64 quest_reboot_interval_second; - bool transition_to_fatal; - bool show_extra_info; - bool quest_flag; - const char *error_msg; - const char *error_desc; - const char *quest_desc; - u64 fatal_auto_reboot_interval; - bool is_auto_reboot_enabled; -}; +namespace sts::fatal::srv { -IEvent *GetFatalSettingsEvent(); -FatalConfig *GetFatalConfig(); + class FatalConfig { + private: + char serial_number[0x18]; + SetSysFirmwareVersion firmware_version; + u64 language_code; + u64 quest_reboot_interval_second; + bool transition_to_fatal; + bool show_extra_info; + bool quest_flag; + const char *error_msg; + const char *error_desc; + const char *quest_desc; + u64 fatal_auto_reboot_interval; + bool fatal_auto_reboot_enabled; + public: + FatalConfig(); -void InitializeFatalConfig(); \ No newline at end of file + const char *GetSerialNumber() const { + return this->serial_number; + } + + const SetSysFirmwareVersion &GetFirmwareVersion() const { + return this->firmware_version; + } + + void UpdateLanguageCode() { + setGetLanguageCode(&this->language_code); + } + + u64 GetLanguageCode() const { + return this->language_code; + } + + bool ShouldTransitionToFatal() const { + return this->transition_to_fatal; + } + + bool ShouldShowExtraInfo() const { + return this->show_extra_info; + } + + bool IsQuest() const { + return this->quest_flag; + } + + bool IsFatalRebootEnabled() const { + return this->fatal_auto_reboot_enabled; + } + + u64 GetQuestRebootTimeoutInterval() const { + return this->quest_reboot_interval_second * 1'000'000'000ul; + } + + u64 GetFatalRebootTimeoutInterval() const { + return this->fatal_auto_reboot_interval * 1'000'000ul; + } + + const char *GetErrorMessage() const { + return this->error_msg; + } + + const char *GetErrorDescription() const { + if (this->IsQuest()) { + return this->quest_desc; + } else { + return this->error_desc; + } + } + }; + + IEvent *GetFatalDirtyEvent(); + const FatalConfig &GetFatalConfig(); + +} diff --git a/stratosphere/fatal/source/fatal_debug.cpp b/stratosphere/fatal/source/fatal_debug.cpp index d8666a92b..fc008b963 100644 --- a/stratosphere/fatal/source/fatal_debug.cpp +++ b/stratosphere/fatal/source/fatal_debug.cpp @@ -13,234 +13,101 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -#include -#include +#include #include "fatal_debug.hpp" #include "fatal_config.hpp" -static bool IsAddressReadable(Handle debug_handle, u64 address, u64 size, MemoryInfo *o_mi) { - MemoryInfo mi; - u32 pi; +namespace sts::fatal::srv { - if (o_mi == NULL) { - o_mi = &mi; - } + namespace { - if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, debug_handle, address))) { - return false; - } + constexpr u32 SvcSendSyncRequestInstruction = 0xD4000421; - /* Must be readable */ - if ((o_mi->perm & Perm_R) != Perm_R) { - return false; - } + struct StackFrame { + u64 fp; + u64 lr; + }; - /* Must have space for both userdata address and userdata size. */ - if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) { - return false; - } + bool IsThreadFatalCaller(u32 error_code, u32 debug_handle, u64 thread_id, u64 thread_tls_addr, ThreadContext *thread_ctx) { + /* Verify that the thread is running or waiting. */ + { + u64 _; + u32 _thread_state; + if (R_FAILED(svcGetDebugThreadParam(&_, &_thread_state, debug_handle, thread_id, DebugThreadParam_State))) { + return false; + } - return true; -} - -static bool CheckThreadIsFatalCaller(FatalThrowContext *ctx, u64 debug_handle, u64 thread_id, u64 thread_tls_addr, ThreadContext *thread_ctx) { - /* Verify that the thread is running or waiting. */ - { - u64 _; - u32 thread_state; - if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, thread_id, DebugThreadParam_State))) { - return false; - } - - if (thread_state > 1) { - return false; - } - } - - /* Get the thread context. */ - if (R_FAILED(svcGetDebugThreadContext(thread_ctx, debug_handle, thread_id, 0xF))) { - return false; - } - - /* Check if PC is readable. */ - if (!IsAddressReadable(debug_handle, thread_ctx->pc.x, sizeof(u32), NULL)) { - return false; - } - - /* Try to read the current instruction. */ - u32 insn; - if (R_FAILED(svcReadDebugProcessMemory(&insn, debug_handle, thread_ctx->pc.x, sizeof(insn)))) { - return false; - } - - /* If the instruction isn't svcSendSyncRequest, it's not the fatal caller. */ - if (insn != 0xD4000421) { - return false; - } - - /* The fatal caller will have readable tls. */ - if (!IsAddressReadable(debug_handle, thread_tls_addr, 0x100, NULL)) { - return false; - } - - /* Read in the fatal caller's tls. */ - u8 thread_tls[0x100]; - if (R_FAILED(svcReadDebugProcessMemory(thread_tls, debug_handle, thread_tls_addr, sizeof(thread_tls)))) { - return false; - } - - /* Replace our tls with the fatal caller's. */ - std::memcpy(armGetTls(), thread_tls, sizeof(thread_tls)); - - /* Parse the command that the thread sent. */ - { - IpcParsedCommand r; - if (R_FAILED(ipcParse(&r))) { - return false; - } - - /* Fatal command takes in a PID, only one buffer max. */ - if (!r.HasPid || r.NumStatics || r.NumStaticsOut || r.NumHandles) { - return false; - } - - struct { - u32 magic; - u32 version; - u64 cmd_id; - u32 err_code; - } *raw = (decltype(raw))(r.Raw); - - if (raw->magic != SFCI_MAGIC) { - return false; - } - - if (raw->cmd_id > 2) { - return false; - } - - if (raw->cmd_id != 2 && r.NumBuffers) { - return false; - } - - if (raw->err_code != ctx->error_code) { - return false; - } - } - - /* We found our caller. */ - return true; -} - -void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) { - Handle debug_handle; - if (R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid))) { - /* Ensure we close the debugged process. */ - ON_SCOPE_EXIT { svcCloseHandle(debug_handle); }; - - /* First things first, check if process is 64 bits, and get list of thread infos. */ - std::unordered_map thread_id_to_tls; - { - bool got_attach_process = false; - DebugEventInfo d; - while (R_SUCCEEDED(svcGetDebugEvent((u8 *)&d, debug_handle))) { - if (d.type == DebugEventType::AttachProcess) { - ctx->cpu_ctx.is_aarch32 = (d.info.attach_process.flags & 1) == 0; - memcpy(ctx->proc_name, d.info.attach_process.name, sizeof(d.info.attach_process.name)); - got_attach_process = true; - } else if (d.type == DebugEventType::AttachThread) { - thread_id_to_tls[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address; + const svc::ThreadState thread_state = static_cast(_thread_state); + if (thread_state != svc::ThreadState::Waiting && thread_state != svc::ThreadState::Running) { + return false; } } - if (!got_attach_process) { - return; - } - } - - /* TODO: Try to collect information on 32-bit fatals. This shouldn't really matter for any real use case. */ - if (ctx->cpu_ctx.is_aarch32) { - return; - } - - /* Welcome to hell. */ - bool found_fatal_caller = false; - u64 thread_id = 0; - ThreadContext thread_ctx; - { - /* We start by trying to get a list of threads. */ - u32 thread_count; - u64 thread_ids[0x60]; - if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle))) { - return; + /* Get the thread context. */ + if (R_FAILED(svcGetDebugThreadContext(thread_ctx, debug_handle, thread_id, svc::ThreadContextFlag_All))) { + return false; } - /* We need to locate the thread that's called fatal. */ - for (u32 i = 0; i < thread_count; i++) { - const u64 cur_thread_id = thread_ids[i]; - if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) { - continue; + /* Try to read the current instruction. */ + u32 insn; + if (R_FAILED(svcReadDebugProcessMemory(&insn, debug_handle, thread_ctx->pc.x, sizeof(insn)))) { + return false; + } + + /* If the instruction isn't svcSendSyncRequest, it's not the fatal caller. */ + if (insn != SvcSendSyncRequestInstruction) { + return false; + } + + /* Read in the fatal caller's TLS. */ + u8 thread_tls[0x100]; + if (R_FAILED(svcReadDebugProcessMemory(thread_tls, debug_handle, thread_tls_addr, sizeof(thread_tls)))) { + return false; + } + + /* HACK: We want to parse the command the fatal caller sent. */ + /* The easiest way to do this is to copy their TLS over ours, and parse ours. */ + std::memcpy(armGetTls(), thread_tls, sizeof(thread_tls)); + { + IpcParsedCommand r; + if (R_FAILED(ipcParse(&r))) { + return false; } - if (CheckThreadIsFatalCaller(ctx, debug_handle, cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) { - thread_id = cur_thread_id; - found_fatal_caller = true; - break; + /* Fatal command takes in a PID, only one buffer max. */ + if (!r.HasPid || r.NumStatics || r.NumStaticsOut || r.NumHandles) { + return false; + } + + struct { + u32 magic; + u32 version; + u64 cmd_id; + u32 err_code; + } *raw = (decltype(raw))(r.Raw); + + if (raw->magic != SFCI_MAGIC) { + return false; + } + + if (raw->cmd_id > 2) { + return false; + } + + if (raw->cmd_id != 2 && r.NumBuffers) { + return false; + } + + if (raw->err_code != error_code) { + return false; } } - if (!found_fatal_caller) { - return; - } - } - if (R_FAILED(svcGetDebugThreadContext(&thread_ctx, debug_handle, thread_id, 0xF))) { - return; + + /* We found our caller. */ + return true; } - /* So we found our caller. */ - for (u32 i = 0; i < 29; i++) { - /* GetDebugThreadContext won't give us any of these registers, because thread is in SVC :( */ - ctx->has_gprs[i] = false; - } - for (u32 i = 29; i < NumAarch64Gprs; i++) { - ctx->has_gprs[i] = true; - } - ctx->cpu_ctx.aarch64_ctx.fp = thread_ctx.fp; - ctx->cpu_ctx.aarch64_ctx.lr = thread_ctx.lr; - ctx->cpu_ctx.aarch64_ctx.sp = thread_ctx.sp; - ctx->cpu_ctx.aarch64_ctx.pc = thread_ctx.pc.x; - - - /* Parse a stack trace. */ - u64 cur_fp = thread_ctx.fp; - for (unsigned int i = 0; i < sizeof(ctx->cpu_ctx.aarch64_ctx.stack_trace)/sizeof(u64); i++) { - /* Validate the current frame. */ - if (cur_fp == 0 || (cur_fp & 0xF)) { - break; - } - - /* Read a new frame. */ - StackFrame cur_frame; - if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) { - break; - } - - /* Advance to the next frame. */ - ctx->cpu_ctx.aarch64_ctx.stack_trace[ctx->cpu_ctx.aarch64_ctx.stack_trace_size++] = cur_frame.lr; - cur_fp = cur_frame.fp; - } - - /* Try to read up to 0x100 of stack. */ - for (size_t sz = 0x100; sz > 0; sz -= 0x10) { - if (IsAddressReadable(debug_handle, ctx->cpu_ctx.aarch64_ctx.sp, sz, nullptr)) { - if (R_SUCCEEDED(svcReadDebugProcessMemory(ctx->stack_dump, debug_handle, ctx->cpu_ctx.aarch64_ctx.sp, sz))) { - ctx->stack_dump_size = sz; - } - break; - } - } - - /* Helper to guess start address. */ - auto TryGuessStartAddress = [&](u64 guess) { + bool TryGuessBaseAddress(u64 *out_base_address, u32 debug_handle, u64 guess) { MemoryInfo mi; u32 pi; if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess)) || mi.perm != Perm_Rx) { @@ -255,29 +122,146 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) { if (mi.type == MemType_Unmapped) { /* Code region will be at the end of the unmapped region preceding it. */ - ctx->cpu_ctx.aarch64_ctx.start_address = mi.addr + mi.size; + *out_base_address = mi.addr + mi.size; return true; } - guess -= 4; + guess = mi.addr - 4; } return false; - }; + } - /* Parse the starting address. */ - { - if (TryGuessStartAddress(thread_ctx.pc.x)) { - return; + u64 GetBaseAddress(const ThrowContext *throw_ctx, const ThreadContext *thread_ctx, u32 debug_handle) { + u64 base_address = 0; + + if (TryGuessBaseAddress(&base_address, debug_handle, thread_ctx->pc.x)) { + return base_address; } - if (TryGuessStartAddress(thread_ctx.lr)) { - return; + + if (TryGuessBaseAddress(&base_address, debug_handle, thread_ctx->lr)) { + return base_address; } - for (size_t i = 0; i < ctx->cpu_ctx.aarch64_ctx.stack_trace_size; i++) { - if (TryGuessStartAddress(ctx->cpu_ctx.aarch64_ctx.stack_trace[i])) { - return; + + for (size_t i = 0; i < throw_ctx->cpu_ctx.aarch64_ctx.stack_trace_size; i++) { + if (TryGuessBaseAddress(&base_address, debug_handle, throw_ctx->cpu_ctx.aarch64_ctx.stack_trace[i])) { + return base_address; } } + + return base_address; } + } -} \ No newline at end of file + + void TryCollectDebugInformation(ThrowContext *ctx, u64 process_id) { + /* Try to debug the process. This may fail, if we called into ourself. */ + AutoHandle debug_handle; + if (R_FAILED(svcDebugActiveProcess(debug_handle.GetPointer(), process_id))) { + return; + } + + /* First things first, check if process is 64 bits, and get list of thread infos. */ + std::unordered_map thread_id_to_tls; + { + bool got_attach_process = false; + svc::DebugEventInfo d; + while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast(&d), debug_handle.Get()))) { + switch (d.type) { + case svc::DebugEventType::AttachProcess: + ctx->cpu_ctx.architecture = (d.info.attach_process.flags & 1) ? CpuContext::Architecture_Aarch64 : CpuContext::Architecture_Aarch32; + std::memcpy(ctx->proc_name, d.info.attach_process.name, sizeof(d.info.attach_process.name)); + got_attach_process = true; + break; + case svc::DebugEventType::AttachThread: + thread_id_to_tls[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address; + break; + case svc::DebugEventType::Exception: + case svc::DebugEventType::ExitProcess: + case svc::DebugEventType::ExitThread: + break; + } + } + + if (!got_attach_process) { + return; + } + } + + /* TODO: Try to collect information on 32-bit fatals. This shouldn't really matter for any real use case. */ + if (ctx->cpu_ctx.architecture == CpuContext::Architecture_Aarch32) { + return; + } + + /* Welcome to hell. Here, we try to identify which thread called into fatal. */ + bool found_fatal_caller = false; + u64 thread_id = 0; + ThreadContext thread_ctx; + { + /* We start by trying to get a list of threads. */ + u32 thread_count; + u64 thread_ids[0x60]; + if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle.Get()))) { + return; + } + + /* We need to locate the thread that's called fatal. */ + for (u32 i = 0; i < thread_count; i++) { + const u64 cur_thread_id = thread_ids[i]; + if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) { + continue; + } + + if (IsThreadFatalCaller(ctx->error_code, debug_handle.Get(), cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) { + thread_id = cur_thread_id; + found_fatal_caller = true; + break; + } + } + if (!found_fatal_caller) { + return; + } + } + if (R_FAILED(svcGetDebugThreadContext(&thread_ctx, debug_handle.Get(), thread_id, svc::ThreadContextFlag_All))) { + return; + } + + /* Set register states. */ + ctx->cpu_ctx.aarch64_ctx.SetRegisterValue(aarch64::RegisterName_FP, thread_ctx.fp); + ctx->cpu_ctx.aarch64_ctx.SetRegisterValue(aarch64::RegisterName_LR, thread_ctx.lr); + ctx->cpu_ctx.aarch64_ctx.SetRegisterValue(aarch64::RegisterName_SP, thread_ctx.sp); + ctx->cpu_ctx.aarch64_ctx.SetRegisterValue(aarch64::RegisterName_PC, thread_ctx.pc.x); + + /* Parse a stack trace. */ + u64 cur_fp = thread_ctx.fp; + ctx->cpu_ctx.aarch64_ctx.stack_trace_size = 0; + for (unsigned int i = 0; i < aarch64::CpuContext::MaxStackTraceDepth; i++) { + /* Validate the current frame. */ + if (cur_fp == 0 || (cur_fp & 0xF)) { + break; + } + + /* Read a new frame. */ + StackFrame cur_frame = {}; + if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle.Get(), cur_fp, sizeof(StackFrame)))) { + break; + } + + /* Advance to the next frame. */ + ctx->cpu_ctx.aarch64_ctx.stack_trace[ctx->cpu_ctx.aarch64_ctx.stack_trace_size++] = cur_frame.lr; + cur_fp = cur_frame.fp; + } + + /* Try to read up to 0x100 of stack. */ + for (size_t sz = 0x100; sz > 0; sz -= 0x10) { + if (R_SUCCEEDED(svcReadDebugProcessMemory(ctx->stack_dump, debug_handle.Get(), thread_ctx.sp, sz))) { + ctx->stack_dump_size = sz; + break; + } + } + + /* Parse the base address. */ + ctx->cpu_ctx.aarch64_ctx.SetBaseAddress(GetBaseAddress(ctx, &thread_ctx, debug_handle.Get())); + } + +} diff --git a/stratosphere/fatal/source/fatal_debug.hpp b/stratosphere/fatal/source/fatal_debug.hpp index 4166aa178..5caedc55f 100644 --- a/stratosphere/fatal/source/fatal_debug.hpp +++ b/stratosphere/fatal/source/fatal_debug.hpp @@ -18,132 +18,8 @@ #include #include -#include "fatal_types.hpp" +namespace sts::fatal::srv { -void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid); + void TryCollectDebugInformation(ThrowContext *ctx, u64 process_id); -struct StackFrame { - u64 fp; - u64 lr; -}; - -struct AttachProcessInfo { - u64 title_id; - u64 process_id; - char name[0xC]; - u32 flags; - u64 user_exception_context_address; /* 5.0.0+ */ -}; - -struct AttachThreadInfo { - u64 thread_id; - u64 tls_address; - u64 entrypoint; -}; - -/* TODO: ExitProcessInfo */ -/* TODO: ExitThreadInfo */ - -enum class DebugExceptionType : u32 { - UndefinedInstruction = 0, - InstructionAbort = 1, - DataAbort = 2, - AlignmentFault = 3, - DebuggerAttached = 4, - BreakPoint = 5, - UserBreak = 6, - DebuggerBreak = 7, - BadSvc = 8, - UnknownNine = 9, -}; - -static inline const char *GetDebugExceptionTypeStr(DebugExceptionType type) { - switch (type) { - case DebugExceptionType::UndefinedInstruction: - return "Undefined Instruction"; - case DebugExceptionType::InstructionAbort: - return "Instruction Abort"; - case DebugExceptionType::DataAbort: - return "Data Abort"; - case DebugExceptionType::AlignmentFault: - return "Alignment Fault"; - case DebugExceptionType::DebuggerAttached: - return "Debugger Attached"; - case DebugExceptionType::BreakPoint: - return "Break Point"; - case DebugExceptionType::UserBreak: - return "User Break"; - case DebugExceptionType::DebuggerBreak: - return "Debugger Break"; - case DebugExceptionType::BadSvc: - return "Bad Svc"; - case DebugExceptionType::UnknownNine: - return "Unknown Nine"; - default: - return "Unknown"; - } } - -struct UndefinedInstructionInfo { - u32 insn; -}; - -struct DataAbortInfo { - u64 address; -}; - -struct AlignmentFaultInfo { - u64 address; -}; - -struct UserBreakInfo { - u64 break_reason; - u64 address; - u64 size; -}; - -struct BadSvcInfo { - u32 id; -}; - -union SpecificExceptionInfo { - UndefinedInstructionInfo undefined_instruction; - DataAbortInfo data_abort; - AlignmentFaultInfo alignment_fault; - UserBreakInfo user_break; - BadSvcInfo bad_svc; - u64 raw; -}; - -struct ExceptionInfo { - DebugExceptionType type; - u64 address; - SpecificExceptionInfo specific; -}; - - -enum class DebugEventType : u32 { - AttachProcess = 0, - AttachThread = 1, - ExitProcess = 2, - ExitThread = 3, - Exception = 4 -}; - -union DebugInfo { - AttachProcessInfo attach_process; - AttachThreadInfo attach_thread; - ExceptionInfo exception; -}; - -struct DebugEventInfo { - DebugEventType type; - u32 flags; - u64 thread_id; - union { - DebugInfo info; - u64 _[0x40/sizeof(u64)]; - }; -}; - -static_assert(sizeof(DebugEventInfo) >= 0x50, "Incorrect DebugEventInfo definition!"); \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_event_manager.cpp b/stratosphere/fatal/source/fatal_event_manager.cpp index 1003ef115..fc6d300e4 100644 --- a/stratosphere/fatal/source/fatal_event_manager.cpp +++ b/stratosphere/fatal/source/fatal_event_manager.cpp @@ -14,39 +14,33 @@ * along with this program. If not, see . */ -#include -#include "fatal_types.hpp" #include "fatal_event_manager.hpp" -static FatalEventManager g_event_manager; +namespace sts::fatal::srv { -FatalEventManager *GetEventManager() { - return &g_event_manager; -} - -FatalEventManager::FatalEventManager() { - /* Just create all the events. */ - for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { - if (R_FAILED(eventCreate(&this->events[i], true))) { - std::abort(); + FatalEventManager::FatalEventManager() { + /* Just create all the events. */ + for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { + R_ASSERT(eventCreate(&this->events[i], true)); } } -} -Result FatalEventManager::GetEvent(Handle *out) { - std::scoped_lock lk{this->lock}; + Result FatalEventManager::GetEvent(Handle *out) { + std::scoped_lock lk{this->lock}; - /* Only allow GetEvent to succeed NumFatalEvents times. */ - if (this->events_gotten >= FatalEventManager::NumFatalEvents) { - return ResultFatalTooManyEvents; + /* Only allow GetEvent to succeed NumFatalEvents times. */ + if (this->num_events_gotten >= FatalEventManager::NumFatalEvents) { + return ResultFatalTooManyEvents; + } + + *out = this->events[this->num_events_gotten++].revent; + return ResultSuccess; } - *out = this->events[this->events_gotten++].revent; - return ResultSuccess; -} - -void FatalEventManager::SignalEvents() { - for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { - eventFire(&this->events[i]); + void FatalEventManager::SignalEvents() { + for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) { + eventFire(&this->events[i]); + } } + } diff --git a/stratosphere/fatal/source/fatal_event_manager.hpp b/stratosphere/fatal/source/fatal_event_manager.hpp index 6d12ac1f1..2b477a482 100644 --- a/stratosphere/fatal/source/fatal_event_manager.hpp +++ b/stratosphere/fatal/source/fatal_event_manager.hpp @@ -18,17 +18,19 @@ #include #include -class FatalEventManager { - private: - static constexpr size_t NumFatalEvents = 3; +namespace sts::fatal::srv { - HosMutex lock; - size_t events_gotten = 0; - Event events[NumFatalEvents]; - public: - FatalEventManager(); - Result GetEvent(Handle *out); - void SignalEvents(); -}; + class FatalEventManager { + private: + static constexpr size_t NumFatalEvents = 3; -FatalEventManager *GetEventManager(); \ No newline at end of file + HosMutex lock; + size_t num_events_gotten = 0; + Event events[NumFatalEvents]; + public: + FatalEventManager(); + Result GetEvent(Handle *out); + void SignalEvents(); + }; + +} diff --git a/stratosphere/fatal/source/fatal_font.cpp b/stratosphere/fatal/source/fatal_font.cpp index 3ad9b538b..c7b66f67c 100644 --- a/stratosphere/fatal/source/fatal_font.cpp +++ b/stratosphere/fatal/source/fatal_font.cpp @@ -14,220 +14,228 @@ * along with this program. If not, see . */ - -#include -#include "fatal_types.hpp" - -#include -#include -#include - #include #include FT_FREETYPE_H #include "fatal_config.hpp" #include "fatal_font.hpp" -static u16 *g_fb = nullptr; -static u32 (*g_unswizzle_func)(u32, u32) = nullptr; -static u16 g_font_color = 0xFFFF; -static float g_font_sz = 16.0f; -static u32 g_line_x = 0, g_cur_x = 0, g_cur_y = 0; +/* Define color conversion macros. */ +#define RGB888_TO_RGB565(r, g, b) ((((r >> 3) << 11) & 0xF800) | (((g >> 2) << 5) & 0x7E0) | ((b >> 3) & 0x1F)) +#define RGB565_GET_R8(c) ((((c >> 11) & 0x1F) << 3) | ((c >> 13) & 7)) +#define RGB565_GET_G8(c) ((((c >> 5) & 0x3F) << 2) | ((c >> 9) & 3)) +#define RGB565_GET_B8(c) ((((c >> 0) & 0x1F) << 3) | ((c >> 2) & 7)) -static u32 g_mono_adv = 0; +namespace sts::fatal::srv::font { -static PlFontData g_font; -static PlFontData g_fonts[PlSharedFontType_Total]; -static FT_Library g_library; -static FT_Face g_face; -static FT_Error g_ft_err = 0; + namespace { -static u16 Blend(u16 color, u16 bg, u8 alpha) { - const u32 c_r = RGB565_GET_R8(color); - const u32 c_g = RGB565_GET_G8(color); - const u32 c_b = RGB565_GET_B8(color); - const u32 b_r = RGB565_GET_R8(bg); - const u32 b_g = RGB565_GET_G8(bg); - const u32 b_b = RGB565_GET_B8(bg); + /* Font state globals. */ + u16 *g_frame_buffer = nullptr; + u32 (*g_unswizzle_func)(u32, u32) = nullptr; + u16 g_font_color = 0xFFFF; /* White. */ + float g_font_size = 16.0f; + u32 g_line_x = 0, g_cur_x = 0, g_cur_y = 0; - const u32 r = ((alpha * c_r) + ((0xFF - alpha) * b_r)) / 0xFF; - const u32 g = ((alpha * c_g) + ((0xFF - alpha) * b_g)) / 0xFF; - const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF; + u32 g_mono_adv = 0; - return RGB888_TO_RGB565(r, g, b); -} + PlFontData g_font; + PlFontData g_fonts[PlSharedFontType_Total]; -static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) { - u8* imageptr = bitmap->buffer; + FT_Library g_library; + FT_Face g_face; + FT_Error g_ft_err = 0; - if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return; + /* Helpers. */ + u16 Blend(u16 color, u16 bg, u8 alpha) { + const u32 c_r = RGB565_GET_R8(color); + const u32 c_g = RGB565_GET_G8(color); + const u32 c_b = RGB565_GET_B8(color); + const u32 b_r = RGB565_GET_R8(bg); + const u32 b_g = RGB565_GET_G8(bg); + const u32 b_b = RGB565_GET_B8(bg); - for (u32 tmpy = 0; tmpy < bitmap->rows; tmpy++) { - for (u32 tmpx = 0; tmpx < bitmap->width; tmpx++) { - /* Implement very simple blending, as the bitmap value is an alpha value. */ - u16 *ptr = &g_fb[g_unswizzle_func(x + tmpx, y + tmpy)]; - *ptr = Blend(g_font_color, *ptr, imageptr[tmpx]); + const u32 r = ((alpha * c_r) + ((0xFF - alpha) * b_r)) / 0xFF; + const u32 g = ((alpha * c_g) + ((0xFF - alpha) * b_g)) / 0xFF; + const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF; + + return RGB888_TO_RGB565(r, g, b); + } + + void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) { + u8* imageptr = bitmap->buffer; + + if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return; + + for (u32 tmpy = 0; tmpy < bitmap->rows; tmpy++) { + for (u32 tmpx = 0; tmpx < bitmap->width; tmpx++) { + /* Implement very simple blending, as the bitmap value is an alpha value. */ + u16 *ptr = &g_frame_buffer[g_unswizzle_func(x + tmpx, y + tmpy)]; + *ptr = Blend(g_font_color, *ptr, imageptr[tmpx]); + } + imageptr += bitmap->pitch; + } + } + + void DrawString(const char *str, bool add_line, bool mono = false) { + FT_UInt glyph_index; + FT_GlyphSlot slot = g_face->glyph; + + const size_t len = strlen(str); + + u32 cur_x = g_cur_x, cur_y = g_cur_y; + ON_SCOPE_EXIT { + if (add_line) { + /* Advance to next line. */ + g_cur_x = g_line_x; + g_cur_y = cur_y + (g_face->size->metrics.height >> 6); + } else { + g_cur_x = cur_x; + g_cur_y = cur_y; + } + }; + + for (u32 i = 0; i < len; ) { + u32 cur_char; + ssize_t unit_count = decode_utf8(&cur_char, reinterpret_cast(&str[i])); + if (unit_count <= 0) break; + i += unit_count; + + if (cur_char == '\n') { + cur_x = g_line_x; + cur_y += g_face->size->metrics.height >> 6; + continue; + } + + glyph_index = FT_Get_Char_Index(g_face, cur_char); + + g_ft_err = FT_Load_Glyph(g_face, glyph_index, FT_LOAD_DEFAULT); + + if (g_ft_err == 0) { + g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL); + } + + if (g_ft_err) { + return; + } + + DrawGlyph(&slot->bitmap, cur_x + slot->bitmap_left + ((mono && g_mono_adv > slot->advance.x) ? ((g_mono_adv - slot->advance.x) >> 7) : 0), cur_y - slot->bitmap_top); + + cur_x += (mono ? g_mono_adv : slot->advance.x) >> 6; + cur_y += slot->advance.y >> 6; + } } - imageptr += bitmap->pitch; } -} -static void DrawString(const char *str, bool add_line, bool mono = false) { - FT_UInt glyph_index; - FT_GlyphSlot slot = g_face->glyph; + void PrintLine(const char *str) { + return DrawString(str, true); + } - const size_t len = strlen(str); + void PrintFormatLine(const char *format, ...) { + char char_buf[0x400]; - u32 cur_x = g_cur_x, cur_y = g_cur_y; - ON_SCOPE_EXIT { - if (add_line) { - /* Advance to next line. */ - g_cur_x = g_line_x; - g_cur_y = cur_y + (g_face->size->metrics.height >> 6); - } else { - g_cur_x = cur_x; - g_cur_y = cur_y; - } - }; + va_list va_arg; + va_start(va_arg, format); + vsnprintf(char_buf, sizeof(char_buf), format, va_arg); + va_end(va_arg); - for (u32 i = 0; i < len; ) { - u32 cur_char; - ssize_t unit_count = decode_utf8(&cur_char, reinterpret_cast(&str[i])); - if (unit_count <= 0) break; - i += unit_count; + PrintLine(char_buf); + } - if (cur_char == '\n') { - cur_x = g_line_x; - cur_y += g_face->size->metrics.height >> 6; - continue; + void Print(const char *str) { + return DrawString(str, false); + } + + void PrintFormat(const char *format, ...) { + char char_buf[0x400]; + + va_list va_arg; + va_start(va_arg, format); + vsnprintf(char_buf, sizeof(char_buf), format, va_arg); + va_end(va_arg); + + Print(char_buf); + } + + void PrintMonospaceU64(u64 x) { + char char_buf[0x400]; + snprintf(char_buf, sizeof(char_buf), "%016lX", x); + + DrawString(char_buf, false, true); + } + + void PrintMonospaceU32(u32 x) { + char char_buf[0x400]; + snprintf(char_buf, sizeof(char_buf), "%08X", x); + + DrawString(char_buf, false, true); + } + + void PrintMonospaceBlank(u32 width) { + char char_buf[0x400] = {0}; + for (size_t i = 0; i < width && i < sizeof(char_buf); i++) { + char_buf[i] = ' '; } - glyph_index = FT_Get_Char_Index(g_face, cur_char); + DrawString(char_buf, false, true); + } - g_ft_err = FT_Load_Glyph(g_face, glyph_index, FT_LOAD_DEFAULT); + void SetFontColor(u16 color) { + g_font_color = color; + } + + void SetPosition(u32 x, u32 y) { + g_line_x = x; + g_cur_x = x; + g_cur_y = y; + } + + u32 GetX() { + return g_cur_x; + } + + u32 GetY() { + return g_cur_y; + } + + void SetFontSize(float fsz) { + g_font_size = fsz; + g_ft_err = FT_Set_Char_Size(g_face, 0, static_cast(g_font_size * 64.0f), 96, 96); + + g_ft_err = FT_Load_Glyph(g_face, FT_Get_Char_Index(g_face, 'A'), FT_LOAD_DEFAULT); if (g_ft_err == 0) { g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL); } - - if (g_ft_err) { - return; + if (g_ft_err == 0) { + g_mono_adv = g_face->glyph->advance.x; } - - DrawGlyph(&slot->bitmap, cur_x + slot->bitmap_left + ((mono && g_mono_adv > slot->advance.x) ? ((g_mono_adv - slot->advance.x) >> 7) : 0), cur_y - slot->bitmap_top); - - cur_x += (mono ? g_mono_adv : slot->advance.x) >> 6; - cur_y += slot->advance.y >> 6; - } -} - -void FontManager::PrintLine(const char *str) { - return DrawString(str, true); -} - -void FontManager::PrintFormatLine(const char *format, ...) { - char char_buf[0x400]; - - va_list va_arg; - va_start(va_arg, format); - vsnprintf(char_buf, sizeof(char_buf), format, va_arg); - va_end(va_arg); - - PrintLine(char_buf); -} - -void FontManager::Print(const char *str) { - return DrawString(str, false); -} - -void FontManager::PrintFormat(const char *format, ...) { - char char_buf[0x400]; - - va_list va_arg; - va_start(va_arg, format); - vsnprintf(char_buf, sizeof(char_buf), format, va_arg); - va_end(va_arg); - - Print(char_buf); -} - -void FontManager::PrintMonospaceU64(u64 x) { - char char_buf[0x400]; - snprintf(char_buf, sizeof(char_buf), "%016lX", x); - - DrawString(char_buf, false, true); -} - -void FontManager::PrintMonospaceU32(u32 x) { - char char_buf[0x400]; - snprintf(char_buf, sizeof(char_buf), "%08X", x); - - DrawString(char_buf, false, true); -} - -void FontManager::PrintMonospaceBlank(u32 width) { - char char_buf[0x400] = {0}; - for (size_t i = 0; i < width && i < sizeof(char_buf); i++) { - char_buf[i] = ' '; } - DrawString(char_buf, false, true); -} - -void FontManager::SetFontColor(u16 color) { - g_font_color = color; -} - -void FontManager::SetPosition(u32 x, u32 y) { - g_line_x = x; - g_cur_x = x; - g_cur_y = y; -} - -u32 FontManager::GetX() { - return g_cur_x; -} - -u32 FontManager::GetY() { - return g_cur_y; -} - -void FontManager::SetFontSize(float fsz) { - g_font_sz = fsz; - g_ft_err = FT_Set_Char_Size(g_face, 0, static_cast(g_font_sz * 64.0f), 96, 96); - - g_ft_err = FT_Load_Glyph(g_face, FT_Get_Char_Index(g_face, 'A'), FT_LOAD_DEFAULT); - - if (g_ft_err == 0) { - g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL); + void AddSpacingLines(float num_lines) { + g_cur_x = g_line_x; + g_cur_y += static_cast((static_cast(g_face->size->metrics.height) * num_lines) / 64.0f); } - if (g_ft_err == 0) { - g_mono_adv = g_face->glyph->advance.x; + + void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)) { + g_frame_buffer = fb; + g_unswizzle_func = unswizzle_func; } + + Result InitializeSharedFont() { + size_t total_fonts = 0; + + R_TRY(plGetSharedFont(GetFatalConfig().GetLanguageCode(), g_fonts, PlSharedFontType_Total, &total_fonts)); + R_TRY(plGetSharedFontByType(&g_font, PlSharedFontType_Standard)); + + g_ft_err = FT_Init_FreeType(&g_library); + if (g_ft_err) return g_ft_err; + + g_ft_err = FT_New_Memory_Face(g_library, reinterpret_cast(g_font.address), g_font.size, 0, &g_face); + if (g_ft_err) return g_ft_err; + + SetFontSize(g_font_size); + return g_ft_err; + } + } - -void FontManager::AddSpacingLines(float num_lines) { - g_cur_x = g_line_x; - g_cur_y += static_cast((static_cast(g_face->size->metrics.height) * num_lines) / 64.0f); -} - -void FontManager::ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)) { - g_fb = fb; - g_unswizzle_func = unswizzle_func; -} - -Result FontManager::InitializeSharedFont() { - size_t total_fonts = 0; - - R_TRY(plGetSharedFont(GetFatalConfig()->language_code, g_fonts, PlSharedFontType_Total, &total_fonts)); - R_TRY(plGetSharedFontByType(&g_font, PlSharedFontType_Standard)); - - g_ft_err = FT_Init_FreeType(&g_library); - if (g_ft_err) return g_ft_err; - - g_ft_err = FT_New_Memory_Face(g_library, reinterpret_cast(g_font.address), g_font.size, 0, &g_face); - if (g_ft_err) return g_ft_err; - - SetFontSize(g_font_sz); - return g_ft_err; -} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_font.hpp b/stratosphere/fatal/source/fatal_font.hpp index e8b89b89e..5ece5e004 100644 --- a/stratosphere/fatal/source/fatal_font.hpp +++ b/stratosphere/fatal/source/fatal_font.hpp @@ -19,27 +19,23 @@ #include #include -#define RGB888_TO_RGB565(r, g, b) ((((r >> 3) << 11) & 0xF800) | (((g >> 2) << 5) & 0x7E0) | ((b >> 3) & 0x1F)) -#define RGB565_GET_R8(c) ((((c >> 11) & 0x1F) << 3) | ((c >> 13) & 7)) -#define RGB565_GET_G8(c) ((((c >> 5) & 0x3F) << 2) | ((c >> 9) & 3)) -#define RGB565_GET_B8(c) ((((c >> 0) & 0x1F) << 3) | ((c >> 2) & 7)) +namespace sts::fatal::srv::font { -class FontManager { - public: - static Result InitializeSharedFont(); - static void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)); + Result InitializeSharedFont(); + void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)); - static void SetFontColor(u16 color); - static void SetPosition(u32 x, u32 y); - static u32 GetX(); - static u32 GetY(); - static void SetFontSize(float fsz); - static void AddSpacingLines(float num_lines); - static void PrintLine(const char *str); - static void PrintFormatLine(const char *format, ...); - static void Print(const char *str); - static void PrintFormat(const char *format, ...); - static void PrintMonospaceU64(u64 x); - static void PrintMonospaceU32(u32 x); - static void PrintMonospaceBlank(u32 width); -}; \ No newline at end of file + void SetFontColor(u16 color); + void SetPosition(u32 x, u32 y); + u32 GetX(); + u32 GetY(); + void SetFontSize(float fsz); + void AddSpacingLines(float num_lines); + void PrintLine(const char *str); + void PrintFormatLine(const char *format, ...); + void Print(const char *str); + void PrintFormat(const char *format, ...); + void PrintMonospaceU64(u64 x); + void PrintMonospaceU32(u32 x); + void PrintMonospaceBlank(u32 width); + +} diff --git a/stratosphere/fatal/source/fatal_main.cpp b/stratosphere/fatal/source/fatal_main.cpp index b246ab267..8d05a0967 100644 --- a/stratosphere/fatal/source/fatal_main.cpp +++ b/stratosphere/fatal/source/fatal_main.cpp @@ -23,9 +23,7 @@ #include #include -#include "fatal_types.hpp" -#include "fatal_private.hpp" -#include "fatal_user.hpp" +#include "fatal_service.hpp" #include "fatal_config.hpp" #include "fatal_repair.hpp" #include "fatal_font.hpp" @@ -123,24 +121,19 @@ void __appExit(void) { int main(int argc, char **argv) { - /* Load settings from set:sys. */ - InitializeFatalConfig(); - /* Load shared font. */ - if (R_FAILED(FontManager::InitializeSharedFont())) { - std::abort(); - } + R_ASSERT(sts::fatal::srv::font::InitializeSharedFont()); /* Check whether we should throw fatal due to repair process. */ - CheckRepairStatus(); + sts::fatal::srv::CheckRepairStatus(); - /* TODO: What's a good timeout value to use here? */ + /* Create waitable manager. */ static auto s_server_manager = WaitableManager(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("fatal:p", 4)); - s_server_manager.AddWaitable(new ServiceServer("fatal:u", 4)); - s_server_manager.AddWaitable(GetFatalSettingsEvent()); + s_server_manager.AddWaitable(new ServiceServer("fatal:p", 4)); + s_server_manager.AddWaitable(new ServiceServer("fatal:u", 4)); + s_server_manager.AddWaitable(sts::fatal::srv::GetFatalDirtyEvent()); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/fatal/source/fatal_repair.cpp b/stratosphere/fatal/source/fatal_repair.cpp index 3fd738d3e..a2ae32aac 100644 --- a/stratosphere/fatal/source/fatal_repair.cpp +++ b/stratosphere/fatal/source/fatal_repair.cpp @@ -14,93 +14,104 @@ * along with this program. If not, see . */ -#include -#include -#include "fatal_types.hpp" #include "fatal_repair.hpp" -#include "fatal_throw.hpp" +#include "fatal_service_for_self.hpp" -static bool InRepairWithoutVolHeld() { - if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { - return false; - } +namespace sts::fatal::srv { - bool in_repair; - if (R_FAILED(setsysGetFlag(SetSysFlag_InRepairProcessEnable, &in_repair)) || !in_repair) { - return false; - } + namespace { - { - GpioPadSession vol_btn; - if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) { - return true; - } - - /* Ensure we close even on early return. */ - ON_SCOPE_EXIT { gpioPadClose(&vol_btn); }; - - /* Set direction input. */ - gpioPadSetDirection(&vol_btn, GpioDirection_Input); - - /* Ensure that we're holding the volume button for a full second. */ - TimeoutHelper timeout_helper(1000000000UL); - while (!timeout_helper.TimedOut()) { - GpioValue val; - if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) { - return true; + bool IsInRepair() { + /* Before firmware 3.0.0, this wasn't implemented. */ + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + return false; } - /* Sleep for 100 ms. */ - svcSleepThread(100000000UL); + bool in_repair; + return R_SUCCEEDED(setsysGetFlag(SetSysFlag_InRepairProcessEnable, &in_repair)) && in_repair; + } + + bool IsInRepairWithoutVolHeld() { + if (IsInRepair()) { + GpioPadSession vol_btn; + if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) { + return true; + } + + /* Ensure we close even on early return. */ + ON_SCOPE_EXIT { gpioPadClose(&vol_btn); }; + + /* Set direction input. */ + gpioPadSetDirection(&vol_btn, GpioDirection_Input); + + /* Ensure that we're holding the volume button for a full second. */ + TimeoutHelper timeout_helper(1'000'000'000ul); + while (!timeout_helper.TimedOut()) { + GpioValue val; + if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) { + return true; + } + + /* Sleep for 100 ms. */ + svcSleepThread(100'000'000ul); + } + } + + return false; + } + + bool NeedsRunTimeReviser() { + /* Before firmware 5.0.0, this wasn't implemented. */ + if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { + return false; + } + + bool requires_time_reviser; + return R_SUCCEEDED(setsysGetFlag(SetSysFlag_RequiresRunRepairTimeReviser, &requires_time_reviser)) && requires_time_reviser; + } + + bool IsTimeReviserCartridgeInserted() { + FsGameCardHandle gc_hnd; + u8 gc_attr; + { + FsDeviceOperator devop; + if (R_FAILED(fsOpenDeviceOperator(&devop))) { + return false; + } + + /* Ensure we close even on early return. */ + ON_SCOPE_EXIT { fsDeviceOperatorClose(&devop); }; + + /* Check that a gamecard is inserted. */ + bool inserted; + if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&devop, &inserted)) || !inserted) { + return false; + } + + /* Check that we can retrieve the gamecard's attributes. */ + if (R_FAILED(fsDeviceOperatorGetGameCardHandle(&devop, &gc_hnd)) || R_FAILED(fsDeviceOperatorGetGameCardAttribute(&devop, &gc_hnd, &gc_attr))) { + return false; + } + } + + /* Check that the gamecard is a repair tool. */ + return (gc_attr & FsGameCardAttribute_Repair) == FsGameCardAttribute_Repair; + } + + bool IsInRepairWithoutTimeReviserCartridge() { + return NeedsRunTimeReviser() && IsTimeReviserCartridgeInserted(); + } + + } + + void CheckRepairStatus() { + if (IsInRepairWithoutVolHeld()) { + ThrowFatalForSelf(ResultFatalInRepairWithoutVolHeld); + } + + if (IsInRepairWithoutTimeReviserCartridge()) { + ThrowFatalForSelf(ResultFatalInRepairWithoutTimeReviserCartridge); } } - return false; -} - -static bool InRepairWithoutTimeReviserCartridge() { - if (GetRuntimeFirmwareVersion() < FirmwareVersion_500) { - return false; - } - - bool requires_time_reviser; - if (R_FAILED(setsysGetFlag(SetSysFlag_RequiresRunRepairTimeReviser, &requires_time_reviser)) || !requires_time_reviser) { - return false; - } - - FsGameCardHandle gc_hnd; - u8 gc_attr; - { - FsDeviceOperator devop; - if (R_FAILED(fsOpenDeviceOperator(&devop))) { - return true; - } - - /* Ensure we close even on early return. */ - ON_SCOPE_EXIT { fsDeviceOperatorClose(&devop); }; - - /* Check that a gamecard is inserted. */ - bool inserted; - if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&devop, &inserted)) || !inserted) { - return true; - } - - /* Check that we can retrieve the gamecard's attributes. */ - if (R_FAILED(fsDeviceOperatorGetGameCardHandle(&devop, &gc_hnd)) || R_FAILED(fsDeviceOperatorGetGameCardAttribute(&devop, &gc_hnd, &gc_attr))) { - return true; - } - } - - /* Check that the gamecard is a repair tool. */ - return (gc_attr & FsGameCardAttribute_Repair) == FsGameCardAttribute_Repair; -} - -void CheckRepairStatus() { - if (InRepairWithoutVolHeld()) { - ThrowFatalForSelf(ResultFatalInRepairWithoutVolHeld); - } - - if (InRepairWithoutTimeReviserCartridge()) { - ThrowFatalForSelf(ResultFatalInRepairWithoutTimeReviserCartridge); - } } diff --git a/stratosphere/fatal/source/fatal_repair.hpp b/stratosphere/fatal/source/fatal_repair.hpp index 47f068348..aa2d2b3a6 100644 --- a/stratosphere/fatal/source/fatal_repair.hpp +++ b/stratosphere/fatal/source/fatal_repair.hpp @@ -18,4 +18,8 @@ #include #include -void CheckRepairStatus(); \ No newline at end of file +namespace sts::fatal::srv { + + void CheckRepairStatus(); + +} diff --git a/stratosphere/fatal/source/fatal_service.cpp b/stratosphere/fatal/source/fatal_service.cpp new file mode 100644 index 000000000..e59b02c3e --- /dev/null +++ b/stratosphere/fatal/source/fatal_service.cpp @@ -0,0 +1,161 @@ +/* + * 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 "fatal_config.hpp" +#include "fatal_debug.hpp" +#include "fatal_service.hpp" +#include "fatal_service_for_self.hpp" +#include "fatal_event_manager.hpp" +#include "fatal_task.hpp" + +namespace sts::fatal::srv { + + namespace { + + /* Service Context. */ + class ServiceContext { + private: + ThrowContext context; + FatalEventManager event_manager; + bool has_thrown; + private: + Result TrySetHasThrown() { + if (this->has_thrown) { + return ResultFatalAlreadyThrown; + } + this->has_thrown = true; + return ResultSuccess; + } + public: + ServiceContext() { + this->context.ClearState(); + R_ASSERT(eventCreate(&this->context.erpt_event, false)); + R_ASSERT(eventCreate(&this->context.battery_event, false)); + this->has_thrown = false; + } + + Result GetEvent(Handle *out) { + return this->event_manager.GetEvent(out); + } + + Result ThrowFatal(u32 error_code, u64 process_id) { + return this->ThrowFatalWithCpuContext(error_code, process_id, FatalType_ErrorReportAndErrorScreen, {}); + } + + Result ThrowFatalWithPolicy(u32 error_code, u64 process_id, FatalType policy) { + return this->ThrowFatalWithCpuContext(error_code, process_id, policy, {}); + } + + Result ThrowFatalWithCpuContext(u32 error_code, u64 process_id, FatalType policy, const CpuContext &cpu_ctx); + }; + + /* Context global. */ + ServiceContext g_context; + + /* Throw implementation. */ + Result ServiceContext::ThrowFatalWithCpuContext(u32 error_code, u64 process_id, FatalType policy, const CpuContext &cpu_ctx) { + /* We don't support Error Report only fatals. */ + if (policy == FatalType_ErrorReport) { + return ResultSuccess; + } + + /* Note that we've thrown fatal. */ + R_TRY(this->TrySetHasThrown()); + + /* At this point we have exclusive access to this->context. */ + this->context.error_code = error_code; + this->context.cpu_ctx = cpu_ctx; + + /* Cap the stack trace to a sane limit. */ + if (cpu_ctx.architecture == CpuContext::Architecture_Aarch64) { + this->context.cpu_ctx.aarch64_ctx.stack_trace_size = std::max(size_t(this->context.cpu_ctx.aarch64_ctx.stack_trace_size), aarch64::CpuContext::MaxStackTraceDepth); + } else { + this->context.cpu_ctx.aarch32_ctx.stack_trace_size = std::max(size_t(this->context.cpu_ctx.aarch32_ctx.stack_trace_size), aarch32::CpuContext::MaxStackTraceDepth); + } + + /* Get title id. */ + pm::info::GetTitleId(&this->context.title_id, process_id); + this->context.is_creport = (this->context.title_id == ncm::TitleId::Creport); + + if (!this->context.is_creport) { + /* On firmware version 2.0.0, use debugging SVCs to collect information. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200) { + fatal::srv::TryCollectDebugInformation(&this->context, process_id); + } + } else { + /* We received info from creport. Parse title id from afsr0. */ + if (cpu_ctx.architecture == CpuContext::Architecture_Aarch64) { + this->context.title_id = cpu_ctx.aarch64_ctx.GetTitleIdForAtmosphere(); + } else { + this->context.title_id = cpu_ctx.aarch32_ctx.GetTitleIdForAtmosphere(); + } + } + + /* Decide whether to generate a report. */ + this->context.generate_error_report = (policy == FatalType_ErrorReportAndErrorScreen); + + /* Adjust error code (2000-0000 -> 2162-0002). */ + if (this->context.error_code == ResultSuccess) { + this->context.error_code = ResultErrSystemModuleAborted; + } + + switch (policy) { + case FatalType_ErrorReportAndErrorScreen: + case FatalType_ErrorScreen: + /* Signal that we're throwing. */ + this->event_manager.SignalEvents(); + + if (GetFatalConfig().ShouldTransitionToFatal()) { + RunTasks(&this->context); + } + break; + default: + /* N aborts here. Should we just return an error code? */ + std::abort(); + } + + return ResultSuccess; + } + + } + + Result ThrowFatalForSelf(Result error_code) { + u64 process_id = 0; + R_ASSERT(svcGetProcessId(&process_id, CUR_PROCESS_HANDLE)); + return g_context.ThrowFatalWithPolicy(static_cast(error_code), process_id, FatalType_ErrorScreen); + } + + Result UserService::ThrowFatal(u32 error, PidDescriptor pid_desc) { + return g_context.ThrowFatal(error, pid_desc.pid); + } + + Result UserService::ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy) { + return g_context.ThrowFatalWithPolicy(error, pid_desc.pid, policy); + } + + Result UserService::ThrowFatalWithCpuContext(u32 error, PidDescriptor pid_desc, FatalType policy, InBuffer _ctx) { + if (_ctx.num_elements < sizeof(CpuContext)) { + return g_context.ThrowFatalWithPolicy(error, pid_desc.pid, policy); + } else { + return g_context.ThrowFatalWithCpuContext(error, pid_desc.pid, policy, *reinterpret_cast(_ctx.buffer)); + } + } + + Result PrivateService::GetFatalEvent(Out out_h) { + return g_context.GetEvent(out_h.GetHandlePointer()); + } + +} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_service.hpp b/stratosphere/fatal/source/fatal_service.hpp new file mode 100644 index 000000000..9de9012ea --- /dev/null +++ b/stratosphere/fatal/source/fatal_service.hpp @@ -0,0 +1,58 @@ +/* + * 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 + +namespace sts::fatal::srv { + + class UserService final : public IServiceObject { + private: + enum class CommandId { + ThrowFatal = 0, + ThrowFatalWithPolicy = 1, + ThrowFatalWithCpuContext = 2, + }; + private: + /* Actual commands. */ + Result ThrowFatal(u32 error, PidDescriptor pid_desc); + Result ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy); + Result ThrowFatalWithCpuContext(u32 error, PidDescriptor pid_desc, FatalType policy, InBuffer _ctx); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(UserService, ThrowFatal), + MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithPolicy), + MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithCpuContext), + }; + }; + + class PrivateService final : public IServiceObject { + private: + enum class CommandId { + GetFatalEvent = 0, + }; + private: + /* Actual commands. */ + Result GetFatalEvent(Out out_h); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MAKE_SERVICE_COMMAND_META(PrivateService, GetFatalEvent), + }; + }; + +} + diff --git a/stratosphere/fatal/source/fatal_throw.hpp b/stratosphere/fatal/source/fatal_service_for_self.hpp similarity index 79% rename from stratosphere/fatal/source/fatal_throw.hpp rename to stratosphere/fatal/source/fatal_service_for_self.hpp index b6c260bbe..e1a84b938 100644 --- a/stratosphere/fatal/source/fatal_throw.hpp +++ b/stratosphere/fatal/source/fatal_service_for_self.hpp @@ -16,9 +16,9 @@ #pragma once #include -#include -#include "fatal_types.hpp" +namespace sts::fatal::srv { -Result ThrowFatalForSelf(u32 error); -Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu_ctx); + Result ThrowFatalForSelf(Result error_code); + +} diff --git a/stratosphere/fatal/source/fatal_task.cpp b/stratosphere/fatal/source/fatal_task.cpp index 8289d4a70..582b222a9 100644 --- a/stratosphere/fatal/source/fatal_task.cpp +++ b/stratosphere/fatal/source/fatal_task.cpp @@ -15,7 +15,7 @@ */ #include -#include "fatal_types.hpp" + #include "fatal_task.hpp" #include "fatal_task_error_report.hpp" @@ -24,40 +24,64 @@ #include "fatal_task_clock.hpp" #include "fatal_task_power.hpp" +namespace sts::fatal::srv { -static constexpr size_t MaxTasks = 8; -static HosThread g_task_threads[MaxTasks]; -static size_t g_num_threads = 0; + namespace { + class TaskThread { + NON_COPYABLE(TaskThread); + private: + static constexpr int TaskThreadPriority = 15; + private: + HosThread thread; + private: + static void RunTaskImpl(void *arg) { + ITask *task = reinterpret_cast(arg); -static void RunTaskThreadFunc(void *arg) { - IFatalTask *task = reinterpret_cast(arg); + if (R_FAILED(task->Run())) { + /* TODO: Log task failure, somehow? */ + } + } + public: + TaskThread() { /* ... */ } + void StartTask(ITask *task) { + R_ASSERT(this->thread.Initialize(&RunTaskImpl, task, task->GetStackSize(), TaskThreadPriority)); + R_ASSERT(this->thread.Start()); + } + }; + + class TaskManager { + NON_COPYABLE(TaskManager); + private: + static constexpr size_t MaxTasks = 8; + private: + TaskThread task_threads[MaxTasks]; + size_t task_count = 0; + public: + TaskManager() { /* ... */ } + void StartTask(ITask *task) { + if (this->task_count >= MaxTasks) { + std::abort(); + } + + this->task_threads[this->task_count++].StartTask(task); + } + }; + + /* Global task manager. */ + TaskManager g_task_manager; - if (R_FAILED(task->Run())) { - /* TODO: Log task failure, somehow? */ } - /* Finish. */ -} - -static void RunTask(IFatalTask *task, u32 stack_size = 0x4000) { - if (g_num_threads >= MaxTasks) { - std::abort(); + void RunTasks(const ThrowContext *ctx) { + g_task_manager.StartTask(GetErrorReportTask(ctx)); + g_task_manager.StartTask(GetPowerControlTask(ctx)); + g_task_manager.StartTask(GetShowFatalTask(ctx)); + g_task_manager.StartTask(GetStopSoundTask(ctx)); + g_task_manager.StartTask(GetBacklightControlTask(ctx)); + g_task_manager.StartTask(GetAdjustClockTask(ctx)); + g_task_manager.StartTask(GetPowerButtonObserveTask(ctx)); + g_task_manager.StartTask(GetStateTransitionStopTask(ctx)); } - HosThread *cur_thread = &g_task_threads[g_num_threads++]; - - cur_thread->Initialize(&RunTaskThreadFunc, task, stack_size, 15); - cur_thread->Start(); -} - -void RunFatalTasks(FatalThrowContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event) { - RunTask(new ErrorReportTask(ctx, title_id, error_report, erpt_event)); - RunTask(new PowerControlTask(ctx, title_id, erpt_event, battery_event)); - RunTask(new ShowFatalTask(ctx, title_id, battery_event), 0x10000); - RunTask(new StopSoundTask(ctx, title_id)); - RunTask(new BacklightControlTask(ctx, title_id)); - RunTask(new AdjustClockTask(ctx, title_id)); - RunTask(new PowerButtonObserveTask(ctx, title_id, erpt_event)); - RunTask(new StateTransitionStopTask(ctx, title_id)); } diff --git a/stratosphere/fatal/source/fatal_task.hpp b/stratosphere/fatal/source/fatal_task.hpp index 407e78e82..599990fa0 100644 --- a/stratosphere/fatal/source/fatal_task.hpp +++ b/stratosphere/fatal/source/fatal_task.hpp @@ -17,16 +17,29 @@ #pragma once #include #include -#include "fatal_types.hpp" +#include "fatal_service.hpp" -class IFatalTask { - protected: - FatalThrowContext *ctx; - u64 title_id; - public: - IFatalTask(FatalThrowContext *ctx, u64 tid) : ctx(ctx), title_id(tid) { } - virtual Result Run() = 0; - virtual const char *GetName() const = 0; -}; +namespace sts::fatal::srv { -void RunFatalTasks(FatalThrowContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event); + class ITask { + public: + static constexpr size_t DefaultStackSize = 0x1000; + protected: + const ThrowContext *context = nullptr; + public: + void Initialize(const ThrowContext *context) { + this->context = context; + } + + virtual Result Run() = 0; + + virtual const char *GetName() const = 0; + + virtual size_t GetStackSize() const { + return DefaultStackSize; + } + }; + + void RunTasks(const ThrowContext *ctx); + +} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_task_clock.cpp b/stratosphere/fatal/source/fatal_task_clock.cpp index ba345e6b5..4cf880145 100644 --- a/stratosphere/fatal/source/fatal_task_clock.cpp +++ b/stratosphere/fatal/source/fatal_task_clock.cpp @@ -14,41 +14,70 @@ * along with this program. If not, see . */ -#include #include "fatal_task_clock.hpp" -Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) { - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { - /* On 8.0.0+, convert to module id + use clkrst API. */ - PcvModuleId module_id; - R_TRY(pcvGetModuleId(&module_id, module)); +namespace sts::fatal::srv { - ClkrstSession session; - R_TRY(clkrstOpenSession(&session, module_id, 3)); - ON_SCOPE_EXIT { clkrstCloseSession(&session); }; - R_TRY(clkrstSetClockRate(&session, hz)); - } else { - /* On 1.0.0-7.0.1, use pcv API. */ - R_TRY(pcvSetClockRate(module, hz)); + namespace { + + /* Task definition. */ + class AdjustClockTask : public ITask { + private: + Result AdjustClockForModule(PcvModule module, u32 hz); + Result AdjustClock(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "AdjustClockTask"; + } + }; + + /* Task global. */ + AdjustClockTask g_adjust_clock_task; + + /* Task implementation. */ + Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { + /* On 8.0.0+, convert to module id + use clkrst API. */ + PcvModuleId module_id; + R_TRY(pcvGetModuleId(&module_id, module)); + + ClkrstSession session; + R_TRY(clkrstOpenSession(&session, module_id, 3)); + ON_SCOPE_EXIT { clkrstCloseSession(&session); }; + + R_TRY(clkrstSetClockRate(&session, hz)); + } else { + /* On 1.0.0-7.0.1, use pcv API. */ + R_TRY(pcvSetClockRate(module, hz)); + } + + return ResultSuccess; + } + + Result AdjustClockTask::AdjustClock() { + /* Fatal sets the CPU to 1020MHz, the GPU to 307 MHz, and the EMC to 1331MHz. */ + constexpr u32 CPU_CLOCK_1020MHZ = 0x3CCBF700L; + constexpr u32 GPU_CLOCK_307MHZ = 0x124F8000L; + constexpr u32 EMC_CLOCK_1331MHZ = 0x4F588000L; + + R_TRY(AdjustClockForModule(PcvModule_CpuBus, CPU_CLOCK_1020MHZ)); + R_TRY(AdjustClockForModule(PcvModule_GPU, GPU_CLOCK_307MHZ)); + R_TRY(AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ)); + + return ResultSuccess; + } + + Result AdjustClockTask::Run() { + return AdjustClock(); + } + + } + + ITask *GetAdjustClockTask(const ThrowContext *ctx) { + g_adjust_clock_task.Initialize(ctx); + return &g_adjust_clock_task; } - return ResultSuccess; -} - -Result AdjustClockTask::AdjustClock() { - /* Fatal sets the CPU to 1020MHz, the GPU to 307 MHz, and the EMC to 1331MHz. */ - constexpr u32 CPU_CLOCK_1020MHZ = 0x3CCBF700L; - constexpr u32 GPU_CLOCK_307MHZ = 0x124F8000L; - constexpr u32 EMC_CLOCK_1331MHZ = 0x4F588000L; - - R_TRY(AdjustClockForModule(PcvModule_CpuBus, CPU_CLOCK_1020MHZ)); - R_TRY(AdjustClockForModule(PcvModule_GPU, GPU_CLOCK_307MHZ)); - R_TRY(AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ)); - - return ResultSuccess; -} - -Result AdjustClockTask::Run() { - return AdjustClock(); } diff --git a/stratosphere/fatal/source/fatal_task_clock.hpp b/stratosphere/fatal/source/fatal_task_clock.hpp index 573cd8739..117312b51 100644 --- a/stratosphere/fatal/source/fatal_task_clock.hpp +++ b/stratosphere/fatal/source/fatal_task_clock.hpp @@ -15,18 +15,10 @@ */ #pragma once -#include -#include #include "fatal_task.hpp" -class AdjustClockTask : public IFatalTask { - private: - Result AdjustClockForModule(PcvModule module, u32 hz); - Result AdjustClock(); - public: - AdjustClockTask(FatalThrowContext *ctx, u64 title_id) : IFatalTask(ctx, title_id) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "AdjustClockTask"; - } -}; \ No newline at end of file +namespace sts::fatal::srv { + + ITask *GetAdjustClockTask(const ThrowContext *ctx); + +} diff --git a/stratosphere/fatal/source/fatal_task_error_report.cpp b/stratosphere/fatal/source/fatal_task_error_report.cpp index 01f232e4a..3b4ec6fe6 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.cpp +++ b/stratosphere/fatal/source/fatal_task_error_report.cpp @@ -17,133 +17,141 @@ #include #include #include -#include #include -#include "fatal_task_error_report.hpp" #include "fatal_config.hpp" +#include "fatal_task_error_report.hpp" -void ErrorReportTask::EnsureReportDirectories() { - char path[FS_MAX_PATH]; - strcpy(path, "sdmc:/atmosphere"); - mkdir(path, S_IRWXU); - strcat(path, "/fatal_reports"); - mkdir(path, S_IRWXU); - strcat(path, "/dumps"); - mkdir(path, S_IRWXU); -} +namespace sts::fatal::srv { -bool ErrorReportTask::GetCurrentTime(u64 *out) { - *out = 0; + namespace { - /* Verify that pcv isn't dead. */ - { - bool has_time_service; - DoWithSmSession([&]() { - Handle dummy; - if (R_SUCCEEDED(smRegisterService(&dummy, "time:s", false, 0x20))) { - svcCloseHandle(dummy); - has_time_service = false; - } else { - has_time_service = true; - } - }); - if (!has_time_service) { - return false; + /* Helpers. */ + void TryEnsureReportDirectories() { + mkdir("sdmc:/atmosphere", S_IRWXU); + mkdir("sdmc:/atmosphere/fatal_reports", S_IRWXU); + mkdir("sdmc:/atmosphere/fatal_reports/dumps", S_IRWXU); } - } - /* Try to get the current time. */ - bool success = true; - DoWithSmSession([&]() { - success &= R_SUCCEEDED(timeInitialize()); - }); - if (success) { - success &= R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out)); - timeExit(); - } - return success; -} + bool TryGetCurrentTimestamp(u64 *out) { + /* Clear output. */ + *out = 0; -void ErrorReportTask::SaveReportToSdCard() { - char file_path[FS_MAX_PATH]; - - /* Ensure path exists. */ - EnsureReportDirectories(); - - /* Get a timestamp. */ - u64 timestamp; - if (!GetCurrentTime(×tamp)) { - timestamp = svcGetSystemTick(); - } - - /* Open report file. */ - snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/%011lu_%016lx.log", timestamp, this->title_id); - FILE *f_report = fopen(file_path, "w"); - if (f_report != NULL) { - ON_SCOPE_EXIT { fclose(f_report); }; - - fprintf(f_report, "Atmosphère Fatal Report (v1.0):\n"); - fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->ctx->error_code, R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code)); - fprintf(f_report, "Title ID: %016lx\n", this->title_id); - if (strlen(this->ctx->proc_name)) { - fprintf(f_report, "Process Name: %s\n", this->ctx->proc_name); - } - fprintf(f_report, u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); - - if (this->ctx->cpu_ctx.is_aarch32) { - fprintf(f_report, "General Purpose Registers:\n"); - for (size_t i = 0; i < NumAarch32Gprs; i++) { - if (this->ctx->has_gprs[i]) { - fprintf(f_report, " %3s: %08x\n", Aarch32GprNames[i], this->ctx->cpu_ctx.aarch32_ctx.r[i]); + /* Check if we have time service. */ + { + bool has_time_service = false; + if (R_FAILED(sm::HasService(&has_time_service, sm::ServiceName::Encode("time:s"))) || !has_time_service) { + return false; } } - fprintf(f_report, " PC: %08x\n", this->ctx->cpu_ctx.aarch32_ctx.pc); - fprintf(f_report, "Start Address: %08x\n", this->ctx->cpu_ctx.aarch32_ctx.start_address); - fprintf(f_report, "Stack Trace:\n"); - for (unsigned int i = 0; i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size; i++) { - fprintf(f_report, " ReturnAddress[%02u]: %08x\n", i, this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i]); + + /* Try to get the current time. */ + { + sm::ScopedServiceHolder time_holder; + return time_holder && R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out)); } - } else { - fprintf(f_report, "General Purpose Registers:\n"); - for (size_t i = 0; i < NumAarch64Gprs; i++) { - if (this->ctx->has_gprs[i]) { - fprintf(f_report, " %3s: %016lx\n", Aarch64GprNames[i], this->ctx->cpu_ctx.aarch64_ctx.x[i]); + } + + /* Task definition. */ + class ErrorReportTask : public ITask { + private: + void SaveReportToSdCard(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "WriteErrorReport"; + } + }; + + /* Task globals. */ + ErrorReportTask g_error_report_task; + + /* Task Implementation. */ + void ErrorReportTask::SaveReportToSdCard() { + char file_path[FS_MAX_PATH]; + + /* Try to Ensure path exists. */ + TryEnsureReportDirectories(); + + /* Get a timestamp. */ + u64 timestamp; + if (!TryGetCurrentTimestamp(×tamp)) { + timestamp = svcGetSystemTick(); + } + + /* Open report file. */ + snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/%011lu_%016lx.log", timestamp, static_cast(this->context->title_id)); + FILE *f_report = fopen(file_path, "w"); + if (f_report != NULL) { + ON_SCOPE_EXIT { fclose(f_report); }; + + fprintf(f_report, "Atmosphère Fatal Report (v1.0):\n"); + fprintf(f_report, "Result: 0x%X (2%03d-%04d)\n\n", this->context->error_code, R_MODULE(this->context->error_code), R_DESCRIPTION(this->context->error_code)); + fprintf(f_report, "Title ID: %016lx\n", static_cast(this->context->title_id)); + if (strlen(this->context->proc_name)) { + fprintf(f_report, "Process Name: %s\n", this->context->proc_name); + } + fprintf(f_report, u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig().GetFirmwareVersion().display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); + + if (this->context->cpu_ctx.architecture == CpuContext::Architecture_Aarch32) { + fprintf(f_report, "General Purpose Registers:\n"); + for (size_t i = 0; i <= aarch32::RegisterName_PC; i++) { + if (this->context->cpu_ctx.aarch32_ctx.HasRegisterValue(static_cast(i))) { + fprintf(f_report, " %3s: %08x\n", aarch32::CpuContext::RegisterNameStrings[i], this->context->cpu_ctx.aarch32_ctx.r[i]); + } + } + fprintf(f_report, "Start Address: %08x\n", this->context->cpu_ctx.aarch32_ctx.base_address); + fprintf(f_report, "Stack Trace:\n"); + for (unsigned int i = 0; i < this->context->cpu_ctx.aarch32_ctx.stack_trace_size; i++) { + fprintf(f_report, " ReturnAddress[%02u]: %08x\n", i, this->context->cpu_ctx.aarch32_ctx.stack_trace[i]); + } + } else { + fprintf(f_report, "General Purpose Registers:\n"); + for (size_t i = 0; i <= aarch64::RegisterName_PC; i++) { + if (this->context->cpu_ctx.aarch64_ctx.HasRegisterValue(static_cast(i))) { + fprintf(f_report, " %3s: %016lx\n", aarch64::CpuContext::RegisterNameStrings[i], this->context->cpu_ctx.aarch64_ctx.x[i]); + } + } + fprintf(f_report, "Start Address: %016lx\n", this->context->cpu_ctx.aarch64_ctx.base_address); + fprintf(f_report, "Stack Trace:\n"); + for (unsigned int i = 0; i < this->context->cpu_ctx.aarch64_ctx.stack_trace_size; i++) { + fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->context->cpu_ctx.aarch64_ctx.stack_trace[i]); + } } } - fprintf(f_report, " PC: %016lx\n", this->ctx->cpu_ctx.aarch64_ctx.pc); - fprintf(f_report, "Start Address: %016lx\n", this->ctx->cpu_ctx.aarch64_ctx.start_address); - fprintf(f_report, "Stack Trace:\n"); - for (unsigned int i = 0; i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size; i++) { - fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i]); + + if (this->context->stack_dump_size) { + snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, static_cast(this->context->title_id)); + FILE *f_stackdump = fopen(file_path, "wb"); + if (f_stackdump == NULL) { return; } + ON_SCOPE_EXIT { fclose(f_stackdump); }; + + fwrite(this->context->stack_dump, this->context->stack_dump_size, 1, f_stackdump); + fflush(f_stackdump); } } + Result ErrorReportTask::Run() { + if (this->context->generate_error_report) { + /* Here, Nintendo creates an error report with erpt. AMS will not do that. */ + } + + /* Save report to SD card. */ + if (!this->context->is_creport) { + this->SaveReportToSdCard(); + } + + /* Signal we're done with our job. */ + eventFire(const_cast(&this->context->erpt_event)); + + return ResultSuccess; + } + } - if (this->ctx->stack_dump_size) { - snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, this->title_id); - FILE *f_stackdump = fopen(file_path, "wb"); - if (f_stackdump == NULL) { return; } - ON_SCOPE_EXIT { fclose(f_stackdump); }; - - fwrite(this->ctx->stack_dump, this->ctx->stack_dump_size, 1, f_stackdump); + ITask *GetErrorReportTask(const ThrowContext *ctx) { + g_error_report_task.Initialize(ctx); + return &g_error_report_task; } + } - -Result ErrorReportTask::Run() { - if (this->create_report) { - /* Here, Nintendo creates an error report with erpt. AMS will not do that. */ - } - - /* Save report to SD card. */ - if (!this->ctx->is_creport) { - SaveReportToSdCard(); - } - - /* Signal we're done with our job. */ - eventFire(this->erpt_event); - - - return ResultSuccess; -} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_task_error_report.hpp b/stratosphere/fatal/source/fatal_task_error_report.hpp index 8cfbafb1c..10338c68e 100644 --- a/stratosphere/fatal/source/fatal_task_error_report.hpp +++ b/stratosphere/fatal/source/fatal_task_error_report.hpp @@ -15,22 +15,10 @@ */ #pragma once -#include -#include #include "fatal_task.hpp" -class ErrorReportTask : public IFatalTask { - private: - bool create_report; - Event *erpt_event; - private: - void EnsureReportDirectories(); - bool GetCurrentTime(u64 *out); - void SaveReportToSdCard(); - public: - ErrorReportTask(FatalThrowContext *ctx, u64 title_id, bool error_report, Event *evt) : IFatalTask(ctx, title_id), create_report(error_report), erpt_event(evt) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "WriteErrorReport"; - } -}; \ No newline at end of file +namespace sts::fatal::srv { + + ITask *GetErrorReportTask(const ThrowContext *ctx); + +} diff --git a/stratosphere/fatal/source/fatal_task_power.cpp b/stratosphere/fatal/source/fatal_task_power.cpp index 1c2bb0522..b6cd86672 100644 --- a/stratosphere/fatal/source/fatal_task_power.cpp +++ b/stratosphere/fatal/source/fatal_task_power.cpp @@ -14,149 +14,199 @@ * along with this program. If not, see . */ -#include -#include "fatal_task_power.hpp" #include "fatal_config.hpp" +#include "fatal_task_power.hpp" -bool PowerControlTask::TryShutdown() { - /* Set a timeout of 30 seconds. */ - TimeoutHelper timeout_helper(30000000000UL); - bool cancel_shutdown = false; - PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; +namespace sts::fatal::srv { - while (true) { - if (timeout_helper.TimedOut()) { - break; - } + namespace { - if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { - break; - } - - if (bv_state == PsmBatteryVoltageState_Normal) { - cancel_shutdown = true; - break; - } - - /* Query voltage state every 5 seconds, for 30 seconds. */ - svcSleepThread(5000000000UL); - } - - if (!cancel_shutdown) { - bpcShutdownSystem(); - return true; - } else { - return false; - } -} - -void PowerControlTask::MonitorBatteryState() { - PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; - - /* Check the battery state, and shutdown on low voltage. */ - if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { - /* Wait a second for the error report task to finish. */ - eventWait(this->erpt_event, TimeoutHelper::NsToTick(1000000000UL)); - this->TryShutdown(); - return; - } - - /* Signal we've checked the battery at least once. */ - eventFire(this->battery_event); - - while (true) { - if (R_FAILED(psmGetBatteryVoltageState(&bv_state))) { - bv_state = PsmBatteryVoltageState_NeedsShutdown; - } - - switch (bv_state) { - case PsmBatteryVoltageState_NeedsShutdown: - case PsmBatteryVoltageState_NeedsSleep: - { - bool shutdown = this->TryShutdown(); - if (shutdown) { - return; - } + /* Task types. */ + class PowerControlTask : public ITask { + private: + bool TryShutdown(); + void MonitorBatteryState(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "PowerControlTask"; } - break; - default: - break; + }; + + class PowerButtonObserveTask : public ITask { + private: + void WaitForPowerButton(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "PowerButtonObserveTask"; + } + }; + + class StateTransitionStopTask : public ITask { + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "StateTransitionStopTask"; + } + }; + + /* Task globals. */ + PowerControlTask g_power_control_task; + PowerButtonObserveTask g_power_button_observe_task; + StateTransitionStopTask g_state_transition_stop_task; + + /* Task Implementations. */ + bool PowerControlTask::TryShutdown() { + /* Set a timeout of 30 seconds. */ + TimeoutHelper timeout_helper(30'000'000'000ul); + + bool perform_shutdown = true; + PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; + + while (true) { + if (timeout_helper.TimedOut()) { + break; + } + + if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { + break; + } + + if (bv_state == PsmBatteryVoltageState_Normal) { + perform_shutdown = false; + break; + } + + /* Query voltage state every 5 seconds, for 30 seconds. */ + svcSleepThread(5'000'000'000ul); + } + + if (perform_shutdown) { + bpcShutdownSystem(); + } + + return perform_shutdown; } - /* Query voltage state every 5 seconds. */ - svcSleepThread(5000000000UL); - } -} + void PowerControlTask::MonitorBatteryState() { + PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; -void PowerButtonObserveTask::WaitForPowerButton() { - /* Wait up to a second for error report generation to finish. */ - eventWait(this->erpt_event, TimeoutHelper::NsToTick(1000000000UL)); + /* Check the battery state, and shutdown on low voltage. */ + if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { + /* Wait a second for the error report task to finish. */ + eventWait(const_cast(&this->context->erpt_event), TimeoutHelper::NsToTick(1'000'000'000ul)); + this->TryShutdown(); + return; + } - /* Force a reboot after some time if kiosk unit. */ - const FatalConfig *config = GetFatalConfig(); - TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL); + /* Signal we've checked the battery at least once. */ + eventFire(const_cast(&this->context->battery_event)); - TimeoutHelper auto_reboot_helper(config->fatal_auto_reboot_interval * 1000000); + /* Loop querying voltage state every 5 seconds. */ + while (true) { + if (R_FAILED(psmGetBatteryVoltageState(&bv_state))) { + bv_state = PsmBatteryVoltageState_NeedsShutdown; + } - bool check_vol_up = true, check_vol_down = true; - GpioPadSession vol_up_btn, vol_down_btn; - if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { - check_vol_up = false; - } - if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) { - check_vol_down = false; - } + switch (bv_state) { + case PsmBatteryVoltageState_NeedsShutdown: + case PsmBatteryVoltageState_NeedsSleep: + { + bool shutdown = this->TryShutdown(); + if (shutdown) { + return; + } + } + break; + default: + break; + } - /* Ensure we close on early return. */ - ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } }; - ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } }; - - /* Set direction input. */ - if (check_vol_up) { - gpioPadSetDirection(&vol_up_btn, GpioDirection_Input); - } - if (check_vol_down) { - gpioPadSetDirection(&vol_down_btn, GpioDirection_Input); - } - - BpcSleepButtonState state; - GpioValue val; - while (true) { - if (config->is_auto_reboot_enabled && auto_reboot_helper.TimedOut() ) { - bpcRebootSystem(); - return; + svcSleepThread(5'000'000'000ul); + } } - if (check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) { - bpcRebootSystem(); + void PowerButtonObserveTask::WaitForPowerButton() { + /* Wait up to a second for error report generation to finish. */ + eventWait(const_cast(&this->context->erpt_event), TimeoutHelper::NsToTick(1'000'000'000ul)); + + /* Force a reboot after some time if kiosk unit. */ + const auto &config = GetFatalConfig(); + TimeoutHelper quest_reboot_helper(config.GetQuestRebootTimeoutInterval()); + TimeoutHelper fatal_reboot_helper(config.GetFatalRebootTimeoutInterval()); + + bool check_vol_up = true, check_vol_down = true; + GpioPadSession vol_up_btn, vol_down_btn; + if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { + check_vol_up = false; + } + if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) { + check_vol_down = false; + } + + /* Ensure we close on early return. */ + ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } }; + ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } }; + + /* Set direction input. */ + if (check_vol_up) { + gpioPadSetDirection(&vol_up_btn, GpioDirection_Input); + } + if (check_vol_down) { + gpioPadSetDirection(&vol_down_btn, GpioDirection_Input); + } + + BpcSleepButtonState state; + GpioValue val; + while (true) { + if ((config.IsFatalRebootEnabled() && fatal_reboot_helper.TimedOut()) || + (check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) || + (check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) || + (R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || + (config.IsQuest() && quest_reboot_helper.TimedOut())) { + /* If any of the above conditions succeeded, we should reboot. */ + bpcRebootSystem(); + return; + } + + + /* Wait 100 ms between button checks. */ + svcSleepThread(100'000'000ul); + } } - if (check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) { - bpcRebootSystem(); + Result PowerControlTask::Run() { + this->MonitorBatteryState(); + return ResultSuccess; } - if ((R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) { - bpcRebootSystem(); - return; + Result PowerButtonObserveTask::Run() { + this->WaitForPowerButton(); + return ResultSuccess; + } + + Result StateTransitionStopTask::Run() { + /* Nintendo ignores the output of this call... */ + spsmPutErrorState(); + return ResultSuccess; } - /* Wait 100 ms between button checks. */ - svcSleepThread(100000000UL); } -} -Result PowerControlTask::Run() { - MonitorBatteryState(); - return ResultSuccess; -} + ITask *GetPowerControlTask(const ThrowContext *ctx) { + g_power_control_task.Initialize(ctx); + return &g_power_control_task; + } -Result PowerButtonObserveTask::Run() { - WaitForPowerButton(); - return ResultSuccess; -} + ITask *GetPowerButtonObserveTask(const ThrowContext *ctx) { + g_power_button_observe_task.Initialize(ctx); + return &g_power_button_observe_task; + } + + ITask *GetStateTransitionStopTask(const ThrowContext *ctx) { + g_state_transition_stop_task.Initialize(ctx); + return &g_state_transition_stop_task; + } -Result StateTransitionStopTask::Run() { - /* Nintendo ignores the output of this call... */ - spsmPutErrorState(); - return ResultSuccess; } diff --git a/stratosphere/fatal/source/fatal_task_power.hpp b/stratosphere/fatal/source/fatal_task_power.hpp index 7c767bbd3..763ef14bf 100644 --- a/stratosphere/fatal/source/fatal_task_power.hpp +++ b/stratosphere/fatal/source/fatal_task_power.hpp @@ -15,43 +15,12 @@ */ #pragma once -#include -#include #include "fatal_task.hpp" -class PowerControlTask : public IFatalTask { - private: - Event *erpt_event; - Event *battery_event; - private: - bool TryShutdown(); - void MonitorBatteryState(); - public: - PowerControlTask(FatalThrowContext *ctx, u64 title_id, Event *er_evt, Event *bt_evt) : IFatalTask(ctx, title_id), erpt_event(er_evt), battery_event(bt_evt) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "PowerControlTask"; - } -}; +namespace sts::fatal::srv { -class PowerButtonObserveTask : public IFatalTask { - private: - Event *erpt_event; - private: - void WaitForPowerButton(); - public: - PowerButtonObserveTask(FatalThrowContext *ctx, u64 title_id, Event *er_evt) : IFatalTask(ctx, title_id), erpt_event(er_evt) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "PowerButtonObserveTask"; - } -}; + ITask *GetPowerControlTask(const ThrowContext *ctx); + ITask *GetPowerButtonObserveTask(const ThrowContext *ctx); + ITask *GetStateTransitionStopTask(const ThrowContext *ctx); -class StateTransitionStopTask : public IFatalTask { - public: - StateTransitionStopTask(FatalThrowContext *ctx, u64 title_id) : IFatalTask(ctx, title_id) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "StateTransitionStopTask"; - } -}; \ No newline at end of file +} diff --git a/stratosphere/fatal/source/fatal_task_screen.cpp b/stratosphere/fatal/source/fatal_task_screen.cpp index 3ce541b1d..cac98f22d 100644 --- a/stratosphere/fatal/source/fatal_task_screen.cpp +++ b/stratosphere/fatal/source/fatal_task_screen.cpp @@ -14,378 +14,434 @@ * along with this program. If not, see . */ -#include - #include #include "fatal_task_screen.hpp" #include "fatal_config.hpp" #include "fatal_font.hpp" -#include "ams_logo.hpp" -static constexpr u32 FatalScreenWidth = 1280; -static constexpr u32 FatalScreenHeight = 720; -static constexpr u32 FatalScreenBpp = 2; +namespace sts::fatal::srv { -static constexpr u32 FatalScreenWidthAlignedBytes = (FatalScreenWidth * FatalScreenBpp + 63) & ~63; -static constexpr u32 FatalScreenWidthAligned = FatalScreenWidthAlignedBytes / FatalScreenBpp; + /* Include Atmosphere logo into its own anonymous namespace. */ -u32 GetPixelOffset(uint32_t x, uint32_t y) -{ - u32 tmp_pos; + namespace { - tmp_pos = ((y & 127) / 16) + (x/32*8) + ((y/16/8)*(((FatalScreenWidthAligned/2)/16*8))); - tmp_pos *= 16*16 * 4; + #include "fatal_ams_logo.inc" - tmp_pos += ((y%16)/8)*512 + ((x%32)/16)*256 + ((y%8)/2)*64 + ((x%16)/8)*32 + (y%2)*16 + (x%8)*2;//This line is a modified version of code from the Tegra X1 datasheet. + } - return tmp_pos / 2; -} + namespace { + + /* Screen definitions. */ + constexpr u32 FatalScreenWidth = 1280; + constexpr u32 FatalScreenHeight = 720; + constexpr u32 FatalScreenBpp = 2; + constexpr u32 FatalLayerZ = 100; + + constexpr u32 FatalScreenWidthAlignedBytes = (FatalScreenWidth * FatalScreenBpp + 63) & ~63; + constexpr u32 FatalScreenWidthAligned = FatalScreenWidthAlignedBytes / FatalScreenBpp; + + /* Pixel calculation helper. */ + constexpr u32 GetPixelOffset(u32 x, u32 y) { + u32 tmp_pos = ((y & 127) / 16) + (x/32*8) + ((y/16/8)*(((FatalScreenWidthAligned/2)/16*8))); + tmp_pos *= 16*16 * 4; + + tmp_pos += ((y%16)/8)*512 + ((x%32)/16)*256 + ((y%8)/2)*64 + ((x%16)/8)*32 + (y%2)*16 + (x%8)*2;//This line is a modified version of code from the Tegra X1 datasheet. + + return tmp_pos / 2; + } + + /* Task definitions. */ + class ShowFatalTask : public ITask { + private: + ViDisplay display; + ViLayer layer; + NWindow win; + Framebuffer fb; + private: + Result SetupDisplayInternal(); + Result SetupDisplayExternal(); + Result PrepareScreenForDrawing(); + Result ShowFatal(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "ShowFatal"; + } + virtual size_t GetStackSize() const override { + return 0x8000; + } + }; + + class BacklightControlTask : public ITask { + private: + void TurnOnBacklight(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "BacklightControlTask"; + } + }; + + /* Task globals. */ + ShowFatalTask g_show_fatal_task; + BacklightControlTask g_backlight_control_task; + + /* Task implementations. */ + Result ShowFatalTask::SetupDisplayInternal() { + ViDisplay temp_display; + /* Try to open the display. */ + R_TRY_CATCH(viOpenDisplay("Internal", &temp_display)) { + R_CATCH(ResultViNotFound) { + return ResultSuccess; + } + } R_END_TRY_CATCH; + + /* Guarantee we close the display. */ + ON_SCOPE_EXIT { viCloseDisplay(&temp_display); }; + + /* Turn on the screen. */ + R_TRY(viSetDisplayPowerState(&temp_display, ViPowerState_On)); + + /* Set alpha to 1.0f. */ + R_TRY(viSetDisplayAlpha(&temp_display, 1.0f)); -Result ShowFatalTask::SetupDisplayInternal() { - ViDisplay display; - /* Try to open the display. */ - R_TRY_CATCH(viOpenDisplay("Internal", &display)) { - R_CATCH(ResultViNotFound) { return ResultSuccess; } - } R_END_TRY_CATCH; - /* Guarantee we close the display. */ - ON_SCOPE_EXIT { viCloseDisplay(&display); }; + Result ShowFatalTask::SetupDisplayExternal() { + ViDisplay temp_display; + /* Try to open the display. */ + R_TRY_CATCH(viOpenDisplay("External", &temp_display)) { + R_CATCH(ResultViNotFound) { + return ResultSuccess; + } + } R_END_TRY_CATCH; - /* Turn on the screen. */ - R_TRY(viSetDisplayPowerState(&display, ViPowerState_On)); + /* Guarantee we close the display. */ + ON_SCOPE_EXIT { viCloseDisplay(&temp_display); }; - /* Set alpha to 1.0f. */ - R_TRY(viSetDisplayAlpha(&display, 1.0f)); + /* Set alpha to 1.0f. */ + R_TRY(viSetDisplayAlpha(&temp_display, 1.0f)); - return ResultSuccess; -} - -Result ShowFatalTask::SetupDisplayExternal() { - ViDisplay display; - /* Try to open the display. */ - R_TRY_CATCH(viOpenDisplay("External", &display)) { - R_CATCH(ResultViNotFound) { return ResultSuccess; } - } R_END_TRY_CATCH; - /* Guarantee we close the display. */ - ON_SCOPE_EXIT { viCloseDisplay(&display); }; + Result ShowFatalTask::PrepareScreenForDrawing() { + /* Connect to vi. */ + R_TRY(viInitialize(ViServiceType_Manager)); - /* Set alpha to 1.0f. */ - R_TRY(viSetDisplayAlpha(&display, 1.0f)); + /* Close other content. */ + viSetContentVisibility(false); - return ResultSuccess; -} + /* Setup the two displays. */ + R_TRY(SetupDisplayInternal()); + R_TRY(SetupDisplayExternal()); -Result ShowFatalTask::PrepareScreenForDrawing() { - /* Connect to vi. */ - R_TRY(viInitialize(ViServiceType_Manager)); + /* Open the default display. */ + R_TRY(viOpenDefaultDisplay(&this->display)); - /* Close other content. */ - viSetContentVisibility(false); + /* Reset the display magnification to its default value. */ + u32 display_width, display_height; + R_TRY(viGetDisplayLogicalResolution(&this->display, &display_width, &display_height)); - /* Setup the two displays. */ - R_TRY(SetupDisplayInternal()); - R_TRY(SetupDisplayExternal()); + /* viSetDisplayMagnification was added in 3.0.0. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { + R_TRY(viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height)); + } - /* Open the default display. */ - R_TRY(viOpenDefaultDisplay(&this->display)); + /* Create layer to draw to. */ + R_TRY(viCreateLayer(&this->display, &this->layer)); - /* Reset the display magnification to its default value. */ - u32 display_width, display_height; - R_TRY(viGetDisplayLogicalResolution(&this->display, &display_width, &display_height)); + /* Setup the layer. */ + { + /* Display a layer of 1280 x 720 at 1.5x magnification */ + /* NOTE: N uses 2 (770x400) RGBA4444 buffers (tiled buffer + linear). */ + /* We use a single 1280x720 tiled RGB565 buffer. */ + constexpr u32 raw_width = FatalScreenWidth; + constexpr u32 raw_height = FatalScreenHeight; + constexpr u32 layer_width = ((raw_width) * 3) / 2; + constexpr u32 layer_height = ((raw_height) * 3) / 2; - /* viSetDisplayMagnification was added in 3.0.0. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { - R_TRY(viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height)); - } + const float layer_x = static_cast((display_width - layer_width) / 2); + const float layer_y = static_cast((display_height - layer_height) / 2); - /* Create layer to draw to. */ - R_TRY(viCreateLayer(&this->display, &this->layer)); + R_TRY(viSetLayerSize(&this->layer, layer_width, layer_height)); - /* Setup the layer. */ - { - /* Display a layer of 1280 x 720 at 1.5x magnification */ - /* NOTE: N uses 2 (770x400) RGBA4444 buffers (tiled buffer + linear). */ - /* We use a single 1280x720 tiled RGB565 buffer. */ - constexpr u32 raw_width = FatalScreenWidth; - constexpr u32 raw_height = FatalScreenHeight; - constexpr u32 layer_width = ((raw_width) * 3) / 2; - constexpr u32 layer_height = ((raw_height) * 3) / 2; + /* Set the layer's Z at display maximum, to be above everything else .*/ + R_TRY(viSetLayerZ(&this->layer, FatalLayerZ)); - const float layer_x = static_cast((display_width - layer_width) / 2); - const float layer_y = static_cast((display_height - layer_height) / 2); - u64 layer_z; + /* Center the layer in the screen. */ + R_TRY(viSetLayerPosition(&this->layer, layer_x, layer_y)); - R_TRY(viSetLayerSize(&this->layer, layer_width, layer_height)); + /* Create framebuffer. */ + R_TRY(nwindowCreateFromLayer(&this->win, &this->layer)); + R_TRY(framebufferCreate(&this->fb, &this->win, raw_width, raw_height, PIXEL_FORMAT_RGB_565, 1)); + } - /* Set the layer's Z at display maximum, to be above everything else .*/ - /* NOTE: Fatal hardcodes 100 here. */ - if (R_SUCCEEDED(viGetDisplayMaximumZ(&this->display, &layer_z))) { - R_TRY(viSetLayerZ(&this->layer, layer_z)); + return ResultSuccess; } - /* Center the layer in the screen. */ - R_TRY(viSetLayerPosition(&this->layer, layer_x, layer_y)); + Result ShowFatalTask::ShowFatal() { + const FatalConfig &config = GetFatalConfig(); - /* Create framebuffer. */ - R_TRY(nwindowCreateFromLayer(&this->win, &this->layer)); - R_TRY(framebufferCreate(&this->fb, &this->win, raw_width, raw_height, PIXEL_FORMAT_RGB_565, 1)); - } + /* Prepare screen for drawing. */ + DoWithSmSession([&]() { + R_ASSERT(PrepareScreenForDrawing()); + }); - return ResultSuccess; -} + /* Dequeue a buffer. */ + u16 *tiled_buf = reinterpret_cast(framebufferBegin(&this->fb, NULL)); + if (tiled_buf == nullptr) { + return ResultFatalNullGraphicsBuffer; + } -Result ShowFatalTask::ShowFatal() { - const FatalConfig *config = GetFatalConfig(); + /* Let the font manager know about our framebuffer. */ + font::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset); + font::SetFontColor(0xFFFF); - /* Prepare screen for drawing. */ - DoWithSmSession([&]() { - if (R_FAILED(PrepareScreenForDrawing())) { - std::abort(); - } - }); + /* Draw a background. */ + for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) { + tiled_buf[i] = 0x39C9; + } - /* Dequeue a buffer. */ - u16 *tiled_buf = reinterpret_cast(framebufferBegin(&this->fb, NULL)); - if (tiled_buf == nullptr) { - return ResultFatalNullGraphicsBuffer; - } + /* Draw the atmosphere logo in the bottom right corner. */ + for (size_t y = 0; y < AtmosphereLogoHeight; y++) { + for (size_t x = 0; x < AtmosphereLogoWidth; x++) { + tiled_buf[GetPixelOffset(FatalScreenWidth - AtmosphereLogoWidth - 32 + x, 32 + y)] = AtmosphereLogoData[y * AtmosphereLogoWidth + x]; + } + } - /* Let the font manager know about our framebuffer. */ - FontManager::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset); - FontManager::SetFontColor(0xFFFF); - - /* Draw a background. */ - for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) { - tiled_buf[i] = 0x39C9; - } - - /* Draw the atmosphere logo in the bottom right corner. */ - for (size_t y = 0; y < AMS_LOGO_HEIGHT; y++) { - for (size_t x = 0; x < AMS_LOGO_WIDTH; x++) { - tiled_buf[GetPixelOffset(FatalScreenWidth - AMS_LOGO_WIDTH - 32 + x, 32 + y)] = AMS_LOGO_BIN[y * AMS_LOGO_WIDTH + x]; - } - } - - /* TODO: Actually draw meaningful shit here. */ - FontManager::SetPosition(32, 64); - FontManager::SetFontSize(16.0f); - FontManager::PrintFormat(config->error_msg, R_MODULE(this->ctx->error_code), R_DESCRIPTION(this->ctx->error_code), this->ctx->error_code); - FontManager::AddSpacingLines(0.5f); - FontManager::PrintFormatLine("Title: %016lX", this->title_id); - FontManager::AddSpacingLines(0.5f); - FontManager::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); - FontManager::AddSpacingLines(1.5f); - if (this->ctx->error_code != ResultAtmosphereVersionMismatch) { - FontManager::Print(config->error_desc); - } else { - /* Print a special message for atmosphere version mismatch. */ - FontManager::Print(u8"Atmosphère version mismatch detected.\n\n" - u8"Please press the POWER Button to restart the console normally, or a VOL button\n" - u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" - u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" - u8"Please ensure that all Atmosphère components are updated.\n" - u8"github.com/Atmosphere-NX/Atmosphere/releases\n"); - } - - /* Add a line. */ - for (size_t x = 32; x < FatalScreenWidth - 32; x++) { - tiled_buf[GetPixelOffset(x, FontManager::GetY())] = 0xFFFF; - } - - - FontManager::AddSpacingLines(1.5f); - - u32 backtrace_y = FontManager::GetY(); - u32 backtrace_x = 0; - - /* Print GPRs. */ - FontManager::SetFontSize(14.0f); - FontManager::Print("General Purpose Registers "); - { - FontManager::SetPosition(FontManager::GetX() + 2, FontManager::GetY()); - u32 x = FontManager::GetX(); - FontManager::Print("PC: "); - FontManager::SetPosition(x + 47, FontManager::GetY()); - } - if (this->ctx->cpu_ctx.is_aarch32) { - FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.pc); - } else { - FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.pc); - } - FontManager::PrintLine(""); - FontManager::SetPosition(32, FontManager::GetY()); - FontManager::AddSpacingLines(0.5f); - if (this->ctx->cpu_ctx.is_aarch32) { - for (size_t i = 0; i < (NumAarch32Gprs / 2); i++) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("%s:", Aarch32GprNames[i]); - FontManager::SetPosition(x + 47, FontManager::GetY()); - if (this->ctx->has_gprs[i]) { - FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.r[i]); + /* TODO: Actually draw meaningful shit here. */ + font::SetPosition(32, 64); + font::SetFontSize(16.0f); + font::PrintFormat(config.GetErrorMessage(), R_MODULE(this->context->error_code), R_DESCRIPTION(this->context->error_code), this->context->error_code); + font::AddSpacingLines(0.5f); + font::PrintFormatLine("Title: %016lX", static_cast(this->context->title_id)); + font::AddSpacingLines(0.5f); + font::PrintFormatLine(u8"Firmware: %s (Atmosphère %u.%u.%u-%s)", config.GetFirmwareVersion().display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); + font::AddSpacingLines(1.5f); + if (this->context->error_code != ResultAtmosphereVersionMismatch) { + font::Print(config.GetErrorDescription()); } else { - FontManager::PrintMonospaceBlank(8); + /* Print a special message for atmosphere version mismatch. */ + font::Print(u8"Atmosphère version mismatch detected.\n\n" + u8"Please press the POWER Button to restart the console normally, or a VOL button\n" + u8"to reboot to a payload (or RCM, if none is present). If you are unable to\n" + u8"restart the console, hold the POWER Button for 12 seconds to turn the console off.\n\n" + u8"Please ensure that all Atmosphère components are updated.\n" + u8"github.com/Atmosphere-NX/Atmosphere/releases\n"); } - FontManager::Print(" "); - x = FontManager::GetX(); - FontManager::PrintFormat("%s:", Aarch32GprNames[i + (NumAarch32Gprs / 2)]); - FontManager::SetPosition(x + 47, FontManager::GetY()); - if (this->ctx->has_gprs[i + (NumAarch32Gprs / 2)]) { - FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.r[i + (NumAarch32Gprs / 2)]); + + /* Add a line. */ + for (size_t x = 32; x < FatalScreenWidth - 32; x++) { + tiled_buf[GetPixelOffset(x, font::GetY())] = 0xFFFF; + } + + font::AddSpacingLines(1.5f); + + u32 backtrace_y = font::GetY(); + u32 backtrace_x = 0; + + /* Note architecutre. */ + const bool is_aarch32 = this->context->cpu_ctx.architecture == CpuContext::Architecture_Aarch32; + + /* Print GPRs. */ + font::SetFontSize(14.0f); + font::Print("General Purpose Registers "); + { + font::SetPosition(font::GetX() + 2, font::GetY()); + u32 x = font::GetX(); + font::Print("PC: "); + font::SetPosition(x + 47, font::GetY()); + } + if (is_aarch32) { + font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.pc); } else { - FontManager::PrintMonospaceBlank(8); + font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.pc); + } + font::PrintLine(""); + font::SetPosition(32, font::GetY()); + font::AddSpacingLines(0.5f); + if (is_aarch32) { + for (size_t i = 0; i < (aarch32::RegisterName_GeneralPurposeCount / 2); i++) { + u32 x = font::GetX(); + font::PrintFormat("%s:", aarch32::CpuContext::RegisterNameStrings[i]); + font::SetPosition(x + 47, font::GetY()); + if (this->context->cpu_ctx.aarch32_ctx.HasRegisterValue(static_cast(i))) { + font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.r[i]); + } else { + font::PrintMonospaceBlank(8); + } + font::Print(" "); + x = font::GetX(); + font::PrintFormat("%s:", aarch32::CpuContext::RegisterNameStrings[i + (aarch32::RegisterName_GeneralPurposeCount / 2)]); + font::SetPosition(x + 47, font::GetY()); + if (this->context->cpu_ctx.aarch32_ctx.HasRegisterValue(static_cast(i + (aarch32::RegisterName_GeneralPurposeCount / 2)))) { + font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.r[i + (aarch32::RegisterName_GeneralPurposeCount / 2)]); + } else { + font::PrintMonospaceBlank(8); + } + + if (i == (aarch32::RegisterName_GeneralPurposeCount / 2) - 1) { + font::Print(" "); + backtrace_x = font::GetX(); + } + + font::PrintLine(""); + font::SetPosition(32, font::GetY()); + } + } else { + for (size_t i = 0; i < aarch64::RegisterName_GeneralPurposeCount / 2; i++) { + u32 x = font::GetX(); + font::PrintFormat("%s:", aarch64::CpuContext::RegisterNameStrings[i]); + font::SetPosition(x + 47, font::GetY()); + if (this->context->cpu_ctx.aarch64_ctx.HasRegisterValue(static_cast(i))) { + font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.x[i]); + } else { + font::PrintMonospaceBlank(16); + } + font::Print(" "); + x = font::GetX(); + font::PrintFormat("%s:", aarch64::CpuContext::RegisterNameStrings[i + (aarch64::RegisterName_GeneralPurposeCount / 2)]); + font::SetPosition(x + 47, font::GetY()); + if (this->context->cpu_ctx.aarch64_ctx.HasRegisterValue(static_cast(i + (aarch64::RegisterName_GeneralPurposeCount / 2)))) { + font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.x[i + (aarch64::RegisterName_GeneralPurposeCount / 2)]); + } else { + font::PrintMonospaceBlank(16); + } + + if (i == (aarch64::RegisterName_GeneralPurposeCount / 2) - 1) { + font::Print(" "); + backtrace_x = font::GetX(); + } + + font::PrintLine(""); + font::SetPosition(32, font::GetY()); + } } - if (i == (NumAarch32Gprs / 2) - 1) { - FontManager::Print(" "); - backtrace_x = FontManager::GetX(); + /* Print Backtrace. */ + u32 bt_size; + if (is_aarch32) { + bt_size = this->context->cpu_ctx.aarch32_ctx.stack_trace_size; + } else { + bt_size = this->context->cpu_ctx.aarch64_ctx.stack_trace_size; } - FontManager::PrintLine(""); - FontManager::SetPosition(32, FontManager::GetY()); + + font::SetPosition(backtrace_x, backtrace_y); + if (bt_size == 0) { + if (is_aarch32) { + font::Print("Start Address: "); + font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.base_address); + font::PrintLine(""); + } else { + font::Print("Start Address: "); + font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.base_address); + font::PrintLine(""); + } + } else { + if (is_aarch32) { + font::Print("Backtrace - Start Address: "); + font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.base_address); + font::PrintLine(""); + font::AddSpacingLines(0.5f); + for (u32 i = 0; i < aarch32::CpuContext::MaxStackTraceDepth / 2; i++) { + u32 bt_cur = 0, bt_next = 0; + if (i < this->context->cpu_ctx.aarch32_ctx.stack_trace_size) { + bt_cur = this->context->cpu_ctx.aarch32_ctx.stack_trace[i]; + } + if (i + aarch32::CpuContext::MaxStackTraceDepth / 2 < this->context->cpu_ctx.aarch32_ctx.stack_trace_size) { + bt_next = this->context->cpu_ctx.aarch32_ctx.stack_trace[i + aarch32::CpuContext::MaxStackTraceDepth / 2]; + } + + if (i < this->context->cpu_ctx.aarch32_ctx.stack_trace_size) { + u32 x = font::GetX(); + font::PrintFormat("BT[%02d]: ", i); + font::SetPosition(x + 72, font::GetY()); + font::PrintMonospaceU32(bt_cur); + font::Print(" "); + } + + if (i + aarch32::CpuContext::MaxStackTraceDepth / 2 < this->context->cpu_ctx.aarch32_ctx.stack_trace_size) { + u32 x = font::GetX(); + font::PrintFormat("BT[%02d]: ", i + aarch32::CpuContext::MaxStackTraceDepth / 2); + font::SetPosition(x + 72, font::GetY()); + font::PrintMonospaceU32(bt_next); + } + + font::PrintLine(""); + font::SetPosition(backtrace_x, font::GetY()); + } + } else { + font::Print("Backtrace - Start Address: "); + font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.base_address); + font::PrintLine(""); + font::AddSpacingLines(0.5f); + for (u32 i = 0; i < aarch64::CpuContext::MaxStackTraceDepth / 2; i++) { + u64 bt_cur = 0, bt_next = 0; + if (i < this->context->cpu_ctx.aarch64_ctx.stack_trace_size) { + bt_cur = this->context->cpu_ctx.aarch64_ctx.stack_trace[i]; + } + if (i + aarch64::CpuContext::MaxStackTraceDepth / 2 < this->context->cpu_ctx.aarch64_ctx.stack_trace_size) { + bt_next = this->context->cpu_ctx.aarch64_ctx.stack_trace[i + aarch64::CpuContext::MaxStackTraceDepth / 2]; + } + + if (i < this->context->cpu_ctx.aarch64_ctx.stack_trace_size) { + u32 x = font::GetX(); + font::PrintFormat("BT[%02d]: ", i); + font::SetPosition(x + 72, font::GetY()); + font::PrintMonospaceU64(bt_cur); + font::Print(" "); + } + + if (i + aarch64::CpuContext::MaxStackTraceDepth / 2 < this->context->cpu_ctx.aarch64_ctx.stack_trace_size) { + u32 x = font::GetX(); + font::PrintFormat("BT[%02d]: ", i + aarch64::CpuContext::MaxStackTraceDepth / 2); + font::SetPosition(x + 72, font::GetY()); + font::PrintMonospaceU64(bt_next); + } + + font::PrintLine(""); + font::SetPosition(backtrace_x, font::GetY()); + } + } + } + + + /* Enqueue the buffer. */ + framebufferEnd(&fb); + + return ResultSuccess; } - } else { - for (size_t i = 0; i < NumAarch64Gprs / 2; i++) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("%s:", Aarch64GprNames[i]); - FontManager::SetPosition(x + 47, FontManager::GetY()); - if (this->ctx->has_gprs[i]) { - FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.x[i]); - } else { - FontManager::PrintMonospaceBlank(16); - } - FontManager::Print(" "); - x = FontManager::GetX(); - FontManager::PrintFormat("%s:", Aarch64GprNames[i + (NumAarch64Gprs / 2)]); - FontManager::SetPosition(x + 47, FontManager::GetY()); - if (this->ctx->has_gprs[i + (NumAarch64Gprs / 2)]) { - FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.x[i + (NumAarch64Gprs / 2)]); - } else { - FontManager::PrintMonospaceBlank(16); - } - if (i == (NumAarch64Gprs / 2) - 1) { - FontManager::Print(" "); - backtrace_x = FontManager::GetX(); - } + Result ShowFatalTask::Run() { + /* Don't show the fatal error screen until we've verified the battery is okay. */ + eventWait(const_cast(&this->context->battery_event), U64_MAX); - FontManager::PrintLine(""); - FontManager::SetPosition(32, FontManager::GetY()); + return ShowFatal(); } + + void BacklightControlTask::TurnOnBacklight() { + lblSwitchBacklightOn(0); + } + + Result BacklightControlTask::Run() { + TurnOnBacklight(); + return ResultSuccess; + } + } - /* Print Backtrace. */ - u32 bt_size; - if (this->ctx->cpu_ctx.is_aarch32) { - bt_size = this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size; - } else { - bt_size = this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size; + ITask *GetShowFatalTask(const ThrowContext *ctx) { + g_show_fatal_task.Initialize(ctx); + return &g_show_fatal_task; } - - FontManager::SetPosition(backtrace_x, backtrace_y); - if (bt_size == 0) { - if (this->ctx->cpu_ctx.is_aarch32) { - FontManager::Print("Start Address: "); - FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.start_address); - FontManager::PrintLine(""); - } else { - FontManager::Print("Start Address: "); - FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.start_address); - FontManager::PrintLine(""); - } - } else { - if (this->ctx->cpu_ctx.is_aarch32) { - FontManager::Print("Backtrace - Start Address: "); - FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.start_address); - FontManager::PrintLine(""); - FontManager::AddSpacingLines(0.5f); - for (u32 i = 0; i < Aarch32CpuContext::MaxStackTraceDepth / 2; i++) { - u32 bt_cur = 0, bt_next = 0; - if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { - bt_cur = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i]; - } - if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { - bt_next = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i + Aarch32CpuContext::MaxStackTraceDepth / 2]; - } - - if (i < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("BT[%02d]: ", i); - FontManager::SetPosition(x + 72, FontManager::GetY()); - FontManager::PrintMonospaceU32(bt_cur); - FontManager::Print(" "); - } - - if (i + Aarch32CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("BT[%02d]: ", i + Aarch32CpuContext::MaxStackTraceDepth / 2); - FontManager::SetPosition(x + 72, FontManager::GetY()); - FontManager::PrintMonospaceU32(bt_next); - } - - FontManager::PrintLine(""); - FontManager::SetPosition(backtrace_x, FontManager::GetY()); - } - } else { - FontManager::Print("Backtrace - Start Address: "); - FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.start_address); - FontManager::PrintLine(""); - FontManager::AddSpacingLines(0.5f); - for (u32 i = 0; i < Aarch64CpuContext::MaxStackTraceDepth / 2; i++) { - u64 bt_cur = 0, bt_next = 0; - if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { - bt_cur = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i]; - } - if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { - bt_next = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i + Aarch64CpuContext::MaxStackTraceDepth / 2]; - } - - if (i < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("BT[%02d]: ", i); - FontManager::SetPosition(x + 72, FontManager::GetY()); - FontManager::PrintMonospaceU64(bt_cur); - FontManager::Print(" "); - } - - if (i + Aarch64CpuContext::MaxStackTraceDepth / 2 < this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) { - u32 x = FontManager::GetX(); - FontManager::PrintFormat("BT[%02d]: ", i + Aarch64CpuContext::MaxStackTraceDepth / 2); - FontManager::SetPosition(x + 72, FontManager::GetY()); - FontManager::PrintMonospaceU64(bt_next); - } - - FontManager::PrintLine(""); - FontManager::SetPosition(backtrace_x, FontManager::GetY()); - } - } + ITask *GetBacklightControlTask(const ThrowContext *ctx) { + g_backlight_control_task.Initialize(ctx); + return &g_backlight_control_task; } - - /* Enqueue the buffer. */ - framebufferEnd(&fb); - - return ResultSuccess; -} - -Result ShowFatalTask::Run() { - /* Don't show the fatal error screen until we've verified the battery is okay. */ - eventWait(this->battery_event, U64_MAX); - - return ShowFatal(); -} - -void BacklightControlTask::TurnOnBacklight() { - lblSwitchBacklightOn(0); -} - -Result BacklightControlTask::Run() { - TurnOnBacklight(); - return ResultSuccess; } diff --git a/stratosphere/fatal/source/fatal_task_screen.hpp b/stratosphere/fatal/source/fatal_task_screen.hpp index 1c96a6f5d..b9384f93a 100644 --- a/stratosphere/fatal/source/fatal_task_screen.hpp +++ b/stratosphere/fatal/source/fatal_task_screen.hpp @@ -15,37 +15,11 @@ */ #pragma once -#include -#include #include "fatal_task.hpp" -class ShowFatalTask : public IFatalTask { - private: - Event *battery_event; - ViDisplay display; - ViLayer layer; - NWindow win; - Framebuffer fb; - private: - Result SetupDisplayInternal(); - Result SetupDisplayExternal(); - Result PrepareScreenForDrawing(); - Result ShowFatal(); - public: - ShowFatalTask(FatalThrowContext *ctx, u64 title_id, Event *evt) : IFatalTask(ctx, title_id), battery_event(evt) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "ShowFatal"; - } -}; +namespace sts::fatal::srv { -class BacklightControlTask : public IFatalTask { - private: - void TurnOnBacklight(); - public: - BacklightControlTask(FatalThrowContext *ctx, u64 title_id) : IFatalTask(ctx, title_id) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "BacklightControlTask"; - } -}; \ No newline at end of file + ITask *GetShowFatalTask(const ThrowContext *ctx); + ITask *GetBacklightControlTask(const ThrowContext *ctx); + +} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_task_sound.cpp b/stratosphere/fatal/source/fatal_task_sound.cpp index 64e7e845e..a8ac6365a 100644 --- a/stratosphere/fatal/source/fatal_task_sound.cpp +++ b/stratosphere/fatal/source/fatal_task_sound.cpp @@ -14,60 +14,86 @@ * along with this program. If not, see . */ -#include #include "fatal_task_sound.hpp" +namespace sts::fatal::srv { -void StopSoundTask::StopSound() { - /* Talk to the ALC5639 over I2C, and disable audio output. */ - { - I2cSession audio; - if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_Alc5639))) { - struct { - u16 dev; - u8 val; - } __attribute__((packed)) cmd; - static_assert(sizeof(cmd) == 3, "I2C command definition!"); + namespace { - cmd.dev = 0xC801; - cmd.val = 200; - i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + /* Task definition. */ + class StopSoundTask : public ITask { + private: + void StopSound(); + public: + virtual Result Run() override; + virtual const char *GetName() const override { + return "SoundTask"; + } + }; - cmd.dev = 0xC802; - cmd.val = 200; - i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + /* Task global. */ + StopSoundTask g_stop_sound_task; - cmd.dev = 0xC802; - cmd.val = 200; - i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + /* Task implementation. */ + void StopSoundTask::StopSound() { + /* Talk to the ALC5639 over I2C, and disable audio output. */ + { + I2cSession audio; + if (R_SUCCEEDED(i2cOpenSession(&audio, I2cDevice_Alc5639))) { + ON_SCOPE_EXIT { i2csessionClose(&audio); }; - for (u16 dev = 97; dev <= 102; dev++) { - cmd.dev = dev; - cmd.val = 0; - i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + struct { + u16 dev; + u8 val; + } __attribute__((packed)) cmd; + static_assert(sizeof(cmd) == 3, "I2C command definition!"); + + cmd.dev = 0xC801; + cmd.val = 200; + i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + + cmd.dev = 0xC802; + cmd.val = 200; + i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + + cmd.dev = 0xC802; + cmd.val = 200; + i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + + for (u16 dev = 97; dev <= 102; dev++) { + cmd.dev = dev; + cmd.val = 0; + i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); + } + } } - i2csessionClose(&audio); + /* Talk to the ALC5639 over GPIO, and disable audio output */ + { + GpioPadSession audio; + if (R_SUCCEEDED(gpioOpenSession(&audio, GpioPadName_AudioCodec))) { + ON_SCOPE_EXIT { gpioPadClose(&audio); }; + + /* Set direction output, sleep 200 ms so it can take effect. */ + gpioPadSetDirection(&audio, GpioDirection_Output); + svcSleepThread(200000000UL); + + /* Pull audio codec low. */ + gpioPadSetValue(&audio, GpioValue_Low); + } + } } + + Result StopSoundTask::Run() { + StopSound(); + return ResultSuccess; + } + } - /* Talk to the ALC5639 over GPIO, and disable audio output */ - { - GpioPadSession audio; - if (R_SUCCEEDED(gpioOpenSession(&audio, GpioPadName_AudioCodec))) { - /* Set direction output, sleep 200 ms so it can take effect. */ - gpioPadSetDirection(&audio, GpioDirection_Output); - svcSleepThread(200000000UL); - - /* Pull audio codec low. */ - gpioPadSetValue(&audio, GpioValue_Low); - - gpioPadClose(&audio); - } + ITask *GetStopSoundTask(const ThrowContext *ctx) { + g_stop_sound_task.Initialize(ctx); + return &g_stop_sound_task; } -} -Result StopSoundTask::Run() { - StopSound(); - return ResultSuccess; } diff --git a/stratosphere/fatal/source/fatal_task_sound.hpp b/stratosphere/fatal/source/fatal_task_sound.hpp index 4fa7b0abe..4e231d715 100644 --- a/stratosphere/fatal/source/fatal_task_sound.hpp +++ b/stratosphere/fatal/source/fatal_task_sound.hpp @@ -15,17 +15,10 @@ */ #pragma once -#include -#include #include "fatal_task.hpp" -class StopSoundTask : public IFatalTask { - private: - void StopSound(); - public: - StopSoundTask(FatalThrowContext *ctx, u64 title_id) : IFatalTask(ctx, title_id) { } - virtual Result Run() override; - virtual const char *GetName() const override { - return "SoundTask"; - } -}; +namespace sts::fatal::srv { + + ITask *GetStopSoundTask(const ThrowContext *ctx); + +} diff --git a/stratosphere/fatal/source/fatal_throw.cpp b/stratosphere/fatal/source/fatal_throw.cpp deleted file mode 100644 index 9e46c0b59..000000000 --- a/stratosphere/fatal/source/fatal_throw.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 "fatal_throw.hpp" -#include "fatal_event_manager.hpp" -#include "fatal_task.hpp" -#include "fatal_config.hpp" -#include "fatal_debug.hpp" - -static bool g_thrown = false; - -static Result SetThrown() { - /* This should be fine, since fatal only has a single IPC thread. */ - if (g_thrown) { - return ResultFatalAlreadyThrown; - } - - g_thrown = true; - return ResultSuccess; -} - -Result ThrowFatalForSelf(u32 error) { - u64 pid = 0; - - svcGetProcessId(&pid, CUR_PROCESS_HANDLE); - return ThrowFatalImpl(error, pid, FatalType_ErrorScreen, nullptr); -} - -Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu_ctx) { - FatalThrowContext ctx = {0}; - ctx.error_code = error; - if (cpu_ctx != nullptr) { - ctx.cpu_ctx = *cpu_ctx; - /* Assume if we're provided a context that it's complete. */ - for (u32 i = 0; i < NumAarch64Gprs; i++) { - ctx.has_gprs[i] = true; - } - /* Cap the stack trace size at a sane limit. */ - /* TODO: Better to set to zero, in order to manually collect debug info ourselves instead? */ - if (cpu_ctx->is_aarch32) { - ctx.cpu_ctx.aarch32_ctx.stack_trace_size = std::max(ctx.cpu_ctx.aarch32_ctx.stack_trace_size, static_cast(Aarch32CpuContext::MaxStackTraceDepth)); - } else { - ctx.cpu_ctx.aarch64_ctx.stack_trace_size = std::max(ctx.cpu_ctx.aarch64_ctx.stack_trace_size, static_cast(Aarch64CpuContext::MaxStackTraceDepth)); - } - } else { - std::memset(&ctx.cpu_ctx, 0, sizeof(ctx.cpu_ctx)); - } - /* Reassign this unconditionally, for convenience. */ - cpu_ctx = &ctx.cpu_ctx; - - /* Get config. */ - const FatalConfig *config = GetFatalConfig(); - - /* Get title id. On failure, it'll be zero. */ - sts::ncm::TitleId title_id = sts::ncm::TitleId::Invalid; - sts::pm::info::GetTitleId(&title_id, pid); - ctx.is_creport = title_id == sts::ncm::TitleId::Creport; - - /* Support for ams creport. TODO: Make this its own command? */ - if (ctx.is_creport && !cpu_ctx->is_aarch32 && cpu_ctx->aarch64_ctx.afsr0 != 0) { - title_id = sts::ncm::TitleId{cpu_ctx->aarch64_ctx.afsr0}; - } - - /* Atmosphere extension: automatic debug info collection. */ - if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 && !ctx.is_creport) { - if ((cpu_ctx->is_aarch32 && cpu_ctx->aarch32_ctx.stack_trace_size == 0) || (!cpu_ctx->is_aarch32 && cpu_ctx->aarch64_ctx.stack_trace_size == 0)) { - TryCollectDebugInformation(&ctx, pid); - } - } - - switch (policy) { - case FatalType_ErrorReport: - /* TODO: Don't write an error report. */ - break; - case FatalType_ErrorReportAndErrorScreen: - case FatalType_ErrorScreen: - { - /* Ensure we only throw once. */ - R_TRY(SetThrown()); - - /* Signal that fatal is about to happen. */ - GetEventManager()->SignalEvents(); - - /* Create events. */ - Event erpt_event; - Event battery_event; - if (R_FAILED(eventCreate(&erpt_event, false)) || R_FAILED(eventCreate(&battery_event, false))) { - std::abort(); - } - - /* Run tasks. */ - if (config->transition_to_fatal) { - RunFatalTasks(&ctx, static_cast(title_id), policy == FatalType_ErrorReportAndErrorScreen, &erpt_event, &battery_event); - } else { - /* If flag is not set, don't show the fatal screen. */ - return ResultSuccess; - } - - } - break; - default: - /* N aborts here. Should we just return an error code? */ - std::abort(); - } - - return ResultSuccess; -} diff --git a/stratosphere/fatal/source/fatal_types.hpp b/stratosphere/fatal/source/fatal_types.hpp deleted file mode 100644 index 2778d03c2..000000000 --- a/stratosphere/fatal/source/fatal_types.hpp +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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 - -static constexpr size_t NumAarch64Gprs = 32; -static constexpr size_t NumAarch32Gprs = 16; - -struct Aarch64CpuContext { - using RegisterType = u64; - static constexpr size_t MaxStackTraceDepth = 0x20; - - /* Registers, exception context. N left names for these fields in fatal .rodata. */ - union { - RegisterType x[NumAarch64Gprs]; - struct { - RegisterType _x[29]; - RegisterType fp; - RegisterType lr; - RegisterType sp; - RegisterType pc; - }; - }; - RegisterType pstate; - RegisterType afsr0; - RegisterType afsr1; - RegisterType esr; - RegisterType far; - - /* Misc. */ - RegisterType stack_trace[MaxStackTraceDepth]; - RegisterType start_address; - RegisterType register_set_flags; - u32 stack_trace_size; -}; - -struct Aarch32CpuContext { - using RegisterType = u32; - static constexpr size_t MaxStackTraceDepth = 0x20; - - /* Registers, exception context. N left names for these fields in fatal .rodata. */ - union { - RegisterType r[NumAarch32Gprs]; - struct { - RegisterType _r[11]; - RegisterType fp; - RegisterType ip; - RegisterType sp; - RegisterType lr; - RegisterType pc; - }; - }; - RegisterType pstate; - RegisterType afsr0; - RegisterType afsr1; - RegisterType esr; - RegisterType far; - - /* Misc. Yes, stack_trace_size is really laid out differently than aarch64... */ - RegisterType stack_trace[MaxStackTraceDepth]; - u32 stack_trace_size; - RegisterType start_address; - RegisterType register_set_flags; -}; - -struct FatalCpuContext { - union { - Aarch64CpuContext aarch64_ctx; - Aarch32CpuContext aarch32_ctx; - }; - - bool is_aarch32; - u32 type; -}; - -struct FatalThrowContext { - u32 error_code; - bool is_creport; - bool has_gprs[NumAarch64Gprs]; - size_t stack_dump_size; - u8 stack_dump[0x100]; - char proc_name[0xD]; - FatalCpuContext cpu_ctx; -}; - -static_assert(sizeof(Aarch64CpuContext) == 0x248, "Aarch64CpuContext definition!"); -static_assert(sizeof(Aarch32CpuContext) == 0xE0, "Aarch32CpuContext definition!"); -static_assert(sizeof(FatalCpuContext) == 0x250, "FatalCpuContext definition!"); -static_assert(std::is_pod_v, "FatalCpuContext definition!"); - -static constexpr const char *Aarch64GprNames[NumAarch64Gprs] = { - u8"X0", - u8"X1", - u8"X2", - u8"X3", - u8"X4", - u8"X5", - u8"X6", - u8"X7", - u8"X8", - u8"X9", - u8"X10", - u8"X11", - u8"X12", - u8"X13", - u8"X14", - u8"X15", - u8"X16", - u8"X17", - u8"X18", - u8"X19", - u8"X20", - u8"X21", - u8"X22", - u8"X23", - u8"X24", - u8"X25", - u8"X26", - u8"X27", - u8"X28", - u8"FP", - u8"LR", - u8"SP", -}; - -static constexpr const char *Aarch32GprNames[NumAarch32Gprs] = { - u8"R0", - u8"R1", - u8"R2", - u8"R3", - u8"R4", - u8"R5", - u8"R6", - u8"R7", - u8"R8", - u8"R9", - u8"R10", - u8"FP", - u8"IP", - u8"LR", - u8"SP", - u8"PC", -}; - diff --git a/stratosphere/fatal/source/fatal_user.cpp b/stratosphere/fatal/source/fatal_user.cpp deleted file mode 100644 index 39148274c..000000000 --- a/stratosphere/fatal/source/fatal_user.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 "fatal_user.hpp" -#include "fatal_throw.hpp" -#include "fatal_event_manager.hpp" -#include "fatal_task.hpp" - -Result UserService::ThrowFatal(u32 error, PidDescriptor pid_desc) { - return ThrowFatalImpl(error, pid_desc.pid, FatalType_ErrorReportAndErrorScreen, nullptr); -} - -Result UserService::ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy) { - return ThrowFatalImpl(error, pid_desc.pid, policy, nullptr); -} - -Result UserService::ThrowFatalWithCpuContext(u32 error, PidDescriptor pid_desc, FatalType policy, InBuffer _ctx) { - if (_ctx.num_elements < sizeof(FatalCpuContext)) { - return ThrowFatalImpl(error, pid_desc.pid, policy, nullptr); - } else { - return ThrowFatalImpl(error, pid_desc.pid, policy, reinterpret_cast(_ctx.buffer)); - } -} \ No newline at end of file diff --git a/stratosphere/fatal/source/fatal_user.hpp b/stratosphere/fatal/source/fatal_user.hpp deleted file mode 100644 index 34b3846ef..000000000 --- a/stratosphere/fatal/source/fatal_user.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 "fatal_types.hpp" - -class UserService final : public IServiceObject { - private: - enum class CommandId { - ThrowFatal = 0, - ThrowFatalWithPolicy = 1, - ThrowFatalWithCpuContext = 2, - }; - private: - /* Actual commands. */ - Result ThrowFatal(u32 error, PidDescriptor pid_desc); - Result ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy); - Result ThrowFatalWithCpuContext(u32 error, PidDescriptor pid_desc, FatalType policy, InBuffer _ctx); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MAKE_SERVICE_COMMAND_META(UserService, ThrowFatal), - MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithPolicy), - MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithCpuContext), - }; -}; diff --git a/stratosphere/libstratosphere/include/stratosphere.hpp b/stratosphere/libstratosphere/include/stratosphere.hpp index 000656cb6..78832a5de 100644 --- a/stratosphere/libstratosphere/include/stratosphere.hpp +++ b/stratosphere/libstratosphere/include/stratosphere.hpp @@ -45,6 +45,7 @@ #include "stratosphere/svc.hpp" #include "stratosphere/cfg.hpp" +#include "stratosphere/fatal.hpp" #include "stratosphere/hid.hpp" #include "stratosphere/ncm.hpp" #include "stratosphere/pm.hpp" diff --git a/stratosphere/fatal/source/fatal_private.cpp b/stratosphere/libstratosphere/include/stratosphere/fatal.hpp similarity index 77% rename from stratosphere/fatal/source/fatal_private.cpp rename to stratosphere/libstratosphere/include/stratosphere/fatal.hpp index c33518100..d3fbb885f 100644 --- a/stratosphere/fatal/source/fatal_private.cpp +++ b/stratosphere/libstratosphere/include/stratosphere/fatal.hpp @@ -14,10 +14,7 @@ * along with this program. If not, see . */ +#pragma once #include -#include "fatal_private.hpp" -#include "fatal_event_manager.hpp" -Result PrivateService::GetFatalEvent(Out out_h) { - return GetEventManager()->GetEvent(out_h.GetHandlePointer()); -} +#include "fatal/fatal_types.hpp" diff --git a/stratosphere/libstratosphere/include/stratosphere/fatal/fatal_types.hpp b/stratosphere/libstratosphere/include/stratosphere/fatal/fatal_types.hpp new file mode 100644 index 000000000..b0fe391d6 --- /dev/null +++ b/stratosphere/libstratosphere/include/stratosphere/fatal/fatal_types.hpp @@ -0,0 +1,340 @@ +/* + * 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 "../defines.hpp" +#include "../results.hpp" +#include "../ncm/ncm_types.hpp" + +namespace sts::fatal { + + namespace aarch64 { + + enum RegisterName { + RegisterName_X0 = 0, + RegisterName_X1 = 1, + RegisterName_X2 = 2, + RegisterName_X3 = 3, + RegisterName_X4 = 4, + RegisterName_X5 = 5, + RegisterName_X6 = 6, + RegisterName_X7 = 7, + RegisterName_X8 = 8, + RegisterName_X9 = 9, + RegisterName_X10 = 10, + RegisterName_X11 = 11, + RegisterName_X12 = 12, + RegisterName_X13 = 13, + RegisterName_X14 = 14, + RegisterName_X15 = 15, + RegisterName_X16 = 16, + RegisterName_X17 = 17, + RegisterName_X18 = 18, + RegisterName_X19 = 19, + RegisterName_X20 = 20, + RegisterName_X21 = 21, + RegisterName_X22 = 22, + RegisterName_X23 = 23, + RegisterName_X24 = 24, + RegisterName_X25 = 25, + RegisterName_X26 = 26, + RegisterName_X27 = 27, + RegisterName_X28 = 28, + RegisterName_FP = 29, + RegisterName_LR = 30, + + RegisterName_SP = 31, + RegisterName_PC = 32, + + RegisterName_GeneralPurposeCount, + + RegisterName_PState = 33, + RegisterName_Afsr0 = 34, + RegisterName_Afsr1 = 35, + RegisterName_Esr = 36, + RegisterName_Far = 37, + + RegisterName_Count, + }; + + struct CpuContext { + using RegisterType = u64; + static constexpr size_t MaxStackTraceDepth = 0x20; + + static constexpr const char *RegisterNameStrings[RegisterName_Count] = { + u8"X0", + u8"X1", + u8"X2", + u8"X3", + u8"X4", + u8"X5", + u8"X6", + u8"X7", + u8"X8", + u8"X9", + u8"X10", + u8"X11", + u8"X12", + u8"X13", + u8"X14", + u8"X15", + u8"X16", + u8"X17", + u8"X18", + u8"X19", + u8"X20", + u8"X21", + u8"X22", + u8"X23", + u8"X24", + u8"X25", + u8"X26", + u8"X27", + u8"X28", + u8"FP", + u8"LR", + u8"SP", + u8"PC", + u8"PState", + u8"Afsr0", + u8"Afsr1", + u8"Esr", + u8"Far", + }; + + /* Registers, exception context. N left names for these fields in fatal .rodata. */ + union { + struct { + union { + RegisterType x[RegisterName_GeneralPurposeCount]; + struct { + RegisterType _x[RegisterName_FP]; + RegisterType fp; + RegisterType lr; + RegisterType sp; + RegisterType pc; + }; + }; + RegisterType pstate; + RegisterType afsr0; + RegisterType afsr1; + RegisterType esr; + RegisterType far; + }; + RegisterType registers[RegisterName_Count]; + }; + + /* Misc. */ + RegisterType stack_trace[MaxStackTraceDepth]; + RegisterType base_address; + RegisterType register_set_flags; + u32 stack_trace_size; + + void ClearState() { + std::memset(this, 0, sizeof(*this)); + } + + void SetTitleIdForAtmosphere(ncm::TitleId title_id) { + /* Right now, we mux title ID in through afsr when creport. */ + /* TODO: Better way to do this? */ + this->afsr0 = static_cast(title_id); + } + + ncm::TitleId GetTitleIdForAtmosphere() const { + return ncm::TitleId{this->afsr0}; + } + + void SetRegisterValue(RegisterName name, RegisterType value) { + this->registers[name] = value; + this->register_set_flags |= (RegisterType(1) << name); + } + + bool HasRegisterValue(RegisterName name) const { + return this->register_set_flags & (RegisterType(1) << name); + } + + void SetBaseAddress(RegisterType base_addr) { + this->base_address = base_addr; + } + }; + + } + + namespace aarch32 { + + enum RegisterName { + RegisterName_R0 = 0, + RegisterName_R1 = 1, + RegisterName_R2 = 2, + RegisterName_R3 = 3, + RegisterName_R4 = 4, + RegisterName_R5 = 5, + RegisterName_R6 = 6, + RegisterName_R7 = 7, + RegisterName_R8 = 8, + RegisterName_R9 = 9, + RegisterName_R10 = 10, + RegisterName_FP = 11, + RegisterName_IP = 12, + RegisterName_LR = 13, + RegisterName_SP = 14, + RegisterName_PC = 15, + + RegisterName_GeneralPurposeCount, + + RegisterName_PState = 16, + RegisterName_Afsr0 = 17, + RegisterName_Afsr1 = 18, + RegisterName_Esr = 29, + RegisterName_Far = 20, + + RegisterName_Count, + }; + + struct CpuContext { + using RegisterType = u32; + static constexpr size_t MaxStackTraceDepth = 0x20; + + static constexpr const char *RegisterNameStrings[RegisterName_Count] = { + u8"R0", + u8"R1", + u8"R2", + u8"R3", + u8"R4", + u8"R5", + u8"R6", + u8"R7", + u8"R8", + u8"R9", + u8"R10", + u8"FP", + u8"IP", + u8"LR", + u8"SP", + u8"PC", + u8"PState", + u8"Afsr0", + u8"Afsr1", + u8"Esr", + u8"Far", + }; + + /* Registers, exception context. N left names for these fields in fatal .rodata. */ + union { + struct { + union { + RegisterType r[RegisterName_GeneralPurposeCount]; + struct { + RegisterType _x[RegisterName_FP]; + RegisterType fp; + RegisterType ip; + RegisterType lr; + RegisterType sp; + RegisterType pc; + }; + }; + RegisterType pstate; + RegisterType afsr0; + RegisterType afsr1; + RegisterType esr; + RegisterType far; + }; + RegisterType registers[RegisterName_Count]; + }; + + /* Misc. Yes, stack_trace_size is really laid out differently than aarch64... */ + RegisterType stack_trace[MaxStackTraceDepth]; + u32 stack_trace_size; + RegisterType base_address; + RegisterType register_set_flags; + + void ClearState() { + std::memset(this, 0, sizeof(*this)); + } + + void SetTitleIdForAtmosphere(ncm::TitleId title_id) { + /* Right now, we mux title ID in through afsr when creport. */ + /* TODO: Better way to do this? */ + this->afsr0 = static_cast(static_cast(title_id) >> 0); + this->afsr1 = static_cast(static_cast(title_id) >> 32); + } + + ncm::TitleId GetTitleIdForAtmosphere() const { + return ncm::TitleId{(static_cast(this->afsr1) << 32ul) | (static_cast(this->afsr0) << 0ul)}; + } + + void SetRegisterValue(RegisterName name, RegisterType value) { + this->registers[name] = value; + this->register_set_flags |= (RegisterType(1) << name); + } + + bool HasRegisterValue(RegisterName name) const { + return this->register_set_flags & (RegisterType(1) << name); + } + + void SetBaseAddress(RegisterType base_addr) { + this->base_address = base_addr; + } + }; + + } + + struct CpuContext { + enum Architecture { + Architecture_Aarch64 = 0, + Architecture_Aarch32 = 1, + }; + + union { + aarch64::CpuContext aarch64_ctx; + aarch32::CpuContext aarch32_ctx; + }; + + Architecture architecture; + u32 type; + + void ClearState() { + std::memset(this, 0, sizeof(*this)); + } + }; + + static_assert(std::is_pod::value && sizeof(aarch64::CpuContext) == 0x248, "aarch64::CpuContext definition!"); + static_assert(std::is_pod::value && sizeof(aarch32::CpuContext) == 0xE0, "aarch32::CpuContext definition!"); + static_assert(std::is_pod::value && sizeof(CpuContext) == 0x250, "CpuContext definition!"); + + namespace srv { + + struct ThrowContext { + u32 error_code; + ncm::TitleId title_id; + char proc_name[0xD]; + bool is_creport; + CpuContext cpu_ctx; + bool generate_error_report; + Event erpt_event; + Event battery_event; + size_t stack_dump_size; + u8 stack_dump[0x100]; + + void ClearState() { + std::memset(this, 0, sizeof(*this)); + } + }; + + } + +} diff --git a/stratosphere/libstratosphere/include/stratosphere/results.hpp b/stratosphere/libstratosphere/include/stratosphere/results.hpp index 70970fee0..5eddf72fc 100644 --- a/stratosphere/libstratosphere/include/stratosphere/results.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results.hpp @@ -23,6 +23,7 @@ #include "results/creport_results.hpp" #include "results/debug_results.hpp" #include "results/dmnt_results.hpp" +#include "results/err_results.hpp" #include "results/fatal_results.hpp" #include "results/fs_results.hpp" #include "results/hipc_results.hpp" diff --git a/stratosphere/fatal/source/fatal_private.hpp b/stratosphere/libstratosphere/include/stratosphere/results/err_results.hpp similarity index 62% rename from stratosphere/fatal/source/fatal_private.hpp rename to stratosphere/libstratosphere/include/stratosphere/results/err_results.hpp index 887623e9a..3aa146c0b 100644 --- a/stratosphere/fatal/source/fatal_private.hpp +++ b/stratosphere/libstratosphere/include/stratosphere/results/err_results.hpp @@ -16,18 +16,8 @@ #pragma once #include -#include -class PrivateService final : public IServiceObject { - private: - enum class CommandId { - GetFatalEvent = 0, - }; - private: - /* Actual commands. */ - Result GetFatalEvent(Out out_h); - public: - DEFINE_SERVICE_DISPATCH_TABLE { - MAKE_SERVICE_COMMAND_META(PrivateService, GetFatalEvent), - }; -}; +static constexpr u32 Module_Err = 162; + +static constexpr Result ResultErrApplicationAborted = MAKERESULT(Module_Err, 1); +static constexpr Result ResultErrSystemModuleAborted = MAKERESULT(Module_Err, 2);