fatal: refactor into sts namespace

This commit is contained in:
Michael Scire 2019-07-18 19:09:35 -07:00 committed by SciresM
parent 442ebff829
commit 39d041466d
38 changed files with 2176 additions and 1926 deletions

View file

@ -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<sts::fatal::CpuContext *>(_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<u32>(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<fatal::aarch64::RegisterName>(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() {

View file

@ -92,5 +92,12 @@
}, {
"type": "handle_table_size",
"value": 128
},
{
"type": "debug_flags",
"value": {
"allow_debug": false,
"force_debug": true
}
}]
}

View file

@ -14,11 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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!");
static_assert(util::size(AtmosphereLogoData) == AtmosphereLogoWidth * AtmosphereLogoHeight, "Logo definition!");

View file

@ -14,78 +14,82 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#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 creator. */
IEvent *CreateFatalDirtyEvent() {
Event evt;
if (R_FAILED(setsysBindFatalDirtyFlagEvent(&evt))) {
std::abort();
}
g_fatal_settings_event = LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) {
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)) {
UpdateLanguageCode();
g_config.UpdateLanguageCode();
}
return ResultSuccess;
}, true);
}
return g_fatal_settings_event;
}
/* Global event. */
IEvent *g_fatal_dirty_event = CreateFatalDirtyEvent();
}
static void SetupConfigLanguages() {
FatalConfig *config = GetFatalConfig();
FatalConfig::FatalConfig() {
/* Clear this. */
std::memset(this, 0, sizeof(*this));
/* Defaults. */
config->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n";
/* Get information from set. */
setsysGetSerialNumber(this->serial_number);
setsysGetFirmwareVersion(&this->firmware_version);
setsysGetFlag(SetSysFlag_Quest, &this->quest_flag);
this->UpdateLanguageCode();
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"
/* 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));
/* 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";
}
/* TODO: Try to load dynamically. */
/* 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"))) { ... } */
}
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();
}
}
IEvent *GetFatalDirtyEvent() {
return g_fatal_dirty_event;
}
const FatalConfig &GetFatalConfig() {
return g_config;
}
}

View file

@ -18,7 +18,10 @@
#include <switch.h>
#include <stratosphere.hpp>
struct FatalConfig {
namespace sts::fatal::srv {
class FatalConfig {
private:
char serial_number[0x18];
SetSysFirmwareVersion firmware_version;
u64 language_code;
@ -30,10 +33,64 @@ struct FatalConfig {
const char *error_desc;
const char *quest_desc;
u64 fatal_auto_reboot_interval;
bool is_auto_reboot_enabled;
};
bool fatal_auto_reboot_enabled;
public:
FatalConfig();
IEvent *GetFatalSettingsEvent();
FatalConfig *GetFatalConfig();
const char *GetSerialNumber() const {
return this->serial_number;
}
void InitializeFatalConfig();
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();
}

View file

@ -13,57 +13,38 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <map>
#include <switch.h>
#include <unordered_map>
#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;
}
return true;
}
static bool CheckThreadIsFatalCaller(FatalThrowContext *ctx, u64 debug_handle, u64 thread_id, u64 thread_tls_addr, ThreadContext *thread_ctx) {
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))) {
u32 _thread_state;
if (R_FAILED(svcGetDebugThreadParam(&_, &_thread_state, debug_handle, thread_id, DebugThreadParam_State))) {
return false;
}
if (thread_state > 1) {
const svc::ThreadState thread_state = static_cast<svc::ThreadState>(_thread_state);
if (thread_state != svc::ThreadState::Waiting && thread_state != svc::ThreadState::Running) {
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)) {
if (R_FAILED(svcGetDebugThreadContext(thread_ctx, debug_handle, thread_id, svc::ThreadContextFlag_All))) {
return false;
}
@ -74,25 +55,19 @@ static bool CheckThreadIsFatalCaller(FatalThrowContext *ctx, u64 debug_handle, u
}
/* If the instruction isn't svcSendSyncRequest, it's not the fatal caller. */
if (insn != 0xD4000421) {
if (insn != SvcSendSyncRequestInstruction) {
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. */
/* 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. */
/* 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));
/* Parse the command that the thread sent. */
{
IpcParsedCommand r;
if (R_FAILED(ipcParse(&r))) {
@ -123,124 +98,16 @@ static bool CheckThreadIsFatalCaller(FatalThrowContext *ctx, u64 debug_handle, u
return false;
}
if (raw->err_code != ctx->error_code) {
if (raw->err_code != 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<u64, u64> 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;
}
}
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;
}
/* 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 (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;
}
}
if (!found_fatal_caller) {
return;
}
}
if (R_FAILED(svcGetDebugThreadContext(&thread_ctx, debug_handle, thread_id, 0xF))) {
return;
}
/* 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. */
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 (TryGuessBaseAddress(&base_address, debug_handle, thread_ctx->lr)) {
return base_address;
}
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;
}
}
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<u64, u64> thread_id_to_tls;
{
if (TryGuessStartAddress(thread_ctx.pc.x)) {
return;
bool got_attach_process = false;
svc::DebugEventInfo d;
while (R_SUCCEEDED(svcGetDebugEvent(reinterpret_cast<u8 *>(&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 (TryGuessStartAddress(thread_ctx.lr)) {
return;
}
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])) {
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()));
}
}

View file

@ -18,132 +18,8 @@
#include <switch.h>
#include <stratosphere.hpp>
#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!");

View file

@ -14,39 +14,33 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#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() {
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();
R_ASSERT(eventCreate(&this->events[i], true));
}
}
}
Result FatalEventManager::GetEvent(Handle *out) {
std::scoped_lock<HosMutex> 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) {
if (this->num_events_gotten >= FatalEventManager::NumFatalEvents) {
return ResultFatalTooManyEvents;
}
*out = this->events[this->events_gotten++].revent;
*out = this->events[this->num_events_gotten++].revent;
return ResultSuccess;
}
}
void FatalEventManager::SignalEvents() {
void FatalEventManager::SignalEvents() {
for (size_t i = 0; i < FatalEventManager::NumFatalEvents; i++) {
eventFire(&this->events[i]);
}
}
}

