#include #include "constants.h" #include "bss.h" #include "lib/entry.h" #include "lib/lib_09660.h" #include "lib/lib_0c000.h" #include "lib/lib_2fa00.h" #include "data.h" #include "types.h" OSThread g_FaultThread; u8 g_FaultStack[STACKSIZE_FAULT]; OSMesgQueue g_FaultMesgQueue; OSMesg g_FaultMesg; u32 var8005d5b0 = 0; s16 g_CrashCurX = 0; s16 g_CrashCurY = 0; struct crashdescription { u32 mask; u32 value; const char *text; }; struct crashdescription g_CrashCauseDescriptions[] = { { CAUSE_BD, CAUSE_BD, "BD" }, { CAUSE_IP8, CAUSE_IP8, "IP8" }, { CAUSE_IP7, CAUSE_IP7, "IP7" }, { CAUSE_IP6, CAUSE_IP6, "IP6" }, { CAUSE_IP5, CAUSE_IP5, "IP5" }, { CAUSE_IP4, CAUSE_IP4, "IP4" }, { CAUSE_IP3, CAUSE_IP3, "IP3" }, { CAUSE_SW2, CAUSE_SW2, "IP2" }, { CAUSE_SW1, CAUSE_SW1, "IP1" }, { CAUSE_EXCMASK, EXC_INT, "Int" }, { CAUSE_EXCMASK, EXC_MOD, "TLBmod" }, { CAUSE_EXCMASK, EXC_RMISS, "TLBload" }, { CAUSE_EXCMASK, EXC_WMISS, "TLBstore" }, { CAUSE_EXCMASK, EXC_RADE, "Address error on load or instruction fetch" }, { CAUSE_EXCMASK, EXC_WADE, "Address error on store" }, { CAUSE_EXCMASK, EXC_IBE, "Bus error exception on instruction fetch" }, { CAUSE_EXCMASK, EXC_DBE, "Bus error exception on data reference" }, { CAUSE_EXCMASK, EXC_SYSCALL, "Syscall" }, { CAUSE_EXCMASK, EXC_BREAK, "Brk" }, { CAUSE_EXCMASK, EXC_II, "Reserved instruction" }, { CAUSE_EXCMASK, EXC_CPU, "Cop unusable" }, { CAUSE_EXCMASK, EXC_OV, "Overflow" }, { CAUSE_EXCMASK, EXC_TRAP, "Trap" }, { CAUSE_EXCMASK, EXC_VCEI, "Virtual coherency exception on intruction fetch" }, { CAUSE_EXCMASK, EXC_FPE, "Fp exception" }, { CAUSE_EXCMASK, EXC_WATCH, "Watchpoint" }, { CAUSE_EXCMASK, EXC_VCED, "Virtual coherency exception on data reference" }, { 0, 0, "" }, }; struct crashdescription g_CrashSrDescriptions[] = { { SR_CU3, SR_CU3, "CU3" }, { SR_CU2, SR_CU2, "CU2" }, { SR_CU1, SR_CU1, "CU1" }, { SR_CU0, SR_CU0, "CU0" }, { SR_RP, SR_RP, "RP" }, { SR_FR, SR_FR, "FR" }, { SR_RE, SR_RE, "RE" }, { SR_BEV, SR_BEV, "BEV" }, { SR_TS, SR_TS, "TS" }, { SR_SR, SR_SR, "SR" }, { SR_CH, SR_CH, "CH" }, { SR_CE, SR_CE, "CE" }, { SR_DE, SR_DE, "DE" }, { SR_IBIT8, SR_IBIT8, "IM8" }, { SR_IBIT7, SR_IBIT7, "IM7" }, { SR_IBIT6, SR_IBIT6, "IM6" }, { SR_IBIT5, SR_IBIT5, "IM5" }, { SR_IBIT4, SR_IBIT4, "IM4" }, { SR_IBIT3, SR_IBIT3, "IM3" }, { SR_IBIT2, SR_IBIT2, "IM2" }, { SR_IBIT1, SR_IBIT1, "IM1" }, { SR_KX, SR_KX, "KX" }, { SR_SX, SR_SX, "SX" }, { SR_UX, SR_UX, "UX" }, { SR_KSU_MASK, SR_KSU_USR, "USR" }, { SR_KSU_MASK, SR_KSU_SUP, "SUP" }, { SR_KSU_MASK, SR_KSU_KER, "KER" }, { SR_ERL, SR_ERL, "ERL" }, { SR_EXL, SR_EXL, "EXL" }, { SR_IE, SR_IE, "IE" }, { 0, 0, "" }, }; struct crashdescription g_CrashFpcsrDescriptions[] = { { FPCSR_FS, FPCSR_FS, "FS" }, { FPCSR_C, FPCSR_C, "C" }, { FPCSR_CE, FPCSR_CE, "Unimplemented" }, { FPCSR_CV, FPCSR_CV, "Invalid op" }, { FPCSR_CZ, FPCSR_CZ, "/ by 0.0" }, { FPCSR_CO, FPCSR_CO, "Overflow" }, { FPCSR_CU, FPCSR_CU, "Underflow" }, { FPCSR_CI, FPCSR_CI, "Inexact op" }, { FPCSR_EV, FPCSR_EV, "EV" }, { FPCSR_EZ, FPCSR_EZ, "EZ" }, { FPCSR_EO, FPCSR_EO, "EO" }, { FPCSR_EU, FPCSR_EU, "EU" }, { FPCSR_EI, FPCSR_EI, "EI" }, { FPCSR_FV, FPCSR_FV, "FV" }, { FPCSR_FZ, FPCSR_FZ, "FZ" }, { FPCSR_FO, FPCSR_FO, "FO" }, { FPCSR_FU, FPCSR_FU, "FU" }, { FPCSR_FI, FPCSR_FI, "FI" }, { FPCSR_RM_MASK, FPCSR_RM_RN, "RN" }, { FPCSR_RM_MASK, FPCSR_RM_RZ, "RZ" }, { FPCSR_RM_MASK, FPCSR_RM_RP, "RP" }, { FPCSR_RM_MASK, FPCSR_RM_RM, "RM" }, { 0, 0, "" }, }; char (*g_CrashCharBuffer)[71] = NULL; u16 *g_CrashFrameBuffer = NULL; extern u32 _libSegmentStart; extern u32 _libSegmentEnd; void faultproc(void *arg0); void crashPrintDescription(u32 mask, char *label, struct crashdescription *descriptions); void faultCreateThread2(void) { osCreateMesgQueue(&g_FaultMesgQueue, &g_FaultMesg, 1); osCreateThread(&g_FaultThread, THREAD_FAULT, faultproc, NULL, &g_FaultStack[1024], THREADPRI_FAULT); osStartThread(&g_FaultThread); } void faultproc(void *arg0) { OSMesg msg = 0; OSIntMask mask; u8 stack2[4]; u8 stack1[44]; static OSThread *thread; static u32 var80094ad0; osSetEventMesg(OS_EVENT_FAULT, &g_FaultMesgQueue, (OSMesg) 16); var80094ad0 = 0; while (true) { do { osRecvMesg(&g_FaultMesgQueue, &msg, 1); mask = osSetIntMask(1); thread = __osGetCurrFaultedThread(); } while (!thread); osSetIntMask(mask); // Beta versions have something like this here: //crashRender(thread, stack1, stack2); //func00001b1c(true); } } /** * Given a pointer to an instruction and a stack frame pointer, attempt to find * the calling function. Return the address of the caller's stack frame and * populate regs with stack addresses where that register was saved. This can be * used to retrieve the RA value and invoke crashGetParentStackFrame again to * build a backtrace. * * origptr is a pointer to an instruction. This should be either the value of * the PC register of the faulted thread, or an RA register if searching a * parent. * minaddr is the memory address of the start of the code segment, * ie. 0x70001050. This is used to prevent the function from walking out of * the code segment. * origsp is the address of the stack frame for the given origptr. * regs is a pointer to an array of 32 words. * * The function works by walking backwards one instruction at a time, looking * for stack frame adjustments and stores of $ra to the stack. Once both of * these are are found, the function returns with this information. * * The function will return 0 if it can't reliably find the caller. This will * happen if the function being analysed didn't adjust the stack pointer or * store $ra in the stack. It can also fail if the function being analysed uses * returns within branches. */ u32 crashGetParentStackFrame(u32 *origptr, u32 *minaddr, u32 origsp, u32 *regs) { u32 sp = origsp; u32 *ptr; bool foundaddsp = false; bool foundra = false; s32 regid; u32 instruction; s16 value; // Clear the regs array (note: reusing the instruction variable here) for (instruction = 0; instruction < 32; instruction++) regs[instruction] = 0; // Walk backwards through the instructions for (ptr = origptr; ptr >= minaddr; ptr--) { instruction = *ptr; value = instruction & 0xffff; if ((instruction & 0xffff0000) == 0x27bd0000) { // Found an addiu $sp, $sp, instruction, which adjusts the // stack pointer. These can exist near the start and end of any // function. The "add" at the start is done with a negative value // which increases the size of the stack, as the stack expands to // the left. This function is interested in these negative adds, // because it needs to reverse it and move the sp variable forward // to the next stack frame (the frame of the caller). foundaddsp = true; if (value > 0) { // Found the addiu sp at the end of the function. It's pretty // rare to crash (or jump elsewhere) after restoring the sp, // so this situation is not supported by this function. break; } // Change sp to point to the caller's stack frame sp -= (value >> 2) * 4; if (foundra) { break; } } else if ((instruction & 0xffe00000) == 0xafa00000) { // Found a store word instruction that stores to the stack. // The encoding of this instruction is: // // oooooodd dddsssss iiiiiiii iiiiiiii // // Where: // o is the opcode (already known to be sw). // d is the destination register (already known to be $sp) // s is the source register // i is the offset to store to // // This looks for a store from $ra to the stack, so the value can // be read from the stack and used to find the caller's address. regid = (instruction >> 16) & 0x1f; regs[regid] = sp + (value >> 2) * 4; if (regid == 31) { foundra = true; } if (foundaddsp && foundra) { break; } } else if (instruction == 0x03e00008) { // Found a jr $ra statement, which is a return. This will happen if // this loop has walked out of the current function and into the one // prior to it, so bail. // It can also happen if the function has return statements within // branches, but handling these correctly would involve a lot of // complexity so that's unsupported. Because of this, this function // can end the backtrace prematurely if it encounters a function // that does this. break; } } if (foundaddsp && foundra) { return sp; } return 0; } bool crashIsReturnAddress(u32 *instruction) { if (((u32)instruction % 4) == 0 && (u32)instruction >= (u32)boot && (u32)instruction <= (u32)&_libSegmentEnd) { // This condition can never pass because 9 is masked out if ((instruction[-2] & 0xfc00003c) == 9) { return true; } // If 2 instructions earlier was a jal if ((instruction[-2] & 0xfc000000) == 0x0c000000) { return true; } } return false; } u32 crashGetStackEnd(u32 sp, s32 tid) { u32 start; u32 end; if (tid <= 0 || tid > 6U) { crashPrint("Bad tid\n"); return 0; } start = (u32)g_StackStartAddrs[tid]; end = (u32)g_StackEndAddrs[tid]; if (sp >= 0x80000000) { return end; } return (sp & 0xf0000000) | (end - start); } u32 crashGetStackStart(u32 sp, s32 tid) { u32 start; if (tid <= 0 || tid > 6U) { crashPrint("Bad tid\n"); return 0; } start = (u32)g_StackStartAddrs[tid]; if (sp >= 0x80000000) { return start; } return sp & 0xf0000000; } bool crashIsDouble(f32 value) { u32 bits = *(u32*)&value; u32 fraction = bits & 0x7fffff; u8 exponent = (u8)(bits >> 23); return fraction == 0 || (exponent != 0 && exponent != 0xff); } void crashPrintFloat(s32 index, f32 value) { if (crashIsDouble(value)) { crashPrint("%s%s%02d: % .7e ", "", "", index, (f64)value); } else { u32 bits = *(u32 *)&value; crashPrint("%02d: I%d.%03d.%07d ", index, (bits & 0x80000000) >> 31, (bits & 0x7f800000) >> 23, bits & 0x7fffff); } } void crashPrint2Floats(s32 index, f32 value1, f32 value2) { crashPrintFloat(index, value1); crashPrint(" "); crashPrintFloat(index + 1, value2); crashPrint("\n"); } void crashPrint3Floats(s32 index, f32 value1, f32 value2, f32 value3) { crashPrintFloat(index, value1); crashPrint(" "); crashPrintFloat(index + 1, value2); crashPrint(" "); crashPrintFloat(index + 2, value3); crashPrint("\n"); } u32 crashRender(OSThread *thread, u32 *callstack, s32 *tracelen) { s32 i; u32 ptr; u32 *sp; u32 regs[32]; u32 *stackend; u32 *stackstart; __OSThreadContext *ctx = &thread->context; bool done; crashPrint("\n\nFAULT-\n"); if (g_Vars.unk0004e0 == 0) { // Print a stack trace in a dodgy way. // It works by iterating through the stack allocation, looking for any // values which could potentially be a return address, and prints them. u32 *stackend = (u32 *) crashGetStackEnd(ctx->sp, thread->id); u32 *sp; crashPrint("DodgyStackTrace: %08llx ", ctx->ra & 0xffffffff); sp = (u32 *) ctx->sp; while (sp < stackend) { if (crashIsReturnAddress((u32 *)*sp)) { crashPrint("%08x ", *sp); } sp++; } crashPrint(".\n"); } crashPrint("%H#@! Another Perfect Crash (tm)\n"); if (g_Vars.unk0004e0 == 0) { // Print floating point registers crashPrint2Floats(0, ctx->fp0.f.f_odd, ctx->fp0.f.f_even); crashPrint3Floats(2, ctx->fp2.f.f_odd, ctx->fp2.f.f_even, ctx->fp4.f.f_odd); crashPrint3Floats(5, ctx->fp4.f.f_even, ctx->fp6.f.f_odd, ctx->fp6.f.f_even); crashPrint3Floats(8, ctx->fp8.f.f_odd, ctx->fp8.f.f_even, ctx->fp10.f.f_odd); crashPrint3Floats(11, ctx->fp10.f.f_even, ctx->fp12.f.f_odd, ctx->fp12.f.f_even); crashPrint3Floats(14, ctx->fp14.f.f_odd, ctx->fp14.f.f_even, ctx->fp16.f.f_odd); crashPrint3Floats(17, ctx->fp16.f.f_even, ctx->fp18.f.f_odd, ctx->fp18.f.f_even); crashPrint3Floats(20, ctx->fp20.f.f_odd, ctx->fp20.f.f_even, ctx->fp22.f.f_odd); crashPrint3Floats(23, ctx->fp22.f.f_even, ctx->fp24.f.f_odd, ctx->fp24.f.f_even); crashPrint3Floats(26, ctx->fp26.f.f_odd, ctx->fp26.f.f_even, ctx->fp28.f.f_odd); crashPrint3Floats(29, ctx->fp28.f.f_even, ctx->fp30.f.f_odd, ctx->fp30.f.f_even); } // Print integer registers crashPrint("at 0x%016llx v0 0x%016llx v1 0x%016llx\n", ctx->at, ctx->v0, ctx->v1); crashPrint("a0 0x%016llx a1 0x%016llx a2 0x%016llx\n", ctx->a0, ctx->a1, ctx->a2); crashPrint("a3 0x%016llx t0 0x%016llx t1 0x%016llx\n", ctx->a3, ctx->t0, ctx->t1); crashPrint("t2 0x%016llx t3 0x%016llx t4 0x%016llx\n", ctx->t2, ctx->t3, ctx->t4); crashPrint("t5 0x%016llx t6 0x%016llx t7 0x%016llx\n", ctx->t5, ctx->t6, ctx->t7); crashPrint("s0 0x%016llx s1 0x%016llx s2 0x%016llx\n", ctx->s0, ctx->s1, ctx->s2); crashPrint("s3 0x%016llx s4 0x%016llx s5 0x%016llx\n", ctx->s3, ctx->s4, ctx->s5); crashPrint("s6 0x%016llx s7 0x%016llx t8 0x%016llx\n", ctx->s6, ctx->s7, ctx->t8); crashPrint("t9 0x%016llx gp 0x%016llx sp 0x%016llx\n", ctx->t9, ctx->gp, ctx->sp); crashPrint("s8 0x%016llx ra 0x%016llx\n", ctx->s8, ctx->ra); crashPrint("TID %d epc %08x caus %08x fp %08x badv %08x sr %08x\n", thread->id, ctx->pc, ctx->cause, ctx->fpcsr, ctx->badvaddr, ctx->sr); // Print the address of the faulted instruction, along with the instruction // itself and the next three - presumably to help the developer locate it. crashPrint("dshex -a %08x %08x %08x %08x %08x\n", ctx->pc, ((u32 *)ctx->pc)[0], ((u32 *)ctx->pc)[1], ((u32 *)ctx->pc)[2], ((u32 *)ctx->pc)[3]); crashPrintDescription(ctx->cause, "cause", g_CrashCauseDescriptions); crashPrint(" : "); crashPrintDescription(ctx->fpcsr, "fpcsr", g_CrashFpcsrDescriptions); crashPrint("\n"); // Print a proper stack trace i = 0; done = false; sp = (u32 *)ctx->sp; stackend = (u32 *) crashGetStackEnd((u32)sp, thread->id); stackstart = (u32 *) crashGetStackStart((u32)sp, thread->id); ptr = ctx->pc; *tracelen = 0; crashPrint("nearl: "); while (!done) { sp = (u32 *) crashGetParentStackFrame((u32 *) ptr, &_libSegmentStart, (u32)sp, regs); crashPrint(" %08x ", ptr); callstack[*tracelen] = ptr; *tracelen = *tracelen + 1; if (i == 4) { crashPrint("\n "); } if (sp == NULL) { sp = (u32 *)ctx->sp; } if (regs[31] == 0) { ptr = ctx->ra; } else { ptr = *(u32 *)(regs[31]); } if (sp < stackstart || sp >= stackend || stackend == &sp[4] || ptr == 0) { break; } done = i >= 9; i++; } crashPrint("\n"); crashPrint("\n"); return 0; } void crashPrintDescription(u32 mask, char *label, struct crashdescription *description) { bool first = true; s32 i; crashPrint("%s <", label); while (description->mask != 0) { if ((description->mask & mask) == description->value) { if (first) { first = false; } else { crashPrint(","); } crashPrint("%s", description->text); } description++; } crashPrint(">"); } void crashPutChar(s32 x, s32 y, char c) { if (c == '\t' || c == '\n') { c = '\0'; } if ((c > '\0' && c < ' ') || c > '~') { c = '?'; } if (x >= 0 && x < 72 && y >= 0 && y < 30 && g_CrashCharBuffer != NULL) { g_CrashCharBuffer[y][x] = c; } } void crashAppendChar(char c) { if (c == '\0') { return; } if (c == '\t') { do { crashAppendChar(' '); } while (g_CrashCurX & 7); return; } if (c == '\n') { g_CrashCurY++; g_CrashCurX = 0; } if (g_CrashCurY > 28) { crashScroll(g_CrashCurY - 28); g_CrashCurY = 28; } if (c != '\n') { crashPutChar(g_CrashCurX, g_CrashCurY, c); g_CrashCurX++; if (g_CrashCurX >= 71) { g_CrashCurX = 0; g_CrashCurY++; } } } void crashScroll(s32 numlines) { s32 i; s32 y; s32 x; if (g_CrashCharBuffer == NULL) { return; } while (numlines-- > 0) { for (y = 0; y < 29; y++) { for (x = 0; x < 71; x++) { g_CrashCharBuffer[y][x] = g_CrashCharBuffer[y + 1][x]; } } } } void func0000cdc8(s32 x, s32 y, char c) { s32 i; s32 j; s32 width; u16 *fbpos; bool hires; s32 tmp; u32 a2; width = viGetWidth(); if (c == '\0') { c = ' '; } if (c < ' ' || c > '~') { return; } hires = (width == 640); if (hires) { fbpos = g_CrashFrameBuffer + x * 2 + y * width; } else { fbpos = g_CrashFrameBuffer + x + y * width; } a2 = 0; for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { // "special" occurs every 32nd pixel u32 special = a2 & 0x80000000; if (special) { fbpos[0] = 0x7bdf; } else { fbpos[0] = 1; } if (hires) { if (special) { fbpos[1] = 0x7bdf; } else { fbpos[1] = 1; } } fbpos++; if (hires) { fbpos++; } a2 *= 2; } if (hires) { fbpos += width; fbpos -= 8; } else { fbpos += width; fbpos -= 4; } } } void crashReset(void) { g_CrashCharBuffer = NULL; if (g_CrashCharBuffer) { // Unreachable s32 x; s32 y; for (y = 0; y < 30; y++) { for (x = 0; x < 71; x++) { g_CrashCharBuffer[y][x] = '\0'; } } } } void func0000cf54(u16 *fb) { s32 width; s32 height; s32 x; s32 y; g_CrashFrameBuffer = (u16 *) PHYS_TO_K1(fb); width = (viGetWidth() - 13) / 4; height = (viGetHeight() - 10) / 7 - 1; if (g_CrashCharBuffer != NULL) { for (y = 0; y < height && y < 29; y++) { for (x = 0; x < width - 5 && x < 71; x++) { func0000cdc8(20 + x * 4, 7 + y * 7, g_CrashCharBuffer[y][x]); } } } }