From 19f7ef372b899818f6da7806288ae2bc4eb9ac6e Mon Sep 17 00:00:00 2001 From: TuxSH <1922548+TuxSH@users.noreply.github.com> Date: Wed, 11 Jan 2023 23:24:59 +0000 Subject: [PATCH] errdisp: properly handle game medium being removed - transform some error codes into "NAND damaged" or "Gamecard removed" like official errdisp does - fix bug in SetUserString - add more info to logfile (/luma/errdisp.txt) --- sysmodules/rosalina/include/utils.h | 1 + sysmodules/rosalina/source/errdisp.c | 258 +++++++++++++++++---------- sysmodules/rosalina/source/menus.c | 52 +----- sysmodules/rosalina/source/utils.c | 53 ++++++ 4 files changed, 223 insertions(+), 141 deletions(-) diff --git a/sysmodules/rosalina/include/utils.h b/sysmodules/rosalina/include/utils.h index 6155308..7f9a910 100644 --- a/sysmodules/rosalina/include/utils.h +++ b/sysmodules/rosalina/include/utils.h @@ -67,3 +67,4 @@ static inline bool isServiceUsable(const char *name) void formatMemoryPermission(char *outbuf, MemPerm perm); void formatUserMemoryState(char *outbuf, MemState state); u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle); +int dateTimeToString(char *out, u64 msSince1900, bool filenameFormat); diff --git a/sysmodules/rosalina/source/errdisp.c b/sysmodules/rosalina/source/errdisp.c index ca20680..e1bd01d 100644 --- a/sysmodules/rosalina/source/errdisp.c +++ b/sysmodules/rosalina/source/errdisp.c @@ -44,7 +44,7 @@ static MyThread errDispThread; static u8 ALIGN(8) errDispThreadStack[0xD00]; static char userString[0x100 + 1] = {0}; -static char staticBuf[0x100 + 1] = {0}; +static char staticBuf[sizeof(userString)] = {0}; MyThread *errDispCreateThread(void) { @@ -63,27 +63,106 @@ static inline int ERRF_FormatRegisterValue(char *out, const char *name, u32 valu return sprintf(out, "%-9s %08lx", name, value); } -static inline void ERRF_GetErrInfo(ERRF_FatalErrInfo* info, u32* in, u32 size) +static inline void ERRF_PreprocessErrInfo(ERRF_FatalErrInfo *info, u32 *in) { - memcpy(info, in, size); + memcpy(info, in, sizeof(ERRF_FatalErrInfo)); + + Result res = info->resCode; + u32 level = R_LEVEL(res); + u32 module = R_MODULE(res); + u32 summary = R_SUMMARY(res); + u32 desc = R_DESCRIPTION(res); + + // ErrDisp has special handling where it expects two errors in a row + // for "card removed", but it's fine if we don't. + + if (module != RM_FS) + return; + + switch (desc) + { + // All kinds of "slot1 stuff removed" error ranges + case 0x08C ... 0x095: + case 0x096 ... 0x0A9: + case 0x122 ... 0x12B: + case 0x12C ... 0x13F: + info->type = ERRF_ERRTYPE_CARD_REMOVED; + module = RM_CARD; + break; + // SDMC removed error range + case 0x0AA ... 0x0B3: + info->type = ERRF_ERRTYPE_CARD_REMOVED; + module = RM_SDMC; + break; + // Say sike right now + case 0x399: + info->type = ERRF_ERRTYPE_NAND_DAMAGED; + break; + } + + info->resCode = MAKERESULT(level, summary, module, desc); } -static int ERRF_FormatError(char *out, ERRF_FatalErrInfo *info) +static int ERRF_FormatRegisterDump(char *out, const ERRF_ExceptionData *exceptionData) +{ + char *out0 = out; + static const char *registerNames[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", + "sp", "lr", "pc", "cpsr" + }; + + u32 *regs = (u32 *)(&exceptionData->regs); + for(u32 i = 0; i < 17; i += 2) + { + out += ERRF_FormatRegisterValue(out, registerNames[i], regs[i]); + if(i != 16) + { + out += sprintf(out, " "); + out += ERRF_FormatRegisterValue(out, registerNames[i + 1], i == 16 ? regs[20] : regs[i + 1]); + out += sprintf(out, "\n"); + } + } + + if(exceptionData->excep.type == ERRF_EXCEPTION_PREFETCH_ABORT + || exceptionData->excep.type == ERRF_EXCEPTION_DATA_ABORT) + { + out += sprintf(out, " "); + out += ERRF_FormatRegisterValue(out, "far", exceptionData->excep.far); + out += sprintf(out, "\n"); + out += ERRF_FormatRegisterValue(out, "fsr", exceptionData->excep.fsr); + } + + else if(exceptionData->excep.type == ERRF_EXCEPTION_VFP) + { + out += sprintf(out, " "); + out += ERRF_FormatRegisterValue(out, "fpexc", exceptionData->excep.fpexc); + out += sprintf(out, "\n"); + out += ERRF_FormatRegisterValue(out, "fpinst", exceptionData->excep.fpinst); + out += sprintf(out, " "); + out += ERRF_FormatRegisterValue(out, "fpinst2", exceptionData->excep.fpinst2); + } + return (int)(out - out0); +} + +static int ERRF_FormatGenericInfo(char *out, const ERRF_FatalErrInfo *info) { - char *outStart = out; static const char *types[] = { - "generic", "corrupted", "card removed", "exception", "result failure", "logged", "invalid" + "generic", "corrupted", "card removed", "exception", "result failure", "generic (log only)", "invalid" }; static const char *exceptionTypes[] = { "prefetch abort", "data abort", "undefined instruction", "VFP", "invalid" }; - const char *type = (u32)info->type > (u32)ERRF_ERRTYPE_LOGGED ? types[6] : types[(u32)info->type]; + const char *type = (u32)info->type > (u32)ERRF_ERRTYPE_LOG_ONLY ? types[6] : types[(u32)info->type]; + + char *out0 = out; + Handle processHandle; + Result res; if(info->type == ERRF_ERRTYPE_EXCEPTION) { - const char *exceptionType = (u32)info->data.exception_data.excep.type > (u32)ERRF_EXCEPTION_VFP ? + const char *exceptionType = (u32) info->data.exception_data.excep.type > (u32)ERRF_EXCEPTION_VFP ? exceptionTypes[4] : exceptionTypes[(u32)info->data.exception_data.excep.type]; out += sprintf(out, "Error type: exception (%s)\n", exceptionType); @@ -91,97 +170,75 @@ static int ERRF_FormatError(char *out, ERRF_FatalErrInfo *info) else out += sprintf(out, "Error type: %s\n", type); - if(info->type != ERRF_ERRTYPE_CARD_REMOVED) + out += sprintf(out, "\nProcess ID: %lu\n", info->procId); + + res = svcOpenProcess(&processHandle, info->procId); + if(R_SUCCEEDED(res)) { - Handle processHandle; - Result res; - - out += sprintf(out, "\nProcess ID: %lu\n", info->procId); - - res = svcOpenProcess(&processHandle, info->procId); - if(R_SUCCEEDED(res)) - { - u64 titleId; - char name[9] = { 0 }; - svcGetProcessInfo((s64 *)name, processHandle, 0x10000); - svcGetProcessInfo((s64 *)&titleId, processHandle, 0x10001); - svcCloseHandle(processHandle); - out += sprintf(out, "Process name: %s\n", name); - out += sprintf(out, "Process title ID: 0x%016llx\n", titleId); - } - - out += sprintf(out, "\n"); + u64 titleId; + char name[9] = { 0 }; + svcGetProcessInfo((s64 *)name, processHandle, 0x10000); + svcGetProcessInfo((s64 *)&titleId, processHandle, 0x10001); + svcCloseHandle(processHandle); + out += sprintf(out, "Process name: %s\n", name); + out += sprintf(out, "Process title ID: %016llx\n", titleId); } - if(info->type == ERRF_ERRTYPE_EXCEPTION) + out += sprintf(out, "\n"); + + return (int)(out - out0); +} + +static int ERRF_FormatError(char *out, const ERRF_FatalErrInfo *info, bool isLogFile) +{ + char *outStart = out; + + if (isLogFile) { - static const char *registerNames[] = { - "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", - "sp", "lr", "pc", "cpsr" - }; + char dateTimeStr[32]; + u64 timeNow = osGetTime(); + u64 timeAtBoot = timeNow - (1000 * svcGetSystemTick() / SYSCLOCK_ARM11); + dateTimeToString(dateTimeStr, timeNow, false); + out += sprintf(out, "Reported on: %s\n", dateTimeStr); + dateTimeToString(dateTimeStr, timeAtBoot, false); + out += sprintf(out, "System booted on: %s\n\n", dateTimeStr); - u32 *regs = (u32 *)(&info->data.exception_data.regs); - for(u32 i = 0; i < 17; i += 2) - { - out += ERRF_FormatRegisterValue(out, registerNames[i], regs[i]); - if(i != 16) - { - out += sprintf(out, " "); - out += ERRF_FormatRegisterValue(out, registerNames[i + 1], i == 16 ? regs[20] : regs[i + 1]); - out += sprintf(out, "\n"); - } - } - - if(info->data.exception_data.excep.type == ERRF_EXCEPTION_PREFETCH_ABORT - || info->data.exception_data.excep.type == ERRF_EXCEPTION_DATA_ABORT) - { - out += sprintf(out, " "); - out += ERRF_FormatRegisterValue(out, "far", info->data.exception_data.excep.far); - out += sprintf(out, "\n"); - out += ERRF_FormatRegisterValue(out, "fsr", info->data.exception_data.excep.fsr); - } - - else if(info->data.exception_data.excep.type == ERRF_EXCEPTION_VFP) - { - out += sprintf(out, " "); - out += ERRF_FormatRegisterValue(out, "fpexc", info->data.exception_data.excep.fpexc); - out += sprintf(out, "\n"); - out += ERRF_FormatRegisterValue(out, "fpinst", info->data.exception_data.excep.fpinst); - out += sprintf(out, " "); - out += ERRF_FormatRegisterValue(out, "fpinst2", info->data.exception_data.excep.fpinst2); - out += sprintf(out, "\n"); - } - - out += sprintf(out, "\n"); } - else if(info->type != ERRF_ERRTYPE_CARD_REMOVED) + switch (info->type) { - if(info->type != ERRF_ERRTYPE_FAILURE) - out += sprintf(out, "Address: 0x%08lx\n", info->pcAddr); - - out += sprintf(out, "Error code: 0x%08lx\n", info->resCode); - } - - const char *desc; - switch(info->type) - { - case ERRF_ERRTYPE_CARD_REMOVED: - desc = "The card was removed."; + case ERRF_ERRTYPE_NAND_DAMAGED: + out += sprintf(out, "The NAND chip has been damaged.\n"); break; - case ERRF_ERRTYPE_MEM_CORRUPT: - desc = "The System Memory has been damaged."; + case ERRF_ERRTYPE_CARD_REMOVED: + { + const char *medium = R_MODULE(info->resCode) == RM_SDMC ? "SD card" : "cartridge"; + out += sprintf(out, "The %s was removed.\n", medium); + break; + } + case ERRF_ERRTYPE_GENERIC: + case ERRF_ERRTYPE_LOG_ONLY: + out += ERRF_FormatGenericInfo(out, info); + out += sprintf(out, "Address: 0x%08lx\n", info->pcAddr); + out += sprintf(out, "Error code: 0x%08lx\n", info->resCode); + break; + case ERRF_ERRTYPE_EXCEPTION: + out += ERRF_FormatGenericInfo(out, info); + out += ERRF_FormatRegisterDump(out, &info->data.exception_data); + out += sprintf(out, "\n"); break; case ERRF_ERRTYPE_FAILURE: - info->data.failure_mesg[0x5F] = 0; // make sure the last byte in the IPC buffer is NULL - desc = info->data.failure_mesg; + out += ERRF_FormatGenericInfo(out, info); + out += sprintf(out, "Error code: 0x%08lx\n", info->resCode); + out += sprintf(out, "Reason: %.96s\n", info->data.failure_mesg); break; default: - desc = ""; - break; + out += sprintf(out, "Invalid fatal error data.\n"); } - if(desc[0] != 0) - out += sprintf(out, "\n%s\n", desc); + // We might not always have enough space to display this on screen, so keep it to the log file + if (isLogFile && userString[0] != '\0') + out += sprintf(out, "\n%.256s\n", userString); + out += sprintf(out, "\n"); return out - outStart; } @@ -191,10 +248,10 @@ static void ERRF_DisplayError(ERRF_FatalErrInfo *info) { Draw_Lock(); - u32 posY = Draw_DrawString(10, 10, COLOR_RED, userString[0] == 0 ? "An error occurred (ErrDisp)" : userString); + u32 posY = Draw_DrawString(10, 10, COLOR_RED, "An error occurred (ErrDisp)"); char buf[0x400]; - ERRF_FormatError(buf, info); + ERRF_FormatError(buf, info, false); posY = posY < 30 ? 30 : posY; posY = Draw_DrawString(10, posY, COLOR_WHITE, buf); @@ -216,12 +273,18 @@ static Result ERRF_SaveErrorToFile(ERRF_FatalErrInfo *info) Result res = 0; IFile file; - n = ERRF_FormatError(buf, info); - n += sprintf(buf + n, "-------------------------------------\n\n"); - if(R_FAILED(svcGetSystemInfo(&out, 0x10000, 0x203))) svcBreak(USERBREAK_ASSERT); isSdMode = (bool)out; + // Bail out early if we know we're unable to write anything, and do not log "SD/cartridge removed" stuff + if (info->type == ERRF_ERRTYPE_CARD_REMOVED) + return 0; + else if (!isSdMode && info->type == ERRF_ERRTYPE_NAND_DAMAGED) + return info->resCode; + + n = ERRF_FormatError(buf, info, true); + n += sprintf(buf + n, "-------------------------------------\n\n"); + archiveId = isSdMode ? ARCHIVE_SDMC : ARCHIVE_NAND_RW; res = IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, "/luma/errdisp.txt"), FS_OPEN_WRITE | FS_OPEN_CREATE); @@ -252,9 +315,9 @@ void ERRF_HandleCommands(void) { case 1: // Throw { - ERRF_GetErrInfo(&info, (cmdbuf + 1), sizeof(ERRF_FatalErrInfo)); + ERRF_PreprocessErrInfo(&info, (cmdbuf + 1)); ERRF_SaveErrorToFile(&info); - if(!menuShouldExit && (info.type != ERRF_ERRTYPE_LOGGED || info.procId == 0)) + if(!menuShouldExit && info.type != ERRF_ERRTYPE_LOG_ONLY) { menuEnter(); @@ -290,8 +353,9 @@ void ERRF_HandleCommands(void) } else { - u32 sz = cmdbuf[1] <= 0x100 ? cmdbuf[1] : 0x100; - memcpy(userString, cmdbuf + 3, sz); + // Official kernel doesn't copy, but this means it could be overwritten by a rogue command + u32 sz = cmdbuf[2] >> 14; + memcpy(userString, (const char *)cmdbuf[3], sz); userString[sz] = 0; cmdbuf[0] = IPC_MakeHeader(2, 1, 0); @@ -299,6 +363,12 @@ void ERRF_HandleCommands(void) } break; } + default: // error + { + cmdbuf[0] = IPC_MakeHeader(0, 1, 0); + cmdbuf[1] = 0xD900182F; + break; + } } } diff --git a/sysmodules/rosalina/source/menus.c b/sysmodules/rosalina/source/menus.c index 2833f4d..02e3af1 100644 --- a/sysmodules/rosalina/source/menus.c +++ b/sysmodules/rosalina/source/menus.c @@ -378,6 +378,7 @@ void RosalinaMenu_TakeScreenshot(void) Result res = 0; char filename[64]; + char dateTimeStr[32]; FS_Archive archive; FS_ArchiveID archiveId; @@ -412,64 +413,21 @@ void RosalinaMenu_TakeScreenshot(void) FSUSER_CloseArchive(archive); } - // Conversion code adapted from https://stackoverflow.com/questions/21593692/convert-unix-timestamp-to-date-without-system-libs - // (original author @gnif under CC-BY-SA 4.0) - u32 seconds, minutes, hours, days, year, month; - u64 milliseconds = osGetTime(); - seconds = milliseconds/1000; - milliseconds %= 1000; - minutes = seconds / 60; - seconds %= 60; - hours = minutes / 60; - minutes %= 60; - days = hours / 24; - hours %= 24; + dateTimeToString(dateTimeStr, osGetTime(), true); - year = 1900; // osGetTime starts in 1900 - - while(true) - { - bool leapYear = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); - u16 daysInYear = leapYear ? 366 : 365; - if(days >= daysInYear) - { - days -= daysInYear; - ++year; - } - else - { - static const u8 daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - for(month = 0; month < 12; ++month) - { - u8 dim = daysInMonth[month]; - - if (month == 1 && leapYear) - ++dim; - - if (days >= dim) - days -= dim; - else - break; - } - break; - } - } - days++; - month++; - - sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top.bmp", year, month, days, hours, minutes, seconds, milliseconds); + sprintf(filename, "/luma/screenshots/%s_top.bmp", dateTimeStr); TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE)); TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, true)); TRY(IFile_Close(&file)); - sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_bot.bmp", year, month, days, hours, minutes, seconds, milliseconds); + sprintf(filename, "/luma/screenshots/%s_bot.bmp", dateTimeStr); TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE)); TRY(RosalinaMenu_WriteScreenshot(&file, bottomWidth, false, true)); TRY(IFile_Close(&file)); if(is3d && (Draw_GetCurrentFramebufferAddress(true, true) != Draw_GetCurrentFramebufferAddress(true, false))) { - sprintf(filename, "/luma/screenshots/%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu_top_right.bmp", year, month, days, hours, minutes, seconds, milliseconds); + sprintf(filename, "/luma/screenshots/%s_top_right.bmp", dateTimeStr); TRY(IFile_Open(&file, archiveId, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_ASCII, filename), FS_OPEN_CREATE | FS_OPEN_WRITE)); TRY(RosalinaMenu_WriteScreenshot(&file, topWidth, true, false)); TRY(IFile_Close(&file)); diff --git a/sysmodules/rosalina/source/utils.c b/sysmodules/rosalina/source/utils.c index 9719896..237989c 100644 --- a/sysmodules/rosalina/source/utils.c +++ b/sysmodules/rosalina/source/utils.c @@ -108,3 +108,56 @@ u32 formatMemoryMapOfProcess(char *outbuf, u32 bufLen, Handle handle) svcCloseHandle(handle); return posInBuffer; } + +int dateTimeToString(char *out, u64 msSince1900, bool filenameFormat) +{ + // Conversion code adapted from https://stackoverflow.com/questions/21593692/convert-unix-timestamp-to-date-without-system-libs + // (original author @gnif under CC-BY-SA 4.0) + u32 seconds, minutes, hours, days, year, month; + u64 milliseconds = msSince1900; + seconds = milliseconds/1000; + milliseconds %= 1000; + minutes = seconds / 60; + seconds %= 60; + hours = minutes / 60; + minutes %= 60; + days = hours / 24; + hours %= 24; + + year = 1900; // osGetTime starts in 1900 + + while(true) + { + bool leapYear = (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)); + u16 daysInYear = leapYear ? 366 : 365; + if(days >= daysInYear) + { + days -= daysInYear; + ++year; + } + else + { + static const u8 daysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + for(month = 0; month < 12; ++month) + { + u8 dim = daysInMonth[month]; + + if (month == 1 && leapYear) + ++dim; + + if (days >= dim) + days -= dim; + else + break; + } + break; + } + } + days++; + month++; + + if (filenameFormat) + return sprintf(out, "%04lu-%02lu-%02lu_%02lu-%02lu-%02lu.%03llu", year, month, days, hours, minutes, seconds, milliseconds); + else + return sprintf(out, "%04lu-%02lu-%02lu %02lu:%02lu:%02lu", year, month, days, hours, minutes, seconds); +}