View file

@ -18,17 +18,19 @@
#include <switch.h>
#include <stratosphere.hpp>
class FatalEventManager {
namespace sts::fatal::srv {
class FatalEventManager {
private:
static constexpr size_t NumFatalEvents = 3;
HosMutex lock;
size_t events_gotten = 0;
size_t num_events_gotten = 0;
Event events[NumFatalEvents];
public:
FatalEventManager();
Result GetEvent(Handle *out);
void SignalEvents();
};
};
FatalEventManager *GetEventManager();
}

View file

@ -14,35 +14,40 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "fatal_types.hpp"
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <ft2build.h>
#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) {
/* 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;
u32 g_mono_adv = 0;
PlFontData g_font;
PlFontData g_fonts[PlSharedFontType_Total];
FT_Library g_library;
FT_Face g_face;
FT_Error g_ft_err = 0;
/* 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);
@ -55,9 +60,9 @@ static u16 Blend(u16 color, u16 bg, u8 alpha) {
const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF;
return RGB888_TO_RGB565(r, g, b);
}
}
static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
u8* imageptr = bitmap->buffer;
if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return;
@ -65,14 +70,14 @@ static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
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)];
u16 *ptr = &g_frame_buffer[g_unswizzle_func(x + tmpx, y + tmpy)];
*ptr = Blend(g_font_color, *ptr, imageptr[tmpx]);
}
imageptr += bitmap->pitch;
}
}
}
static void DrawString(const char *str, bool add_line, bool mono = false) {
void DrawString(const char *str, bool add_line, bool mono = false) {
FT_UInt glyph_index;
FT_GlyphSlot slot = g_face->glyph;
@ -119,13 +124,14 @@ static void DrawString(const char *str, bool add_line, bool mono = false) {
cur_x += (mono ? g_mono_adv : slot->advance.x) >> 6;
cur_y += slot->advance.y >> 6;
}
}
}
}
void FontManager::PrintLine(const char *str) {
void PrintLine(const char *str) {
return DrawString(str, true);
}
}
void FontManager::PrintFormatLine(const char *format, ...) {
void PrintFormatLine(const char *format, ...) {
char char_buf[0x400];
va_list va_arg;
@ -134,13 +140,13 @@ void FontManager::PrintFormatLine(const char *format, ...) {
va_end(va_arg);
PrintLine(char_buf);
}
}
void FontManager::Print(const char *str) {
void Print(const char *str) {
return DrawString(str, false);
}
}
void FontManager::PrintFormat(const char *format, ...) {
void PrintFormat(const char *format, ...) {
char char_buf[0x400];
va_list va_arg;
@ -149,52 +155,52 @@ void FontManager::PrintFormat(const char *format, ...) {
va_end(va_arg);
Print(char_buf);
}
}
void FontManager::PrintMonospaceU64(u64 x) {
void 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) {
void 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) {
void 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) {
void SetFontColor(u16 color) {
g_font_color = color;
}
}
void FontManager::SetPosition(u32 x, u32 y) {
void SetPosition(u32 x, u32 y) {
g_line_x = x;
g_cur_x = x;
g_cur_y = y;
}
}
u32 FontManager::GetX() {
u32 GetX() {
return g_cur_x;
}
}
u32 FontManager::GetY() {
u32 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<u32>(g_font_sz * 64.0f), 96, 96);
void SetFontSize(float fsz) {
g_font_size = fsz;
g_ft_err = FT_Set_Char_Size(g_face, 0, static_cast<u32>(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);
@ -204,22 +210,22 @@ void FontManager::SetFontSize(float fsz) {
if (g_ft_err == 0) {
g_mono_adv = g_face->glyph->advance.x;
}
}
}
void FontManager::AddSpacingLines(float num_lines) {
void AddSpacingLines(float num_lines) {
g_cur_x = g_line_x;
g_cur_y += static_cast<u32>((static_cast<float>(g_face->size->metrics.height) * num_lines) / 64.0f);
}
}
void FontManager::ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)) {
g_fb = fb;
void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)) {
g_frame_buffer = fb;
g_unswizzle_func = unswizzle_func;
}
}
Result FontManager::InitializeSharedFont() {
Result InitializeSharedFont() {
size_t total_fonts = 0;
R_TRY(plGetSharedFont(GetFatalConfig()->language_code, g_fonts, PlSharedFontType_Total, &total_fonts));
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);
@ -228,6 +234,8 @@ Result FontManager::InitializeSharedFont() {
g_ft_err = FT_New_Memory_Face(g_library, reinterpret_cast<const FT_Byte *>(g_font.address), g_font.size, 0, &g_face);
if (g_ft_err) return g_ft_err;
SetFontSize(g_font_sz);
SetFontSize(g_font_size);
return g_ft_err;
}
}

View file

@ -19,27 +19,23 @@
#include <switch.h>
#include <stratosphere.hpp>
#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);
};
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);
}

View file

@ -23,9 +23,7 @@
#include <atmosphere.h>
#include <stratosphere.hpp>
#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<PrivateService>("fatal:p", 4));
s_server_manager.AddWaitable(new ServiceServer<UserService>("fatal:u", 4));
s_server_manager.AddWaitable(GetFatalSettingsEvent());
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::PrivateService>("fatal:p", 4));
s_server_manager.AddWaitable(new ServiceServer<sts::fatal::srv::UserService>("fatal:u", 4));
s_server_manager.AddWaitable(sts::fatal::srv::GetFatalDirtyEvent());
/* Loop forever, servicing our services. */
s_server_manager.Process();

