fatal: refactor for R_TRY

This commit is contained in:
Michael Scire 2019-06-17 16:41:03 -07:00
parent f9bf8923b1
commit 31fde233e1
30 changed files with 226 additions and 285 deletions

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#define AMS_LOGO_WIDTH 0xA0 #define AMS_LOGO_WIDTH 0xA0
#define AMS_LOGO_HEIGHT 0x80 #define AMS_LOGO_HEIGHT 0x80

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_types.hpp" #include "fatal_types.hpp"
#include "fatal_config.hpp" #include "fatal_config.hpp"
@ -38,22 +38,22 @@ IEvent *GetFatalSettingsEvent() {
} }
g_fatal_settings_event = LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) { g_fatal_settings_event = LoadReadOnlySystemEvent(evt.revent, [](u64 timeout) {
u64 flags_0, flags_1; u64 flags_0, flags_1;
if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) { if (R_SUCCEEDED(setsysGetFatalDirtyFlags(&flags_0, &flags_1)) && (flags_0 & 1)) {
UpdateLanguageCode(); UpdateLanguageCode();
} }
return ResultSuccess; return ResultSuccess;
}, true); }, true);
} }
return g_fatal_settings_event; return g_fatal_settings_event;
} }
static void SetupConfigLanguages() { static void SetupConfigLanguages() {
FatalConfig *config = GetFatalConfig(); FatalConfig *config = GetFatalConfig();
/* Defaults. */ /* Defaults. */
config->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n"; config->error_msg = u8"Error Code: 2%03d-%04d (0x%x)\n";
if (config->quest_flag) { if (config->quest_flag) {
config->error_desc = u8"Please call 1-800-875-1852 for service.\n"; config->error_desc = u8"Please call 1-800-875-1852 for service.\n";
} else { } else {
@ -64,7 +64,7 @@ static void SetupConfigLanguages() {
u8"If the problem persists, refer to the Nintendo Support Website.\n" u8"If the problem persists, refer to the Nintendo Support Website.\n"
u8"support.nintendo.com/switch/error\n"; u8"support.nintendo.com/switch/error\n";
} }
/* TODO: Try to load dynamically. */ /* TODO: Try to load dynamically. */
/* FsStorage message_storage; */ /* FsStorage message_storage; */
/* TODO: if (R_SUCCEEDED(fsOpenDataStorageByDataId(0x010000000000081D, "fatal_msg"))) { ... } */ /* TODO: if (R_SUCCEEDED(fsOpenDataStorageByDataId(0x010000000000081D, "fatal_msg"))) { ... } */
@ -72,18 +72,18 @@ static void SetupConfigLanguages() {
void InitializeFatalConfig() { void InitializeFatalConfig() {
FatalConfig *config = GetFatalConfig(); FatalConfig *config = GetFatalConfig();
memset(config, 0, sizeof(*config)); memset(config, 0, sizeof(*config));
setsysGetSerialNumber(config->serial_number); setsysGetSerialNumber(config->serial_number);
setsysGetFirmwareVersion(&config->firmware_version); setsysGetFirmwareVersion(&config->firmware_version);
UpdateLanguageCode(); UpdateLanguageCode();
setsysGetSettingsItemValue("fatal", "transition_to_fatal", &config->transition_to_fatal, sizeof(config->transition_to_fatal)); 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", "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)); setsysGetSettingsItemValue("fatal", "quest_reboot_interval_second", &config->quest_reboot_interval_second, sizeof(config->quest_reboot_interval_second));
setsysGetFlag(SetSysFlag_Quest, &config->quest_flag); 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 = 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); config->is_auto_reboot_enabled &= (config->fatal_auto_reboot_interval != 0);

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -21,20 +21,20 @@
static bool IsAddressReadable(Handle debug_handle, u64 address, u64 size, MemoryInfo *o_mi) { static bool IsAddressReadable(Handle debug_handle, u64 address, u64 size, MemoryInfo *o_mi) {
MemoryInfo mi; MemoryInfo mi;
u32 pi; u32 pi;
if (o_mi == NULL) { if (o_mi == NULL) {
o_mi = &mi; o_mi = &mi;
} }
if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, debug_handle, address))) { if (R_FAILED(svcQueryDebugProcessMemory(o_mi, &pi, debug_handle, address))) {
return false; return false;
} }
/* Must be readable */ /* Must be readable */
if ((o_mi->perm & Perm_R) != Perm_R) { if ((o_mi->perm & Perm_R) != Perm_R) {
return false; return false;
} }
/* Must have space for both userdata address and userdata size. */ /* Must have space for both userdata address and userdata size. */
if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) { if (address < o_mi->addr || o_mi->addr + o_mi->size < address + size) {
return false; return false;
@ -51,83 +51,83 @@ static bool CheckThreadIsFatalCaller(FatalThrowContext *ctx, u64 debug_handle, u
if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, thread_id, DebugThreadParam_State))) { if (R_FAILED(svcGetDebugThreadParam(&_, &thread_state, debug_handle, thread_id, DebugThreadParam_State))) {
return false; return false;
} }
if (thread_state > 1) { if (thread_state > 1) {
return false; return false;
} }
} }
/* Get the thread context. */ /* Get the thread context. */
if (R_FAILED(svcGetDebugThreadContext(thread_ctx, debug_handle, thread_id, 0xF))) { if (R_FAILED(svcGetDebugThreadContext(thread_ctx, debug_handle, thread_id, 0xF))) {
return false; return false;
} }
/* Check if PC is readable. */ /* Check if PC is readable. */
if (!IsAddressReadable(debug_handle, thread_ctx->pc.x, sizeof(u32), NULL)) { if (!IsAddressReadable(debug_handle, thread_ctx->pc.x, sizeof(u32), NULL)) {
return false; return false;
} }
/* Try to read the current instruction. */ /* Try to read the current instruction. */
u32 insn; u32 insn;
if (R_FAILED(svcReadDebugProcessMemory(&insn, debug_handle, thread_ctx->pc.x, sizeof(insn)))) { if (R_FAILED(svcReadDebugProcessMemory(&insn, debug_handle, thread_ctx->pc.x, sizeof(insn)))) {
return false; return false;
} }
/* If the instruction isn't svcSendSyncRequest, it's not the fatal caller. */ /* If the instruction isn't svcSendSyncRequest, it's not the fatal caller. */
if (insn != 0xD4000421) { if (insn != 0xD4000421) {
return false; return false;
} }
/* The fatal caller will have readable tls. */ /* The fatal caller will have readable tls. */
if (!IsAddressReadable(debug_handle, thread_tls_addr, 0x100, NULL)) { if (!IsAddressReadable(debug_handle, thread_tls_addr, 0x100, NULL)) {
return false; return false;
} }
/* Read in the fatal caller's tls. */ /* Read in the fatal caller's tls. */
u8 thread_tls[0x100]; u8 thread_tls[0x100];
if (R_FAILED(svcReadDebugProcessMemory(thread_tls, debug_handle, thread_tls_addr, sizeof(thread_tls)))) { if (R_FAILED(svcReadDebugProcessMemory(thread_tls, debug_handle, thread_tls_addr, sizeof(thread_tls)))) {
return false; return false;
} }
/* Replace our tls with the fatal caller's. */ /* Replace our tls with the fatal caller's. */
std::memcpy(armGetTls(), thread_tls, sizeof(thread_tls)); std::memcpy(armGetTls(), thread_tls, sizeof(thread_tls));
/* Parse the command that the thread sent. */ /* Parse the command that the thread sent. */
{ {
IpcParsedCommand r; IpcParsedCommand r;
if (R_FAILED(ipcParse(&r))) { if (R_FAILED(ipcParse(&r))) {
return false; return false;
} }
/* Fatal command takes in a PID, only one buffer max. */ /* Fatal command takes in a PID, only one buffer max. */
if (!r.HasPid || r.NumStatics || r.NumStaticsOut || r.NumHandles) { if (!r.HasPid || r.NumStatics || r.NumStaticsOut || r.NumHandles) {
return false; return false;
} }
struct { struct {
u32 magic; u32 magic;
u32 version; u32 version;
u64 cmd_id; u64 cmd_id;
u32 err_code; u32 err_code;
} *raw = (decltype(raw))(r.Raw); } *raw = (decltype(raw))(r.Raw);
if (raw->magic != SFCI_MAGIC) { if (raw->magic != SFCI_MAGIC) {
return false; return false;
} }
if (raw->cmd_id > 2) { if (raw->cmd_id > 2) {
return false; return false;
} }
if (raw->cmd_id != 2 && r.NumBuffers) { if (raw->cmd_id != 2 && r.NumBuffers) {
return false; return false;
} }
if (raw->err_code != ctx->error_code) { if (raw->err_code != ctx->error_code) {
return false; return false;
} }
} }
/* We found our caller. */ /* We found our caller. */
return true; return true;
} }
@ -137,7 +137,7 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
if (R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid))) { if (R_SUCCEEDED(svcDebugActiveProcess(&debug_handle, pid))) {
/* Ensure we close the debugged process. */ /* Ensure we close the debugged process. */
ON_SCOPE_EXIT { svcCloseHandle(debug_handle); }; ON_SCOPE_EXIT { svcCloseHandle(debug_handle); };
/* First things first, check if process is 64 bits, and get list of thread infos. */ /* First things first, check if process is 64 bits, and get list of thread infos. */
std::unordered_map<u64, u64> thread_id_to_tls; std::unordered_map<u64, u64> thread_id_to_tls;
{ {
@ -152,17 +152,17 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
thread_id_to_tls[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address; thread_id_to_tls[d.info.attach_thread.thread_id] = d.info.attach_thread.tls_address;
} }
} }
if (!got_attach_process) { if (!got_attach_process) {
return; return;
} }
} }
/* TODO: Try to collect information on 32-bit fatals. This shouldn't really matter for any real use case. */ /* 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) { if (ctx->cpu_ctx.is_aarch32) {
return; return;
} }
/* Welcome to hell. */ /* Welcome to hell. */
bool found_fatal_caller = false; bool found_fatal_caller = false;
u64 thread_id = 0; u64 thread_id = 0;
@ -174,14 +174,14 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle))) { if (R_FAILED(svcGetThreadList(&thread_count, thread_ids, 0x60, debug_handle))) {
return; return;
} }
/* We need to locate the thread that's called fatal. */ /* We need to locate the thread that's called fatal. */
for (u32 i = 0; i < thread_count; i++) { for (u32 i = 0; i < thread_count; i++) {
const u64 cur_thread_id = thread_ids[i]; const u64 cur_thread_id = thread_ids[i];
if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) { if (thread_id_to_tls.find(cur_thread_id) == thread_id_to_tls.end()) {
continue; continue;
} }
if (CheckThreadIsFatalCaller(ctx, debug_handle, cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) { if (CheckThreadIsFatalCaller(ctx, debug_handle, cur_thread_id, thread_id_to_tls[cur_thread_id], &thread_ctx)) {
thread_id = cur_thread_id; thread_id = cur_thread_id;
found_fatal_caller = true; found_fatal_caller = true;
@ -195,7 +195,7 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
if (R_FAILED(svcGetDebugThreadContext(&thread_ctx, debug_handle, thread_id, 0xF))) { if (R_FAILED(svcGetDebugThreadContext(&thread_ctx, debug_handle, thread_id, 0xF))) {
return; return;
} }
/* So we found our caller. */ /* So we found our caller. */
for (u32 i = 0; i < 29; i++) { for (u32 i = 0; i < 29; i++) {
/* GetDebugThreadContext won't give us any of these registers, because thread is in SVC :( */ /* GetDebugThreadContext won't give us any of these registers, because thread is in SVC :( */
@ -208,7 +208,7 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
ctx->cpu_ctx.aarch64_ctx.lr = thread_ctx.lr; ctx->cpu_ctx.aarch64_ctx.lr = thread_ctx.lr;
ctx->cpu_ctx.aarch64_ctx.sp = thread_ctx.sp; ctx->cpu_ctx.aarch64_ctx.sp = thread_ctx.sp;
ctx->cpu_ctx.aarch64_ctx.pc = thread_ctx.pc.x; ctx->cpu_ctx.aarch64_ctx.pc = thread_ctx.pc.x;
/* Parse a stack trace. */ /* Parse a stack trace. */
u64 cur_fp = thread_ctx.fp; u64 cur_fp = thread_ctx.fp;
@ -217,18 +217,18 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
if (cur_fp == 0 || (cur_fp & 0xF)) { if (cur_fp == 0 || (cur_fp & 0xF)) {
break; break;
} }
/* Read a new frame. */ /* Read a new frame. */
StackFrame cur_frame; StackFrame cur_frame;
if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) { if (R_FAILED(svcReadDebugProcessMemory(&cur_frame, debug_handle, cur_fp, sizeof(StackFrame)))) {
break; break;
} }
/* Advance to the next frame. */ /* Advance to the next frame. */
ctx->cpu_ctx.aarch64_ctx.stack_trace[ctx->cpu_ctx.aarch64_ctx.stack_trace_size++] = cur_frame.lr; ctx->cpu_ctx.aarch64_ctx.stack_trace[ctx->cpu_ctx.aarch64_ctx.stack_trace_size++] = cur_frame.lr;
cur_fp = cur_frame.fp; cur_fp = cur_frame.fp;
} }
/* Try to read up to 0x100 of stack. */ /* Try to read up to 0x100 of stack. */
for (size_t sz = 0x100; sz > 0; sz -= 0x10) { for (size_t sz = 0x100; sz > 0; sz -= 0x10) {
if (IsAddressReadable(debug_handle, ctx->cpu_ctx.aarch64_ctx.sp, sz, nullptr)) { if (IsAddressReadable(debug_handle, ctx->cpu_ctx.aarch64_ctx.sp, sz, nullptr)) {
@ -246,25 +246,25 @@ void TryCollectDebugInformation(FatalThrowContext *ctx, u64 pid) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess)) || mi.perm != Perm_Rx) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess)) || mi.perm != Perm_Rx) {
return false; return false;
} }
/* Iterate backwards until we find the memory before the code region. */ /* Iterate backwards until we find the memory before the code region. */
while (mi.addr > 0) { while (mi.addr > 0) {
if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) { if (R_FAILED(svcQueryDebugProcessMemory(&mi, &pi, debug_handle, guess))) {
return false; return false;
} }
if (mi.type == MemType_Unmapped) { if (mi.type == MemType_Unmapped) {
/* Code region will be at the end of the unmapped region preceding it. */ /* Code region will be at the end of the unmapped region preceding it. */
ctx->cpu_ctx.aarch64_ctx.start_address = mi.addr + mi.size; ctx->cpu_ctx.aarch64_ctx.start_address = mi.addr + mi.size;
return true; return true;
} }
guess -= 4; guess -= 4;
} }
return false; return false;
}; };
/* Parse the starting address. */ /* Parse the starting address. */
{ {
if (TryGuessStartAddress(thread_ctx.pc.x)) { if (TryGuessStartAddress(thread_ctx.pc.x)) {

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_types.hpp" #include "fatal_types.hpp"
#include "fatal_event_manager.hpp" #include "fatal_event_manager.hpp"
@ -35,12 +35,12 @@ FatalEventManager::FatalEventManager() {
Result FatalEventManager::GetEvent(Handle *out) { Result FatalEventManager::GetEvent(Handle *out) {
std::scoped_lock<HosMutex> lk{this->lock}; std::scoped_lock<HosMutex> lk{this->lock};
/* Only allow GetEvent to succeed NumFatalEvents times. */ /* Only allow GetEvent to succeed NumFatalEvents times. */
if (this->events_gotten >= FatalEventManager::NumFatalEvents) { if (this->events_gotten >= FatalEventManager::NumFatalEvents) {
return ResultFatalTooManyEvents; return ResultFatalTooManyEvents;
} }
*out = this->events[this->events_gotten++].revent; *out = this->events[this->events_gotten++].revent;
return ResultSuccess; return ResultSuccess;
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>
@ -21,7 +21,7 @@
class FatalEventManager { class FatalEventManager {
private: private:
static constexpr size_t NumFatalEvents = 3; static constexpr size_t NumFatalEvents = 3;
HosMutex lock; HosMutex lock;
size_t events_gotten = 0; size_t events_gotten = 0;
Event events[NumFatalEvents]; Event events[NumFatalEvents];

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_types.hpp" #include "fatal_types.hpp"
@ -49,11 +49,11 @@ static u16 Blend(u16 color, u16 bg, u8 alpha) {
const u32 b_r = RGB565_GET_R8(bg); const u32 b_r = RGB565_GET_R8(bg);
const u32 b_g = RGB565_GET_G8(bg); const u32 b_g = RGB565_GET_G8(bg);
const u32 b_b = RGB565_GET_B8(bg); const u32 b_b = RGB565_GET_B8(bg);
const u32 r = ((alpha * c_r) + ((0xFF - alpha) * b_r)) / 0xFF; const u32 r = ((alpha * c_r) + ((0xFF - alpha) * b_r)) / 0xFF;
const u32 g = ((alpha * c_g) + ((0xFF - alpha) * b_g)) / 0xFF; const u32 g = ((alpha * c_g) + ((0xFF - alpha) * b_g)) / 0xFF;
const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF; const u32 b = ((alpha * c_b) + ((0xFF - alpha) * b_b)) / 0xFF;
return RGB888_TO_RGB565(r, g, b); return RGB888_TO_RGB565(r, g, b);
} }
@ -61,7 +61,7 @@ static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
u8* imageptr = bitmap->buffer; u8* imageptr = bitmap->buffer;
if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return; if (bitmap->pixel_mode!=FT_PIXEL_MODE_GRAY) return;
for (u32 tmpy = 0; tmpy < bitmap->rows; tmpy++) { for (u32 tmpy = 0; tmpy < bitmap->rows; tmpy++) {
for (u32 tmpx = 0; tmpx < bitmap->width; tmpx++) { for (u32 tmpx = 0; tmpx < bitmap->width; tmpx++) {
/* Implement very simple blending, as the bitmap value is an alpha value. */ /* Implement very simple blending, as the bitmap value is an alpha value. */
@ -75,47 +75,47 @@ static void DrawGlyph(FT_Bitmap *bitmap, u32 x, u32 y) {
static void DrawString(const char *str, bool add_line, bool mono = false) { static void DrawString(const char *str, bool add_line, bool mono = false) {
FT_UInt glyph_index; FT_UInt glyph_index;
FT_GlyphSlot slot = g_face->glyph; FT_GlyphSlot slot = g_face->glyph;
const size_t len = strlen(str); const size_t len = strlen(str);
u32 cur_x = g_cur_x, cur_y = g_cur_y; u32 cur_x = g_cur_x, cur_y = g_cur_y;
ON_SCOPE_EXIT { ON_SCOPE_EXIT {
if (add_line) { if (add_line) {
/* Advance to next line. */ /* Advance to next line. */
g_cur_x = g_line_x; g_cur_x = g_line_x;
g_cur_y = cur_y + (g_face->size->metrics.height >> 6); g_cur_y = cur_y + (g_face->size->metrics.height >> 6);
} else { } else {
g_cur_x = cur_x; g_cur_x = cur_x;
g_cur_y = cur_y; g_cur_y = cur_y;
} }
}; };
for (u32 i = 0; i < len; ) { for (u32 i = 0; i < len; ) {
u32 cur_char; u32 cur_char;
ssize_t unit_count = decode_utf8(&cur_char, reinterpret_cast<const u8 *>(&str[i])); ssize_t unit_count = decode_utf8(&cur_char, reinterpret_cast<const u8 *>(&str[i]));
if (unit_count <= 0) break; if (unit_count <= 0) break;
i += unit_count; i += unit_count;
if (cur_char == '\n') { if (cur_char == '\n') {
cur_x = g_line_x; cur_x = g_line_x;
cur_y += g_face->size->metrics.height >> 6; cur_y += g_face->size->metrics.height >> 6;
continue; continue;
} }
glyph_index = FT_Get_Char_Index(g_face, cur_char); glyph_index = FT_Get_Char_Index(g_face, cur_char);
g_ft_err = FT_Load_Glyph(g_face, glyph_index, FT_LOAD_DEFAULT); g_ft_err = FT_Load_Glyph(g_face, glyph_index, FT_LOAD_DEFAULT);
if (g_ft_err == 0) { if (g_ft_err == 0) {
g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL); g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL);
} }
if (g_ft_err) { if (g_ft_err) {
return; return;
} }
DrawGlyph(&slot->bitmap, cur_x + slot->bitmap_left + ((mono && g_mono_adv > slot->advance.x) ? ((g_mono_adv - slot->advance.x) >> 7) : 0), cur_y - slot->bitmap_top); DrawGlyph(&slot->bitmap, cur_x + slot->bitmap_left + ((mono && g_mono_adv > slot->advance.x) ? ((g_mono_adv - slot->advance.x) >> 7) : 0), cur_y - slot->bitmap_top);
cur_x += (mono ? g_mono_adv : slot->advance.x) >> 6; cur_x += (mono ? g_mono_adv : slot->advance.x) >> 6;
cur_y += slot->advance.y >> 6; cur_y += slot->advance.y >> 6;
} }
@ -151,17 +151,17 @@ void FontManager::PrintFormat(const char *format, ...) {
Print(char_buf); Print(char_buf);
} }
void FontManager::PrintMonospaceU64(u64 x) { void FontManager::PrintMonospaceU64(u64 x) {
char char_buf[0x400]; char char_buf[0x400];
snprintf(char_buf, sizeof(char_buf), "%016lX", x); snprintf(char_buf, sizeof(char_buf), "%016lX", x);
DrawString(char_buf, false, true); DrawString(char_buf, false, true);
} }
void FontManager::PrintMonospaceU32(u32 x) { void FontManager::PrintMonospaceU32(u32 x) {
char char_buf[0x400]; char char_buf[0x400];
snprintf(char_buf, sizeof(char_buf), "%08X", x); snprintf(char_buf, sizeof(char_buf), "%08X", x);
DrawString(char_buf, false, true); DrawString(char_buf, false, true);
} }
@ -170,7 +170,7 @@ void FontManager::PrintMonospaceBlank(u32 width) {
for (size_t i = 0; i < width && i < sizeof(char_buf); i++) { for (size_t i = 0; i < width && i < sizeof(char_buf); i++) {
char_buf[i] = ' '; char_buf[i] = ' ';
} }
DrawString(char_buf, false, true); DrawString(char_buf, false, true);
} }
@ -195,9 +195,9 @@ u32 FontManager::GetY() {
void FontManager::SetFontSize(float fsz) { void FontManager::SetFontSize(float fsz) {
g_font_sz = 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); g_ft_err = FT_Set_Char_Size(g_face, 0, static_cast<u32>(g_font_sz * 64.0f), 96, 96);
g_ft_err = FT_Load_Glyph(g_face, FT_Get_Char_Index(g_face, 'A'), FT_LOAD_DEFAULT); g_ft_err = FT_Load_Glyph(g_face, FT_Get_Char_Index(g_face, 'A'), FT_LOAD_DEFAULT);
if (g_ft_err == 0) { if (g_ft_err == 0) {
g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL); g_ft_err = FT_Render_Glyph(g_face->glyph, FT_RENDER_MODE_NORMAL);
} }
@ -217,23 +217,17 @@ void FontManager::ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u
} }
Result FontManager::InitializeSharedFont() { Result FontManager::InitializeSharedFont() {
Result rc;
size_t total_fonts = 0; size_t total_fonts = 0;
if (R_FAILED((rc = plGetSharedFont(GetFatalConfig()->language_code, g_fonts, PlSharedFontType_Total, &total_fonts)))) {
return rc;
}
if (R_FAILED((rc = plGetSharedFontByType(&g_font, PlSharedFontType_Standard)))) { R_TRY(plGetSharedFont(GetFatalConfig()->language_code, g_fonts, PlSharedFontType_Total, &total_fonts));
return rc; R_TRY(plGetSharedFontByType(&g_font, PlSharedFontType_Standard));
}
g_ft_err = FT_Init_FreeType(&g_library); g_ft_err = FT_Init_FreeType(&g_library);
if (g_ft_err) return g_ft_err; if (g_ft_err) return g_ft_err;
g_ft_err = FT_New_Memory_Face(g_library, reinterpret_cast<const FT_Byte *>(g_font.address), g_font.size, 0, &g_face); 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; if (g_ft_err) return g_ft_err;
SetFontSize(g_font_sz); SetFontSize(g_font_sz);
return g_ft_err; return g_ft_err;
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <cstdarg> #include <cstdarg>
#include <switch.h> #include <switch.h>
@ -24,7 +24,7 @@
#define RGB565_GET_G8(c) ((((c >> 5) & 0x3F) << 2) | ((c >> 9) & 3)) #define RGB565_GET_G8(c) ((((c >> 5) & 0x3F) << 2) | ((c >> 9) & 3))
#define RGB565_GET_B8(c) ((((c >> 0) & 0x1F) << 3) | ((c >> 2) & 7)) #define RGB565_GET_B8(c) ((((c >> 0) & 0x1F) << 3) | ((c >> 2) & 7))
class FontManager { class FontManager {
public: public:
static Result InitializeSharedFont(); static Result InitializeSharedFont();
static void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32)); static void ConfigureFontFramebuffer(u16 *fb, u32 (*unswizzle_func)(u32, u32));

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_private.hpp" #include "fatal_private.hpp"
#include "fatal_event_manager.hpp" #include "fatal_event_manager.hpp"

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "fatal_types.hpp" #include "fatal_types.hpp"
@ -24,21 +24,21 @@ static bool InRepairWithoutVolHeld() {
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) { if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
return false; return false;
} }
bool in_repair; bool in_repair;
if (R_FAILED(setsysGetFlag(SetSysFlag_InRepairProcessEnable, &in_repair)) || !in_repair) { if (R_FAILED(setsysGetFlag(SetSysFlag_InRepairProcessEnable, &in_repair)) || !in_repair) {
return false; return false;
} }
{ {
GpioPadSession vol_btn; GpioPadSession vol_btn;
if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) { if (R_FAILED(gpioOpenSession(&vol_btn, GpioPadName_ButtonVolUp))) {
return true; return true;
} }
/* Ensure we close even on early return. */ /* Ensure we close even on early return. */
ON_SCOPE_EXIT { gpioPadClose(&vol_btn); }; ON_SCOPE_EXIT { gpioPadClose(&vol_btn); };
/* Set direction input. */ /* Set direction input. */
gpioPadSetDirection(&vol_btn, GpioDirection_Input); gpioPadSetDirection(&vol_btn, GpioDirection_Input);
@ -49,12 +49,12 @@ static bool InRepairWithoutVolHeld() {
if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) { if (R_FAILED(gpioPadGetValue(&vol_btn, &val)) || val != GpioValue_Low) {
return true; return true;
} }
/* Sleep for 100 ms. */ /* Sleep for 100 ms. */
svcSleepThread(100000000UL); svcSleepThread(100000000UL);
} }
} }
return false; return false;
} }
@ -62,12 +62,12 @@ static bool InRepairWithoutTimeReviserCartridge() {
if (GetRuntimeFirmwareVersion() < FirmwareVersion_500) { if (GetRuntimeFirmwareVersion() < FirmwareVersion_500) {
return false; return false;
} }
bool requires_time_reviser; bool requires_time_reviser;
if (R_FAILED(setsysGetFlag(SetSysFlag_RequiresRunRepairTimeReviser, &requires_time_reviser)) || !requires_time_reviser) { if (R_FAILED(setsysGetFlag(SetSysFlag_RequiresRunRepairTimeReviser, &requires_time_reviser)) || !requires_time_reviser) {
return false; return false;
} }
FsGameCardHandle gc_hnd; FsGameCardHandle gc_hnd;
u8 gc_attr; u8 gc_attr;
{ {
@ -75,22 +75,22 @@ static bool InRepairWithoutTimeReviserCartridge() {
if (R_FAILED(fsOpenDeviceOperator(&devop))) { if (R_FAILED(fsOpenDeviceOperator(&devop))) {
return true; return true;
} }
/* Ensure we close even on early return. */ /* Ensure we close even on early return. */
ON_SCOPE_EXIT { fsDeviceOperatorClose(&devop); }; ON_SCOPE_EXIT { fsDeviceOperatorClose(&devop); };
/* Check that a gamecard is inserted. */ /* Check that a gamecard is inserted. */
bool inserted; bool inserted;
if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&devop, &inserted)) || !inserted) { if (R_FAILED(fsDeviceOperatorIsGameCardInserted(&devop, &inserted)) || !inserted) {
return true; return true;
} }
/* Check that we can retrieve the gamecard's attributes. */ /* Check that we can retrieve the gamecard's attributes. */
if (R_FAILED(fsDeviceOperatorGetGameCardHandle(&devop, &gc_hnd)) || R_FAILED(fsDeviceOperatorGetGameCardAttribute(&devop, &gc_hnd, &gc_attr))) { if (R_FAILED(fsDeviceOperatorGetGameCardHandle(&devop, &gc_hnd)) || R_FAILED(fsDeviceOperatorGetGameCardAttribute(&devop, &gc_hnd, &gc_attr))) {
return true; return true;
} }
} }
/* Check that the gamecard is a repair tool. */ /* Check that the gamecard is a repair tool. */
return (gc_attr & FsGameCardAttribute_Repair) == FsGameCardAttribute_Repair; return (gc_attr & FsGameCardAttribute_Repair) == FsGameCardAttribute_Repair;
} }
@ -99,7 +99,7 @@ void CheckRepairStatus() {
if (InRepairWithoutVolHeld()) { if (InRepairWithoutVolHeld()) {
ThrowFatalForSelf(ResultFatalInRepairWithoutVolHeld); ThrowFatalForSelf(ResultFatalInRepairWithoutVolHeld);
} }
if (InRepairWithoutTimeReviserCartridge()) { if (InRepairWithoutTimeReviserCartridge()) {
ThrowFatalForSelf(ResultFatalInRepairWithoutTimeReviserCartridge); ThrowFatalForSelf(ResultFatalInRepairWithoutTimeReviserCartridge);
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_types.hpp" #include "fatal_types.hpp"
#include "fatal_task.hpp" #include "fatal_task.hpp"
@ -32,22 +32,22 @@ static size_t g_num_threads = 0;
static void RunTaskThreadFunc(void *arg) { static void RunTaskThreadFunc(void *arg) {
IFatalTask *task = reinterpret_cast<IFatalTask *>(arg); IFatalTask *task = reinterpret_cast<IFatalTask *>(arg);
Result rc = task->Run(); Result rc = task->Run();
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
/* TODO: Log task failure, somehow? */ /* TODO: Log task failure, somehow? */
} }
/* Finish. */ /* Finish. */
} }
static void RunTask(IFatalTask *task, u32 stack_size = 0x4000) { static void RunTask(IFatalTask *task, u32 stack_size = 0x4000) {
if (g_num_threads >= MaxTasks) { if (g_num_threads >= MaxTasks) {
std::abort(); std::abort();
} }
HosThread *cur_thread = &g_task_threads[g_num_threads++]; HosThread *cur_thread = &g_task_threads[g_num_threads++];
cur_thread->Initialize(&RunTaskThreadFunc, task, stack_size, 15); cur_thread->Initialize(&RunTaskThreadFunc, task, stack_size, 15);
cur_thread->Start(); cur_thread->Start();
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,34 +13,27 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_task_clock.hpp" #include "fatal_task_clock.hpp"
Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) { Result AdjustClockTask::AdjustClockForModule(PcvModule module, u32 hz) {
Result rc;
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) { if (GetRuntimeFirmwareVersion() >= FirmwareVersion_800) {
/* On 8.0.0+, convert to module id + use clkrst API. */ /* On 8.0.0+, convert to module id + use clkrst API. */
PcvModuleId module_id; PcvModuleId module_id;
if (R_FAILED((rc = pcvGetModuleId(&module_id, module)))) { R_TRY(pcvGetModuleId(&module_id, module));
return rc;
}
ClkrstSession session; ClkrstSession session;
Result rc = clkrstOpenSession(&session, module_id, 3); R_TRY(clkrstOpenSession(&session, module_id, 3));
if (R_FAILED(rc)) {
return rc;
}
ON_SCOPE_EXIT { clkrstCloseSession(&session); }; ON_SCOPE_EXIT { clkrstCloseSession(&session); };
rc = clkrstSetClockRate(&session, hz); R_TRY(clkrstSetClockRate(&session, hz));
} else { } else {
/* On 1.0.0-7.0.1, use pcv API. */ /* On 1.0.0-7.0.1, use pcv API. */
rc = pcvSetClockRate(module, hz); R_TRY(pcvSetClockRate(module, hz));
} }
return rc; return ResultSuccess;
} }
Result AdjustClockTask::AdjustClock() { Result AdjustClockTask::AdjustClock() {
@ -48,23 +41,14 @@ Result AdjustClockTask::AdjustClock() {
constexpr u32 CPU_CLOCK_1020MHZ = 0x3CCBF700L; constexpr u32 CPU_CLOCK_1020MHZ = 0x3CCBF700L;
constexpr u32 GPU_CLOCK_307MHZ = 0x124F8000L; constexpr u32 GPU_CLOCK_307MHZ = 0x124F8000L;
constexpr u32 EMC_CLOCK_1331MHZ = 0x4F588000L; constexpr u32 EMC_CLOCK_1331MHZ = 0x4F588000L;
Result rc = ResultSuccess;
if (R_FAILED((rc = AdjustClockForModule(PcvModule_CpuBus, CPU_CLOCK_1020MHZ)))) { R_TRY(AdjustClockForModule(PcvModule_CpuBus, CPU_CLOCK_1020MHZ));
return rc; R_TRY(AdjustClockForModule(PcvModule_GPU, GPU_CLOCK_307MHZ));
} R_TRY(AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ));
if (R_FAILED((rc = AdjustClockForModule(PcvModule_GPU, GPU_CLOCK_307MHZ)))) { return ResultSuccess;
return rc;
}
if (R_FAILED((rc = AdjustClockForModule(PcvModule_EMC, EMC_CLOCK_1331MHZ)))) {
return rc;
}
return rc;
} }
Result AdjustClockTask::Run() { Result AdjustClockTask::Run() {
return AdjustClock(); return AdjustClock();
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstdio> #include <cstdio>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@ -24,7 +24,7 @@
#include "fatal_config.hpp" #include "fatal_config.hpp"
void ErrorReportTask::EnsureReportDirectories() { void ErrorReportTask::EnsureReportDirectories() {
char path[FS_MAX_PATH]; char path[FS_MAX_PATH];
strcpy(path, "sdmc:/atmosphere"); strcpy(path, "sdmc:/atmosphere");
mkdir(path, S_IRWXU); mkdir(path, S_IRWXU);
strcat(path, "/fatal_reports"); strcat(path, "/fatal_reports");
@ -35,7 +35,7 @@ void ErrorReportTask::EnsureReportDirectories() {
bool ErrorReportTask::GetCurrentTime(u64 *out) { bool ErrorReportTask::GetCurrentTime(u64 *out) {
*out = 0; *out = 0;
/* Verify that pcv isn't dead. */ /* Verify that pcv isn't dead. */
{ {
bool has_time_service; bool has_time_service;
@ -52,7 +52,7 @@ bool ErrorReportTask::GetCurrentTime(u64 *out) {
return false; return false;
} }
} }
/* Try to get the current time. */ /* Try to get the current time. */
bool success = true; bool success = true;
DoWithSmSession([&]() { DoWithSmSession([&]() {
@ -67,22 +67,22 @@ bool ErrorReportTask::GetCurrentTime(u64 *out) {
void ErrorReportTask::SaveReportToSdCard() { void ErrorReportTask::SaveReportToSdCard() {
char file_path[FS_MAX_PATH]; char file_path[FS_MAX_PATH];
/* Ensure path exists. */ /* Ensure path exists. */
EnsureReportDirectories(); EnsureReportDirectories();
/* Get a timestamp. */ /* Get a timestamp. */
u64 timestamp; u64 timestamp;
if (!GetCurrentTime(&timestamp)) { if (!GetCurrentTime(&timestamp)) {
timestamp = svcGetSystemTick(); timestamp = svcGetSystemTick();
} }
/* Open report file. */ /* 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, this->title_id);
FILE *f_report = fopen(file_path, "w"); FILE *f_report = fopen(file_path, "w");
if (f_report != NULL) { if (f_report != NULL) {
ON_SCOPE_EXIT { fclose(f_report); }; ON_SCOPE_EXIT { fclose(f_report); };
fprintf(f_report, "Atmosphère Fatal Report (v1.0):\n"); 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, "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); fprintf(f_report, "Title ID: %016lx\n", this->title_id);
@ -90,7 +90,7 @@ void ErrorReportTask::SaveReportToSdCard() {
fprintf(f_report, "Process Name: %s\n", this->ctx->proc_name); fprintf(f_report, "Process Name: %s\n", this->ctx->proc_name);
} }
fprintf(f_report, u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision()); fprintf(f_report, u8"Firmware: %s (Atmosphère %u.%u.%u-%s)\n", GetFatalConfig()->firmware_version.display_version, CURRENT_ATMOSPHERE_VERSION, GetAtmosphereGitRevision());
if (this->ctx->cpu_ctx.is_aarch32) { if (this->ctx->cpu_ctx.is_aarch32) {
fprintf(f_report, "General Purpose Registers:\n"); fprintf(f_report, "General Purpose Registers:\n");
for (size_t i = 0; i < NumAarch32Gprs; i++) { for (size_t i = 0; i < NumAarch32Gprs; i++) {
@ -118,15 +118,15 @@ void ErrorReportTask::SaveReportToSdCard() {
fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i]); fprintf(f_report, " ReturnAddress[%02u]: %016lx\n", i, this->ctx->cpu_ctx.aarch64_ctx.stack_trace[i]);
} }
} }
} }
if (this->ctx->stack_dump_size) { 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); snprintf(file_path, sizeof(file_path) - 1, "sdmc:/atmosphere/fatal_reports/dumps/%011lu_%016lx.bin", timestamp, this->title_id);
FILE *f_stackdump = fopen(file_path, "wb"); FILE *f_stackdump = fopen(file_path, "wb");
if (f_stackdump == NULL) { return; } if (f_stackdump == NULL) { return; }
ON_SCOPE_EXIT { fclose(f_stackdump); }; ON_SCOPE_EXIT { fclose(f_stackdump); };
fwrite(this->ctx->stack_dump, this->ctx->stack_dump_size, 1, f_stackdump); fwrite(this->ctx->stack_dump, this->ctx->stack_dump_size, 1, f_stackdump);
} }
} }
@ -135,15 +135,15 @@ Result ErrorReportTask::Run() {
if (this->create_report) { if (this->create_report) {
/* Here, Nintendo creates an error report with erpt. AMS will not do that. */ /* Here, Nintendo creates an error report with erpt. AMS will not do that. */
} }
/* Save report to SD card. */ /* Save report to SD card. */
if (!this->ctx->is_creport) { if (!this->ctx->is_creport) {
SaveReportToSdCard(); SaveReportToSdCard();
} }
/* Signal we're done with our job. */ /* Signal we're done with our job. */
eventFire(this->erpt_event); eventFire(this->erpt_event);
return ResultSuccess; return ResultSuccess;
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,35 +13,35 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_task_power.hpp" #include "fatal_task_power.hpp"
#include "fatal_config.hpp" #include "fatal_config.hpp"
bool PowerControlTask::TryShutdown() { bool PowerControlTask::TryShutdown() {
/* Set a timeout of 30 seconds. */ /* Set a timeout of 30 seconds. */
TimeoutHelper timeout_helper(30000000000UL); TimeoutHelper timeout_helper(30000000000UL);
bool cancel_shutdown = false; bool cancel_shutdown = false;
PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal;
while (true) { while (true) {
if (timeout_helper.TimedOut()) { if (timeout_helper.TimedOut()) {
break; break;
} }
if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) {
break; break;
} }
if (bv_state == PsmBatteryVoltageState_Normal) { if (bv_state == PsmBatteryVoltageState_Normal) {
cancel_shutdown = true; cancel_shutdown = true;
break; break;
} }
/* Query voltage state every 5 seconds, for 30 seconds. */ /* Query voltage state every 5 seconds, for 30 seconds. */
svcSleepThread(5000000000UL); svcSleepThread(5000000000UL);
} }
if (!cancel_shutdown) { if (!cancel_shutdown) {
bpcShutdownSystem(); bpcShutdownSystem();
return true; return true;
@ -52,7 +52,7 @@ bool PowerControlTask::TryShutdown() {
void PowerControlTask::MonitorBatteryState() { void PowerControlTask::MonitorBatteryState() {
PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal; PsmBatteryVoltageState bv_state = PsmBatteryVoltageState_Normal;
/* Check the battery state, and shutdown on low voltage. */ /* Check the battery state, and shutdown on low voltage. */
if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) { if (R_FAILED(psmGetBatteryVoltageState(&bv_state)) || bv_state == PsmBatteryVoltageState_NeedsShutdown) {
/* Wait a second for the error report task to finish. */ /* Wait a second for the error report task to finish. */
@ -60,15 +60,15 @@ void PowerControlTask::MonitorBatteryState() {
this->TryShutdown(); this->TryShutdown();
return; return;
} }
/* Signal we've checked the battery at least once. */ /* Signal we've checked the battery at least once. */
eventFire(this->battery_event); eventFire(this->battery_event);
while (true) { while (true) {
if (R_FAILED(psmGetBatteryVoltageState(&bv_state))) { if (R_FAILED(psmGetBatteryVoltageState(&bv_state))) {
bv_state = PsmBatteryVoltageState_NeedsShutdown; bv_state = PsmBatteryVoltageState_NeedsShutdown;
} }
switch (bv_state) { switch (bv_state) {
case PsmBatteryVoltageState_NeedsShutdown: case PsmBatteryVoltageState_NeedsShutdown:
case PsmBatteryVoltageState_NeedsSleep: case PsmBatteryVoltageState_NeedsSleep:
@ -82,7 +82,7 @@ void PowerControlTask::MonitorBatteryState() {
default: default:
break; break;
} }
/* Query voltage state every 5 seconds. */ /* Query voltage state every 5 seconds. */
svcSleepThread(5000000000UL); svcSleepThread(5000000000UL);
} }
@ -91,13 +91,13 @@ void PowerControlTask::MonitorBatteryState() {
void PowerButtonObserveTask::WaitForPowerButton() { void PowerButtonObserveTask::WaitForPowerButton() {
/* Wait up to a second for error report generation to finish. */ /* Wait up to a second for error report generation to finish. */
eventWait(this->erpt_event, TimeoutHelper::NsToTick(1000000000UL)); eventWait(this->erpt_event, TimeoutHelper::NsToTick(1000000000UL));
/* Force a reboot after some time if kiosk unit. */ /* Force a reboot after some time if kiosk unit. */
const FatalConfig *config = GetFatalConfig(); const FatalConfig *config = GetFatalConfig();
TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL); TimeoutHelper reboot_helper(config->quest_reboot_interval_second * 1000000000UL);
TimeoutHelper auto_reboot_helper(config->fatal_auto_reboot_interval * 1000000); TimeoutHelper auto_reboot_helper(config->fatal_auto_reboot_interval * 1000000);
bool check_vol_up = true, check_vol_down = true; bool check_vol_up = true, check_vol_down = true;
GpioPadSession vol_up_btn, vol_down_btn; GpioPadSession vol_up_btn, vol_down_btn;
if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) { if (R_FAILED(gpioOpenSession(&vol_up_btn, GpioPadName_ButtonVolUp))) {
@ -106,11 +106,11 @@ void PowerButtonObserveTask::WaitForPowerButton() {
if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) { if (R_FAILED(gpioOpenSession(&vol_down_btn, GpioPadName_ButtonVolDown))) {
check_vol_down = false; check_vol_down = false;
} }
/* Ensure we close on early return. */ /* Ensure we close on early return. */
ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } }; ON_SCOPE_EXIT { if (check_vol_up) { gpioPadClose(&vol_up_btn); } };
ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } }; ON_SCOPE_EXIT { if (check_vol_down) { gpioPadClose(&vol_down_btn); } };
/* Set direction input. */ /* Set direction input. */
if (check_vol_up) { if (check_vol_up) {
gpioPadSetDirection(&vol_up_btn, GpioDirection_Input); gpioPadSetDirection(&vol_up_btn, GpioDirection_Input);
@ -118,7 +118,7 @@ void PowerButtonObserveTask::WaitForPowerButton() {
if (check_vol_down) { if (check_vol_down) {
gpioPadSetDirection(&vol_down_btn, GpioDirection_Input); gpioPadSetDirection(&vol_down_btn, GpioDirection_Input);
} }
BpcSleepButtonState state; BpcSleepButtonState state;
GpioValue val; GpioValue val;
while (true) { while (true) {
@ -128,20 +128,20 @@ void PowerButtonObserveTask::WaitForPowerButton() {
bpcRebootSystem(); bpcRebootSystem();
return; return;
} }
if (check_vol_up && R_SUCCEEDED((rc = gpioPadGetValue(&vol_up_btn, &val))) && val == GpioValue_Low) { if (check_vol_up && R_SUCCEEDED((rc = gpioPadGetValue(&vol_up_btn, &val))) && val == GpioValue_Low) {
bpcRebootSystem(); bpcRebootSystem();
} }
if (check_vol_down && R_SUCCEEDED((rc = gpioPadGetValue(&vol_down_btn, &val))) && val == GpioValue_Low) { if (check_vol_down && R_SUCCEEDED((rc = gpioPadGetValue(&vol_down_btn, &val))) && val == GpioValue_Low) {
bpcRebootSystem(); bpcRebootSystem();
} }
if ((R_SUCCEEDED(rc = bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) { if ((R_SUCCEEDED(rc = bpcGetSleepButtonState(&state)) && state == BpcSleepButtonState_Held) || (config->quest_flag && reboot_helper.TimedOut())) {
bpcRebootSystem(); bpcRebootSystem();
return; return;
} }
/* Wait 100 ms between button checks. */ /* Wait 100 ms between button checks. */
svcSleepThread(100000000UL); svcSleepThread(100000000UL);
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -43,92 +43,69 @@ u32 GetPixelOffset(uint32_t x, uint32_t y)
} }
Result ShowFatalTask::SetupDisplayInternal() { Result ShowFatalTask::SetupDisplayInternal() {
Result rc;
ViDisplay display; ViDisplay display;
/* Try to open the display. */ /* Try to open the display. */
if (R_FAILED((rc = viOpenDisplay("Internal", &display)))) { R_TRY_CATCH(viOpenDisplay("Internal", &display)) {
if (rc == ResultViNotFound) { R_CATCH(ResultViNotFound) {
return ResultSuccess; return ResultSuccess;
} else {
return rc;
} }
} } R_END_TRY_CATCH;
/* Guarantee we close the display. */ /* Guarantee we close the display. */
ON_SCOPE_EXIT { viCloseDisplay(&display); }; ON_SCOPE_EXIT { viCloseDisplay(&display); };
/* Turn on the screen. */ /* Turn on the screen. */
if (R_FAILED((rc = viSetDisplayPowerState(&display, ViPowerState_On)))) { R_TRY(viSetDisplayPowerState(&display, ViPowerState_On));
return rc;
}
/* Set alpha to 1.0f. */ /* Set alpha to 1.0f. */
if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) { R_TRY(viSetDisplayAlpha(&display, 1.0f));
return rc;
}
return rc; return ResultSuccess;
} }
Result ShowFatalTask::SetupDisplayExternal() { Result ShowFatalTask::SetupDisplayExternal() {
Result rc;
ViDisplay display; ViDisplay display;
/* Try to open the display. */ /* Try to open the display. */
if (R_FAILED((rc = viOpenDisplay("External", &display)))) { R_TRY_CATCH(viOpenDisplay("External", &display)) {
if (rc == ResultViNotFound) { R_CATCH(ResultViNotFound) {
return ResultSuccess; return ResultSuccess;
} else {
return rc;
} }
} } R_END_TRY_CATCH;
/* Guarantee we close the display. */ /* Guarantee we close the display. */
ON_SCOPE_EXIT { viCloseDisplay(&display); }; ON_SCOPE_EXIT { viCloseDisplay(&display); };
/* Set alpha to 1.0f. */ /* Set alpha to 1.0f. */
if (R_FAILED((rc = viSetDisplayAlpha(&display, 1.0f)))) { R_TRY(viSetDisplayAlpha(&display, 1.0f));
return rc;
}
return rc; return ResultSuccess;
} }
Result ShowFatalTask::PrepareScreenForDrawing() { Result ShowFatalTask::PrepareScreenForDrawing() {
Result rc = ResultSuccess;
/* Connect to vi. */ /* Connect to vi. */
if (R_FAILED((rc = viInitialize(ViServiceType_Manager)))) { R_TRY(viInitialize(ViServiceType_Manager));
return rc;
}
/* Close other content. */ /* Close other content. */
viSetContentVisibility(false); viSetContentVisibility(false);
/* Setup the two displays. */ /* Setup the two displays. */
if (R_FAILED((rc = SetupDisplayInternal())) || R_FAILED((rc = SetupDisplayExternal()))) { R_TRY(SetupDisplayInternal());
return rc; R_TRY(SetupDisplayExternal());
}
/* Open the default display. */ /* Open the default display. */
if (R_FAILED((rc = viOpenDefaultDisplay(&this->display)))) { R_TRY(viOpenDefaultDisplay(&this->display));
return rc;
}
/* Reset the display magnification to its default value. */ /* Reset the display magnification to its default value. */
u32 display_width, display_height; u32 display_width, display_height;
if (R_FAILED((rc = viGetDisplayLogicalResolution(&this->display, &display_width, &display_height)))) { R_TRY(viGetDisplayLogicalResolution(&this->display, &display_width, &display_height));
return rc;
}
/* viSetDisplayMagnification was added in 3.0.0. */ /* viSetDisplayMagnification was added in 3.0.0. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) { if (GetRuntimeFirmwareVersion() >= FirmwareVersion_300) {
if (R_FAILED((rc = viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height)))) { R_TRY(viSetDisplayMagnification(&this->display, 0, 0, display_width, display_height));
return rc;
}
} }
/* Create layer to draw to. */ /* Create layer to draw to. */
if (R_FAILED((rc = viCreateLayer(&this->display, &this->layer)))) { R_TRY(viCreateLayer(&this->display, &this->layer));
return rc;
}
/* Setup the layer. */ /* Setup the layer. */
{ {
@ -144,47 +121,36 @@ Result ShowFatalTask::PrepareScreenForDrawing() {
const float layer_y = static_cast<float>((display_height - layer_height) / 2); const float layer_y = static_cast<float>((display_height - layer_height) / 2);
u64 layer_z; u64 layer_z;
if (R_FAILED((rc = viSetLayerSize(&this->layer, layer_width, layer_height)))) { R_TRY(viSetLayerSize(&this->layer, layer_width, layer_height));
return rc;
}
/* Set the layer's Z at display maximum, to be above everything else .*/ /* Set the layer's Z at display maximum, to be above everything else .*/
/* NOTE: Fatal hardcodes 100 here. */ /* NOTE: Fatal hardcodes 100 here. */
if (R_SUCCEEDED((rc = viGetDisplayMaximumZ(&this->display, &layer_z)))) { if (R_SUCCEEDED(viGetDisplayMaximumZ(&this->display, &layer_z))) {
if (R_FAILED((rc = viSetLayerZ(&this->layer, layer_z)))) { R_TRY(viSetLayerZ(&this->layer, layer_z));
return rc;
}
} }
/* Center the layer in the screen. */ /* Center the layer in the screen. */
if (R_FAILED((rc = viSetLayerPosition(&this->layer, layer_x, layer_y)))) { R_TRY(viSetLayerPosition(&this->layer, layer_x, layer_y));
return rc;
}
/* Create framebuffer. */ /* Create framebuffer. */
if (R_FAILED(rc = nwindowCreateFromLayer(&this->win, &this->layer))) { R_TRY(nwindowCreateFromLayer(&this->win, &this->layer));
return rc; R_TRY(framebufferCreate(&this->fb, &this->win, raw_width, raw_height, PIXEL_FORMAT_RGB_565, 1));
}
if (R_FAILED(rc = framebufferCreate(&this->fb, &this->win, raw_width, raw_height, PIXEL_FORMAT_RGB_565, 1))) {
return rc;
}
} }
return ResultSuccess;
return rc;
} }
Result ShowFatalTask::ShowFatal() { Result ShowFatalTask::ShowFatal() {
Result rc = ResultSuccess;
const FatalConfig *config = GetFatalConfig(); const FatalConfig *config = GetFatalConfig();
/* Prepare screen for drawing. */
DoWithSmSession([&]() { DoWithSmSession([&]() {
rc = PrepareScreenForDrawing(); Result rc = PrepareScreenForDrawing();
if (R_FAILED(rc)) {
*(volatile u32 *)(0xCAFEBABE) = rc;
std::abort();
}
}); });
if (R_FAILED(rc)) {
*(volatile u32 *)(0xCAFEBABE) = rc;
return rc;
}
/* Dequeue a buffer. */ /* Dequeue a buffer. */
u16 *tiled_buf = reinterpret_cast<u16 *>(framebufferBegin(&this->fb, NULL)); u16 *tiled_buf = reinterpret_cast<u16 *>(framebufferBegin(&this->fb, NULL));
@ -407,7 +373,7 @@ Result ShowFatalTask::ShowFatal() {
/* Enqueue the buffer. */ /* Enqueue the buffer. */
framebufferEnd(&fb); framebufferEnd(&fb);
return rc; return ResultSuccess;
} }
Result ShowFatalTask::Run() { Result ShowFatalTask::Run() {

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_task_sound.hpp" #include "fatal_task_sound.hpp"
@ -40,17 +40,17 @@ void StopSoundTask::StopSound() {
cmd.dev = 0xC802; cmd.dev = 0xC802;
cmd.val = 200; cmd.val = 200;
i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All);
for (u16 dev = 97; dev <= 102; dev++) { for (u16 dev = 97; dev <= 102; dev++) {
cmd.dev = dev; cmd.dev = dev;
cmd.val = 0; cmd.val = 0;
i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All); i2csessionSendAuto(&audio, &cmd, sizeof(cmd), I2cTransactionOption_All);
} }
i2csessionClose(&audio); i2csessionClose(&audio);
} }
} }
/* Talk to the ALC5639 over GPIO, and disable audio output */ /* Talk to the ALC5639 over GPIO, and disable audio output */
{ {
GpioPadSession audio; GpioPadSession audio;
@ -58,10 +58,10 @@ void StopSoundTask::StopSound() {
/* Set direction output, sleep 200 ms so it can take effect. */ /* Set direction output, sleep 200 ms so it can take effect. */
gpioPadSetDirection(&audio, GpioDirection_Output); gpioPadSetDirection(&audio, GpioDirection_Output);
svcSleepThread(200000000UL); svcSleepThread(200000000UL);
/* Pull audio codec low. */ /* Pull audio codec low. */
gpioPadSetValue(&audio, GpioValue_Low); gpioPadSetValue(&audio, GpioValue_Low);
gpioPadClose(&audio); gpioPadClose(&audio);
} }
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_throw.hpp" #include "fatal_throw.hpp"
#include "fatal_event_manager.hpp" #include "fatal_event_manager.hpp"
@ -28,20 +28,19 @@ static Result SetThrown() {
if (g_thrown) { if (g_thrown) {
return ResultFatalAlreadyThrown; return ResultFatalAlreadyThrown;
} }
g_thrown = true; g_thrown = true;
return ResultSuccess; return ResultSuccess;
} }
Result ThrowFatalForSelf(u32 error) { Result ThrowFatalForSelf(u32 error) {
u64 pid = 0; u64 pid = 0;
svcGetProcessId(&pid, CUR_PROCESS_HANDLE); svcGetProcessId(&pid, CUR_PROCESS_HANDLE);
return ThrowFatalImpl(error, pid, FatalType_ErrorScreen, nullptr); return ThrowFatalImpl(error, pid, FatalType_ErrorScreen, nullptr);
} }
Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu_ctx) { Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu_ctx) {
Result rc = ResultSuccess;
FatalThrowContext ctx = {0}; FatalThrowContext ctx = {0};
ctx.error_code = error; ctx.error_code = error;
if (cpu_ctx != nullptr) { if (cpu_ctx != nullptr) {
@ -62,27 +61,27 @@ Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu
} }
/* Reassign this unconditionally, for convenience. */ /* Reassign this unconditionally, for convenience. */
cpu_ctx = &ctx.cpu_ctx; cpu_ctx = &ctx.cpu_ctx;
/* Get config. */ /* Get config. */
const FatalConfig *config = GetFatalConfig(); const FatalConfig *config = GetFatalConfig();
/* Get title id. On failure, it'll be zero. */ /* Get title id. On failure, it'll be zero. */
u64 title_id = 0; u64 title_id = 0;
pminfoGetTitleId(&title_id, pid); pminfoGetTitleId(&title_id, pid);
ctx.is_creport = title_id == TitleId_Creport; ctx.is_creport = title_id == TitleId_Creport;
/* Support for ams creport. TODO: Make this its own command? */ /* Support for ams creport. TODO: Make this its own command? */
if (ctx.is_creport && !cpu_ctx->is_aarch32 && cpu_ctx->aarch64_ctx.afsr0 != 0) { if (ctx.is_creport && !cpu_ctx->is_aarch32 && cpu_ctx->aarch64_ctx.afsr0 != 0) {
title_id = cpu_ctx->aarch64_ctx.afsr0; title_id = cpu_ctx->aarch64_ctx.afsr0;
} }
/* Atmosphere extension: automatic debug info collection. */ /* Atmosphere extension: automatic debug info collection. */
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_200 && !ctx.is_creport) { 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)) { 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); TryCollectDebugInformation(&ctx, pid);
} }
} }
switch (policy) { switch (policy) {
case FatalType_ErrorReport: case FatalType_ErrorReport:
/* TODO: Don't write an error report. */ /* TODO: Don't write an error report. */
@ -91,20 +90,18 @@ Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu
case FatalType_ErrorScreen: case FatalType_ErrorScreen:
{ {
/* Ensure we only throw once. */ /* Ensure we only throw once. */
if (R_FAILED((rc = SetThrown()))) { R_TRY(SetThrown());
return rc;
}
/* Signal that fatal is about to happen. */ /* Signal that fatal is about to happen. */
GetEventManager()->SignalEvents(); GetEventManager()->SignalEvents();
/* Create events. */ /* Create events. */
Event erpt_event; Event erpt_event;
Event battery_event; Event battery_event;
if (R_FAILED(eventCreate(&erpt_event, false)) || R_FAILED(eventCreate(&battery_event, false))) { if (R_FAILED(eventCreate(&erpt_event, false)) || R_FAILED(eventCreate(&battery_event, false))) {
std::abort(); std::abort();
} }
/* Run tasks. */ /* Run tasks. */
if (config->transition_to_fatal) { if (config->transition_to_fatal) {
RunFatalTasks(&ctx, title_id, policy == FatalType_ErrorReportAndErrorScreen, &erpt_event, &battery_event); RunFatalTasks(&ctx, title_id, policy == FatalType_ErrorReportAndErrorScreen, &erpt_event, &battery_event);
@ -112,13 +109,13 @@ Result ThrowFatalImpl(u32 error, u64 pid, FatalType policy, FatalCpuContext *cpu
/* If flag is not set, don't show the fatal screen. */ /* If flag is not set, don't show the fatal screen. */
return ResultSuccess; return ResultSuccess;
} }
} }
break; break;
default: default:
/* N aborts here. Should we just return an error code? */ /* N aborts here. Should we just return an error code? */
std::abort(); std::abort();
} }
return ResultSuccess; return ResultSuccess;
} }

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>
@ -24,7 +24,7 @@ static constexpr size_t NumAarch32Gprs = 16;
struct Aarch64CpuContext { struct Aarch64CpuContext {
using RegisterType = u64; using RegisterType = u64;
static constexpr size_t MaxStackTraceDepth = 0x20; static constexpr size_t MaxStackTraceDepth = 0x20;
/* Registers, exception context. N left names for these fields in fatal .rodata. */ /* Registers, exception context. N left names for these fields in fatal .rodata. */
union { union {
RegisterType x[NumAarch64Gprs]; RegisterType x[NumAarch64Gprs];
@ -41,7 +41,7 @@ struct Aarch64CpuContext {
RegisterType afsr1; RegisterType afsr1;
RegisterType esr; RegisterType esr;
RegisterType far; RegisterType far;
/* Misc. */ /* Misc. */
RegisterType stack_trace[MaxStackTraceDepth]; RegisterType stack_trace[MaxStackTraceDepth];
RegisterType start_address; RegisterType start_address;
@ -52,7 +52,7 @@ struct Aarch64CpuContext {
struct Aarch32CpuContext { struct Aarch32CpuContext {
using RegisterType = u32; using RegisterType = u32;
static constexpr size_t MaxStackTraceDepth = 0x20; static constexpr size_t MaxStackTraceDepth = 0x20;
/* Registers, exception context. N left names for these fields in fatal .rodata. */ /* Registers, exception context. N left names for these fields in fatal .rodata. */
union { union {
RegisterType r[NumAarch32Gprs]; RegisterType r[NumAarch32Gprs];
@ -70,7 +70,7 @@ struct Aarch32CpuContext {
RegisterType afsr1; RegisterType afsr1;
RegisterType esr; RegisterType esr;
RegisterType far; RegisterType far;
/* Misc. Yes, stack_trace_size is really laid out differently than aarch64... */ /* Misc. Yes, stack_trace_size is really laid out differently than aarch64... */
RegisterType stack_trace[MaxStackTraceDepth]; RegisterType stack_trace[MaxStackTraceDepth];
u32 stack_trace_size; u32 stack_trace_size;
@ -83,7 +83,7 @@ struct FatalCpuContext {
Aarch64CpuContext aarch64_ctx; Aarch64CpuContext aarch64_ctx;
Aarch32CpuContext aarch32_ctx; Aarch32CpuContext aarch32_ctx;
}; };
bool is_aarch32; bool is_aarch32;
u32 type; u32 type;
}; };

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <switch.h> #include <switch.h>
#include "fatal_user.hpp" #include "fatal_user.hpp"
#include "fatal_throw.hpp" #include "fatal_throw.hpp"

View file

@ -13,7 +13,7 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <switch.h> #include <switch.h>
#include <stratosphere.hpp> #include <stratosphere.hpp>
@ -27,7 +27,7 @@ enum UserCmd {
}; };
class UserService final : public IServiceObject { class UserService final : public IServiceObject {
private: private:
/* Actual commands. */ /* Actual commands. */
Result ThrowFatal(u32 error, PidDescriptor pid_desc); Result ThrowFatal(u32 error, PidDescriptor pid_desc);
Result ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy); Result ThrowFatalWithPolicy(u32 error, PidDescriptor pid_desc, FatalType policy);