diff --git a/exceptions/arm11/source/handlers.h b/exceptions/arm11/source/handlers.h index 33da59b..f0875f5 100644 --- a/exceptions/arm11/source/handlers.h +++ b/exceptions/arm11/source/handlers.h @@ -8,6 +8,8 @@ #pragma once void __attribute__((noreturn)) mcuReboot(void); +void clearDCacheAndDMB(void); + void FIQHandler(void); void undefinedInstructionHandler(void); void dataAbortHandler(void); diff --git a/exceptions/arm11/source/handlers.s b/exceptions/arm11/source/handlers.s index 6378b03..61e9e23 100644 --- a/exceptions/arm11/source/handlers.s +++ b/exceptions/arm11/source/handlers.s @@ -68,16 +68,6 @@ _commonHandler: noFPUInit: stmfd sp!, {r2,lr} @ it's a bit of a mess, but we will fix that later @ order of saved regs now: cpsr, pc + (2/4/8), r8-r14, r0-r7 - - ldr r4, =#0xdfff3ffc - ldr r5, =#0xffff0014 - ldr r5, [r5] @ 0xeafffffe - mov r6, #0 - poisonLoop: - str r5, [r4, #4]! @ poison exception vectors in order to hang the other threads - add r6, #1 - cmp r6, #8 - blt poisonLoop mov r0, sp mrc p15,0,r2,c0,c0,5 @ CPU ID register @@ -93,3 +83,11 @@ GEN_HANDLER dataAbortHandler .type mcuReboot, %function mcuReboot: b . @ will be replaced + +.global clearDCacheAndDMB +.type clearDCacheAndDMB, %function +clearDCacheAndDMB: + mov r0, #0 + mcr p15,0,r0,c7,c14,0 @ Clean and Invalidate Entire Data Cache + mcr p15,0,r0,c7,c10,4 @ Drain Memory Barrier + bx lr diff --git a/exceptions/arm11/source/mainHandler.c b/exceptions/arm11/source/mainHandler.c index 103c4db..9704266 100644 --- a/exceptions/arm11/source/mainHandler.c +++ b/exceptions/arm11/source/mainHandler.c @@ -12,7 +12,6 @@ #define REG_DUMP_SIZE (4*18) #define CODE_DUMP_SIZE 48 -#define STACK_DUMP_SIZE 0x2000 #define OTHER_DATA_SIZE 0 void __attribute__((noreturn)) mainHandler(u32 regs[18], u32 type, u32 cpuId, u32 fpexc) @@ -29,9 +28,7 @@ void __attribute__((noreturn)) mainHandler(u32 regs[18], u32 type, u32 cpuId, u3 dump[4] = type; //Exception type dump[6] = REG_DUMP_SIZE; //Register dump size (r0-r12, sp, lr, pc, cpsr, fpexc) dump[7] = CODE_DUMP_SIZE; //Code dump size (10 ARM instructions, up to 20 Thumb instructions). - dump[8] = STACK_DUMP_SIZE; //Stack dump size dump[9] = OTHER_DATA_SIZE; //Other data size - dump[5] = 40 + REG_DUMP_SIZE + CODE_DUMP_SIZE + STACK_DUMP_SIZE + OTHER_DATA_SIZE; //Total size //Dump registers //Current order of saved regs: cpsr, pc, r8-r12, sp, lr, r0-r7 @@ -50,22 +47,24 @@ void __attribute__((noreturn)) mainHandler(u32 regs[18], u32 type, u32 cpuId, u3 for(u32 i = 0; i < 8; i++) regdump[i] = regs[9 + i]; + dump[8] = 0x1000 - (regdump[13] & 0xfff); //Stack dump size (max. 0x1000 bytes) + dump[5] = 40 + REG_DUMP_SIZE + CODE_DUMP_SIZE + dump[8] + OTHER_DATA_SIZE; //Total size + //Dump code u16 *codedump = (u16 *)(regdump + dump[6] / 4); - u16 *instr = (u16 *)pc - dump[7] / 2 + 1; + vu16 *instr = (vu16 *)pc - dump[7] / 2 + 1; for(u32 i = 0; i < dump[7] / 2; i++) codedump[i] = instr[i]; //Dump stack in place vu32 *sp = (vu32 *)regdump[13]; vu32 *stackdump = (vu32 *)((vu8 *)FINAL_BUFFER + 40 + REG_DUMP_SIZE + CODE_DUMP_SIZE); - for(u32 i = 0; i < dump[8] / 4; i++) stackdump[i] = sp[i]; for(u32 i = 0; i < (40 + REG_DUMP_SIZE + CODE_DUMP_SIZE) / 4; i++) final[i] = dump[i]; - while(final[0] != 0xDEADC0DE); + clearDCacheAndDMB(); mcuReboot(); } diff --git a/exceptions/arm9/linker.ld b/exceptions/arm9/linker.ld index b27d4e0..fc25c15 100644 --- a/exceptions/arm9/linker.ld +++ b/exceptions/arm9/linker.ld @@ -1,7 +1,7 @@ ENTRY(_start) SECTIONS { - . = 0x01FF8000; + . = 0x01FF7FE0; .text.start : { *(.text.start) } .text : { *(.text) } .data : { *(.data) } diff --git a/exceptions/arm9/source/handlers.s b/exceptions/arm9/source/handlers.s index af6e6eb..a59086b 100644 --- a/exceptions/arm9/source/handlers.s +++ b/exceptions/arm9/source/handlers.s @@ -9,6 +9,9 @@ .global \name .type \name, %function \name: + ldr sp, =#0x02000000 @ We make the (full descending) stack point to the end of ITCM for our exception handlers. + @ It doesn't matter if we're overwriting stuff here, since we're going to reboot. + stmfd sp!, {r0-r7} @ FIQ has its own r8-r14 regs ldr r1, =\@ @ macro expansion counter b _commonHandler diff --git a/exceptions/arm9/source/mainHandler.c b/exceptions/arm9/source/mainHandler.c index d201a0d..2e07225 100644 --- a/exceptions/arm9/source/mainHandler.c +++ b/exceptions/arm9/source/mainHandler.c @@ -13,13 +13,12 @@ #define REG_DUMP_SIZE (4*17) #define CODE_DUMP_SIZE 48 -#define STACK_DUMP_SIZE 0x2000 #define OTHER_DATA_SIZE 0 void __attribute__((noreturn)) mainHandler(u32 regs[17], u32 type) { //vu32 *dump = (u32 *)TEMP_BUFFER; - u32 dump[(40 + REG_DUMP_SIZE + CODE_DUMP_SIZE + STACK_DUMP_SIZE + OTHER_DATA_SIZE) / 4]; + u32 dump[(40 + REG_DUMP_SIZE + CODE_DUMP_SIZE) / 4]; dump[0] = 0xDEADC0DE; //Magic dump[1] = 0xDEADCAFE; //Magic @@ -28,9 +27,7 @@ void __attribute__((noreturn)) mainHandler(u32 regs[17], u32 type) dump[4] = type; //Exception type dump[6] = REG_DUMP_SIZE; //Register dump size (r0-r12, sp, lr, pc, cpsr) dump[7] = CODE_DUMP_SIZE; //Code dump size (10 ARM instructions, up to 20 Thumb instructions). - dump[8] = STACK_DUMP_SIZE; //Stack dump size dump[9] = OTHER_DATA_SIZE; //Other data size - dump[5] = sizeof(dump); //Total size //Dump registers //Current order of saved regs: cpsr, pc, r8-r12, sp, lr, r0-r7 @@ -48,22 +45,23 @@ void __attribute__((noreturn)) mainHandler(u32 regs[17], u32 type) for(u32 i = 0; i < 8; i++) regdump[i] = regs[9 + i]; + dump[8] = 0x1000 - (regdump[13] & 0xfff); //Stack dump size (max. 0x1000 bytes) + dump[5] = 40 + REG_DUMP_SIZE + CODE_DUMP_SIZE + dump[8] + OTHER_DATA_SIZE; //Total size + //Dump code - vu16 *codedump = (vu16 *)(regdump + dump[6] / 4); + u16 *codedump = (u16 *)(regdump + dump[6] / 4); vu16 *instr = (vu16 *)pc - dump[7] / 2 + 1; for(u32 i = 0; i < dump[7] / 2; i++) codedump[i] = instr[i]; //Dump stack vu32 *sp = (vu32 *)regdump[13]; - vu32 *stackdump = (vu32 *)(codedump + dump[7] / 2); - /* Homebrew/CFW set their stack at 0x27000000, but we'd better not make any assumption here - as it breaks things it seems */ + vu32 *stackdump = (vu32 *)((vu8 *)FINAL_BUFFER + 40 + REG_DUMP_SIZE + CODE_DUMP_SIZE); for(u32 i = 0; i < dump[8] / 4; i++) stackdump[i] = sp[i]; - vu32 *final = (u32 *)FINAL_BUFFER; - for(u32 i = 0; i < sizeof(dump) / 4; i++) + vu32 *final = (vu32 *)FINAL_BUFFER; + for(u32 i = 0; i < (40 + REG_DUMP_SIZE + CODE_DUMP_SIZE) / 4; i++) final[i] = dump[i]; i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot diff --git a/exceptions/exception_dump_parser.py b/exceptions/exception_dump_parser.py index 1432fab..e2f7c65 100644 --- a/exceptions/exception_dump_parser.py +++ b/exceptions/exception_dump_parser.py @@ -78,33 +78,25 @@ if __name__ == "__main__": if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe): raise SystemExit("Invalid file format") - processor, exceptionType, _, _, codeDumpSize, stackDumpSize = unpack_from("<6I", data, 12) + processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize = unpack_from("<6I", data, 12) + nbRegisters //= 4 if processor == 9: print("Processor: ARM9") else: print("Processor: ARM11 (core {0})".format(processor >> 16)) print("Exception type: {0}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType])) - registers = [] + registers = unpack_from("<{0}I".format(nbRegisters), data, 40) print("\nRegister dump:\n") - if processor == 9: - registers = unpack_from("<17I", data, 40) - for i in range(0, 16, 2): - print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1])) - print("{0:<15}{1:<20}".format(registerNames[-2], "{0:08x}".format(registers[-1]))) - else: - registers = unpack_from("<18I", data, 40) - for i in range(0, 18, 2): - print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1])) + for i in range(0, nbRegisters - (nbRegisters % 2), 2): + print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1])) + if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[-2], "{0:08x}".format(registers[-1]))) - codeDump = data[40+4*len(registers) : 40+4*len(registers) + codeDumpSize] + codeDump = data[40 + 4 * nbRegisters : 40 + 4 * nbRegisters + codeDumpSize] print("\nCode dump:\n") print(hexdump(registers[15] - codeDumpSize + 2, codeDump)) - # Homebrew/CFW set their stack at 0x27000000, let's detect it - if 0 <= 0x27000000 - registers[13] <= stackDumpSize: stackDumpSize = 0x27000000 - registers[13] - - stackOffset = 40+4*len(registers) + codeDumpSize + stackOffset = 40 + 4*nbRegisters + codeDumpSize stackDump = data[stackOffset : stackOffset + stackDumpSize] print("\nStack dump:\n") print(hexdump(registers[13], stackDump)) diff --git a/source/exceptions.c b/source/exceptions.c index 98a4617..2cfb5f7 100644 --- a/source/exceptions.c +++ b/source/exceptions.c @@ -13,47 +13,20 @@ #include "../build/arm9_exceptions.h" #include "../build/arm11_exceptions.h" - -#define _U __attribute__((unused)) //Silence "unused parameter" warnings -static void __attribute__((naked)) setupStack(_U u32 mode, _U void* SP) -{ - __asm__ volatile( - "cmp r0, #0 \n" - "moveq r0, #0xf @ usr => sys \n" - "mrs r2, cpsr \n" - "bic r3, r2, #0xf \n" - "orr r3, r0 @ processor mode \n" - "msr cpsr_c, r3 @ change processor mode \n" - "mov sp, r1 \n" - "msr cpsr_c, r2 @ restore processor mode \n" - "bx lr \n" - ); -} -#undef _U - void installArm9Handlers(void) { - void *payloadAddress = (void *)0x01FF8000; - u32 *handlers = (u32 *)payloadAddress + 1; - - void* SP = (void *)0x02000000; //We make the (full descending) stack point to the end of ITCM for our exception handlers. - //It doesn't matter if we're overwriting stuff, since we're going to reboot. - - memcpy(payloadAddress, arm9_exceptions, arm9_exceptions_size); - - setupStack(1, SP); //FIQ - setupStack(7, SP); //Abort - setupStack(11, SP); //Undefined - + void *payloadAddress = (void *)0x01FF8000; const u32 offsets[] = {0x08, 0x18, 0x20, 0x28}; + memcpy(payloadAddress, arm9_exceptions + 32, arm9_exceptions_size - 32); + //IRQHandler is at 0x08000000, but we won't handle it for some reasons //svcHandler is at 0x08000010, but we won't handle svc either for(u32 i = 0; i < 4; i++) { *(vu32 *)(0x08000000 + offsets[i]) = 0xE51FF004; - *(vu32 *)(0x08000000 + offsets[i] + 4) = handlers[i]; + *(vu32 *)(0x08000000 + offsets[i] + 4) = *((const u32 *)arm9_exceptions + 1 + i); } } @@ -67,25 +40,25 @@ void installArm11Handlers(u32 *exceptionsPage, u32 stackAddr) u32 *mcuReboot; for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot += 1); + mcuReboot--; u32 *freeSpace; for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace += 1); - //freeSpace += 4 - ((u32)(freeSpace - exceptionsPage) & 3); - memcpy(freeSpace, arm11_exceptions + 20, arm11_exceptions_size - 20); + memcpy(freeSpace, arm11_exceptions + 32, arm11_exceptions_size - 32); - exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 8) - 20); //Undefined Instruction - exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 12) - 20); //Prefetch Abort - exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 16) - 20); //Data Abort - exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 4) - 20); //FIQ + exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 8) - 32); //Undefined Instruction + exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 12) - 32); //Prefetch Abort + exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 16) - 32); //Data Abort + exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 4) - 32); //FIQ - for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_size - 20); pos++) + for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_size - 32); pos++) { - switch(*pos) + switch(*pos) //Perform relocations { case 0xFFFF3000: *pos = stackAddr; break; case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break; case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break; - case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 20; break; // bx r12 (mainHandler) + case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; // bx r12 (mainHandler) default: break; } } @@ -171,13 +144,23 @@ void detectAndProcessExceptionDumps(void) } posY += 2 * SPACING_Y; + + u32 mode = dump[40 + 16] & 0xF; + if(dump[4] == 3 && (mode == 7 || mode == 11)) + posY = drawString("Incorrect dump: failed to dump code and/or stack", 10, posY, 0x00FFFF) + 2 * SPACING_Y; //in yellow + posY = drawString("You can find a dump in the following file:", 10, posY, COLOR_WHITE) + SPACING_Y; posY = drawString((dump[3] == 9) ? path9 : path11, 10, posY, COLOR_WHITE) + 2 * SPACING_Y; drawString("Press any button to shutdown", 10, posY, COLOR_WHITE); waitInput(); - - i2cWriteRegister(I2C_DEV_MCU, 0x20, 1); + + *(vu32 *)0x25000000 = 0; //Make sure we won't detect a corrupted exception dump after we've rebooted. It doesn't seem to be sufficient though + *(vu32 *)0x25000004 = 0; + + clearScreens(); + + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1); //Shutdown while(1); } } \ No newline at end of file diff --git a/source/firm.c b/source/firm.c index 6a7bbd6..3405af1 100755 --- a/source/firm.c +++ b/source/firm.c @@ -335,7 +335,7 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode) //Install arm11 exception handlers u32 stackAddress; u32 *exceptionsPage = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress); - //installArm11Handlers(exceptionsPage, stackAddress); + installArm11Handlers(exceptionsPage, stackAddress); //Make FCRAM (and VRAM as a side effect) globally executable from arm11 kernel patchKernelFCRAMAndVRAMMappingPermissions(arm11Section1, section[1].size);