View file

@ -14,23 +14,25 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <stratosphere.hpp>
#include "fatal_types.hpp"
#include "fatal_repair.hpp"
#include "fatal_throw.hpp"
#include "fatal_service_for_self.hpp"
static bool InRepairWithoutVolHeld() {
namespace sts::fatal::srv {
namespace {
bool IsInRepair() {
/* Before firmware 3.0.0, this wasn't implemented. */
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
return false;
}
bool in_repair;
if (R_FAILED(setsysGetFlag(SetSysFlag_InRepairProcessEnable, &in_repair)) || !in_repair) {
return false;
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;
@ -43,7 +45,7 @@ static bool InRepairWithoutVolHeld() {
gpioPadSetDirection(&vol_btn, GpioDirection_Input);
/* Ensure that we're holding the volume button for a full second. */
TimeoutHelper timeout_helper(1000000000UL);
TimeoutHelper timeout_helper(1'000'000'000ul);
while (!timeout_helper.TimedOut()) {
GpioValue val;
if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) {
@ -51,29 +53,30 @@ static bool InRepairWithoutVolHeld() {
}
/* Sleep for 100 ms. */
svcSleepThread(100000000UL);
svcSleepThread(100'000'000ul);
}
}
return false;
}
}
static bool InRepairWithoutTimeReviserCartridge() {
if (GetRuntimeFirmwareVersion() < FirmwareVersion_500) {
bool NeedsRunTimeReviser() {
/* Before firmware 5.0.0, this wasn't implemented. */
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
return false;
}
bool requires_time_reviser;
if (R_FAILED(setsysGetFlag(SetSysFlag_RequiresRunRepairTimeReviser, &requires_time_reviser)) || !requires_time_reviser) {
return false;
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 true;
return false;
}
/* Ensure we close even on early return. */
@ -82,25 +85,33 @@ static bool InRepairWithoutTimeReviserCartridge() {
/* Check that a gamecard is inserted. */
bool inserted;
if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&devop, &inserted)) || !inserted) {
return true;
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 true;
return false;
}
}
/* Check that the gamecard is a repair tool. */
return (gc_attr & FsGameCardAttribute_Repair) == FsGameCardAttribute_Repair;
}
}
void CheckRepairStatus() {
if (InRepairWithoutVolHeld()) {
bool IsInRepairWithoutTimeReviserCartridge() {
return NeedsRunTimeReviser() && IsTimeReviserCartridgeInserted();
}
}
void CheckRepairStatus() {
if (IsInRepairWithoutVolHeld()) {
ThrowFatalForSelf(ResultFatalInRepairWithoutVolHeld);
}
if (InRepairWithoutTimeReviserCartridge()) {
if (IsInRepairWithoutTimeReviserCartridge()) {
ThrowFatalForSelf(ResultFatalInRepairWithoutTimeReviserCartridge);
}
}
}

View file

@ -18,4 +18,8 @@
#include <switch.h>
#include <stratosphere.hpp>
void CheckRepairStatus();
namespace sts::fatal::srv {
void CheckRepairStatus();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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<u32>(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<u8> _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<const CpuContext *>(_ctx.buffer));
}
}
Result PrivateService::GetFatalEvent(Out<CopiedHandle> out_h) {
return g_context.GetEvent(out_h.GetHandlePointer());
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
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<u8> _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<CopiedHandle> out_h);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(PrivateService, GetFatalEvent),
};
};
}

