Add ARM9 exception vectors feature from @TuxSH
This commit is contained in:
parent
efd08ff731
commit
f0e1937eeb
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ build
|
|||||||
loader/build
|
loader/build
|
||||||
screeninit/build
|
screeninit/build
|
||||||
injector/build
|
injector/build
|
||||||
|
exceptions/arm9/build
|
||||||
*.bin
|
*.bin
|
||||||
*.3dsx
|
*.3dsx
|
||||||
*.smdh
|
*.smdh
|
||||||
|
10
Makefile
10
Makefile
@ -17,6 +17,7 @@ version := $(shell git describe --abbrev=0 --tags)
|
|||||||
dir_source := source
|
dir_source := source
|
||||||
dir_patches := patches
|
dir_patches := patches
|
||||||
dir_loader := loader
|
dir_loader := loader
|
||||||
|
dir_arm9_exceptions := exceptions/arm9
|
||||||
dir_screeninit := screeninit
|
dir_screeninit := screeninit
|
||||||
dir_injector := injector
|
dir_injector := injector
|
||||||
dir_mset := CakeHax
|
dir_mset := CakeHax
|
||||||
@ -33,7 +34,7 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
|||||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||||
|
|
||||||
bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/screeninit.h
|
bundled = $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/arm9_exceptions.h $(dir_build)/screeninit.h
|
||||||
|
|
||||||
.PHONY: all
|
.PHONY: all
|
||||||
all: launcher a9lh ninjhax
|
all: launcher a9lh ninjhax
|
||||||
@ -55,12 +56,14 @@ clean:
|
|||||||
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||||
@$(MAKE) -C $(dir_loader) clean
|
@$(MAKE) -C $(dir_loader) clean
|
||||||
|
@$(MAKE) -C $(dir_arm9_exceptions) clean
|
||||||
@$(MAKE) -C $(dir_screeninit) clean
|
@$(MAKE) -C $(dir_screeninit) clean
|
||||||
@$(MAKE) -C $(dir_injector) clean
|
@$(MAKE) -C $(dir_injector) clean
|
||||||
@rm -rf $(dir_out) $(dir_build)
|
@rm -rf $(dir_out) $(dir_build)
|
||||||
|
|
||||||
$(dir_out):
|
$(dir_out):
|
||||||
@mkdir -p "$(dir_out)/luma/payloads"
|
@mkdir -p "$(dir_out)/luma/payloads"
|
||||||
|
@mkdir -p "$(dir_out)/luma/dumps/arm9"
|
||||||
|
|
||||||
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
||||||
@ -96,6 +99,11 @@ $(dir_build)/loader.h: $(dir_loader)/Makefile
|
|||||||
@mv $(dir_loader)/loader.bin $(@D)
|
@mv $(dir_loader)/loader.bin $(@D)
|
||||||
@bin2c -o $@ -n loader $(@D)/loader.bin
|
@bin2c -o $@ -n loader $(@D)/loader.bin
|
||||||
|
|
||||||
|
$(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile
|
||||||
|
@$(MAKE) -C $(dir_arm9_exceptions)
|
||||||
|
@mv $(dir_arm9_exceptions)/arm9_exceptions.bin $(@D)
|
||||||
|
@bin2c -o $@ -n arm9_exceptions $(@D)/arm9_exceptions.bin
|
||||||
|
|
||||||
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
|
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
|
||||||
@$(MAKE) -C $(dir_screeninit)
|
@$(MAKE) -C $(dir_screeninit)
|
||||||
@mv $(dir_screeninit)/screeninit.bin $(@D)
|
@mv $(dir_screeninit)/screeninit.bin $(@D)
|
||||||
|
47
exceptions/arm9/Makefile
Normal file
47
exceptions/arm9/Makefile
Normal 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 := arm9_exceptions
|
||||||
|
|
||||||
|
dir_source := source
|
||||||
|
dir_build := build
|
||||||
|
|
||||||
|
ASFLAGS := -mcpu=arm946e-s
|
||||||
|
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: $(name).bin
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@rm -rf $(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)
|
11
exceptions/arm9/linker.ld
Normal file
11
exceptions/arm9/linker.ld
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x01FF8000;
|
||||||
|
.text.start : { *(.text.start) }
|
||||||
|
.text : { *(.text) }
|
||||||
|
.data : { *(.data) }
|
||||||
|
.bss : { *(.bss COMMON) }
|
||||||
|
.rodata : { *(.rodata) }
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
14
exceptions/arm9/source/handlers.h
Normal file
14
exceptions/arm9/source/handlers.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
* handlers.h
|
||||||
|
* by TuxSH
|
||||||
|
*
|
||||||
|
* This is part of Luma3DS, see LICENSE.txt for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void setupStack(u32 mode, void *stack);
|
||||||
|
void FIQHandler(void);
|
||||||
|
void undefinedInstructionHandler(void);
|
||||||
|
void dataAbortHandler(void);
|
||||||
|
void prefetchAbortHandler(void);
|
57
exceptions/arm9/source/handlers.s
Normal file
57
exceptions/arm9/source/handlers.s
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
@
|
||||||
|
@ handlers.s
|
||||||
|
@ by TuxSH
|
||||||
|
@
|
||||||
|
@ This is part of Luma3DS, see LICENSE.txt for details
|
||||||
|
@
|
||||||
|
|
||||||
|
.macro GEN_HANDLER name, addr_offset
|
||||||
|
.global \name
|
||||||
|
.type \name, %function
|
||||||
|
\name:
|
||||||
|
stmfd sp!, {r0-r7} @ FIQ has its own r8-r14 regs
|
||||||
|
ldr r0, =\addr_offset
|
||||||
|
sub r0, lr, r0 @ address of instruction that triggered the exception; we will handle the undef+Thumb case later
|
||||||
|
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-r14}
|
||||||
|
msr cpsr_c, r3 @ restore processor mode
|
||||||
|
mov sp, r6
|
||||||
|
|
||||||
|
stmfd sp!, {r0,r2} @ it's a bit of a mess, but we will fix that later
|
||||||
|
@ order of regs now: pc, spsr, r8-r14, r0-r7
|
||||||
|
mov r0, sp
|
||||||
|
ldr r1, =\@ @ macro expansion counter
|
||||||
|
b mainHandler
|
||||||
|
|
||||||
|
.size \name, . - \name
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.text
|
||||||
|
.arm
|
||||||
|
.align 4
|
||||||
|
|
||||||
|
GEN_HANDLER FIQHandler, 4
|
||||||
|
GEN_HANDLER undefinedInstructionHandler, 4
|
||||||
|
GEN_HANDLER prefetchAbortHandler, 4
|
||||||
|
GEN_HANDLER dataAbortHandler, 8
|
||||||
|
|
||||||
|
.global setupStack
|
||||||
|
.type setupStack, %function
|
||||||
|
setupStack:
|
||||||
|
cmp r0, #0
|
||||||
|
moveq r0, #0xf @ usr => sys
|
||||||
|
mrs r2, cpsr
|
||||||
|
bic r3, r2, #0xf
|
||||||
|
orr r3, r0 @ processor mode
|
||||||
|
msr cpsr_c, r3 @ change processor mode
|
||||||
|
mov sp, r1
|
||||||
|
msr cpsr_c, r2 @ restore processor mode
|
||||||
|
bx lr
|
114
exceptions/arm9/source/i2c.c
Normal file
114
exceptions/arm9/source/i2c.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include "i2c.h"
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
||||||
|
{0, 0x4A}, {0, 0x7A}, {0, 0x78},
|
||||||
|
{1, 0x4A}, {1, 0x78}, {1, 0x2C},
|
||||||
|
{1, 0x2E}, {1, 0x40}, {1, 0x44},
|
||||||
|
{2, 0xD6}, {2, 0xD0}, {2, 0xD2},
|
||||||
|
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline u8 i2cGetDeviceBusId(u8 device_id)
|
||||||
|
{
|
||||||
|
return dev_data[device_id].bus_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u8 i2cGetDeviceRegAddr(u8 device_id)
|
||||||
|
{
|
||||||
|
return dev_data[device_id].reg_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static vu8 *reg_data_addrs[] = {
|
||||||
|
(vu8 *)(I2C1_REG_OFF + I2C_REG_DATA),
|
||||||
|
(vu8 *)(I2C2_REG_OFF + I2C_REG_DATA),
|
||||||
|
(vu8 *)(I2C3_REG_OFF + I2C_REG_DATA),
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline vu8 *i2cGetDataReg(u8 bus_id)
|
||||||
|
{
|
||||||
|
return reg_data_addrs[bus_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static vu8 *reg_cnt_addrs[] = {
|
||||||
|
(vu8 *)(I2C1_REG_OFF + I2C_REG_CNT),
|
||||||
|
(vu8 *)(I2C2_REG_OFF + I2C_REG_CNT),
|
||||||
|
(vu8 *)(I2C3_REG_OFF + I2C_REG_CNT),
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline vu8 *i2cGetCntReg(u8 bus_id)
|
||||||
|
{
|
||||||
|
return reg_cnt_addrs[bus_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static inline void i2cWaitBusy(u8 bus_id)
|
||||||
|
{
|
||||||
|
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 i2cGetResult(u8 bus_id)
|
||||||
|
{
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
|
||||||
|
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2cStop(u8 bus_id, u8 arg0)
|
||||||
|
{
|
||||||
|
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
*i2cGetCntReg(bus_id) = 0xC5;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||||
|
{
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
*i2cGetDataReg(bus_id) = dev_reg;
|
||||||
|
*i2cGetCntReg(bus_id) = 0xC2;
|
||||||
|
|
||||||
|
return i2cGetResult(bus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 i2cSelectRegister(u8 bus_id, u8 reg)
|
||||||
|
{
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
*i2cGetDataReg(bus_id) = reg;
|
||||||
|
*i2cGetCntReg(bus_id) = 0xC0;
|
||||||
|
|
||||||
|
return i2cGetResult(bus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||||
|
{
|
||||||
|
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||||
|
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||||
|
|
||||||
|
for(u32 i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||||
|
{
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
*i2cGetDataReg(bus_id) = data;
|
||||||
|
*i2cGetCntReg(bus_id) = 0xC1;
|
||||||
|
i2cStop(bus_id, 0);
|
||||||
|
|
||||||
|
if(i2cGetResult(bus_id))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
*i2cGetCntReg(bus_id) = 0xC5;
|
||||||
|
i2cWaitBusy(bus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
18
exceptions/arm9/source/i2c.h
Normal file
18
exceptions/arm9/source/i2c.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define I2C1_REG_OFF 0x10161000
|
||||||
|
#define I2C2_REG_OFF 0x10144000
|
||||||
|
#define I2C3_REG_OFF 0x10148000
|
||||||
|
|
||||||
|
#define I2C_REG_DATA 0
|
||||||
|
#define I2C_REG_CNT 1
|
||||||
|
#define I2C_REG_CNTEX 2
|
||||||
|
#define I2C_REG_SCL 4
|
||||||
|
|
||||||
|
#define I2C_DEV_MCU 3
|
||||||
|
#define I2C_DEV_GYRO 10
|
||||||
|
#define I2C_DEV_IR 13
|
||||||
|
|
||||||
|
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
84
exceptions/arm9/source/main.c
Normal file
84
exceptions/arm9/source/main.c
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* main.c
|
||||||
|
* by TuxSH
|
||||||
|
*
|
||||||
|
* This is part of Luma3DS, see LICENSE.txt for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "i2c.h"
|
||||||
|
#include "handlers.h"
|
||||||
|
|
||||||
|
#define TEMP_BUFFER 0x1FF80000 //We choose AXIWRAM as tmp buffer since it's usually ARM11 payloads running there
|
||||||
|
#define FINAL_BUFFER 0x25000000
|
||||||
|
#define STACK_DUMP_SIZE 0x2000
|
||||||
|
#define SP ((void *)(TEMP_BUFFER + 4 * STACK_DUMP_SIZE))
|
||||||
|
|
||||||
|
void __attribute__((noreturn)) mainHandler(u32 regs[17], u32 type)
|
||||||
|
{
|
||||||
|
vu32 *dump = (u32 *)TEMP_BUFFER;
|
||||||
|
|
||||||
|
dump[0] = 0xDEADC0DE; //Magic
|
||||||
|
dump[1] = 0xDEADCAFE; //Magic
|
||||||
|
dump[2] = (1 << 16) | 0; //Dump format version number
|
||||||
|
dump[3] = 9; //Processor
|
||||||
|
dump[4] = type; //Exception type
|
||||||
|
dump[6] = 4 * 17; //Register dump size (r0-r12, sp, lr, pc, cpsr)
|
||||||
|
dump[7] = 40; //Code dump size (10 ARM instructions, up to 20 Thumb instructions).
|
||||||
|
dump[8] = STACK_DUMP_SIZE; //Stack dump size
|
||||||
|
dump[9] = 0; //Other data size
|
||||||
|
dump[5] = 40 + dump[6] + dump[7] + dump[8] + dump[9]; //Total size
|
||||||
|
|
||||||
|
//Dump registers
|
||||||
|
//Current order of regs: pc, spsr, r8-r12, sp, lr, r0-r7
|
||||||
|
vu32 *regdump = dump + 10;
|
||||||
|
|
||||||
|
u32 cpsr = regs[1];
|
||||||
|
u32 pc = regs[0] + (((cpsr & 0x20) != 0 && type == 1) ? 2 : 0);
|
||||||
|
|
||||||
|
regdump[15] = pc;
|
||||||
|
regdump[16] = cpsr;
|
||||||
|
|
||||||
|
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
|
||||||
|
vu16 *codedump = (vu16 *)(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 */
|
||||||
|
for(u32 i = 0; i < dump[8] / 4; i++)
|
||||||
|
stackdump[i] = sp[i];
|
||||||
|
|
||||||
|
vu32 *final = (u32 *)FINAL_BUFFER;
|
||||||
|
for(u32 i = 0; i < dump[5] / 4; i++)
|
||||||
|
final[i] = dump[i];
|
||||||
|
|
||||||
|
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
setupStack(1, SP); //FIQ
|
||||||
|
setupStack(7, SP); //Abort
|
||||||
|
setupStack(11, SP); //Undefined
|
||||||
|
|
||||||
|
*(vu32 *)0x08000004 = 0xE51FF004;
|
||||||
|
*(vu32 *)0x08000008 = (u32)FIQHandler;
|
||||||
|
*(vu32 *)0x08000014 = 0xE51FF004;
|
||||||
|
*(vu32 *)0x08000018 = (u32)undefinedInstructionHandler;
|
||||||
|
*(vu32 *)0x0800001C = 0xE51FF004;
|
||||||
|
*(vu32 *)0x08000020 = (u32)prefetchAbortHandler;
|
||||||
|
*(vu32 *)0x08000028 = 0xE51FF004;
|
||||||
|
*(vu32 *)0x0800002C = (u32)dataAbortHandler;
|
||||||
|
}
|
5
exceptions/arm9/source/start.s
Normal file
5
exceptions/arm9/source/start.s
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
.section .text.start
|
||||||
|
.align 4
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
b main
|
13
exceptions/arm9/source/types.h
Normal file
13
exceptions/arm9/source/types.h
Normal 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;
|
103
exceptions/exception_dump_parser.py
Normal file
103
exceptions/exception_dump_parser.py
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# Requires Python >= 3.2 or >= 2.7
|
||||||
|
|
||||||
|
# This is part of AuReiNand
|
||||||
|
|
||||||
|
__author__ = "TuxSH"
|
||||||
|
__copyright__ = "Copyright (c) 2016 TuxSH"
|
||||||
|
__license__ = "GPLv3"
|
||||||
|
__version__ = "v1.0"
|
||||||
|
|
||||||
|
"""
|
||||||
|
Parses AuReiNand exception dumps
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
from struct import unpack_from
|
||||||
|
|
||||||
|
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
|
||||||
|
# Credits for hexdump go to the original authors
|
||||||
|
# Slightly edited by TuxSH
|
||||||
|
|
||||||
|
def hexdump(addr, src, length=16, sep='.' ):
|
||||||
|
'''
|
||||||
|
@brief Return {src} in hex dump.
|
||||||
|
@param[in] length {Int} Nb Bytes by row.
|
||||||
|
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
|
||||||
|
@return {Str} The hexdump
|
||||||
|
@note Full support for python2 and python3 !
|
||||||
|
'''
|
||||||
|
result = []
|
||||||
|
|
||||||
|
# Python3 support
|
||||||
|
try:
|
||||||
|
xrange(0,1)
|
||||||
|
except NameError:
|
||||||
|
xrange = range
|
||||||
|
|
||||||
|
for i in xrange(0, len(src), length):
|
||||||
|
subSrc = src[i:i+length]
|
||||||
|
hexa = ''
|
||||||
|
isMiddle = False
|
||||||
|
for h in xrange(0,len(subSrc)):
|
||||||
|
if h == length/2:
|
||||||
|
hexa += ' '
|
||||||
|
h = subSrc[h]
|
||||||
|
if not isinstance(h, int):
|
||||||
|
h = ord(h)
|
||||||
|
h = hex(h).replace('0x','')
|
||||||
|
if len(h) == 1:
|
||||||
|
h = '0'+h
|
||||||
|
hexa += h+' '
|
||||||
|
hexa = hexa.strip(' ')
|
||||||
|
text = ''
|
||||||
|
for c in subSrc:
|
||||||
|
if not isinstance(c, int):
|
||||||
|
c = ord(c)
|
||||||
|
if 0x20 <= c < 0x7F:
|
||||||
|
text += chr(c)
|
||||||
|
else:
|
||||||
|
text += sep
|
||||||
|
result.append(('%08X: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
|
||||||
|
|
||||||
|
return '\n'.join(result)
|
||||||
|
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Parse AuReiNand exception dumps")
|
||||||
|
parser.add_argument("filename")
|
||||||
|
args = parser.parse_args()
|
||||||
|
data = b""
|
||||||
|
with open(args.filename, "rb") as f: data = f.read()
|
||||||
|
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
||||||
|
raise SystemExit("Invalid file format")
|
||||||
|
|
||||||
|
processor, exceptionType, _, _, codeDumpSize, stackDumpSize = unpack_from("<6I", data, 12)
|
||||||
|
|
||||||
|
print("Processor: ARM{0}".format(processor))
|
||||||
|
print("Exception type: {0}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType]))
|
||||||
|
|
||||||
|
registers = unpack_from("<17I", data, 40)
|
||||||
|
print("\nRegister dump:\n")
|
||||||
|
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])))
|
||||||
|
|
||||||
|
codeDump = data[40+4*17 : 40+4*17 + 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
|
||||||
|
stackDump = data[stackOffset : stackOffset + stackDumpSize]
|
||||||
|
print("\nStack dump:\n")
|
||||||
|
print(hexdump(registers[13], stackDump))
|
||||||
|
|
@ -24,7 +24,7 @@ void configureCFW(const char *configPath)
|
|||||||
"( ) Force A9LH detection",
|
"( ) Force A9LH detection",
|
||||||
"( ) Use second EmuNAND as default",
|
"( ) Use second EmuNAND as default",
|
||||||
"( ) Enable region/language emulation",
|
"( ) Enable region/language emulation",
|
||||||
"( ) Use developer UNITINFO",
|
"( ) Enable developer features",
|
||||||
"( ) Show current NAND in System Settings",
|
"( ) Show current NAND in System Settings",
|
||||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||||
"( ) Enable splash screen with no screen-init" };
|
"( ) Enable splash screen with no screen-init" };
|
||||||
|
38
source/exceptions.c
Normal file
38
source/exceptions.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* exceptions.c
|
||||||
|
* by TuxSH
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exceptions.h"
|
||||||
|
#include "fs.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "../build/arm9_exceptions.h"
|
||||||
|
|
||||||
|
#define PAYLOAD_ADDRESS 0x01FF8000
|
||||||
|
|
||||||
|
void installArm9Handlers(void)
|
||||||
|
{
|
||||||
|
memcpy((void *)PAYLOAD_ADDRESS, arm9_exceptions, arm9_exceptions_size);
|
||||||
|
((void (*)())PAYLOAD_ADDRESS)();
|
||||||
|
}
|
||||||
|
|
||||||
|
void detectAndProcessExceptionDumps(void)
|
||||||
|
{
|
||||||
|
vu32 *dump = (u32 *)0x25000000;
|
||||||
|
|
||||||
|
if(dump[0] == 0xDEADC0DE && dump[1] == 0xDEADCAFE && dump[3] == 9)
|
||||||
|
{
|
||||||
|
char path[41] = "/luma/dumps/arm9";
|
||||||
|
char fileName[] = "crash_dump_00000000.dmp";
|
||||||
|
|
||||||
|
findDumpFile(path, fileName);
|
||||||
|
|
||||||
|
path[16] = '/';
|
||||||
|
memcpy(&path[17], fileName, sizeof(fileName));
|
||||||
|
|
||||||
|
fileWrite((void *)dump, path, dump[5]);
|
||||||
|
|
||||||
|
error("An ARM9 exception occured.\nPlease check your /luma/dumps/arm9 folder.");
|
||||||
|
}
|
||||||
|
}
|
11
source/exceptions.h
Normal file
11
source/exceptions.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* exceptions.h
|
||||||
|
* by TuxSH
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void installArm9Handlers(void);
|
||||||
|
void detectAndProcessExceptionDumps(void);
|
@ -13,6 +13,7 @@
|
|||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "screeninit.h"
|
#include "screeninit.h"
|
||||||
#include "loader.h"
|
#include "loader.h"
|
||||||
|
#include "exceptions.h"
|
||||||
#include "buttons.h"
|
#include "buttons.h"
|
||||||
#include "../build/patches.h"
|
#include "../build/patches.h"
|
||||||
|
|
||||||
@ -67,6 +68,13 @@ void main(void)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
//Only when "Enable developer features" is set
|
||||||
|
if(CONFIG(5))
|
||||||
|
{
|
||||||
|
detectAndProcessExceptionDumps();
|
||||||
|
installArm9Handlers();
|
||||||
|
}
|
||||||
|
|
||||||
bootType = 0;
|
bootType = 0;
|
||||||
firmType = 0;
|
firmType = 0;
|
||||||
|
|
||||||
|
21
source/fs.c
21
source/fs.c
@ -61,6 +61,27 @@ u32 defPayloadExists(void)
|
|||||||
return (result == FR_OK && info.fname[0]);
|
return (result == FR_OK && info.fname[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void findDumpFile(const char *path, char *fileName)
|
||||||
|
{
|
||||||
|
DIR dir;
|
||||||
|
FILINFO info;
|
||||||
|
u32 n = 0;
|
||||||
|
|
||||||
|
while(f_findfirst(&dir, &info, path, fileName) == FR_OK && info.fname[0])
|
||||||
|
{
|
||||||
|
u32 i = 18,
|
||||||
|
tmp = ++n;
|
||||||
|
|
||||||
|
while(tmp)
|
||||||
|
{
|
||||||
|
fileName[i--] = '0' + (tmp % 10);
|
||||||
|
tmp /= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f_closedir(&dir);
|
||||||
|
}
|
||||||
|
|
||||||
void firmRead(void *dest, const char *firmFolder)
|
void firmRead(void *dest, const char *firmFolder)
|
||||||
{
|
{
|
||||||
char path[48] = "1:/title/00040138/00000000/content";
|
char path[48] = "1:/title/00040138/00000000/content";
|
||||||
|
@ -10,4 +10,5 @@ u32 mountFs(void);
|
|||||||
u32 fileRead(void *dest, const char *path, u32 size);
|
u32 fileRead(void *dest, const char *path, u32 size);
|
||||||
u32 fileWrite(const void *buffer, const char *path, u32 size);
|
u32 fileWrite(const void *buffer, const char *path, u32 size);
|
||||||
u32 defPayloadExists(void);
|
u32 defPayloadExists(void);
|
||||||
|
void findDumpFile(const char *path, char *fileName);
|
||||||
void firmRead(void *dest, const char *firmFolder);
|
void firmRead(void *dest, const char *firmFolder);
|
Reference in New Issue
Block a user