mirror of
https://github.com/AuroraWright/SafeA9LHInstaller.git
synced 2025-06-26 05:32:45 +00:00
Added OTPless installs for New 3DS (you need 10.0 NATIVE_FIRM as firm0_100.bin in /a9lh, temporarily removed support for 2xrsa
This commit is contained in:
parent
4ba801938b
commit
a2e44e797d
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -4,6 +4,3 @@
|
|||||||
[submodule "CakeHax"]
|
[submodule "CakeHax"]
|
||||||
path = CakeHax
|
path = CakeHax
|
||||||
url = https://github.com/mid-kid/CakeHax.git
|
url = https://github.com/mid-kid/CakeHax.git
|
||||||
[submodule "2xrsa"]
|
|
||||||
path = 2xrsa
|
|
||||||
url = https://github.com/b1l1s/2xrsa.git
|
|
||||||
|
1
2xrsa
1
2xrsa
@ -1 +0,0 @@
|
|||||||
Subproject commit c3f93cb492d61978fd0620d715510932837495dd
|
|
62
Makefile
62
Makefile
@ -9,9 +9,9 @@ name := SafeA9LHInstaller
|
|||||||
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/i')
|
||||||
|
|
||||||
dir_source := source
|
dir_source := source
|
||||||
dir_mset := CakeHax
|
dir_loader := loader
|
||||||
dir_ninjhax := CakeBrah
|
dir_cakehax := CakeHax
|
||||||
dir_2xrsa := 2xrsa
|
dir_cakebrah := CakeBrah
|
||||||
dir_build := build
|
dir_build := build
|
||||||
dir_out := out
|
dir_out := out
|
||||||
|
|
||||||
@ -24,64 +24,66 @@ objects= $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
|||||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||||
|
|
||||||
|
bundled = $(dir_build)/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
|
.PHONY: all
|
||||||
all: launcher a9lh ninjhax 2xrsa
|
all: launcher a9lh cakebrah
|
||||||
|
|
||||||
.PHONY: launcher
|
.PHONY: launcher
|
||||||
launcher: $(dir_out)/$(name).dat
|
launcher: $(dir_out)/$(name).dat
|
||||||
|
|
||||||
.PHONY: a9lh
|
.PHONY: a9lh
|
||||||
a9lh: $(dir_out)/arm9loaderhax.bin
|
a9lh: $(dir_out)/arm9loaderhax.bin
|
||||||
|
|
||||||
.PHONY: 2xrsa
|
.PHONY: cakebrah
|
||||||
2xrsa: $(dir_out)/arm9.bin $(dir_out)/arm11.bin
|
cakebrah: $(dir_out)/3ds/$(name)
|
||||||
|
|
||||||
.PHONY: ninjhax
|
|
||||||
ninjhax: $(dir_out)/3ds/$(name)
|
|
||||||
|
|
||||||
.PHONY: release
|
.PHONY: release
|
||||||
release: $(dir_out)/$(name)$(revision).7z
|
release: $(dir_out)/$(name)$(revision).7z
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
@$(MAKE) -C $(dir_loader) clean
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
@$(MAKE) $(FLAGS) -C $(dir_cakehax) clean
|
||||||
@$(MAKE) -C $(dir_2xrsa) clean
|
@$(MAKE) $(FLAGS) -C $(dir_cakebrah) clean
|
||||||
@rm -rf $(dir_out) $(dir_build)
|
@rm -rf $(dir_out) $(dir_build)
|
||||||
|
|
||||||
$(dir_out):
|
$(dir_out) $(dir_build):
|
||||||
@mkdir -p "$(dir_out)"
|
@mkdir -p "$@"
|
||||||
|
|
||||||
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
$(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
||||||
@mkdir -p $(dir_out)
|
@$(MAKE) $(FLAGS) -C $(dir_cakehax) launcher
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
|
||||||
dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||||
|
|
||||||
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||||
@cp -av $(dir_build)/main.bin $@
|
@cp -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)
|
$(dir_out)/3ds/$(name): $(dir_out)
|
||||||
@mkdir -p $(dir_out)/3ds/$(name)
|
@mkdir -p "$@"
|
||||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax)
|
@$(MAKE) $(FLAGS) -C $(dir_cakebrah)
|
||||||
@mv $(dir_out)/$(name).3dsx $@
|
@mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@
|
||||||
@mv $(dir_out)/$(name).smdh $@
|
|
||||||
|
|
||||||
$(dir_out)/$(name)$(revision).7z: launcher a9lh ninjhax 2xrsa
|
$(dir_out)/$(name)$(revision).7z: all
|
||||||
@7z a -mx $@ ./$(@D)/*
|
@7z a -mx $@ ./$(@D)/*
|
||||||
|
|
||||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||||
$(OC) -S -O binary $< $@
|
$(OC) -S -O binary $< $@
|
||||||
|
|
||||||
$(dir_build)/main.elf: $(objects)
|
$(dir_build)/main.elf: $(bundled) $(objects)
|
||||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
$(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)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||||
$(dir_build)/installer.o: CFLAGS += -DTITLE="\"$(name) $(revision)\""
|
$(dir_build)/installer.o: CFLAGS += -DTITLE="\"$(name) $(revision)\""
|
||||||
|
|
||||||
|
67
loader/Makefile
Normal file
67
loader/Makefile
Normal file
@ -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=<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)
|
48
loader/arm11/Makefile
Executable file
48
loader/arm11/Makefile
Executable file
@ -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=<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)
|
12
loader/arm11/linker.ld
Executable file
12
loader/arm11/linker.ld
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x1FFF4C80;
|
||||||
|
.text.start : { *(.text.start) }
|
||||||
|
.text : { *(.text) }
|
||||||
|
.data : { *(.data) }
|
||||||
|
.bss : { *(.bss COMMON) }
|
||||||
|
.rodata : { *(.rodata) }
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
||||||
|
|
15
loader/arm11/source/main.c
Executable file
15
loader/arm11/source/main.c
Executable file
@ -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)();
|
||||||
|
}
|
8
loader/arm11/source/start.s
Normal file
8
loader/arm11/source/start.s
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.section .text.start
|
||||||
|
.align 4
|
||||||
|
.global _start
|
||||||
|
_start:
|
||||||
|
@ Disable interrupts
|
||||||
|
CPSID aif
|
||||||
|
|
||||||
|
b main
|
13
loader/arm11/source/types.h
Executable file
13
loader/arm11/source/types.h
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
* types.h
|
||||||
|
* by Reisyukaku
|
||||||
|
* Copyright (c) 2015 All Rights Reserved
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
//Common data types
|
||||||
|
typedef uint32_t u32;
|
||||||
|
typedef volatile u32 vu32;
|
11
loader/linker.ld
Normal file
11
loader/linker.ld
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
ENTRY(_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0x80F0000;
|
||||||
|
.text.start : { *(.text.start) }
|
||||||
|
.text : { *(.text) }
|
||||||
|
.data : { *(.data) }
|
||||||
|
.bss : { *(.bss COMMON) }
|
||||||
|
.rodata : { *(.rodata) }
|
||||||
|
. = ALIGN(4);
|
||||||
|
}
|
27
loader/source/cache.h
Normal file
27
loader/source/cache.h
Normal file
@ -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 <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);
|
54
loader/source/cache.s
Normal file
54
loader/source/cache.s
Normal file
@ -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 <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
|
53
loader/source/main.c
Normal file
53
loader/source/main.c
Normal file
@ -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 <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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)();
|
||||||
|
}
|
14
loader/source/memory.c
Normal file
14
loader/source/memory.c
Normal file
@ -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];
|
||||||
|
}
|
9
loader/source/memory.h
Normal file
9
loader/source/memory.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
* memory.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
void memcpy(void *dest, const void *src, u32 size);
|
86
loader/source/start.s
Normal file
86
loader/source/start.s
Normal file
@ -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 <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
|
37
loader/source/types.h
Normal file
37
loader/source/types.h
Normal file
@ -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 <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>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdbool.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;
|
@ -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 __attribute__((aligned(4))) nandCtr[AES_BLOCK_SIZE];
|
||||||
static u8 nandSlot;
|
static u8 nandSlot;
|
||||||
static u32 fatStart;
|
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},
|
{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},
|
{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))) cid[AES_BLOCK_SIZE];
|
||||||
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
u8 __attribute__((aligned(4))) shaSum[SHA_256_HASH_SIZE];
|
||||||
@ -293,7 +294,7 @@ void getNandCTR(void)
|
|||||||
|
|
||||||
void ctrNandInit(void)
|
void ctrNandInit(void)
|
||||||
{
|
{
|
||||||
getNandCTR();
|
getNandCtr();
|
||||||
|
|
||||||
if(isN3DS)
|
if(isN3DS)
|
||||||
{
|
{
|
||||||
@ -355,7 +356,7 @@ void writeFirm(u8 *inbuf, bool isFirm1, u32 size)
|
|||||||
sdmmc_nand_writesectors(offset / 0x200, size / 0x200, inbuf);
|
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))) shasum[SHA_256_HASH_SIZE];
|
||||||
u8 __attribute__((aligned(4))) keyX[AES_BLOCK_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)
|
void generateSector(u8 *keySector, u32 mode)
|
||||||
{
|
{
|
||||||
//Inject key2
|
//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
|
if(mode != 1)
|
||||||
aes_use_keyslot(0x11);
|
{
|
||||||
for(u32 i = 0; i < 32; i++)
|
//Encrypt key sector
|
||||||
aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_ENCRYPT_MODE, 0);
|
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
|
//Read keysector from NAND
|
||||||
sdmmc_nand_readsectors(0x96, 1, keySector);
|
sdmmc_nand_readsectors(0x96, 1, keySector);
|
||||||
|
|
||||||
//Decrypt key sector
|
if(isA9lh)
|
||||||
aes_use_keyslot(0x11);
|
{
|
||||||
for(u32 i = 0; i < 32; i++)
|
//Decrypt key sector
|
||||||
aes(keySector + (AES_BLOCK_SIZE * i), keySector + (AES_BLOCK_SIZE * i), 1, NULL, AES_ECB_DECRYPT_MODE, 0);
|
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)
|
u32 verifyHash(const void *data, u32 size, const u8 *hash)
|
||||||
|
@ -80,15 +80,15 @@
|
|||||||
#define SHA_1_HASH_SIZE (160 / 8)
|
#define SHA_1_HASH_SIZE (160 / 8)
|
||||||
|
|
||||||
extern bool isN3DS;
|
extern bool isN3DS;
|
||||||
const u8 key2s[3][0x10];
|
const u8 key2s[4][AES_BLOCK_SIZE];
|
||||||
|
|
||||||
void getNandCTR(void);
|
void getNandCtr(void);
|
||||||
void ctrNandInit(void);
|
void ctrNandInit(void);
|
||||||
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||||
void readFirm0(u8 *outbuf, u32 size);
|
void readFirm0(u8 *outbuf, u32 size);
|
||||||
void writeFirm(u8 *inbuf, bool isFirm1, 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 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 verifyHash(const void *data, u32 size, const u8 *hash);
|
||||||
u32 decryptExeFs(u8 *inbuf);
|
u32 decryptExeFs(u8 *inbuf);
|
10
source/fs.c
10
source/fs.c
@ -27,12 +27,12 @@
|
|||||||
|
|
||||||
static FATFS fs;
|
static FATFS fs;
|
||||||
|
|
||||||
u32 mountSD(void)
|
bool mountSd(void)
|
||||||
{
|
{
|
||||||
return f_mount(&fs, "0:", 1) == FR_OK;
|
return f_mount(&fs, "0:", 1) == FR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 mountCTRNAND(void)
|
bool mountCtrNand(void)
|
||||||
{
|
{
|
||||||
return f_mount(&fs, "1:", 1) == FR_OK;
|
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)
|
if(f_open(&file, path, FA_READ) == FR_OK)
|
||||||
{
|
{
|
||||||
u32 size = f_size(&file);
|
u32 size = f_size(&file);
|
||||||
if(dest == NULL) ret = size;
|
if(!(size > maxSize))
|
||||||
else if(!(maxSize > 0 && size > maxSize))
|
f_read(&file, dest, size, (unsigned int *)ret);
|
||||||
f_read(&file, dest, size, (unsigned int *)&ret);
|
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +63,7 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
|||||||
{
|
{
|
||||||
unsigned int written;
|
unsigned int written;
|
||||||
f_write(&file, buffer, size, &written);
|
f_write(&file, buffer, size, &written);
|
||||||
|
f_truncate(&file);
|
||||||
f_close(&file);
|
f_close(&file);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
extern bool isN3DS;
|
extern bool isN3DS;
|
||||||
|
|
||||||
u32 mountSD(void);
|
bool mountSd(void);
|
||||||
u32 mountCTRNAND(void);
|
bool mountCtrNand(void);
|
||||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||||
u32 firmRead(void *dest);
|
u32 firmRead(void *dest);
|
@ -9,7 +9,10 @@
|
|||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "draw.h"
|
#include "draw.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include "types.h"
|
||||||
#include "fatfs/sdmmc/sdmmc.h"
|
#include "fatfs/sdmmc/sdmmc.h"
|
||||||
|
#include "../build/bundled.h"
|
||||||
|
|
||||||
static const u8 sectorHash[0x20] = {
|
static const u8 sectorHash[0x20] = {
|
||||||
0x82, 0xF2, 0x73, 0x0D, 0x2C, 0x2D, 0xA3, 0xF3, 0x01, 0x65, 0xF9, 0x87, 0xFD, 0xCC, 0xAC, 0x5C,
|
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
|
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] = {
|
static const u8 firm1Hash[0x20] = {
|
||||||
0xD2, 0x53, 0xC1, 0xCC, 0x0A, 0x5F, 0xFA, 0xC6, 0xB3, 0x83, 0xDA, 0xC1, 0x82, 0x7C, 0xFB, 0x3B,
|
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
|
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)
|
void main(void)
|
||||||
{
|
{
|
||||||
initScreens();
|
|
||||||
sdmmc_sdcard_init();
|
|
||||||
|
|
||||||
//Determine if booting with A9LH
|
//Determine if booting with A9LH
|
||||||
bool isA9lh = !PDN_SPI_CNT;
|
bool isA9lh = !PDN_SPI_CNT;
|
||||||
|
|
||||||
//Detect the console being used
|
//Detect the console being used
|
||||||
isN3DS = PDN_MPCORE_CFG == 7;
|
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);
|
drawString(TITLE, 10, 10, COLOR_TITLE);
|
||||||
posY = drawString("Thanks to delebile, #cakey and StandardBus", 10, 40, COLOR_WHITE);
|
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();
|
if(pressed == BUTTON_START && isA9lh) uninstaller();
|
||||||
|
|
||||||
shutdown(0, NULL);
|
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");
|
shutdown(1, "Error: failed to mount the SD card");
|
||||||
|
|
||||||
bool updateA9lh = false;
|
bool updateA9lh = false;
|
||||||
|
|
||||||
//If making a first install, we need the OTP
|
//If making a first install on O3DS, we need the OTP
|
||||||
if(!isA9lh)
|
if(!isA9lh && !isN3DS)
|
||||||
{
|
{
|
||||||
const char otpPath[] = "a9lh/otp.bin";
|
const char otpPath[] = "a9lh/otp.bin";
|
||||||
const u8 zeroes[256] = {0};
|
const u8 zeroes[256] = {0};
|
||||||
@ -75,7 +96,7 @@ static inline void installer(bool isA9lh)
|
|||||||
if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0)
|
if(memcmp((void *)OTP_FROM_MEM, zeroes, 256) == 0)
|
||||||
{
|
{
|
||||||
// Read OTP from file
|
// 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");
|
shutdown(1, "Error: otp.bin doesn't exist and can't be dumped");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -87,86 +108,108 @@ static inline void installer(bool isA9lh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Setup the key sector de/encryption with the SHA register or otp.bin
|
//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
|
//Calculate the CTR for the 3DS partitions
|
||||||
getNandCTR();
|
getNandCtr();
|
||||||
|
|
||||||
//Get NAND FIRM0 and test that the CTR is correct
|
//Get NAND FIRM0 and test that the CTR is correct
|
||||||
readFirm0((u8 *)FIRM0_OFFSET, FIRM0_SIZE);
|
if(!isOtpless)
|
||||||
if(memcmp((void *)FIRM0_OFFSET, "FIRM", 4) != 0)
|
{
|
||||||
shutdown(1, "Error: failed to setup FIRM encryption");
|
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 booting from A9LH or on N3DS, we can use the key sector from NAND
|
||||||
if(isA9lh || isN3DS)
|
if(isA9lh || isN3DS) getSector((u8 *)SECTOR_OFFSET, isA9lh);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Read decrypted key sector
|
//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");
|
shutdown(1, "Error: secret_sector.bin doesn't exist or has\na wrong size");
|
||||||
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
if(!verifyHash((void *)SECTOR_OFFSET, 0x200, sectorHash))
|
||||||
shutdown(1, "Error: secret_sector.bin is invalid or corrupted");
|
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
|
u32 i;
|
||||||
generateSector((u8 *)SECTOR_OFFSET, 0);
|
for(i = 0; i < 4; i++)
|
||||||
|
if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[i], 0x10) == 0) break;
|
||||||
|
|
||||||
//Read FIRM0
|
if(i == 4) shutdown(1, "Error: the OTP hash or the NAND key sector\nare invalid");
|
||||||
if(!fileRead((void *)FIRM0_OFFSET, "a9lh/firm0.bin", FIRM0_SIZE))
|
if(i == 0) updateA9lh = true;
|
||||||
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(!verifyHash((void *)FIRM0_OFFSET, SECTION2_POSITION, firm0A9lhHash))
|
|
||||||
shutdown(1, "Error: NAND FIRM0 is invalid");
|
|
||||||
|
|
||||||
if(!isA9lh)
|
if(!isA9lh)
|
||||||
{
|
{
|
||||||
//Read FIRM1
|
//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");
|
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, firm1Hash))
|
||||||
shutdown(1, "Error: firm1.bin is invalid or corrupted");
|
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
|
if(!isA9lh || updateA9lh || isOtpless) generateSector((u8 *)SECTOR_OFFSET, (isN3DS && !isA9lh) ? 1 : 0);
|
||||||
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");
|
|
||||||
|
|
||||||
const u8 zeroes[688] = {0};
|
if(!isA9lh || updateA9lh)
|
||||||
if(memcmp(zeroes, (void *)STAGE1_OFFSET, 688) == 0)
|
{
|
||||||
shutdown(1, "Error: the payload_stage1.bin you're attempting\nto install is not compatible");
|
//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
|
if(!isOtpless)
|
||||||
memset32((void *)STAGE2_OFFSET, 0, MAX_STAGE2_SIZE);
|
{
|
||||||
if(!fileRead((void *)STAGE2_OFFSET, "a9lh/payload_stage2.bin", MAX_STAGE2_SIZE))
|
//Inject stage1
|
||||||
shutdown(1, "Error: payload_stage2.bin doesn't exist or\nexceeds max size");
|
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) 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);
|
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)
|
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
|
//New 3DSes need a key sector with a proper key2, Old 3DSes have a blank key sector
|
||||||
if(isN3DS)
|
if(isN3DS)
|
||||||
{
|
{
|
||||||
setupKeyslot0x11(1, NULL);
|
setupKeyslot0x11(NULL, true);
|
||||||
getSector((u8 *)SECTOR_OFFSET);
|
getSector((u8 *)SECTOR_OFFSET, true);
|
||||||
if(memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[1], 0x10) != 0 && memcmp((void *)(SECTOR_OFFSET + 0x10), key2s[2], 0x10) != 0)
|
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");
|
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);
|
else memset32((void *)SECTOR_OFFSET, 0, 0x200);
|
||||||
|
|
||||||
if(!mountCTRNAND())
|
if(!mountCtrNand())
|
||||||
shutdown(1, "Error: failed to mount CTRNAND");
|
shutdown(1, "Error: failed to mount CTRNAND");
|
||||||
|
|
||||||
//Read FIRM cxi from CTRNAND
|
//Read FIRM cxi from CTRNAND
|
||||||
|
@ -13,15 +13,17 @@
|
|||||||
#define OTP_OFFSET 0x24000000
|
#define OTP_OFFSET 0x24000000
|
||||||
#define SECTOR_OFFSET 0x24100000
|
#define SECTOR_OFFSET 0x24100000
|
||||||
#define FIRM0_OFFSET 0x24200000
|
#define FIRM0_OFFSET 0x24200000
|
||||||
#define FIRM1_OFFSET 0x24300000
|
#define FIRM0_100_OFFSET 0x24300000
|
||||||
|
#define FIRM1_OFFSET 0x24400000
|
||||||
#define FIRM0_SIZE 0xF3000
|
#define FIRM0_SIZE 0xF3000
|
||||||
|
#define FIRM0100_SIZE 0xF2000
|
||||||
#define SECTION2_POSITION 0x66A00
|
#define SECTION2_POSITION 0x66A00
|
||||||
#define FIRM1_SIZE 0xF2000
|
#define FIRM1_SIZE 0xF2000
|
||||||
#define STAGE1_POSITION 0xF0590
|
#define STAGE1_POSITION 0xF0590
|
||||||
#define STAGE1_OFFSET FIRM0_OFFSET + STAGE1_POSITION
|
#define STAGE1_OFFSET FIRM0_OFFSET + STAGE1_POSITION
|
||||||
#define STAGE2_OFFSET 0x24400000
|
#define STAGE2_OFFSET 0x24500000
|
||||||
#define MAX_STAGE1_SIZE 0x1E70
|
#define MAX_STAGE1_SIZE 0x1E70
|
||||||
#define MAX_STAGE2_SIZE 0x89A00
|
#define MAX_STAGE2_SIZE 0x89A00
|
||||||
|
|
||||||
static inline void installer(bool isA9lh);
|
static inline void installer(bool isA9lh, bool isOtpless);
|
||||||
static inline void uninstaller(void);
|
static inline void uninstaller(void);
|
@ -35,7 +35,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "screen.h"
|
#include "screen.h"
|
||||||
#include "draw.h"
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
#include "i2c.h"
|
#include "i2c.h"
|
||||||
|
|
||||||
@ -92,8 +91,8 @@ void initScreens(void)
|
|||||||
*(vu32 *)0x10141200 = 0x1007F;
|
*(vu32 *)0x10141200 = 0x1007F;
|
||||||
*(vu32 *)0x10202014 = 0x00000001;
|
*(vu32 *)0x10202014 = 0x00000001;
|
||||||
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
*(vu32 *)0x1020200C &= 0xFFFEFFFE;
|
||||||
*(vu32 *)0x10202240 = 0x45;
|
*(vu32 *)0x10202240 = 0x4C;
|
||||||
*(vu32 *)0x10202A40 = 0x45;
|
*(vu32 *)0x10202A40 = 0x4C;
|
||||||
*(vu32 *)0x10202244 = 0x1023E;
|
*(vu32 *)0x10202244 = 0x1023E;
|
||||||
*(vu32 *)0x10202A44 = 0x1023E;
|
*(vu32 *)0x10202A44 = 0x1023E;
|
||||||
|
|
||||||
@ -188,22 +187,16 @@ void initScreens(void)
|
|||||||
WAIT_FOR_ARM9();
|
WAIT_FOR_ARM9();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool needToSetup = true;
|
if(PDN_GPU_CNT == 1)
|
||||||
|
|
||||||
if(needToSetup)
|
|
||||||
{
|
{
|
||||||
if(PDN_GPU_CNT == 1)
|
invokeArm11Function(initSequence);
|
||||||
{
|
|
||||||
invokeArm11Function(initSequence);
|
|
||||||
|
|
||||||
//Turn on backlight
|
//Turn on backlight
|
||||||
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
||||||
}
|
|
||||||
|
|
||||||
flushDCacheRange((void *)fb, sizeof(struct fb));
|
|
||||||
invokeArm11Function(setupFramebuffers);
|
|
||||||
needToSetup = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flushDCacheRange((void *)fb, sizeof(struct fb));
|
||||||
|
invokeArm11Function(setupFramebuffers);
|
||||||
|
|
||||||
clearScreens();
|
clearScreens();
|
||||||
}
|
}
|
@ -35,6 +35,17 @@ u32 waitInput(void)
|
|||||||
return key;
|
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)
|
void shutdown(u32 mode, const char *message)
|
||||||
{
|
{
|
||||||
if(mode)
|
if(mode)
|
||||||
@ -45,7 +56,10 @@ void shutdown(u32 mode, const char *message)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(PDN_GPU_CNT != 1) clearScreens();
|
if(PDN_GPU_CNT != 1) clearScreens();
|
||||||
|
|
||||||
|
//Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||||
flushEntireDCache();
|
flushEntireDCache();
|
||||||
|
|
||||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
|
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 0);
|
||||||
while(true);
|
while(true);
|
||||||
}
|
}
|
@ -24,4 +24,5 @@
|
|||||||
extern u32 posY;
|
extern u32 posY;
|
||||||
|
|
||||||
u32 waitInput(void);
|
u32 waitInput(void);
|
||||||
|
void mcuReboot(void);
|
||||||
void shutdown(u32 mode, const char *message);
|
void shutdown(u32 mode, const char *message);
|
Loading…
x
Reference in New Issue
Block a user