View file

@ -16,9 +16,9 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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);
}

View file

@ -15,7 +15,7 @@
*/
#include <switch.h>
#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 {
static void RunTaskThreadFunc(void *arg) {
IFatalTask *task = reinterpret_cast<IFatalTask *>(arg);
class TaskThread {
NON_COPYABLE(TaskThread);
private:
static constexpr int TaskThreadPriority = 15;
private:
HosThread thread;
private:
static void RunTaskImpl(void *arg) {
ITask *task = reinterpret_cast<ITask *>(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());
}
};
/* Finish. */
}
static void RunTask(IFatalTask *task, u32 stack_size = 0x4000) {
if (g_num_threads >= MaxTasks) {
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();
}
HosThread *cur_thread = &g_task_threads[g_num_threads++];
this->task_threads[this->task_count++].StartTask(task);
}
};
cur_thread->Initialize(&RunTaskThreadFunc, task, stack_size, 15);
cur_thread->Start();
}
/* Global task manager. */
TaskManager g_task_manager;
}
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));
}
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));
}

View file

@ -17,16 +17,29 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#include "fatal_types.hpp"
#include "fatal_service.hpp"
class IFatalTask {
protected:
FatalThrowContext *ctx;
u64 title_id;
namespace sts::fatal::srv {
class ITask {
public:
IFatalTask(FatalThrowContext *ctx, u64 tid) : ctx(ctx), title_id(tid) { }
virtual Result Run() = 0;
virtual const char *GetName() const = 0;
};
static constexpr size_t DefaultStackSize = 0x1000;
protected:
const ThrowContext *context = nullptr;
public:
void Initialize(const ThrowContext *context) {
this->context = context;
}
void RunFatalTasks(FatalThrowContext *ctx, u64 title_id, bool error_report, Event *erpt_event, Event *battery_event);
virtual Result Run() = 0;
virtual const char *GetName() const = 0;
virtual size_t GetStackSize() const {
return DefaultStackSize;
}
};
void RunTasks(const ThrowContext *ctx);
}

View file

@ -14,10 +14,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "fatal_task_clock.hpp"
Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) {
namespace sts::fatal::srv {
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;
@ -34,9 +54,9 @@ Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) {
}
return ResultSuccess;
}
}
Result AdjustClockTask::AdjustClock() {
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;
@ -47,8 +67,17 @@ Result AdjustClockTask::AdjustClock() {
R_TRY(AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ));
return ResultSuccess;
}
}
Result AdjustClockTask::Run() {
Result AdjustClockTask::Run() {
return AdjustClock();
}
}
ITask *GetAdjustClockTask(const ThrowContext *ctx) {
g_adjust_clock_task.Initialize(ctx);
return &g_adjust_clock_task;
}
}

View file

@ -15,18 +15,10 @@
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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";
}
};
namespace sts::fatal::srv {
ITask *GetAdjustClockTask(const ThrowContext *ctx);
}

View file

@ -17,133 +17,141 @@
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <switch.h>
#include <atmosphere/version.h>
#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) {
namespace {
/* Helpers. */
void TryEnsureReportDirectories() {
mkdir("sdmc:/atmosphere", S_IRWXU);
mkdir("sdmc:/atmosphere/fatal_reports", S_IRWXU);
mkdir("sdmc:/atmosphere/fatal_reports/dumps", S_IRWXU);
}
bool TryGetCurrentTimestamp(u64 *out) {
/* Clear output. */
*out = 0;
/* Verify that pcv isn't dead. */
/* Check if we have time service. */
{
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) {
bool has_time_service = false;
if (R_FAILED(sm::HasService(&has_time_service, sm::ServiceName::Encode("time:s"))) || !has_time_service) {
return false;
}
}
/* Try to get the current time. */
bool success = true;
DoWithSmSession([&]() {
success &= R_SUCCEEDED(timeInitialize());
});
if (success) {
success &= R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out));
timeExit();
{
sm::ScopedServiceHolder<timeInitialize, timeExit> time_holder;
return time_holder && R_SUCCEEDED(timeGetCurrentTime(TimeType_LocalSystemClock, out));
}
}
return success;
}
void ErrorReportTask::SaveReportToSdCard() {
/* 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];
/* Ensure path exists. */
EnsureReportDirectories();
/* Try to Ensure path exists. */
TryEnsureReportDirectories();
/* Get a timestamp. */
u64 timestamp;
if (!GetCurrentTime(&timestamp)) {
if (!TryGetCurrentTimestamp(&timestamp)) {
timestamp = svcGetSystemTick();
}
/* Open report file. */
snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/%011lu_%016lx.log", timestamp, this->title_id);
snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/%011lu_%016lx.log", timestamp, static_cast<u64>(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->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, "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<u64>(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()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision());
fprintf(f_report, u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig().GetFirmwareVersion().display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision());
if (this->ctx->cpu_ctx.is_aarch32) {
if (this->context->cpu_ctx.architecture == CpuContext::Architecture_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]);
for (size_t i = 0; i <= aarch32::RegisterName_PC; i++) {
if (this->context->cpu_ctx.aarch32_ctx.HasRegisterValue(static_cast<aarch32::RegisterName>(i))) {
fprintf(f_report, " %3s: %08x\n", aarch32::CpuContext::RegisterNameStrings[i], this->context->cpu_ctx.aarch32_ctx.r[i]);
}
}
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, "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->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]);
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 < NumAarch64Gprs; i++) {
if (this->ctx->has_gprs[i]) {
fprintf(f_report, " %3s: %016lx\n", Aarch64GprNames[i], this->ctx->cpu_ctx.aarch64_ctx.x[i]);
for (size_t i = 0; i <= aarch64::RegisterName_PC; i++) {
if (this->context->cpu_ctx.aarch64_ctx.HasRegisterValue(static_cast<aarch64::RegisterName>(i))) {
fprintf(f_report, " %3s: %016lx\n", aarch64::CpuContext::RegisterNameStrings[i], this->context->cpu_ctx.aarch64_ctx.x[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, "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->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]);
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]);
}
}
}
}
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);
if (this->context->stack_dump_size) {
snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, static_cast<u64>(this->context->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);
fwrite(this->context->stack_dump, this->context->stack_dump_size, 1, f_stackdump);
fflush(f_stackdump);
}
}
}
Result ErrorReportTask::Run() {
if (this->create_report) {
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->ctx->is_creport) {
SaveReportToSdCard();
if (!this->context->is_creport) {
this->SaveReportToSdCard();
}
/* Signal we're done with our job. */
eventFire(this->erpt_event);
eventFire(const_cast<Event *>(&this->context->erpt_event));
return ResultSuccess;
}
}
ITask *GetErrorReportTask(const ThrowContext *ctx) {
g_error_report_task.Initialize(ctx);
return &g_error_report_task;
}
}

