mirror of
https://github.com/AuroraWright/SafeA9LHInstaller.git
synced 2025-06-26 21:52:47 +00:00
Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d55a454b15 | ||
|
2b686a4de7 | ||
|
64b1eefc5d | ||
|
c77746f383 | ||
|
5d1c717cb4 | ||
|
bccdfe4404 | ||
|
04a33143b7 | ||
|
2be5a24262 | ||
|
7067c6d0e8 | ||
|
b1d596177a | ||
|
d605111e28 | ||
|
d95851286c | ||
|
445d22db97 | ||
|
9898020267 | ||
|
3d477d3565 | ||
|
1cad3e44d5 | ||
|
27d7d5c036 | ||
|
d18b0bd7d1 | ||
|
0c11564ac6 | ||
|
4a3a351301 | ||
|
5ded6aa619 | ||
|
c43960f7d4 | ||
|
e1b1b259ac | ||
|
1cc678adc3 | ||
|
68fce834fb | ||
|
051ebad2f8 | ||
|
4ab5ad9a89 | ||
|
5f96d42a9c | ||
|
ae7fc28c74 | ||
|
06abe5a004 | ||
|
ef351c70de |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
out
|
||||
build
|
||||
loader/build
|
||||
loader/arm11/build
|
||||
arm11/build
|
||||
*.bin
|
||||
*.3dsx
|
||||
*.smdh
|
||||
|
2
CakeBrah
2
CakeBrah
@ -1 +1 @@
|
||||
Subproject commit 9f7cea77d4db4d743e45b2e5193df76ffed0a571
|
||||
Subproject commit 1efda4e89476d34aeb307e0acd7a8dbcd1344601
|
2
CakeHax
2
CakeHax
@ -1 +1 @@
|
||||
Subproject commit 5245c7b9dc232956a8578a36468f9024d8cf7001
|
||||
Subproject commit 329212a8e09d4718e304cb9d94a0e10f66d9813d
|
28
Makefile
28
Makefile
@ -4,18 +4,12 @@ ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
include $(DEVKITARM)/base_tools
|
||||
|
||||
name := SafeA9LHInstaller
|
||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||
|
||||
dir_source := source
|
||||
dir_loader := loader
|
||||
dir_cakehax := CakeHax
|
||||
dir_cakebrah := CakeBrah
|
||||
dir_build := build
|
||||
@ -30,14 +24,6 @@ objects= $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/loader.bin.o
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $(dir_build)/bundled.h
|
||||
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $(dir_build)/bundled.h
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: launcher a9lh cakebrah
|
||||
|
||||
@ -55,7 +41,6 @@ release: $(dir_out)/$(name)$(revision).7z
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) $(FLAGS) -C $(dir_cakehax) clean
|
||||
@$(MAKE) $(FLAGS) -C $(dir_cakebrah) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
@ -79,17 +64,11 @@ $(dir_out)/$(name)$(revision).7z: all
|
||||
@7z a -mx $@ ./$(@D)/*
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
$(OBJCOPY) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/main.elf: $(bundled) $(objects)
|
||||
$(dir_build)/main.elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
$(dir_build)/loader.bin: $(dir_loader) $(dir_build)
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
$(dir_build)/installer.o: CFLAGS += -DTITLE="\"$(name) $(revision)\""
|
||||
|
||||
@ -100,4 +79,3 @@ $(dir_build)/%.o: $(dir_source)/%.c
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
**Usage / Features:**
|
||||
|
||||
*DO NOT USE THIS VERSION TO INSTALL ON NEW 3DS UNLESS YOU HAVE AN HARDMOD, CAUSES RANDOM BRICKS! UNINSTALLATION IS FINE*
|
||||
For a comprehensive guide to installing A9LH and to 3DS hacking in general, refer to [Plailect's guide](https://github.com/Plailect/Guide/wiki/Get-Started).
|
||||
For other details about the program, refer to the [GBATemp thread](http://gbatemp.net/threads/safea9lhinstaller.419577/).
|
||||
|
||||
|
14
linker.ld
14
linker.ld
@ -1,11 +1,15 @@
|
||||
OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
|
||||
OUTPUT_ARCH(arm)
|
||||
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x23F00000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
|
||||
.text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); }
|
||||
.rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); }
|
||||
.data : ALIGN(4) { *(.data*); . = ALIGN(4); }
|
||||
.bss : ALIGN(8) { __bss_start = .; *(.bss* COMMON); . = ALIGN(8); __bss_end = .; }
|
||||
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
@ -1,67 +0,0 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_arm11 := arm11
|
||||
dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/arm11.bin.o
|
||||
|
||||
define bin2o
|
||||
bin2s $< | $(AS) -o $(@)
|
||||
echo "extern const u8" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $(dir_build)/bundled.h
|
||||
echo "extern const u32" `(echo $(<F) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $(dir_build)/bundled.h
|
||||
endef
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@$(MAKE) -C $(dir_arm11) clean
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(bundled) $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.bin.o: $(dir_build)/%.bin
|
||||
@$(bin2o)
|
||||
|
||||
$(dir_build)/arm11.bin: $(dir_arm11)
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $<
|
||||
|
||||
$(dir_build)/memory.o: CFLAGS += -O3
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
@ -1,48 +0,0 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := $(shell basename $(CURDIR))
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
dir_out := ../$(dir_build)
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: $(dir_out)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
$(dir_out)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
@ -1,12 +0,0 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x1FFF4C80;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
#include "types.h"
|
||||
|
||||
void main(void)
|
||||
{
|
||||
vu32 *arm11 = (vu32 *)0x1FFFFFF8;
|
||||
|
||||
//Clear ARM11 entrypoint
|
||||
*arm11 = 0;
|
||||
|
||||
//Wait for the entrypoint to be set
|
||||
while(!*arm11);
|
||||
|
||||
//Jump to it
|
||||
((void (*)())*arm11)();
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Disable interrupts
|
||||
CPSID aif
|
||||
|
||||
b main
|
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//Common data types
|
||||
typedef uint32_t u32;
|
||||
typedef volatile u32 vu32;
|
@ -1,11 +0,0 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x80F0000;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void flushCaches(void);
|
@ -1,54 +0,0 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global flushCaches
|
||||
.type flushCaches, %function
|
||||
flushCaches:
|
||||
@ Clean and flush data cache
|
||||
@ Adpated from http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0155a/ch03s03s05.html ,
|
||||
@ and https://github.com/gemarcano/libctr9_io/blob/master/src/ctr_system_ARM.c#L39 as well
|
||||
@ Note: ARM's example is actually for a 8KB DCache (which is what the 3DS has)
|
||||
@ Implemented in bootROM at address 0xffff0830
|
||||
|
||||
mov r1, #0 @ segment counter
|
||||
outer_loop:
|
||||
mov r0, #0 @ line counter
|
||||
|
||||
inner_loop:
|
||||
orr r2, r1, r0 @ generate segment and line address
|
||||
mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line
|
||||
add r0, #0x20 @ increment to next line
|
||||
cmp r0, #0x400
|
||||
bne inner_loop
|
||||
|
||||
add r1, #0x40000000
|
||||
cmp r1, #0
|
||||
bne outer_loop
|
||||
|
||||
mcr p15, 0, r1, c7, c10, 4 @ drain write buffer
|
||||
|
||||
@ Flush instruction cache
|
||||
mcr p15, 0, r1, c7, c5, 0
|
||||
|
||||
bx lr
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include "memory.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
#define A11_PAYLOAD_LOC 0x1FFF4C80 //Keep in mind this needs to be changed in the ld script for arm11 too
|
||||
#define A11_ENTRYPOINT 0x1FFFFFF8
|
||||
|
||||
static inline void ownArm11(void)
|
||||
{
|
||||
memcpy((void *)A11_PAYLOAD_LOC, arm11_bin, arm11_bin_size);
|
||||
|
||||
*(vu32 *)A11_ENTRYPOINT = 1;
|
||||
*(vu32 *)0x1FFAED80 = 0xE51FF004;
|
||||
*(vu32 *)0x1FFAED84 = A11_PAYLOAD_LOC;
|
||||
*(vu8 *)0x1FFFFFF0 = 2;
|
||||
while(*(vu32 *)A11_ENTRYPOINT != 0);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
ownArm11();
|
||||
|
||||
vu32 *magic = (vu32 *)0x25000000;
|
||||
magic[0] = 0xABADCAFE;
|
||||
magic[1] = 0xDEADCAFE;
|
||||
|
||||
//Ensure that all memory transfers have completed and that the caches have been flushed
|
||||
flushCaches();
|
||||
|
||||
((void (*)())0x23F00000)();
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* memory.c
|
||||
*/
|
||||
|
||||
#include "memory.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size)
|
||||
{
|
||||
u8 *destc = (u8 *)dest;
|
||||
const u8 *srcc = (const u8 *)src;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/*
|
||||
* memory.h
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
@ -1,86 +0,0 @@
|
||||
@ This file is part of Luma3DS
|
||||
@ Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
@
|
||||
@ This program is free software: you can redistribute it and/or modify
|
||||
@ it under the terms of the GNU General Public License as published by
|
||||
@ the Free Software Foundation, either version 3 of the License, or
|
||||
@ (at your option) any later version.
|
||||
@
|
||||
@ This program is distributed in the hope that it will be useful,
|
||||
@ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
@ GNU General Public License for more details.
|
||||
@
|
||||
@ You should have received a copy of the GNU General Public License
|
||||
@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
@
|
||||
@ Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
@ reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
@ Notices displayed by works containing it.
|
||||
|
||||
@ Thanks to the numerous people who took part in writing this file
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
|
||||
@ Disable interrupts
|
||||
mrs r0, cpsr
|
||||
orr r0, #0x1C0
|
||||
msr cpsr_cx, r0
|
||||
|
||||
@ Disable caches / MPU
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||
bic r0, #(1<<12) @ - instruction cache disable
|
||||
bic r0, #(1<<2) @ - data cache disable
|
||||
bic r0, #(1<<0) @ - mpu disable
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
||||
|
||||
@ Flush caches
|
||||
bl flushCaches
|
||||
|
||||
@ Give read/write access to all the memory regions
|
||||
ldr r0, =0x3333333
|
||||
mcr p15, 0, r0, c5, c0, 2 @ write data access
|
||||
mcr p15, 0, r0, c5, c0, 3 @ write instruction access
|
||||
|
||||
@ Set MPU permissions and cache settings
|
||||
ldr r0, =0xFFFF001D @ ffff0000 32k | bootrom (unprotected part)
|
||||
ldr r1, =0x01FF801D @ 01ff8000 32k | itcm
|
||||
ldr r2, =0x08000029 @ 08000000 2M | arm9 mem (O3DS / N3DS)
|
||||
ldr r3, =0x10000029 @ 10000000 2M | io mem (ARM9 / first 2MB)
|
||||
ldr r4, =0x20000037 @ 20000000 256M | fcram (O3DS / N3DS)
|
||||
ldr r5, =0x1FF00027 @ 1FF00000 1M | dsp / axi wram
|
||||
ldr r6, =0x1800002D @ 18000000 8M | vram (+ 2MB)
|
||||
mov r7, #0
|
||||
mov r8, #0x15
|
||||
mcr p15, 0, r0, c6, c0, 0
|
||||
mcr p15, 0, r1, c6, c1, 0
|
||||
mcr p15, 0, r2, c6, c2, 0
|
||||
mcr p15, 0, r3, c6, c3, 0
|
||||
mcr p15, 0, r4, c6, c4, 0
|
||||
mcr p15, 0, r5, c6, c5, 0
|
||||
mcr p15, 0, r6, c6, c6, 0
|
||||
mcr p15, 0, r7, c6, c7, 0
|
||||
mcr p15, 0, r8, c3, c0, 0 @ Write bufferable 0, 2, 4
|
||||
mcr p15, 0, r8, c2, c0, 0 @ Data cacheable 0, 2, 4
|
||||
mcr p15, 0, r8, c2, c0, 1 @ Inst cacheable 0, 2, 4
|
||||
|
||||
@ Enable caches / MPU / ITCM
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||
orr r0, r0, #(1<<18) @ - ITCM enable
|
||||
orr r0, r0, #(1<<13) @ - alternate exception vectors enable
|
||||
orr r0, r0, #(1<<12) @ - instruction cache enable
|
||||
orr r0, r0, #(1<<2) @ - data cache enable
|
||||
orr r0, r0, #(1<<0) @ - mpu enable
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write control register
|
||||
|
||||
@ Fix mounting of SDMC
|
||||
ldr r0, =0x10000020
|
||||
mov r1, #0x340
|
||||
str r1, [r0]
|
||||
|
||||
b main
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
#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;
|
161
source/3dsheaders.h
Normal file
161
source/3dsheaders.h
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of Luma3DS
|
||||
* Copyright (C) 2016 Aurora Wright, TuxSH
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Additional Terms 7.b of GPLv3 applies to this file: Requiring preservation of specified
|
||||
* reasonable legal notices or author attributions in that material or in the Appropriate Legal
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Adapted from 3DBrew and https://github.com/mid-kid/CakesForeveryWan/blob/master/source/headers.h
|
||||
*/
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 address;
|
||||
u32 phyRegionSize;
|
||||
u32 size;
|
||||
} CodeSetInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 saveDataSize[2];
|
||||
u32 jumpID[2];
|
||||
u8 reserved[0x30];
|
||||
} SystemInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char appTitle[8];
|
||||
u8 reserved1[5];
|
||||
u8 flag;
|
||||
u8 remasterVersion[2];
|
||||
CodeSetInfo textCodeSet;
|
||||
u32 stackSize;
|
||||
CodeSetInfo roCodeSet;
|
||||
u8 reserved2[4];
|
||||
CodeSetInfo dataCodeSet;
|
||||
u32 bssSize;
|
||||
char depends[0x180];
|
||||
SystemInfo systemInfo;
|
||||
} SystemControlInfo;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
SystemControlInfo systemControlInfo;
|
||||
u8 aci[0x200];
|
||||
u8 accessDescSig[0x100];
|
||||
u8 ncchPubKey[0x100];
|
||||
u8 aciLim[0x200];
|
||||
} ExHeader;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 sig[0x100]; //RSA-2048 signature of the NCCH header, using SHA-256
|
||||
char magic[4]; //NCCH
|
||||
u32 contentSize; //Media unit
|
||||
u8 partitionId[8];
|
||||
u8 makerCode[2];
|
||||
u16 version;
|
||||
u8 reserved1[4];
|
||||
u8 programID[8];
|
||||
u8 reserved2[0x10];
|
||||
u8 logoHash[0x20]; //Logo Region SHA-256 hash
|
||||
char productCode[0x10];
|
||||
u8 exHeaderHash[0x20]; //Extended header SHA-256 hash
|
||||
u32 exHeaderSize; //Extended header size
|
||||
u32 reserved3;
|
||||
u8 flags[8];
|
||||
u32 plainOffset; //Media unit
|
||||
u32 plainSize; //Media unit
|
||||
u32 logoOffset; //Media unit
|
||||
u32 logoSize; //Media unit
|
||||
u32 exeFsOffset; //Media unit
|
||||
u32 exeFsSize; //Media unit
|
||||
u32 exeFsHashSize; //Media unit
|
||||
u32 reserved4;
|
||||
u32 romFsOffset; //Media unit
|
||||
u32 romFsSize; //Media unit
|
||||
u32 romFsHashSize; //Media unit
|
||||
u32 reserved5;
|
||||
u8 exeFsHash[0x20]; //ExeFS superblock SHA-256 hash
|
||||
u8 romFsHash[0x20]; //RomFS superblock SHA-256 hash
|
||||
} Ncch;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
Ncch ncch;
|
||||
ExHeader exHeader;
|
||||
} Cxi;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char sigIssuer[0x40];
|
||||
u8 eccPubKey[0x3C];
|
||||
u8 version;
|
||||
u8 caCrlVersion;
|
||||
u8 signerCrlVersion;
|
||||
u8 titleKey[0x10];
|
||||
u8 reserved1;
|
||||
u8 ticketId[8];
|
||||
u8 consoleId[4];
|
||||
u8 titleId[8];
|
||||
u8 reserved2[2];
|
||||
u16 ticketTitleVersion;
|
||||
u8 reserved3[8];
|
||||
u8 licenseType;
|
||||
u8 ticketCommonKeyYIndex; //Ticket common keyY index, usually 0x1 for retail system titles.
|
||||
u8 reserved4[0x2A];
|
||||
u8 unk[4]; //eShop Account ID?
|
||||
u8 reserved5;
|
||||
u8 audit;
|
||||
u8 reserved6[0x42];
|
||||
u8 limits[0x40];
|
||||
u8 contentIndex[0xAC];
|
||||
} Ticket;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 offset;
|
||||
u8 *address;
|
||||
u32 size;
|
||||
u32 procType;
|
||||
u8 hash[0x20];
|
||||
} FirmSection;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic;
|
||||
u32 reserved1;
|
||||
u8 *arm11Entry;
|
||||
u8 *arm9Entry;
|
||||
u8 reserved2[0x30];
|
||||
FirmSection section[4];
|
||||
} Firm;
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u8 keyX[0x10];
|
||||
u8 keyY[0x10];
|
||||
u8 ctr[0x10];
|
||||
char size[8];
|
||||
u8 reserved[8];
|
||||
u8 ctlBlock[0x10];
|
||||
char magic[4];
|
||||
u8 reserved2[0xC];
|
||||
u8 slot0x16keyX[0x10];
|
||||
} Arm9Bin;
|
@ -275,16 +275,22 @@ static void sha(void *res, const void *src, u32 size, u32 mode)
|
||||
static u8 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE];
|
||||
static u8 nandSlot;
|
||||
static u32 fatStart;
|
||||
const u8 __attribute__((aligned(4))) key2s[3][AES_BLOCK_SIZE] = {
|
||||
__attribute__((aligned(4))) const u8 key2s[5][AES_BLOCK_SIZE] = {
|
||||
{0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0},
|
||||
{0xA5, 0x25, 0x87, 0x11, 0x8F, 0x42, 0x9F, 0x3D, 0x65, 0x1D, 0xDD, 0x58, 0x0B, 0x6D, 0xF2, 0x17},
|
||||
{0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75},
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0xF5, 0xF6},
|
||||
{0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75}
|
||||
{0x08, 0x24, 0xD3, 0xCB, 0x4A, 0xE9, 0x4D, 0x62, 0x4D, 0xAA, 0x52, 0x60, 0x47, 0xC5, 0x93, 0x94}
|
||||
},
|
||||
devKey2s[2][AES_BLOCK_SIZE] = {
|
||||
{0xFF, 0x77, 0xA0, 0x9A, 0x99, 0x81, 0xE9, 0x48, 0xEC, 0x51, 0xC9, 0x32, 0x5D, 0x14, 0xEC, 0x25},
|
||||
{0x59, 0x5B, 0x98, 0x5B, 0xD7, 0x74, 0x58, 0x7F, 0x89, 0x30, 0x85, 0x70, 0xD6, 0x24, 0x49, 0x1E}
|
||||
};
|
||||
|
||||
void getNandCtr(void)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE];
|
||||
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
||||
__attribute__((aligned(4))) u8 cid[AES_BLOCK_SIZE],
|
||||
shaSum[SHA_256_HASH_SIZE];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
sha(shaSum, cid, sizeof(cid), SHA_256_MODE);
|
||||
@ -295,9 +301,9 @@ void ctrNandInit(void)
|
||||
{
|
||||
getNandCtr();
|
||||
|
||||
if(isN3DS)
|
||||
if(ISN3DS)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||
__attribute__((aligned(4))) u8 keyY0x5[AES_BLOCK_SIZE] = {0x4D, 0x80, 0x4F, 0x4E, 0x99, 0x90, 0x19, 0x46, 0x13, 0xA2, 0x04, 0xAC, 0x58, 0x44, 0x60, 0xBE};
|
||||
aes_setkey(0x05, keyY0x5, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
nandSlot = 0x05;
|
||||
@ -312,11 +318,11 @@ void ctrNandInit(void)
|
||||
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) tmpCtr[sizeof(nandCtr)];
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, ((sector + fatStart) * 0x200) / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Read
|
||||
//Read from NAND
|
||||
int result = sdmmc_nand_readsectors(sector + fatStart, sectorCount, outbuf);
|
||||
|
||||
//Decrypt
|
||||
@ -328,39 +334,39 @@ int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf)
|
||||
|
||||
void readFirm0(u8 *outbuf, u32 size)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)];
|
||||
memcpy(ctrTmp, nandCtr, sizeof(nandCtr));
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, 0x0B130000 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Read FIRM0 data
|
||||
//Read from NAND
|
||||
sdmmc_nand_readsectors(0x0B130000 / 0x200, size / 0x200, outbuf);
|
||||
|
||||
//Decrypt
|
||||
aes_advctr(ctrTmp, 0x0B130000 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x06);
|
||||
aes(outbuf, outbuf, size / AES_BLOCK_SIZE, ctrTmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes(outbuf, outbuf, size / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
void writeFirm(u8 *inbuf, bool isFirm1, u32 size)
|
||||
{
|
||||
u32 offset = isFirm1 ? 0x0B530000 : 0x0B130000;
|
||||
u8 __attribute__((aligned(4))) ctrTmp[sizeof(nandCtr)];
|
||||
memcpy(ctrTmp, nandCtr, sizeof(nandCtr));
|
||||
__attribute__((aligned(4))) u8 tmpCtr[sizeof(nandCtr)];
|
||||
memcpy(tmpCtr, nandCtr, sizeof(nandCtr));
|
||||
aes_advctr(tmpCtr, offset / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Encrypt FIRM data
|
||||
aes_advctr(ctrTmp, offset / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
//Encrypt
|
||||
aes_use_keyslot(0x06);
|
||||
aes(inbuf, inbuf, size / AES_BLOCK_SIZE, ctrTmp, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes(inbuf, inbuf, size / AES_BLOCK_SIZE, tmpCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
//Write to NAND
|
||||
sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf);
|
||||
}
|
||||
|
||||
void setupKeyslot0x11(const void *otp, bool isA9lh)
|
||||
void setupKeyslot0x11(const void *otp)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE];
|
||||
__attribute__((aligned(4))) u8 shasum[SHA_256_HASH_SIZE];
|
||||
|
||||
//If booting via A9LH, use the leftover contents of the SHA register
|
||||
if(isA9lh) memcpy(shasum, (void *)REG_SHA_HASH, sizeof(shasum));
|
||||
if(ISA9LH) memcpy(shasum, (void *)REG_SHA_HASH, sizeof(shasum));
|
||||
|
||||
//Otherwise, calculate the otp.bin hash
|
||||
else sha(shasum, otp, 0x90, SHA_256_MODE);
|
||||
@ -375,62 +381,64 @@ void generateSector(u8 *keySector, u32 mode)
|
||||
//Inject key2
|
||||
switch(mode)
|
||||
{
|
||||
case 0:
|
||||
memcpy(keySector + AES_BLOCK_SIZE, !ISDEVUNIT ? key2s[1] : devKey2s[1], AES_BLOCK_SIZE);
|
||||
break;
|
||||
case 1:
|
||||
memcpy(keySector + AES_BLOCK_SIZE, keySector, AES_BLOCK_SIZE);
|
||||
break;
|
||||
return;
|
||||
case 2:
|
||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[0], AES_BLOCK_SIZE);
|
||||
break;
|
||||
default:
|
||||
memcpy(keySector + AES_BLOCK_SIZE, key2s[2], AES_BLOCK_SIZE);
|
||||
memcpy(keySector + AES_BLOCK_SIZE, !ISDEVUNIT ? key2s[0] : devKey2s[0], AES_BLOCK_SIZE);
|
||||
break;
|
||||
}
|
||||
|
||||
if(mode != 1)
|
||||
{
|
||||
//Encrypt key sector
|
||||
aes_use_keyslot(0x11);
|
||||
for(u32 i = 0; i < 32; i++)
|
||||
aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_ENCRYPT_MODE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void getSector(u8 *keySector, bool isA9lh)
|
||||
void getSector(u8 *keySector)
|
||||
{
|
||||
//Read keysector from NAND
|
||||
sdmmc_nand_readsectors(0x96, 1, keySector);
|
||||
|
||||
if(isA9lh)
|
||||
{
|
||||
if(!ISA9LH && !ISDEVUNIT) return;
|
||||
|
||||
//Decrypt key sector
|
||||
aes_use_keyslot(0x11);
|
||||
for(u32 i = 0; i < 32; i++)
|
||||
aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool verifyHash(const void *data, u32 size, const u8 *hash)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE];
|
||||
__attribute__((aligned(4))) u8 shasum[SHA_256_HASH_SIZE];
|
||||
sha(shasum, data, size, SHA_256_MODE);
|
||||
|
||||
return memcmp(shasum, hash, sizeof(shasum)) == 0;
|
||||
}
|
||||
|
||||
u32 decryptExeFs(u8 *inbuf)
|
||||
u32 decryptExeFs(Cxi *cxi)
|
||||
{
|
||||
u8 *exeFsOffset = inbuf + *(u32 *)(inbuf + 0x1A0) * 0x200;
|
||||
u32 exeFsSize = *(u32 *)(inbuf + 0x1A4) * 0x200;
|
||||
u8 __attribute__((aligned(4))) ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
u32 exeFsSize = 0;
|
||||
|
||||
if(memcmp(cxi->ncch.magic, "NCCH", 4) != 0) return exeFsSize;
|
||||
|
||||
u8 *exeFsOffset = (u8 *)cxi + (cxi->ncch.exeFsOffset + 1) * 0x200;
|
||||
exeFsSize = (cxi->ncch.exeFsSize - 1) * 0x200;
|
||||
__attribute__((aligned(4))) u8 ncchCtr[AES_BLOCK_SIZE] = {0};
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
ncchCtr[7 - i] = *(inbuf + 0x108 + i);
|
||||
ncchCtr[7 - i] = cxi->ncch.partitionId[i];
|
||||
ncchCtr[8] = 2;
|
||||
|
||||
aes_setkey(0x2C, inbuf, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_setkey(0x2C, cxi, AES_KEYY, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_advctr(ncchCtr, 0x200 / AES_BLOCK_SIZE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes_use_keyslot(0x2C);
|
||||
aes(inbuf, exeFsOffset + 0x200, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
aes(cxi, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCtr, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
return exeFsSize - 0x200;
|
||||
if(memcmp(cxi, "FIRM", 4) != 0) exeFsSize = 0;
|
||||
|
||||
return exeFsSize;
|
||||
}
|
@ -79,16 +79,13 @@
|
||||
#define SHA_224_HASH_SIZE (224 / 8)
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
extern bool isN3DS;
|
||||
const u8 key2s[3][AES_BLOCK_SIZE];
|
||||
|
||||
void getNandCtr(void);
|
||||
void ctrNandInit(void);
|
||||
int ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
void readFirm0(u8 *outbuf, u32 size);
|
||||
void writeFirm(u8 *inbuf, bool isFirm1, u32 size);
|
||||
void setupKeyslot0x11(const void *otp, bool isA9lh);
|
||||
void setupKeyslot0x11(const void *otp);
|
||||
void generateSector(u8 *keySector, u32 mode);
|
||||
void getSector(u8 *keySector, bool isA9lh);
|
||||
void getSector(u8 *keySector);
|
||||
bool verifyHash(const void *data, u32 size, const u8 *hash);
|
||||
u32 decryptExeFs(u8 *inbuf);
|
||||
u32 decryptExeFs(Cxi *cxi);
|
13
source/fatfs/00history.txt
Executable file → Normal file
13
source/fatfs/00history.txt
Executable file → Normal file
@ -212,7 +212,7 @@ R0.10a (January 15, 2014)
|
||||
R0.10b (May 19, 2014)
|
||||
|
||||
Fixed a hard error in the disk I/O layer can collapse the directory entry.
|
||||
Fixed LFN entry is not deleted on delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
|
||||
|
||||
|
||||
|
||||
@ -268,7 +268,7 @@ R0.12a (July 10, 2016)
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Improved f_rename() to be able to rename objects with the same name but case.
|
||||
Made f_rename() be able to rename objects with the same name but case.
|
||||
Fixed an error in the case conversion teble of code page 866. (ff.c)
|
||||
Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
|
||||
Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
|
||||
@ -277,3 +277,12 @@ R0.12b (September 04, 2016)
|
||||
Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
|
||||
Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
|
||||
|
||||
|
||||
|
||||
R0.12c (March 04, 2017)
|
||||
|
||||
Improved write throughput at the fragmented file on the exFAT volume.
|
||||
Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
|
||||
Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
|
||||
Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
|
||||
|
||||
|
16
source/fatfs/00readme.txt
Executable file → Normal file
16
source/fatfs/00readme.txt
Executable file → Normal file
@ -1,21 +1,21 @@
|
||||
FatFs Module Source Files R0.12a
|
||||
FatFs Module Source Files R0.12c
|
||||
|
||||
|
||||
FILES
|
||||
|
||||
00readme.txt This file.
|
||||
history.txt Revision history.
|
||||
ffconf.h Configuration file for FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
00history.txt Revision history.
|
||||
ff.c FatFs module.
|
||||
ffconf.h Configuration file of FatFs module.
|
||||
ff.h Common include file for FatFs and application module.
|
||||
diskio.h Common include file for FatFs and disk I/O module.
|
||||
diskio.c An example of glue function to attach existing disk I/O module to FatFs.
|
||||
integer.h Integer type definitions for FatFs.
|
||||
option Optional external functions.
|
||||
option Optional external modules.
|
||||
|
||||
|
||||
Low level disk I/O module is not included in this archive because the FatFs
|
||||
module is only a generic file system layer and not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module that written
|
||||
to control the target storage device.
|
||||
module is only a generic file system layer and it does not depend on any specific
|
||||
storage device. You have to provide a low level disk I/O module written to
|
||||
control the storage device that attached to the target system.
|
||||
|
||||
|
@ -39,7 +39,7 @@ DSTATUS disk_initialize (
|
||||
{
|
||||
if(pdrv == CTRNAND) ctrNandInit();
|
||||
|
||||
return RES_OK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -55,8 +55,8 @@ DRESULT disk_read (
|
||||
UINT count /* Number of sectors to read */
|
||||
)
|
||||
{
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, (BYTE *)buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, (BYTE *)buff))) ? RES_OK : RES_PARERR;
|
||||
return ((pdrv == SDCARD && !sdmmc_sdcard_readsectors(sector, count, buff)) ||
|
||||
(pdrv == CTRNAND && !ctrNandRead(sector, count, buff))) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +73,7 @@ DRESULT disk_write (
|
||||
UINT count /* Number of sectors to write */
|
||||
)
|
||||
{
|
||||
return (pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, (BYTE *)buff)) ? RES_OK : RES_PARERR;
|
||||
return (pdrv == SDCARD && !sdmmc_sdcard_writesectors(sector, count, buff)) ? RES_OK : RES_PARERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
657
source/fatfs/ff.c
Executable file → Normal file
657
source/fatfs/ff.c
Executable file → Normal file
File diff suppressed because it is too large
Load Diff
25
source/fatfs/ff.h
Executable file → Normal file
25
source/fatfs/ff.h
Executable file → Normal file
@ -1,8 +1,8 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12b /
|
||||
/ FatFs - Generic FAT file system module R0.12c /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
/ Copyright (C) 2017, ChaN, all right reserved.
|
||||
/
|
||||
/ FatFs module is an open source software. Redistribution and use of FatFs in
|
||||
/ source and binary forms, with or without modification, are permitted provided
|
||||
@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 68020 /* Revision ID */
|
||||
#define _FATFS 68300 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -42,13 +42,6 @@ typedef struct {
|
||||
BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */
|
||||
} PARTITION;
|
||||
extern PARTITION VolToPart[]; /* Volume - Partition resolution table */
|
||||
#define LD2PD(vol) (VolToPart[vol].pd) /* Get physical drive number */
|
||||
#define LD2PT(vol) (VolToPart[vol].pt) /* Get partition index */
|
||||
|
||||
#else /* Single partition configuration */
|
||||
#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */
|
||||
#define LD2PT(vol) 0 /* Find first valid partition or in SFD */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@ -140,14 +133,15 @@ typedef struct {
|
||||
FATFS* fs; /* Pointer to the owner file system object */
|
||||
WORD id; /* Owner file system mount ID */
|
||||
BYTE attr; /* Object attribute */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:got flagmented, b2:sub-directory stretched) */
|
||||
BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */
|
||||
DWORD sclust; /* Object start cluster (0:no cluster or root directory) */
|
||||
FSIZE_t objsize; /* Object size (valid when sclust != 0) */
|
||||
#if _FS_EXFAT
|
||||
DWORD n_cont; /* Size of coutiguous part, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_cont; /* Size of first fragment, clusters - 1 (valid when stat == 3) */
|
||||
DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */
|
||||
DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */
|
||||
DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0) */
|
||||
DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0 and non-directory object) */
|
||||
#endif
|
||||
#if _FS_LOCK != 0
|
||||
UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */
|
||||
@ -163,7 +157,7 @@ typedef struct {
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fprt is 0) */
|
||||
DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */
|
||||
DWORD sect; /* Sector number appearing in buf[] (0:invalid) */
|
||||
#if !_FS_READONLY
|
||||
DWORD dir_sect; /* Sector number containing the directory entry */
|
||||
@ -185,7 +179,7 @@ typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
DWORD dptr; /* Current read/write offset */
|
||||
DWORD clust; /* Current cluster */
|
||||
DWORD sect; /* Current sector */
|
||||
DWORD sect; /* Current sector (0:Read operation has terminated) */
|
||||
BYTE* dir; /* Pointer to the directory item in the win[] */
|
||||
BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */
|
||||
#if _USE_LFN != 0
|
||||
@ -285,6 +279,7 @@ TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the fil
|
||||
#define f_size(fp) ((fp)->obj.objsize)
|
||||
#define f_rewind(fp) f_lseek((fp), 0)
|
||||
#define f_rewinddir(dp) f_readdir((dp), 0)
|
||||
#define f_rmdir(path) f_unlink(path)
|
||||
|
||||
#ifndef EOF
|
||||
#define EOF (-1)
|
||||
|
21
source/fatfs/ffconf.h
Executable file → Normal file
21
source/fatfs/ffconf.h
Executable file → Normal file
@ -2,7 +2,7 @@
|
||||
/ FatFs - FAT file system module configuration file
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 68020 /* Revision ID */
|
||||
#define _FFCONF 68300 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@ -73,7 +73,7 @@
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 1 - ASCII (No support of extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
@ -148,7 +148,7 @@
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
/* Number of volumes (logical drives) to be used. (1-10) */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
@ -172,11 +172,11 @@
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ 2048 or 4096) Always set both 512 for most systems, generic memory card and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
/ to variable sector size and GET_SECTOR_SIZE command needs to be implemented to
|
||||
/ the disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
@ -204,7 +204,7 @@
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes.
|
||||
/ At the tiny configuration, size of file object (FIL) is shrinked _MAX_SS bytes.
|
||||
/ Instead of private sector buffer eliminated from the file object, common sector
|
||||
/ buffer in the file system object (FATFS) is used for the file data transfer. */
|
||||
|
||||
@ -212,13 +212,13 @@
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system. (0:Disable or 1:Enable)
|
||||
/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1)
|
||||
/ Note that enabling exFAT discards C89 compatibility. */
|
||||
/ Note that enabling exFAT discards ANSI C (C89) compatibility. */
|
||||
|
||||
|
||||
#define _FS_NORTC 1
|
||||
#define _NORTC_MON 1
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2016
|
||||
#define _NORTC_YEAR 2017
|
||||
/* The option _FS_NORTC switches timestamp functiton. If the system does not have
|
||||
/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable
|
||||
/ the timestamp function. All objects modified by FatFs will have a fixed timestamp
|
||||
@ -258,10 +258,11 @@
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
/* #include <windows.h> // O/S definitions */
|
||||
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
2
source/fatfs/integer.h
Executable file → Normal file
2
source/fatfs/integer.h
Executable file → Normal file
@ -30,7 +30,7 @@ typedef unsigned short WCHAR;
|
||||
typedef long LONG;
|
||||
typedef unsigned long DWORD;
|
||||
|
||||
/* This type MUST be 64-bit (Remove this for C89 compatibility) */
|
||||
/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */
|
||||
typedef unsigned long long QWORD;
|
||||
|
||||
#endif
|
||||
|
0
source/fatfs/option/ccsbcs.c
Executable file → Normal file
0
source/fatfs/option/ccsbcs.c
Executable file → Normal file
@ -63,11 +63,13 @@ static inline void setckl(u32 data)
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||
}
|
||||
|
||||
/*
|
||||
mmcdevice *getMMCDevice(int drive)
|
||||
{
|
||||
if(drive == 0) return &handleNAND;
|
||||
return &handleSD;
|
||||
}
|
||||
*/
|
||||
|
||||
static int geterror(struct mmcdevice *ctx)
|
||||
{
|
||||
@ -470,8 +472,11 @@ void sdmmc_get_cid(bool isNand, u32 *info)
|
||||
sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
|
||||
}
|
||||
|
||||
bool sdmmc_sdcard_init()
|
||||
bool sdmmc_sdcard_init(bool initSd, bool initNand)
|
||||
{
|
||||
InitSD();
|
||||
return (Nand_Init() | SD_Init()) == 0;
|
||||
int ret = 0;
|
||||
if(initNand) ret |= Nand_Init();
|
||||
if(initSd) ret |= SD_Init();
|
||||
return ret == 0;
|
||||
}
|
@ -91,10 +91,10 @@ typedef struct mmcdevice {
|
||||
u32 res;
|
||||
} mmcdevice;
|
||||
|
||||
bool sdmmc_sdcard_init();
|
||||
bool sdmmc_sdcard_init(bool initSd, bool initNand);
|
||||
int sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out);
|
||||
int sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
|
||||
int sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out);
|
||||
int sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in);
|
||||
void sdmmc_get_cid(bool isNand, u32 *info);
|
||||
mmcdevice *getMMCDevice(int drive);
|
||||
//mmcdevice *getMMCDevice(int drive);
|
65
source/fs.c
65
source/fs.c
@ -37,13 +37,12 @@ u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
FIL file;
|
||||
u32 ret = 0;
|
||||
|
||||
if(f_open(&file, path, FA_READ) == FR_OK)
|
||||
{
|
||||
if(f_open(&file, path, FA_READ) != FR_OK) return ret;
|
||||
|
||||
u32 size = f_size(&file);
|
||||
if(size <= maxSize)
|
||||
f_read(&file, dest, size, (unsigned int *)&ret);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -51,53 +50,60 @@ u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
FIL file;
|
||||
bool ret;
|
||||
|
||||
FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
|
||||
if(result == FR_OK)
|
||||
switch(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS))
|
||||
{
|
||||
case FR_OK:
|
||||
{
|
||||
unsigned int written;
|
||||
f_write(&file, buffer, size, &written);
|
||||
f_truncate(&file);
|
||||
f_close(&file);
|
||||
|
||||
ret = (u32)written == size;
|
||||
return (u32)written == size;
|
||||
}
|
||||
else if(result == FR_NO_PATH)
|
||||
{
|
||||
case FR_NO_PATH:
|
||||
for(u32 i = 1; path[i] != 0; i++)
|
||||
if(path[i] == '/')
|
||||
{
|
||||
char folder[i + 1];
|
||||
memcpy(folder, path, i);
|
||||
folder[i] = 0;
|
||||
ret = f_mkdir(folder) == FR_OK;
|
||||
if(!ret) break;
|
||||
f_mkdir(folder);
|
||||
}
|
||||
|
||||
if(ret) ret = fileWrite(buffer, path, size);
|
||||
return fileWrite(buffer, path, size);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else ret = false;
|
||||
|
||||
return ret;
|
||||
void fileDelete(const char *path)
|
||||
{
|
||||
f_unlink(path);
|
||||
}
|
||||
|
||||
void fileRename(const char *oldPath, const char *newPath)
|
||||
{
|
||||
f_rename(oldPath, newPath);
|
||||
}
|
||||
|
||||
u32 firmRead(void *dest)
|
||||
{
|
||||
const char *firmFolders[] = {"00000002", "20000002"};
|
||||
char path[48] = "1:/title/00040138/";
|
||||
concatenateStrings(path, firmFolders[isN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, firmFolders[ISN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, "/content");
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
|
||||
u32 firmVersion = 0xFFFFFFFF,
|
||||
ret = 0;
|
||||
|
||||
if(f_opendir(&dir, path) == FR_OK)
|
||||
{
|
||||
if(f_opendir(&dir, path) != FR_OK) goto exit;
|
||||
|
||||
FILINFO info;
|
||||
|
||||
//Parse the target directory
|
||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0] != 0)
|
||||
{
|
||||
@ -107,16 +113,10 @@ u32 firmRead(void *dest)
|
||||
//Multiple cxis were found
|
||||
if(firmVersion != 0xFFFFFFFF) ret = 1;
|
||||
|
||||
//Convert the .app name to an integer
|
||||
u32 tempVersion = 0;
|
||||
for(char *tmp = info.altname; *tmp != '.'; tmp++)
|
||||
{
|
||||
tempVersion <<= 4;
|
||||
tempVersion += *tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0';
|
||||
}
|
||||
u32 tempVersion = hexAtoi(info.altname, 8);
|
||||
|
||||
//FIRM is equal or newer than 11.0
|
||||
if(tempVersion >= (isN3DS ? 0x21 : 0x52)) ret = 2;
|
||||
//FIRM is newer than 11.3
|
||||
if(!ISDEVUNIT && tempVersion > (ISN3DS ? 0x2D : 0x5C)) ret = 2;
|
||||
|
||||
//Found an older cxi
|
||||
if(tempVersion < firmVersion) firmVersion = tempVersion;
|
||||
@ -124,18 +124,17 @@ u32 firmRead(void *dest)
|
||||
|
||||
f_closedir(&dir);
|
||||
|
||||
if(!ret && firmVersion != 0xFFFFFFFF)
|
||||
{
|
||||
if(ret == 1 || firmVersion == 0xFFFFFFFF) goto exit;
|
||||
|
||||
//Complete the string with the .app name
|
||||
concatenateStrings(path, "/00000000.app");
|
||||
|
||||
//Convert back the .app name from integer to array
|
||||
hexItoa(firmVersion, &path[35], 8);
|
||||
hexItoa(firmVersion, path + 35, 8);
|
||||
|
||||
if(!fileRead(dest, path, 0x100000)) ret = 3;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if(firmVersion == 0xFFFFFFFF) ret = 4;
|
||||
|
||||
return ret;
|
||||
|
@ -24,9 +24,9 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern bool isN3DS;
|
||||
|
||||
bool mountFs(bool isSd);
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||
void fileDelete(const char *path);
|
||||
void fileRename(const char *oldPath, const char *newPath);
|
||||
u32 firmRead(void *dest);
|
@ -2,7 +2,6 @@
|
||||
* installer.c
|
||||
*/
|
||||
|
||||
#include "installer.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "crypto.h"
|
||||
@ -10,80 +9,85 @@
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "types.h"
|
||||
#include "installer.h"
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/bundled.h"
|
||||
|
||||
static const u8 sectorHash[0x20] = {
|
||||
static const u8 sectorHashRetail[SHA_256_HASH_SIZE] = {
|
||||
0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C,
|
||||
0xBA, 0xB2, 0x4B, 0x4E, 0x5F, 0x65, 0xC9, 0x81, 0xCD, 0x7B, 0xE6, 0xF4, 0x38, 0xE6, 0xD9, 0xD3
|
||||
};
|
||||
|
||||
static const u8 firm0Hash[0x20] = {
|
||||
},
|
||||
firm0HashRetail[SHA_256_HASH_SIZE] = {
|
||||
0x6E, 0x4D, 0x14, 0xAD, 0x51, 0x50, 0xA5, 0x9A, 0x87, 0x59, 0x62, 0xB7, 0x09, 0x0A, 0x3C, 0x74,
|
||||
0x4F, 0x72, 0x4B, 0xBD, 0x97, 0x39, 0x33, 0xF2, 0x11, 0xC9, 0x35, 0x22, 0xC8, 0xBB, 0x1C, 0x7D
|
||||
};
|
||||
|
||||
static const u8 firm0A9lhHash[0x20] = {
|
||||
},
|
||||
firm0A9lhHashRetail[SHA_256_HASH_SIZE] = {
|
||||
0x79, 0x3D, 0x35, 0x7B, 0x8F, 0xF1, 0xFC, 0xF0, 0x8F, 0xB6, 0xDB, 0x51, 0x31, 0xD4, 0xA7, 0x74,
|
||||
0x8E, 0xF0, 0x4A, 0xB1, 0xA6, 0x7F, 0xCD, 0xAB, 0x0C, 0x0A, 0xC0, 0x69, 0xA7, 0x9D, 0xC5, 0x04
|
||||
};
|
||||
|
||||
static const u8 firm0100Hash[0x20] = {
|
||||
},
|
||||
firm090A9lhHash[SHA_256_HASH_SIZE] = {
|
||||
0x68, 0x52, 0xCC, 0x21, 0x89, 0xAE, 0x28, 0x38, 0x1A, 0x75, 0x90, 0xE7, 0x38, 0x23, 0x48, 0x41,
|
||||
0x8E, 0x80, 0x78, 0x75, 0x27, 0x64, 0x04, 0xD6, 0x28, 0xD6, 0xFA, 0x39, 0xA8, 0x6F, 0xB0, 0x3F
|
||||
},
|
||||
firm1HashRetail[SHA_256_HASH_SIZE] = {
|
||||
0xD8, 0x2D, 0xB7, 0xB4, 0x38, 0x2B, 0x07, 0x88, 0x99, 0x77, 0x91, 0x0C, 0xC6, 0xEC, 0x6D, 0x87,
|
||||
0x7D, 0x21, 0x79, 0x23, 0xD7, 0x60, 0xAF, 0x4E, 0x8B, 0x3A, 0xAB, 0xB2, 0x63, 0xE4, 0x21, 0xC6
|
||||
};
|
||||
|
||||
static const u8 firm1Hash[0x20] = {
|
||||
0xD2, 0x53, 0xC1, 0xCC, 0x0A, 0x5F, 0xFA, 0xC6, 0xB3, 0x83, 0xDA, 0xC1, 0x82, 0x7C, 0xFB, 0x3B,
|
||||
0x2D, 0x3D, 0x56, 0x6C, 0x6A, 0x1A, 0x8E, 0x52, 0x54, 0xE3, 0x89, 0xC2, 0x95, 0x06, 0x23, 0xE5
|
||||
},
|
||||
sectorHashDev[SHA_256_HASH_SIZE] = {
|
||||
0xB2, 0x91, 0xD9, 0xB1, 0x33, 0x05, 0x79, 0x0D, 0x47, 0xC6, 0x06, 0x98, 0x4C, 0x67, 0xC3, 0x70,
|
||||
0x09, 0x54, 0xE3, 0x85, 0xDE, 0x47, 0x55, 0xAF, 0xC6, 0xCB, 0x1D, 0x8D, 0xC7, 0x84, 0x5A, 0x64
|
||||
},
|
||||
firm0HashDev[SHA_256_HASH_SIZE] = {
|
||||
0xCD, 0x62, 0xA6, 0x58, 0x40, 0x1B, 0x8B, 0x8F, 0xD3, 0x2C, 0x72, 0x58, 0xD8, 0x24, 0x21, 0x36,
|
||||
0xCF, 0x83, 0x40, 0xA3, 0x34, 0x8E, 0xED, 0x33, 0x0A, 0x1A, 0x16, 0x04, 0x49, 0xC9, 0x74, 0x3E
|
||||
},
|
||||
firm0A9lhHashDev[SHA_256_HASH_SIZE] = {
|
||||
0x60, 0xBB, 0xD1, 0x35, 0x44, 0x2F, 0xBD, 0x47, 0x69, 0xBF, 0x36, 0x4B, 0x0B, 0x79, 0x6E, 0x4C,
|
||||
0xE1, 0xB2, 0xDB, 0x7A, 0xAD, 0xF0, 0x04, 0x31, 0xCB, 0xBD, 0x54, 0xD3, 0x99, 0x8C, 0x9C, 0xD2
|
||||
},
|
||||
firm1HashDev[SHA_256_HASH_SIZE] = {
|
||||
0xCD, 0x87, 0x85, 0x33, 0x76, 0xCA, 0x2A, 0x3F, 0xFC, 0x24, 0x4C, 0x29, 0x95, 0x8B, 0xA8, 0x34,
|
||||
0xF2, 0x38, 0x14, 0x58, 0x10, 0x83, 0x56, 0x4F, 0x0D, 0x5A, 0xDB, 0x29, 0x12, 0xD8, 0xA9, 0x84
|
||||
};
|
||||
|
||||
u32 posY;
|
||||
bool isN3DS;
|
||||
|
||||
static void drawTitle(void)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
posY = drawString(TITLE, 10, 10, COLOR_TITLE);
|
||||
posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
//Determine if booting with A9LH
|
||||
bool isA9lh = !PDN_SPI_CNT;
|
||||
bool isOtpless = ISA9LH && *(vu32 *)0x80FD0FC == 0xEAFE4AA3 && magic == 0xDEADCAFE;
|
||||
|
||||
//Detect the console being used
|
||||
isN3DS = PDN_MPCORE_CFG == 7;
|
||||
if(!isOtpless) drawTitle();
|
||||
|
||||
vu32 *magic = (vu32 *)0x25000000;
|
||||
bool isOtpless = isA9lh && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE;
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString(TITLE, 10, 10, COLOR_TITLE);
|
||||
posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE);
|
||||
|
||||
if(!sdmmc_sdcard_init() && !isOtpless)
|
||||
if(!sdmmc_sdcard_init(!isOtpless, true) && !isOtpless)
|
||||
shutdown(1, "Error: failed to initialize SD and NAND");
|
||||
|
||||
u32 pressed;
|
||||
|
||||
if(!isOtpless)
|
||||
{
|
||||
posY = drawString(isA9lh ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
posY = drawString(ISA9LH ? "Press SELECT to update A9LH, START to uninstall" : "Press SELECT for a full install", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
posY = drawString("Press any other button to shutdown", 10, posY, COLOR_WHITE);
|
||||
pressed = waitInput();
|
||||
}
|
||||
else
|
||||
{
|
||||
magic[0] = magic[1] = 0;
|
||||
posY = drawString("Finalizing install...", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
pressed = 0;
|
||||
}
|
||||
|
||||
if(isOtpless || pressed == BUTTON_SELECT) installer(isA9lh, isOtpless);
|
||||
if(pressed == BUTTON_START && isA9lh) uninstaller();
|
||||
if(isOtpless || pressed == BUTTON_SELECT) installer(isOtpless);
|
||||
if(pressed == BUTTON_START && ISA9LH) uninstaller();
|
||||
|
||||
shutdown(0, NULL);
|
||||
}
|
||||
|
||||
static inline void installer(bool isA9lh, bool isOtpless)
|
||||
static inline void installer(bool isOtpless)
|
||||
{
|
||||
bool updateA9lh = false;
|
||||
bool updateKey2 = false,
|
||||
updateFirm0 = false,
|
||||
updateFirm1 = false;
|
||||
u8 otp[256] = {0},
|
||||
keySector[512];
|
||||
|
||||
@ -91,7 +95,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
shutdown(1, "Error: failed to mount the SD card");
|
||||
|
||||
//If making a first install on O3DS, we need the OTP
|
||||
if(!isA9lh && !isN3DS)
|
||||
if(!ISA9LH && (!ISN3DS || ISDEVUNIT))
|
||||
{
|
||||
const char otpPath[] = "a9lh/otp.bin";
|
||||
|
||||
@ -111,7 +115,7 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
}
|
||||
|
||||
//Setup the key sector de/encryption with the SHA register or otp.bin
|
||||
if(isA9lh || !isN3DS) setupKeyslot0x11(otp, isA9lh);
|
||||
if(ISA9LH || !ISN3DS || ISDEVUNIT) setupKeyslot0x11(otp);
|
||||
|
||||
//Calculate the CTR for the 3DS partitions
|
||||
getNandCtr();
|
||||
@ -125,121 +129,187 @@ static inline void installer(bool isA9lh, bool isOtpless)
|
||||
}
|
||||
|
||||
//If booting from A9LH or on N3DS, we can use the key sector from NAND
|
||||
if(isA9lh || isN3DS) getSector(keySector, isA9lh);
|
||||
if(ISA9LH || ISN3DS) getSector(keySector);
|
||||
else
|
||||
{
|
||||
//Read decrypted key sector
|
||||
if(fileRead(keySector, "a9lh/secret_sector.bin", sizeof(keySector)) != sizeof(keySector))
|
||||
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
||||
if(!verifyHash(keySector, sizeof(keySector), sectorHash))
|
||||
if(!verifyHash(keySector, sizeof(keySector), !ISDEVUNIT ? sectorHashRetail : sectorHashDev))
|
||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
if(isA9lh && !isOtpless)
|
||||
if(ISA9LH && !isOtpless)
|
||||
{
|
||||
u32 i;
|
||||
for(i = 1; i < 3; i++)
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
||||
|
||||
if(i == 3) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
if(i == 1) updateA9lh = true;
|
||||
if(!ISDEVUNIT)
|
||||
{
|
||||
for(i = 1; i < 5; i++)
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
||||
}
|
||||
else i = memcmp(keySector + AES_BLOCK_SIZE, devKey2s[1], AES_BLOCK_SIZE) == 0 ? 1 : 5;
|
||||
|
||||
switch(i)
|
||||
{
|
||||
case 5:
|
||||
shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
updateFirm1 = true;
|
||||
updateKey2 = true;
|
||||
break;
|
||||
case 1:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!isA9lh || updateA9lh || isOtpless) generateSector(keySector, (!isA9lh && isN3DS) ? 1 : 0);
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, !ISDEVUNIT ? firm0A9lhHashRetail : firm0A9lhHashDev))
|
||||
{
|
||||
if(ISDEVUNIT || !verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm090A9lhHash))
|
||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||
|
||||
if(!isA9lh || updateA9lh)
|
||||
updateFirm0 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ISA9LH || updateKey2 || isOtpless) generateSector(keySector, (!ISA9LH && ISN3DS && !ISDEVUNIT) ? 1 : 0);
|
||||
|
||||
if(!ISA9LH || updateFirm0)
|
||||
{
|
||||
//Read FIRM0
|
||||
if(fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE) != FIRM0_SIZE)
|
||||
shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size");
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash))
|
||||
if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, !ISDEVUNIT ? firm0HashRetail : firm0HashDev))
|
||||
shutdown(1, "Error: firm0.bin is invalid or corrupted");
|
||||
}
|
||||
else if(!isOtpless && !verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
||||
|
||||
if(!isA9lh)
|
||||
if(!ISA9LH || updateFirm1)
|
||||
{
|
||||
if(isN3DS)
|
||||
{
|
||||
//Read 10.0 FIRM0
|
||||
if(fileRead((void *)FIRM0_100_OFFSET, "a9lh/firm0_100.bin", FIRM0100_SIZE) != FIRM0100_SIZE)
|
||||
shutdown(1, "Error: firm0_100.bin doesn't exist or has a wrong size");
|
||||
if(!verifyHash((void *)FIRM0_100_OFFSET, FIRM0100_SIZE, firm0100Hash))
|
||||
shutdown(1, "Error: firm0_100.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
//Read FIRM1
|
||||
if(fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE) != FIRM1_SIZE)
|
||||
shutdown(1, "Error: firm1.bin doesn't exist or has a wrong size");
|
||||
if(!verifyHash((void *)FIRM1_OFFSET, FIRM1_SIZE, firm1Hash))
|
||||
if(!verifyHash((void *)FIRM1_OFFSET, FIRM1_SIZE, !ISDEVUNIT ? firm1HashRetail : firm1HashDev))
|
||||
shutdown(1, "Error: firm1.bin is invalid or corrupted");
|
||||
}
|
||||
|
||||
if(!ISA9LH && ISN3DS && !ISDEVUNIT)
|
||||
{
|
||||
magic = 0xDEADCAFE;
|
||||
|
||||
fileRename("arm9loaderhax.bin", "arm9loaderhax.bak");
|
||||
|
||||
if(!fileWrite((void *)0x23F00000, "arm9loaderhax.bin", 0x10000))
|
||||
shutdown(1, "Error: couldn't write arm9loaderhax.bin");
|
||||
}
|
||||
|
||||
if(!isOtpless)
|
||||
{
|
||||
bool missingStage1Hash,
|
||||
missingStage2Hash;
|
||||
u8 stageHash[SHA_256_HASH_SIZE];
|
||||
|
||||
//Inject stage1
|
||||
memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE);
|
||||
if(!fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE))
|
||||
u32 stageSize = fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE);
|
||||
if(!stageSize)
|
||||
shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size");
|
||||
|
||||
const u8 zeroes[688] = {0};
|
||||
if(memcmp(zeroes, (void *)STAGE1_OFFSET, 688) == 0)
|
||||
shutdown(1, "Error: the payload_stage1.bin you're attempting\nto install is not compatible");
|
||||
|
||||
//Verify stage1
|
||||
if(fileRead(stageHash, "a9lh/payload_stage1.bin.sha", sizeof(stageHash)) == sizeof(stageHash))
|
||||
{
|
||||
if(!verifyHash((void *)STAGE1_OFFSET, stageSize, stageHash))
|
||||
shutdown(1, "Error: payload_stage1.bin is invalid\nor corrupted");
|
||||
|
||||
missingStage1Hash = false;
|
||||
}
|
||||
else missingStage1Hash = true;
|
||||
|
||||
//Read stage2
|
||||
memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
||||
if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE))
|
||||
stageSize = fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE);
|
||||
if(!stageSize)
|
||||
shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max size");
|
||||
|
||||
//Verify stage2
|
||||
if(fileRead(stageHash, "a9lh/payload_stage2.bin.sha", sizeof(stageHash)) == sizeof(stageHash))
|
||||
{
|
||||
if(!verifyHash((void *)STAGE2_OFFSET, stageSize, stageHash))
|
||||
shutdown(1, "Error: payload_stage2.bin is invalid\nor corrupted");
|
||||
|
||||
missingStage2Hash = false;
|
||||
}
|
||||
else missingStage2Hash = true;
|
||||
|
||||
if(missingStage1Hash || missingStage2Hash)
|
||||
{
|
||||
posY = drawString("Couldn't verify stage1 and/or stage2 integrity!", 10, posY + SPACING_Y, COLOR_RED);
|
||||
posY = drawString("Continuing might be dangerous!", 10, posY, COLOR_RED);
|
||||
inputSequence();
|
||||
}
|
||||
|
||||
posY = drawString("All checks passed, installing...", 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
|
||||
sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET);
|
||||
}
|
||||
|
||||
if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||
if(!isA9lh || updateA9lh || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
||||
|
||||
if(!isA9lh && isN3DS)
|
||||
if(!ISA9LH || updateFirm1) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE);
|
||||
if(!ISA9LH || updateKey2 || isOtpless) sdmmc_nand_writesectors(0x96, 1, keySector);
|
||||
if(!isOtpless) writeFirm((u8 *)FIRM0_OFFSET, false, FIRM0_SIZE);
|
||||
else
|
||||
{
|
||||
*(u32 *)0x80FD0FC = 0xEAFFCBBF; //B 0x80F0000
|
||||
memcpy((void *)0x80F0000, loader_bin, loader_bin_size);
|
||||
*(vu32 *)0x80FD0FC = 0;
|
||||
|
||||
writeFirm((u8 *)FIRM0_100_OFFSET, false, FIRM0_SIZE);
|
||||
drawTitle();
|
||||
|
||||
if(sdmmc_sdcard_init(true, false) && mountFs(true))
|
||||
{
|
||||
fileDelete("arm9loaderhax.bin");
|
||||
fileRename("arm9loaderhax.bak", "arm9loaderhax.bin");
|
||||
}
|
||||
else
|
||||
{
|
||||
posY = drawString("Couldn't remove arm9loaderhax.bin!", 10, posY + SPACING_Y, COLOR_RED);
|
||||
posY = drawString("Do it yourself after the install ends", 10, posY, COLOR_RED);
|
||||
}
|
||||
}
|
||||
|
||||
if(!ISA9LH && ISN3DS && !ISDEVUNIT)
|
||||
{
|
||||
*(vu32 *)0x80FD0FC = 0xEAFE4AA3;
|
||||
|
||||
mcuReboot();
|
||||
}
|
||||
|
||||
writeFirm((u8 *)FIRM0_OFFSET, false, FIRM0_SIZE);
|
||||
|
||||
shutdown(2, isA9lh && !isOtpless ? "Update: success!" : "Full install: success!");
|
||||
shutdown(2, ISA9LH && !isOtpless ? "Update: success!" : "Full install: success!");
|
||||
}
|
||||
|
||||
static inline void uninstaller(void)
|
||||
{
|
||||
u8 keySector[512];
|
||||
|
||||
posY = drawString("You are about to uninstall A9LH!", 10, posY + 10, COLOR_RED);
|
||||
posY = drawString("Doing this will require having 9.0 to reinstall!", 10, posY, COLOR_RED);
|
||||
posY = drawString("If you would like to continue, press:", 10, posY, COLOR_WHITE);
|
||||
posY = drawString("Up, Down, Left, Right, B, A, START, SELECT", 10, posY, COLOR_WHITE);
|
||||
|
||||
u32 unlockSequence[] = { BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_B, BUTTON_A, BUTTON_START, BUTTON_SELECT },
|
||||
sequenceSize = sizeof(unlockSequence) / sizeof(u32);
|
||||
|
||||
for(u32 correctPresses = 0; correctPresses < sequenceSize; correctPresses++)
|
||||
{
|
||||
if(waitInput() != unlockSequence[correctPresses])
|
||||
shutdown(1, "Button sequence not entered correctly");
|
||||
}
|
||||
|
||||
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
||||
if(isN3DS)
|
||||
if(ISN3DS)
|
||||
{
|
||||
setupKeyslot0x11(NULL, true);
|
||||
getSector(keySector, true);
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[1], AES_BLOCK_SIZE) != 0 && memcmp(keySector + AES_BLOCK_SIZE, key2s[2], AES_BLOCK_SIZE) != 0)
|
||||
shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
setupKeyslot0x11(NULL);
|
||||
getSector(keySector);
|
||||
|
||||
u32 i;
|
||||
|
||||
if(!ISDEVUNIT)
|
||||
{
|
||||
for(i = 1; i < 5; i++)
|
||||
if(memcmp(keySector + AES_BLOCK_SIZE, key2s[i], AES_BLOCK_SIZE) == 0) break;
|
||||
}
|
||||
else i = memcmp(keySector + AES_BLOCK_SIZE, devKey2s[1], AES_BLOCK_SIZE) == 0 ? 1 : 5;
|
||||
|
||||
if(i == 5) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||
|
||||
generateSector(keySector, 2);
|
||||
}
|
||||
else memset32(keySector, 0, sizeof(keySector));
|
||||
@ -248,13 +318,18 @@ static inline void uninstaller(void)
|
||||
shutdown(1, "Error: failed to mount CTRNAND");
|
||||
|
||||
//Read FIRM cxi from CTRNAND
|
||||
switch(firmRead((void *)FIRM0_OFFSET))
|
||||
u32 firmSize = 0,
|
||||
result = firmRead((void *)FIRM0_OFFSET);
|
||||
|
||||
switch(result)
|
||||
{
|
||||
case 1:
|
||||
shutdown(1, "Error: more than one FIRM cxi has been detected");
|
||||
shutdown(1, "Error: more than one FIRM has been detected");
|
||||
break;
|
||||
case 2:
|
||||
shutdown(1, "Error: a FIRM equal or newer than 11.0\nhas been detected");
|
||||
posY = drawString("A FIRM newer than 11.3 has been detected!", 10, posY + SPACING_Y, COLOR_RED);
|
||||
posY = drawString("You are about to uninstall A9LH!", 10, posY + SPACING_Y, COLOR_RED);
|
||||
posY = drawString("To reinstall you'll need hardmod + NAND backup!", 10, posY, COLOR_RED);
|
||||
break;
|
||||
case 3:
|
||||
shutdown(1, "Error: the CTRNAND FIRM is too large");
|
||||
@ -263,11 +338,16 @@ static inline void uninstaller(void)
|
||||
shutdown(1, "Error: couldn't read FIRM from CTRNAND");
|
||||
break;
|
||||
default:
|
||||
posY = drawString("You are about to uninstall A9LH!", 10, posY + SPACING_Y, COLOR_RED);
|
||||
posY = drawString("To reinstall you'll need 9.2 or lower!", 10, posY, COLOR_RED);
|
||||
break;
|
||||
}
|
||||
|
||||
inputSequence();
|
||||
|
||||
//Decrypt it and get its size
|
||||
u32 firmSize = decryptExeFs((u8 *)FIRM0_OFFSET);
|
||||
firmSize = decryptExeFs((Cxi *)FIRM0_OFFSET);
|
||||
if(firmSize == 0) shutdown(1, "Error: couldn't decrypt the CTRNAND FIRM");
|
||||
|
||||
//writeFirm encrypts in-place, so we need two copies
|
||||
memcpy((void *)FIRM1_OFFSET, (void *)FIRM0_OFFSET, firmSize);
|
||||
|
@ -6,22 +6,22 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
#define OTP_FROM_MEM 0x10012000
|
||||
#define FIRM0_OFFSET 0x24000000
|
||||
#define SECTION2_POSITION 0x66A00
|
||||
#define FIRM0_100_OFFSET 0x24100000
|
||||
#define FIRM1_OFFSET 0x24200000
|
||||
#define FIRM1_OFFSET 0x24100000
|
||||
#define FIRM0_SIZE 0xF3000
|
||||
#define FIRM0100_SIZE 0xF2000
|
||||
#define FIRM1_SIZE 0xF2000
|
||||
#define STAGE1_POSITION 0xF0590
|
||||
#define STAGE1_OFFSET FIRM0_OFFSET + STAGE1_POSITION
|
||||
#define STAGE2_OFFSET 0x24300000
|
||||
#define STAGE2_OFFSET 0x24200000
|
||||
#define MAX_STAGE1_SIZE 0x1E70
|
||||
#define MAX_STAGE2_SIZE 0x89A00
|
||||
|
||||
static inline void installer(bool isA9lh, bool isOtpless);
|
||||
extern u32 magic;
|
||||
|
||||
extern const u8 key2s[5][AES_BLOCK_SIZE],
|
||||
devKey2s[2][AES_BLOCK_SIZE];
|
||||
|
||||
static inline void installer(bool isOtpless);
|
||||
static inline void uninstaller(void);
|
@ -36,9 +36,11 @@
|
||||
|
||||
#include "screen.h"
|
||||
#include "cache.h"
|
||||
#include "utils.h"
|
||||
#include "memory.h"
|
||||
#include "i2c.h"
|
||||
|
||||
vu32 *const arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY;
|
||||
vu32 *arm11Entry = (vu32 *)BRAHMA_ARM11_ENTRY;
|
||||
|
||||
static void invokeArm11Function(void (*func)())
|
||||
{
|
||||
@ -179,7 +181,7 @@ void initScreens(void)
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
if(PDN_GPU_CNT == 1)
|
||||
if(!ARESCREENSINITED)
|
||||
{
|
||||
invokeArm11Function(initSequence);
|
||||
|
||||
|
@ -30,6 +30,8 @@
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
#define ARESCREENSINITED (PDN_GPU_CNT != 1)
|
||||
|
||||
#define BRAHMA_ARM11_ENTRY 0x1FFFFFF8
|
||||
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
|
||||
|
@ -24,14 +24,21 @@
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
b start
|
||||
|
||||
.global magic
|
||||
magic:
|
||||
.word 0
|
||||
|
||||
start:
|
||||
@ Disable interrupts
|
||||
mrs r0, cpsr
|
||||
orr r0, #0x1C0
|
||||
msr cpsr_cx, r0
|
||||
|
||||
@ Change the stack pointer
|
||||
mov sp, #0x27000000
|
||||
|
||||
@ Disable caches / MPU
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control register
|
||||
bic r0, #(1<<12) @ - instruction cache disable
|
||||
@ -84,4 +91,11 @@ _start:
|
||||
mov r1, #0x340
|
||||
str r1, [r0]
|
||||
|
||||
@ Clear BSS
|
||||
ldr r0, =__bss_start
|
||||
mov r1, #0
|
||||
ldr r2, =__bss_end
|
||||
sub r2, r0
|
||||
bl memset32
|
||||
|
||||
b main
|
||||
|
@ -27,7 +27,7 @@ u32 strlen(const char *string)
|
||||
{
|
||||
char *stringEnd = (char *)string;
|
||||
|
||||
while(*stringEnd) stringEnd++;
|
||||
while(*stringEnd != 0) stringEnd++;
|
||||
|
||||
return stringEnd - string;
|
||||
}
|
||||
@ -43,13 +43,22 @@ void concatenateStrings(char *destination, const char *source)
|
||||
void hexItoa(u32 number, char *out, u32 digits)
|
||||
{
|
||||
const char hexDigits[] = "0123456789ABCDEF";
|
||||
u32 i = 0;
|
||||
u32 i;
|
||||
|
||||
while(number > 0)
|
||||
for(i = 0; number > 0; i++)
|
||||
{
|
||||
out[digits - 1 - i++] = hexDigits[number & 0xF];
|
||||
out[digits - 1 - i] = hexDigits[number & 0xF];
|
||||
number >>= 4;
|
||||
}
|
||||
|
||||
while(i < digits) out[digits - 1 - i++] = '0';
|
||||
}
|
||||
|
||||
u32 hexAtoi(const char *in, u32 digits)
|
||||
{
|
||||
u32 res = 0;
|
||||
char *tmp = (char *)in;
|
||||
|
||||
for(u32 i = 0; i < digits && *tmp != 0; tmp++, i++)
|
||||
res = (*tmp > '9' ? *tmp - 'A' + 10 : *tmp - '0') + (res << 4);
|
||||
|
||||
return res;
|
||||
}
|
@ -27,3 +27,4 @@
|
||||
u32 strlen(const char *string);
|
||||
void concatenateStrings(char *destination, const char *source);
|
||||
void hexItoa(u32 number, char *out, u32 digits);
|
||||
u32 hexAtoi(const char *in, u32 digits);
|
@ -17,3 +17,13 @@ typedef volatile u8 vu8;
|
||||
typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
|
||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
#define ISN3DS (PDN_MPCORE_CFG == 7)
|
||||
#define ISDEVUNIT (CFG_UNITINFO != 0)
|
||||
#define ISA9LH (!PDN_SPI_CNT)
|
||||
|
||||
#include "3dsheaders.h"
|
@ -10,27 +10,26 @@
|
||||
|
||||
u32 waitInput(void)
|
||||
{
|
||||
bool pressedKey = false;
|
||||
u32 key;
|
||||
u32 key,
|
||||
oldKey = HID_PAD;
|
||||
|
||||
//Wait for no keys to be pressed
|
||||
while(HID_PAD);
|
||||
|
||||
do
|
||||
while(true)
|
||||
{
|
||||
//Wait for a key to be pressed
|
||||
while(!HID_PAD);
|
||||
|
||||
key = HID_PAD;
|
||||
|
||||
//Make sure it's pressed
|
||||
for(u32 i = 0x13000; i > 0; i--)
|
||||
if(!key)
|
||||
{
|
||||
if(key != HID_PAD) break;
|
||||
if(i == 1) pressedKey = true;
|
||||
oldKey = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(key == oldKey) continue;
|
||||
|
||||
//Make sure the key is pressed
|
||||
u32 i;
|
||||
for(i = 0; i < 0x13000 && key == HID_PAD; i++);
|
||||
if(i == 0x13000) break;
|
||||
}
|
||||
while(!pressedKey);
|
||||
|
||||
return key;
|
||||
}
|
||||
@ -46,6 +45,21 @@ void mcuReboot(void)
|
||||
while(true);
|
||||
}
|
||||
|
||||
void inputSequence(void)
|
||||
{
|
||||
posY = drawString("If you would like to continue, press:", 10, posY, COLOR_WHITE);
|
||||
posY = drawString("Up, Down, Left, Right, B, A, START, SELECT", 10, posY, COLOR_WHITE);
|
||||
|
||||
u32 unlockSequence[] = { BUTTON_UP, BUTTON_DOWN, BUTTON_LEFT, BUTTON_RIGHT, BUTTON_B, BUTTON_A, BUTTON_START, BUTTON_SELECT },
|
||||
sequenceSize = sizeof(unlockSequence) / sizeof(u32);
|
||||
|
||||
for(u32 correctPresses = 0; correctPresses < sequenceSize; correctPresses++)
|
||||
{
|
||||
if(waitInput() != unlockSequence[correctPresses])
|
||||
shutdown(1, "Button sequence not entered correctly");
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown(u32 mode, const char *message)
|
||||
{
|
||||
if(mode != 0)
|
||||
|
@ -21,8 +21,13 @@
|
||||
#define COLOR_RED 0x0000FF
|
||||
#define COLOR_GREEN 0x00FF00
|
||||
|
||||
#define TICKS_PER_SEC 67027964ULL
|
||||
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
||||
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
||||
|
||||
extern u32 posY;
|
||||
|
||||
u32 waitInput(void);
|
||||
void mcuReboot(void);
|
||||
void inputSequence(void);
|
||||
void shutdown(u32 mode, const char *message);
|
Loading…
x
Reference in New Issue
Block a user