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)
This commit is contained in:
TuxSH 2023-01-11 23:24:59 +00:00
parent 635235c86c
commit 19f7ef372b
4 changed files with 223 additions and 141 deletions

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -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));

View File

@ -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);
}