diff --git a/.gitmodules b/.gitmodules index b0edb12..b91366b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "CakeHax"] path = CakeHax url = https://github.com/mid-kid/CakeHax.git -[submodule "2xrsa"] - path = 2xrsa - url = https://github.com/b1l1s/2xrsa.git diff --git a/2xrsa b/2xrsa deleted file mode 160000 index c3f93cb..0000000 --- a/2xrsa +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c3f93cb492d61978fd0620d715510932837495dd diff --git a/Makefile b/Makefile index 4460010..654a001 100644 --- a/Makefile +++ b/Makefile @@ -9,9 +9,9 @@ name := SafeA9LHInstaller revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i') dir_source := source -dir_mset := CakeHax -dir_ninjhax := CakeBrah -dir_2xrsa := 2xrsa +dir_loader := loader +dir_cakehax := CakeHax +dir_cakebrah := CakeBrah dir_build := build dir_out := out @@ -24,64 +24,66 @@ 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 $(> $(dir_build)/bundled.h + echo "extern const u32" `(echo $(> $(dir_build)/bundled.h +endef + .PHONY: all -all: launcher a9lh ninjhax 2xrsa +all: launcher a9lh cakebrah .PHONY: launcher -launcher: $(dir_out)/$(name).dat +launcher: $(dir_out)/$(name).dat .PHONY: a9lh a9lh: $(dir_out)/arm9loaderhax.bin -.PHONY: 2xrsa -2xrsa: $(dir_out)/arm9.bin $(dir_out)/arm11.bin - -.PHONY: ninjhax -ninjhax: $(dir_out)/3ds/$(name) +.PHONY: cakebrah +cakebrah: $(dir_out)/3ds/$(name) .PHONY: release release: $(dir_out)/$(name)$(revision).7z .PHONY: clean clean: - @$(MAKE) $(FLAGS) -C $(dir_mset) clean - @$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean - @$(MAKE) -C $(dir_2xrsa) clean + @$(MAKE) -C $(dir_loader) clean + @$(MAKE) $(FLAGS) -C $(dir_cakehax) clean + @$(MAKE) $(FLAGS) -C $(dir_cakebrah) clean @rm -rf $(dir_out) $(dir_build) -$(dir_out): - @mkdir -p "$(dir_out)" +$(dir_out) $(dir_build): + @mkdir -p "$@" $(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out) - @mkdir -p $(dir_out) - @$(MAKE) $(FLAGS) -C $(dir_mset) launcher + @$(MAKE) $(FLAGS) -C $(dir_cakehax) launcher dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144 $(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out) @cp -av $(dir_build)/main.bin $@ -$(dir_out)/arm9.bin: $(dir_build)/main.bin $(dir_out) - @cp -av $(dir_build)/main.bin $@ - -$(dir_out)/arm11.bin: - @$(MAKE) -C $(dir_2xrsa) - @cp -av $(dir_2xrsa)/bin/arm11.bin $@ - $(dir_out)/3ds/$(name): $(dir_out) - @mkdir -p $(dir_out)/3ds/$(name) - @$(MAKE) $(FLAGS) -C $(dir_ninjhax) - @mv $(dir_out)/$(name).3dsx $@ - @mv $(dir_out)/$(name).smdh $@ + @mkdir -p "$@" + @$(MAKE) $(FLAGS) -C $(dir_cakebrah) + @mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@ -$(dir_out)/$(name)$(revision).7z: launcher a9lh ninjhax 2xrsa +$(dir_out)/$(name)$(revision).7z: all @7z a -mx $@ ./$(@D)/* $(dir_build)/main.bin: $(dir_build)/main.elf $(OC) -S -O binary $< $@ -$(dir_build)/main.elf: $(objects) +$(dir_build)/main.elf: $(bundled) $(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)\"" diff --git a/loader/Makefile b/loader/Makefile new file mode 100644 index 0000000..ef099c7 --- /dev/null +++ b/loader/Makefile @@ -0,0 +1,67 @@ +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=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 $(> $(dir_build)/bundled.h + echo "extern const u32" `(echo $(> $(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) diff --git a/loader/arm11/Makefile b/loader/arm11/Makefile new file mode 100755 index 0000000..2fbc841 --- /dev/null +++ b/loader/arm11/Makefile @@ -0,0 +1,48 @@ +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=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) diff --git a/loader/arm11/linker.ld b/loader/arm11/linker.ld new file mode 100755 index 0000000..832320b --- /dev/null +++ b/loader/arm11/linker.ld @@ -0,0 +1,12 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x1FFF4C80; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} + diff --git a/loader/arm11/source/main.c b/loader/arm11/source/main.c new file mode 100755 index 0000000..0f2eb07 --- /dev/null +++ b/loader/arm11/source/main.c @@ -0,0 +1,15 @@ +#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)(); +} \ No newline at end of file diff --git a/loader/arm11/source/start.s b/loader/arm11/source/start.s new file mode 100644 index 0000000..a02aa4c --- /dev/null +++ b/loader/arm11/source/start.s @@ -0,0 +1,8 @@ +.section .text.start +.align 4 +.global _start +_start: + @ Disable interrupts + CPSID aif + + b main diff --git a/loader/arm11/source/types.h b/loader/arm11/source/types.h new file mode 100755 index 0000000..7829adf --- /dev/null +++ b/loader/arm11/source/types.h @@ -0,0 +1,13 @@ +/* +* types.h +* by Reisyukaku +* Copyright (c) 2015 All Rights Reserved +*/ + +#pragma once + +#include + +//Common data types +typedef uint32_t u32; +typedef volatile u32 vu32; \ No newline at end of file diff --git a/loader/linker.ld b/loader/linker.ld new file mode 100644 index 0000000..38691c6 --- /dev/null +++ b/loader/linker.ld @@ -0,0 +1,11 @@ +ENTRY(_start) +SECTIONS +{ + . = 0x80F0000; + .text.start : { *(.text.start) } + .text : { *(.text) } + .data : { *(.data) } + .bss : { *(.bss COMMON) } + .rodata : { *(.rodata) } + . = ALIGN(4); +} diff --git a/loader/source/cache.h b/loader/source/cache.h new file mode 100644 index 0000000..8f80b43 --- /dev/null +++ b/loader/source/cache.h @@ -0,0 +1,27 @@ +/* +* 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 . +* +* 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); \ No newline at end of file diff --git a/loader/source/cache.s b/loader/source/cache.s new file mode 100644 index 0000000..b2a8319 --- /dev/null +++ b/loader/source/cache.s @@ -0,0 +1,54 @@ +@ 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 . +@ +@ 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 diff --git a/loader/source/main.c b/loader/source/main.c new file mode 100644 index 0000000..bba321d --- /dev/null +++ b/loader/source/main.c @@ -0,0 +1,53 @@ +/* +* 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 . +* +* 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); +} + +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)(); +} \ No newline at end of file diff --git a/loader/source/memory.c b/loader/source/memory.c new file mode 100644 index 0000000..3833531 --- /dev/null +++ b/loader/source/memory.c @@ -0,0 +1,14 @@ +/* +* 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]; +} \ No newline at end of file diff --git a/loader/source/memory.h b/loader/source/memory.h new file mode 100644 index 0000000..13a360e --- /dev/null +++ b/loader/source/memory.h @@ -0,0 +1,9 @@ +/* +* memory.h +*/ + +#pragma once + +#include "types.h" + +void memcpy(void *dest, const void *src, u32 size); \ No newline at end of file diff --git a/loader/source/start.s b/loader/source/start.s new file mode 100644 index 0000000..c94cf41 --- /dev/null +++ b/loader/source/start.s @@ -0,0 +1,86 @@ +@ 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 . +@ +@ 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 diff --git a/loader/source/types.h b/loader/source/types.h new file mode 100644 index 0000000..2b89edb --- /dev/null +++ b/loader/source/types.h @@ -0,0 +1,37 @@ +/* +* 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 . +* +* 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 +#include +#include + +//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; \ No newline at end of file diff --git a/source/crypto.c b/source/crypto.c index 434eec5..24f07ff 100755 --- a/source/crypto.c +++ b/source/crypto.c @@ -275,13 +275,14 @@ 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] = { +const u8 __attribute__((aligned(4))) key2s[4][AES_BLOCK_SIZE] = { {0x42, 0x3F, 0x81, 0x7A, 0x23, 0x52, 0x58, 0x31, 0x6E, 0x75, 0x8E, 0x3A, 0x39, 0x43, 0x2E, 0xD0}, {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} + {0x65, 0x29, 0x3E, 0x12, 0x56, 0x0C, 0x0B, 0xD1, 0xDD, 0xB5, 0x63, 0x1C, 0xB6, 0xD9, 0x52, 0x75}, + {0x07, 0x29, 0x44, 0x38, 0xF8, 0xC9, 0x75, 0x93, 0xAA, 0x0E, 0x4A, 0xB4, 0xAE, 0x84, 0xC1, 0xD8} }; -void getNandCTR(void) +void getNandCtr(void) { u8 __attribute__((aligned(4))) cid[AES_BLOCK_SIZE]; u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE]; @@ -293,7 +294,7 @@ void getNandCTR(void) void ctrNandInit(void) { - getNandCTR(); + getNandCtr(); if(isN3DS) { @@ -355,7 +356,7 @@ void writeFirm(u8 *inbuf, bool isFirm1, u32 size) sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf); } -void setupKeyslot0x11(bool isA9lh, const void *otp) +void setupKeyslot0x11(const void *otp, bool isA9lh) { u8 __attribute__((aligned(4))) shasum[SHA_256_HASH_SIZE]; u8 __attribute__((aligned(4))) keyX[AES_BLOCK_SIZE]; @@ -377,23 +378,31 @@ void setupKeyslot0x11(bool isA9lh, const void *otp) void generateSector(u8 *keySector, u32 mode) { //Inject key2 - memcpy(keySector + AES_BLOCK_SIZE, mode ? key2s[0] : key2s[2], AES_BLOCK_SIZE); + if(mode == 0) memcpy(keySector + AES_BLOCK_SIZE, key2s[2], AES_BLOCK_SIZE); + else if(mode == 1) memcpy(keySector + AES_BLOCK_SIZE, keySector, AES_BLOCK_SIZE); + else memcpy(keySector + AES_BLOCK_SIZE, key2s[0], AES_BLOCK_SIZE); - //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); + 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) +void getSector(u8 *keySector, bool isA9lh) { //Read keysector from NAND sdmmc_nand_readsectors(0x96, 1, keySector); - //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); + if(isA9lh) + { + //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); + } } u32 verifyHash(const void *data, u32 size, const u8 *hash) diff --git a/source/crypto.h b/source/crypto.h index c0a8b9b..9a0de7c 100755 --- a/source/crypto.h +++ b/source/crypto.h @@ -80,15 +80,15 @@ #define SHA_1_HASH_SIZE (160 / 8) extern bool isN3DS; -const u8 key2s[3][0x10]; +const u8 key2s[4][AES_BLOCK_SIZE]; -void getNandCTR(void); +void getNandCtr(void); void ctrNandInit(void); u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf); void readFirm0(u8 *outbuf, u32 size); void writeFirm(u8 *inbuf, bool isFirm1, u32 size); -void setupKeyslot0x11(bool isA9lh, const void *otp); +void setupKeyslot0x11(const void *otp, bool isA9lh); void generateSector(u8 *keySector, u32 mode); -void getSector(u8 *keySector); +void getSector(u8 *keySector, bool isA9lh); u32 verifyHash(const void *data, u32 size, const u8 *hash); u32 decryptExeFs(u8 *inbuf); \ No newline at end of file diff --git a/source/fs.c b/source/fs.c index c615ecb..edef111 100644 --- a/source/fs.c +++ b/source/fs.c @@ -27,12 +27,12 @@ static FATFS fs; -u32 mountSD(void) +bool mountSd(void) { return f_mount(&fs, "0:", 1) == FR_OK; } -u32 mountCTRNAND(void) +bool mountCtrNand(void) { return f_mount(&fs, "1:", 1) == FR_OK; } @@ -45,9 +45,8 @@ u32 fileRead(void *dest, const char *path, u32 maxSize) if(f_open(&file, path, FA_READ) == FR_OK) { u32 size = f_size(&file); - if(dest == NULL) ret = size; - else if(!(maxSize > 0 && size > maxSize)) - f_read(&file, dest, size, (unsigned int *)&ret); + if(!(size > maxSize)) + f_read(&file, dest, size, (unsigned int *)ret); f_close(&file); } @@ -64,6 +63,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size) { unsigned int written; f_write(&file, buffer, size, &written); + f_truncate(&file); f_close(&file); return true; diff --git a/source/fs.h b/source/fs.h index 3717057..8f12048 100644 --- a/source/fs.h +++ b/source/fs.h @@ -26,8 +26,8 @@ extern bool isN3DS; -u32 mountSD(void); -u32 mountCTRNAND(void); +bool mountSd(void); +bool mountCtrNand(void); u32 fileRead(void *dest, const char *path, u32 maxSize); bool fileWrite(const void *buffer, const char *path, u32 size); u32 firmRead(void *dest); \ No newline at end of file diff --git a/source/installer.c b/source/installer.c index 6b44a4e..3c86cbe 100755 --- a/source/installer.c +++ b/source/installer.c @@ -9,7 +9,10 @@ #include "screen.h" #include "draw.h" #include "utils.h" +#include "cache.h" +#include "types.h" #include "fatfs/sdmmc/sdmmc.h" +#include "../build/bundled.h" static const u8 sectorHash[0x20] = { 0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C, @@ -26,6 +29,11 @@ static const u8 firm0A9lhHash[0x20] = { 0x8E, 0xF0, 0x4A, 0xB1, 0xA6, 0x7F, 0xCD, 0xAB, 0x0C, 0x0A, 0xC0, 0x69, 0xA7, 0x9D, 0xC5, 0x04 }; +static const u8 firm0100Hash[0x20] = { + 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 @@ -36,37 +44,50 @@ bool isN3DS; void main(void) { - initScreens(); - sdmmc_sdcard_init(); - //Determine if booting with A9LH bool isA9lh = !PDN_SPI_CNT; //Detect the console being used isN3DS = PDN_MPCORE_CFG == 7; + vu32 *magic = (vu32 *)0x25000000; + bool isOtpless = isA9lh && magic[0] == 0xABADCAFE && magic[1] == 0xDEADCAFE; + + sdmmc_sdcard_init(); + initScreens(); + drawString(TITLE, 10, 10, COLOR_TITLE); posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, 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); - u32 pressed = waitInput(); + u32 pressed; - if(pressed == BUTTON_SELECT) installer(isA9lh); + 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("Press any other button to shutdown", 10, posY, COLOR_WHITE); + pressed = waitInput(); + } + else + { + 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(); shutdown(0, NULL); } -static inline void installer(bool isA9lh) +static inline void installer(bool isA9lh, bool isOtpless) { - if(!mountSD()) + if(!isOtpless && !mountSd()) shutdown(1, "Error: failed to mount the SD card"); bool updateA9lh = false; - //If making a first install, we need the OTP - if(!isA9lh) + //If making a first install on O3DS, we need the OTP + if(!isA9lh && !isN3DS) { const char otpPath[] = "a9lh/otp.bin"; const u8 zeroes[256] = {0}; @@ -75,7 +96,7 @@ static inline void installer(bool isA9lh) if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0) { // Read OTP from file - if(!fileRead((void *)OTP_OFFSET, otpPath, 256)) + if(fileRead((void *)OTP_OFFSET, otpPath, 256) != 256) shutdown(1, "Error: otp.bin doesn't exist and can't be dumped"); } else @@ -87,86 +108,108 @@ static inline void installer(bool isA9lh) } //Setup the key sector de/encryption with the SHA register or otp.bin - setupKeyslot0x11(isA9lh, (void *)OTP_OFFSET); + if(isA9lh || !isN3DS) setupKeyslot0x11((void *)OTP_OFFSET, isA9lh); //Calculate the CTR for the 3DS partitions - getNandCTR(); + getNandCtr(); //Get NAND FIRM0 and test that the CTR is correct - readFirm0((u8 *)FIRM0_OFFSET, FIRM0_SIZE); - if(memcmp((void *)FIRM0_OFFSET, "FIRM", 4) != 0) - shutdown(1, "Error: failed to setup FIRM encryption"); + if(!isOtpless) + { + readFirm0((u8 *)FIRM0_OFFSET, FIRM0_SIZE); + if(memcmp((void *)FIRM0_OFFSET, "FIRM", 4) != 0) + shutdown(1, "Error: failed to setup FIRM encryption"); + } //If booting from A9LH or on N3DS, we can use the key sector from NAND - if(isA9lh || isN3DS) - { - getSector((u8 *)SECTOR_OFFSET); - - u32 i; - for(i = 0; i < 3; i++) - if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[i], 0x10) == 0) break; - - if(i == 3) shutdown(1, isA9lh ? "Error: the OTP hash or the NAND key sector\nare invalid" : - "Error: the otp.bin or the NAND key sector\nare invalid"); - else if(i == 1) updateA9lh = true; - } + if(isA9lh || isN3DS) getSector((u8 *)SECTOR_OFFSET, isA9lh); else { //Read decrypted key sector - if(!fileRead((void *)SECTOR_OFFSET, "a9lh/secret_sector.bin", 0x200)) + if(fileRead((void *)SECTOR_OFFSET, "a9lh/secret_sector.bin", 0x200) != 0x200) shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size"); if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash)) shutdown(1, "Error: secret_sector.bin is invalid or corrupted"); } - if(!isA9lh || updateA9lh) + if(isA9lh && !isOtpless) { - //Generate and encrypt a per-console A9LH key sector - generateSector((u8 *)SECTOR_OFFSET, 0); + u32 i; + for(i = 0; i < 4; i++) + if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[i], 0x10) == 0) break; - //Read FIRM0 - if(!fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE)) - shutdown(1, "Error: firm0.bin doesn't exist or has a wrong size"); - - if(!verifyHash((void *)FIRM0_OFFSET, FIRM0_SIZE, firm0Hash)) - shutdown(1, "Error: firm0.bin is invalid or corrupted"); + if(i == 4) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid"); + if(i == 0) updateA9lh = true; } - else if(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash)) - shutdown(1, "Error: NAND FIRM0 is invalid"); if(!isA9lh) { //Read FIRM1 - if(!fileRead((void *)FIRM1_OFFSET, "a9lh/firm1.bin", FIRM1_SIZE)) + 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)) shutdown(1, "Error: firm1.bin is invalid or corrupted"); + + if(isN3DS) + { + //Read 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"); + } } - //Inject stage1 - memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE); - if(!fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE)) - shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size"); + if(!isA9lh || updateA9lh || isOtpless) generateSector((u8 *)SECTOR_OFFSET, (isN3DS && !isA9lh) ? 1 : 0); - 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"); + if(!isA9lh || updateA9lh) + { + //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)) + 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"); - //Read stage2 - memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE); - if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE)) - shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max size"); + if(!isOtpless) + { + //Inject stage1 + memset32((void *)STAGE1_OFFSET, 0, MAX_STAGE1_SIZE); + if(!fileRead((void *)STAGE1_OFFSET, "a9lh/payload_stage1.bin", MAX_STAGE1_SIZE)) + shutdown(1, "Error: payload_stage1.bin doesn't exist or\nexceeds max size"); - posY = drawString("All checks passed, installing...", 10, posY + SPACING_Y, COLOR_WHITE); + 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"); + + //Read stage2 + memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE); + if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE)) + shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max size"); + + posY = drawString("All checks passed, installing...", 10, posY + SPACING_Y, COLOR_WHITE); + + sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET); + } - //Point of no return, install stuff in the safest order - sdmmc_nand_writesectors(0x5C000, MAX_STAGE2_SIZE / 0x200, (u8 *)STAGE2_OFFSET); if(!isA9lh) writeFirm((u8 *)FIRM1_OFFSET, true, FIRM1_SIZE); - if(!isA9lh || updateA9lh) sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET); + if(!isA9lh || updateA9lh || isOtpless) sdmmc_nand_writesectors(0x96, 1, (u8 *)SECTOR_OFFSET); + + if(!isA9lh && isN3DS) + { + *(u32 *)0x80FD0FC = 0xEAFFCBBF; //B 0x80F0000 + memcpy((void *)0x80F0000, loader_bin, loader_bin_size); + + writeFirm((u8 *)FIRM0_100_OFFSET, false, FIRM0_SIZE); + + mcuReboot(); + } + writeFirm((u8 *)FIRM0_OFFSET, false, FIRM0_SIZE); - shutdown(2, isA9lh ? "Update: success!" : "Full install: success!"); + shutdown(2, isA9lh && !isOtpless ? "Update: success!" : "Full install: success!"); } static inline void uninstaller(void) @@ -188,15 +231,15 @@ static inline void uninstaller(void) //New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector if(isN3DS) { - setupKeyslot0x11(1, NULL); - getSector((u8 *)SECTOR_OFFSET); + setupKeyslot0x11(NULL, true); + getSector((u8 *)SECTOR_OFFSET, true); if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[1], 0x10) != 0 && memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[2], 0x10) != 0) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid"); - generateSector((u8 *)SECTOR_OFFSET, 1); + generateSector((u8 *)SECTOR_OFFSET, 2); } else memset32((void *)SECTOR_OFFSET, 0, 0x200); - if(!mountCTRNAND()) + if(!mountCtrNand()) shutdown(1, "Error: failed to mount CTRNAND"); //Read FIRM cxi from CTRNAND diff --git a/source/installer.h b/source/installer.h index b5d73d3..aeb24e6 100644 --- a/source/installer.h +++ b/source/installer.h @@ -13,15 +13,17 @@ #define OTP_OFFSET 0x24000000 #define SECTOR_OFFSET 0x24100000 #define FIRM0_OFFSET 0x24200000 -#define FIRM1_OFFSET 0x24300000 +#define FIRM0_100_OFFSET 0x24300000 +#define FIRM1_OFFSET 0x24400000 #define FIRM0_SIZE 0xF3000 +#define FIRM0100_SIZE 0xF2000 #define SECTION2_POSITION 0x66A00 #define FIRM1_SIZE 0xF2000 #define STAGE1_POSITION 0xF0590 #define STAGE1_OFFSET FIRM0_OFFSET + STAGE1_POSITION -#define STAGE2_OFFSET 0x24400000 +#define STAGE2_OFFSET 0x24500000 #define MAX_STAGE1_SIZE 0x1E70 #define MAX_STAGE2_SIZE 0x89A00 -static inline void installer(bool isA9lh); +static inline void installer(bool isA9lh, bool isOtpless); static inline void uninstaller(void); \ No newline at end of file diff --git a/source/screen.c b/source/screen.c index bcf3384..01feaec 100644 --- a/source/screen.c +++ b/source/screen.c @@ -35,7 +35,6 @@ */ #include "screen.h" -#include "draw.h" #include "cache.h" #include "i2c.h" @@ -92,8 +91,8 @@ void initScreens(void) *(vu32 *)0x10141200 = 0x1007F; *(vu32 *)0x10202014 = 0x00000001; *(vu32 *)0x1020200C &= 0xFFFEFFFE; - *(vu32 *)0x10202240 = 0x45; - *(vu32 *)0x10202A40 = 0x45; + *(vu32 *)0x10202240 = 0x4C; + *(vu32 *)0x10202A40 = 0x4C; *(vu32 *)0x10202244 = 0x1023E; *(vu32 *)0x10202A44 = 0x1023E; @@ -188,22 +187,16 @@ void initScreens(void) WAIT_FOR_ARM9(); } - static bool needToSetup = true; - - if(needToSetup) + if(PDN_GPU_CNT == 1) { - if(PDN_GPU_CNT == 1) - { - invokeArm11Function(initSequence); + invokeArm11Function(initSequence); - //Turn on backlight - i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); - } - - flushDCacheRange((void *)fb, sizeof(struct fb)); - invokeArm11Function(setupFramebuffers); - needToSetup = false; + //Turn on backlight + i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A); } + flushDCacheRange((void *)fb, sizeof(struct fb)); + invokeArm11Function(setupFramebuffers); + clearScreens(); } \ No newline at end of file diff --git a/source/utils.c b/source/utils.c index 4e1891d..0c22b91 100755 --- a/source/utils.c +++ b/source/utils.c @@ -35,6 +35,17 @@ u32 waitInput(void) return key; } +void mcuReboot(void) +{ + if(PDN_GPU_CNT != 1) clearScreens(); + + //Ensure that all memory transfers have completed and that the data cache has been flushed + flushEntireDCache(); + + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); + while(true); +} + void shutdown(u32 mode, const char *message) { if(mode) @@ -45,7 +56,10 @@ void shutdown(u32 mode, const char *message) } if(PDN_GPU_CNT != 1) clearScreens(); + + //Ensure that all memory transfers have completed and that the data cache has been flushed flushEntireDCache(); + i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0); while(true); } \ No newline at end of file diff --git a/source/utils.h b/source/utils.h index e519de3..468f53d 100644 --- a/source/utils.h +++ b/source/utils.h @@ -24,4 +24,5 @@ extern u32 posY; u32 waitInput(void); +void mcuReboot(void); void shutdown(u32 mode, const char *message); \ No newline at end of file