ARM11 exception handlers (not working yet, it fails to retrieve the data after the reboot)

Uncomment the appropriate line in firm.c to test.
This commit is contained in:
TuxSH 2016-06-02 22:33:44 +02:00
parent b77d619873
commit 2d7dde9cf9
14 changed files with 375 additions and 25 deletions

View File

@ -18,6 +18,7 @@ dir_source := source
dir_patches := patches
dir_loader := loader
dir_arm9_exceptions := exceptions/arm9
dir_arm11_exceptions := exceptions/arm11
dir_screeninit := screeninit
dir_injector := injector
dir_mset := CakeHax
@ -34,7 +35,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.s *.c)))
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/injector.h $(dir_build)/loader.h $(dir_build)/screeninit.h
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/arm11_exceptions.h $(dir_build)/injector.h $(dir_build)/loader.h $(dir_build)/screeninit.h
.PHONY: all
all: launcher a9lh ninjhax
@ -56,6 +57,7 @@ clean:
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
@$(MAKE) -C $(dir_loader) clean
@$(MAKE) -C $(dir_arm9_exceptions) clean
@$(MAKE) -C $(dir_arm11_exceptions) clean
@$(MAKE) -C $(dir_screeninit) clean
@$(MAKE) -C $(dir_injector) clean
@rm -rf $(dir_out) $(dir_build)
@ -63,6 +65,7 @@ clean:
$(dir_out):
@mkdir -p "$(dir_out)/luma/payloads"
@mkdir -p "$(dir_out)/luma/dumps/arm9"
@mkdir -p "$(dir_out)/luma/dumps/arm11"
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
@ -108,6 +111,10 @@ $(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile
@$(MAKE) -C $(dir_arm9_exceptions)
@bin2c -o $@ -n arm9_exceptions $(@D)/arm9_exceptions.bin
$(dir_build)/arm11_exceptions.h: $(dir_arm11_exceptions)/Makefile
@$(MAKE) -C $(dir_arm11_exceptions)
@bin2c -o $@ -n arm11_exceptions $(@D)/arm11_exceptions.bin
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
@$(MAKE) -C $(dir_screeninit)
@bin2c -o $@ -n screeninit $(@D)/screeninit.bin

47
exceptions/arm11/Makefile Normal file
View File

@ -0,0 +1,47 @@
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/3ds_rules
CC := arm-none-eabi-gcc
AS := arm-none-eabi-as
LD := arm-none-eabi-ld
OC := arm-none-eabi-objcopy
name := arm11_exceptions
dir_source := source
dir_build := build
ASFLAGS := -mcpu=mpcore -mfpu=vfp
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
LDFLAGS := -nostdlib
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
$(call rwildcard, $(dir_source), *.s *.c)))
.PHONY: all
all: ../../$(dir_build)/$(name).bin
.PHONY: clean
clean:
@rm -rf $(dir_build)
../../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
$(OC) -S -O binary $< $@
$(dir_build)/$(name).elf: $(objects)
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
$(dir_build)/%.o: $(dir_source)/%.c
@mkdir -p "$(@D)"
$(COMPILE.c) $(OUTPUT_OPTION) $<
$(dir_build)/%.o: $(dir_source)/%.s
@mkdir -p "$(@D)"
$(COMPILE.s) $(OUTPUT_OPTION) $<
include $(call rwildcard, $(dir_build), *.d)

View File

@ -0,0 +1,11 @@
ENTRY(_start)
SECTIONS
{
. = 0;
.text.start : { *(.text.start) }
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss COMMON) }
.rodata : { *(.rodata) }
. = ALIGN(4);
}

View File

@ -0,0 +1,14 @@
/*
* handlers.h
* by TuxSH
*
* This is part of Luma3DS, see LICENSE.txt for details
*/
#pragma once
void __attribute__((noreturn)) mcuReboot(void);
void FIQHandler(void);
void undefinedInstructionHandler(void);
void dataAbortHandler(void);
void prefetchAbortHandler(void);

View File