View file

@ -15,22 +15,10 @@
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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";
}
};
namespace sts::fatal::srv {
ITask *GetErrorReportTask(const ThrowContext *ctx);
}

View file

@ -14,14 +14,54 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "fatal_task_power.hpp"
#include "fatal_config.hpp"
#include "fatal_task_power.hpp"
bool PowerControlTask::TryShutdown() {
namespace sts::fatal::srv {
namespace {
/* Task types. */
class PowerControlTask : public ITask {
private:
bool TryShutdown();
void MonitorBatteryState();
public:
virtual Result Run() override;
virtual const char *GetName() const override {
return "PowerControlTask";
}
};
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(30000000000UL);
bool cancel_shutdown = false;
TimeoutHelper timeout_helper(30'000'000'000ul);
bool perform_shutdown = true;
PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal;
while (true) {
@ -34,36 +74,36 @@ bool PowerControlTask::TryShutdown() {
}
if (bv_state == PsmBatteryVoltageState_Normal) {
cancel_shutdown = true;
perform_shutdown = false;
break;
}
/* Query voltage state every 5 seconds, for 30 seconds. */
svcSleepThread(5000000000UL);
svcSleepThread(5'000'000'000ul);
}
if (!cancel_shutdown) {
if (perform_shutdown) {
bpcShutdownSystem();
return true;
} else {
return false;
}
}
void PowerControlTask::MonitorBatteryState() {
return perform_shutdown;
}
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));
eventWait(const_cast<Event *>(&this->context->erpt_event), TimeoutHelper::NsToTick(1'000'000'000ul));
this->TryShutdown();
return;
}
/* Signal we've checked the battery at least once. */
eventFire(this->battery_event);
eventFire(const_cast<Event *>(&this->context->battery_event));
/* Loop querying voltage state every 5 seconds. */
while (true) {
if (R_FAILED(psmGetBatteryVoltageState(&bv_state))) {
bv_state = PsmBatteryVoltageState_NeedsShutdown;
@ -83,20 +123,18 @@ void PowerControlTask::MonitorBatteryState() {
break;
}
/* Query voltage state every 5 seconds. */
svcSleepThread(5000000000UL);
svcSleepThread(5'000'000'000ul);
}
}
}
void PowerButtonObserveTask::WaitForPowerButton() {
void PowerButtonObserveTask::WaitForPowerButton() {
/* Wait up to a second for error report generation to finish. */
eventWait(this->erpt_event, TimeoutHelper::NsToTick(1000000000UL));
eventWait(const_cast<Event *>(&this->context->erpt_event), TimeoutHelper::NsToTick(1'000'000'000ul));
/* Force a reboot after some time if kiosk unit. */
const FatalConfig *config = GetFatalConfig();
TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL);
TimeoutHelper auto_reboot_helper(config->fatal_auto_reboot_interval * 1000000);
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;
@ -122,41 +160,53 @@ void PowerButtonObserveTask::WaitForPowerButton() {
BpcSleepButtonState state;
GpioValue val;
while (true) {
if (config->is_auto_reboot_enabled && auto_reboot_helper.TimedOut() ) {
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;
}
if (check_vol_up && R_SUCCEEDED(gpioPadGetValue(&vol_up_btn, &val)) && val == GpioValue_Low) {
bpcRebootSystem();
}
if (check_vol_down && R_SUCCEEDED(gpioPadGetValue(&vol_down_btn, &val)) && val == GpioValue_Low) {
bpcRebootSystem();
}
if ((R_SUCCEEDED(bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) {
bpcRebootSystem();
return;
}
/* Wait 100 ms between button checks. */
svcSleepThread(100000000UL);
svcSleepThread(100'000'000ul);
}
}
}
Result PowerControlTask::Run() {
MonitorBatteryState();
Result PowerControlTask::Run() {
this->MonitorBatteryState();
return ResultSuccess;
}
}
Result PowerButtonObserveTask::Run() {
WaitForPowerButton();
Result PowerButtonObserveTask::Run() {
this->WaitForPowerButton();
return ResultSuccess;
}
}
Result StateTransitionStopTask::Run() {
Result StateTransitionStopTask::Run() {
/* Nintendo ignores the output of this call... */
spsmPutErrorState();
return ResultSuccess;
}
}
ITask *GetPowerControlTask(const ThrowContext *ctx) {
g_power_control_task.Initialize(ctx);
return &g_power_control_task;
}
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;
}
}

View file

@ -15,43 +15,12 @@
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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";
}
};
}

View file

@ -14,74 +14,120 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include <atmosphere/version.h>
#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)));
#include "fatal_ams_logo.inc"
}
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;
}
}
Result ShowFatalTask::SetupDisplayInternal() {
/* 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", &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(&display); };
ON_SCOPE_EXIT { viCloseDisplay(&temp_display); };
/* Turn on the screen. */
R_TRY(viSetDisplayPowerState(&display, ViPowerState_On));
R_TRY(viSetDisplayPowerState(&temp_display, ViPowerState_On));
/* Set alpha to 1.0f. */
R_TRY(viSetDisplayAlpha(&display, 1.0f));
R_TRY(viSetDisplayAlpha(&temp_display, 1.0f));
return ResultSuccess;
}
}
Result ShowFatalTask::SetupDisplayExternal() {
ViDisplay display;
Result ShowFatalTask::SetupDisplayExternal() {
ViDisplay temp_display;
/* Try to open the display. */
R_TRY_CATCH(viOpenDisplay("External", &display)) {
R_TRY_CATCH(viOpenDisplay("External", &temp_display)) {
R_CATCH(ResultViNotFound) {
return ResultSuccess;
}
} R_END_TRY_CATCH;
/* Guarantee we close the display. */
ON_SCOPE_EXIT { viCloseDisplay(&display); };
ON_SCOPE_EXIT { viCloseDisplay(&temp_display); };
/* Set alpha to 1.0f. */
R_TRY(viSetDisplayAlpha(&display, 1.0f));
R_TRY(viSetDisplayAlpha(&temp_display, 1.0f));
return ResultSuccess;
}
}
Result ShowFatalTask::PrepareScreenForDrawing() {
Result ShowFatalTask::PrepareScreenForDrawing() {
/* Connect to vi. */
R_TRY(viInitialize(ViServiceType_Manager));
@ -119,15 +165,11 @@ Result ShowFatalTask::PrepareScreenForDrawing() {
const float layer_x = static_cast<float>((display_width - layer_width) / 2);
const float layer_y = static_cast<float>((display_height - layer_height) / 2);
u64 layer_z;
R_TRY(viSetLayerSize(&this->layer, layer_width, layer_height));
/* 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));
}
R_TRY(viSetLayerZ(&this->layer, FatalLayerZ));
/* Center the layer in the screen. */
R_TRY(viSetLayerPosition(&this->layer, layer_x, layer_y));
@ -138,16 +180,14 @@ Result ShowFatalTask::PrepareScreenForDrawing() {
}
return ResultSuccess;
}
}
Result ShowFatalTask::ShowFatal() {
const FatalConfig *config = GetFatalConfig();
Result ShowFatalTask::ShowFatal() {
const FatalConfig &config = GetFatalConfig();
/* Prepare screen for drawing. */
DoWithSmSession([&]() {
if (R_FAILED(PrepareScreenForDrawing())) {
std::abort();
}
R_ASSERT(PrepareScreenForDrawing());
});
/* Dequeue a buffer. */
@ -157,8 +197,8 @@ Result ShowFatalTask::ShowFatal() {
}
/* Let the font manager know about our framebuffer. */
FontManager::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset);
FontManager::SetFontColor(0xFFFF);
font::ConfigureFontFramebuffer(tiled_buf, GetPixelOffset);
font::SetFontColor(0xFFFF);
/* Draw a background. */
for (size_t i = 0; i < this->fb.fb_size / sizeof(*tiled_buf); i++) {
@ -166,26 +206,26 @@ Result ShowFatalTask::ShowFatal() {
}
/* 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];
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];
}
}
/* 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);
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<u64>(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 {
/* Print a special message for atmosphere version mismatch. */
FontManager::Print(u8"Atmosphère version mismatch detected.\n\n"
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"
@ -195,174 +235,176 @@ Result ShowFatalTask::ShowFatal() {
/* Add a line. */
for (size_t x = 32; x < FatalScreenWidth - 32; x++) {
tiled_buf[GetPixelOffset(x, FontManager::GetY())] = 0xFFFF;
tiled_buf[GetPixelOffset(x, font::GetY())] = 0xFFFF;
}
font::AddSpacingLines(1.5f);
FontManager::AddSpacingLines(1.5f);
u32 backtrace_y = FontManager::GetY();
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. */
FontManager::SetFontSize(14.0f);
FontManager::Print("General Purpose Registers ");
font::SetFontSize(14.0f);
font::Print("General Purpose Registers ");
{
FontManager::SetPosition(FontManager::GetX() + 2, FontManager::GetY());
u32 x = FontManager::GetX();
FontManager::Print("PC: ");
FontManager::SetPosition(x + 47, FontManager::GetY());
font::SetPosition(font::GetX() + 2, font::GetY());
u32 x = font::GetX();
font::Print("PC: ");
font::SetPosition(x + 47, font::GetY());
}
if (this->ctx->cpu_ctx.is_aarch32) {
FontManager::PrintMonospaceU32(this->ctx->cpu_ctx.aarch32_ctx.pc);
if (is_aarch32) {
font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.pc);
} else {
FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.pc);
font::PrintMonospaceU64(this->context->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]);
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<aarch32::RegisterName>(i))) {
font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.r[i]);
} else {
FontManager::PrintMonospaceBlank(8);
font::PrintMonospaceBlank(8);
}
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)]);
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<aarch32::RegisterName>(i + (aarch32::RegisterName_GeneralPurposeCount / 2)))) {
font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.r[i + (aarch32::RegisterName_GeneralPurposeCount / 2)]);
} else {
FontManager::PrintMonospaceBlank(8);
font::PrintMonospaceBlank(8);
}
if (i == (NumAarch32Gprs / 2) - 1) {
FontManager::Print(" ");
backtrace_x = FontManager::GetX();
if (i == (aarch32::RegisterName_GeneralPurposeCount / 2) - 1) {
font::Print(" ");
backtrace_x = font::GetX();
}
FontManager::PrintLine("");
FontManager::SetPosition(32, FontManager::GetY());
font::PrintLine("");
font::SetPosition(32, font::GetY());
}
} 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]);
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<aarch64::RegisterName>(i))) {
font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.x[i]);
} else {
FontManager::PrintMonospaceBlank(16);
font::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)]);
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<aarch64::RegisterName>(i + (aarch64::RegisterName_GeneralPurposeCount / 2)))) {
font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.x[i + (aarch64::RegisterName_GeneralPurposeCount / 2)]);
} else {
FontManager::PrintMonospaceBlank(16);
font::PrintMonospaceBlank(16);
}
if (i == (NumAarch64Gprs / 2) - 1) {
FontManager::Print(" ");
backtrace_x = FontManager::GetX();
if (i == (aarch64::RegisterName_GeneralPurposeCount / 2) - 1) {
font::Print(" ");
backtrace_x = font::GetX();
}
FontManager::PrintLine("");
FontManager::SetPosition(32, FontManager::GetY());
font::PrintLine("");
font::SetPosition(32, font::GetY());
}
}
/* Print Backtrace. */
u32 bt_size;
if (this->ctx->cpu_ctx.is_aarch32) {
bt_size = this->ctx->cpu_ctx.aarch32_ctx.stack_trace_size;
if (is_aarch32) {
bt_size = this->context->cpu_ctx.aarch32_ctx.stack_trace_size;
} else {
bt_size = this->ctx->cpu_ctx.aarch64_ctx.stack_trace_size;
bt_size = this->context->cpu_ctx.aarch64_ctx.stack_trace_size;
}
FontManager::SetPosition(backtrace_x, backtrace_y);
font::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("");
if (is_aarch32) {
font::Print("Start Address: ");
font::PrintMonospaceU32(this->context->cpu_ctx.aarch32_ctx.base_address);
font::PrintLine("");
} else {
FontManager::Print("Start Address: ");
FontManager::PrintMonospaceU64(this->ctx->cpu_ctx.aarch64_ctx.start_address);
FontManager::PrintLine("");
font::Print("Start Address: ");
font::PrintMonospaceU64(this->context->cpu_ctx.aarch64_ctx.base_address);
font::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++) {
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->ctx->cpu_ctx.aarch32_ctx.stack_trace_size) {
bt_cur = this->ctx->cpu_ctx.aarch32_ctx.stack_trace[i];
if (i < this->context->cpu_ctx.aarch32_ctx.stack_trace_size) {
bt_cur = this->context->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 + 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->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 < 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 + 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);
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);
}
FontManager::PrintLine("");
FontManager::SetPosition(backtrace_x, FontManager::GetY());
font::PrintLine("");
font::SetPosition(backtrace_x, font::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++) {
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->ctx->cpu_ctx.aarch64_ctx.stack_trace_size) {
bt_cur = this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i];
if (i < this->context->cpu_ctx.aarch64_ctx.stack_trace_size) {
bt_cur = this->context->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 + 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->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 < 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 + 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);
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);
}
FontManager::PrintLine("");
FontManager::SetPosition(backtrace_x, FontManager::GetY());
font::PrintLine("");
font::SetPosition(backtrace_x, font::GetY());
}
}
}
@ -372,20 +414,34 @@ Result ShowFatalTask::ShowFatal() {
framebufferEnd(&fb);
return ResultSuccess;
}
}
Result ShowFatalTask::Run() {
Result ShowFatalTask::Run() {
/* Don't show the fatal error screen until we've verified the battery is okay. */
eventWait(this->battery_event, U64_MAX);
eventWait(const_cast<Event *>(&this->context->battery_event), U64_MAX);
return ShowFatal();
}
}
void BacklightControlTask::TurnOnBacklight() {
void BacklightControlTask::TurnOnBacklight() {
lblSwitchBacklightOn(0);
}
}
Result BacklightControlTask::Run() {
Result BacklightControlTask::Run() {
TurnOnBacklight();
return ResultSuccess;
}
}
ITask *GetShowFatalTask(const ThrowContext *ctx) {
g_show_fatal_task.Initialize(ctx);
return &g_show_fatal_task;
}
ITask *GetBacklightControlTask(const ThrowContext *ctx) {
g_backlight_control_task.Initialize(ctx);
return &g_backlight_control_task;
}
}

