Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aacbd17ce | ||
|
|
6f8a9421ef | ||
|
|
bb437f6f7b | ||
|
|
0001f301f8 | ||
|
|
f7bbc4bfec | ||
|
|
a127a38438 | ||
|
|
16225b97d7 | ||
|
|
0587c14162 | ||
|
|
a181bba9f2 | ||
|
|
993e564fbb | ||
|
|
956829864c | ||
|
|
24186a7148 | ||
|
|
3475cfe1e6 | ||
|
|
6b64a10362 |
15
Makefile
15
Makefile
@@ -11,6 +11,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_screeninit := screeninit
|
||||||
dir_injector := injector
|
dir_injector := injector
|
||||||
dir_mset := CakeHax
|
dir_mset := CakeHax
|
||||||
dir_ninjhax := CakeBrah
|
dir_ninjhax := CakeBrah
|
||||||
@@ -19,7 +20,6 @@ dir_out := out
|
|||||||
|
|
||||||
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
|
ASFLAGS := -mlittle-endian -mcpu=arm946e-s -march=armv5te
|
||||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -ffast-math
|
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -fshort-wchar -std=c11 -Wno-main -O2 -ffast-math
|
||||||
CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\""
|
|
||||||
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Reisyukaku/Aurora Wright" --no-print-directory
|
FLAGS := name=$(name).dat dir_out=$(abspath $(dir_out)) ICON=$(abspath icon.png) APP_DESCRIPTION="Noob-friendly 3DS CFW." APP_AUTHOR="Reisyukaku/Aurora Wright" --no-print-directory
|
||||||
|
|
||||||
objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
objects_cfw = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||||
@@ -48,6 +48,7 @@ clean:
|
|||||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||||
@rm -rf $(dir_out) $(dir_build)
|
@rm -rf $(dir_out) $(dir_build)
|
||||||
@$(MAKE) -C $(dir_loader) clean
|
@$(MAKE) -C $(dir_loader) clean
|
||||||
|
@$(MAKE) -C $(dir_screeninit) clean
|
||||||
@$(MAKE) -C $(dir_injector) clean
|
@$(MAKE) -C $(dir_injector) clean
|
||||||
|
|
||||||
$(dir_out):
|
$(dir_out):
|
||||||
@@ -58,7 +59,7 @@ $(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
|||||||
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||||
|
|
||||||
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||||
@cp -av $(dir_build)/main.bin $@
|
@cp -a $(dir_build)/main.bin $@
|
||||||
|
|
||||||
$(dir_out)/3ds/$(name): $(dir_out)
|
$(dir_out)/3ds/$(name): $(dir_out)
|
||||||
@mkdir -p "$(dir_out)/3ds/$(name)"
|
@mkdir -p "$(dir_out)/3ds/$(name)"
|
||||||
@@ -82,6 +83,11 @@ $(dir_build)/loader.h: $(dir_loader)/Makefile
|
|||||||
@mv $(dir_loader)/loader.bin $(dir_build)
|
@mv $(dir_loader)/loader.bin $(dir_build)
|
||||||
@bin2c -o $@ -n loader $(dir_build)/loader.bin
|
@bin2c -o $@ -n loader $(dir_build)/loader.bin
|
||||||
|
|
||||||
|
$(dir_build)/screeninit.h: $(dir_screeninit)/Makefile
|
||||||
|
@$(MAKE) -C $(dir_screeninit)
|
||||||
|
@mv $(dir_screeninit)/screeninit.bin $(dir_build)
|
||||||
|
@bin2c -o $@ -n screeninit $(dir_build)/screeninit.bin
|
||||||
|
|
||||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||||
$(OC) -S -O binary $< $@
|
$(OC) -S -O binary $< $@
|
||||||
|
|
||||||
@@ -89,7 +95,10 @@ $(dir_build)/main.elf: $(objects_cfw)
|
|||||||
# FatFs requires libgcc for __aeabi_uidiv
|
# FatFs requires libgcc for __aeabi_uidiv
|
||||||
$(CC) -nostartfiles $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
$(CC) -nostartfiles $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||||
|
|
||||||
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/patches.h $(dir_build)/loader.h
|
$(dir_build)/memory.o : CFLAGS+=-O3
|
||||||
|
$(dir_build)/utils.o : CFLAGS += -DCONFIG_TITLE="\"$(name) $(version) configuration\""
|
||||||
|
|
||||||
|
$(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/patches.h $(dir_build)/loader.h $(dir_build)/screeninit.h
|
||||||
@mkdir -p "$(@D)"
|
@mkdir -p "$(@D)"
|
||||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Copy everything in 'out' folder to SD root and run!
|
|||||||
|
|
||||||
See https://github.com/Reisyukaku/ReiNand and http://gbatemp.net/threads/reinand-mod-o3ds-n3ds-sysnand.411110
|
See https://github.com/Reisyukaku/ReiNand and http://gbatemp.net/threads/reinand-mod-o3ds-n3ds-sysnand.411110
|
||||||
|
|
||||||
The FIRMs you need are [HERE](http://www99.zippyshare.com/v/kEIiQl0x/file.html).
|
The FIRMs you need are [HERE](http://www77.zippyshare.com/v/oXDn2Hes/file.html).
|
||||||
|
|
||||||
**Credits:**
|
**Credits:**
|
||||||
|
|
||||||
@@ -31,6 +31,6 @@ A skilled reverser gave me the new reboot patch.
|
|||||||
|
|
||||||
The screen init code is from dark_samus, bil1s, Normmatt, delebile and everyone who contributed.
|
The screen init code is from dark_samus, bil1s, Normmatt, delebile and everyone who contributed.
|
||||||
|
|
||||||
The code for printing to the screen is from CakesFW.
|
The code for printing to the screen, and the heavy revision to the reboot patch to allow for AGB/TWL loading are from CakesFW.
|
||||||
|
|
||||||
ARM11 userland patching is only possible thanks to @yifanlu's 3ds_injector, which is bundled in the CFW.
|
ARM11 userland patching is only possible thanks to @yifanlu's 3ds_injector, which is bundled in the CFW.
|
||||||
@@ -158,7 +158,7 @@ static Result load_code(u64 progid, prog_addrs_t *shared, u64 prog_handle, int i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// patch
|
// patch
|
||||||
patch_code(progid, (u8 *)shared->text_addr, shared->total_size << 12);
|
patchCode(progid, (u8 *)shared->text_addr, shared->total_size << 12);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ static u32 config = 0;
|
|||||||
static u8 secureinfo[0x111] = {0};
|
static u8 secureinfo[0x111] = {0};
|
||||||
|
|
||||||
//Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
//Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||||
static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||||
|
{
|
||||||
const u8 *patternc = (const u8 *)pattern;
|
const u8 *patternc = (const u8 *)pattern;
|
||||||
|
|
||||||
//Preprocessing
|
//Preprocessing
|
||||||
@@ -25,7 +26,8 @@ static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSiz
|
|||||||
//Searching
|
//Searching
|
||||||
u32 j = 0;
|
u32 j = 0;
|
||||||
|
|
||||||
while(j <= size - patternSize){
|
while(j <= size - patternSize)
|
||||||
|
{
|
||||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||||
return startPos + j;
|
return startPos + j;
|
||||||
j += table[startPos[j + patternSize]];
|
j += table[startPos[j + patternSize]];
|
||||||
@@ -34,28 +36,32 @@ static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSiz
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 patch_memory(u8 *start, u32 size, const void *pattern, u32 patsize, int offset, const void *replace, u32 repsize, u32 count){
|
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||||
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
for(i = 0; i < count; i++){
|
for(i = 0; i < count; i++)
|
||||||
u8 *found = memsearch(start, pattern, size, patsize);
|
{
|
||||||
|
u8 *found = memsearch(start, pattern, size, patSize);
|
||||||
|
|
||||||
if(found == NULL)
|
if(found == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
memcpy(found + offset, replace, repsize);
|
memcpy(found + offset, replace, repSize);
|
||||||
|
|
||||||
u32 at = (u32)(found - start);
|
u32 at = (u32)(found - start);
|
||||||
|
|
||||||
if(at + patsize > size) size = 0;
|
if(at + patSize > size) size = 0;
|
||||||
else size = size - (at + patsize);
|
else size = size - (at + patSize);
|
||||||
|
|
||||||
start = found + patsize;
|
start = found + patSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int file_open(IFile *file, FS_ArchiveID id, const char *path, int flags){
|
static int fileOpen(IFile *file, FS_ArchiveID id, const char *path, int flags)
|
||||||
|
{
|
||||||
FS_Archive archive;
|
FS_Archive archive;
|
||||||
FS_Path ppath;
|
FS_Path ppath;
|
||||||
|
|
||||||
@@ -67,10 +73,12 @@ static int file_open(IFile *file, FS_ArchiveID id, const char *path, int flags){
|
|||||||
ppath.type = PATH_ASCII;
|
ppath.type = PATH_ASCII;
|
||||||
ppath.data = path;
|
ppath.data = path;
|
||||||
ppath.size = len+1;
|
ppath.size = len+1;
|
||||||
|
|
||||||
return IFile_Open(file, archive, ppath, flags);
|
return IFile_Open(file, archive, ppath, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_secureinfo(){
|
static int loadSecureinfo()
|
||||||
|
{
|
||||||
IFile file;
|
IFile file;
|
||||||
Result ret;
|
Result ret;
|
||||||
u64 total;
|
u64 total;
|
||||||
@@ -78,8 +86,9 @@ static int load_secureinfo(){
|
|||||||
if(secureinfo[0] == 0xFF)
|
if(secureinfo[0] == 0xFF)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = file_open(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ);
|
ret = fileOpen(&file, ARCHIVE_NAND_RW, "/sys/SecureInfo_C", FS_OPEN_READ);
|
||||||
if(R_SUCCEEDED(ret)){
|
if(R_SUCCEEDED(ret))
|
||||||
|
{
|
||||||
ret = IFile_Read(&file, &total, secureinfo, sizeof(secureinfo));
|
ret = IFile_Read(&file, &total, secureinfo, sizeof(secureinfo));
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
if(R_SUCCEEDED(ret) && total == sizeof(secureinfo))
|
if(R_SUCCEEDED(ret) && total == sizeof(secureinfo))
|
||||||
@@ -89,7 +98,8 @@ static int load_secureinfo(){
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_config(){
|
static int loadConfig()
|
||||||
|
{
|
||||||
IFile file;
|
IFile file;
|
||||||
Result ret;
|
Result ret;
|
||||||
u64 total;
|
u64 total;
|
||||||
@@ -97,18 +107,20 @@ static int load_config(){
|
|||||||
if(config)
|
if(config)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = file_open(&file, ARCHIVE_SDMC, "/aurei/config.bin", FS_OPEN_READ);
|
ret = fileOpen(&file, ARCHIVE_SDMC, "/aurei/config.bin", FS_OPEN_READ);
|
||||||
if(R_SUCCEEDED(ret)){
|
if(R_SUCCEEDED(ret))
|
||||||
ret = IFile_Read(&file, &total, (void *)&config, 3);
|
{
|
||||||
|
ret = IFile_Read(&file, &total, &config, 3);
|
||||||
IFile_Close(&file);
|
IFile_Close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 patch_code(u64 progid, u8 *code, u32 size){
|
void patchCode(u64 progId, u8 *code, u32 size)
|
||||||
switch(progid){
|
{
|
||||||
|
switch(progId)
|
||||||
|
{
|
||||||
case 0x0004003000008F02LL: // USA Menu
|
case 0x0004003000008F02LL: // USA Menu
|
||||||
case 0x0004003000008202LL: // EUR Menu
|
case 0x0004003000008202LL: // EUR Menu
|
||||||
case 0x0004003000009802LL: // JPN Menu
|
case 0x0004003000009802LL: // JPN Menu
|
||||||
@@ -123,12 +135,14 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||||
};
|
};
|
||||||
|
|
||||||
patch_memory(code, size,
|
//Patch SMDH region checks
|
||||||
|
patchMemory(code, size,
|
||||||
regionFreePattern,
|
regionFreePattern,
|
||||||
sizeof(regionFreePattern), -16,
|
sizeof(regionFreePattern), -16,
|
||||||
regionFreePatch,
|
regionFreePatch,
|
||||||
sizeof(regionFreePatch), 1
|
sizeof(regionFreePatch), 1
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,60 +154,29 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
static const u8 blockAutoUpdatesPatch[] = {
|
static const u8 blockAutoUpdatesPatch[] = {
|
||||||
0xE3, 0xA0
|
0xE3, 0xA0
|
||||||
};
|
};
|
||||||
static const u8 blockEShopUpdateCheckPattern[] = {
|
static const u8 skipEshopUpdateCheckPattern[] = {
|
||||||
0x30, 0xB5, 0xF1, 0xB0
|
0x30, 0xB5, 0xF1, 0xB0
|
||||||
};
|
};
|
||||||
static const u8 blockEShopUpdateCheckPatch[] = {
|
static const u8 skipEshopUpdateCheckPatch[] = {
|
||||||
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
|
0x00, 0x20, 0x08, 0x60, 0x70, 0x47
|
||||||
};
|
};
|
||||||
|
|
||||||
patch_memory(code, size,
|
//Block silent auto-updates
|
||||||
|
patchMemory(code, size,
|
||||||
blockAutoUpdatesPattern,
|
blockAutoUpdatesPattern,
|
||||||
sizeof(blockAutoUpdatesPattern), 0,
|
sizeof(blockAutoUpdatesPattern), 0,
|
||||||
blockAutoUpdatesPatch,
|
blockAutoUpdatesPatch,
|
||||||
sizeof(blockAutoUpdatesPatch), 1
|
sizeof(blockAutoUpdatesPatch), 1
|
||||||
);
|
);
|
||||||
patch_memory(code, size,
|
|
||||||
blockEShopUpdateCheckPattern,
|
//Skip update checks to access the EShop
|
||||||
sizeof(blockEShopUpdateCheckPattern), 0,
|
patchMemory(code, size,
|
||||||
blockEShopUpdateCheckPatch,
|
skipEshopUpdateCheckPattern,
|
||||||
sizeof(blockEShopUpdateCheckPatch), 1
|
sizeof(skipEshopUpdateCheckPattern), 0,
|
||||||
|
skipEshopUpdateCheckPatch,
|
||||||
|
sizeof(skipEshopUpdateCheckPatch), 1
|
||||||
);
|
);
|
||||||
|
|
||||||
if(R_SUCCEEDED(load_secureinfo())){
|
|
||||||
static const char countryRespPattern[] = {
|
|
||||||
0x01, 0x20, 0x01, 0x90, 0x22, 0x46, 0x06, 0x9B
|
|
||||||
};
|
|
||||||
static const char countryRespPatchModel[] = {
|
|
||||||
0x06, 0x9A, 0x03, 0x20, 0x90, 0x47, 0x55, 0x21, 0x01, 0x70, 0x53, 0x21, 0x41, 0x70, 0x00, 0x21,
|
|
||||||
0x81, 0x70, 0x60, 0x61, 0x00, 0x20
|
|
||||||
};
|
|
||||||
const char *country;
|
|
||||||
char countryRespPatch[sizeof(countryRespPatchModel)];
|
|
||||||
|
|
||||||
switch(secureinfo[0x100]){
|
|
||||||
case 1: country = "US"; break;
|
|
||||||
case 2: country = "GB"; break; // sorry rest-of-Europe, you have to change this
|
|
||||||
case 3: country = "AU"; break;
|
|
||||||
case 4: country = "CN"; break;
|
|
||||||
case 5: country = "KR"; break;
|
|
||||||
case 6: country = "TW"; break;
|
|
||||||
default: case 0: country = "JP"; break;
|
|
||||||
}
|
|
||||||
// patch XML response Country
|
|
||||||
memcpy(countryRespPatch,
|
|
||||||
countryRespPatchModel,
|
|
||||||
sizeof(countryRespPatchModel)
|
|
||||||
);
|
|
||||||
countryRespPatch[6] = country[0];
|
|
||||||
countryRespPatch[10] = country[1];
|
|
||||||
patch_memory(code, size,
|
|
||||||
countryRespPattern,
|
|
||||||
sizeof(countryRespPattern), 0,
|
|
||||||
countryRespPatch,
|
|
||||||
sizeof(countryRespPatch), 1
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,12 +187,14 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
case 0x0004001000027000LL: // KOR MSET
|
case 0x0004001000027000LL: // KOR MSET
|
||||||
case 0x0004001000028000LL: // TWN MSET
|
case 0x0004001000028000LL: // TWN MSET
|
||||||
{
|
{
|
||||||
if(R_SUCCEEDED(load_config()) && ((config >> 5) & 1)){
|
if(R_SUCCEEDED(loadConfig()) && ((config >> 5) & 1))
|
||||||
|
{
|
||||||
static const u16 verPattern[] = u"Ver.";
|
static const u16 verPattern[] = u"Ver.";
|
||||||
const u32 currentFirm = ((config >> 12) & 1);
|
const u32 currentFirm = ((config >> 12) & 1);
|
||||||
const u32 currentNand = ((config >> 13) & 3);
|
const u32 currentNand = ((config >> 13) & 3);
|
||||||
|
|
||||||
patch_memory(code, size,
|
//Patch Ver. string
|
||||||
|
patchMemory(code, size,
|
||||||
verPattern,
|
verPattern,
|
||||||
sizeof(verPattern) - sizeof(u16), 0,
|
sizeof(verPattern) - sizeof(u16), 0,
|
||||||
currentNand ? ((currentNand == 1) ? ((currentFirm == 1) ? u" Emu" : u"Emu9") : u"Emu2") :
|
currentNand ? ((currentNand == 1) ? ((currentFirm == 1) ? u" Emu" : u"Emu9") : u"Emu2") :
|
||||||
@@ -217,6 +202,7 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
sizeof(verPattern) - sizeof(u16), 1
|
sizeof(verPattern) - sizeof(u16), 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,12 +215,14 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
0x0B, 0x18, 0x21, 0xC8
|
0x0B, 0x18, 0x21, 0xC8
|
||||||
};
|
};
|
||||||
|
|
||||||
patch_memory(code, size,
|
//Disable updates from foreign carts (makes carts region-free)
|
||||||
|
patchMemory(code, size,
|
||||||
stopCartUpdatesPattern,
|
stopCartUpdatesPattern,
|
||||||
sizeof(stopCartUpdatesPattern), 0,
|
sizeof(stopCartUpdatesPattern), 0,
|
||||||
stopCartUpdatesPatch,
|
stopCartUpdatesPatch,
|
||||||
sizeof(stopCartUpdatesPatch), 2
|
sizeof(stopCartUpdatesPatch), 2
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,20 +235,21 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
0x00, 0x26
|
0x00, 0x26
|
||||||
};
|
};
|
||||||
|
|
||||||
// disable SecureInfo signature check
|
//Disable SecureInfo signature check
|
||||||
patch_memory(code, size,
|
patchMemory(code, size,
|
||||||
secureinfoSigCheckPattern,
|
secureinfoSigCheckPattern,
|
||||||
sizeof(secureinfoSigCheckPattern), 0,
|
sizeof(secureinfoSigCheckPattern), 0,
|
||||||
secureinfoSigCheckPatch,
|
secureinfoSigCheckPatch,
|
||||||
sizeof(secureinfoSigCheckPatch), 1
|
sizeof(secureinfoSigCheckPatch), 1
|
||||||
);
|
);
|
||||||
|
|
||||||
if(R_SUCCEEDED(load_secureinfo())){
|
if(R_SUCCEEDED(loadSecureinfo()))
|
||||||
|
{
|
||||||
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
|
static const u16 secureinfoFilenamePattern[] = u"SecureInfo_";
|
||||||
static const u16 secureinfoFilenamePatch[] = u"C";
|
static const u16 secureinfoFilenamePatch[] = u"C";
|
||||||
|
|
||||||
// use SecureInfo_C
|
//Use SecureInfo_C
|
||||||
patch_memory(code, size,
|
patchMemory(code, size,
|
||||||
secureinfoFilenamePattern,
|
secureinfoFilenamePattern,
|
||||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||||
@@ -268,9 +257,8 @@ u32 patch_code(u64 progid, u8 *code, u32 size){
|
|||||||
sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
|
sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
@@ -2,4 +2,4 @@
|
|||||||
|
|
||||||
#include <3ds/types.h>
|
#include <3ds/types.h>
|
||||||
|
|
||||||
u32 patch_code(u64 progid, u8 *code, u32 size);
|
void patchCode(u64 progId, u8 *code, u32 size);
|
||||||
@@ -16,11 +16,9 @@ include $(DEVKITARM)/3ds_rules
|
|||||||
# INCLUDES is a list of directories containing header files
|
# INCLUDES is a list of directories containing header files
|
||||||
# SPECS is the directory containing the important build and link files
|
# SPECS is the directory containing the important build and link files
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
export TARGET := $(shell basename $(CURDIR))
|
export TARGET := $(shell basename $(CURDIR))
|
||||||
BUILD := build
|
BUILD := build
|
||||||
SOURCES := source source/fatfs source/fatfs/sdmmc
|
SOURCES := source source/fatfs source/fatfs/sdmmc
|
||||||
DATA := data
|
|
||||||
INCLUDES := include source/fatfs source/fatfs/sdmmc
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# Setup some defines
|
# Setup some defines
|
||||||
@@ -43,14 +41,6 @@ CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
|||||||
ASFLAGS := -g $(ARCH)
|
ASFLAGS := -g $(ARCH)
|
||||||
LDFLAGS = -nostartfiles -g --specs=../stub.specs $(ARCH) -Wl,-Map,$(TARGET).map
|
LDFLAGS = -nostartfiles -g --specs=../stub.specs $(ARCH) -Wl,-Map,$(TARGET).map
|
||||||
|
|
||||||
LIBS :=
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
|
||||||
# list of directories containing libraries, this must be the top level containing
|
|
||||||
# include and lib
|
|
||||||
#---------------------------------------------------------------------------------
|
|
||||||
LIBDIRS :=
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# no real need to edit anything past this point unless you need to add additional
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
# rules for different file extensions
|
# rules for different file extensions
|
||||||
@@ -60,15 +50,13 @@ ifneq ($(BUILD),$(notdir $(CURDIR)))
|
|||||||
|
|
||||||
export OUTPUT := $(CURDIR)/$(TARGET)
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
|
||||||
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
|
||||||
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
|
|
||||||
|
|
||||||
export DEPSDIR := $(CURDIR)/$(BUILD)
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
# use CXX for linking C++ projects, CC for standard C
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
@@ -84,14 +72,7 @@ else
|
|||||||
endif
|
endif
|
||||||
#---------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
export OFILES := $(addsuffix .o,$(BINFILES)) \
|
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
|
||||||
|
|
||||||
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
|
|
||||||
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
|
|
||||||
-I$(CURDIR)/$(BUILD)
|
|
||||||
|
|
||||||
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
|
||||||
|
|
||||||
.PHONY: $(BUILD) clean all
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define HID_PAD (*(vu16 *)0x10146000 ^ 0xFFF)
|
#define HID_PAD (*(vu32 *)0x10146000 ^ 0xFFF)
|
||||||
#define BUTTON_X (1 << 10)
|
#define BUTTON_X (1 << 10)
|
||||||
#define BUTTON_Y (1 << 11)
|
#define BUTTON_Y (1 << 11)
|
||||||
|
#define BUTTON_R1 (1 << 8)
|
||||||
#define BUTTON_SELECT (1 << 2)
|
#define BUTTON_SELECT (1 << 2)
|
||||||
#define BUTTON_START (1 << 3)
|
|
||||||
#define BUTTON_RIGHT (1 << 4)
|
#define BUTTON_RIGHT (1 << 4)
|
||||||
#define BUTTON_LEFT (1 << 5)
|
#define BUTTON_LEFT (1 << 5)
|
||||||
#define BUTTON_UP (1 << 6)
|
#define BUTTON_UP (1 << 6)
|
||||||
|
|||||||
@@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
#define PAYLOAD_ADDRESS 0x23F00000
|
#define PAYLOAD_ADDRESS 0x23F00000
|
||||||
|
|
||||||
static u32 loadPayload(const char *path){
|
static u32 loadPayload(const char *path)
|
||||||
|
{
|
||||||
FIL payload;
|
FIL payload;
|
||||||
unsigned int br;
|
unsigned int br;
|
||||||
|
|
||||||
if(f_open(&payload, path, FA_READ) == FR_OK)
|
if(f_open(&payload, path, FA_READ) == FR_OK)
|
||||||
{
|
{
|
||||||
f_read(&payload, (void *)PAYLOAD_ADDRESS, f_size(&payload), &br);
|
f_read(&payload, (void *)PAYLOAD_ADDRESS, f_size(&payload), &br);
|
||||||
@@ -18,21 +20,23 @@ static u32 loadPayload(const char *path){
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void main(void){
|
void main(void)
|
||||||
|
{
|
||||||
FATFS fs;
|
FATFS fs;
|
||||||
|
|
||||||
f_mount(&fs, "0:", 1);
|
f_mount(&fs, "0:", 1);
|
||||||
|
|
||||||
//Get pressed buttons
|
//Get pressed buttons
|
||||||
u16 pressed = HID_PAD;
|
u32 pressed = HID_PAD;
|
||||||
|
|
||||||
if(((pressed & BUTTON_X) && loadPayload("/aurei/payloads/x.bin")) ||
|
if(((pressed & BUTTON_RIGHT) && loadPayload("/aurei/payloads/right.bin")) ||
|
||||||
((pressed & BUTTON_Y) && loadPayload("/aurei/payloads/y.bin")) ||
|
|
||||||
((pressed & BUTTON_SELECT) && loadPayload("/aurei/payloads/select.bin")) ||
|
|
||||||
((pressed & BUTTON_START) && loadPayload("/aurei/payloads/start.bin")) ||
|
|
||||||
((pressed & BUTTON_RIGHT) && loadPayload("/aurei/payloads/right.bin")) ||
|
|
||||||
((pressed & BUTTON_LEFT) && loadPayload("/aurei/payloads/left.bin")) ||
|
((pressed & BUTTON_LEFT) && loadPayload("/aurei/payloads/left.bin")) ||
|
||||||
((pressed & BUTTON_UP) && loadPayload("/aurei/payloads/up.bin")) ||
|
((pressed & BUTTON_UP) && loadPayload("/aurei/payloads/up.bin")) ||
|
||||||
((pressed & BUTTON_DOWN) && loadPayload("/aurei/payloads/down.bin")) ||
|
((pressed & BUTTON_DOWN) && loadPayload("/aurei/payloads/down.bin")) ||
|
||||||
|
((pressed & BUTTON_X) && loadPayload("/aurei/payloads/x.bin")) ||
|
||||||
|
((pressed & BUTTON_Y) && loadPayload("/aurei/payloads/y.bin")) ||
|
||||||
|
((pressed & BUTTON_SELECT) && loadPayload("/aurei/payloads/select.bin")) ||
|
||||||
|
((pressed & BUTTON_R1) && loadPayload("/aurei/payloads/r.bin")) ||
|
||||||
loadPayload("/aurei/payloads/default.bin"))
|
loadPayload("/aurei/payloads/default.bin"))
|
||||||
((void (*)())PAYLOAD_ADDRESS)();
|
((void (*)())PAYLOAD_ADDRESS)();
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,4 @@ _start:
|
|||||||
mcr p15, 0, r0, c7, c6, 0 @ flush D-cache
|
mcr p15, 0, r0, c7, c6, 0 @ flush D-cache
|
||||||
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
mcr p15, 0, r0, c7, c10, 4 @ drain write buffer
|
||||||
|
|
||||||
bl main
|
b main
|
||||||
|
|
||||||
.die:
|
|
||||||
b .die
|
|
||||||
|
|||||||
397
patches/reboot.s
397
patches/reboot.s
@@ -1,221 +1,232 @@
|
|||||||
.arm.little
|
.arm.little
|
||||||
|
|
||||||
byteswritten equ 0x2000E000
|
firm_addr equ 0x24000000 ; Temporary location where we'll load the FIRM to
|
||||||
kernelCode equ 0x080F0000
|
firm_maxsize equ 0x200000 ; Random value that's bigger than any of the currently known firm's sizes.
|
||||||
buffer equ 0x24000000
|
kernel_code equ 0x080F0000 ; Offset to copy the Kernel9 code to
|
||||||
fileOpen equ 0x4E45504F ;dummy
|
|
||||||
|
|
||||||
.create "reboot.bin", 0
|
.create "reboot.bin", 0
|
||||||
.arm
|
.arm
|
||||||
//Code jumps here right after the sprintf call
|
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||||
process9Reboot:
|
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||||
doPxi:
|
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||||
ldr r4, =0x44846
|
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||||
ldr r0, =0x10008000
|
|
||||||
readPxiLoop1:
|
|
||||||
ldrh r1, [r0,#4]
|
|
||||||
.word 0xE1B01B81 //lsls r1, r1, #0x17
|
|
||||||
bmi readPxiLoop1
|
|
||||||
ldr r0, [r0,#0xC]
|
|
||||||
cmp r0, r4
|
|
||||||
bne doPxi
|
|
||||||
|
|
||||||
GetFirmPath:
|
pxi_wait_recv:
|
||||||
add r0, sp, #0x3A8-0x70+0x24
|
ldr r2, =0x44846
|
||||||
ldr r1, [r0], #4
|
ldr r0, =0x10008000
|
||||||
ldr r2, =0x00300030
|
readPxiLoop1:
|
||||||
cmp r1, r2
|
ldrh r1, [r0, #4]
|
||||||
ldreq r1, [r0], #4
|
lsls r1, #0x17
|
||||||
ldreq r2, =0x002F0032
|
bmi readPxiLoop1
|
||||||
cmpeq r1, r2
|
ldr r0, [r0, #0xC]
|
||||||
|
cmp r0, r2
|
||||||
|
bne pxi_wait_recv
|
||||||
|
|
||||||
OpenFirm:
|
; Convert 2 bytes of the path string
|
||||||
ldreq r1, =(FileName - OpenFirm - 12)
|
; This will be the method of getting the lower 2 bytes of the title ID
|
||||||
addeq r1, pc
|
; until someone bothers figuring out where the value is derived from.
|
||||||
addne r1, sp, #0x3A8-0x70
|
mov r0, #0 ; Result
|
||||||
moveq r2, #1
|
add r1, sp, #0x3A8 - 0x70
|
||||||
movne r2, #0
|
add r1, #0x22 ; The significant bytes
|
||||||
str r2, [externalFirm]
|
mov r2, #4 ; Maximum loops (amount of bytes * 2)
|
||||||
mov r2, #1
|
|
||||||
add r0, r7, #8
|
|
||||||
ldr r6, =fileOpen
|
|
||||||
blx r6
|
|
||||||
|
|
||||||
SeekFirm:
|
hex_string_to_int_loop:
|
||||||
ldr r0, [externalFirm]
|
ldr r3, [r1], #2 ; 2 because it's a utf-16 string.
|
||||||
cmp r0, #1
|
and r3, #0xFF
|
||||||
moveq r0, r7
|
|
||||||
ldreq r1, =byteswritten
|
|
||||||
ldreq r2, =buffer
|
|
||||||
ldreq r3, =0x0
|
|
||||||
ldreq r6, [sp,#0x3A8-0x198]
|
|
||||||
ldreq r6, [r6,#0x28] //fread function stored here
|
|
||||||
blxeq r6
|
|
||||||
|
|
||||||
ReadFirm:
|
; Check if it"s a number
|
||||||
|
cmp r3, #'0'
|
||||||
|
blo hex_string_to_int_end
|
||||||
|
sub r3, #'0'
|
||||||
|
cmp r3, #9
|
||||||
|
bls hex_string_to_int_calc
|
||||||
|
|
||||||
|
; Check if it"s a capital letter
|
||||||
|
cmp r3, #'A' - '0'
|
||||||
|
blo hex_string_to_int_end
|
||||||
|
sub r3, #'A' - '0' - 0xA ; Make the correct value: 0xF >= al >= 0xA
|
||||||
|
cmp r3, #0xF
|
||||||
|
bls hex_string_to_int_calc
|
||||||
|
|
||||||
|
; Incorrect value: x > "A"
|
||||||
|
bhi hex_string_to_int_end
|
||||||
|
|
||||||
|
hex_string_to_int_calc:
|
||||||
|
orr r0, r3, r0, lsl #4
|
||||||
|
subs r2, #1
|
||||||
|
bne hex_string_to_int_loop
|
||||||
|
hex_string_to_int_end:
|
||||||
|
|
||||||
|
; Get the FIRM path
|
||||||
|
cmp r0, #0x0002 ; NATIVE_FIRM
|
||||||
|
adreq r1, firm_fname
|
||||||
|
beq load_firm
|
||||||
|
ldr r5, =0x0102 ; TWL_FIRM
|
||||||
|
cmp r0, r5
|
||||||
|
adreq r1, twlfirm_fname
|
||||||
|
beq load_firm
|
||||||
|
ldr r5, =0x0202 ; AGB_FIRM
|
||||||
|
cmp r0, r5
|
||||||
|
adreq r1, agbfirm_fname
|
||||||
|
beq load_firm
|
||||||
|
bne fallback ; TODO: Stubbed
|
||||||
|
|
||||||
|
fallback:
|
||||||
|
; Fallback: Load specified FIRM from exefs
|
||||||
|
add r1, sp, #0x3A8-0x70 ; Location of exefs string.
|
||||||
|
b load_firm
|
||||||
|
|
||||||
|
load_firm:
|
||||||
|
; Open file
|
||||||
|
add r0, r7, #8
|
||||||
|
mov r2, #1
|
||||||
|
ldr r6, [fopen]
|
||||||
|
orr r6, 1
|
||||||
|
blx r6
|
||||||
|
|
||||||
|
cmp r0, #0 ; Check if we were able to load the FIRM
|
||||||
|
bne fallback ; Otherwise, try again with the FIRM from exefs.
|
||||||
|
; This will loop indefinitely if the exefs FIRM fails to load, but whatever.
|
||||||
|
|
||||||
|
; Read file
|
||||||
mov r0, r7
|
mov r0, r7
|
||||||
ldr r1, =byteswritten
|
adr r1, bytes_read
|
||||||
ldr r2, =buffer
|
mov r2, firm_addr
|
||||||
ldr r3, =0x200000
|
mov r3, firm_maxsize
|
||||||
ldr r6, [sp,#0x3A8-0x198]
|
ldr r6, [sp, #0x3A8-0x198]
|
||||||
ldr r6, [r6,#0x28] //fread function stored here
|
ldr r6, [r6, #0x28]
|
||||||
blx r6
|
blx r6
|
||||||
|
|
||||||
KernelSetState:
|
; Set kernel state
|
||||||
mov r2, #0
|
mov r0, #0
|
||||||
mov r3, r2
|
mov r1, #0
|
||||||
mov r1, r2
|
mov r2, #0
|
||||||
mov r0, r2
|
mov r3, #0
|
||||||
.word 0xEF00007C //SVC 0x7C
|
swi 0x7C
|
||||||
|
|
||||||
GoToReboot:
|
goto_reboot:
|
||||||
ldr r0, =(KernelCodeStart - GoToReboot - 12)
|
; Jump to reboot code
|
||||||
add r0, pc
|
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||||
ldr r1, =kernelCode
|
add r0, pc
|
||||||
ldr r2, =0x300
|
ldr r1, =kernel_code
|
||||||
bl Memcpy
|
ldr r2, =0x300
|
||||||
|
bl memcpy32
|
||||||
|
ldr r0, =kernel_code
|
||||||
|
swi 0x7B
|
||||||
|
|
||||||
ldr r0, =kernelCode
|
die:
|
||||||
.word 0xEF00007B //SVC 0x7B
|
b die
|
||||||
|
|
||||||
InfiniteLoop:
|
memcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||||
b InfiniteLoop
|
add r2, r0
|
||||||
|
memcpy32_loop:
|
||||||
Memcpy:
|
ldmia r0!, {r3}
|
||||||
MOV R12, LR
|
stmia r1!, {r3}
|
||||||
STMFD SP!, {R0-R4}
|
cmp r0, r2
|
||||||
ADD R2, R2, R0
|
blo memcpy32_loop
|
||||||
|
bx lr
|
||||||
memcpyLoop:
|
|
||||||
LDR R3, [R0],#4
|
|
||||||
STR R3, [R1],#4
|
|
||||||
CMP R0, R2
|
|
||||||
BLT memcpyLoop
|
|
||||||
LDMFD SP!, {R0-R4}
|
|
||||||
MOV LR, R12
|
|
||||||
BX LR
|
|
||||||
|
|
||||||
FileName:
|
|
||||||
.dcw "sdmc:/aurei/patched_firmware_sys.bin"
|
|
||||||
.word 0x0
|
|
||||||
|
|
||||||
externalFirm:
|
|
||||||
.word 0x2000A000
|
|
||||||
|
|
||||||
|
bytes_read: .word 0
|
||||||
|
fopen: .ascii "OPEN"
|
||||||
.pool
|
.pool
|
||||||
|
firm_fname: .dcw "sdmc:/aurei/patched_firmware_sys.bin"
|
||||||
|
.word 0x0
|
||||||
|
.pool
|
||||||
|
twlfirm_fname: .dcw "sdmc:/aurei/patched_firmware_twl.bin"
|
||||||
|
.word 0x0
|
||||||
|
.pool
|
||||||
|
agbfirm_fname: .dcw "sdmc:/aurei/patched_firmware_agb.bin"
|
||||||
|
.word 0x0
|
||||||
|
|
||||||
// Kernel Code
|
|
||||||
.align 4
|
.align 4
|
||||||
KernelCodeStart:
|
kernelcode_start:
|
||||||
memorySetting:
|
; Set MPU settings
|
||||||
MRC p15, 0, R0,c2,c0, 0
|
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
MRC p15, 0, R12,c2,c0, 1
|
mrc p15, 0, r12, c2, c0, 1 ; icacheable
|
||||||
MRC p15, 0, R1,c3,c0, 0
|
mrc p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||||
MRC p15, 0, R2,c5,c0, 2
|
mrc p15, 0, r2, c5, c0, 2 ; daccess
|
||||||
MRC p15, 0, R3,c5,c0, 3
|
mrc p15, 0, r3, c5, c0, 3 ; iaccess
|
||||||
LDR R4, =0x18000035
|
ldr r4, =0x18000035 ; 0x18000000 128M
|
||||||
BIC R2, R2, #0xF0000
|
bic r2, r2, #0xF0000 ; unprotect region 4
|
||||||
BIC R3, R3, #0xF0000
|
bic r3, r3, #0xF0000 ; unprotect region 4
|
||||||
ORR R0, R0, #0x10
|
orr r0, r0, #0x10 ; dcacheable region 4
|
||||||
ORR R2, R2, #0x30000
|
orr r2, r2, #0x30000 ; region 4 r/w
|
||||||
ORR R3, R3, #0x30000
|
orr r3, r3, #0x30000 ; region 4 r/w
|
||||||
ORR R12, R12, #0x10
|
orr r12, r12, #0x10 ; icacheable region 4
|
||||||
ORR R1, R1, #0x10
|
orr r1, r1, #0x10 ; write bufferable region 4
|
||||||
MCR p15, 0, R0,c2,c0, 0
|
mcr p15, 0, r0, c2, c0, 0
|
||||||
MCR p15, 0, R12,c2,c0, 1
|
mcr p15, 0, r12, c2, c0, 1
|
||||||
MCR p15, 0, R1,c3,c0, 0
|
mcr p15, 0, r1, c3, c0, 0 ; write bufferable
|
||||||
MCR p15, 0, R2,c5,c0, 2
|
mcr p15, 0, r2, c5, c0, 2 ; daccess
|
||||||
MCR p15, 0, R3,c5,c0, 3
|
mcr p15, 0, r3, c5, c0, 3 ; iaccess
|
||||||
MCR p15, 0, R4,c6,c4, 0
|
mcr p15, 0, r4, c6, c4, 0 ; region 4 (hmmm)
|
||||||
MRC p15, 0, R0,c2,c0, 0
|
|
||||||
MRC p15, 0, R1,c2,c0, 1
|
|
||||||
MRC p15, 0, R2,c3,c0, 0
|
|
||||||
ORR R0, R0, #0x20
|
|
||||||
ORR R1, R1, #0x20
|
|
||||||
ORR R2, R2, #0x20
|
|
||||||
MCR p15, 0, R0,c2,c0, 0
|
|
||||||
MCR p15, 0, R1,c2,c0, 1
|
|
||||||
MCR p15, 0, R2,c3,c0, 0
|
|
||||||
|
|
||||||
copyFirmPartitions:
|
mrc p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
LDR R4, =buffer
|
mrc p15, 0, r1, c2, c0, 1 ; icacheable
|
||||||
ADD R3, R4, #0x40
|
mrc p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||||
LDR R0, [R3]
|
orr r0, r0, #0x20 ; dcacheable region 5
|
||||||
ADD R0, R0, R4
|
orr r1, r1, #0x20 ; icacheable region 5
|
||||||
LDR R1, [R3,#4]
|
orr r2, r2, #0x20 ; write bufferable region 5
|
||||||
LDR R2, [R3,#8]
|
mcr p15, 0, r0, c2, c0, 0 ; dcacheable
|
||||||
bl KernelMemcpy
|
mcr p15, 0, r1, c2, c0, 1 ; icacheable
|
||||||
|
mcr p15, 0, r2, c3, c0, 0 ; write bufferable
|
||||||
|
|
||||||
ADD R3, R4, #0x70
|
; Copy the firmware
|
||||||
LDR R0, [R3]
|
mov r4, firm_addr
|
||||||
ADD R0, R0, R4
|
add r5, r4, #0x40 ; Start of loop
|
||||||
LDR R1, [R3,#4]
|
add r6, r5, #0x30 * 3 ; End of loop (scan 4 entries)
|
||||||
LDR R2, [R3,#8]
|
|
||||||
bl KernelMemcpy
|
|
||||||
|
|
||||||
ADD R3, R4, #0xA0
|
copy_firm_loop:
|
||||||
LDR R0, [R3]
|
ldr r0, [r5]
|
||||||
ADD R0, R0, R4
|
cmp r0, #0
|
||||||
LDR R1, [R3,#4]
|
addne r0, r4 ; src
|
||||||
LDR R2, [R3,#8]
|
ldrne r1, [r5, #4] ; dest
|
||||||
bl KernelMemcpy
|
ldrne r2, [r5, #8] ; size
|
||||||
|
blne kernelmemcpy32
|
||||||
|
|
||||||
ADD R3, R4, #0xD0
|
cmp r5, r6
|
||||||
LDR R0, [R3]
|
addlo r5, #0x30
|
||||||
CMP R0, #0
|
blo copy_firm_loop
|
||||||
BEQ invalidateDataCache
|
|
||||||
ADD R0, R0, R4
|
|
||||||
LDR R1, [R3,#4]
|
|
||||||
LDR R2, [R3,#8]
|
|
||||||
bl KernelMemcpy
|
|
||||||
|
|
||||||
invalidateDataCache:
|
; Flush cache
|
||||||
MOV R2, #0
|
mov r2, #0
|
||||||
MOV R1, R2
|
mov r1, r2
|
||||||
loc_809460C:
|
flush_cache:
|
||||||
MOV R0, #0
|
mov r0, #0
|
||||||
MOV R3, R2,LSL#30
|
mov r3, r2, lsl #30
|
||||||
loc_8094614:
|
flush_cache_inner_loop:
|
||||||
ORR R12, R3, R0,LSL#5
|
orr r12, r3, r0, lsl#5
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||||
MCR p15, 0, R12,c7,c14, 2
|
mcr p15, 0, r12, c7, c14, 2 ; clean and flush dcache entry (index and segment)
|
||||||
ADD R0, R0, #1
|
add r0, #1
|
||||||
CMP R0, #0x20
|
cmp r0, #0x20
|
||||||
BCC loc_8094614
|
bcc flush_cache_inner_loop
|
||||||
ADD R2, R2, #1
|
add r2, #1
|
||||||
CMP R2, #4
|
cmp r2, #4
|
||||||
BCC loc_809460C
|
bcc flush_cache
|
||||||
|
|
||||||
jumpToEntrypoint:
|
; Enable MPU
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
ldr r0, =0x42078 ; alt vector select, enable itcm
|
||||||
LDR R0, =0x42078
|
mcr p15, 0, r0, c1, c0, 0
|
||||||
MCR p15, 0, R0,c1,c0, 0
|
mcr p15, 0, r1, c7, c5, 0 ; flush dcache
|
||||||
MCR p15, 0, R1,c7,c5, 0
|
mcr p15, 0, r1, c7, c6, 0 ; flush icache
|
||||||
MCR p15, 0, R1,c7,c6, 0
|
mcr p15, 0, r1, c7, c10, 4 ; drain write buffer
|
||||||
MCR p15, 0, R1,c7,c10, 4
|
mov r0, firm_addr
|
||||||
LDR R4, =buffer
|
|
||||||
MOV R1, #0x1FFFFFFC
|
; Boot FIRM
|
||||||
LDR R2, [R4,#8]
|
mov r1, #0x1FFFFFFC
|
||||||
STR R2, [R1]
|
ldr r2, [r0, #8] ; arm11 entry
|
||||||
LDR R0, [R4,#0xC]
|
str r2, [r1]
|
||||||
BX R0
|
ldr r0, [r0, #0xC] ; arm9 entry
|
||||||
|
bx r0
|
||||||
.pool
|
.pool
|
||||||
|
|
||||||
KernelMemcpy:
|
kernelmemcpy32: ; memcpy32(void *src, void *dst, unsigned int size)
|
||||||
MOV R12, LR
|
add r2, r0
|
||||||
STMFD SP!, {R0-R4}
|
kmemcpy32_loop:
|
||||||
ADD R2, R2, R0
|
ldmia r0!, {r3}
|
||||||
|
stmia r1!, {r3}
|
||||||
kmemcpyLoop:
|
cmp r0, r2
|
||||||
LDR R3, [R0],#4
|
blo kmemcpy32_loop
|
||||||
STR R3, [R1],#4
|
bx lr
|
||||||
CMP R0, R2
|
|
||||||
BLT kmemcpyLoop
|
|
||||||
LDMFD SP!, {R0-R4}
|
|
||||||
MOV LR, R12
|
|
||||||
BX LR
|
|
||||||
.pool
|
|
||||||
|
|
||||||
KernelCodeEnd:
|
|
||||||
|
|
||||||
.close
|
.close
|
||||||
|
|||||||
115
screeninit/Makefile
Executable file
115
screeninit/Makefile
Executable file
@@ -0,0 +1,115 @@
|
|||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
.SUFFIXES:
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ifeq ($(strip $(DEVKITARM)),)
|
||||||
|
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||||
|
endif
|
||||||
|
|
||||||
|
include $(DEVKITARM)/3ds_rules
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# TARGET is the name of the output
|
||||||
|
# BUILD is the directory where object files & intermediate files will be placed
|
||||||
|
# SOURCES is a list of directories containing source code
|
||||||
|
# DATA is a list of directories containing data files
|
||||||
|
# INCLUDES is a list of directories containing header files
|
||||||
|
# SPECS is the directory containing the important build and link files
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export TARGET := $(shell basename $(CURDIR))
|
||||||
|
BUILD := build
|
||||||
|
SOURCES := source
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# Setup some defines
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# options for code generation
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ARCH := -mthumb -mthumb-interwork
|
||||||
|
|
||||||
|
CFLAGS := -g -Wall -O2\
|
||||||
|
-mcpu=mpcore -mlittle-endian\
|
||||||
|
-ffast-math -Wno-main -std=c99\
|
||||||
|
$(ARCH)
|
||||||
|
|
||||||
|
CFLAGS += $(INCLUDE) -DARM11
|
||||||
|
|
||||||
|
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
|
||||||
|
|
||||||
|
ASFLAGS := -g $(ARCH)
|
||||||
|
LDFLAGS = -nostartfiles -g --specs=../stub.specs $(ARCH) -Wl,-Map,$(TARGET).map
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# no real need to edit anything past this point unless you need to add additional
|
||||||
|
# rules for different file extensions
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifneq ($(BUILD),$(notdir $(CURDIR)))
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OUTPUT := $(CURDIR)/$(TARGET)
|
||||||
|
|
||||||
|
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
|
||||||
|
|
||||||
|
export DEPSDIR := $(CURDIR)/$(BUILD)
|
||||||
|
|
||||||
|
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
||||||
|
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||||
|
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# use CXX for linking C++ projects, CC for standard C
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
ifeq ($(strip $(CPPFILES)),)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CC)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
export LD := $(CXX)
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
|
||||||
|
|
||||||
|
.PHONY: $(BUILD) clean all
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
all: $(BUILD)
|
||||||
|
|
||||||
|
$(BUILD):
|
||||||
|
@[ -d $@ ] || mkdir -p $@
|
||||||
|
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
clean:
|
||||||
|
@echo clean ...
|
||||||
|
@rm -fr $(BUILD) $(OUTPUT).elf
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
else
|
||||||
|
|
||||||
|
DEPENDS := $(OFILES:.o=.d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
# main targets
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
$(OUTPUT).bin : $(OUTPUT).elf
|
||||||
|
$(OUTPUT).elf : $(OFILES)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------
|
||||||
|
%.bin: %.elf
|
||||||
|
@$(OBJCOPY) -O binary $< $@
|
||||||
|
@echo built ... $(notdir $@)
|
||||||
|
|
||||||
|
|
||||||
|
-include $(DEPENDS)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
endif
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
105
screeninit/source/main.c
Executable file
105
screeninit/source/main.c
Executable file
@@ -0,0 +1,105 @@
|
|||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void main(void)
|
||||||
|
{
|
||||||
|
const u32 brightness[4] = {0x5F, 0x4C, 0x39, 0x26};
|
||||||
|
u32 brightnessLevel = *(vu32 *)0x24F04000;
|
||||||
|
vu32 *const arm11 = (u32 *)0x1FFFFFF8;
|
||||||
|
|
||||||
|
*(vu32 *)0x10141200 = 0x1007F;
|
||||||
|
*(vu32 *)0x10202014 = 0x00000001;
|
||||||
|
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
||||||
|
*(vu32 *)0x10202240 = brightness[brightnessLevel];
|
||||||
|
*(vu32 *)0x10202A40 = brightness[brightnessLevel];
|
||||||
|
*(vu32 *)0x10202244 = 0x1023E;
|
||||||
|
*(vu32 *)0x10202A44 = 0x1023E;
|
||||||
|
|
||||||
|
// Top screen
|
||||||
|
*(vu32 *)0x10400400 = 0x000001c2;
|
||||||
|
*(vu32 *)0x10400404 = 0x000000d1;
|
||||||
|
*(vu32 *)0x10400408 = 0x000001c1;
|
||||||
|
*(vu32 *)0x1040040c = 0x000001c1;
|
||||||
|
*(vu32 *)0x10400410 = 0x00000000;
|
||||||
|
*(vu32 *)0x10400414 = 0x000000cf;
|
||||||
|
*(vu32 *)0x10400418 = 0x000000d1;
|
||||||
|
*(vu32 *)0x1040041c = 0x01c501c1;
|
||||||
|
*(vu32 *)0x10400420 = 0x00010000;
|
||||||
|
*(vu32 *)0x10400424 = 0x0000019d;
|
||||||
|
*(vu32 *)0x10400428 = 0x00000002;
|
||||||
|
*(vu32 *)0x1040042c = 0x00000192;
|
||||||
|
*(vu32 *)0x10400430 = 0x00000192;
|
||||||
|
*(vu32 *)0x10400434 = 0x00000192;
|
||||||
|
*(vu32 *)0x10400438 = 0x00000001;
|
||||||
|
*(vu32 *)0x1040043c = 0x00000002;
|
||||||
|
*(vu32 *)0x10400440 = 0x01960192;
|
||||||
|
*(vu32 *)0x10400444 = 0x00000000;
|
||||||
|
*(vu32 *)0x10400448 = 0x00000000;
|
||||||
|
*(vu32 *)0x1040045C = 0x00f00190;
|
||||||
|
*(vu32 *)0x10400460 = 0x01c100d1;
|
||||||
|
*(vu32 *)0x10400464 = 0x01920002;
|
||||||
|
*(vu32 *)0x10400468 = 0x18300000;
|
||||||
|
*(vu32 *)0x10400470 = 0x80341;
|
||||||
|
*(vu32 *)0x10400474 = 0x00010501;
|
||||||
|
*(vu32 *)0x10400478 = 0;
|
||||||
|
*(vu32 *)0x10400490 = 0x000002D0;
|
||||||
|
*(vu32 *)0x1040049C = 0x00000000;
|
||||||
|
|
||||||
|
// Disco register
|
||||||
|
for(u32 i = 0; i < 256; i++)
|
||||||
|
*(vu32 *)0x10400484 = 0x10101 * i;
|
||||||
|
|
||||||
|
// Bottom screen
|
||||||
|
*(vu32 *)0x10400500 = 0x000001c2;
|
||||||
|
*(vu32 *)0x10400504 = 0x000000d1;
|
||||||
|
*(vu32 *)0x10400508 = 0x000001c1;
|
||||||
|
*(vu32 *)0x1040050c = 0x000001c1;
|
||||||
|
*(vu32 *)0x10400510 = 0x000000cd;
|
||||||
|
*(vu32 *)0x10400514 = 0x000000cf;
|
||||||
|
*(vu32 *)0x10400518 = 0x000000d1;
|
||||||
|
*(vu32 *)0x1040051c = 0x01c501c1;
|
||||||
|
*(vu32 *)0x10400520 = 0x00010000;
|
||||||
|
*(vu32 *)0x10400524 = 0x0000019d;
|
||||||
|
*(vu32 *)0x10400528 = 0x00000052;
|
||||||
|
*(vu32 *)0x1040052c = 0x00000192;
|
||||||
|
*(vu32 *)0x10400530 = 0x00000192;
|
||||||
|
*(vu32 *)0x10400534 = 0x0000004f;
|
||||||
|
*(vu32 *)0x10400538 = 0x00000050;
|
||||||
|
*(vu32 *)0x1040053c = 0x00000052;
|
||||||
|
*(vu32 *)0x10400540 = 0x01980194;
|
||||||
|
*(vu32 *)0x10400544 = 0x00000000;
|
||||||
|
*(vu32 *)0x10400548 = 0x00000011;
|
||||||
|
*(vu32 *)0x1040055C = 0x00f00140;
|
||||||
|
*(vu32 *)0x10400560 = 0x01c100d1;
|
||||||
|
*(vu32 *)0x10400564 = 0x01920052;
|
||||||
|
*(vu32 *)0x10400568 = 0x18300000 + 0x46500;
|
||||||
|
*(vu32 *)0x10400570 = 0x80301;
|
||||||
|
*(vu32 *)0x10400574 = 0x00010501;
|
||||||
|
*(vu32 *)0x10400578 = 0;
|
||||||
|
*(vu32 *)0x10400590 = 0x000002D0;
|
||||||
|
*(vu32 *)0x1040059C = 0x00000000;
|
||||||
|
|
||||||
|
// Disco register
|
||||||
|
for(u32 i = 0; i < 256; i++)
|
||||||
|
*(vu32 *)0x10400584 = 0x10101 * i;
|
||||||
|
|
||||||
|
*(vu32 *)0x10400468 = 0x18300000;
|
||||||
|
*(vu32 *)0x1040046c = 0x18300000;
|
||||||
|
*(vu32 *)0x10400494 = 0x18300000;
|
||||||
|
*(vu32 *)0x10400498 = 0x18300000;
|
||||||
|
*(vu32 *)0x10400568 = 0x18346500;
|
||||||
|
*(vu32 *)0x1040056c = 0x18346500;
|
||||||
|
|
||||||
|
//Set CakeBrah framebuffers
|
||||||
|
*((vu32 *)0x23FFFE00) = 0x18300000;
|
||||||
|
*((vu32 *)0x23FFFE04) = 0x18300000;
|
||||||
|
*((vu32 *)0x23FFFE08) = 0x18346500;
|
||||||
|
|
||||||
|
//Clear ARM11 entry offset
|
||||||
|
*arm11 = 0;
|
||||||
|
|
||||||
|
//Wait for the entry to be set
|
||||||
|
while(!*arm11);
|
||||||
|
|
||||||
|
//Jump to it
|
||||||
|
((void (*)())*arm11)();
|
||||||
|
}
|
||||||
8
screeninit/source/start.s
Normal file
8
screeninit/source/start.s
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.section .text.start
|
||||||
|
.align 4
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
@ Disable interrupts
|
||||||
|
CPSID aif
|
||||||
|
|
||||||
|
b main
|
||||||
19
screeninit/source/types.h
Executable file
19
screeninit/source/types.h
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* types.h
|
||||||
|
* by Reisyukaku
|
||||||
|
* Copyright (c) 2015 All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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;
|
||||||
12
screeninit/stub.ld
Executable file
12
screeninit/stub.ld
Executable file
@@ -0,0 +1,12 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x24F03000;
|
||||||
|
.text.start : { *(.text.start) }
|
||||||
|
.text : { *(.text) }
|
||||||
|
.data : { *(.data) }
|
||||||
|
.bss : { *(.bss COMMON) }
|
||||||
|
.rodata : { *(.rodata) }
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
||||||
5
screeninit/stub.specs
Executable file
5
screeninit/stub.specs
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
%rename link old_link
|
||||||
|
|
||||||
|
*link:
|
||||||
|
%(old_link) -T ../stub.ld%s
|
||||||
|
|
||||||
@@ -8,19 +8,22 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define HID_PAD (*(vu16 *)0x10146000 ^ 0xFFF)
|
#define HID_PAD (*(vu32 *)0x10146000 ^ 0xFFF)
|
||||||
#define BUTTON_R1 (1 << 8)
|
#define BUTTON_R1 (1 << 8)
|
||||||
#define BUTTON_L1 (1 << 9)
|
#define BUTTON_L1 (1 << 9)
|
||||||
#define BUTTON_A 1
|
#define BUTTON_A 1
|
||||||
#define BUTTON_B (1 << 1)
|
#define BUTTON_B (1 << 1)
|
||||||
#define BUTTON_SELECT (1 << 2)
|
#define BUTTON_X (1 << 10)
|
||||||
#define BUTTON_START (1 << 3)
|
#define BUTTON_Y (1 << 11)
|
||||||
#define BUTTON_RIGHT (1 << 4)
|
#define BUTTON_SELECT (1 << 2)
|
||||||
#define BUTTON_LEFT (1 << 5)
|
#define BUTTON_START (1 << 3)
|
||||||
#define BUTTON_UP (1 << 6)
|
#define BUTTON_RIGHT (1 << 4)
|
||||||
#define BUTTON_DOWN (1 << 7)
|
#define BUTTON_LEFT (1 << 5)
|
||||||
#define BUTTON_L1R1 ((1 << 8) | (1 << 9))
|
#define BUTTON_UP (1 << 6)
|
||||||
#define SAFE_MODE (BUTTON_L1R1 | BUTTON_A | BUTTON_UP)
|
#define BUTTON_DOWN (1 << 7)
|
||||||
#define OPTION_BUTTONS (BUTTON_L1R1 | BUTTON_A | BUTTON_SELECT)
|
#define BUTTON_L1R1 (BUTTON_R1 | BUTTON_L1)
|
||||||
#define PAYLOAD_BUTTONS ((BUTTON_L1 | BUTTON_A | BUTTON_B) ^ 0xFFF)
|
#define SAFE_MODE (BUTTON_L1R1 | BUTTON_A | BUTTON_UP)
|
||||||
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
#define OVERRIDE_BUTTONS (BUTTON_B ^ 0xFFF)
|
||||||
|
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y)
|
||||||
|
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_SELECT)
|
||||||
|
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
||||||
@@ -239,14 +239,16 @@ static const u8 key2[0x10] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
//Get Nand CTR key
|
//Get Nand CTR key
|
||||||
static void getNandCTR(u8 *buf, u32 console){
|
static void getNandCTR(u8 *buf, u32 console)
|
||||||
|
{
|
||||||
u8 *addr = (console ? (u8 *)0x080D8BBC : (u8 *)0x080D797C) + 0x0F;
|
u8 *addr = (console ? (u8 *)0x080D8BBC : (u8 *)0x080D797C) + 0x0F;
|
||||||
for(u8 keyLen = 0x10; keyLen; keyLen--)
|
for(u8 keyLen = 0x10; keyLen; keyLen--)
|
||||||
*(buf++) = *(addr--);
|
*(buf++) = *(addr--);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Read firm0 from NAND and write to buffer
|
//Read firm0 from NAND and write to buffer
|
||||||
void nandFirm0(u8 *outbuf, u32 size, u32 console){
|
void nandFirm0(u8 *outbuf, u32 size, u32 console)
|
||||||
|
{
|
||||||
u8 CTR[0x10];
|
u8 CTR[0x10];
|
||||||
getNandCTR(CTR, console);
|
getNandCTR(CTR, console);
|
||||||
|
|
||||||
@@ -258,8 +260,8 @@ void nandFirm0(u8 *outbuf, u32 size, u32 console){
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Decrypts the N3DS arm9bin
|
//Decrypts the N3DS arm9bin
|
||||||
void decryptArm9Bin(u8 *arm9Section, u32 mode){
|
void decryptArm9Bin(u8 *arm9Section, u32 mode)
|
||||||
|
{
|
||||||
//Firm keys
|
//Firm keys
|
||||||
u8 keyY[0x10];
|
u8 keyY[0x10];
|
||||||
u8 CTR[0x10];
|
u8 CTR[0x10];
|
||||||
@@ -268,12 +270,15 @@ void decryptArm9Bin(u8 *arm9Section, u32 mode){
|
|||||||
//Setup keys needed for arm9bin decryption
|
//Setup keys needed for arm9bin decryption
|
||||||
memcpy(keyY, arm9Section + 0x10, 0x10);
|
memcpy(keyY, arm9Section + 0x10, 0x10);
|
||||||
memcpy(CTR, arm9Section + 0x20, 0x10);
|
memcpy(CTR, arm9Section + 0x20, 0x10);
|
||||||
|
|
||||||
|
//Calculate the size of the ARM9 binary
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
|
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
|
||||||
for(u8 *tmp = arm9Section + 0x30; *tmp; tmp++)
|
for(u8 *tmp = arm9Section + 0x30; *tmp; tmp++)
|
||||||
size = (size << 3) + (size << 1) + (*tmp) - '0';
|
size = (size << 3) + (size << 1) + (*tmp) - '0';
|
||||||
|
|
||||||
if(mode){
|
if(mode)
|
||||||
|
{
|
||||||
u8 keyX[0x10];
|
u8 keyX[0x10];
|
||||||
|
|
||||||
//Set 0x11 to key2 for the arm9bin and misc keys
|
//Set 0x11 to key2 for the arm9bin and misc keys
|
||||||
@@ -292,15 +297,16 @@ void decryptArm9Bin(u8 *arm9Section, u32 mode){
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Sets the N3DS 9.6 KeyXs
|
//Sets the N3DS 9.6 KeyXs
|
||||||
void setKeyXs(u8 *arm9Section){
|
void setKeyXs(u8 *arm9Section)
|
||||||
|
{
|
||||||
u8 *keyData = arm9Section + 0x89814;
|
u8 *keyData = arm9Section + 0x89814;
|
||||||
u8 *decKey = keyData + 0x10;
|
u8 *decKey = keyData + 0x10;
|
||||||
|
|
||||||
//Set keys 0x19..0x1F keyXs
|
//Set keys 0x19..0x1F keyXs
|
||||||
aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(0x11, key2, AES_KEYNORMAL, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
aes_use_keyslot(0x11);
|
aes_use_keyslot(0x11);
|
||||||
for(u8 slot = 0x19; slot < 0x20; slot++){
|
for(u8 slot = 0x19; slot < 0x20; slot++)
|
||||||
|
{
|
||||||
aes(decKey, keyData, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
aes(decKey, keyData, 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||||
aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
aes_setkey(slot, decKey, AES_KEYX, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||||
*(keyData + 0xF) += 1;
|
*(keyData + 0xF) += 1;
|
||||||
|
|||||||
@@ -20,38 +20,47 @@ static const struct fb {
|
|||||||
u8 *bottom;
|
u8 *bottom;
|
||||||
} *const fb = (struct fb *)0x23FFFE00;
|
} *const fb = (struct fb *)0x23FFFE00;
|
||||||
|
|
||||||
static inline int strlen(const char *string){
|
static inline int strlen(const char *string)
|
||||||
|
{
|
||||||
char *stringEnd = (char *)string;
|
char *stringEnd = (char *)string;
|
||||||
while(*stringEnd) stringEnd++;
|
while(*stringEnd) stringEnd++;
|
||||||
return stringEnd - string;
|
return stringEnd - string;
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearScreens(void){
|
void clearScreens(void)
|
||||||
|
{
|
||||||
memset32(fb->top_left, 0, 0x46500);
|
memset32(fb->top_left, 0, 0x46500);
|
||||||
memset32(fb->top_right, 0, 0x46500);
|
memset32(fb->top_right, 0, 0x46500);
|
||||||
memset32(fb->bottom, 0, 0x38400);
|
memset32(fb->bottom, 0, 0x38400);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadSplash(void){
|
void loadSplash(void)
|
||||||
|
{
|
||||||
clearScreens();
|
clearScreens();
|
||||||
|
|
||||||
//Don't delay boot if no splash image is on the SD
|
//Don't delay boot if no splash image is on the SD
|
||||||
if(fileRead(fb->top_left, "/aurei/splash.bin", 0x46500) +
|
if(fileRead(fb->top_left, "/aurei/splash.bin", 0x46500) +
|
||||||
fileRead(fb->bottom, "/aurei/splashbottom.bin", 0x38400)){
|
fileRead(fb->bottom, "/aurei/splashbottom.bin", 0x38400))
|
||||||
u64 i = 0x1300000; while(--i) __asm("mov r0, r0"); //Less Ghetto sleep func
|
{
|
||||||
|
u64 i = 0x1300000;
|
||||||
|
while(--i) __asm("mov r0, r0"); //Less Ghetto sleep func
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawCharacter(char character, int posX, int posY, u32 color){
|
void drawCharacter(char character, int posX, int posY, u32 color)
|
||||||
|
{
|
||||||
u8 *const select = fb->top_left;
|
u8 *const select = fb->top_left;
|
||||||
|
|
||||||
for(int y = 0; y < 8; y++){
|
for(int y = 0; y < 8; y++)
|
||||||
|
{
|
||||||
char charPos = font[character * 8 + y];
|
char charPos = font[character * 8 + y];
|
||||||
|
|
||||||
for(int x = 7; x >= 0; x--){
|
for(int x = 7; x >= 0; x--)
|
||||||
|
{
|
||||||
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||||
|
|
||||||
if ((charPos >> x) & 1) {
|
if ((charPos >> x) & 1)
|
||||||
|
{
|
||||||
select[screenPos] = color >> 16;
|
select[screenPos] = color >> 16;
|
||||||
select[screenPos + 1] = color >> 8;
|
select[screenPos + 1] = color >> 8;
|
||||||
select[screenPos + 2] = color;
|
select[screenPos + 2] = color;
|
||||||
@@ -60,15 +69,20 @@ void drawCharacter(char character, int posX, int posY, u32 color){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int drawString(const char *string, int posX, int posY, u32 color){
|
int drawString(const char *string, int posX, int posY, u32 color)
|
||||||
|
{
|
||||||
int length = strlen(string);
|
int length = strlen(string);
|
||||||
|
|
||||||
for(int i = 0, line_i = 0; i < length; i++, line_i++){
|
for(int i = 0, line_i = 0; i < length; i++, line_i++)
|
||||||
if(string[i] == '\n'){
|
{
|
||||||
|
if(string[i] == '\n')
|
||||||
|
{
|
||||||
posY += SPACING_Y;
|
posY += SPACING_Y;
|
||||||
line_i = 0;
|
line_i = 0;
|
||||||
i++;
|
i++;
|
||||||
} else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X){
|
}
|
||||||
|
else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X)
|
||||||
|
{
|
||||||
// Make sure we never get out of the screen.
|
// Make sure we never get out of the screen.
|
||||||
posY += SPACING_Y;
|
posY += SPACING_Y;
|
||||||
line_i = 2; // Little offset so we know the same string continues.
|
line_i = 2; // Little offset so we know the same string continues.
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
|
|
||||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND){
|
u32 getEmunandSect(u32 *off, u32 *head, u32 *emuNAND)
|
||||||
|
{
|
||||||
u8 *const temp = (u8 *)0x24300000;
|
u8 *const temp = (u8 *)0x24300000;
|
||||||
|
|
||||||
const u32 nandSize = getMMCDevice(0)->total_size;
|
const u32 nandSize = getMMCDevice(0)->total_size;
|
||||||
@@ -16,28 +17,40 @@ void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND){
|
|||||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||||
|
|
||||||
//Check for RedNAND
|
//Check for RedNAND
|
||||||
if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0){
|
if(sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) == 0)
|
||||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
{
|
||||||
|
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||||
|
{
|
||||||
*off = nandOffset + 1;
|
*off = nandOffset + 1;
|
||||||
*head = nandOffset + 1;
|
*head = nandOffset + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for Gateway emuNAND
|
//Check for Gateway emuNAND
|
||||||
else if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0){
|
else if(sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) == 0)
|
||||||
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC){
|
{
|
||||||
|
if(*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||||
|
{
|
||||||
*off = nandOffset;
|
*off = nandOffset;
|
||||||
*head = nandOffset + nandSize;
|
*head = nandOffset + nandSize;
|
||||||
}
|
}
|
||||||
//Fallback to the first emuNAND if there's no second one
|
|
||||||
else if(*emuNAND == 2){
|
/* Fallback to the first emuNAND if there's no second one,
|
||||||
*emuNAND = 1;
|
or to SysNAND if there isn't any */
|
||||||
getEmunandSect(off, head, emuNAND);
|
else
|
||||||
|
{
|
||||||
|
(*emuNAND)--;
|
||||||
|
if(*emuNAND) getEmunandSect(off, head, emuNAND);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else *emuNAND = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getSDMMC(u8 *pos, u32 size){
|
u32 getSDMMC(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
//Look for struct code
|
//Look for struct code
|
||||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||||
const u8 *off = memsearch(pos, pattern, size, 4) - 1;
|
const u8 *off = memsearch(pos, pattern, size, 4) - 1;
|
||||||
@@ -45,7 +58,8 @@ u32 getSDMMC(u8 *pos, u32 size){
|
|||||||
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
return *(u32 *)(off + 0x0A) + *(u32 *)(off + 0x0E);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff){
|
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff)
|
||||||
|
{
|
||||||
//Look for read/write code
|
//Look for read/write code
|
||||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||||
|
|
||||||
@@ -53,14 +67,16 @@ void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff){
|
|||||||
*writeOff = (u32)memsearch((u8 *)(*readOff + 0xA), pattern, 0x100, 4) - 6;
|
*writeOff = (u32)memsearch((u8 *)(*readOff + 0xA), pattern, 0x100, 4) - 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 *getMPU(u8 *pos, u32 size){
|
u32 *getMPU(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
//Look for MPU pattern
|
//Look for MPU pattern
|
||||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||||
|
|
||||||
return (u32 *)memsearch(pos, pattern, size, 4);
|
return (u32 *)memsearch(pos, pattern, size, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *getEmuCode(u8 *proc9Offset){
|
void *getEmuCode(u8 *proc9Offset)
|
||||||
|
{
|
||||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||||
|
|
||||||
//Looking for the last free space before Process9
|
//Looking for the last free space before Process9
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
#define NCSD_MAGIC (0x4453434E)
|
#define NCSD_MAGIC (0x4453434E)
|
||||||
|
|
||||||
void getEmunandSect(u32 *off, u32 *head, u32 *emuNAND);
|
u32 getEmunandSect(u32 *off, u32 *head, u32 *emuNAND);
|
||||||
u32 getSDMMC(u8 *pos, u32 size);
|
u32 getSDMMC(u8 *pos, u32 size);
|
||||||
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff);
|
void getEmuRW(u8 *pos, u32 size, u32 *readOff, u32 *writeOff);
|
||||||
u32 *getMPU(u8 *pos, u32 size);
|
u32 *getMPU(u8 *pos, u32 size);
|
||||||
|
|||||||
318
source/firm.c
318
source/firm.c
@@ -18,7 +18,7 @@
|
|||||||
#include "../build/patches.h"
|
#include "../build/patches.h"
|
||||||
|
|
||||||
//FIRM patches version
|
//FIRM patches version
|
||||||
#define PATCH_VER 3
|
#define PATCH_VER 4
|
||||||
|
|
||||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||||
static const firmSectionHeader *section;
|
static const firmSectionHeader *section;
|
||||||
@@ -26,7 +26,9 @@ static u8 *arm9Section;
|
|||||||
static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin",
|
static const char *patchedFirms[] = { "/aurei/patched_firmware_sys.bin",
|
||||||
"/aurei/patched_firmware_emu.bin",
|
"/aurei/patched_firmware_emu.bin",
|
||||||
"/aurei/patched_firmware_em2.bin",
|
"/aurei/patched_firmware_em2.bin",
|
||||||
"/aurei/patched_firmware90.bin" };
|
"/aurei/patched_firmware90.bin",
|
||||||
|
"/aurei/patched_firmware_twl.bin",
|
||||||
|
"/aurei/patched_firmware_agb.bin" };
|
||||||
|
|
||||||
static u32 firmSize,
|
static u32 firmSize,
|
||||||
console,
|
console,
|
||||||
@@ -37,34 +39,47 @@ static u32 firmSize,
|
|||||||
usePatchedFirm,
|
usePatchedFirm,
|
||||||
emuOffset,
|
emuOffset,
|
||||||
emuHeader;
|
emuHeader;
|
||||||
|
u32 config;
|
||||||
|
|
||||||
void setupCFW(void){
|
void setupCFW(void)
|
||||||
|
{
|
||||||
//Determine if booting with A9LH
|
//Determine if booting with A9LH
|
||||||
u32 a9lhBoot = (PDN_SPI_CNT == 0x0) ? 1 : 0;
|
u32 a9lhBoot = !PDN_SPI_CNT ? 1 : 0;
|
||||||
|
|
||||||
//Retrieve the last booted FIRM
|
//Retrieve the last booted FIRM
|
||||||
u8 previousFirm = CFG_BOOTENV;
|
u32 previousFirm = CFG_BOOTENV;
|
||||||
|
|
||||||
//Detect the console being used
|
//Detect the console being used
|
||||||
console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
console = (PDN_MPCORE_CFG == 1) ? 0 : 1;
|
||||||
|
|
||||||
//Get pressed buttons
|
//Get pressed buttons
|
||||||
u16 pressed = HID_PAD;
|
u32 pressed = HID_PAD;
|
||||||
|
|
||||||
//Attempt to read the configuration file
|
//Attempt to read the configuration file
|
||||||
const char configPath[] = "aurei/config.bin";
|
const char configPath[] = "/aurei/config.bin";
|
||||||
u32 config = 0,
|
u32 needConfig;
|
||||||
needConfig = fileRead(&config, configPath, 3) ? 1 : 2;
|
if(fileRead(&config, configPath, 3)) needConfig = 1;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config = 0;
|
||||||
|
needConfig = 2;
|
||||||
|
}
|
||||||
|
|
||||||
//Determine if A9LH is installed and the user has an updated sysNAND
|
//Determine if A9LH is installed and the user has an updated sysNAND
|
||||||
u32 updatedSys;
|
u32 updatedSys;
|
||||||
|
|
||||||
if(a9lhBoot || (config >> 2) & 1){
|
if(a9lhBoot || (config >> 2) & 1)
|
||||||
|
{
|
||||||
if(pressed == SAFE_MODE)
|
if(pressed == SAFE_MODE)
|
||||||
error("Using Safe Mode would brick you, or remove A9LH!");
|
error("Using Safe Mode would brick you, or remove A9LH!");
|
||||||
|
|
||||||
a9lhSetup = 1;
|
a9lhSetup = 1;
|
||||||
|
|
||||||
//Check setting for > 9.2 sysNAND
|
//Check setting for > 9.2 sysNAND
|
||||||
updatedSys = config & 1;
|
updatedSys = config & 1;
|
||||||
} else{
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
a9lhSetup = 0;
|
a9lhSetup = 0;
|
||||||
updatedSys = 0;
|
updatedSys = 0;
|
||||||
}
|
}
|
||||||
@@ -73,48 +88,58 @@ void setupCFW(void){
|
|||||||
|
|
||||||
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
/* If booting with A9LH, it's a MCU reboot and a previous configuration exists,
|
||||||
try to force boot options */
|
try to force boot options */
|
||||||
if(a9lhBoot && previousFirm && needConfig == 1){
|
if(a9lhBoot && previousFirm && needConfig == 1)
|
||||||
|
{
|
||||||
//Always force a sysNAND boot when quitting AGB_FIRM
|
//Always force a sysNAND boot when quitting AGB_FIRM
|
||||||
if(previousFirm == 7){
|
if(previousFirm == 7)
|
||||||
|
{
|
||||||
mode = updatedSys ? 1 : (config >> 12) & 1;
|
mode = updatedSys ? 1 : (config >> 12) & 1;
|
||||||
emuNAND = 0;
|
emuNAND = 0;
|
||||||
|
needConfig = 0;
|
||||||
|
|
||||||
//Flag to prevent multiple boot options-forcing
|
//Flag to prevent multiple boot options-forcing
|
||||||
tempConfig |= 1 << 15;
|
tempConfig |= 1 << 15;
|
||||||
needConfig = 0;
|
}
|
||||||
/* Else, force the last used boot options unless A, L or R are pressed
|
/* Else, force the last used boot options unless a payload button or A/L/R are pressed
|
||||||
or the no-forcing flag is set */
|
or the no-forcing flag is set */
|
||||||
} else if(!(pressed & OPTION_BUTTONS) && !((config >> 15) & 1)){
|
else if(!(pressed & OVERRIDE_BUTTONS) && !((config >> 15) & 1))
|
||||||
|
{
|
||||||
mode = (config >> 12) & 1;
|
mode = (config >> 12) & 1;
|
||||||
emuNAND = (config >> 13) & 3;
|
emuNAND = (config >> 13) & 3;
|
||||||
needConfig = 0;
|
needConfig = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(needConfig){
|
//Boot options aren't being forced
|
||||||
|
if(needConfig)
|
||||||
/* If L and one of the payload buttons are pressed, and if not using A9LH
|
{
|
||||||
|
/* If L and R/Select or one of the single payload buttons are pressed and, if not using A9LH,
|
||||||
the Safe Mode combo is not pressed, chainload an external payload */
|
the Safe Mode combo is not pressed, chainload an external payload */
|
||||||
if((pressed & BUTTON_L1) && (pressed & PAYLOAD_BUTTONS) &&
|
if(((pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS)))
|
||||||
pressed != SAFE_MODE) loadPayload();
|
&& pressed != SAFE_MODE)
|
||||||
|
loadPayload();
|
||||||
|
|
||||||
//If no configuration file exists or SELECT is held, load configuration menu
|
//If no configuration file exists or SELECT is held, load configuration menu
|
||||||
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
if(needConfig == 2 || (pressed & BUTTON_SELECT))
|
||||||
configureCFW(configPath, patchedFirms[3]);
|
configureCFW(configPath, patchedFirms);
|
||||||
|
|
||||||
//If screens are inited, load splash screen
|
//If screens are inited, load splash screen
|
||||||
if(PDN_GPU_CNT != 1) loadSplash();
|
if(PDN_GPU_CNT != 1) loadSplash();
|
||||||
|
|
||||||
/* If L is pressed, boot 9.0 FIRM */
|
/* If L is pressed, or L or R are not pressed when it is the default FIRM,
|
||||||
|
boot 9.0 FIRM */
|
||||||
mode = ((config >> 3) & 1) ? ((!(pressed & BUTTON_L1R1)) ? 0 : 1) :
|
mode = ((config >> 3) & 1) ? ((!(pressed & BUTTON_L1R1)) ? 0 : 1) :
|
||||||
((pressed & BUTTON_L1) ? 0 : 1);
|
((pressed & BUTTON_L1) ? 0 : 1);
|
||||||
|
|
||||||
/* If L or R aren't pressed on a 9.0/9.2 sysNAND, or the 9.0 FIRM is selected
|
/* If L or R aren't pressed on a 9.0/9.2 sysNAND, or the 9.0 FIRM is selected
|
||||||
or R is pressed on a > 9.2 sysNAND, boot emuNAND */
|
or R is pressed on a > 9.2 sysNAND, boot emuNAND */
|
||||||
if((updatedSys && (!mode || (pressed & BUTTON_R1))) ||
|
if((updatedSys && (!mode || (pressed & BUTTON_R1))) || (!updatedSys && mode && !(pressed & BUTTON_R1)))
|
||||||
(!updatedSys && mode && !(pressed & BUTTON_R1))){
|
{
|
||||||
//If not 9.0 FIRM and B is pressed, attempt booting the second emuNAND
|
/* If not 9.0 FIRM and the second emuNAND is set as default and B isn't pressed, or vice-versa,
|
||||||
|
attempt to boot it */
|
||||||
emuNAND = (mode && ((!(pressed & BUTTON_B)) == ((config >> 4) & 1))) ? 2 : 1;
|
emuNAND = (mode && ((!(pressed & BUTTON_B)) == ((config >> 4) & 1))) ? 2 : 1;
|
||||||
} else emuNAND = 0;
|
}
|
||||||
|
else emuNAND = 0;
|
||||||
|
|
||||||
/* If tha FIRM patches version is different or user switched to/from A9LH,
|
/* If tha FIRM patches version is different or user switched to/from A9LH,
|
||||||
delete all patched FIRMs */
|
delete all patched FIRMs */
|
||||||
@@ -124,7 +149,8 @@ void setupCFW(void){
|
|||||||
|
|
||||||
u32 usePatchedFirmSet = ((config >> 1) & 1);
|
u32 usePatchedFirmSet = ((config >> 1) & 1);
|
||||||
|
|
||||||
while(1){
|
while(1)
|
||||||
|
{
|
||||||
/* Determine which patched FIRM we need to write or attempt to use (if any).
|
/* Determine which patched FIRM we need to write or attempt to use (if any).
|
||||||
Patched 9.0 FIRM is only needed if "Use pre-patched FIRMs" is set */
|
Patched 9.0 FIRM is only needed if "Use pre-patched FIRMs" is set */
|
||||||
selectedFirm = mode ? (emuNAND ? (emuNAND == 1 ? 2 : 3) : 1) :
|
selectedFirm = mode ? (emuNAND ? (emuNAND == 1 ? 2 : 3) : 1) :
|
||||||
@@ -133,46 +159,53 @@ void setupCFW(void){
|
|||||||
//If "Use pre-patched FIRMs" is set and the appropriate FIRM exists, use it
|
//If "Use pre-patched FIRMs" is set and the appropriate FIRM exists, use it
|
||||||
if(usePatchedFirmSet && fileExists(patchedFirms[selectedFirm - 1]))
|
if(usePatchedFirmSet && fileExists(patchedFirms[selectedFirm - 1]))
|
||||||
usePatchedFirm = 1;
|
usePatchedFirm = 1;
|
||||||
//Detect EmuNAND
|
|
||||||
else if(emuNAND){
|
/* If the user is booting EmuNAND but the chosen one is not found,
|
||||||
getEmunandSect(&emuOffset, &emuHeader, &emuNAND);
|
force 9.6/10.x FIRM and re-detect the patched FIRMs */
|
||||||
//If none exists, force SysNAND + 9.6/10.x FIRM and re-detect patched FIRMs
|
else if(emuNAND && !getEmunandSect(&emuOffset, &emuHeader, &emuNAND))
|
||||||
if(!emuNAND){
|
{
|
||||||
mode = 1;
|
mode = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
tempConfig |= (emuNAND << 13) | (mode << 12);
|
tempConfig |= (emuNAND << 13) | (mode << 12);
|
||||||
|
|
||||||
//If the boot configuration is different from previously, overwrite it
|
//If the boot configuration is different from previously, overwrite it
|
||||||
if(tempConfig != (config & 0xFFF000)){
|
if(tempConfig != (config & 0xFFF000))
|
||||||
|
{
|
||||||
//Preserve user settings (first 12 bits)
|
//Preserve user settings (first 12 bits)
|
||||||
tempConfig |= config & 0xFFF;
|
tempConfig |= config & 0xFFF;
|
||||||
|
|
||||||
fileWrite(&tempConfig, configPath, 3);
|
fileWrite(&tempConfig, configPath, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load FIRM into FCRAM
|
//Load FIRM into FCRAM
|
||||||
void loadFirm(void){
|
void loadFirm(void)
|
||||||
|
{
|
||||||
//If not using an A9LH setup or the patched FIRM, load 9.0 FIRM from NAND
|
//If not using an A9LH setup or the patched FIRM, load 9.0 FIRM from NAND
|
||||||
if(!usePatchedFirm && !a9lhSetup && !mode){
|
if(!usePatchedFirm && !a9lhSetup && !mode)
|
||||||
|
{
|
||||||
//Read FIRM from NAND and write to FCRAM
|
//Read FIRM from NAND and write to FCRAM
|
||||||
firmSize = console ? 0xF2000 : 0xE9000;
|
firmSize = console ? 0xF2000 : 0xE9000;
|
||||||
nandFirm0((u8 *)firm, firmSize, console);
|
nandFirm0((u8 *)firm, firmSize, console);
|
||||||
|
|
||||||
//Check for correct decryption
|
//Check for correct decryption
|
||||||
if(memcmp(firm, "FIRM", 4) != 0)
|
if(memcmp(firm, "FIRM", 4) != 0)
|
||||||
error("Couldn't decrypt NAND FIRM0 (O3DS not on 9.x?)");
|
error("Couldn't decrypt NAND FIRM0 (O3DS not on 9.x?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
//Load FIRM from SD
|
//Load FIRM from SD
|
||||||
else{
|
else
|
||||||
|
{
|
||||||
const char *path = usePatchedFirm ? patchedFirms[selectedFirm - 1] :
|
const char *path = usePatchedFirm ? patchedFirms[selectedFirm - 1] :
|
||||||
(mode ? "/aurei/firmware.bin" : "/aurei/firmware90.bin");
|
(mode ? "/aurei/firmware.bin" : "/aurei/firmware90.bin");
|
||||||
firmSize = fileSize(path);
|
firmSize = fileSize(path);
|
||||||
if(!firmSize) error("aurei/firmware(90).bin doesn't exist");
|
if(!firmSize) error(mode ? "aurei/firmware.bin doesn't exist" :
|
||||||
|
"aurei/firmware90.bin doesn't exist");
|
||||||
fileRead(firm, path, firmSize);
|
fileRead(firm, path, firmSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,16 +213,99 @@ void loadFirm(void){
|
|||||||
|
|
||||||
//Check that the loaded FIRM matches the console
|
//Check that the loaded FIRM matches the console
|
||||||
if((((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
if((((u32)section[2].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
||||||
error("aurei/firmware(90).bin doesn't match this\nconsole, or it's encrypted");
|
error(mode ? "aurei/firmware.bin doesn't match this console,\nor it's encrypted" :
|
||||||
|
"aurei/firmware90.bin doesn't match this console,\nor it's encrypted");
|
||||||
|
|
||||||
arm9Section = (u8 *)firm + section[2].offset;
|
arm9Section = (u8 *)firm + section[2].offset;
|
||||||
|
|
||||||
if(console && !usePatchedFirm) decryptArm9Bin(arm9Section, mode);
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||||
|
if(console && !usePatchedFirm)
|
||||||
|
{
|
||||||
|
decryptArm9Bin(arm9Section, mode);
|
||||||
|
firm->arm9Entry = (u8 *)0x801B01C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void patchTwlAgb(u32 whichFirm)
|
||||||
|
{
|
||||||
|
static firmHeader *const twlAgbFirm = (firmHeader *)0x25000000;
|
||||||
|
|
||||||
|
const char *path = whichFirm ? "/aurei/firmware_agb.bin" : "/aurei/firmware_twl.bin";
|
||||||
|
u32 size = fileSize(path);
|
||||||
|
|
||||||
|
//Skip patching if the file doesn't exist
|
||||||
|
if(!size) return;
|
||||||
|
|
||||||
|
fileRead(twlAgbFirm, path, size);
|
||||||
|
|
||||||
|
static const firmSectionHeader *twlAgbSection = twlAgbFirm->section;
|
||||||
|
|
||||||
|
//Check that the loaded FIRM matches the console
|
||||||
|
if((((u32)twlAgbSection[3].address >> 8) & 0xFF) != (console ? 0x60 : 0x68))
|
||||||
|
error(whichFirm ? "aurei/firmware_agb.bin doesn't match this\nconsole, or it's encrypted" :
|
||||||
|
"aurei/firmware_twl.bin doesn't match this\nconsole, or it's encrypted");
|
||||||
|
|
||||||
|
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||||
|
if(console)
|
||||||
|
{
|
||||||
|
decryptArm9Bin((u8 *)twlAgbFirm + twlAgbSection[3].offset, 0);
|
||||||
|
twlAgbFirm->arm9Entry = (u8 *)0x801301C;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct patchData {
|
||||||
|
u32 offset[2];
|
||||||
|
union {
|
||||||
|
u8 type0[8];
|
||||||
|
u16 type1;
|
||||||
|
} patch;
|
||||||
|
u32 type;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct patchData twlPatches[] = {
|
||||||
|
{{0x1650C0, 0x165D64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||||
|
{{0x173A0E, 0x17474A}, { .type1 = 0x2001 }, 1},
|
||||||
|
{{0x174802, 0x17553E}, { .type1 = 0x2000 }, 2},
|
||||||
|
{{0x174964, 0x1756A0}, { .type1 = 0x2000 }, 2},
|
||||||
|
{{0x174D52, 0x175A8E}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174D5E, 0x175A9A}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174D6A, 0x175AA6}, { .type1 = 0x2001 }, 2},
|
||||||
|
{{0x174E56, 0x175B92}, { .type1 = 0x2001 }, 1},
|
||||||
|
{{0x174E58, 0x175B94}, { .type1 = 0x4770 }, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct patchData agbPatches[] = {
|
||||||
|
{{0x9D2A8, 0x9DF64}, {{ 6, 0x00, 0x20, 0x4E, 0xB0, 0x70, 0xBD }}, 0},
|
||||||
|
{{0xD7A12, 0xD8B8A}, { .type1 = 0xEF26 }, 1}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
||||||
|
if the matching option was enabled (keep it as last) */
|
||||||
|
u32 numPatches = whichFirm ? (sizeof(agbPatches) / sizeof(struct patchData)) - !((config >> 6) & 1) :
|
||||||
|
(sizeof(twlPatches) / sizeof(struct patchData));
|
||||||
|
const struct patchData *patches = whichFirm ? agbPatches : twlPatches;
|
||||||
|
|
||||||
|
//Patch
|
||||||
|
for(u32 i = 0; i < numPatches; i++)
|
||||||
|
{
|
||||||
|
switch(patches[i].type)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
memcpy((u8 *)twlAgbFirm + patches[i].offset[console], patches[i].patch.type0 + 1, patches[i].patch.type0[0]);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
*(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console] + 2) = 0;
|
||||||
|
case 1:
|
||||||
|
*(u16 *)((u8 *)twlAgbFirm + patches[i].offset[console]) = patches[i].patch.type1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fileWrite(twlAgbFirm, whichFirm ? patchedFirms[5] : patchedFirms[4], size);
|
||||||
}
|
}
|
||||||
|
|
||||||
//NAND redirection
|
//NAND redirection
|
||||||
static inline void loadEmu(u8 *proc9Offset){
|
static inline void patchEmuNAND(u8 *proc9Offset)
|
||||||
|
{
|
||||||
//Copy emuNAND code
|
//Copy emuNAND code
|
||||||
void *emuCodeOffset = getEmuCode(proc9Offset);
|
void *emuCodeOffset = getEmuCode(proc9Offset);
|
||||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||||
@@ -227,49 +343,82 @@ static inline void loadEmu(u8 *proc9Offset){
|
|||||||
*(mpuOffset + 9) = mpuPatch[2];
|
*(mpuOffset + 9) = mpuPatch[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void patchReboots(u8 *proc9Offset)
|
||||||
|
{
|
||||||
|
//Calculate offset for the firmlaunch code
|
||||||
|
void *rebootOffset = getReboot(arm9Section, section[2].size);
|
||||||
|
|
||||||
|
//Calculate offset for the fOpen function
|
||||||
|
u32 fOpenOffset = getfOpen(proc9Offset, rebootOffset);
|
||||||
|
|
||||||
|
//Copy firmlaunch code
|
||||||
|
memcpy(rebootOffset, reboot, reboot_size);
|
||||||
|
|
||||||
|
//Put the fOpen offset in the right location
|
||||||
|
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
||||||
|
*pos_fopen = fOpenOffset;
|
||||||
|
|
||||||
|
//Patch path for emuNAND-patched FIRM
|
||||||
|
if(emuNAND)
|
||||||
|
{
|
||||||
|
void *pos_path = memsearch(rebootOffset, L"sy", reboot_size, 4);
|
||||||
|
memcpy(pos_path, emuNAND == 1 ? L"emu" : L"em2", 5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void injectLoader(void)
|
||||||
|
{
|
||||||
|
u32 loaderOffset,
|
||||||
|
loaderSize;
|
||||||
|
|
||||||
|
getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderOffset, &loaderSize);
|
||||||
|
|
||||||
|
//Check that the injector CXI isn't larger than the original
|
||||||
|
if(injector_size <= (int)loaderSize)
|
||||||
|
{
|
||||||
|
memset((void *)loaderOffset, 0, loaderSize);
|
||||||
|
memcpy((void *)loaderOffset, injector, injector_size);
|
||||||
|
|
||||||
|
//Patch content size and ExeFS size to match the repaced loader's ones
|
||||||
|
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
||||||
|
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Patches
|
//Patches
|
||||||
void patchFirm(void){
|
void patchFirm(void)
|
||||||
|
{
|
||||||
|
if(mode)
|
||||||
|
{
|
||||||
|
//Only patch AGB_FIRM/TWL_FIRM if the patched ones don't already exist
|
||||||
|
if(!fileExists(patchedFirms[4])) patchTwlAgb(0);
|
||||||
|
if(!fileExists(patchedFirms[5])) patchTwlAgb(1);
|
||||||
|
}
|
||||||
|
|
||||||
//Skip patching
|
//Skip patching
|
||||||
if(usePatchedFirm) return;
|
if(usePatchedFirm) return;
|
||||||
|
|
||||||
if(mode || emuNAND){
|
if(mode || emuNAND)
|
||||||
|
{
|
||||||
//Find the Process9 NCCH location
|
//Find the Process9 NCCH location
|
||||||
u8 *proc9Offset = getProc9(arm9Section, section[2].size);
|
u8 *proc9Offset = getProc9(arm9Section, section[2].size);
|
||||||
|
|
||||||
//Apply emuNAND patches
|
//Apply emuNAND patches
|
||||||
if(emuNAND) loadEmu(proc9Offset);
|
if(emuNAND) patchEmuNAND(proc9Offset);
|
||||||
|
|
||||||
//Patch FIRM reboots, not on 9.0 FIRM as it breaks firmlaunchhax
|
//Apply FIRM reboot patches, not on 9.0 FIRM as it breaks firmlaunchhax
|
||||||
if(mode){
|
if(mode) patchReboots(proc9Offset);
|
||||||
//Calculate offset for the firmlaunch code
|
|
||||||
void *rebootOffset = getReboot(arm9Section, section[2].size);
|
|
||||||
//Calculate offset for the fOpen function
|
|
||||||
u32 fOpenOffset = getfOpen(proc9Offset, rebootOffset);
|
|
||||||
|
|
||||||
//Copy firmlaunch code
|
|
||||||
memcpy(rebootOffset, reboot, reboot_size);
|
|
||||||
|
|
||||||
//Put the fOpen offset in the right location
|
|
||||||
u32 *pos_fopen = (u32 *)memsearch(rebootOffset, "OPEN", reboot_size, 4);
|
|
||||||
*pos_fopen = fOpenOffset;
|
|
||||||
|
|
||||||
//Patch path for emuNAND-patched FIRM
|
|
||||||
if(emuNAND){
|
|
||||||
void *pos_path = memsearch(rebootOffset, L"sy", reboot_size, 4);
|
|
||||||
memcpy(pos_path, emuNAND == 1 ? L"emu" : L"em2", 5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(a9lhSetup && !emuNAND){
|
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||||
//Patch FIRM partitions writes on sysNAND to protect A9LH
|
if(a9lhSetup && !emuNAND)
|
||||||
|
{
|
||||||
u16 *writeOffset = getFirmWrite(arm9Section, section[2].size);
|
u16 *writeOffset = getFirmWrite(arm9Section, section[2].size);
|
||||||
*writeOffset = writeBlock[0];
|
*writeOffset = writeBlock[0];
|
||||||
*(writeOffset + 1) = writeBlock[1];
|
*(writeOffset + 1) = writeBlock[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
//Disable signature checks
|
//Apply signature checks patches
|
||||||
u32 sigOffset,
|
u32 sigOffset,
|
||||||
sigOffset2;
|
sigOffset2;
|
||||||
|
|
||||||
@@ -279,22 +428,7 @@ void patchFirm(void){
|
|||||||
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
*((u16 *)sigOffset2 + 1) = sigPatch[1];
|
||||||
|
|
||||||
//Replace the FIRM loader with the injector
|
//Replace the FIRM loader with the injector
|
||||||
u32 loaderOffset,
|
injectLoader();
|
||||||
loaderSize;
|
|
||||||
|
|
||||||
getLoader((u8 *)firm + section[0].offset, section[0].size, &loaderOffset, &loaderSize);
|
|
||||||
//Check that the injector CXI isn't larger than the original
|
|
||||||
if(injector_size <= (int)loaderSize){
|
|
||||||
memset((void *)loaderOffset, 0, loaderSize);
|
|
||||||
memcpy((void *)loaderOffset, injector, injector_size);
|
|
||||||
//Patch content size and ExeFS size to match the repaced loader's ones
|
|
||||||
*((u32 *)loaderOffset + 0x41) = loaderSize / 0x200;
|
|
||||||
*((u32 *)loaderOffset + 0x69) = loaderSize / 0x200 - 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Patch ARM9 entrypoint on N3DS to skip arm9loader
|
|
||||||
if(console)
|
|
||||||
firm->arm9Entry = (u8 *)0x801B01C;
|
|
||||||
|
|
||||||
//Write patched FIRM to SD if needed
|
//Write patched FIRM to SD if needed
|
||||||
if(selectedFirm)
|
if(selectedFirm)
|
||||||
@@ -302,8 +436,8 @@ void patchFirm(void){
|
|||||||
error("Couldn't write the patched FIRM (no free space?)");
|
error("Couldn't write the patched FIRM (no free space?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void launchFirm(void){
|
void launchFirm(void)
|
||||||
|
{
|
||||||
if(console && mode) setKeyXs(arm9Section);
|
if(console && mode) setKeyXs(arm9Section);
|
||||||
|
|
||||||
//Copy firm partitions to respective memory locations
|
//Copy firm partitions to respective memory locations
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define PDN_MPCORE_CFG (*(vu8 *)0x10140FFC)
|
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||||
#define PDN_SPI_CNT (*(vu8 *)0x101401C0)
|
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||||
|
|
||||||
//FIRM Header layout
|
//FIRM Header layout
|
||||||
typedef struct firmSectionHeader {
|
typedef struct firmSectionHeader {
|
||||||
|
|||||||
21
source/fs.c
21
source/fs.c
@@ -9,18 +9,21 @@
|
|||||||
|
|
||||||
static FATFS fs;
|
static FATFS fs;
|
||||||
|
|
||||||
u32 mountSD(void){
|
u32 mountSD(void)
|
||||||
|
{
|
||||||
if(f_mount(&fs, "0:", 1) != FR_OK) return 0;
|
if(f_mount(&fs, "0:", 1) != FR_OK) return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 fileRead(void *dest, const char *path, u32 size){
|
u32 fileRead(void *dest, const char *path, u32 size)
|
||||||
|
{
|
||||||
FRESULT fr;
|
FRESULT fr;
|
||||||
FIL fp;
|
FIL fp;
|
||||||
unsigned int br = 0;
|
unsigned int br = 0;
|
||||||
|
|
||||||
fr = f_open(&fp, path, FA_READ);
|
fr = f_open(&fp, path, FA_READ);
|
||||||
if(fr == FR_OK){
|
if(fr == FR_OK)
|
||||||
|
{
|
||||||
if(!size) size = f_size(&fp);
|
if(!size) size = f_size(&fp);
|
||||||
fr = f_read(&fp, dest, size, &br);
|
fr = f_read(&fp, dest, size, &br);
|
||||||
}
|
}
|
||||||
@@ -29,7 +32,8 @@ u32 fileRead(void *dest, const char *path, u32 size){
|
|||||||
return fr ? 0 : 1;
|
return fr ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 fileWrite(const void *buffer, const char *path, u32 size){
|
u32 fileWrite(const void *buffer, const char *path, u32 size)
|
||||||
|
{
|
||||||
FRESULT fr;
|
FRESULT fr;
|
||||||
FIL fp;
|
FIL fp;
|
||||||
unsigned int br = 0;
|
unsigned int br = 0;
|
||||||
@@ -41,7 +45,8 @@ u32 fileWrite(const void *buffer, const char *path, u32 size){
|
|||||||
return fr ? 0 : 1;
|
return fr ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 fileSize(const char *path){
|
u32 fileSize(const char *path)
|
||||||
|
{
|
||||||
FIL fp;
|
FIL fp;
|
||||||
u32 size = 0;
|
u32 size = 0;
|
||||||
|
|
||||||
@@ -52,7 +57,8 @@ u32 fileSize(const char *path){
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 fileExists(const char *path){
|
u32 fileExists(const char *path)
|
||||||
|
{
|
||||||
FIL fp;
|
FIL fp;
|
||||||
u32 exists = 0;
|
u32 exists = 0;
|
||||||
|
|
||||||
@@ -62,6 +68,7 @@ u32 fileExists(const char *path){
|
|||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
void fileDelete(const char *path){
|
void fileDelete(const char *path)
|
||||||
|
{
|
||||||
f_unlink(path);
|
f_unlink(path);
|
||||||
}
|
}
|
||||||
40
source/i2c.c
40
source/i2c.c
@@ -10,11 +10,13 @@ static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
|||||||
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline u8 i2cGetDeviceBusId(u8 device_id) {
|
static inline u8 i2cGetDeviceBusId(u8 device_id)
|
||||||
|
{
|
||||||
return dev_data[device_id].bus_id;
|
return dev_data[device_id].bus_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u8 i2cGetDeviceRegAddr(u8 device_id) {
|
static inline u8 i2cGetDeviceRegAddr(u8 device_id)
|
||||||
|
{
|
||||||
return dev_data[device_id].reg_addr;
|
return dev_data[device_id].reg_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +28,8 @@ static vu8* reg_data_addrs[] = {
|
|||||||
(vu8*)(I2C3_REG_OFF + I2C_REG_DATA),
|
(vu8*)(I2C3_REG_OFF + I2C_REG_DATA),
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline vu8* i2cGetDataReg(u8 bus_id) {
|
static inline vu8* i2cGetDataReg(u8 bus_id)
|
||||||
|
{
|
||||||
return reg_data_addrs[bus_id];
|
return reg_data_addrs[bus_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,22 +41,27 @@ static vu8* reg_cnt_addrs[] = {
|
|||||||
(vu8*)(I2C3_REG_OFF + I2C_REG_CNT),
|
(vu8*)(I2C3_REG_OFF + I2C_REG_CNT),
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline vu8* i2cGetCntReg(u8 bus_id) {
|
static inline vu8* i2cGetCntReg(u8 bus_id)
|
||||||
|
{
|
||||||
return reg_cnt_addrs[bus_id];
|
return reg_cnt_addrs[bus_id];
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static inline void i2cWaitBusy(u8 bus_id) {
|
static inline void i2cWaitBusy(u8 bus_id)
|
||||||
|
{
|
||||||
while (*i2cGetCntReg(bus_id) & 0x80);
|
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline u32 i2cGetResult(u8 bus_id) {
|
static inline u32 i2cGetResult(u8 bus_id)
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
|
|
||||||
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void i2cStop(u8 bus_id, u8 arg0) {
|
static void i2cStop(u8 bus_id, u8 arg0)
|
||||||
|
{
|
||||||
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetCntReg(bus_id) = 0xC5;
|
*i2cGetCntReg(bus_id) = 0xC5;
|
||||||
@@ -61,32 +69,40 @@ static void i2cStop(u8 bus_id, u8 arg0) {
|
|||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg) {
|
static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = dev_reg;
|
*i2cGetDataReg(bus_id) = dev_reg;
|
||||||
*i2cGetCntReg(bus_id) = 0xC2;
|
*i2cGetCntReg(bus_id) = 0xC2;
|
||||||
|
|
||||||
return i2cGetResult(bus_id);
|
return i2cGetResult(bus_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 i2cSelectRegister(u8 bus_id, u8 reg) {
|
static u32 i2cSelectRegister(u8 bus_id, u8 reg)
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = reg;
|
*i2cGetDataReg(bus_id) = reg;
|
||||||
*i2cGetCntReg(bus_id) = 0xC0;
|
*i2cGetCntReg(bus_id) = 0xC0;
|
||||||
|
|
||||||
return i2cGetResult(bus_id);
|
return i2cGetResult(bus_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data) {
|
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||||
|
{
|
||||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (u32 i = 0; i < 8; i++)
|
||||||
if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg)) {
|
{
|
||||||
|
if (i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||||
|
{
|
||||||
i2cWaitBusy(bus_id);
|
i2cWaitBusy(bus_id);
|
||||||
*i2cGetDataReg(bus_id) = data;
|
*i2cGetDataReg(bus_id) = data;
|
||||||
*i2cGetCntReg(bus_id) = 0xC1;
|
*i2cGetCntReg(bus_id) = 0xC1;
|
||||||
i2cStop(bus_id, 0);
|
i2cStop(bus_id, 0);
|
||||||
|
|
||||||
if (i2cGetResult(bus_id))
|
if (i2cGetResult(bus_id))
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
|
|
||||||
#define PAYLOAD_ADDRESS 0x24F00000
|
#define PAYLOAD_ADDRESS 0x24F00000
|
||||||
|
|
||||||
void loadPayload(void){
|
void loadPayload(void)
|
||||||
if(fileExists("aurei/payloads/default.bin")){
|
{
|
||||||
|
if(fileExists("aurei/payloads/default.bin"))
|
||||||
|
{
|
||||||
initScreens();
|
initScreens();
|
||||||
memcpy((void *)PAYLOAD_ADDRESS, loader, loader_size);
|
memcpy((void *)PAYLOAD_ADDRESS, loader, loader_size);
|
||||||
((void (*)())PAYLOAD_ADDRESS)();
|
((void (*)())PAYLOAD_ADDRESS)();
|
||||||
|
|||||||
@@ -9,7 +9,8 @@
|
|||||||
#include "fs.h"
|
#include "fs.h"
|
||||||
#include "firm.h"
|
#include "firm.h"
|
||||||
|
|
||||||
void main(void){
|
void main(void)
|
||||||
|
{
|
||||||
mountSD();
|
mountSD();
|
||||||
setupCFW();
|
setupCFW();
|
||||||
loadFirm();
|
loadFirm();
|
||||||
|
|||||||
@@ -8,36 +8,47 @@
|
|||||||
|
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
void memcpy(void *dest, const void *src, u32 size){
|
void memcpy(void *dest, const void *src, u32 size)
|
||||||
|
{
|
||||||
u8 *destc = (u8 *)dest;
|
u8 *destc = (u8 *)dest;
|
||||||
const u8 *srcc = (const u8 *)src;
|
const u8 *srcc = (const u8 *)src;
|
||||||
|
|
||||||
for(u32 i = 0; i < size; i++)
|
for(u32 i = 0; i < size; i++)
|
||||||
destc[i] = srcc[i];
|
destc[i] = srcc[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void memset(void *dest, int filler, u32 size){
|
void memset(void *dest, int filler, u32 size)
|
||||||
|
{
|
||||||
u8 *destc = (u8 *)dest;
|
u8 *destc = (u8 *)dest;
|
||||||
|
|
||||||
for(u32 i = 0; i < size; i++)
|
for(u32 i = 0; i < size; i++)
|
||||||
destc[i] = (u8)filler;
|
destc[i] = (u8)filler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void memset32(void *dest, u32 filler, u32 size){
|
void memset32(void *dest, u32 filler, u32 size)
|
||||||
|
{
|
||||||
u32 *dest32 = (u32 *)dest;
|
u32 *dest32 = (u32 *)dest;
|
||||||
|
|
||||||
for (u32 i = 0; i < size / 4; i++)
|
for (u32 i = 0; i < size / 4; i++)
|
||||||
dest32[i] = filler;
|
dest32[i] = filler;
|
||||||
}
|
}
|
||||||
|
|
||||||
int memcmp(const void *buf1, const void *buf2, u32 size){
|
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||||
|
{
|
||||||
const u8 *buf1c = (const u8 *)buf1;
|
const u8 *buf1c = (const u8 *)buf1;
|
||||||
const u8 *buf2c = (const u8 *)buf2;
|
const u8 *buf2c = (const u8 *)buf2;
|
||||||
for(u32 i = 0; i < size; i++){
|
|
||||||
|
for(u32 i = 0; i < size; i++)
|
||||||
|
{
|
||||||
int cmp = buf1c[i] - buf2c[i];
|
int cmp = buf1c[i] - buf2c[i];
|
||||||
if(cmp) return cmp;
|
if(cmp) return cmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||||
|
{
|
||||||
const u8 *patternc = (const u8 *)pattern;
|
const u8 *patternc = (const u8 *)pattern;
|
||||||
|
|
||||||
//Preprocessing
|
//Preprocessing
|
||||||
@@ -51,7 +62,8 @@ u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize){
|
|||||||
//Searching
|
//Searching
|
||||||
u32 j = 0;
|
u32 j = 0;
|
||||||
|
|
||||||
while(j <= size - patternSize){
|
while(j <= size - patternSize)
|
||||||
|
{
|
||||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||||
return startPos + j;
|
return startPos + j;
|
||||||
j += table[startPos[j + patternSize]];
|
j += table[startPos[j + patternSize]];
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ const u16 writeBlock[2] = {0x2000, 0x46C0};
|
|||||||
* Functions
|
* Functions
|
||||||
**************************************************/
|
**************************************************/
|
||||||
|
|
||||||
u8 *getProc9(u8 *pos, u32 size){
|
u8 *getProc9(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
return memsearch(pos, "ess9", size, 4);
|
return memsearch(pos, "ess9", size, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2){
|
void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2)
|
||||||
|
{
|
||||||
//Look for signature checks
|
//Look for signature checks
|
||||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||||
@@ -36,16 +38,19 @@ void getSigChecks(u8 *pos, u32 size, u32 *off, u32 *off2){
|
|||||||
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
*off2 = (u32)memsearch(pos, pattern2, size, 4) - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *getReboot(u8 *pos, u32 size){
|
void *getReboot(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
//Look for FIRM reboot code
|
//Look for FIRM reboot code
|
||||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||||
|
|
||||||
return memsearch(pos, pattern, size, 4) - 0x10;
|
return memsearch(pos, pattern, size, 4) - 0x10;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
u32 getfOpen(u8 *proc9Offset, void *rebootOffset)
|
||||||
|
{
|
||||||
//Offset Process9 code gets loaded to in memory (defined in ExHeader)
|
//Offset Process9 code gets loaded to in memory (defined in ExHeader)
|
||||||
u32 p9MemAddr = *(u32 *)(proc9Offset + 0xC);
|
u32 p9MemAddr = *(u32 *)(proc9Offset + 0xC);
|
||||||
|
|
||||||
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
|
//Process9 code offset (start of NCCH + ExeFS offset + ExeFS header size)
|
||||||
u32 p9CodeOff = (u32)(proc9Offset - 0x204) + (*(u32 *)(proc9Offset - 0x64) * 0x200) + 0x200;
|
u32 p9CodeOff = (u32)(proc9Offset - 0x204) + (*(u32 *)(proc9Offset - 0x64) * 0x200) + 0x200;
|
||||||
|
|
||||||
@@ -53,7 +58,8 @@ u32 getfOpen(u8 *proc9Offset, void *rebootOffset){
|
|||||||
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
return (u32)rebootOffset + 9 - (-((*(u32 *)rebootOffset & 0x00FFFFFF) << 2) & 0xFFFFF) - p9CodeOff + p9MemAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 *getFirmWrite(u8 *pos, u32 size){
|
u16 *getFirmWrite(u8 *pos, u32 size)
|
||||||
|
{
|
||||||
//Look for FIRM writing code
|
//Look for FIRM writing code
|
||||||
u8 *const off = memsearch(pos, "exe:", size, 4);
|
u8 *const off = memsearch(pos, "exe:", size, 4);
|
||||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||||
@@ -61,7 +67,8 @@ u16 *getFirmWrite(u8 *pos, u32 size){
|
|||||||
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
return (u16 *)memsearch(off - 0x100, pattern, 0x100, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize){
|
void getLoader(u8 *pos, u32 size, u32 *loaderOffset, u32 *loaderSize)
|
||||||
|
{
|
||||||
u8 *const off = memsearch(pos, "loade", size, 5);
|
u8 *const off = memsearch(pos, "loade", size, 5);
|
||||||
|
|
||||||
*loaderOffset = (u32)off - 0x200;
|
*loaderOffset = (u32)off - 0x200;
|
||||||
|
|||||||
@@ -8,13 +8,19 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "screeninit.h"
|
#include "screeninit.h"
|
||||||
|
#include "memory.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
#include "../build/screeninit.h"
|
||||||
|
|
||||||
|
#define SCREENINIT_ADDRESS 0x24F03000
|
||||||
|
|
||||||
static vu32 *const arm11 = (u32 *)0x1FFFFFF8;
|
static vu32 *const arm11 = (u32 *)0x1FFFFFF8;
|
||||||
|
|
||||||
void deinitScreens(void){
|
void deinitScreens(void)
|
||||||
void __attribute__((naked)) ARM11(void){
|
{
|
||||||
|
void __attribute__((naked)) ARM11(void)
|
||||||
|
{
|
||||||
//Disable interrupts
|
//Disable interrupts
|
||||||
__asm(".word 0xF10C01C0");
|
__asm(".word 0xF10C01C0");
|
||||||
|
|
||||||
@@ -28,124 +34,32 @@ void deinitScreens(void){
|
|||||||
|
|
||||||
//Wait for the entry to be set
|
//Wait for the entry to be set
|
||||||
while(!*arm11);
|
while(!*arm11);
|
||||||
|
|
||||||
//Jump to it
|
//Jump to it
|
||||||
((void (*)())*arm11)();
|
((void (*)())*arm11)();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(PDN_GPU_CNT != 1){
|
if(PDN_GPU_CNT != 1)
|
||||||
|
{
|
||||||
*arm11 = (u32)ARM11;
|
*arm11 = (u32)ARM11;
|
||||||
while(*arm11);
|
while(*arm11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void initScreens(void){
|
void initScreens(void)
|
||||||
void __attribute__((naked)) ARM11(void){
|
{
|
||||||
//Disable interrupts
|
if(PDN_GPU_CNT == 1)
|
||||||
__asm(".word 0xF10C01C0");
|
{
|
||||||
|
//Write brightness level for the stub to pick up
|
||||||
|
*(vu32 *)0x24F04000 = (config >> 10) & 3;
|
||||||
|
|
||||||
*(vu32 *)0x10141200 = 0x1007F;
|
memcpy((void *)SCREENINIT_ADDRESS, screeninit, screeninit_size);
|
||||||
*(vu32 *)0x10202014 = 0x00000001;
|
|
||||||
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
|
||||||
*(vu32 *)0x10202240 = 0x45;
|
|
||||||
*(vu32 *)0x10202A40 = 0x45;
|
|
||||||
*(vu32 *)0x10202244 = 0x1023E;
|
|
||||||
*(vu32 *)0x10202A44 = 0x1023E;
|
|
||||||
|
|
||||||
// Top screen
|
*arm11 = SCREENINIT_ADDRESS;
|
||||||
*(vu32 *)0x10400400 = 0x000001c2;
|
|
||||||
*(vu32 *)0x10400404 = 0x000000d1;
|
|
||||||
*(vu32 *)0x10400408 = 0x000001c1;
|
|
||||||
*(vu32 *)0x1040040c = 0x000001c1;
|
|
||||||
*(vu32 *)0x10400410 = 0x00000000;
|
|
||||||
*(vu32 *)0x10400414 = 0x000000cf;
|
|
||||||
*(vu32 *)0x10400418 = 0x000000d1;
|
|
||||||
*(vu32 *)0x1040041c = 0x01c501c1;
|
|
||||||
*(vu32 *)0x10400420 = 0x00010000;
|
|
||||||
*(vu32 *)0x10400424 = 0x0000019d;
|
|
||||||
*(vu32 *)0x10400428 = 0x00000002;
|
|
||||||
*(vu32 *)0x1040042c = 0x00000192;
|
|
||||||
*(vu32 *)0x10400430 = 0x00000192;
|
|
||||||
*(vu32 *)0x10400434 = 0x00000192;
|
|
||||||
*(vu32 *)0x10400438 = 0x00000001;
|
|
||||||
*(vu32 *)0x1040043c = 0x00000002;
|
|
||||||
*(vu32 *)0x10400440 = 0x01960192;
|
|
||||||
*(vu32 *)0x10400444 = 0x00000000;
|
|
||||||
*(vu32 *)0x10400448 = 0x00000000;
|
|
||||||
*(vu32 *)0x1040045C = 0x00f00190;
|
|
||||||
*(vu32 *)0x10400460 = 0x01c100d1;
|
|
||||||
*(vu32 *)0x10400464 = 0x01920002;
|
|
||||||
*(vu32 *)0x10400468 = 0x18300000;
|
|
||||||
*(vu32 *)0x10400470 = 0x80341;
|
|
||||||
*(vu32 *)0x10400474 = 0x00010501;
|
|
||||||
*(vu32 *)0x10400478 = 0;
|
|
||||||
*(vu32 *)0x10400490 = 0x000002D0;
|
|
||||||
*(vu32 *)0x1040049C = 0x00000000;
|
|
||||||
|
|
||||||
// Disco register
|
|
||||||
for(u32 i = 0; i < 256; i++)
|
|
||||||
*(vu32 *)0x10400484 = 0x10101 * i;
|
|
||||||
|
|
||||||
// Bottom screen
|
|
||||||
*(vu32 *)0x10400500 = 0x000001c2;
|
|
||||||
*(vu32 *)0x10400504 = 0x000000d1;
|
|
||||||
*(vu32 *)0x10400508 = 0x000001c1;
|
|
||||||
*(vu32 *)0x1040050c = 0x000001c1;
|
|
||||||
*(vu32 *)0x10400510 = 0x000000cd;
|
|
||||||
*(vu32 *)0x10400514 = 0x000000cf;
|
|
||||||
*(vu32 *)0x10400518 = 0x000000d1;
|
|
||||||
*(vu32 *)0x1040051c = 0x01c501c1;
|
|
||||||
*(vu32 *)0x10400520 = 0x00010000;
|
|
||||||
*(vu32 *)0x10400524 = 0x0000019d;
|
|
||||||
*(vu32 *)0x10400528 = 0x00000052;
|
|
||||||
*(vu32 *)0x1040052c = 0x00000192;
|
|
||||||
*(vu32 *)0x10400530 = 0x00000192;
|
|
||||||
*(vu32 *)0x10400534 = 0x0000004f;
|
|
||||||
*(vu32 *)0x10400538 = 0x00000050;
|
|
||||||
*(vu32 *)0x1040053c = 0x00000052;
|
|
||||||
*(vu32 *)0x10400540 = 0x01980194;
|
|
||||||
*(vu32 *)0x10400544 = 0x00000000;
|
|
||||||
*(vu32 *)0x10400548 = 0x00000011;
|
|
||||||
*(vu32 *)0x1040055C = 0x00f00140;
|
|
||||||
*(vu32 *)0x10400560 = 0x01c100d1;
|
|
||||||
*(vu32 *)0x10400564 = 0x01920052;
|
|
||||||
*(vu32 *)0x10400568 = 0x18300000 + 0x46500;
|
|
||||||
*(vu32 *)0x10400570 = 0x80301;
|
|
||||||
*(vu32 *)0x10400574 = 0x00010501;
|
|
||||||
*(vu32 *)0x10400578 = 0;
|
|
||||||
*(vu32 *)0x10400590 = 0x000002D0;
|
|
||||||
*(vu32 *)0x1040059C = 0x00000000;
|
|
||||||
|
|
||||||
// Disco register
|
|
||||||
for(u32 i = 0; i < 256; i++)
|
|
||||||
*(vu32 *)0x10400584 = 0x10101 * i;
|
|
||||||
|
|
||||||
// Enable backlight
|
|
||||||
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
|
||||||
|
|
||||||
*(vu32 *)0x10400468 = 0x18300000;
|
|
||||||
*(vu32 *)0x1040046c = 0x18300000;
|
|
||||||
*(vu32 *)0x10400494 = 0x18300000;
|
|
||||||
*(vu32 *)0x10400498 = 0x18300000;
|
|
||||||
*(vu32 *)0x10400568 = 0x18346500;
|
|
||||||
*(vu32 *)0x1040056c = 0x18346500;
|
|
||||||
|
|
||||||
//Set CakeBrah framebuffers
|
|
||||||
*((vu32 *)0x23FFFE00) = 0x18300000;
|
|
||||||
*((vu32 *)0x23FFFE04) = 0x18300000;
|
|
||||||
*((vu32 *)0x23FFFE08) = 0x18346500;
|
|
||||||
|
|
||||||
//Clear ARM11 entry offset
|
|
||||||
*arm11 = 0;
|
|
||||||
|
|
||||||
//Wait for the entry to be set
|
|
||||||
while(!*arm11);
|
|
||||||
//Jump to it
|
|
||||||
((void (*)())*arm11)();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(PDN_GPU_CNT == 1){
|
|
||||||
*arm11 = (u32)ARM11;
|
|
||||||
while(*arm11);
|
while(*arm11);
|
||||||
|
|
||||||
|
//Turn on backlight
|
||||||
|
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearScreens();
|
clearScreens();
|
||||||
|
|||||||
@@ -13,5 +13,7 @@
|
|||||||
|
|
||||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||||
|
|
||||||
|
u32 config;
|
||||||
|
|
||||||
void deinitScreens(void);
|
void deinitScreens(void);
|
||||||
void initScreens(void);
|
void initScreens(void);
|
||||||
@@ -50,7 +50,4 @@ _start:
|
|||||||
mov r1, #0x340
|
mov r1, #0x340
|
||||||
str r1, [r0]
|
str r1, [r0]
|
||||||
|
|
||||||
bl main
|
b main
|
||||||
|
|
||||||
.die:
|
|
||||||
b .die
|
|
||||||
|
|||||||
@@ -21,9 +21,10 @@ struct option {
|
|||||||
u32 enabled;
|
u32 enabled;
|
||||||
};
|
};
|
||||||
|
|
||||||
static u16 waitInput(void){
|
static u32 waitInput(void)
|
||||||
|
{
|
||||||
u32 pressedKey = 0;
|
u32 pressedKey = 0;
|
||||||
u16 key;
|
u32 key;
|
||||||
|
|
||||||
//Wait for no keys to be pressed
|
//Wait for no keys to be pressed
|
||||||
while(HID_PAD);
|
while(HID_PAD);
|
||||||
@@ -31,19 +32,23 @@ static u16 waitInput(void){
|
|||||||
do {
|
do {
|
||||||
//Wait for a key to be pressed
|
//Wait for a key to be pressed
|
||||||
while(!HID_PAD);
|
while(!HID_PAD);
|
||||||
|
|
||||||
key = HID_PAD;
|
key = HID_PAD;
|
||||||
|
|
||||||
//Make sure it's pressed
|
//Make sure it's pressed
|
||||||
for(u32 i = 0x13000; i; i--){
|
for(u32 i = 0x13000; i; i--)
|
||||||
|
{
|
||||||
if(key != HID_PAD) break;
|
if(key != HID_PAD) break;
|
||||||
if(i == 1) pressedKey = 1;
|
if(i == 1) pressedKey = 1;
|
||||||
}
|
}
|
||||||
} while(!pressedKey);
|
}
|
||||||
|
while(!pressedKey);
|
||||||
|
|
||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
void configureCFW(const char *configPath, const char *firm90Path){
|
void configureCFW(const char *configPath, const char *patchedFirms[])
|
||||||
|
{
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
||||||
@@ -54,7 +59,8 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
"( ) Force A9LH detection",
|
"( ) Force A9LH detection",
|
||||||
"( ) Use 9.0 FIRM as default",
|
"( ) Use 9.0 FIRM as default",
|
||||||
"( ) Use second EmuNAND as default",
|
"( ) Use second EmuNAND as default",
|
||||||
"( ) Show current NAND in System Settings" };
|
"( ) Show current NAND in System Settings",
|
||||||
|
"( ) Show GBA boot screen in patched AGB_FIRM" };
|
||||||
|
|
||||||
u32 optionsAmount = sizeof(optionsText) / sizeof(char *);
|
u32 optionsAmount = sizeof(optionsText) / sizeof(char *);
|
||||||
struct option options[optionsAmount];
|
struct option options[optionsAmount];
|
||||||
@@ -62,30 +68,43 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
//Read and parse the existing configuration
|
//Read and parse the existing configuration
|
||||||
u32 tempConfig = 0;
|
u32 tempConfig = 0;
|
||||||
fileRead(&tempConfig, configPath, 3);
|
fileRead(&tempConfig, configPath, 3);
|
||||||
|
|
||||||
for(u32 i = 0; i < optionsAmount; i++)
|
for(u32 i = 0; i < optionsAmount; i++)
|
||||||
options[i].enabled = (tempConfig >> i) & 1;
|
options[i].enabled = (tempConfig >> i) & 1;
|
||||||
|
|
||||||
|
options[optionsAmount].enabled = (tempConfig >> 10) & 3;
|
||||||
|
|
||||||
//Pre-select the first configuration option
|
//Pre-select the first configuration option
|
||||||
u32 selectedOption = 0;
|
u32 selectedOption = 0;
|
||||||
|
|
||||||
//Boring configuration menu
|
//Boring configuration menu
|
||||||
while(1){
|
while(1)
|
||||||
|
{
|
||||||
u16 pressed = 0;
|
u16 pressed = 0;
|
||||||
|
|
||||||
do{
|
do {
|
||||||
for(u32 i = 0; i < optionsAmount; i++){
|
options[optionsAmount].posY = drawString("Screen-init brightness: 4( ) 3( ) 2( ) 1( )", 10, 53, selectedOption == optionsAmount ? COLOR_RED : COLOR_WHITE);
|
||||||
options[i].posY = drawString(optionsText[i], 10, !i ? 60 : options[i - 1].posY + SPACING_Y, selectedOption == i ? COLOR_RED : COLOR_WHITE);
|
|
||||||
|
for(u32 i = 0; i < optionsAmount; i++)
|
||||||
|
{
|
||||||
|
options[i].posY = drawString(optionsText[i], 10, !i ? options[optionsAmount].posY + 2 * SPACING_Y : options[i - 1].posY + SPACING_Y, selectedOption == i ? COLOR_RED : COLOR_WHITE);
|
||||||
drawCharacter('x', 10 + SPACING_X, options[i].posY, options[i].enabled ? (selectedOption == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
drawCharacter('x', 10 + SPACING_X, options[i].posY, options[i].enabled ? (selectedOption == i ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
||||||
}
|
}
|
||||||
pressed = waitInput();
|
|
||||||
} while(!(pressed & MENU_BUTTONS));
|
|
||||||
|
|
||||||
switch(pressed){
|
for(u32 i = 0; i < 4; i++)
|
||||||
|
drawCharacter('x', 10 + (26 + 5 * i) * SPACING_X, options[optionsAmount].posY, options[optionsAmount].enabled == i ? (selectedOption == optionsAmount ? COLOR_RED : COLOR_WHITE) : COLOR_BLACK);
|
||||||
|
|
||||||
|
pressed = waitInput();
|
||||||
|
}
|
||||||
|
while(!(pressed & MENU_BUTTONS));
|
||||||
|
|
||||||
|
switch(pressed)
|
||||||
|
{
|
||||||
case BUTTON_UP:
|
case BUTTON_UP:
|
||||||
selectedOption = !selectedOption ? optionsAmount - 1 : selectedOption - 1;
|
selectedOption = !selectedOption ? optionsAmount : selectedOption - 1;
|
||||||
break;
|
break;
|
||||||
case BUTTON_DOWN:
|
case BUTTON_DOWN:
|
||||||
selectedOption = selectedOption == optionsAmount - 1 ? 0 : selectedOption + 1;
|
selectedOption = selectedOption >= optionsAmount - 1 ? 0 : selectedOption + 1;
|
||||||
break;
|
break;
|
||||||
case BUTTON_LEFT:
|
case BUTTON_LEFT:
|
||||||
selectedOption = 0;
|
selectedOption = 0;
|
||||||
@@ -94,7 +113,8 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
selectedOption = optionsAmount - 1;
|
selectedOption = optionsAmount - 1;
|
||||||
break;
|
break;
|
||||||
case BUTTON_A:
|
case BUTTON_A:
|
||||||
options[selectedOption].enabled = !options[selectedOption].enabled;
|
if(selectedOption != optionsAmount) options[selectedOption].enabled = !options[selectedOption].enabled;
|
||||||
|
else options[optionsAmount].enabled = options[optionsAmount].enabled == 3 ? 0 : options[optionsAmount].enabled + 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +122,10 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If the user has been using A9LH and the "Updated SysNAND" setting changed, delete the patched 9.0 FIRM
|
//If the user has been using A9LH and the "Updated SysNAND" setting changed, delete the patched 9.0 FIRM
|
||||||
if(((tempConfig >> 16) & 1) && ((tempConfig & 1) != options[0].enabled)) fileDelete(firm90Path);
|
if(((tempConfig >> 16) & 1) && ((tempConfig & 1) != options[0].enabled)) fileDelete(patchedFirms[3]);
|
||||||
|
|
||||||
|
//If the "Show GBA boot screen in patched AGB_FIRM" setting changed, delete the patched AGB_FIRM
|
||||||
|
if(((tempConfig >> 6) & 1) != options[6].enabled) fileDelete(patchedFirms[5]);
|
||||||
|
|
||||||
//Preserve the last-used boot options (last 12 bits)
|
//Preserve the last-used boot options (last 12 bits)
|
||||||
tempConfig &= 0xFFF000;
|
tempConfig &= 0xFFF000;
|
||||||
@@ -110,6 +133,9 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
//Parse and write the selected options
|
//Parse and write the selected options
|
||||||
for(u32 i = 0; i < optionsAmount; i++)
|
for(u32 i = 0; i < optionsAmount; i++)
|
||||||
tempConfig |= options[i].enabled << i;
|
tempConfig |= options[i].enabled << i;
|
||||||
|
|
||||||
|
tempConfig |= options[optionsAmount].enabled << 10;
|
||||||
|
|
||||||
fileWrite(&tempConfig, configPath, 3);
|
fileWrite(&tempConfig, configPath, 3);
|
||||||
|
|
||||||
//Zero the last booted FIRM flag
|
//Zero the last booted FIRM flag
|
||||||
@@ -120,14 +146,17 @@ void configureCFW(const char *configPath, const char *firm90Path){
|
|||||||
while(1);
|
while(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void deleteFirms(const char *firmPaths[], u32 firms){
|
void deleteFirms(const char *firmPaths[], u32 firms)
|
||||||
while(firms){
|
{
|
||||||
|
while(firms)
|
||||||
|
{
|
||||||
fileDelete(firmPaths[firms - 1]);
|
fileDelete(firmPaths[firms - 1]);
|
||||||
firms--;
|
firms--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void error(const char *message){
|
void error(const char *message)
|
||||||
|
{
|
||||||
initScreens();
|
initScreens();
|
||||||
|
|
||||||
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
#define CFG_BOOTENV (*(vu8 *)0x10010000)
|
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||||
|
|
||||||
void configureCFW(const char *configPath, const char *firm90Path);
|
void configureCFW(const char *configPath, const char *patchedFirms[]);
|
||||||
void deleteFirms(const char *firmPaths[], u32 firms);
|
void deleteFirms(const char *firmPaths[], u32 firms);
|
||||||
void error(const char *message);
|
void error(const char *message);
|
||||||
Reference in New Issue
Block a user