@ -0,0 +1,95 @@
@
@ handlers.s
@ by TuxSH
@
@ This is part of Luma3DS, see LICENSE.txt for details
@
.macro GEN_HANDLER name
.global \name
.type \name, %function
\name:
ldr sp, =#0xffff3000
stmfd sp!, {r0-r7}
mov r1, #\@ @ macro expansion counter
b _commonHandler
.size \name, . - \name
.endm
.text
.arm
.align 4
.global _commonHandler
.type _commonHandler, %function
_commonHandler:
clrex
cpsid aif
mrs r2, spsr
mov r6, sp
mrs r3, cpsr
ands r4, r2, #0xf @ get the mode that triggered the exception
moveq r4, #0xf @ usr => sys
bic r5, r3, #0xf
orr r5, r4
msr cpsr_c, r5 @ change processor mode
stmfd r6!, {r8-lr}
msr cpsr_c, r3 @ restore processor mode
mov sp, r6
vmrs r3, fpexc
cmp r1, #1
bne noFPUInit
tst r5, #0x20
bne noFPUInit
ldr r4, [lr, #-4]
lsl r4, #4
sub r4, #0xc0000000
cmp r4, #0x30000000
bcs noFPUInit
tst r3, #0x40000000
bne noFPUInit
sub lr, #4
srsfd sp!, #0x13
add sp, #28 @ restore context
ldmfd sp!, {r0-r7}
cps #0x13 @ FPU init
stmfd sp, {r0-r3, r11-lr}^
sub sp, #0x20
bl . @ will be replaced
ldmfd sp, {r0-r3, r11-lr}^
add sp, #0x20
rfefd sp!
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
b mainHandler
GEN_HANDLER FIQHandler
GEN_HANDLER undefinedInstructionHandler
GEN_HANDLER prefetchAbortHandler
GEN_HANDLER dataAbortHandler
.global mcuReboot
.type mcuReboot, %function
mcuReboot:
b . @ will be replaced

View File

@ -0,0 +1,71 @@
/*
* mainHandler.c
* by TuxSH
*
* This is part of Luma3DS, see LICENSE.txt for details
*/
#include "types.h"
#include "handlers.h"
#define FINAL_BUFFER 0xE5000000 //0x25000000
#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)
{
u32 dump[(40 + REG_DUMP_SIZE + CODE_DUMP_SIZE) / 4];
vu32 *final = (vu32 *)FINAL_BUFFER;
while(final[0] == 0xDEADC0DE && final[1] == 0xDEADCAFE && ((final[3] & 0xFFFF) == 9 || (final[3] & 0xFFFF) == 11));
dump[0] = 0xDEADC0DE; //Magic
dump[1] = 0xDEADCAFE; //Magic
dump[2] = (1 << 16) | 0; //Dump format version number
dump[3] = ((cpuId & 0xf) << 16) | 11; //Processor
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
u32 *regdump = dump + 10;
u32 cpsr = regs[0];
u32 pc = regs[1] - ((type < 3) ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
regdump[15] = pc;
regdump[16] = cpsr;
regdump[17] = fpexc;
for(u32 i = 0; i < 7; i++)
regdump[8 + i] = regs[2 + i];
for(u32 i = 0; i < 8; i++)
regdump[i] = regs[9 + i];
//Dump code
u16 *codedump = (u16 *)(regdump + dump[6] / 4);
u16 *instr = (u16 *)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);
mcuReboot();
}

View File

@ -0,0 +1,11 @@
.section .text.start
.align 4
.global _start
_start:
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
handlers:
.word FIQHandler
.word undefinedInstructionHandler
.word prefetchAbortHandler
.word dataAbortHandler

View File

@ -0,0 +1,13 @@
#pragma once
#include <stdint.h>
//Common data types
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef volatile u8 vu8;
typedef volatile u16 vu16;
typedef volatile u32 vu32;
typedef volatile u64 vu64;

View File

@ -67,7 +67,7 @@ def makeRegisterLine(A, rA, B, rB):
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr")
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr", "fpexc")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
@ -80,23 +80,31 @@ if __name__ == "__main__":
processor, exceptionType, _, _, codeDumpSize, stackDumpSize = unpack_from("<6I", data, 12)
print("Processor: ARM{0}".format(processor))
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 = unpack_from("<17I", data, 40)
registers = []
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[-1], "{0:08x}".format(registers[-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]))
codeDump = data[40+4*17 : 40+4*17 + codeDumpSize]
codeDump = data[40+4*len(registers) : 40+4*len(registers) + 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*17 + codeDumpSize
stackOffset = 40+4*len(registers) + codeDumpSize
stackDump = data[stackOffset : stackOffset + stackDumpSize]
print("\nStack dump:\n")
print(hexdump(registers[13], stackDump))

View File

@ -11,6 +11,8 @@
#include "i2c.h"
#include "utils.h"
#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)
@ -55,6 +57,40 @@ void installArm9Handlers(void)
}
}
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddr)
{
u32 *initFPU;
for(initFPU = exceptionsPage; initFPU < (exceptionsPage + 0x400) && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU += 1);
u32 *mcuReboot;
for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot += 1);
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);
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
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_size - 20); pos++)
{
switch(*pos)
{
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)
default: break;
}
}
}
static void hexItoa(u32 n, char *out)
{
const char hexDigits[] = "0123456789ABCDEF";
@ -77,29 +113,43 @@ void detectAndProcessExceptionDumps(void)
const char *registerNames[] = {
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
"SP", "LR", "PC", "CPSR"
"SP", "LR", "PC", "CPSR", "FPEXC"
};
char hexstring[] = "00000000";
vu32 *dump = (u32 *)0x25000000;
vu32 *dump = (vu32 *)0x25000000;
if(dump[0] == 0xDEADC0DE && dump[1] == 0xDEADCAFE && dump[3] == 9)
if(dump[0] == 0xDEADC0DE && dump[1] == 0xDEADCAFE && (dump[3] == 9 || (dump[3] & 0xFFFF) == 11))
{
char path[41] = "/luma/dumps/arm9";
char path9[41] = "/luma/dumps/arm9";
char path11[42] = "/luma/dumps/arm11";
char fileName[] = "crash_dump_00000000.dmp";
findDumpFile(path, fileName);
if(dump[3] == 9)
{
findDumpFile(path9, fileName);
path9[16] = '/';
memcpy(&path9[17], fileName, sizeof(fileName));
fileWrite((void *)dump, path9, dump[5]);
}
path[16] = '/';
memcpy(&path[17], fileName, sizeof(fileName));
else
{
findDumpFile(path11, fileName);
path11[17] = '/';
memcpy(&path11[18], fileName, sizeof(fileName));
fileWrite((void *)dump, path11, dump[5]);
}
fileWrite((void *)dump, path, dump[5]);
char arm11Str[] = "Processor: ARM11 (core X)";
if((dump[3] & 0xFFFF) == 11) arm11Str[28] = '0' + (char)(dump[3] >> 16);
initScreens();
drawString("An exception occurred", 10, 10, COLOR_RED);
int posY = drawString("Processor: ARM9", 10, 30, COLOR_WHITE) + SPACING_Y;
int posY = drawString(((dump[3] & 0xFFFF) == 11) ? arm11Str : "Processor: ARM9", 10, 30, COLOR_WHITE) + SPACING_Y;
posY = drawString("Exception type: ", 10, posY, COLOR_WHITE);
posY = drawString(handledExceptionNames[dump[4]], 10 + 16 * SPACING_X, posY, COLOR_WHITE);
@ -110,7 +160,7 @@ void detectAndProcessExceptionDumps(void)
hexItoa(dump[10 + i], hexstring);
posY = drawString(hexstring, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
if(i != 16)
if(dump[3] != 9 || i != 16)
{
posY = drawString(registerNames[i + 1], 10 + 22 * SPACING_X, posY, COLOR_WHITE);
hexItoa(dump[10 + i + 1], hexstring);
@ -122,7 +172,7 @@ void detectAndProcessExceptionDumps(void)
posY += 2 * SPACING_Y;
posY = drawString("You can find a dump in the following file:", 10, posY, COLOR_WHITE) + SPACING_Y;
posY = drawString(path, 10, posY, COLOR_WHITE) + 2 * 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();

View File

@ -8,4 +8,5 @@
#include "types.h"
void installArm9Handlers(void);
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddr);
void detectAndProcessExceptionDumps(void);

View File

@ -332,6 +332,11 @@ static inline void patchNativeFirm(u32 nandType, u32 emuHeader, u32 a9lhMode)
//Apply UNITINFO patch
if(DEVMODE == 2) patchUnitInfoValueSet(arm9Section, section[2].size);
//Install arm11 exception handlers
u32 stackAddress;
u32 *exceptionsPage = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &stackAddress);
//installArm11Handlers(exceptionsPage, stackAddress);
//Make FCRAM (and VRAM as a side effect) globally executable from arm11 kernel
patchKernelFCRAMAndVRAMMappingPermissions(arm11Section1, section[1].size);
}

View File

@ -7,6 +7,8 @@
#include "config.h"
#include "../build/rebootpatch.h"
static u32 *exceptionsPage = NULL;
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
{
u8 *off = memsearch(pos, "ess9", size, 4);
@ -18,6 +20,20 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
}
u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr)
{
//This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways)
const u8 callExceptionDispatcherPattern[] = {0x0F, 0x00, 0xBD, 0xE8, 0x13, 0x00, 0x02, 0xF1};
const u8 exceptionsPagePattern[] = {0x00, 0xB0, 0x9C, 0xE5};
*stackAddr = *((u32 *)memsearch(pos, callExceptionDispatcherPattern, size, 8) + 3);
if(exceptionsPage == NULL) exceptionsPage = (u32 *)memsearch(pos, exceptionsPagePattern, size, 4) - 0xB;
return exceptionsPage;
}
void patchSignatureChecks(u8 *pos, u32 size)
{
const u16 sigPatch[2] = {0x2000, 0x4770};
@ -152,7 +168,7 @@ void reimplementSvcBackdoor(u8 *pos, u32 size)
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5}; //cpsid aif
u32 *exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
if(exceptionsPage == NULL) exceptionsPage = (u32 *)memsearch(pos, pattern, size, 4) - 0xB;
u32 svcOffset = (-((exceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
u32 *svcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address

View File

@ -16,6 +16,7 @@ typedef struct patchData {
} patchData;
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
u32* getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *stackAddr);
void patchSignatureChecks(u8 *pos, u32 size);
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);