View file

@ -15,37 +15,11 @@
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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";
}
};
ITask *GetShowFatalTask(const ThrowContext *ctx);
ITask *GetBacklightControlTask(const ThrowContext *ctx);
}

View file

@ -14,15 +14,34 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#include "fatal_task_sound.hpp"
namespace sts::fatal::srv {
void StopSoundTask::StopSound() {
namespace {
/* Task definition. */
class StopSoundTask : public ITask {
private:
void StopSound();
public:
virtual Result Run() override;
virtual const char *GetName() const override {
return "SoundTask";
}
};
/* Task global. */
StopSoundTask g_stop_sound_task;
/* 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); };
struct {
u16 dev;
u8 val;
@ -46,8 +65,6 @@ void StopSoundTask::StopSound() {
cmd.val = 0;
i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All);
}
i2csessionClose(&audio);
}
}
@ -55,19 +72,28 @@ void StopSoundTask::StopSound() {
{
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);
gpioPadClose(&audio);
}
}
}
}
Result StopSoundTask::Run() {
Result StopSoundTask::Run() {
StopSound();
return ResultSuccess;
}
}
ITask *GetStopSoundTask(const ThrowContext *ctx) {
g_stop_sound_task.Initialize(ctx);
return &g_stop_sound_task;
}
}

View file

@ -15,17 +15,10 @@
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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);
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#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<u32>(Aarch32CpuContext::MaxStackTraceDepth));
} else {
ctx.cpu_ctx.aarch64_ctx.stack_trace_size = std::max(ctx.cpu_ctx.aarch64_ctx.stack_trace_size, static_cast<u32>(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<u64>(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;
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
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>, "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",
};

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <switch.h>
#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<u8> _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<FatalCpuContext *>(_ctx.buffer));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
#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<u8> _ctx);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MAKE_SERVICE_COMMAND_META(UserService, ThrowFatal),
MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithPolicy),
MAKE_SERVICE_COMMAND_META(UserService, ThrowFatalWithCpuContext),
};
};

View file

@ -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"

View file

@ -14,10 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#include "fatal_private.hpp"
#include "fatal_event_manager.hpp"
Result PrivateService::GetFatalEvent(Out<CopiedHandle> out_h) {
return GetEventManager()->GetEvent(out_h.GetHandlePointer());
}
#include "fatal/fatal_types.hpp"

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <switch.h>
#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<RegisterType>(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<RegisterType>(static_cast<u64>(title_id) >> 0);
this->afsr1 = static_cast<RegisterType>(static_cast<u64>(title_id) >> 32);
}
ncm::TitleId GetTitleIdForAtmosphere() const {
return ncm::TitleId{(static_cast<u64>(this->afsr1) << 32ul) | (static_cast<u64>(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<aarch64::CpuContext>::value && sizeof(aarch64::CpuContext) == 0x248, "aarch64::CpuContext definition!");
static_assert(std::is_pod<aarch32::CpuContext>::value && sizeof(aarch32::CpuContext) == 0xE0, "aarch32::CpuContext definition!");
static_assert(std::is_pod<CpuContext>::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));
}
};
}
}

View file

@ -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"

View file

@ -16,18 +16,8 @@
#pragma once
#include <switch.h>
#include <stratosphere.hpp>
class PrivateService final : public IServiceObject {
private:
enum class CommandId {
GetFatalEvent = 0,
};
private:
/* Actual commands. */
Result GetFatalEvent(Out<CopiedHandle> 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);