Compare commits
271 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7bdf3fc19 | ||
|
|
c4e5f4410c | ||
|
|
f5039dca56 | ||
|
|
de6f9789cb | ||
|
|
4376a1093f | ||
|
|
a413ed01f3 | ||
|
|
05bb831321 | ||
|
|
6e0f1e5235 | ||
|
|
2f915401dd | ||
|
|
14152b3072 | ||
|
|
8f3cba37b3 | ||
|
|
0853f6f7ad | ||
|
|
ae7a7ba365 | ||
|
|
724af0fdc4 | ||
|
|
550ea2116e | ||
|
|
7952271d61 | ||
|
|
ffaa5790c2 | ||
|
|
934c4bed0b | ||
|
|
6f040caa98 | ||
|
|
50e5c29b1c | ||
|
|
e0b5539b91 | ||
|
|
050f58a3bb | ||
|
|
c739ed8e48 | ||
|
|
a09fc6ebcb | ||
|
|
4367ea6afa | ||
|
|
da0adeb39e | ||
|
|
63160a22a6 | ||
|
|
2bc1927ebd | ||
|
|
66e0cfc185 | ||
|
|
7bd0e4f5b4 | ||
|
|
7271850df2 | ||
|
|
8c79285774 | ||
|
|
56a77ceac1 | ||
|
|
3034420ac2 | ||
|
|
99a157649d | ||
|
|
e6645554f0 | ||
|
|
0f83d1ffaa | ||
|
|
2286eb21a7 | ||
|
|
64c81aa5f8 | ||
|
|
29dcfca608 | ||
|
|
f1cb9630ec | ||
|
|
ebbea57400 | ||
|
|
6016dc14f8 | ||
|
|
dc8e4e5f14 | ||
|
|
72c8212a0e | ||
|
|
2cd2a081d6 | ||
|
|
b44ec544f4 | ||
|
|
063eae7e95 | ||
|
|
2552572904 | ||
|
|
b74dda42a2 | ||
|
|
6e178e2069 | ||
|
|
277d723992 | ||
|
|
2f6afe9932 | ||
|
|
edcc617780 | ||
|
|
5d39242b83 | ||
|
|
ddbe5fd27b | ||
|
|
deb91d1d02 | ||
|
|
63073ea07f | ||
|
|
a331fcd873 | ||
|
|
99654bd5b2 | ||
|
|
f10427287f | ||
|
|
5b6318ee3a | ||
|
|
98cdbe0784 | ||
|
|
e4eb3ae38d | ||
|
|
6686e4add7 | ||
|
|
96a274bf7d | ||
|
|
add17a7994 | ||
|
|
26e404c6a2 | ||
|
|
144de8b328 | ||
|
|
c0226e6fd9 | ||
|
|
57d728a9be | ||
|
|
b2e9f5377b | ||
|
|
d3d315784c | ||
|
|
942a371b6a | ||
|
|
66ffc78f60 | ||
|
|
a9570cef14 | ||
|
|
16bc08f046 | ||
|
|
024335e5cd | ||
|
|
8f32048774 | ||
|
|
33d3dd256d | ||
|
|
fc151f7595 | ||
|
|
9152884815 | ||
|
|
2b4e97bec5 | ||
|
|
a63393504f | ||
|
|
97ae8d2d44 | ||
|
|
6c59728516 | ||
|
|
201fe05a06 | ||
|
|
a78889523f | ||
|
|
5cd1a207c9 | ||
|
|
423f0ca631 | ||
|
|
7612e38d41 | ||
|
|
bf234950cb | ||
|
|
bd992fca15 | ||
|
|
45fee57abd | ||
|
|
a2bca96d47 | ||
|
|
9862256ca7 | ||
|
|
6afc8b3b5c | ||
|
|
9b4b4ec543 | ||
|
|
d412711868 | ||
|
|
0b33551d94 | ||
|
|
9c1b585054 | ||
|
|
fc3a69d9ca | ||
|
|
cce4a6d07f | ||
|
|
b16539408b | ||
|
|
eba56d0f64 | ||
|
|
f2181b1d3e | ||
|
|
c93a97f8d7 | ||
|
|
8fd2525cd1 | ||
|
|
62a70cfdac | ||
|
|
ca0a79fc89 | ||
|
|
104b8e859f | ||
|
|
a99b26ff8c | ||
|
|
9bc62ec12d | ||
|
|
84a6bb772f | ||
|
|
263a5eda56 | ||
|
|
7fb17ce52f | ||
|
|
631fad24c0 | ||
|
|
f1d77fa62a | ||
|
|
3bc247b43b | ||
|
|
3608799dff | ||
|
|
c0f042bc2a | ||
|
|
8982ff259a | ||
|
|
726b06b748 | ||
|
|
0057ab9c5a | ||
|
|
048bf0ee4d | ||
|
|
4da0708b51 | ||
|
|
4c529e3145 | ||
|
|
8ceeca372f | ||
|
|
4326ac9be1 | ||
|
|
697c64abe4 | ||
|
|
7e8cf84c42 | ||
|
|
b6d91375a5 | ||
|
|
61305688fd | ||
|
|
492ae4139f | ||
|
|
a84f393bd5 | ||
|
|
5de54d6f9f | ||
|
|
8fc797120f | ||
|
|
5406d648bc | ||
|
|
a2003fba95 | ||
|
|
a76c15d018 | ||
|
|
ae8caf3d3a | ||
|
|
f221915a95 | ||
|
|
7f93733107 | ||
|
|
e8b9e49f57 | ||
|
|
cb9576b10e | ||
|
|
06fe06f9f1 | ||
|
|
abf7c8e565 | ||
|
|
46227e6763 | ||
|
|
2ffe4a5451 | ||
|
|
18bd4bbcf6 | ||
|
|
8209433696 | ||
|
|
ad9e00acaa | ||
|
|
c96f96258f | ||
|
|
82699f3e00 | ||
|
|
e2d828a4a2 | ||
|
|
92328c6a7e | ||
|
|
3f8ad17e86 | ||
|
|
52999db43a | ||
|
|
760aa99709 | ||
|
|
51c514de84 | ||
|
|
317899b4bf | ||
|
|
4c93d2b1f9 | ||
|
|
71c5404bfe | ||
|
|
9c5248b87a | ||
|
|
83a0293af4 | ||
|
|
84f3eddaf8 | ||
|
|
a5c6b908b6 | ||
|
|
94f0d873df | ||
|
|
d6f66d24fa | ||
|
|
31458e9938 | ||
|
|
dca612ffd7 | ||
|
|
5177308d48 | ||
|
|
59ab44a8c7 | ||
|
|
58c6be17eb | ||
|
|
259bcc686c | ||
|
|
4c6a2f7ab1 | ||
|
|
76cee0d95b | ||
|
|
27ea6b96c4 | ||
|
|
4885e0b8d8 | ||
|
|
2a84911d3b | ||
|
|
7f672fb417 | ||
|
|
a13fdeecf6 | ||
|
|
0951260df7 | ||
|
|
1dd6a9821a | ||
|
|
61e5a4444f | ||
|
|
de3eb6ccd7 | ||
|
|
ef8438a214 | ||
|
|
dfd699c16b | ||
|
|
e4c87a59d0 | ||
|
|
76411387ba | ||
|
|
8d5d8d2100 | ||
|
|
802bce12a7 | ||
|
|
a4531b01d5 | ||
|
|
24ddf5ca3d | ||
|
|
57fc4df86f | ||
|
|
070f7fcc68 | ||
|
|
4d48238fb1 | ||
|
|
8e5ced8e3c | ||
|
|
915f9ccb0d | ||
|
|
3709ac301a | ||
|
|
88891ef6cd | ||
|
|
25811e2b52 | ||
|
|
f81c92e35b | ||
|
|
39b2aff627 | ||
|
|
4e21cf4f1c | ||
|
|
eccf9eab93 | ||
|
|
c36ad54bcb | ||
|
|
81e2b7c11c | ||
|
|
fe31114779 | ||
|
|
df30a2a320 | ||
|
|
cf7fa8ecbd | ||
|
|
fd1253caac | ||
|
|
004f0652c9 | ||
|
|
d5190cd788 | ||
|
|
2412faf033 | ||
|
|
7df9917a00 | ||
|
|
2d6debddb9 | ||
|
|
153bbd6242 | ||
|
|
0eb87df84f | ||
|
|
a9db998d84 | ||
|
|
fcb8edffdf | ||
|
|
0ced86b55f | ||
|
|
984c3e549c | ||
|
|
ef60c8ea4c | ||
|
|
e478908dca | ||
|
|
ca587692eb | ||
|
|
f9d6cdccb0 | ||
|
|
35fdd470b3 | ||
|
|
edff11be7b | ||
|
|
bb230de72c | ||
|
|
2d7dde9cf9 | ||
|
|
b77d619873 | ||
|
|
99d28c4f79 | ||
|
|
2424865fa1 | ||
|
|
6a280723f8 | ||
|
|
f85bbfff58 | ||
|
|
9017de6082 | ||
|
|
477414a636 | ||
|
|
bafba1197e | ||
|
|
2fab0be5e8 | ||
|
|
b6969cf0d1 | ||
|
|
58a33ee6e9 | ||
|
|
d48f6b62ce | ||
|
|
ca07af2fe9 | ||
|
|
2fe5da97eb | ||
|
|
6ffaeadd1b | ||
|
|
2c10b6bff2 | ||
|
|
b4029dcd73 | ||
|
|
ac01fe417e | ||
|
|
02c6a3c214 | ||
|
|
8dd9c1a1af | ||
|
|
17964f8cd1 | ||
|
|
a63fb971d4 | ||
|
|
1d77206ae4 | ||
|
|
4340c326dc | ||
|
|
c6cb97ae30 | ||
|
|
e2596a0a61 | ||
|
|
f55fc421ea | ||
|
|
169b12fc20 | ||
|
|
408c70b90c | ||
|
|
1e6d7a5aee | ||
|
|
a28b50cee8 | ||
|
|
4a4de608ec | ||
|
|
9c12496b30 | ||
|
|
faa04b0309 | ||
|
|
ca1ea7fd6e | ||
|
|
57dc1b09ad | ||
|
|
f6d3abbc11 | ||
|
|
9ab3a7c451 | ||
|
|
f0e1937eeb | ||
|
|
efd08ff731 |
69
Makefile
69
Makefile
@@ -19,8 +19,13 @@ dir_source := source
|
||||
dir_patches := patches
|
||||
dir_loader := loader
|
||||
dir_injector := injector
|
||||
dir_exceptions := exceptions
|
||||
dir_arm9_exceptions := $(dir_exceptions)/arm9
|
||||
dir_arm11_exceptions := $(dir_exceptions)/arm11
|
||||
dir_mset := CakeHax
|
||||
dir_ninjhax := CakeBrah
|
||||
dir_menuhax := menuhax
|
||||
dir_pathchanger := pathchanger
|
||||
dir_build := build
|
||||
dir_out := out
|
||||
|
||||
@@ -33,11 +38,18 @@ objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/twl_k11modulespatch.h \
|
||||
$(dir_build)/injector.h $(dir_build)/loader.h
|
||||
bundled = $(dir_build)/rebootpatch.h $(dir_build)/emunandpatch.h $(dir_build)/svcGetCFWInfopatch.h $(dir_build)/injector.h $(dir_build)/loader.h
|
||||
|
||||
ifeq ($(strip $(DEV)),TRUE)
|
||||
CFLAGS += -DDEV
|
||||
bundled += $(dir_build)/k11modulespatch.h $(dir_build)/arm9_exceptions.h $(dir_build)/arm11_exceptions.h
|
||||
title := \"$(name) $(revision) (dev) configuration\"
|
||||
else
|
||||
title := \"$(name) $(revision) configuration\"
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: launcher a9lh ninjhax
|
||||
all: launcher a9lh ninjhax menuhax
|
||||
|
||||
.PHONY: launcher
|
||||
launcher: $(dir_out)/$(name).dat
|
||||
@@ -48,14 +60,23 @@ a9lh: $(dir_out)/arm9loaderhax.bin
|
||||
.PHONY: ninjhax
|
||||
ninjhax: $(dir_out)/3ds/$(name)
|
||||
|
||||
.PHONY: menuhax
|
||||
menuhax: $(dir_out)/menuhax/boot.3dsx
|
||||
|
||||
.PHONY: release
|
||||
ifeq ($(strip $(DEV)),TRUE)
|
||||
release: $(dir_out)/$(name)$(revision)-dev.7z
|
||||
else
|
||||
release: $(dir_out)/$(name)$(revision).7z
|
||||
endif
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) clean
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax) clean
|
||||
@$(MAKE) -C $(dir_loader) clean
|
||||
@$(MAKE) -C $(dir_arm9_exceptions) clean
|
||||
@$(MAKE) -C $(dir_arm11_exceptions) clean
|
||||
@$(MAKE) -C $(dir_injector) clean
|
||||
@rm -rf $(dir_out) $(dir_build)
|
||||
|
||||
@@ -66,6 +87,12 @@ $(dir_out)/$(name).dat: $(dir_build)/main.bin $(dir_out)
|
||||
@$(MAKE) $(FLAGS) -C $(dir_mset) launcher
|
||||
@dd if=$(dir_build)/main.bin of=$@ bs=512 seek=144
|
||||
|
||||
$(dir_out)/menuhax/boot.3dsx: $(dir_menuhax)/menuhax.diff $(dir_out)
|
||||
@mkdir -p "$(@D)"
|
||||
@cd $(dir_ninjhax); patch -p1 < ../$(dir_menuhax)/menuhax.diff; $(MAKE) $(FLAGS); git reset --hard
|
||||
@mv $(dir_out)/$(name).3dsx $@
|
||||
@rm $(dir_out)/$(name).smdh
|
||||
|
||||
$(dir_out)/arm9loaderhax.bin: $(dir_build)/main.bin $(dir_out)
|
||||
@cp -a $(dir_build)/main.bin $@
|
||||
|
||||
@@ -74,16 +101,24 @@ $(dir_out)/3ds/$(name): $(dir_out)
|
||||
@$(MAKE) $(FLAGS) -C $(dir_ninjhax)
|
||||
@mv $(dir_out)/$(name).3dsx $(dir_out)/$(name).smdh $@
|
||||
|
||||
$(dir_out)/$(name)$(revision).7z: launcher a9lh ninjhax
|
||||
$(dir_out)/pathchanger: $(dir_pathchanger)/pathchanger.py $(dir_pathchanger)/prebuilt $(dir_out)
|
||||
@mkdir -p "$@"
|
||||
@cp $(dir_pathchanger)/pathchanger.py $@
|
||||
@cp -rfT $(dir_pathchanger)/prebuilt $@
|
||||
|
||||
$(dir_out)/$(name)$(revision).7z: all $(dir_out)/pathchanger
|
||||
@7z a -mx $@ ./$(@D)/*
|
||||
|
||||
$(dir_out)/$(name)$(revision)-dev.7z: all $(dir_out)/pathchanger
|
||||
@7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser.py
|
||||
|
||||
$(dir_build)/main.bin: $(dir_build)/main.elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/main.elf: $(objects)
|
||||
$(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/emunandpatch.h: $(dir_patches)/emunand.s $(dir_injector)/Makefile
|
||||
$(dir_build)/emunandpatch.h: $(dir_patches)/emunand.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@bin2c -o $@ -n emunand $(@D)/emunand.bin
|
||||
@@ -98,22 +133,30 @@ $(dir_build)/svcGetCFWInfopatch.h: $(dir_patches)/svcGetCFWInfo.s
|
||||
@armips $<
|
||||
@bin2c -o $@ -n svcGetCFWInfo $(@D)/svcGetCFWInfo.bin
|
||||
|
||||
$(dir_build)/twl_k11modulespatch.h: $(dir_patches)/twl_k11modules.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@bin2c -o $@ -n twl_k11modules $(@D)/twl_k11modules.bin
|
||||
|
||||
$(dir_build)/injector.h: $(dir_injector)/Makefile
|
||||
@mkdir -p "$(@D)"
|
||||
@$(MAKE) -C $(dir_injector)
|
||||
@$(MAKE) -C $(dir_injector) DEV=$(DEV)
|
||||
@bin2c -o $@ -n injector $(@D)/injector.cxi
|
||||
|
||||
$(dir_build)/loader.h: $(dir_loader)/Makefile
|
||||
@$(MAKE) -C $(dir_loader)
|
||||
@bin2c -o $@ -n loader $(@D)/loader.bin
|
||||
|
||||
$(dir_build)/memory.o: CFLAGS += -O3
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\""
|
||||
$(dir_build)/k11modulespatch.h: $(dir_patches)/k11modules.s
|
||||
@mkdir -p "$(@D)"
|
||||
@armips $<
|
||||
@bin2c -o $@ -n k11modules $(@D)/k11modules.bin
|
||||
|
||||
$(dir_build)/arm9_exceptions.h: $(dir_arm9_exceptions)/Makefile
|
||||
@$(MAKE) -C $(dir_arm9_exceptions)
|
||||
@bin2c -o $@ -n arm9_exceptions $(@D)/arm9_exceptions.bin
|
||||
|
||||
$(dir_build)/arm11_exceptions.h: $(dir_arm11_exceptions)/Makefile
|
||||
@$(MAKE) -C $(dir_arm11_exceptions)
|
||||
@bin2c -o $@ -n arm11_exceptions $(@D)/arm11_exceptions.bin
|
||||
|
||||
$(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3
|
||||
$(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="$(title)"
|
||||
$(dir_build)/patches.o: CFLAGS += -DREVISION=\"$(revision)\" -DCOMMIT_HASH="0x$(commit)"
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c $(bundled)
|
||||
|
||||
26
README.md
26
README.md
@@ -1,23 +1,37 @@
|
||||
# Luma3DS
|
||||
*Noob-proof (N)3DS "Custom Firmware"*
|
||||
|
||||
**Compiling:**
|
||||
## What it is
|
||||
|
||||
**Luma3DS** is a program to patch the system software of (New) Nintendo 3DS handheld consoles "on the fly", adding features (such as per-game language settings and debugging capabilities for developers) and removing restrictions enforced by Nintendo (such as the region lock).
|
||||
It also allows you to run unauthorized ("homebrew") content by removing signature checks.
|
||||
To use it, you will need a console capable of running homebrew software on the ARM9 processor. We recommend [Plailect's guide](https://github.com/Plailect/Guide/wiki) for details on how to get your system ready.
|
||||
|
||||
---
|
||||
|
||||
## Compiling
|
||||
|
||||
First you need to clone the repository recursively with: `git clone --recursive https://github.com/AuroraWright/Luma3DS.git`
|
||||
To compile, you'll need [armips](https://github.com/Kingcom/armips), [bin2c](https://sourceforge.net/projects/bin2c/), and a recent build of [makerom](https://github.com/profi200/Project_CTR) added to your PATH.
|
||||
For your convenience, here are [Windows](http://www91.zippyshare.com/v/ePGpjk9r/file.html) and [Linux](https://mega.nz/#!uQ1T1IAD!Q91O0e12LXKiaXh_YjXD3D5m8_W3FuMI-hEa6KVMRDQ) builds of armips (thanks to who compiled them!).
|
||||
Finally just run `make` and everything should work!
|
||||
You can find the compiled files in the 'out' folder.
|
||||
Finally just run `make` (for the regular version) or `make DEV=TRUE` (for the dev version) and everything should work!
|
||||
You can find the compiled files in the `out` folder.
|
||||
|
||||
**Setup / Usage / Features:**
|
||||
---
|
||||
|
||||
## Setup / Usage / Features
|
||||
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki
|
||||
|
||||
**Credits:**
|
||||
---
|
||||
|
||||
## Credits
|
||||
|
||||
See https://github.com/AuroraWright/Luma3DS/wiki/Credits
|
||||
|
||||
**Licensing:**
|
||||
---
|
||||
|
||||
## Licensing
|
||||
|
||||
This software is licensed under the terms of the GPLv3.
|
||||
You can find a copy of the license in the LICENSE.txt file.
|
||||
|
||||
47
exceptions/arm11/Makefile
Normal file
47
exceptions/arm11/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := arm11_exceptions
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
|
||||
ASFLAGS := -mcpu=mpcore -mfpu=vfp
|
||||
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_build)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
../../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
11
exceptions/arm11/linker.ld
Normal file
11
exceptions/arm11/linker.ld
Normal file
@@ -0,0 +1,11 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
48
exceptions/arm11/source/handlers.h
Normal file
48
exceptions/arm11/source/handlers.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic[2];
|
||||
u16 versionMinor, versionMajor;
|
||||
|
||||
u16 processor, core;
|
||||
u32 type;
|
||||
|
||||
u32 totalSize;
|
||||
u32 registerDumpSize;
|
||||
u32 codeDumpSize;
|
||||
u32 stackDumpSize;
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
void __attribute__((noreturn)) mcuReboot(void);
|
||||
void cleanInvalidateDCacheAndDMB(void);
|
||||
bool cannotAccessVA(const void *address);
|
||||
void FIQHandler(void);
|
||||
void undefinedInstructionHandler(void);
|
||||
void dataAbortHandler(void);
|
||||
void prefetchAbortHandler(void);
|
||||
150
exceptions/arm11/source/handlers.s
Normal file
150
exceptions/arm11/source/handlers.s
Normal file
@@ -0,0 +1,150 @@
|
||||
@ 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.
|
||||
|
||||
.macro GEN_HANDLER name
|
||||
.global \name
|
||||
.type \name, %function
|
||||
\name:
|
||||
ldr sp, =#0xffff3000
|
||||
stmfd sp!, {r0-r7}
|
||||
mov r1, #\@ @ macro expansion counter
|
||||
b _commonHandler
|
||||
|
||||
.size \name, . - \name
|
||||
.endm
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global _commonHandler
|
||||
.type _commonHandler, %function
|
||||
_commonHandler:
|
||||
clrex
|
||||
cpsid aif
|
||||
mrs r2, spsr
|
||||
mov r6, sp
|
||||
mrs r3, cpsr
|
||||
|
||||
tst r2, #0x20
|
||||
bne noFPUInitNorSvcBreak
|
||||
sub r0, lr, #4
|
||||
stmfd sp!, {lr}
|
||||
bl cannotAccessVA
|
||||
ldmfd sp!, {lr}
|
||||
cmp r0, #0
|
||||
bne noFPUInitNorSvcBreak
|
||||
ldr r4, [lr, #-4]
|
||||
cmp r1, #1
|
||||
bne noFPUInit
|
||||
|
||||
lsl r4, #4
|
||||
sub r4, #0xc0000000
|
||||
cmp r4, #0x30000000
|
||||
bcs noFPUInitNorSvcBreak
|
||||
fmrx r0, fpexc
|
||||
tst r0, #0x40000000
|
||||
bne noFPUInitNorSvcBreak
|
||||
|
||||
sub lr, #4
|
||||
srsfd sp!, #0x13
|
||||
ldmfd sp!, {r0-r7} @ restore context
|
||||
cps #0x13 @ FPU init
|
||||
stmfd sp, {r0-r3, r11-lr}^
|
||||
sub sp, #0x20
|
||||
bl . @ will be replaced
|
||||
ldmfd sp, {r0-r3, r11-lr}^
|
||||
add sp, #0x20
|
||||
rfefd sp!
|
||||
|
||||
noFPUInit:
|
||||
cmp r1, #2
|
||||
bne noFPUInitNorSvcBreak
|
||||
ldr r5, =#0xe12fff7f
|
||||
cmp r4, r5
|
||||
bne noFPUInitNorSvcBreak
|
||||
cps #0x13 @ switch to supervisor mode
|
||||
cmp r10, #0
|
||||
addne sp, #0x28
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
addne lr, r4, #2 @ adjust address for later
|
||||
moveq lr, r4
|
||||
|
||||
noFPUInitNorSvcBreak:
|
||||
ands r4, r2, #0xf @ get the mode that triggered the exception
|
||||
moveq r4, #0xf @ usr => sys
|
||||
bic r5, r3, #0xf
|
||||
orr r5, r4
|
||||
msr cpsr_c, r5 @ change processor mode
|
||||
stmfd r6!, {r8-lr}
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
mov sp, r6
|
||||
|
||||
stmfd sp!, {r2,lr}
|
||||
|
||||
mrc p15,0,r4,c5,c0,0 @ dfsr
|
||||
mrc p15,0,r5,c5,c0,1 @ ifsr
|
||||
mrc p15,0,r6,c6,c0,0 @ far
|
||||
fmrx r7, fpexc
|
||||
fmrx r8, fpinst
|
||||
fmrx r9, fpinst2
|
||||
|
||||
stmfd sp!, {r4-r9} @ it's a bit of a mess, but we will fix that later
|
||||
@ order of saved regs now: dfsr, ifsr, far, fpexc, fpinst, fpinst2, cpsr, pc + (2/4/8), r8-r14, r0-r7
|
||||
|
||||
bic r3, #(1<<31)
|
||||
fmxr fpexc, r3 @ clear the VFP11 exception flag (if it's set)
|
||||
|
||||
mov r0, sp
|
||||
mrc p15,0,r2,c0,c0,5 @ CPU ID register
|
||||
|
||||
b mainHandler
|
||||
|
||||
GEN_HANDLER FIQHandler
|
||||
GEN_HANDLER undefinedInstructionHandler
|
||||
GEN_HANDLER prefetchAbortHandler
|
||||
GEN_HANDLER dataAbortHandler
|
||||
|
||||
.global mcuReboot
|
||||
.type mcuReboot, %function
|
||||
mcuReboot:
|
||||
b . @ will be replaced
|
||||
|
||||
.global cleanInvalidateDCacheAndDMB
|
||||
.type cleanInvalidateDCacheAndDMB, %function
|
||||
cleanInvalidateDCacheAndDMB:
|
||||
mov r0, #0
|
||||
mcr p15,0,r0,c7,c14,0 @ Clean and Invalidate Entire Data Cache
|
||||
mcr p15,0,r0,c7,c10,4 @ Drain Memory Barrier
|
||||
bx lr
|
||||
|
||||
.global cannotAccessVA
|
||||
.type cannotAccessVA, %function
|
||||
cannotAccessVA:
|
||||
@ Thanks yellows8 for the hint
|
||||
lsr r0, #12
|
||||
lsl r0, #12
|
||||
mcr p15,0,r0,c7,c8,0 @ VA to PA translation with privileged read permission check
|
||||
mrc p15,0,r0,c7,c4,0 @ read PA register
|
||||
and r0, #1 @ failure bit
|
||||
bx lr
|
||||
110
exceptions/arm11/source/mainHandler.c
Normal file
110
exceptions/arm11/source/mainHandler.c
Normal file
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 "handlers.h"
|
||||
|
||||
#define FINAL_BUFFER 0xE5000000 //0x25000000
|
||||
|
||||
#define REG_DUMP_SIZE 4 * 23
|
||||
#define CODE_DUMP_SIZE 48
|
||||
|
||||
#define CODESET_OFFSET 0xBEEFBEEF
|
||||
|
||||
static u32 __attribute__((noinline)) copyMemory(void *dst, const void *src, u32 size, u32 alignment)
|
||||
{
|
||||
u8 *out = (u8 *)dst;
|
||||
const u8 *in = (const u8 *)src;
|
||||
|
||||
if(((u32)src & (alignment - 1)) != 0 || cannotAccessVA(src) || (size != 0 && cannotAccessVA((u8 *)src + size - 1)))
|
||||
return 0;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
*out++ = *in++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type, u32 cpuId)
|
||||
{
|
||||
ExceptionDumpHeader dumpHeader;
|
||||
|
||||
u32 registerDump[REG_DUMP_SIZE / 4];
|
||||
u8 codeDump[CODE_DUMP_SIZE];
|
||||
u8 *final = (u8 *)FINAL_BUFFER;
|
||||
|
||||
while(*(vu32 *)final == 0xDEADC0DE && *((vu32 *)final + 1) == 0xDEADCAFE);
|
||||
|
||||
dumpHeader.magic[0] = 0xDEADC0DE;
|
||||
dumpHeader.magic[1] = 0xDEADCAFE;
|
||||
dumpHeader.versionMajor = 1;
|
||||
dumpHeader.versionMinor = 2;
|
||||
|
||||
dumpHeader.processor = 11;
|
||||
dumpHeader.core = cpuId & 0xF;
|
||||
dumpHeader.type = type;
|
||||
|
||||
dumpHeader.registerDumpSize = REG_DUMP_SIZE;
|
||||
dumpHeader.codeDumpSize = CODE_DUMP_SIZE;
|
||||
|
||||
//Dump registers
|
||||
//Current order of saved regs: dfsr, ifsr, far, fpexc, fpinst, fpinst2, cpsr, pc, r8-r12, sp, lr, r0-r7
|
||||
u32 cpsr = regs[6];
|
||||
u32 pc = regs[7] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
for(u32 i = 0; i < 6; i++) registerDump[17 + i] = regs[i];
|
||||
for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[8 + i];
|
||||
for(u32 i = 0; i < 8; i++) registerDump[i] = regs[15 + i];
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
||||
|
||||
//Copy register dump and code dump
|
||||
final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader));
|
||||
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
|
||||
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
|
||||
|
||||
//Dump stack in place
|
||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
||||
final += dumpHeader.stackDumpSize;
|
||||
|
||||
if(!cannotAccessVA((u8 *)0xFFFF9004))
|
||||
{
|
||||
vu64 *additionalData = (vu64 *)final;
|
||||
dumpHeader.additionalDataSize = 16;
|
||||
vu8 *currentKCodeSet = *(vu8 **)(*(vu8 **)0xFFFF9004 + CODESET_OFFSET); //currentKProcess + CodeSet
|
||||
|
||||
additionalData[0] = *(vu64 *)(currentKCodeSet + 0x50); //Process name
|
||||
additionalData[1] = *(vu64 *)(currentKCodeSet + 0x5C); //Title ID
|
||||
}
|
||||
else dumpHeader.additionalDataSize = 0;
|
||||
|
||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
||||
|
||||
//Copy header (actually optimized by the compiler)
|
||||
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader;
|
||||
|
||||
cleanInvalidateDCacheAndDMB();
|
||||
mcuReboot(); //Also contains DCache-cleaning code
|
||||
}
|
||||
31
exceptions/arm11/source/start.s
Normal file
31
exceptions/arm11/source/start.s
Normal file
@@ -0,0 +1,31 @@
|
||||
@ 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.
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
|
||||
|
||||
handlers:
|
||||
.word FIQHandler
|
||||
.word undefinedInstructionHandler
|
||||
.word prefetchAbortHandler
|
||||
.word dataAbortHandler
|
||||
37
exceptions/arm11/source/types.h
Normal file
37
exceptions/arm11/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;
|
||||
47
exceptions/arm9/Makefile
Normal file
47
exceptions/arm9/Makefile
Normal file
@@ -0,0 +1,47 @@
|
||||
rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2))
|
||||
|
||||
ifeq ($(strip $(DEVKITARM)),)
|
||||
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
|
||||
endif
|
||||
|
||||
include $(DEVKITARM)/3ds_rules
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
AS := arm-none-eabi-as
|
||||
LD := arm-none-eabi-ld
|
||||
OC := arm-none-eabi-objcopy
|
||||
|
||||
name := arm9_exceptions
|
||||
|
||||
dir_source := source
|
||||
dir_build := build
|
||||
|
||||
ASFLAGS := -mcpu=arm946e-s
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -mthumb -mthumb-interwork $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math
|
||||
LDFLAGS := -nostdlib
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \
|
||||
$(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.s *.c)))
|
||||
|
||||
.PHONY: all
|
||||
all: ../../$(dir_build)/$(name).bin
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
@rm -rf $(dir_build)
|
||||
|
||||
../../$(dir_build)/$(name).bin: $(dir_build)/$(name).elf
|
||||
$(OC) -S -O binary $< $@
|
||||
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(CC) $(LDFLAGS) -T linker.ld $(OUTPUT_OPTION) $^
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.c
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.c) $(OUTPUT_OPTION) $<
|
||||
|
||||
$(dir_build)/%.o: $(dir_source)/%.s
|
||||
@mkdir -p "$(@D)"
|
||||
$(COMPILE.s) $(OUTPUT_OPTION) $<
|
||||
include $(call rwildcard, $(dir_build), *.d)
|
||||
11
exceptions/arm9/linker.ld
Normal file
11
exceptions/arm9/linker.ld
Normal file
@@ -0,0 +1,11 @@
|
||||
ENTRY(_start)
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x01FF7FE0;
|
||||
.text.start : { *(.text.start) }
|
||||
.text : { *(.text) }
|
||||
.data : { *(.data) }
|
||||
.bss : { *(.bss COMMON) }
|
||||
.rodata : { *(.rodata) }
|
||||
. = ALIGN(4);
|
||||
}
|
||||
46
exceptions/arm9/source/handlers.h
Normal file
46
exceptions/arm9/source/handlers.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic[2];
|
||||
u16 versionMinor, versionMajor;
|
||||
|
||||
u16 processor, core;
|
||||
u32 type;
|
||||
|
||||
u32 totalSize;
|
||||
u32 registerDumpSize;
|
||||
u32 codeDumpSize;
|
||||
u32 stackDumpSize;
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
u32 readMPUConfig(u32 *regionSettings);
|
||||
void FIQHandler(void);
|
||||
void undefinedInstructionHandler(void);
|
||||
void dataAbortHandler(void);
|
||||
void prefetchAbortHandler(void);
|
||||
108
exceptions/arm9/source/handlers.s
Normal file
108
exceptions/arm9/source/handlers.s
Normal file
@@ -0,0 +1,108 @@
|
||||
@ 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.
|
||||
|
||||
.macro GEN_HANDLER name
|
||||
.global \name
|
||||
.type \name, %function
|
||||
\name:
|
||||
ldr sp, =#0x02000000 @ We make the (full descending) stack point to the end of ITCM for our exception handlers.
|
||||
@ It doesn't matter if we're overwriting stuff here, since we're going to reboot.
|
||||
|
||||
stmfd sp!, {r0-r7} @ FIQ has its own r8-r14 regs
|
||||
ldr r1, =\@ @ macro expansion counter
|
||||
b _commonHandler
|
||||
|
||||
.size \name, . - \name
|
||||
.endm
|
||||
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global _commonHandler
|
||||
.type _commonHandler, %function
|
||||
_commonHandler:
|
||||
mrs r2, spsr
|
||||
mov r6, sp
|
||||
mrs r3, cpsr
|
||||
|
||||
orr r3, #0x1c0 @ disable Imprecise Aborts, IRQ and FIQ (equivalent to "cpsid aif" on arm11)
|
||||
msr cpsr_cx, r3
|
||||
|
||||
tst r2, #0x20
|
||||
bne noSvcBreak
|
||||
cmp r1, #2
|
||||
bne noSvcBreak
|
||||
|
||||
sub r0, lr, #4 @ calling cannotAccessAddress cause more problems that it actually solves... (I've to save a lot of regs and that's a pain tbh)
|
||||
lsr r0, #20 @ we'll just do some address checks (to see if it's in ARM9 internal memory)
|
||||
cmp r0, #0x80
|
||||
bne noSvcBreak
|
||||
ldr r4, [lr, #-4]
|
||||
ldr r5, =#0xe12fff7f
|
||||
cmp r4, r5
|
||||
bne noSvcBreak
|
||||
bic r5, r3, #0xf
|
||||
orr r5, #0x3
|
||||
msr cpsr_c, r5 @ switch to supervisor mode
|
||||
ldr r2, [sp, #0x1c] @ implementation details of the official svc handler
|
||||
ldr r4, [sp, #0x18]
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
tst r2, #0x20
|
||||
addne lr, r4, #2 @ adjust address for later
|
||||
moveq lr, r4
|
||||
|
||||
noSvcBreak:
|
||||
ands r4, r2, #0xf @ get the mode that triggered the exception
|
||||
moveq r4, #0xf @ usr => sys
|
||||
bic r5, r3, #0xf
|
||||
orr r5, r4
|
||||
msr cpsr_c, r5 @ change processor mode
|
||||
stmfd r6!, {r8-lr}
|
||||
msr cpsr_c, r3 @ restore processor mode
|
||||
mov sp, r6
|
||||
|
||||
stmfd sp!, {r2,lr} @ it's a bit of a mess, but we will fix that later
|
||||
@ order of saved regs now: cpsr, pc + (2/4/8), r8-r14, r0-r7
|
||||
|
||||
mov r0, sp
|
||||
|
||||
b mainHandler
|
||||
|
||||
GEN_HANDLER FIQHandler
|
||||
GEN_HANDLER undefinedInstructionHandler
|
||||
GEN_HANDLER prefetchAbortHandler
|
||||
GEN_HANDLER dataAbortHandler
|
||||
|
||||
.global readMPUConfig
|
||||
.type readMPUConfig, %function
|
||||
readMPUConfig:
|
||||
stmfd sp!, {r4-r8, lr}
|
||||
mrc p15,0,r1,c6,c0,0
|
||||
mrc p15,0,r2,c6,c1,0
|
||||
mrc p15,0,r3,c6,c2,0
|
||||
mrc p15,0,r4,c6,c3,0
|
||||
mrc p15,0,r5,c6,c4,0
|
||||
mrc p15,0,r6,c6,c5,0
|
||||
mrc p15,0,r7,c6,c6,0
|
||||
mrc p15,0,r8,c6,c7,0
|
||||
stmia r0, {r1-r8}
|
||||
mrc p15,0,r0,c5,c0,2 @ read data access permission bits
|
||||
ldmfd sp!, {r4-r8, pc}
|
||||
139
exceptions/arm9/source/i2c.c
Normal file
139
exceptions/arm9/source/i2c.c
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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 everyone who contributed in the development of this file
|
||||
*/
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static const struct { u8 bus_id, reg_addr; } dev_data[] = {
|
||||
{0, 0x4A}, {0, 0x7A}, {0, 0x78},
|
||||
{1, 0x4A}, {1, 0x78}, {1, 0x2C},
|
||||
{1, 0x2E}, {1, 0x40}, {1, 0x44},
|
||||
{2, 0xD6}, {2, 0xD0}, {2, 0xD2},
|
||||
{2, 0xA4}, {2, 0x9A}, {2, 0xA0},
|
||||
};
|
||||
|
||||
static inline u8 i2cGetDeviceBusId(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].bus_id;
|
||||
}
|
||||
|
||||
static inline u8 i2cGetDeviceRegAddr(u8 device_id)
|
||||
{
|
||||
return dev_data[device_id].reg_addr;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_data_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_DATA),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_DATA),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetDataReg(u8 bus_id)
|
||||
{
|
||||
return reg_data_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static vu8 *reg_cnt_addrs[] = {
|
||||
(vu8 *)(I2C1_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C2_REG_OFF + I2C_REG_CNT),
|
||||
(vu8 *)(I2C3_REG_OFF + I2C_REG_CNT),
|
||||
};
|
||||
|
||||
static inline vu8 *i2cGetCntReg(u8 bus_id)
|
||||
{
|
||||
return reg_cnt_addrs[bus_id];
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static inline void i2cWaitBusy(u8 bus_id)
|
||||
{
|
||||
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||
}
|
||||
|
||||
static inline bool i2cGetResult(u8 bus_id)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
|
||||
return (*i2cGetCntReg(bus_id) >> 4) & 1;
|
||||
}
|
||||
|
||||
static void i2cStop(u8 bus_id, u8 arg0)
|
||||
{
|
||||
*i2cGetCntReg(bus_id) = (arg0 << 5) | 0xC0;
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static bool i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = dev_reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC2;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
static bool i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = reg;
|
||||
*i2cGetCntReg(bus_id) = 0xC0;
|
||||
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if(i2cSelectDevice(bus_id, dev_addr) && i2cSelectRegister(bus_id, reg))
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = data;
|
||||
*i2cGetCntReg(bus_id) = 0xC1;
|
||||
i2cStop(bus_id, 0);
|
||||
|
||||
if(i2cGetResult(bus_id)) return true;
|
||||
}
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
i2cWaitBusy(bus_id);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
44
exceptions/arm9/source/i2c.h
Normal file
44
exceptions/arm9/source/i2c.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 everyone who contributed in the development of this file
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define I2C1_REG_OFF 0x10161000
|
||||
#define I2C2_REG_OFF 0x10144000
|
||||
#define I2C3_REG_OFF 0x10148000
|
||||
|
||||
#define I2C_REG_DATA 0
|
||||
#define I2C_REG_CNT 1
|
||||
#define I2C_REG_CNTEX 2
|
||||
#define I2C_REG_SCL 4
|
||||
|
||||
#define I2C_DEV_MCU 3
|
||||
#define I2C_DEV_GYRO 10
|
||||
#define I2C_DEV_IR 13
|
||||
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
118
exceptions/arm9/source/mainHandler.c
Normal file
118
exceptions/arm9/source/mainHandler.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 "i2c.h"
|
||||
#include "handlers.h"
|
||||
|
||||
#define FINAL_BUFFER 0x25000000
|
||||
|
||||
#define REG_DUMP_SIZE 4 * 17
|
||||
#define CODE_DUMP_SIZE 48
|
||||
|
||||
bool cannotAccessAddress(const void *address)
|
||||
{
|
||||
u32 regionSettings[8];
|
||||
u32 addr = (u32)address;
|
||||
|
||||
u32 dataAccessPermissions = readMPUConfig(regionSettings);
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
if((dataAccessPermissions & 0xF) == 0 || (regionSettings[i] & 1) == 0)
|
||||
continue; //No access / region not enabled
|
||||
|
||||
u32 regionAddrBase = regionSettings[i] & ~0xFFF;
|
||||
u32 regionSize = 1 << (((regionSettings[i] >> 1) & 0x1F) + 1);
|
||||
|
||||
if(addr >= regionAddrBase && addr < regionAddrBase + regionSize)
|
||||
return false;
|
||||
|
||||
dataAccessPermissions >>= 4;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) copyMemory(void *dst, const void *src, u32 size, u32 alignment)
|
||||
{
|
||||
u8 *out = (u8 *)dst;
|
||||
const u8 *in = (const u8 *)src;
|
||||
|
||||
if(((u32)src & (alignment - 1)) != 0 || cannotAccessAddress(src) || (size != 0 && cannotAccessAddress((u8 *)src + size - 1)))
|
||||
return 0;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
*out++ = *in++;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) mainHandler(u32 *regs, u32 type)
|
||||
{
|
||||
ExceptionDumpHeader dumpHeader;
|
||||
|
||||
u32 registerDump[REG_DUMP_SIZE / 4];
|
||||
u8 codeDump[CODE_DUMP_SIZE];
|
||||
|
||||
dumpHeader.magic[0] = 0xDEADC0DE;
|
||||
dumpHeader.magic[1] = 0xDEADCAFE;
|
||||
dumpHeader.versionMajor = 1;
|
||||
dumpHeader.versionMinor = 2;
|
||||
|
||||
dumpHeader.processor = 9;
|
||||
dumpHeader.core = 0;
|
||||
dumpHeader.type = type;
|
||||
|
||||
dumpHeader.registerDumpSize = REG_DUMP_SIZE;
|
||||
dumpHeader.codeDumpSize = CODE_DUMP_SIZE;
|
||||
dumpHeader.additionalDataSize = 0;
|
||||
|
||||
//Dump registers
|
||||
//Current order of saved regs: cpsr, pc, r8-r14, r0-r7
|
||||
u32 cpsr = regs[0];
|
||||
u32 pc = regs[1] - (type < 3 ? (((cpsr & 0x20) != 0 && type == 1) ? 2 : 4) : 8);
|
||||
|
||||
registerDump[15] = pc;
|
||||
registerDump[16] = cpsr;
|
||||
for(u32 i = 0; i < 7; i++) registerDump[8 + i] = regs[2 + i];
|
||||
for(u32 i = 0; i < 8; i++) registerDump[i] = regs[9 + i];
|
||||
|
||||
//Dump code
|
||||
u8 *instr = (u8 *)pc + ((cpsr & 0x20) ? 2 : 4) - dumpHeader.codeDumpSize; //Doesn't work well on 32-bit Thumb instructions, but it isn't much of a problem
|
||||
dumpHeader.codeDumpSize = copyMemory(codeDump, instr, dumpHeader.codeDumpSize, ((cpsr & 0x20) != 0) ? 2 : 4);
|
||||
|
||||
//Copy register dump and code dump
|
||||
u8 *final = (u8 *)(FINAL_BUFFER + sizeof(ExceptionDumpHeader));
|
||||
final += copyMemory(final, registerDump, dumpHeader.registerDumpSize, 1);
|
||||
final += copyMemory(final, codeDump, dumpHeader.codeDumpSize, 1);
|
||||
|
||||
//Dump stack in place
|
||||
dumpHeader.stackDumpSize = copyMemory(final, (const void *)registerDump[13], 0x1000 - (registerDump[13] & 0xFFF), 1);
|
||||
|
||||
dumpHeader.totalSize = sizeof(ExceptionDumpHeader) + dumpHeader.registerDumpSize + dumpHeader.codeDumpSize + dumpHeader.stackDumpSize + dumpHeader.additionalDataSize;
|
||||
|
||||
//Copy header (actually optimized by the compiler)
|
||||
*(ExceptionDumpHeader *)FINAL_BUFFER = dumpHeader;
|
||||
|
||||
((void (*)())0xFFFF0830)(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x20, 1 << 2); //Reboot
|
||||
while(true);
|
||||
}
|
||||
31
exceptions/arm9/source/start.s
Normal file
31
exceptions/arm9/source/start.s
Normal file
@@ -0,0 +1,31 @@
|
||||
@ 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.
|
||||
|
||||
.section .text.start
|
||||
.align 4
|
||||
.global _start
|
||||
_start:
|
||||
add pc, r0, #(handlers - .) @ Dummy instruction to prevent compiler optimizations
|
||||
|
||||
handlers:
|
||||
.word FIQHandler
|
||||
.word undefinedInstructionHandler
|
||||
.word prefetchAbortHandler
|
||||
.word dataAbortHandler
|
||||
37
exceptions/arm9/source/types.h
Normal file
37
exceptions/arm9/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;
|
||||
147
exceptions/exception_dump_parser.py
Normal file
147
exceptions/exception_dump_parser.py
Normal file
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python
|
||||
# Requires Python >= 3.2 or >= 2.7
|
||||
|
||||
# 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.
|
||||
|
||||
__author__ = "TuxSH"
|
||||
__copyright__ = "Copyright (c) 2016 TuxSH"
|
||||
__license__ = "GPLv3"
|
||||
__version__ = "v1.0"
|
||||
|
||||
"""
|
||||
Parses Luma3DS exception dumps
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from struct import unpack_from
|
||||
|
||||
# Source of hexdump: https://gist.github.com/ImmortalPC/c340564823f283fe530b
|
||||
# Credits for hexdump go to the original authors
|
||||
# Slightly edited by TuxSH
|
||||
|
||||
def hexdump(addr, src, length=16, sep='.' ):
|
||||
'''
|
||||
@brief Return {src} in hex dump.
|
||||
@param[in] length {Int} Nb Bytes by row.
|
||||
@param[in] sep {Char} For the text part, {sep} will be used for non ASCII char.
|
||||
@return {Str} The hexdump
|
||||
@note Full support for python2 and python3 !
|
||||
'''
|
||||
result = []
|
||||
|
||||
# Python3 support
|
||||
try:
|
||||
xrange(0,1)
|
||||
except NameError:
|
||||
xrange = range
|
||||
|
||||
for i in xrange(0, len(src), length):
|
||||
subSrc = src[i:i+length]
|
||||
hexa = ''
|
||||
isMiddle = False
|
||||
for h in xrange(0,len(subSrc)):
|
||||
if h == length/2:
|
||||
hexa += ' '
|
||||
h = subSrc[h]
|
||||
if not isinstance(h, int):
|
||||
h = ord(h)
|
||||
h = hex(h).replace('0x','')
|
||||
if len(h) == 1:
|
||||
h = '0'+h
|
||||
hexa += h+' '
|
||||
hexa = hexa.strip(' ')
|
||||
text = ''
|
||||
for c in subSrc:
|
||||
if not isinstance(c, int):
|
||||
c = ord(c)
|
||||
if 0x20 <= c < 0x7F:
|
||||
text += chr(c)
|
||||
else:
|
||||
text += sep
|
||||
result.append(('%08X: %-'+str(length*(2+1)+1)+'s |%s|') % (addr + i, hexa, text))
|
||||
|
||||
return '\n'.join(result)
|
||||
|
||||
|
||||
def makeRegisterLine(A, rA, B, rB):
|
||||
return "{0:<15}{1:<20}{2:<15}{3:<20}".format(A, "{0:08x}".format(rA), B, "{0:08x}".format(rB))
|
||||
|
||||
handledExceptionNames = ("FIQ", "undefined instruction", "prefetch abort", "data abort")
|
||||
registerNames = tuple("r{0}".format(i) for i in range(13)) + ("sp", "lr", "pc", "cpsr") + ("dfsr", "ifsr", "far") + ("fpexc", "fpinst", "fpinst2")
|
||||
svcBreakReasons = ("(svcBreak: panic)", "(svcBreak: assertion failed)", "(svcBreak: user-related)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description="Parse Luma3DS exception dumps")
|
||||
parser.add_argument("filename")
|
||||
args = parser.parse_args()
|
||||
data = b""
|
||||
with open(args.filename, "rb") as f: data = f.read()
|
||||
if unpack_from("<2I", data) != (0xdeadc0de, 0xdeadcafe):
|
||||
raise SystemExit("Invalid file format")
|
||||
|
||||
version, processor, exceptionType, _, nbRegisters, codeDumpSize, stackDumpSize, additionalDataSize = unpack_from("<8I", data, 8)
|
||||
nbRegisters //= 4
|
||||
|
||||
if version < (1 << 16) | 2:
|
||||
raise SystemExit("Incompatible format version, please use the appropriate parser.")
|
||||
|
||||
registers = unpack_from("<{0}I".format(nbRegisters), data, 40)
|
||||
codeDump = data[40 + 4 * nbRegisters : 40 + 4 * nbRegisters + codeDumpSize]
|
||||
stackOffset = 40 + 4 * nbRegisters + codeDumpSize
|
||||
stackDump = data[stackOffset : stackOffset + stackDumpSize]
|
||||
addtionalDataOffset = stackOffset + stackDumpSize
|
||||
additionalData = data[addtionalDataOffset : addtionalDataOffset + additionalDataSize]
|
||||
|
||||
if processor == 9: print("Processor: ARM9")
|
||||
else: print("Processor: ARM11 (core {0})".format(processor >> 16))
|
||||
|
||||
typeDetailsStr = ""
|
||||
if exceptionType == 2:
|
||||
if (registers[16] & 0x20) == 0 and codeDumpSize >= 4:
|
||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
||||
if instr == 0xe12fff7e:
|
||||
typeDetailsStr = " (kernel panic)"
|
||||
elif instr == 0xef00003c:
|
||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
||||
elif (registers[16] & 0x20) == 1 and codeDumpSize >= 2:
|
||||
instr = unpack_from("<I", codeDump[-4:])[0]
|
||||
if instr == 0xdf3c:
|
||||
typeDetailsStr = " " + (svcBreakReasons[registers[0]] if registers[0] < 3 else "(svcBreak)")
|
||||
|
||||
elif processor != 9 and (registers[20] & 0x80000000) != 0:
|
||||
typeDetailsStr = " (VFP exception)"
|
||||
|
||||
print("Exception type: {0}{1}".format("unknown" if exceptionType >= len(handledExceptionNames) else handledExceptionNames[exceptionType], typeDetailsStr))
|
||||
if additionalDataSize != 0:
|
||||
print("Current process: {0} ({1:016x})".format(additionalData[:8].decode("ascii"), unpack_from("<Q", additionalData, 8)[0]))
|
||||
|
||||
print("\nRegister dump:\n")
|
||||
for i in range(0, nbRegisters - (nbRegisters % 2), 2):
|
||||
if i == 16: print("")
|
||||
print(makeRegisterLine(registerNames[i], registers[i], registerNames[i+1], registers[i+1]))
|
||||
if nbRegisters % 2 == 1: print("{0:<15}{1:<20}".format(registerNames[nbRegisters - 1], "{0:08x}".format(registers[nbRegisters - 1])))
|
||||
|
||||
print("\nCode dump:\n")
|
||||
print(hexdump(registers[15] - codeDumpSize + (4 if (registers[16] & 0x20 == 0) else 2), codeDump))
|
||||
|
||||
print("\nStack dump:\n")
|
||||
print(hexdump(registers[13], stackDump))
|
||||
|
||||
@@ -22,13 +22,17 @@ LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
|
||||
|
||||
INCLUDE := $(foreach dir,$(LIBDIRS),-I$(dir)/include)
|
||||
|
||||
ARCH := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ARCH) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
|
||||
ASFLAGS := -mcpu=mpcore -mfloat-abi=hard -mtp=soft
|
||||
CFLAGS := -Wall -Wextra -MMD -MP -marm $(ASFLAGS) -fno-builtin -std=c11 -O2 -flto -ffast-math -mword-relocations \
|
||||
-ffunction-sections -fdata-sections $(INCLUDE) -DARM11 -D_3DS
|
||||
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ARCH)
|
||||
LDFLAGS := -Xlinker --defsym="__start__=0x14000000" -specs=3dsx.specs $(ASFLAGS)
|
||||
|
||||
objects = $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \
|
||||
$(call rwildcard, $(dir_source), *.c))
|
||||
$(call rwildcard, $(dir_source), *.s *.c))
|
||||
|
||||
ifeq ($(strip $(DEV)),TRUE)
|
||||
CFLAGS += -DDEV
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: ../$(dir_build)/$(name).cxi
|
||||
@@ -43,9 +47,13 @@ clean:
|
||||
$(dir_build)/$(name).elf: $(objects)
|
||||
$(LINK.o) $(OUTPUT_OPTION) $^ $(LIBPATHS) $(LIBS)
|
||||
|
||||
$(dir_build)/memory.o : CFLAGS += -O3
|
||||
$(dir_build)/memory.o $(dir_build)/strings.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)
|
||||
|
||||
19
injector/source/CFWInfo.h
Normal file
19
injector/source/CFWInfo.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
|
||||
u8 versionMajor;
|
||||
u8 versionMinor;
|
||||
u8 versionBuild;
|
||||
u8 flags; /* bit 0: dev branch; bit 1: is release */
|
||||
|
||||
u32 commitHash;
|
||||
|
||||
u32 config;
|
||||
} CFWInfo;
|
||||
|
||||
u32 svcGetCFWInfo(CFWInfo *info);
|
||||
9
injector/source/CFWInfo.s
Normal file
9
injector/source/CFWInfo.s
Normal file
@@ -0,0 +1,9 @@
|
||||
.text
|
||||
.arm
|
||||
.align 4
|
||||
|
||||
.global svcGetCFWInfo
|
||||
.type svcGetCFWInfo, %function
|
||||
svcGetCFWInfo:
|
||||
svc 0x2e
|
||||
bx lr
|
||||
@@ -8,3 +8,42 @@ void memcpy(void *dest, const void *src, u32 size)
|
||||
for(u32 i = 0; i < size; i++)
|
||||
destc[i] = srcc[i];
|
||||
}
|
||||
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1;
|
||||
const u8 *buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if(cmp) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Boyer-Moore Horspool algorithm, adapted from http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
u32 table[256];
|
||||
|
||||
//Preprocessing
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
table[i] = patternSize;
|
||||
for(u32 i = 0; i < patternSize - 1; i++)
|
||||
table[patternc[i]] = patternSize - i - 1;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
u8 c = startPos[j + patternSize - 1];
|
||||
if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0)
|
||||
return startPos + j;
|
||||
j += table[c];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -3,3 +3,5 @@
|
||||
#include <3ds/types.h>
|
||||
|
||||
void memcpy(void *dest, const void *src, u32 size);
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size);
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize);
|
||||
@@ -1,55 +1,15 @@
|
||||
#include <3ds.h>
|
||||
#include "memory.h"
|
||||
#include "patcher.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "ifile.h"
|
||||
#include "CFWInfo.h"
|
||||
|
||||
static CFWInfo info = {0};
|
||||
static CFWInfo info;
|
||||
|
||||
static int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
static void patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1;
|
||||
const u8 *buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if(cmp) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Quick Search algorithm, adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
static u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
u32 table[256];
|
||||
|
||||
for(u32 i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(u32 i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, int offset, const void *replace, u32 repSize, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for(i = 0; i < count; i++)
|
||||
for(u32 i = 0; i < count; i++)
|
||||
{
|
||||
u8 *found = memsearch(start, pattern, size, patSize);
|
||||
|
||||
@@ -64,32 +24,16 @@ static u32 patchMemory(u8 *start, u32 size, const void *pattern, u32 patSize, in
|
||||
size -= at + patSize;
|
||||
start = found + patSize;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static inline size_t strnlen(const char *string, size_t maxlen)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
for(size = 0; *string && size < maxlen; string++, size++);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int fileOpen(IFile *file, FS_ArchiveID archiveId, const char *path, int flags)
|
||||
{
|
||||
FS_Path filePath = {PATH_ASCII, strnlen(path, PATH_MAX) + 1, path},
|
||||
FS_Path filePath = {PATH_ASCII, strnlen(path, 255) + 1, path},
|
||||
archivePath = {PATH_EMPTY, 1, (u8 *)""};
|
||||
|
||||
return IFile_Open(file, archiveId, archivePath, filePath, flags);
|
||||
}
|
||||
|
||||
int __attribute__((naked)) svcGetCFWInfo(CFWInfo __attribute__((unused)) *out)
|
||||
{
|
||||
__asm__ volatile("svc 0x2E; bx lr");
|
||||
}
|
||||
|
||||
static void loadCFWInfo(void)
|
||||
{
|
||||
static bool infoLoaded = false;
|
||||
@@ -97,11 +41,10 @@ static void loadCFWInfo(void)
|
||||
if(!infoLoaded)
|
||||
{
|
||||
svcGetCFWInfo(&info);
|
||||
|
||||
IFile file;
|
||||
if(BOOTCONFIG(5, 1) && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
|
||||
{
|
||||
if(BOOTCFG_SAFEMODE != 0 && R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, "/", FS_OPEN_READ))) //Init SD card if SAFE_MODE is being booted
|
||||
IFile_Close(&file);
|
||||
}
|
||||
|
||||
infoLoaded = true;
|
||||
}
|
||||
@@ -124,13 +67,53 @@ static bool secureInfoExists(void)
|
||||
return exists;
|
||||
}
|
||||
|
||||
static void progIdToStr(char *strEnd, u64 progId)
|
||||
static void loadCustomVerString(u16 *out, u32 *verStringSize)
|
||||
{
|
||||
while(progId)
|
||||
static const char path[] = "/luma/customversion.txt";
|
||||
|
||||
IFile file;
|
||||
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
*strEnd-- = hexDigits[(u32)(progId & 0xF)];
|
||||
progId >>= 4;
|
||||
u64 fileSize;
|
||||
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= 60)
|
||||
{
|
||||
u8 buf[fileSize];
|
||||
u64 total;
|
||||
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, fileSize)))
|
||||
{
|
||||
static const u8 bom[] = {0xEF, 0xBB, 0xBF};
|
||||
u32 finalSize = 0;
|
||||
|
||||
//Convert from UTF-8 to UTF-16 (Nintendo doesn't support 4-byte UTF-16, so 4-byte UTF-8 is unsupported)
|
||||
for(u32 increase, fileSizeTmp = (u32)fileSize, i = (fileSizeTmp > 2 && memcmp(buf, bom, 3) == 0) ? 3 : 0;
|
||||
i < fileSizeTmp && finalSize < 19; i += increase, finalSize++)
|
||||
{
|
||||
if((buf[i] & 0x80) == 0)
|
||||
{
|
||||
increase = 1;
|
||||
out[finalSize] = (u16)buf[i];
|
||||
}
|
||||
else if((buf[i] & 0xE0) == 0xC0 && i + 1 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80)
|
||||
{
|
||||
increase = 2;
|
||||
out[finalSize] = (u16)(((buf[i] & 0x1F) << 6) | (buf[i + 1] & 0x3F));
|
||||
}
|
||||
else if((buf[i] & 0xF0) == 0xE0 && i + 2 < fileSizeTmp && (buf[i + 1] & 0xC0) == 0x80 && (buf[i + 2] & 0xC0) == 0x80)
|
||||
{
|
||||
increase = 3;
|
||||
out[finalSize] = (u16)(((buf[i] & 0xF) << 12) | ((buf[i + 1] & 0x3F) << 6) | (buf[i + 2] & 0x3F));
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
if(finalSize > 0) *verStringSize = finalSize * 2;
|
||||
}
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,23 +126,22 @@ static void loadTitleCodeSection(u64 progId, u8 *code, u32 size)
|
||||
progIdToStr(path + 35, progId);
|
||||
|
||||
IFile file;
|
||||
Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
|
||||
if(R_SUCCEEDED(ret))
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
|
||||
{
|
||||
u64 fileSize, total;
|
||||
u64 fileSize;
|
||||
|
||||
ret = IFile_GetSize(&file, &fileSize);
|
||||
|
||||
if(R_SUCCEEDED(ret) && fileSize <= size)
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize <= size)
|
||||
{
|
||||
ret = IFile_Read(&file, &total, code, fileSize);
|
||||
IFile_Close(&file);
|
||||
u64 total;
|
||||
IFile_Read(&file, &total, code, fileSize);
|
||||
}
|
||||
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
static void loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
{
|
||||
/* Here we look for "/luma/locales/[u64 titleID in hex, uppercase].txt"
|
||||
If it exists it should contain, for example, "EUR IT" */
|
||||
@@ -168,18 +150,19 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
progIdToStr(path + 29, progId);
|
||||
|
||||
IFile file;
|
||||
Result ret = fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ);
|
||||
if(R_SUCCEEDED(ret))
|
||||
|
||||
if(R_SUCCEEDED(fileOpen(&file, ARCHIVE_SDMC, path, FS_OPEN_READ)))
|
||||
{
|
||||
u64 fileSize;
|
||||
|
||||
if(R_SUCCEEDED(IFile_GetSize(&file, &fileSize)) && fileSize == 6)
|
||||
{
|
||||
char buf[6];
|
||||
u64 total;
|
||||
|
||||
ret = IFile_Read(&file, &total, buf, 6);
|
||||
IFile_Close(&file);
|
||||
|
||||
if(!R_SUCCEEDED(ret) || total < 6) return -1;
|
||||
|
||||
for(u32 i = 0; i < 7; ++i)
|
||||
if(R_SUCCEEDED(IFile_Read(&file, &total, buf, 6)))
|
||||
{
|
||||
for(u32 i = 0; i < 7; i++)
|
||||
{
|
||||
static const char *regions[] = {"JPN", "USA", "EUR", "AUS", "CHN", "KOR", "TWN"};
|
||||
|
||||
@@ -190,7 +173,7 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
}
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < 12; ++i)
|
||||
for(u32 i = 0; i < 12; i++)
|
||||
{
|
||||
static const char *languages[] = {"JP", "EN", "FR", "DE", "IT", "ES", "ZH", "KO", "NL", "PT", "RU", "TW"};
|
||||
|
||||
@@ -201,8 +184,10 @@ static int loadTitleLocaleConfig(u64 progId, u8 *regionId, u8 *languageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
IFile_Close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 *getCfgOffsets(u8 *code, u32 size, u32 *CFGUHandleOffset)
|
||||
@@ -330,7 +315,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x000400300000B102LL: // TWN Menu
|
||||
{
|
||||
static const u8 regionFreePattern[] = {
|
||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0, 0xE3
|
||||
0x00, 0x00, 0x55, 0xE3, 0x01, 0x10, 0xA0
|
||||
};
|
||||
static const u8 regionFreePatch[] = {
|
||||
0x01, 0x00, 0xA0, 0xE3, 0x1E, 0xFF, 0x2F, 0xE1
|
||||
@@ -364,8 +349,8 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
sizeof(blockAutoUpdatesPatch), 1
|
||||
);
|
||||
|
||||
//Apply only if the updated NAND hasn't been booted
|
||||
if((BOOTCONFIG(0, 3) != 0) == (BOOTCONFIG(2, 1) && CONFIG(1)))
|
||||
//Apply only if the user booted with R
|
||||
if((BOOTCFG_NAND != 0) != (BOOTCFG_FIRM != 0))
|
||||
{
|
||||
static const u8 skipEshopUpdateCheckPattern[] = {
|
||||
0x30, 0xB5, 0xF1, 0xB0
|
||||
@@ -389,14 +374,14 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004013000003202LL: // FRIENDS
|
||||
{
|
||||
static const u8 fpdVerPattern[] = {
|
||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01, 0x01
|
||||
0xE0, 0x1E, 0xFF, 0x2F, 0xE1, 0x01, 0x01
|
||||
};
|
||||
|
||||
static const u8 mostRecentFpdVer = 0x06;
|
||||
u8 mostRecentFpdVer = 7;
|
||||
|
||||
u8 *fpdVer = memsearch(code, fpdVerPattern, size, sizeof(fpdVerPattern));
|
||||
|
||||
//Allow online access to work with old friends modules, without breaking newer firmwares
|
||||
//Allow online access to work with old friends modules
|
||||
if(fpdVer != NULL && fpdVer[9] < mostRecentFpdVer) fpdVer[9] = mostRecentFpdVer;
|
||||
|
||||
break;
|
||||
@@ -409,19 +394,69 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004001000027000LL: // KOR MSET
|
||||
case 0x0004001000028000LL: // TWN MSET
|
||||
{
|
||||
if(CONFIG(4))
|
||||
if(CONFIG(PATCHVERSTRING))
|
||||
{
|
||||
static const u16 verPattern[] = u"Ver.";
|
||||
const u32 currentNand = BOOTCONFIG(0, 3);
|
||||
const u32 matchingFirm = BOOTCONFIG(2, 1) == (currentNand != 0);
|
||||
static u16 *verString;
|
||||
u32 verStringSize = 0;
|
||||
|
||||
u16 customVerString[19];
|
||||
loadCustomVerString(customVerString, &verStringSize);
|
||||
|
||||
if(verStringSize != 0) verString = customVerString;
|
||||
else
|
||||
{
|
||||
verStringSize = 8;
|
||||
u32 currentNand = BOOTCFG_NAND,
|
||||
currentFirm = BOOTCFG_FIRM;
|
||||
bool matchingFirm = (currentFirm != 0) == (currentNand != 0);
|
||||
|
||||
static u16 verStringEmu[] = u"Emu ",
|
||||
verStringEmuSys[] = u"Em S",
|
||||
verStringSysEmu[] = u"SyE ";
|
||||
|
||||
switch(currentNand)
|
||||
{
|
||||
case 1:
|
||||
verString = matchingFirm ? u" Emu" : u"EmuS";
|
||||
break;
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
{
|
||||
if(matchingFirm)
|
||||
{
|
||||
verStringEmu[3] = '0' + currentNand;
|
||||
verString = verStringEmu;
|
||||
}
|
||||
else
|
||||
{
|
||||
verStringEmuSys[2] = '0' + currentNand;
|
||||
verString = verStringEmuSys;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(matchingFirm) verString = u" Sys";
|
||||
else
|
||||
{
|
||||
if(currentFirm == 1) verString = u"SysE";
|
||||
else
|
||||
{
|
||||
verStringSysEmu[3] = '0' + currentFirm;
|
||||
verString = verStringSysEmu;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Patch Ver. string
|
||||
patchMemory(code, size,
|
||||
verPattern,
|
||||
sizeof(verPattern) - sizeof(u16), 0,
|
||||
!currentNand ? ((matchingFirm) ? u" Sys" : u"SysE") :
|
||||
((currentNand == 1) ? (matchingFirm ? u" Emu" : u"EmuS") : ((matchingFirm) ? u"Emu2" : u"Em2S")),
|
||||
sizeof(verPattern) - sizeof(u16), 1
|
||||
sizeof(verPattern) - 2, 0,
|
||||
verString,
|
||||
verStringSize, 1
|
||||
);
|
||||
}
|
||||
|
||||
@@ -445,12 +480,12 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
sizeof(stopCartUpdatesPatch), 2
|
||||
);
|
||||
|
||||
u32 cpuSetting = MULTICONFIG(1);
|
||||
u32 cpuSetting = MULTICONFIG(NEWCPU);
|
||||
|
||||
if(cpuSetting)
|
||||
if(cpuSetting != 0)
|
||||
{
|
||||
static const u8 cfgN3dsCpuPattern[] = {
|
||||
0x00, 0x40, 0xA0, 0xE1, 0x07, 0x00
|
||||
0x00, 0x40, 0xA0, 0xE1, 0x07
|
||||
};
|
||||
|
||||
u32 *cfgN3dsCpuLoc = (u32 *)memsearch(code, cfgN3dsCpuPattern, size, sizeof(cfgN3dsCpuPattern));
|
||||
@@ -469,7 +504,7 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
case 0x0004013000001702LL: // CFG
|
||||
{
|
||||
static const u8 secureinfoSigCheckPattern[] = {
|
||||
0x06, 0x46, 0x10, 0x48, 0xFC
|
||||
0x06, 0x46, 0x10, 0x48
|
||||
};
|
||||
static const u8 secureinfoSigCheckPatch[] = {
|
||||
0x00, 0x26
|
||||
@@ -491,10 +526,10 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
//Use SecureInfo_C
|
||||
patchMemory(code, size,
|
||||
secureinfoFilenamePattern,
|
||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||
sizeof(secureinfoFilenamePattern) - sizeof(u16),
|
||||
sizeof(secureinfoFilenamePattern) - 2,
|
||||
sizeof(secureinfoFilenamePattern) - 2,
|
||||
secureinfoFilenamePatch,
|
||||
sizeof(secureinfoFilenamePatch) - sizeof(u16), 2
|
||||
sizeof(secureinfoFilenamePatch) - 2, 2
|
||||
);
|
||||
}
|
||||
|
||||
@@ -543,12 +578,46 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if(CONFIG(3))
|
||||
#ifdef DEV
|
||||
case 0x0004003000008A02LL: // ErrDisp
|
||||
{
|
||||
u32 tidHigh = (progId & 0xFFFFFFF000000000LL) >> 0x24;
|
||||
if(MULTICONFIG(DEVOPTIONS) == 0)
|
||||
{
|
||||
static const u8 unitinfoCheckPattern1[] = {
|
||||
0x14, 0x00, 0xD0, 0xE5, 0xDB
|
||||
};
|
||||
|
||||
if(tidHigh == 0x0004000)
|
||||
static const u8 unitinfoCheckPattern2[] = {
|
||||
0x14, 0x00, 0xD0, 0xE5, 0x01
|
||||
} ;
|
||||
|
||||
static const u8 unitinfoCheckPatch[] = {
|
||||
0x00, 0x00, 0xA0, 0xE3
|
||||
} ;
|
||||
|
||||
patchMemory(code, size,
|
||||
unitinfoCheckPattern1,
|
||||
sizeof(unitinfoCheckPattern1), 0,
|
||||
unitinfoCheckPatch,
|
||||
sizeof(unitinfoCheckPatch), 1
|
||||
);
|
||||
|
||||
patchMemory(code, size,
|
||||
unitinfoCheckPattern2,
|
||||
sizeof(unitinfoCheckPattern2), 0,
|
||||
unitinfoCheckPatch,
|
||||
sizeof(unitinfoCheckPatch), 3
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
default:
|
||||
if(CONFIG(USELANGEMUANDCODE))
|
||||
{
|
||||
if((u32)((progId & 0xFFFFFFF000000000LL) >> 0x24) == 0x0004000)
|
||||
{
|
||||
//External .code section loading
|
||||
loadTitleCodeSection(progId, code, size);
|
||||
@@ -556,11 +625,11 @@ void patchCode(u64 progId, u8 *code, u32 size)
|
||||
//Language emulation
|
||||
u8 regionId = 0xFF,
|
||||
languageId = 0xFF;
|
||||
loadTitleLocaleConfig(progId, ®ionId, &languageId);
|
||||
|
||||
if(R_SUCCEEDED(loadTitleLocaleConfig(progId, ®ionId, &languageId)))
|
||||
if(regionId != 0xFF || regionId != 0xFF)
|
||||
{
|
||||
u32 CFGUHandleOffset;
|
||||
|
||||
u8 *CFGU_GetConfigInfoBlk2_endPos = getCfgOffsets(code, size, &CFGUHandleOffset);
|
||||
|
||||
if(CFGU_GetConfigInfoBlk2_endPos != NULL)
|
||||
|
||||
@@ -2,24 +2,38 @@
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
#define PATH_MAX 255
|
||||
|
||||
#define CONFIG(a) (((info.config >> (a + 16)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((info.config >> (a * 2 + 6)) & 3)
|
||||
#define CONFIG(a) (((info.config >> (a + 21)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((info.config >> (a * 2 + 9)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((info.config >> a) & b)
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_A9LH BOOTCONFIG(6, 1)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
|
||||
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
char magic[4];
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
PIN,
|
||||
NEWCPU
|
||||
#ifdef DEV
|
||||
, DEVOPTIONS
|
||||
#endif
|
||||
};
|
||||
|
||||
u8 versionMajor;
|
||||
u8 versionMinor;
|
||||
u8 versionBuild;
|
||||
u8 flags; /* bit 0: dev branch; bit 1: is release */
|
||||
|
||||
u32 commitHash;
|
||||
|
||||
u32 config;
|
||||
} CFWInfo;
|
||||
enum singleOptions
|
||||
{
|
||||
AUTOBOOTSYS = 0,
|
||||
USESYSFIRM,
|
||||
USELANGEMUANDCODE,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PAYLOADSPLASH
|
||||
#ifdef DEV
|
||||
, PATCHACCESS
|
||||
#endif
|
||||
};
|
||||
|
||||
void patchCode(u64 progId, u8 *code, u32 size);
|
||||
20
injector/source/strings.c
Normal file
20
injector/source/strings.c
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "strings.h"
|
||||
|
||||
size_t strnlen(const char *string, size_t maxlen)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
for(size = 0; *string && size < maxlen; string++, size++);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
void progIdToStr(char *strEnd, u64 progId)
|
||||
{
|
||||
while(progId)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
*strEnd-- = hexDigits[(u32)(progId & 0xF)];
|
||||
progId >>= 4;
|
||||
}
|
||||
}
|
||||
6
injector/source/strings.h
Normal file
6
injector/source/strings.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <3ds/types.h>
|
||||
|
||||
size_t strnlen(const char *string, size_t maxlen);
|
||||
void progIdToStr(char *strEnd, u64 progId);
|
||||
@@ -23,14 +23,15 @@
|
||||
#include "memory.h"
|
||||
#include "cache.h"
|
||||
|
||||
extern u32 payloadSize; //defined in start.s
|
||||
extern u32 payloadSize; //Defined in start.s
|
||||
|
||||
void main(void)
|
||||
{
|
||||
void *payloadAddress = (void *)0x23F00000;
|
||||
|
||||
memcpy(payloadAddress, (void*)0x24F00000, payloadSize);
|
||||
memcpy(payloadAddress, (void *)0x24F00000, payloadSize);
|
||||
|
||||
//Ensure that all memory transfers have completed and that the caches have been flushed
|
||||
flushCaches();
|
||||
|
||||
((void (*)())payloadAddress)();
|
||||
|
||||
11
menuhax/menuhax.diff
Normal file
11
menuhax/menuhax.diff
Normal file
@@ -0,0 +1,11 @@
|
||||
diff -uNr a/source/main.c b/source/main.c
|
||||
--- a/source/main.c 2016-09-11 01:04:25.665231884 +0200
|
||||
+++ b/source/main.c 2016-09-14 12:36:28.601439550 +0200
|
||||
@@ -9,6 +9,7 @@
|
||||
#endif
|
||||
|
||||
int main (void) {
|
||||
+ svcSleepThread(2500 * 1000000ULL);
|
||||
if (brahma_init()) {
|
||||
if (load_arm9_payload_offset("/" LAUNCHER_PATH, 0x12000, 0x10000) != 1)
|
||||
goto error;
|
||||
119
patches/k11modules.s
Normal file
119
patches/k11modules.s
Normal file
@@ -0,0 +1,119 @@
|
||||
;
|
||||
; 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.
|
||||
;
|
||||
|
||||
; This is mainly Subv's code, big thanks to him.
|
||||
|
||||
.arm.little
|
||||
|
||||
.create "build/k11modules.bin", 0
|
||||
.arm
|
||||
; This code searches the sm module for a specific byte pattern and patches some of the instructions
|
||||
; in the code to disable service access checks when calling srv:GetServiceHandle
|
||||
|
||||
; It also searches the fs module for archive access check code
|
||||
|
||||
; Save the registers we'll be using
|
||||
; Register contents:
|
||||
; r4: Pointer to a pointer to the exheader of the current NCCH
|
||||
; r6: Constant 0
|
||||
; SP + 0x80 - 0x7C: Pointer to the memory location where the NCCH text was loaded
|
||||
|
||||
; Save the value of sp
|
||||
mov r0, sp
|
||||
; Save the value of all registers
|
||||
push {r0-r12}
|
||||
|
||||
ldr r0, [r0, #(0x80 - 0x7C)] ; Load the .text address
|
||||
ldr r7, [r4]
|
||||
ldr r2, [r7, #0x18] ; Load the size of the .text
|
||||
ldr r8, [r7, #0x200] ; Load the low title id of the current NCCH
|
||||
mov r5, r0
|
||||
add r11, r5, r2 ; Max bounds of the memory region
|
||||
|
||||
ldr r9, =0x00001002 ; Low title id of the sm module
|
||||
cmp r8, r9 ; Compare the low title id to the id of the sm module
|
||||
bne fs_patch ; Skip if they're not the same
|
||||
|
||||
ldr r7, =0xE1A01006 ; mov r1, r6
|
||||
ldr r8, =0xE1A00005 ; mov r0, r5
|
||||
ldr r9, =0xE3500000 ; cmp r0, #0
|
||||
ldr r10, =0xE2850004 ; add r0, r5, #4
|
||||
|
||||
loop:
|
||||
cmp r11, r5
|
||||
blo out ; Check if we didn't go past the bounds of the memory region
|
||||
ldr r6, [r5]
|
||||
cmp r6, r7
|
||||
ldreq r6, [r5, #4]
|
||||
cmpeq r6, r8
|
||||
ldreq r6, [r5, #12]
|
||||
cmpeq r6, r9
|
||||
ldreq r6, [r5, #24]
|
||||
cmpeq r6, r10
|
||||
moveq r8, r5
|
||||
addne r5, r5, #4
|
||||
bne loop
|
||||
|
||||
; r8 now contains the start address of the pattern we found
|
||||
|
||||
; Write NOPs to the four instructions we want to patch
|
||||
ldr r9, =0xE320F000 ; nop
|
||||
str r9, [r8, #8] ; Patch the bl
|
||||
str r9, [r8, #12] ; Patch the cmp
|
||||
str r9, [r8, #16] ; Patch the ldreq
|
||||
str r9, [r8, #20] ; Patch the beq
|
||||
b out
|
||||
|
||||
fs_patch: ; patch adapted from BootNTR
|
||||
ldr r9, =0x00001102 ; Low title id of the fs module
|
||||
cmp r8, r9 ; Compare the low title id to the id of the sm module
|
||||
bne out ; Skip if they're not the same
|
||||
|
||||
ldr r7, =0x4618 ; mov r0, r3
|
||||
ldr r8, =0x3481 ; add r4, #0x81
|
||||
|
||||
loop_fs:
|
||||
cmp r11, r5
|
||||
blo out
|
||||
ldrh r6, [r5]
|
||||
cmp r6, r7
|
||||
ldreqh r6, [r5, #2]
|
||||
cmpeq r6, r8
|
||||
subeq r8, r5, #8
|
||||
addne r5, #2
|
||||
bne loop_fs
|
||||
|
||||
; r8 now contains the start address of the pattern we found
|
||||
ldr r9, =0x2001 ; mov r0, #1
|
||||
ldr r10, =0x4770 ; bx lr
|
||||
strh r9, [r8]
|
||||
strh r10, [r8, #2]
|
||||
|
||||
out:
|
||||
pop {r0-r12} ; Restore the registers we used
|
||||
|
||||
ldr r0, [r4] ; Execute the instruction we overwrote in our detour
|
||||
|
||||
bx lr ; Jump back to whoever called us
|
||||
|
||||
.pool
|
||||
.close
|
||||
@@ -5,10 +5,14 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
|
||||
.create "build/reboot.bin", 0
|
||||
.arm
|
||||
; Interesting registers and locations to keep in mind, set before this code is ran:
|
||||
; - sp + 0x3A8 - 0x70: FIRM path in exefs.
|
||||
; - r7 (which is sp + 0x3A8 - 0x198): Reserved space for file handle
|
||||
; - *(sp + 0x3A8 - 0x198) + 0x28: fread function.
|
||||
; Interesting registers and locations to keep in mind, set just before this code is ran:
|
||||
; - r1: FIRM path in exefs.
|
||||
; - r7: pointer to file object
|
||||
; - *r7: vtable
|
||||
; - *(vtable + 0x28): fread function
|
||||
; - *(r7 + 8): file handle
|
||||
|
||||
mov r8, r1
|
||||
|
||||
pxi_wait_recv:
|
||||
ldr r2, =0x44846
|
||||
@@ -47,7 +51,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
cmp r4, #0
|
||||
movne r3, #0x12000 ; Skip the first 0x12000 bytes.
|
||||
moveq r3, payload_maxsize
|
||||
ldr r6, [sp, #0x3A8-0x198]
|
||||
ldr r6, [r7]
|
||||
ldr r6, [r6, #0x28]
|
||||
blx r6
|
||||
cmp r4, #0
|
||||
@@ -55,8 +59,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
bne read_payload ; Go read the real payload.
|
||||
|
||||
; Copy the low TID (in UTF-16) of the wanted firm to the 5th byte of the payload
|
||||
add r0, sp, #0x3A8 - 0x70
|
||||
add r0, 0x1A
|
||||
add r0, r8, 0x1A
|
||||
add r1, r0, #0x10
|
||||
ldr r2, =payload_addr + 4
|
||||
copy_TID_low:
|
||||
@@ -75,7 +78,7 @@ payload_maxsize equ 0x10000 ; Maximum size for the payload (maximum that CakeB
|
||||
goto_reboot:
|
||||
; Jump to reboot code
|
||||
ldr r0, =(kernelcode_start - goto_reboot - 12)
|
||||
add r0, pc
|
||||
add r0, pc ; pc is two instructions ahead of the instruction being executed (12 = 2*4 + 4)
|
||||
swi 0x7B
|
||||
|
||||
die:
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
.arm.little
|
||||
|
||||
.create "build/svcGetCFWInfo.bin", 0
|
||||
|
||||
.arm
|
||||
|
||||
adr r1, infoStart
|
||||
@@ -36,6 +35,7 @@
|
||||
blo loop
|
||||
|
||||
mov r0, #0
|
||||
|
||||
bx lr
|
||||
|
||||
.pool
|
||||
|
||||
@@ -1,144 +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.
|
||||
;
|
||||
|
||||
.arm.little
|
||||
|
||||
.create "build/twl_k11modules.bin", 0
|
||||
|
||||
.align 4
|
||||
.arm
|
||||
|
||||
patch:
|
||||
; r4: Pointer to a pointer to the exheader of the current NCCH
|
||||
; sp + 0xb0 - 0xa4: Pointer to the memory location where the NCCH text was loaded
|
||||
|
||||
add r3, sp, #(0xb0 - 0xa4)
|
||||
add r1, sp, #(0xb0 - 0xac)
|
||||
|
||||
push {r0-r11, lr}
|
||||
|
||||
ldr r9, [r3] ; load the address of the code section
|
||||
ldr r8, [r4] ; load the address of the exheader
|
||||
|
||||
ldr r7, [r8, #0x200] ; low titleID
|
||||
ldr r6, =#0x000001ff
|
||||
cmp r7, r6
|
||||
bne end
|
||||
|
||||
ldr r7, =#0xabcdabcd ; offset of the dev launcher (will be replaced later)
|
||||
add r7, r9
|
||||
|
||||
adr r5, patchesStart
|
||||
add r6, r5, #(patchesEnd - patchesStart)
|
||||
|
||||
patchLoop:
|
||||
ldrh r0, [r5, #4]
|
||||
cmp r0, #0
|
||||
moveq r4, r9
|
||||
movne r4, r7
|
||||
|
||||
ldrh r2, [r5, #6]
|
||||
add r1, r5, #8
|
||||
ldr r0, [r5]
|
||||
add r0, r4
|
||||
blx memcmp
|
||||
cmp r0, #0
|
||||
bne skipPatch
|
||||
|
||||
ldrh r2, [r5, #6]
|
||||
add r1, r5, #0x08
|
||||
add r1, r2
|
||||
ldr r0, [r5]
|
||||
add r0, r4
|
||||
blx memcpy
|
||||
|
||||
skipPatch:
|
||||
|
||||
ldrh r0, [r5, #6]
|
||||
add r5, r5, #0x08
|
||||
add r5, r0,lsl#1
|
||||
cmp r5, r6
|
||||
blo patchLoop
|
||||
|
||||
end:
|
||||
|
||||
pop {r0-r11, pc}
|
||||
|
||||
.align 2
|
||||
.thumb
|
||||
|
||||
memcmp:
|
||||
push {r4-r7, lr}
|
||||
mov r4, #0
|
||||
cmp_loop:
|
||||
cmp r4, r2
|
||||
bhs cmp_loop_end
|
||||
ldrb r6, [r0, r4]
|
||||
ldrb r7, [r1, r4]
|
||||
add r4, #1
|
||||
sub r6, r7
|
||||
cmp r6, #0
|
||||
beq cmp_loop
|
||||
|
||||
cmp_loop_end:
|
||||
mov r0, r6
|
||||
pop {r4-r7, pc}
|
||||
|
||||
memcpy:
|
||||
push {r4-r5, lr}
|
||||
mov r4, #0
|
||||
|
||||
copy_loop:
|
||||
cmp r4, r2
|
||||
bhs copy_loop_end
|
||||
ldrb r5, [r1, r4]
|
||||
strb r5, [r0, r4]
|
||||
add r4, #1
|
||||
b copy_loop
|
||||
|
||||
copy_loop_end:
|
||||
pop {r4-r5, pc}
|
||||
|
||||
.align 4
|
||||
|
||||
; Available space for patches: 152 bytes on N3DS, 666 on O3DS
|
||||
|
||||
patchesStart:
|
||||
; SCFG_EXT bit31 patches, based on https://github.com/ahezard/twl_firm_patcher (credits where they're due)
|
||||
|
||||
.word 0x07368 ; offset
|
||||
.halfword 1 ; type (0: relative to the start of TwlBg's code; 1: relative to the start of the dev SRL launcher)
|
||||
.halfword 4 ; size (must be a multiple of 4)
|
||||
.byte 0x94, 0x09, 0xfc, 0xed ; expected data (decrypted = 0x08, 0x60, 0x87, 0x05)
|
||||
.byte 0x24, 0x09, 0xbc, 0xe9 ; patched data (decrypted = 0xb8, 0x60, 0xc7, 0x01)
|
||||
|
||||
.word 0xa5888
|
||||
.halfword 1
|
||||
.halfword 8
|
||||
.byte 0x83, 0x30, 0x2e, 0xa4, 0xb0, 0xe2, 0xc2, 0xd6 ; (decrypted = 0x02, 0x01, 0x1a, 0xe3, 0x08, 0x60, 0x87, 0x05)
|
||||
.byte 0x83, 0x50, 0xf2, 0xa4, 0xb0, 0xe2, 0xc2, 0xd6 ; (decrypted = 0x02, 0x61, 0xc6, 0xe3, 0x08, 0x60, 0x87, 0xe5)
|
||||
|
||||
patchesEnd:
|
||||
|
||||
.pool
|
||||
|
||||
.close
|
||||
@@ -8,24 +8,23 @@ typedef uint8_t u8;
|
||||
static u8 *memsearch(u8 *startPos, const void *pattern, int size, int patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
int table[256];
|
||||
|
||||
//Preprocessing
|
||||
int i;
|
||||
for(i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
for(i = 0; i < 256; i++)
|
||||
table[i] = patternSize;
|
||||
for(i = 0; i < patternSize - 1; i++)
|
||||
table[patternc[i]] = patternSize - i - 1;
|
||||
|
||||
//Searching
|
||||
int j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
u8 c = startPos[j + patternSize - 1];
|
||||
if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
j += table[c];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
BIN
pathchanger/prebuilt/Linux/pathchanger
Executable file
BIN
pathchanger/prebuilt/Linux/pathchanger
Executable file
Binary file not shown.
BIN
pathchanger/prebuilt/Mac OS X/pathchanger
Executable file
BIN
pathchanger/prebuilt/Mac OS X/pathchanger
Executable file
Binary file not shown.
BIN
pathchanger/prebuilt/Windows/pathchanger.exe
Executable file
BIN
pathchanger/prebuilt/Windows/pathchanger.exe
Executable file
Binary file not shown.
@@ -40,7 +40,8 @@
|
||||
#define BUTTON_DOWN (1 << 7)
|
||||
|
||||
#define SAFE_MODE (BUTTON_R1 | BUTTON_L1 | BUTTON_A | BUTTON_UP)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_X | BUTTON_Y)
|
||||
#define SINGLE_PAYLOAD_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START | BUTTON_B | BUTTON_X | BUTTON_Y)
|
||||
#define L_PAYLOAD_BUTTONS (BUTTON_R1 | BUTTON_A | BUTTON_SELECT)
|
||||
#define EMUNAND_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN)
|
||||
#define MENU_BUTTONS (BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_A | BUTTON_START)
|
||||
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_START)
|
||||
#define PIN_BUTTONS (BUTTON_A | BUTTON_B | BUTTON_X | BUTTON_Y | BUTTON_LEFT | BUTTON_RIGHT | BUTTON_UP | BUTTON_DOWN | BUTTON_START)
|
||||
222
source/config.c
222
source/config.c
@@ -21,38 +21,161 @@
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
#include "memory.h"
|
||||
#include "fs.h"
|
||||
#include "utils.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "buttons.h"
|
||||
#include "pin.h"
|
||||
|
||||
void configureCFW(void)
|
||||
bool readConfig(void)
|
||||
{
|
||||
initScreens();
|
||||
if(fileRead(&configData, CONFIG_PATH, sizeof(CfgData)) != sizeof(CfgData) ||
|
||||
memcmp(configData.magic, "CONF", 4) != 0 ||
|
||||
configData.formatVersionMajor != CONFIG_VERSIONMAJOR ||
|
||||
configData.formatVersionMinor != CONFIG_VERSIONMINOR)
|
||||
{
|
||||
configData.config = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
drawString(CONFIG_TITLE, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to save", 10, 30, COLOR_WHITE);
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *multiOptionsText[] = { "Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )" };
|
||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp)
|
||||
{
|
||||
/* If the configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if(needConfig == CREATE_CONFIGURATION || (configTemp & 0xFFFFFF7F) != configData.config)
|
||||
{
|
||||
if(needConfig == CREATE_CONFIGURATION)
|
||||
{
|
||||
memcpy(configData.magic, "CONF", 4);
|
||||
configData.formatVersionMajor = CONFIG_VERSIONMAJOR;
|
||||
configData.formatVersionMinor = CONFIG_VERSIONMINOR;
|
||||
}
|
||||
|
||||
//Merge the new options and new boot configuration
|
||||
configData.config = (configData.config & 0xFFFFFE00) | (configTemp & 0x1FF);
|
||||
|
||||
if(!fileWrite(&configData, CONFIG_PATH, sizeof(CfgData)))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
}
|
||||
|
||||
void configMenu(bool oldPinStatus)
|
||||
{
|
||||
const char *multiOptionsText[] = { "Default EmuNAND: 1( ) 2( ) 3( ) 4( )",
|
||||
"Screen brightness: 4( ) 3( ) 2( ) 1( )",
|
||||
"PIN lock: Off( ) 4( ) 6( ) 8( ) digits",
|
||||
"New 3DS CPU: Off( ) Clock( ) L2( ) Clock+L2( )"
|
||||
#ifdef DEV
|
||||
, "Dev. features: ErrDisp( ) UNITINFO( ) Off( )"
|
||||
#endif
|
||||
};
|
||||
|
||||
const char *singleOptionsText[] = { "( ) Autoboot SysNAND",
|
||||
"( ) Use SysNAND FIRM if booting with R (A9LH)",
|
||||
"( ) Use second EmuNAND as default",
|
||||
"( ) Enable region/language emu. and ext. .code",
|
||||
"( ) Show current NAND in System Settings",
|
||||
"( ) Show NAND or user string in System Settings",
|
||||
"( ) Show GBA boot screen in patched AGB_FIRM",
|
||||
"( ) Display splash screen before payloads",
|
||||
"( ) Use a PIN",
|
||||
"( ) Enable experimental TwlBg patches" };
|
||||
"( ) Display splash screen before payloads"
|
||||
#ifdef DEV
|
||||
, "( ) Patch SVC/service/archive/ARM9 access"
|
||||
#endif
|
||||
};
|
||||
|
||||
const char *optionsDescription[] = { "Select the default EmuNAND.\n"
|
||||
"It will booted with no directional pad\n"
|
||||
"buttons pressed",
|
||||
|
||||
"Select the screen brightness",
|
||||
|
||||
"Activate a PIN lock.\n"
|
||||
"The PIN will be asked each time\n"
|
||||
"Luma3DS boots.\n"
|
||||
"4, 6 or 8 digits can be selected.\n"
|
||||
"The ABXY buttons and the directional\n"
|
||||
"pad buttons can be used as keys",
|
||||
|
||||
"Select the New 3DS CPU mode.\n"
|
||||
"It will be always enabled.\n"
|
||||
"'Clock+L2' can cause issues with some\n"
|
||||
"games",
|
||||
#ifdef DEV
|
||||
"Select the developer features.\n"
|
||||
"'ErrDisp' displays debug information\n"
|
||||
"on the 'An error has occurred' screen.\n"
|
||||
"'UNITINFO' makes the console be always\n"
|
||||
"detected as a development unit (which\n"
|
||||
"breaks online features and allows\n"
|
||||
"booting some developer software).\n"
|
||||
"'Off' disables exception handlers\n"
|
||||
"in FIRM",
|
||||
#endif
|
||||
"If enabled SysNAND will be launched on\n"
|
||||
"boot. Otherwise, an EmuNAND will.\n"
|
||||
"Hold L on boot to switch NAND.\n"
|
||||
"To use a different EmuNAND from the\n"
|
||||
"default, hold a directional pad button\n"
|
||||
"(Up/Right/Down/Left equal EmuNANDs\n"
|
||||
"1/2/3/4)",
|
||||
|
||||
"If enabled, when holding R on boot\n"
|
||||
"EmuNAND will be booted with the\n"
|
||||
"SysNAND FIRM. Otherwise, SysNAND will\n"
|
||||
"be booted with an EmuNAND FIRM.\n"
|
||||
"To use a different EmuNAND from the\n"
|
||||
"default, hold a directional pad button\n"
|
||||
"(Up/Right/Down/Left equal EmuNANDs\n"
|
||||
"1/2/3/4)",
|
||||
|
||||
"Enable overriding the region and\n"
|
||||
"language configuration and the usage\n"
|
||||
"of patched code binaries for specific\n"
|
||||
"games.\n"
|
||||
"Also makes certain DLCs for\n"
|
||||
"out-of-region games work.\n"
|
||||
"Refer to the wiki for instructions",
|
||||
|
||||
"Show the currently booted NAND\n"
|
||||
"(Sys = SysNAND, Emu = EmuNAND 1,\n"
|
||||
"EmuX = EmuNAND X,\n"
|
||||
"SysE = SysNAND with EmuNAND 1 FIRM,\n"
|
||||
"SyEX = SysNAND with EmuNAND X FIRM,\n"
|
||||
"EmXS = EmuNAND X with SysNAND FIRM)\n"
|
||||
"or an user-defined custom string in\n"
|
||||
"System Settings.\n"
|
||||
"Refer to the wiki for instructions",
|
||||
|
||||
"Show the GBA boot screen when booting\n"
|
||||
"GBA games",
|
||||
|
||||
"If enabled, the splash screen will be\n"
|
||||
"displayed before booting payloads,\n"
|
||||
"otherwise it will be displayed\n"
|
||||
"afterwards.\n"
|
||||
"Intended for splash screens that\n"
|
||||
"display button hints"
|
||||
#ifdef DEV
|
||||
, "Disable SVC, service, archive and ARM9\n"
|
||||
"exheader access checks"
|
||||
#endif
|
||||
};
|
||||
|
||||
struct multiOption {
|
||||
int posXs[4];
|
||||
int posY;
|
||||
u32 posXs[4];
|
||||
u32 posY;
|
||||
u32 enabled;
|
||||
} multiOptions[] = {
|
||||
{ .posXs = {19, 24, 29, 34} },
|
||||
{ .posXs = {21, 26, 31, 36} },
|
||||
{ .posXs = {14, 19, 24, 29} },
|
||||
{ .posXs = {17, 26, 32, 44} }
|
||||
#ifdef DEV
|
||||
, { .posXs = {23, 35, 43, 0} }
|
||||
#endif
|
||||
};
|
||||
|
||||
//Calculate the amount of the various kinds of options and pre-select the first single one
|
||||
@@ -62,7 +185,7 @@ void configureCFW(void)
|
||||
selectedOption = multiOptionsAmount;
|
||||
|
||||
struct singleOption {
|
||||
int posY;
|
||||
u32 posY;
|
||||
bool enabled;
|
||||
} singleOptions[singleOptionsAmount];
|
||||
|
||||
@@ -72,17 +195,25 @@ void configureCFW(void)
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
singleOptions[i].enabled = CONFIG(i);
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString(CONFIG_TITLE, true, 10, 10, COLOR_TITLE);
|
||||
drawString("Press A to select, START to save", true, 10, 30, COLOR_WHITE);
|
||||
|
||||
//Character to display a selected option
|
||||
char selected = 'x';
|
||||
|
||||
int endPos = 42;
|
||||
u32 endPos = 42;
|
||||
|
||||
//Display all the multiple choice options in white
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
{
|
||||
if(!(i == NEWCPU && !isN3DS))
|
||||
{
|
||||
multiOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(multiOptionsText[i], 10, multiOptions[i].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
||||
endPos = drawString(multiOptionsText[i], true, 10, multiOptions[i].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, true, 10 + multiOptions[i].posXs[multiOptions[i].enabled] * SPACING_X, multiOptions[i].posY, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
endPos += SPACING_Y / 2;
|
||||
@@ -92,11 +223,13 @@ void configureCFW(void)
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
{
|
||||
singleOptions[i].posY = endPos + SPACING_Y;
|
||||
endPos = drawString(singleOptionsText[i], 10, singleOptions[i].posY, color);
|
||||
if(singleOptions[i].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||
endPos = drawString(singleOptionsText[i], true, 10, singleOptions[i].posY, color);
|
||||
if(singleOptions[i].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[i].posY, color);
|
||||
color = COLOR_WHITE;
|
||||
}
|
||||
|
||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||
|
||||
u32 pressed = 0;
|
||||
|
||||
//Boring configuration menu
|
||||
@@ -116,10 +249,12 @@ void configureCFW(void)
|
||||
switch(pressed)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
selectedOption = !selectedOption ? totalIndexes : selectedOption - 1;
|
||||
if(!selectedOption) selectedOption = totalIndexes;
|
||||
else selectedOption = (selectedOption == NEWCPU + 1 && !isN3DS) ? selectedOption - 2 : selectedOption - 1;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
selectedOption = selectedOption == totalIndexes ? 0 : selectedOption + 1;
|
||||
if(selectedOption == totalIndexes) selectedOption = 0;
|
||||
else selectedOption = (selectedOption == NEWCPU - 1 && !isN3DS) ? selectedOption + 2 : selectedOption + 1;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
selectedOption = 0;
|
||||
@@ -136,23 +271,26 @@ void configureCFW(void)
|
||||
//The user moved to a different option, print the old option in white and the new one in red. Only print 'x's if necessary
|
||||
if(oldSelectedOption < multiOptionsAmount)
|
||||
{
|
||||
drawString(multiOptionsText[oldSelectedOption], 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
drawString(multiOptionsText[oldSelectedOption], true, 10, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
drawCharacter(selected, true, 10 + multiOptions[oldSelectedOption].posXs[multiOptions[oldSelectedOption].enabled] * SPACING_X, multiOptions[oldSelectedOption].posY, COLOR_WHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
u32 singleOldSelected = oldSelectedOption - multiOptionsAmount;
|
||||
drawString(singleOptionsText[singleOldSelected], 10, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
drawString(singleOptionsText[singleOldSelected], true, 10, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
if(singleOptions[singleOldSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleOldSelected].posY, COLOR_WHITE);
|
||||
}
|
||||
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawString(multiOptionsText[selectedOption], 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
drawString(multiOptionsText[selectedOption], true, 10, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
drawString(singleOptionsText[singleSelected], 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
drawString(singleOptionsText[singleSelected], true, 10, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
|
||||
clearScreens(false, true);
|
||||
drawString(optionsDescription[selectedOption], false, 10, 10, COLOR_WHITE);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -160,39 +298,45 @@ void configureCFW(void)
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
{
|
||||
u32 oldEnabled = multiOptions[selectedOption].enabled;
|
||||
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
|
||||
multiOptions[selectedOption].enabled = oldEnabled == 3 ? 0 : oldEnabled + 1;
|
||||
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[oldEnabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_BLACK);
|
||||
multiOptions[selectedOption].enabled = (oldEnabled == 3 || !multiOptions[selectedOption].posXs[oldEnabled + 1]) ? 0 : oldEnabled + 1;
|
||||
|
||||
if(!selectedOption)
|
||||
updateBrightness(multiOptions[selectedOption].enabled);
|
||||
if(selectedOption == BRIGHTNESS) updateBrightness(multiOptions[BRIGHTNESS].enabled);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool oldEnabled = singleOptions[selectedOption - multiOptionsAmount].enabled;
|
||||
singleOptions[selectedOption - multiOptionsAmount].enabled = !oldEnabled;
|
||||
if(oldEnabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
|
||||
if(oldEnabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[selectedOption - multiOptionsAmount].posY, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
//In any case, if the current option is enabled (or a multiple choice option is selected) we must display a red 'x'
|
||||
if(selectedOption < multiOptionsAmount)
|
||||
drawCharacter(selected, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
drawCharacter(selected, true, 10 + multiOptions[selectedOption].posXs[multiOptions[selectedOption].enabled] * SPACING_X, multiOptions[selectedOption].posY, COLOR_RED);
|
||||
else
|
||||
{
|
||||
u32 singleSelected = selectedOption - multiOptionsAmount;
|
||||
if(singleOptions[singleSelected].enabled) drawCharacter(selected, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
if(singleOptions[singleSelected].enabled) drawCharacter(selected, true, 10 + SPACING_X, singleOptions[singleSelected].posY, COLOR_RED);
|
||||
}
|
||||
}
|
||||
|
||||
//Preserve the last-used boot options (last 12 bits)
|
||||
config &= 0x3F;
|
||||
u32 oldPinLength = MULTICONFIG(PIN);
|
||||
|
||||
//Preserve the last-used boot options (first 9 bits)
|
||||
configData.config &= 0x1FF;
|
||||
|
||||
//Parse and write the new configuration
|
||||
for(u32 i = 0; i < multiOptionsAmount; i++)
|
||||
config |= multiOptions[i].enabled << (i * 2 + 6);
|
||||
configData.config |= multiOptions[i].enabled << (i * 2 + 9);
|
||||
for(u32 i = 0; i < singleOptionsAmount; i++)
|
||||
config |= (singleOptions[i].enabled ? 1 : 0) << (i + 16);
|
||||
configData.config |= (singleOptions[i].enabled ? 1 : 0) << (i + 21);
|
||||
|
||||
if(MULTICONFIG(PIN) != 0) newPin(oldPinStatus && MULTICONFIG(PIN) == oldPinLength);
|
||||
else if(oldPinStatus) fileDelete(PIN_PATH);
|
||||
|
||||
//Wait for the pressed buttons to change
|
||||
while(HID_PAD == BUTTON_START);
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
|
||||
chrono(2);
|
||||
}
|
||||
@@ -24,10 +24,62 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define CONFIG(a) (((config >> (a + 16)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((config >> (a * 2 + 6)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((config >> a) & b)
|
||||
#define CONFIG(a) (((configData.config >> (a + 21)) & 1) != 0)
|
||||
#define MULTICONFIG(a) ((configData.config >> (a * 2 + 9)) & 3)
|
||||
#define BOOTCONFIG(a, b) ((configData.config >> a) & b)
|
||||
|
||||
extern u32 config;
|
||||
#define CONFIG_PATH "/luma/config.bin"
|
||||
#define CONFIG_VERSIONMAJOR 1
|
||||
#define CONFIG_VERSIONMINOR 4
|
||||
|
||||
void configureCFW(void);
|
||||
#define BOOTCFG_NAND BOOTCONFIG(0, 7)
|
||||
#define BOOTCFG_FIRM BOOTCONFIG(3, 7)
|
||||
#define BOOTCFG_A9LH BOOTCONFIG(6, 1)
|
||||
#define BOOTCFG_NOFORCEFLAG BOOTCONFIG(7, 1)
|
||||
#define BOOTCFG_SAFEMODE BOOTCONFIG(8, 1)
|
||||
|
||||
enum multiOptions
|
||||
{
|
||||
DEFAULTEMU = 0,
|
||||
BRIGHTNESS,
|
||||
PIN,
|
||||
NEWCPU
|
||||
#ifdef DEV
|
||||
, DEVOPTIONS
|
||||
#endif
|
||||
};
|
||||
|
||||
enum singleOptions
|
||||
{
|
||||
AUTOBOOTSYS = 0,
|
||||
USESYSFIRM,
|
||||
USELANGEMUANDCODE,
|
||||
PATCHVERSTRING,
|
||||
SHOWGBABOOT,
|
||||
PAYLOADSPLASH
|
||||
#ifdef DEV
|
||||
, PATCHACCESS
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
u16 formatVersionMajor, formatVersionMinor;
|
||||
|
||||
u32 config;
|
||||
} CfgData;
|
||||
|
||||
typedef enum ConfigurationStatus
|
||||
{
|
||||
DONT_CONFIGURE = 0,
|
||||
MODIFY_CONFIGURATION,
|
||||
CREATE_CONFIGURATION
|
||||
} ConfigurationStatus;
|
||||
|
||||
extern CfgData configData;
|
||||
extern bool isN3DS;
|
||||
|
||||
bool readConfig(void);
|
||||
void writeConfig(ConfigurationStatus needConfig, u32 configTemp);
|
||||
void configMenu(bool oldPinStatus);
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
/*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* ARM9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||
*/
|
||||
|
||||
#include "crypto.h"
|
||||
@@ -383,8 +384,7 @@ void decryptExeFs(u8 *inbuf)
|
||||
aes(inbuf - 0x200, exeFsOffset, exeFsSize / AES_BLOCK_SIZE, ncchCTR, AES_CTR_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
}
|
||||
|
||||
/* ARM9Loader replacement
|
||||
Originally adapted from: https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233 */
|
||||
//ARM9Loader replacement
|
||||
void arm9Loader(u8 *arm9Section)
|
||||
{
|
||||
//Determine the arm9loader version
|
||||
@@ -405,7 +405,7 @@ void arm9Loader(u8 *arm9Section)
|
||||
//Firm keys
|
||||
u8 __attribute__((aligned(4))) keyY[0x10];
|
||||
u8 __attribute__((aligned(4))) arm9BinCTR[0x10];
|
||||
u8 arm9BinSlot = a9lVersion ? 0x16 : 0x15;
|
||||
u8 arm9BinSlot = a9lVersion != 0 ? 0x16 : 0x15;
|
||||
|
||||
//Setup keys needed for arm9bin decryption
|
||||
memcpy(keyY, arm9Section + 0x10, 0x10);
|
||||
@@ -414,7 +414,7 @@ void arm9Loader(u8 *arm9Section)
|
||||
//Calculate the size of the ARM9 binary
|
||||
u32 arm9BinSize = 0;
|
||||
//http://stackoverflow.com/questions/12791077/atoi-implementation-in-c
|
||||
for(u8 *tmp = arm9Section + 0x30; *tmp; tmp++)
|
||||
for(u8 *tmp = arm9Section + 0x30; *tmp != 0; tmp++)
|
||||
arm9BinSize = (arm9BinSize << 3) + (arm9BinSize << 1) + *tmp - '0';
|
||||
|
||||
if(a9lVersion)
|
||||
@@ -457,14 +457,13 @@ void arm9Loader(u8 *arm9Section)
|
||||
}
|
||||
}
|
||||
|
||||
void computePINHash(u8 out[32], u8 *in, u32 blockCount)
|
||||
void computePinHash(u8 *out, u8 *in)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) cid[0x10];
|
||||
u8 __attribute__((aligned(4))) cipherText[0x10];
|
||||
|
||||
sdmmc_get_cid(1, (u32 *)cid);
|
||||
|
||||
aes_use_keyslot(4); // console-unique keyslot which keys are set by the Arm9 bootROM
|
||||
aes(cipherText, in, blockCount, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
|
||||
aes_use_keyslot(4); //Console-unique keyslot whose keys are set by the ARM9 bootROM
|
||||
aes(cipherText, in, 1, cid, AES_CBC_ENCRYPT_MODE, AES_INPUT_BE | AES_INPUT_NORMAL);
|
||||
sha(out, cipherText, 0x10, SHA_256_MODE);
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
/*
|
||||
* Crypto libs from http://github.com/b1l1s/ctr
|
||||
* ARM9Loader code originally adapted from https://github.com/Reisyukaku/ReiNand/blob/228c378255ba693133dec6f3368e14d386f2cde7/source/crypto.c#L233
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
@@ -100,8 +101,7 @@
|
||||
#define SHA_1_HASH_SIZE (160 / 8)
|
||||
|
||||
extern u32 emuOffset;
|
||||
extern bool isN3DS;
|
||||
extern bool isDevUnit;
|
||||
extern bool isN3DS, isDevUnit;
|
||||
extern FirmwareSource firmSource;
|
||||
|
||||
void ctrNandInit(void);
|
||||
@@ -109,5 +109,4 @@ u32 ctrNandRead(u32 sector, u32 sectorCount, u8 *outbuf);
|
||||
void setRSAMod0DerivedKeys(void);
|
||||
void decryptExeFs(u8 *inbuf);
|
||||
void arm9Loader(u8 *arm9Section);
|
||||
|
||||
void computePINHash(u8 out[32], u8 *in, u32 blockCount);
|
||||
void computePinHash(u8 *out, u8 *in);
|
||||
@@ -22,52 +22,50 @@
|
||||
|
||||
/*
|
||||
* Code to print to the screen by mid-kid @CakesFW
|
||||
* https://github.com/mid-kid/CakesForeveryWan/
|
||||
* https://github.com/mid-kid/CakesForeveryWan
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "strings.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
#include "fs.h"
|
||||
#include "font.h"
|
||||
|
||||
static inline int strlen(const char *string)
|
||||
{
|
||||
char *stringEnd = (char *)string;
|
||||
|
||||
while(*stringEnd) stringEnd++;
|
||||
|
||||
return stringEnd - string;
|
||||
}
|
||||
|
||||
bool loadSplash(void)
|
||||
{
|
||||
//Don't delay boot nor init the screens if no splash image is on the SD
|
||||
if(getFileSize("/luma/splash.bin") + getFileSize("/luma/splashbottom.bin") == 0)
|
||||
const char topSplashPath[] = "/luma/splash.bin",
|
||||
bottomSplashPath[] = "/luma/splashbottom.bin";
|
||||
|
||||
bool isTopSplashValid = getFileSize(topSplashPath) == SCREEN_TOP_FBSIZE,
|
||||
isBottomSplashValid = getFileSize(bottomSplashPath) == SCREEN_BOTTOM_FBSIZE;
|
||||
|
||||
//Don't delay boot nor init the screens if no splash images or invalid splash images are on the SD
|
||||
if(!isTopSplashValid && !isBottomSplashValid)
|
||||
return false;
|
||||
|
||||
initScreens();
|
||||
|
||||
fileRead(fb->top_left, "/luma/splash.bin");
|
||||
fileRead(fb->bottom, "/luma/splashbottom.bin");
|
||||
if(isTopSplashValid) fileRead(fb->top_left, topSplashPath, 0);
|
||||
if(isBottomSplashValid) fileRead(fb->bottom, bottomSplashPath, 0);
|
||||
|
||||
chrono(3);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void drawCharacter(char character, int posX, int posY, u32 color)
|
||||
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
||||
{
|
||||
u8 *const select = fb->top_left;
|
||||
u8 *select = isTopScreen ? fb->top_left : fb->bottom;
|
||||
|
||||
for(int y = 0; y < 8; y++)
|
||||
for(u32 y = 0; y < 8; y++)
|
||||
{
|
||||
char charPos = font[character * 8 + y];
|
||||
|
||||
for(int x = 7; x >= 0; x--)
|
||||
if ((charPos >> x) & 1)
|
||||
for(u32 x = 0; x < 8; x++)
|
||||
if(((charPos >> (7 - x)) & 1) == 1)
|
||||
{
|
||||
int screenPos = (posX * SCREEN_TOP_HEIGHT * 3 + (SCREEN_TOP_HEIGHT - y - posY - 1) * 3) + (7 - x) * 3 * SCREEN_TOP_HEIGHT;
|
||||
u32 screenPos = (posX * SCREEN_HEIGHT * 3 + (SCREEN_HEIGHT - y - posY - 1) * 3) + x * 3 * SCREEN_HEIGHT;
|
||||
|
||||
select[screenPos] = color >> 16;
|
||||
select[screenPos + 1] = color >> 8;
|
||||
@@ -76,9 +74,9 @@ void drawCharacter(char character, int posX, int posY, u32 color)
|
||||
}
|
||||
}
|
||||
|
||||
int drawString(const char *string, int posX, int posY, u32 color)
|
||||
u32 drawString(const char *string, bool isTopScreen, u32 posX, u32 posY, u32 color)
|
||||
{
|
||||
for(int i = 0, line_i = 0; i < strlen(string); i++, line_i++)
|
||||
for(u32 i = 0, line_i = 0; i < strlen(string); i++, line_i++)
|
||||
{
|
||||
if(string[i] == '\n')
|
||||
{
|
||||
@@ -86,15 +84,15 @@ int drawString(const char *string, int posX, int posY, u32 color)
|
||||
line_i = 0;
|
||||
i++;
|
||||
}
|
||||
else if(line_i >= (SCREEN_TOP_WIDTH - posX) / SPACING_X)
|
||||
else if(line_i >= ((isTopScreen ? SCREEN_TOP_WIDTH : SCREEN_BOTTOM_WIDTH) - posX) / SPACING_X)
|
||||
{
|
||||
// Make sure we never get out of the screen.
|
||||
//Make sure we never get out of the screen
|
||||
posY += SPACING_Y;
|
||||
line_i = 2; //Little offset so we know the same string continues.
|
||||
line_i = 1; //Little offset so we know the same string continues
|
||||
if(string[i] == ' ') i++; //Spaces at the start look weird
|
||||
}
|
||||
|
||||
drawCharacter(string[i], posX + line_i * SPACING_X, posY, color);
|
||||
drawCharacter(string[i], isTopScreen, posX + line_i * SPACING_X, posY, color);
|
||||
}
|
||||
|
||||
return posY;
|
||||
|
||||
@@ -30,7 +30,10 @@
|
||||
#include "types.h"
|
||||
|
||||
#define SCREEN_TOP_WIDTH 400
|
||||
#define SCREEN_TOP_HEIGHT 240
|
||||
#define SCREEN_BOTTOM_WIDTH 320
|
||||
#define SCREEN_HEIGHT 240
|
||||
#define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT)
|
||||
#define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT)
|
||||
|
||||
#define SPACING_Y 10
|
||||
#define SPACING_X 8
|
||||
@@ -39,7 +42,8 @@
|
||||
#define COLOR_WHITE 0xFFFFFF
|
||||
#define COLOR_RED 0x0000FF
|
||||
#define COLOR_BLACK 0x000000
|
||||
#define COLOR_YELLOW 0x00FFFF
|
||||
|
||||
bool loadSplash(void);
|
||||
void drawCharacter(char character, int posX, int posY, u32 color);
|
||||
int drawString(const char *string, int posX, int posY, u32 color);
|
||||
void drawCharacter(char character, bool isTopScreen, u32 posX, u32 posY, u32 color);
|
||||
u32 drawString(const char *string, bool isTopScreen, u32 posX, u32 posY, u32 color);
|
||||
116
source/emunand.c
116
source/emunand.c
@@ -25,65 +25,87 @@
|
||||
#include "fatfs/sdmmc/sdmmc.h"
|
||||
#include "../build/emunandpatch.h"
|
||||
|
||||
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND)
|
||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType)
|
||||
{
|
||||
static u8 temp[0x200];
|
||||
|
||||
const u32 nandSize = getMMCDevice(0)->total_size;
|
||||
u32 nandOffset = *emuNAND == FIRMWARE_EMUNAND ? 0 :
|
||||
(nandSize > 0x200000 ? 0x400000 : 0x200000);
|
||||
bool found = false;
|
||||
|
||||
for(u32 i = 0; i < 3 && !found; i++)
|
||||
{
|
||||
u32 nandOffset;
|
||||
switch(i)
|
||||
{
|
||||
case 1:
|
||||
nandOffset = ROUND_TO_4MB(nandSize + 1); //"Default" layout
|
||||
break;
|
||||
case 2:
|
||||
nandOffset = isN3DS ? 0x26E000 : 0x1D8000; //"Minsize" layout
|
||||
break;
|
||||
default:
|
||||
nandOffset = *nandType == FIRMWARE_EMUNAND ? 0 : (nandSize > 0x200000 ? 0x400000 : 0x200000); //"Legacy" layout
|
||||
break;
|
||||
}
|
||||
|
||||
if(*nandType != FIRMWARE_EMUNAND) nandOffset *= ((u32)*nandType - 1);
|
||||
|
||||
//Check for RedNAND
|
||||
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) &&
|
||||
*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
if(!sdmmc_sdcard_readsectors(nandOffset + 1, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
{
|
||||
*off = nandOffset + 1;
|
||||
*head = nandOffset + 1;
|
||||
emuOffset = nandOffset + 1;
|
||||
*emuHeader = nandOffset + 1;
|
||||
found = true;
|
||||
}
|
||||
|
||||
//Check for Gateway emuNAND
|
||||
else if(!sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) &&
|
||||
*(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
//Check for Gateway EmuNAND
|
||||
else if(i != 2 && !sdmmc_sdcard_readsectors(nandOffset + nandSize, 1, temp) && *(u32 *)(temp + 0x100) == NCSD_MAGIC)
|
||||
{
|
||||
*off = nandOffset;
|
||||
*head = nandOffset + nandSize;
|
||||
emuOffset = nandOffset;
|
||||
*emuHeader = nandOffset + nandSize;
|
||||
found = true;
|
||||
}
|
||||
|
||||
/* Fallback to the first emuNAND if there's no second one,
|
||||
or to SysNAND if there isn't any */
|
||||
else
|
||||
if(*nandType == FIRMWARE_EMUNAND) break;
|
||||
}
|
||||
|
||||
//Fallback to the first EmuNAND if there's no second/third/fourth one, or to SysNAND if there isn't any
|
||||
if(!found)
|
||||
{
|
||||
*emuNAND = (*emuNAND == FIRMWARE_EMUNAND2) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
if(*emuNAND) locateEmuNAND(off, head, emuNAND);
|
||||
if(*nandType != FIRMWARE_EMUNAND)
|
||||
{
|
||||
*nandType = FIRMWARE_EMUNAND;
|
||||
locateEmuNand(emuHeader, nandType);
|
||||
}
|
||||
else *nandType = FIRMWARE_SYSNAND;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *getEmuCode(u8 *pos, u32 size)
|
||||
static inline u8 *getFreeK9Space(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00};
|
||||
|
||||
//Looking for the last free space before Process9
|
||||
return memsearch(pos + 0x13500, pattern, size - 0x13500, 6) + 0x455;
|
||||
return memsearch(pos + 0x13500, pattern, size - 0x13500, sizeof(pattern)) + 0x455;
|
||||
}
|
||||
|
||||
static inline u32 getSDMMC(u8 *pos, u32 size)
|
||||
static inline u32 getSdmmc(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for struct code
|
||||
const u8 pattern[] = {0x21, 0x20, 0x18, 0x20};
|
||||
const u8 *off = memsearch(pos, pattern, size, 4);
|
||||
const u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
return *(u32 *)(off + 9) + *(u32 *)(off + 0xD);
|
||||
}
|
||||
|
||||
static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset)
|
||||
static inline void patchNandRw(u8 *pos, u32 size, u32 branchOffset)
|
||||
{
|
||||
const u16 nandRedir[2] = {0x4C00, 0x47A0};
|
||||
|
||||
//Look for read/write code
|
||||
const u8 pattern[] = {0x1E, 0x00, 0xC8, 0x05};
|
||||
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, 4) - 3,
|
||||
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, 4) - 3;
|
||||
u16 *readOffset = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 3,
|
||||
*writeOffset = (u16 *)memsearch((u8 *)(readOffset + 5), pattern, 0x100, sizeof(pattern)) - 3;
|
||||
|
||||
*readOffset = nandRedir[0];
|
||||
readOffset[1] = nandRedir[1];
|
||||
@@ -93,40 +115,38 @@ static inline void patchNANDRW(u8 *pos, u32 size, u32 branchOffset)
|
||||
((u32 *)writeOffset)[1] = branchOffset;
|
||||
}
|
||||
|
||||
static inline void patchMPU(u8 *pos, u32 size)
|
||||
static inline void patchMpu(u8 *pos, u32 size)
|
||||
{
|
||||
const u32 mpuPatch[3] = {0x00360003, 0x00200603, 0x001C0603};
|
||||
|
||||
//Look for MPU pattern
|
||||
const u8 pattern[] = {0x03, 0x00, 0x24, 0x00};
|
||||
|
||||
u32 *off = (u32 *)memsearch(pos, pattern, size, 4);
|
||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
off[0] = mpuPatch[0];
|
||||
off[6] = mpuPatch[1];
|
||||
off[9] = mpuPatch[2];
|
||||
off[0] = 0x00360003;
|
||||
off[6] = 0x00200603;
|
||||
off[9] = 0x001C0603;
|
||||
}
|
||||
|
||||
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive)
|
||||
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive)
|
||||
{
|
||||
//Copy emuNAND code
|
||||
void *emuCodeOffset = getEmuCode(arm9Section, arm9SectionSize);
|
||||
memcpy(emuCodeOffset, emunand, emunand_size);
|
||||
//Copy EmuNAND code
|
||||
u8 *freeK9Space = getFreeK9Space(arm9Section, arm9SectionSize);
|
||||
memcpy(freeK9Space, emunand, emunand_size);
|
||||
|
||||
//Add the data of the found emuNAND
|
||||
u32 *pos_offset = (u32 *)memsearch(emuCodeOffset, "NAND", emunand_size, 4),
|
||||
*pos_header = (u32 *)memsearch(emuCodeOffset, "NCSD", emunand_size, 4);
|
||||
*pos_offset = emuOffset;
|
||||
*pos_header = emuHeader;
|
||||
//Add the data of the found EmuNAND
|
||||
u32 *posOffset = (u32 *)memsearch(freeK9Space, "NAND", emunand_size, 4),
|
||||
*posHeader = (u32 *)memsearch(freeK9Space, "NCSD", emunand_size, 4);
|
||||
*posOffset = emuOffset;
|
||||
*posHeader = emuHeader;
|
||||
|
||||
//Find and add the SDMMC struct
|
||||
u32 *pos_sdmmc = (u32 *)memsearch(emuCodeOffset, "SDMC", emunand_size, 4);
|
||||
*pos_sdmmc = getSDMMC(process9Offset, process9Size);
|
||||
u32 *posSdmmc = (u32 *)memsearch(freeK9Space, "SDMC", emunand_size, 4);
|
||||
*posSdmmc = getSdmmc(process9Offset, process9Size);
|
||||
|
||||
//Add emuNAND hooks
|
||||
u32 branchOffset = (u32)emuCodeOffset - branchAdditive;
|
||||
patchNANDRW(process9Offset, process9Size, branchOffset);
|
||||
//Add EmuNAND hooks
|
||||
u32 branchOffset = (u32)freeK9Space - branchAdditive;
|
||||
patchNandRw(process9Offset, process9Size, branchOffset);
|
||||
|
||||
//Set MPU for emu code region
|
||||
patchMPU(arm9Section, arm9SectionSize);
|
||||
//Set MPU
|
||||
patchMpu(arm9Section, arm9SectionSize);
|
||||
}
|
||||
@@ -25,6 +25,10 @@
|
||||
#include "types.h"
|
||||
|
||||
#define NCSD_MAGIC 0x4453434E
|
||||
#define ROUND_TO_4MB(a) (((a) + 0x2000 - 1) & (~(0x2000 - 1)))
|
||||
|
||||
void locateEmuNAND(u32 *off, u32 *head, FirmwareSource *emuNAND);
|
||||
void patchEmuNAND(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuOffset, u32 emuHeader, u32 branchAdditive);
|
||||
extern u32 emuOffset;
|
||||
extern bool isN3DS;
|
||||
|
||||
void locateEmuNand(u32 *emuHeader, FirmwareSource *nandType);
|
||||
void patchEmuNand(u8 *arm9Section, u32 arm9SectionSize, u8 *process9Offset, u32 process9Size, u32 emuHeader, u32 branchAdditive);
|
||||
201
source/exceptions.c
Normal file
201
source/exceptions.c
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef DEV
|
||||
#include "exceptions.h"
|
||||
#include "fs.h"
|
||||
#include "strings.h"
|
||||
#include "memory.h"
|
||||
#include "screen.h"
|
||||
#include "draw.h"
|
||||
#include "utils.h"
|
||||
#include "../build/arm9_exceptions.h"
|
||||
#include "../build/arm11_exceptions.h"
|
||||
|
||||
void installArm9Handlers(void)
|
||||
{
|
||||
const u32 offsets[] = {0x08, 0x18, 0x20, 0x28};
|
||||
|
||||
memcpy((void *)0x01FF8000, arm9_exceptions + 32, arm9_exceptions_size - 32);
|
||||
|
||||
/* IRQHandler is at 0x08000000, but we won't handle it for some reasons
|
||||
svcHandler is at 0x08000010, but we won't handle svc either */
|
||||
|
||||
for(u32 i = 0; i < 4; i++)
|
||||
{
|
||||
*(vu32 *)(0x08000000 + offsets[i]) = 0xE51FF004;
|
||||
*(vu32 *)(0x08000000 + offsets[i] + 4) = *((u32 *)arm9_exceptions + 1 + i);
|
||||
}
|
||||
}
|
||||
|
||||
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset)
|
||||
{
|
||||
u32 *initFPU;
|
||||
for(initFPU = exceptionsPage; initFPU < (exceptionsPage + 0x400) && (initFPU[0] != 0xE59F0008 || initFPU[1] != 0xE5900000); initFPU++);
|
||||
|
||||
u32 *mcuReboot;
|
||||
for(mcuReboot = exceptionsPage; mcuReboot < (exceptionsPage + 0x400) && (mcuReboot[0] != 0xE59F4104 || mcuReboot[1] != 0xE3A0A0C2); mcuReboot++);
|
||||
mcuReboot--;
|
||||
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = initFPU; freeSpace < (exceptionsPage + 0x400) && (freeSpace[0] != 0xFFFFFFFF || freeSpace[1] != 0xFFFFFFFF); freeSpace++);
|
||||
|
||||
memcpy(freeSpace, arm11_exceptions + 32, arm11_exceptions_size - 32);
|
||||
|
||||
exceptionsPage[1] = MAKE_BRANCH(exceptionsPage + 1, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 8) - 32); //Undefined Instruction
|
||||
exceptionsPage[3] = MAKE_BRANCH(exceptionsPage + 3, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 12) - 32); //Prefetch Abort
|
||||
exceptionsPage[4] = MAKE_BRANCH(exceptionsPage + 4, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 16) - 32); //Data Abort
|
||||
exceptionsPage[7] = MAKE_BRANCH(exceptionsPage + 7, (u8 *)freeSpace + *(u32 *)(arm11_exceptions + 4) - 32); //FIQ
|
||||
|
||||
for(u32 *pos = freeSpace; pos < (u32 *)((u8 *)freeSpace + arm11_exceptions_size - 32); pos++)
|
||||
{
|
||||
switch(*pos) //Perform relocations
|
||||
{
|
||||
case 0xFFFF3000: *pos = stackAddress; break;
|
||||
case 0xEBFFFFFE: *pos = MAKE_BRANCH_LINK(pos, initFPU); break;
|
||||
case 0xEAFFFFFE: *pos = MAKE_BRANCH(pos, mcuReboot); break;
|
||||
case 0xE12FFF1C: pos[1] = 0xFFFF0000 + 4 * (u32)(freeSpace - exceptionsPage) + pos[1] - 32; break; //bx r12 (mainHandler)
|
||||
case 0xBEEFBEEF: *pos = codeSetOffset; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void detectAndProcessExceptionDumps(void)
|
||||
{
|
||||
volatile ExceptionDumpHeader *dumpHeader = (volatile ExceptionDumpHeader *)0x25000000;
|
||||
|
||||
if(dumpHeader->magic[0] == 0xDEADC0DE && dumpHeader->magic[1] == 0xDEADCAFE && (dumpHeader->processor == 9 || dumpHeader->processor == 11))
|
||||
{
|
||||
const vu32 *regs = (vu32 *)((vu8 *)dumpHeader + sizeof(ExceptionDumpHeader));
|
||||
const vu8 *stackDump = (vu8 *)regs + dumpHeader->registerDumpSize + dumpHeader->codeDumpSize;
|
||||
const vu8 *additionalData = stackDump + dumpHeader->stackDumpSize;
|
||||
|
||||
const char *handledExceptionNames[] = {
|
||||
"FIQ", "undefined instruction", "prefetch abort", "data abort"
|
||||
};
|
||||
|
||||
const char *specialExceptions[] = {
|
||||
"(kernel panic)", "(svcBreak)"
|
||||
};
|
||||
|
||||
const char *registerNames[] = {
|
||||
"R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12",
|
||||
"SP", "LR", "PC", "CPSR", "FPEXC"
|
||||
};
|
||||
|
||||
char hexString[] = "00000000";
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString("An exception occurred", true, 10, 10, COLOR_RED);
|
||||
u32 posY = drawString(dumpHeader->processor == 11 ? "Processor: ARM11 (core )" : "Processor: ARM9", true, 10, 30, COLOR_WHITE);
|
||||
if(dumpHeader->processor == 11) drawCharacter('0' + dumpHeader->core, true, 10 + 29 * SPACING_X, 30, COLOR_WHITE);
|
||||
|
||||
posY = drawString("Exception type: ", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
drawString(handledExceptionNames[dumpHeader->type], true, 10 + 17 * SPACING_X, posY, COLOR_WHITE);
|
||||
|
||||
if(dumpHeader->type == 2)
|
||||
{
|
||||
if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 4)
|
||||
{
|
||||
u32 instr = *(vu32 *)(stackDump - 4);
|
||||
if(instr == 0xE12FFF7E) drawString(specialExceptions[0], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
else if(instr == 0xEF00003C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
}
|
||||
else if((regs[16] & 0x20) == 0 && dumpHeader->codeDumpSize >= 2)
|
||||
{
|
||||
u16 instr = *(vu16 *)(stackDump - 2);
|
||||
if(instr == 0xDF3C) drawString(specialExceptions[1], true, 10 + 32 * SPACING_X, posY, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
if(dumpHeader->processor == 11 && dumpHeader->additionalDataSize != 0)
|
||||
{
|
||||
char processName[] = "Current process: ";
|
||||
memcpy(processName + sizeof(processName) - 9, (void *)additionalData, 8);
|
||||
posY = drawString(processName, true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
}
|
||||
|
||||
posY += SPACING_Y;
|
||||
|
||||
for(u32 i = 0; i < 17; i += 2)
|
||||
{
|
||||
posY = drawString(registerNames[i], true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
hexItoa(regs[i], hexString, 8);
|
||||
drawString(hexString, true, 10 + 7 * SPACING_X, posY, COLOR_WHITE);
|
||||
|
||||
if(i != 16 || dumpHeader->processor != 9)
|
||||
{
|
||||
drawString(registerNames[i + 1], true, 10 + 22 * SPACING_X, posY, COLOR_WHITE);
|
||||
hexItoa(i == 16 ? regs[20] : regs[i + 1], hexString, 8);
|
||||
drawString(hexString, true, 10 + 29 * SPACING_X, posY, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
posY += SPACING_Y;
|
||||
|
||||
u32 mode = regs[16] & 0xF;
|
||||
if(dumpHeader->type == 3 && (mode == 7 || mode == 11))
|
||||
posY = drawString("Incorrect dump: failed to dump code and/or stack", true, 10, posY + SPACING_Y, COLOR_YELLOW) + SPACING_Y;
|
||||
|
||||
u32 posYBottom = drawString("Stack dump:", false, 10, 10, COLOR_WHITE) + SPACING_Y;
|
||||
|
||||
for(u32 line = 0; line < 19 && stackDump < additionalData; line++)
|
||||
{
|
||||
hexItoa(regs[13] + 8 * line, hexString, 8);
|
||||
posYBottom = drawString(hexString, false, 10, posYBottom + SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter(':', false, 10 + 8 * SPACING_X, posYBottom, COLOR_WHITE);
|
||||
|
||||
for(u32 i = 0; i < 8 && stackDump < additionalData; i++, stackDump++)
|
||||
{
|
||||
char byteString[] = "00";
|
||||
hexItoa(*stackDump, byteString, 2);
|
||||
drawString(byteString, false, 10 + 10 * SPACING_X + 3 * i * SPACING_X, posYBottom, COLOR_WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
char path[42];
|
||||
char fileName[] = "crash_dump_00000000.dmp";
|
||||
const char *pathFolder = dumpHeader->processor == 9 ? "/luma/dumps/arm9" : "/luma/dumps/arm11";
|
||||
|
||||
findDumpFile(pathFolder, fileName);
|
||||
memcpy(path, pathFolder, strlen(pathFolder) + 1);
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, fileName);
|
||||
|
||||
if(fileWrite((void *)dumpHeader, path, dumpHeader->totalSize))
|
||||
{
|
||||
posY = drawString("You can find a dump in the following file:", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
posY = drawString(path, true, 10, posY + SPACING_Y, COLOR_WHITE) + SPACING_Y;
|
||||
}
|
||||
else posY = drawString("Error writing the dump file", true, 10, posY + SPACING_Y, COLOR_RED);
|
||||
|
||||
drawString("Press any button to shutdown", true, 10, posY + SPACING_Y, COLOR_WHITE);
|
||||
|
||||
memset32((void *)dumpHeader, 0, dumpHeader->totalSize);
|
||||
|
||||
waitInput();
|
||||
mcuPowerOff();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
49
source/exceptions.h
Normal file
49
source/exceptions.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifdef DEV
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define MAKE_BRANCH(src,dst) (0xEA000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
#define MAKE_BRANCH_LINK(src,dst) (0xEB000000 | ((u32)((((u8 *)(dst) - (u8 *)(src)) >> 2) - 2) & 0xFFFFFF))
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
u32 magic[2];
|
||||
u16 versionMinor, versionMajor;
|
||||
|
||||
u16 processor, core;
|
||||
u32 type;
|
||||
|
||||
u32 totalSize;
|
||||
u32 registerDumpSize;
|
||||
u32 codeDumpSize;
|
||||
u32 stackDumpSize;
|
||||
u32 additionalDataSize;
|
||||
} ExceptionDumpHeader;
|
||||
|
||||
void installArm9Handlers(void);
|
||||
void installArm11Handlers(u32 *exceptionsPage, u32 stackAddress, u32 codeSetOffset);
|
||||
void detectAndProcessExceptionDumps(void);
|
||||
#endif
|
||||
@@ -260,8 +260,20 @@ R0.12a (July 10, 2016)
|
||||
Added support for creating exFAT volume with some changes of f_mkfs().
|
||||
Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
|
||||
f_forward() is available regardless of _FS_TINY.
|
||||
Fixed f_mkfs() creates wrong volume.
|
||||
Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
|
||||
Fixed wrong memory read in create_name(). (appeared at R0.12)
|
||||
Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
|
||||
Fixed wrong memory read in create_name().
|
||||
|
||||
|
||||
|
||||
R0.12b (September 04, 2016)
|
||||
|
||||
Improved f_rename() to 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)
|
||||
Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
|
||||
Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
|
||||
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)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12a /
|
||||
/ FatFs - Generic FAT file system module R0.12b /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
#if _FATFS != 80186 /* Revision ID */
|
||||
#if _FATFS != 68020 /* Revision ID */
|
||||
#error Wrong include file (ff.h).
|
||||
#endif
|
||||
|
||||
@@ -396,7 +396,7 @@ typedef struct {
|
||||
#define BPB_NumFATs 16 /* Number of FATs (BYTE) */
|
||||
#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */
|
||||
#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */
|
||||
#define BPB_Media 21 /* Media descriptor (BYTE) */
|
||||
#define BPB_Media 21 /* Media descriptor byte (BYTE) */
|
||||
#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */
|
||||
#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */
|
||||
#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */
|
||||
@@ -409,6 +409,7 @@ typedef struct {
|
||||
#define BS_VolLab 43 /* Volume label string (8-byte) */
|
||||
#define BS_FilSysType 54 /* File system type string (8-byte) */
|
||||
#define BS_BootCode 62 /* Boot code (448-byte) */
|
||||
#define BS_55AA 510 /* Signature word (WORD) */
|
||||
|
||||
#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */
|
||||
#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */
|
||||
@@ -462,8 +463,6 @@ typedef struct {
|
||||
#define PTE_StLba 8 /* MBR PTE: Start in LBA */
|
||||
#define PTE_SizLba 12 /* MBR PTE: Size in LBA */
|
||||
|
||||
#define BS_55AA 510 /* Signature word (WORD) */
|
||||
|
||||
#define DIR_Name 0 /* Short file name (11-byte) */
|
||||
#define DIR_Attr 11 /* Attribute (BYTE) */
|
||||
#define DIR_NTres 12 /* Lower case flag (BYTE) */
|
||||
@@ -1141,7 +1140,7 @@ DWORD find_bitmap ( /* 0:No free cluster, 2..:Free cluster found, 0xFFFFFFFF:Dis
|
||||
if (clst >= fs->n_fatent - 2) clst = 0;
|
||||
scl = val = clst; ctr = 0;
|
||||
for (;;) {
|
||||
if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
|
||||
if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */
|
||||
i = val / 8 % SS(fs); bm = 1 << (val % 8);
|
||||
do {
|
||||
do {
|
||||
@@ -1180,7 +1179,7 @@ FRESULT change_bitmap (
|
||||
|
||||
|
||||
clst -= 2; /* The first bit corresponds to cluster #2 */
|
||||
sect = fs->database + clst / 8 / SS(fs); /* Sector address */
|
||||
sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */
|
||||
i = clst / 8 % SS(fs); /* Byte offset in the sector */
|
||||
bm = 1 << (clst % 8); /* Bit mask in the byte */
|
||||
for (;;) {
|
||||
@@ -1321,7 +1320,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err
|
||||
|
||||
|
||||
if (clst == 0) { /* Create a new chain */
|
||||
scl = fs->last_clst; /* Get suggested cluster to start at */
|
||||
scl = fs->last_clst; /* Get suggested cluster to start from */
|
||||
if (scl == 0 || scl >= fs->n_fatent) scl = 1;
|
||||
}
|
||||
else { /* Stretch current chain */
|
||||
@@ -1333,7 +1332,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err
|
||||
}
|
||||
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */
|
||||
if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */
|
||||
res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */
|
||||
@@ -1349,7 +1348,7 @@ DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk err
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{ /* At the FAT12/16/32 */
|
||||
{ /* On the FAT12/16/32 volume */
|
||||
ncl = scl; /* Start cluster */
|
||||
for (;;) {
|
||||
ncl++; /* Next cluster */
|
||||
@@ -1775,7 +1774,7 @@ void gen_numname (
|
||||
/* itoa (hexdecimal) */
|
||||
i = 7;
|
||||
do {
|
||||
c = (seq % 16) + '0';
|
||||
c = (BYTE)((seq % 16) + '0');
|
||||
if (c > '9') c += 7;
|
||||
ns[i--] = c;
|
||||
seq /= 16;
|
||||
@@ -2105,7 +2104,7 @@ FRESULT dir_read (
|
||||
c = dp->dir[DIR_Name]; /* Test for the entry type */
|
||||
if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of the directory */
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
if (_USE_LABEL && vol) {
|
||||
if (c == 0x83) break; /* Volume label entry? */
|
||||
} else {
|
||||
@@ -2120,7 +2119,7 @@ FRESULT dir_read (
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{ /* At the FAT12/16/32 */
|
||||
{ /* On the FAT12/16/32 volume */
|
||||
dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */
|
||||
#if _USE_LFN != 0 /* LFN configuration */
|
||||
if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */
|
||||
@@ -2129,7 +2128,7 @@ FRESULT dir_read (
|
||||
if (a == AM_LFN) { /* An LFN entry is found */
|
||||
if (c & LLEF) { /* Is it start of an LFN sequence? */
|
||||
sum = dp->dir[LDIR_Chksum];
|
||||
c &= ~LLEF; ord = c;
|
||||
c &= (BYTE)~LLEF; ord = c;
|
||||
dp->blk_ofs = dp->dptr;
|
||||
}
|
||||
/* Check LFN validity and capture it */
|
||||
@@ -2178,7 +2177,7 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
|
||||
res = dir_sdi(dp, 0); /* Rewind directory object */
|
||||
if (res != FR_OK) return res;
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
BYTE nc;
|
||||
UINT di, ni;
|
||||
WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */
|
||||
@@ -2194,7 +2193,7 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
|
||||
return res;
|
||||
}
|
||||
#endif
|
||||
/* At the FAT12/16/32 */
|
||||
/* On the FAT12/16/32 volume */
|
||||
#if _USE_LFN != 0
|
||||
ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */
|
||||
#endif
|
||||
@@ -2212,7 +2211,7 @@ FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */
|
||||
if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
|
||||
if (c & LLEF) { /* Is it start of LFN sequence? */
|
||||
sum = dp->dir[LDIR_Chksum];
|
||||
c &= ~LLEF; ord = c; /* LFN start order */
|
||||
c &= (BYTE)~LLEF; ord = c; /* LFN start order */
|
||||
dp->blk_ofs = dp->dptr; /* Start offset of LFN */
|
||||
}
|
||||
/* Check validity of the LFN entry and compare it with given name */
|
||||
@@ -2258,7 +2257,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S
|
||||
for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */
|
||||
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
DIR dj;
|
||||
|
||||
nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */
|
||||
@@ -2266,7 +2265,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S
|
||||
if (res != FR_OK) return res;
|
||||
dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set block position */
|
||||
|
||||
if (dp->obj.stat & 4) { /* Has the sub-directory been stretched? */
|
||||
if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */
|
||||
dp->obj.stat &= 3;
|
||||
dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase object size by cluster size */
|
||||
res = fill_fat_chain(&dp->obj); /* Complement FAT chain if needed */
|
||||
@@ -2284,7 +2283,7 @@ FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many S
|
||||
return FR_OK;
|
||||
}
|
||||
#endif
|
||||
/* At the FAT12/16/32 */
|
||||
/* On the FAT12/16/32 volume */
|
||||
mem_cpy(sn, dp->fn, 12);
|
||||
if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */
|
||||
dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */
|
||||
@@ -2361,9 +2360,9 @@ FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
|
||||
res = move_window(fs, dp->sect);
|
||||
if (res != FR_OK) break;
|
||||
/* Mark an entry 'deleted' */
|
||||
if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
dp->dir[XDIR_Type] &= 0x7F;
|
||||
} else { /* At the FAT12/16/32 */
|
||||
} else { /* On the FAT12/16/32 volume */
|
||||
dp->dir[DIR_Name] = DDEM;
|
||||
}
|
||||
fs->wflag = 1;
|
||||
@@ -2413,12 +2412,12 @@ void get_fileinfo ( /* No return code */
|
||||
|
||||
#if _USE_LFN != 0 /* LFN configuration */
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
get_xdir_info(fs->dirbuf, fno);
|
||||
return;
|
||||
} else
|
||||
#endif
|
||||
{ /* At the FAT12/16/32 */
|
||||
{ /* On the FAT12/16/32 volume */
|
||||
if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */
|
||||
i = j = 0;
|
||||
while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */
|
||||
@@ -2430,7 +2429,7 @@ void get_fileinfo ( /* No return code */
|
||||
}
|
||||
#endif
|
||||
if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */
|
||||
fno->fname[i++] = (char)w;
|
||||
fno->fname[i++] = (TCHAR)w;
|
||||
}
|
||||
fno->fname[i] = 0; /* Terminate the LFN */
|
||||
}
|
||||
@@ -2628,7 +2627,7 @@ FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */
|
||||
if (si) cf |= NS_LOSS | NS_LFN;
|
||||
while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */
|
||||
|
||||
b = i = 0; ni = 8;
|
||||
i = b = 0; ni = 8;
|
||||
for (;;) {
|
||||
w = lfn[si++]; /* Get an LFN character */
|
||||
if (!w) break; /* Break on end of the LFN */
|
||||
@@ -2955,7 +2954,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
|
||||
UINT i;
|
||||
|
||||
|
||||
/* Get logical drive number from the path name */
|
||||
/* Get logical drive number */
|
||||
*rfs = 0;
|
||||
vol = get_ldnumber(path);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
@@ -2967,7 +2966,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
|
||||
ENTER_FF(fs); /* Lock the volume */
|
||||
*rfs = fs; /* Return pointer to the file system object */
|
||||
|
||||
mode &= ~FA_READ; /* Desired access mode, write access or not */
|
||||
mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */
|
||||
if (fs->fs_type) { /* If the volume has been mounted */
|
||||
stat = disk_status(fs->drv);
|
||||
if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */
|
||||
@@ -3045,7 +3044,7 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
|
||||
fs->volbase = bsect;
|
||||
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
|
||||
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
|
||||
if (maxlba < fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
|
||||
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
|
||||
fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
|
||||
|
||||
/* Check if bitmap location is in assumption (at the first cluster) */
|
||||
@@ -3162,15 +3161,14 @@ FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */
|
||||
|
||||
static
|
||||
FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */
|
||||
void* dfp, /* Pointer to the FIL/DIR object to check validity */
|
||||
_FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */
|
||||
FATFS** fs /* Pointer to pointer to the owner file system object to return */
|
||||
)
|
||||
{
|
||||
_FDID *obj = (_FDID*)dfp; /* Assuming .obj in the FIL/DIR is the first member */
|
||||
FRESULT res;
|
||||
|
||||
|
||||
if (!dfp || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) {
|
||||
if (!obj || !obj->fs || !obj->fs->fs_type || obj->fs->id != obj->id || (disk_status(obj->fs->drv) & STA_NOINIT)) {
|
||||
*fs = 0; /* The object is invalid */
|
||||
res = FR_INVALID_OBJECT;
|
||||
} else {
|
||||
@@ -3208,6 +3206,7 @@ FRESULT f_mount (
|
||||
const TCHAR *rp = path;
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
vol = get_ldnumber(&rp);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
cfs = FatFs[vol]; /* Pointer to fs object */
|
||||
@@ -3261,7 +3260,7 @@ FRESULT f_open (
|
||||
|
||||
if (!fp) return FR_INVALID_OBJECT;
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND;
|
||||
res = find_volume(&path, &fs, mode);
|
||||
if (res == FR_OK) {
|
||||
@@ -3460,7 +3459,7 @@ FRESULT f_read (
|
||||
|
||||
|
||||
*br = 0; /* Clear read byte counter */
|
||||
res = validate(fp, &fs);
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
remain = fp->obj.objsize - fp->fptr;
|
||||
@@ -3495,9 +3494,7 @@ FRESULT f_read (
|
||||
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
|
||||
cc = fs->csize - csect;
|
||||
}
|
||||
if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) {
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */
|
||||
#if _FS_TINY
|
||||
if (fs->wflag && fs->winsect - sect < cc) {
|
||||
@@ -3517,12 +3514,10 @@ FRESULT f_read (
|
||||
#if !_FS_READONLY
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { /* Fill sector cache */
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
|
||||
}
|
||||
#endif
|
||||
fp->sect = sect;
|
||||
@@ -3563,7 +3558,7 @@ FRESULT f_write (
|
||||
|
||||
|
||||
*bw = 0; /* Clear write byte counter */
|
||||
res = validate(fp, &fs);
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
|
||||
@@ -3603,7 +3598,7 @@ FRESULT f_write (
|
||||
#else
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back sector cache */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
sect = clust2sect(fs, fp->clust); /* Get current sector */
|
||||
@@ -3614,9 +3609,7 @@ FRESULT f_write (
|
||||
if (csect + cc > fs->csize) { /* Clip at cluster boundary */
|
||||
cc = fs->csize - csect;
|
||||
}
|
||||
if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) {
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
#if _FS_MINIMIZE <= 2
|
||||
#if _FS_TINY
|
||||
if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
|
||||
@@ -3626,7 +3619,7 @@ FRESULT f_write (
|
||||
#else
|
||||
if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
|
||||
mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -3634,17 +3627,16 @@ FRESULT f_write (
|
||||
continue;
|
||||
}
|
||||
#if _FS_TINY
|
||||
if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling at growing edge */
|
||||
if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */
|
||||
if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fs->winsect = sect;
|
||||
}
|
||||
#else
|
||||
if (fp->sect != sect) { /* Fill sector cache with file data */
|
||||
if (fp->fptr < fp->obj.objsize &&
|
||||
if (fp->sect != sect && /* Fill sector cache with file data */
|
||||
fp->fptr < fp->obj.objsize &&
|
||||
disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) {
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
fp->sect = sect;
|
||||
}
|
||||
@@ -3680,15 +3672,18 @@ FRESULT f_sync (
|
||||
FATFS *fs;
|
||||
DWORD tm;
|
||||
BYTE *dir;
|
||||
#if _FS_EXFAT
|
||||
DEF_NAMBUF
|
||||
#endif
|
||||
|
||||
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res == FR_OK) {
|
||||
if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */
|
||||
#if !_FS_TINY
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
/* Update the directory entry */
|
||||
@@ -3713,7 +3708,7 @@ FRESULT f_sync (
|
||||
res = store_xdir(&dj); /* Restore it to the directory */
|
||||
if (res == FR_OK) {
|
||||
res = sync_fs(fs);
|
||||
fp->flag &= ~FA_MODIFIED;
|
||||
fp->flag &= (BYTE)~FA_MODIFIED;
|
||||
}
|
||||
}
|
||||
FREE_NAMBUF();
|
||||
@@ -3731,7 +3726,7 @@ FRESULT f_sync (
|
||||
st_word(dir + DIR_LstAccDate, 0);
|
||||
fs->wflag = 1;
|
||||
res = sync_fs(fs); /* Restore it to the directory */
|
||||
fp->flag &= ~FA_MODIFIED;
|
||||
fp->flag &= (BYTE)~FA_MODIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3761,7 +3756,7 @@ FRESULT f_close (
|
||||
if (res == FR_OK)
|
||||
#endif
|
||||
{
|
||||
res = validate(fp, &fs); /* Lock volume */
|
||||
res = validate(&fp->obj, &fs); /* Lock volume */
|
||||
if (res == FR_OK) {
|
||||
#if _FS_LOCK != 0
|
||||
res = dec_lock(fp->obj.lockid); /* Decrement file open counter */
|
||||
@@ -3794,10 +3789,11 @@ FRESULT f_chdrive (
|
||||
int vol;
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
vol = get_ldnumber(&path);
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
|
||||
CurrVol = (BYTE)vol;
|
||||
CurrVol = (BYTE)vol; /* Set it as current volume */
|
||||
|
||||
return FR_OK;
|
||||
}
|
||||
@@ -3813,7 +3809,7 @@ FRESULT f_chdir (
|
||||
FATFS *fs;
|
||||
DEF_NAMBUF
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, 0);
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
@@ -3872,7 +3868,7 @@ FRESULT f_getcwd (
|
||||
|
||||
|
||||
*buff = 0;
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */
|
||||
if (res == FR_OK) {
|
||||
dj.obj.fs = fs;
|
||||
@@ -3949,7 +3945,7 @@ FRESULT f_lseek (
|
||||
DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl;
|
||||
#endif
|
||||
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
#if _USE_FASTSEEK
|
||||
if (fp->cltbl) { /* Fast seek */
|
||||
@@ -3990,13 +3986,11 @@ FRESULT f_lseek (
|
||||
#if !_FS_TINY
|
||||
#if !_FS_READONLY
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fp, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) { /* Load current sector */
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */
|
||||
#endif
|
||||
fp->sect = dsc;
|
||||
}
|
||||
@@ -4007,6 +4001,9 @@ FRESULT f_lseek (
|
||||
|
||||
/* Normal Seek */
|
||||
{
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */
|
||||
#endif
|
||||
if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */
|
||||
ofs = fp->obj.objsize;
|
||||
}
|
||||
@@ -4016,7 +4013,7 @@ FRESULT f_lseek (
|
||||
bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */
|
||||
if (ifptr > 0 &&
|
||||
(ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
|
||||
fp->fptr = (ifptr - 1) & ~(bcs - 1); /* start from the current cluster */
|
||||
fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */
|
||||
ofs -= fp->fptr;
|
||||
clst = fp->clust;
|
||||
} else { /* When seek to back cluster, */
|
||||
@@ -4033,20 +4030,25 @@ FRESULT f_lseek (
|
||||
}
|
||||
if (clst != 0) {
|
||||
while (ofs > bcs) { /* Cluster following loop */
|
||||
ofs -= bcs; fp->fptr += bcs;
|
||||
#if !_FS_READONLY
|
||||
if (fp->flag & FA_WRITE) { /* Check if in write mode or not */
|
||||
clst = create_chain(&fp->obj, clst); /* Force stretch if in write mode */
|
||||
if (clst == 0) { /* When disk gets full, clip file size */
|
||||
ofs = bcs; break;
|
||||
if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */
|
||||
fp->obj.objsize = fp->fptr;
|
||||
fp->flag |= FA_MODIFIED;
|
||||
}
|
||||
clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */
|
||||
if (clst == 0) { /* Clip file size in case of disk full */
|
||||
ofs = 0; break;
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */
|
||||
}
|
||||
if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
|
||||
if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
|
||||
fp->clust = clst;
|
||||
fp->fptr += bcs;
|
||||
ofs -= bcs;
|
||||
}
|
||||
fp->fptr += ofs;
|
||||
if (ofs % SS(fs)) {
|
||||
@@ -4056,26 +4058,22 @@ FRESULT f_lseek (
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */
|
||||
fp->obj.objsize = fp->fptr;
|
||||
fp->flag |= FA_MODIFIED;
|
||||
}
|
||||
if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */
|
||||
#if !_FS_TINY
|
||||
#if !_FS_READONLY
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) { /* Fill sector cache */
|
||||
ABORT(fs, FR_DISK_ERR);
|
||||
}
|
||||
if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
|
||||
#endif
|
||||
fp->sect = nsect;
|
||||
}
|
||||
#if !_FS_READONLY
|
||||
if (fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */
|
||||
fp->obj.objsize = fp->fptr;
|
||||
fp->flag |= FA_MODIFIED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
LEAVE_FF(fs, res);
|
||||
@@ -4101,7 +4099,7 @@ FRESULT f_opendir (
|
||||
|
||||
if (!dp) return FR_INVALID_OBJECT;
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
obj = &dp->obj;
|
||||
res = find_volume(&path, &fs, 0);
|
||||
if (res == FR_OK) {
|
||||
@@ -4166,7 +4164,7 @@ FRESULT f_closedir (
|
||||
FATFS *fs;
|
||||
|
||||
|
||||
res = validate(dp, &fs);
|
||||
res = validate(&dp->obj, &fs); /* Check validity of the file object */
|
||||
if (res == FR_OK) {
|
||||
#if _FS_LOCK != 0
|
||||
if (dp->obj.lockid) { /* Decrement sub-directory open counter */
|
||||
@@ -4201,7 +4199,7 @@ FRESULT f_readdir (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = validate(dp, &fs); /* Check validity of the object */
|
||||
res = validate(&dp->obj, &fs); /* Check validity of the directory object */
|
||||
if (res == FR_OK) {
|
||||
if (!fno) {
|
||||
res = dir_sdi(dp, 0); /* Rewind the directory object */
|
||||
@@ -4289,7 +4287,7 @@ FRESULT f_stat (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &dj.obj.fs, 0);
|
||||
if (res == FR_OK) {
|
||||
INIT_NAMBUF(dj.obj.fs);
|
||||
@@ -4328,7 +4326,7 @@ FRESULT f_getfree (
|
||||
_FDID obj;
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, 0);
|
||||
if (res == FR_OK) {
|
||||
*fatfs = fs; /* Return ptr to the fs object */
|
||||
@@ -4410,8 +4408,8 @@ FRESULT f_truncate (
|
||||
DWORD ncl;
|
||||
|
||||
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
|
||||
if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
|
||||
if (fp->obj.objsize > fp->fptr) {
|
||||
@@ -4434,7 +4432,7 @@ FRESULT f_truncate (
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) {
|
||||
res = FR_DISK_ERR;
|
||||
} else {
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -4465,7 +4463,7 @@ FRESULT f_unlink (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, FA_WRITE);
|
||||
dj.obj.fs = fs;
|
||||
if (res == FR_OK) {
|
||||
@@ -4559,7 +4557,7 @@ FRESULT f_mkdir (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, FA_WRITE);
|
||||
dj.obj.fs = fs;
|
||||
if (res == FR_OK) {
|
||||
@@ -4653,7 +4651,7 @@ FRESULT f_rename (
|
||||
|
||||
|
||||
get_ldnumber(&path_new); /* Ignore drive number of new name */
|
||||
res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive number of the old object */
|
||||
res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */
|
||||
if (res == FR_OK) {
|
||||
djo.obj.fs = fs;
|
||||
INIT_NAMBUF(fs);
|
||||
@@ -4671,7 +4669,9 @@ FRESULT f_rename (
|
||||
mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */
|
||||
mem_cpy(&djn, &djo, sizeof djo);
|
||||
res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
|
||||
if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */
|
||||
if (res == FR_OK) { /* Is new name already in use by any other object? */
|
||||
res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
|
||||
}
|
||||
if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
|
||||
res = dir_register(&djn); /* Register the new entry */
|
||||
if (res == FR_OK) {
|
||||
@@ -4690,7 +4690,9 @@ FRESULT f_rename (
|
||||
mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */
|
||||
mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */
|
||||
res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */
|
||||
if (res == FR_OK) res = FR_EXIST; /* Is new name already in use? */
|
||||
if (res == FR_OK) { /* Is new name already in use by any other object? */
|
||||
res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
|
||||
}
|
||||
if (res == FR_NO_FILE) { /* It is a valid path and no name collision */
|
||||
res = dir_register(&djn); /* Register the new entry */
|
||||
if (res == FR_OK) {
|
||||
@@ -4753,7 +4755,7 @@ FRESULT f_chmod (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */
|
||||
res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
|
||||
dj.obj.fs = fs;
|
||||
if (res == FR_OK) {
|
||||
INIT_NAMBUF(fs);
|
||||
@@ -4797,7 +4799,7 @@ FRESULT f_utime (
|
||||
DEF_NAMBUF
|
||||
|
||||
|
||||
res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive number */
|
||||
res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */
|
||||
dj.obj.fs = fs;
|
||||
if (res == FR_OK) {
|
||||
INIT_NAMBUF(fs);
|
||||
@@ -4845,7 +4847,7 @@ FRESULT f_getlabel (
|
||||
WCHAR w;
|
||||
#endif
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&path, &fs, 0);
|
||||
|
||||
/* Get volume label */
|
||||
@@ -4933,7 +4935,7 @@ FRESULT f_setlabel (
|
||||
static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F";
|
||||
|
||||
|
||||
/* Get logical drive number */
|
||||
/* Get logical drive */
|
||||
res = find_volume(&label, &fs, FA_WRITE);
|
||||
if (res != FR_OK) LEAVE_FF(fs, res);
|
||||
dj.obj.fs = fs;
|
||||
@@ -4942,7 +4944,7 @@ FRESULT f_setlabel (
|
||||
for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */
|
||||
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type == FS_EXFAT) { /* At the exFAT */
|
||||
if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */
|
||||
for (i = j = 0; i < slen; ) { /* Create volume label in directory form */
|
||||
w = label[i++];
|
||||
#if !_LFN_UNICODE
|
||||
@@ -4959,7 +4961,7 @@ FRESULT f_setlabel (
|
||||
slen = j;
|
||||
} else
|
||||
#endif
|
||||
{ /* At the FAT12/16/32 */
|
||||
{ /* On the FAT12/16/32 volume */
|
||||
for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */
|
||||
if (slen) { /* Is there a volume label to be set? */
|
||||
dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */
|
||||
@@ -5000,7 +5002,7 @@ FRESULT f_setlabel (
|
||||
res = dir_read(&dj, 1); /* Get volume label entry */
|
||||
if (res == FR_OK) {
|
||||
if (_FS_EXFAT && fs->fs_type == FS_EXFAT) {
|
||||
dj.dir[XDIR_NumLabel] = slen / 2; /* Change the volume label */
|
||||
dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */
|
||||
mem_cpy(dj.dir + XDIR_Label, dirvn, slen);
|
||||
} else {
|
||||
if (slen) {
|
||||
@@ -5020,7 +5022,7 @@ FRESULT f_setlabel (
|
||||
mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */
|
||||
if (_FS_EXFAT && fs->fs_type == FS_EXFAT) {
|
||||
dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */
|
||||
dj.dir[XDIR_NumLabel] = slen / 2;
|
||||
dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2);
|
||||
mem_cpy(dj.dir + XDIR_Label, dirvn, slen);
|
||||
} else {
|
||||
dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */
|
||||
@@ -5058,8 +5060,8 @@ FRESULT f_expand (
|
||||
DWORD n, clst, stcl, scl, ncl, tcl, lclst;
|
||||
|
||||
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
|
||||
if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
|
||||
#if _FS_EXFAT
|
||||
if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */
|
||||
@@ -5153,8 +5155,8 @@ FRESULT f_forward (
|
||||
|
||||
|
||||
*bf = 0; /* Clear transfer byte counter */
|
||||
res = validate(fp, &fs); /* Check validity of the object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */
|
||||
res = validate(&fp->obj, &fs); /* Check validity of the file object */
|
||||
if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
|
||||
if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
|
||||
|
||||
remain = fp->obj.objsize - fp->fptr;
|
||||
@@ -5183,7 +5185,7 @@ FRESULT f_forward (
|
||||
#if !_FS_READONLY
|
||||
if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */
|
||||
if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
fp->flag &= ~FA_DIRTY;
|
||||
fp->flag &= (BYTE)~FA_DIRTY;
|
||||
}
|
||||
#endif
|
||||
if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
|
||||
@@ -5218,14 +5220,14 @@ FRESULT f_mkfs (
|
||||
{
|
||||
const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */
|
||||
const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */
|
||||
static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4KS unit) */
|
||||
static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128KS unit) */
|
||||
static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */
|
||||
static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */
|
||||
BYTE fmt, sys, *buf, *pte, pdrv, part;
|
||||
WORD ss;
|
||||
DWORD n, pau, n_clst, sz_blk, sect, szb_buf, sz_buf;
|
||||
DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n;
|
||||
DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */
|
||||
DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */
|
||||
UINT i, ns;
|
||||
UINT i;
|
||||
int vol;
|
||||
DSTATUS stat;
|
||||
#if _USE_TRIM || _FS_EXFAT
|
||||
@@ -5238,34 +5240,33 @@ FRESULT f_mkfs (
|
||||
if (vol < 0) return FR_INVALID_DRIVE;
|
||||
if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear mounted volume */
|
||||
pdrv = LD2PD(vol); /* Physical drive */
|
||||
part = LD2PT(vol); /* Partition (0:create as new, 1-4:get by partition table) */
|
||||
part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */
|
||||
|
||||
/* Check physical drive status */
|
||||
stat = disk_initialize(pdrv);
|
||||
if (stat & STA_NOINIT) return FR_NOT_READY;
|
||||
if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
|
||||
if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1;
|
||||
if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */
|
||||
#if _MAX_SS != _MIN_SS /* Get sector size of the medium */
|
||||
if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
|
||||
if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
|
||||
#else
|
||||
ss = _MAX_SS;
|
||||
#endif
|
||||
if ((au != 0 && au < ss) || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */
|
||||
au /= ss; /* Cluster size in byte to in sector */
|
||||
if (au > 32768) return FR_INVALID_PARAMETER;
|
||||
if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */
|
||||
au /= ss; /* Cluster size in unit of sector */
|
||||
|
||||
/* Set size and pointer of the working buffer */
|
||||
buf = (BYTE*)work; /* Use given working buffer */
|
||||
if (len < ss) return FR_MKFS_ABORTED;
|
||||
szb_buf = len & ~(ss - 1); /* Round-down by sector size [byte] */
|
||||
sz_buf = szb_buf / ss; /* Size of sector buffer [sector] */
|
||||
/* Get working buffer */
|
||||
buf = (BYTE*)work; /* Working buffer */
|
||||
sz_buf = len / ss; /* Size of working buffer (sector) */
|
||||
szb_buf = sz_buf * ss; /* Size of working buffer (byte) */
|
||||
if (!szb_buf) return FR_MKFS_ABORTED;
|
||||
|
||||
/* Determine where the volume to be located (b_vol, sz_vol) */
|
||||
if (_MULTI_PARTITION && part != 0) {
|
||||
/* Get partition information from partition table in the MBR */
|
||||
if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */
|
||||
if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check MBR is valid */
|
||||
if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */
|
||||
pte = buf + (MBR_Table + (part - 1) * SZ_PTE);
|
||||
if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */
|
||||
b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */
|
||||
@@ -5277,16 +5278,16 @@ FRESULT f_mkfs (
|
||||
if (sz_vol < b_vol) return FR_MKFS_ABORTED;
|
||||
sz_vol -= b_vol; /* Volume size */
|
||||
}
|
||||
if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check volume size (>=128s) */
|
||||
if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */
|
||||
|
||||
/* Pre-determine the FAT type by argument */
|
||||
/* Pre-determine the FAT type */
|
||||
do {
|
||||
if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */
|
||||
if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au >= 256) { /* exFAT only, vol >= 64Ms or au >= 256s ? */
|
||||
if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */
|
||||
fmt = FS_EXFAT; break;
|
||||
}
|
||||
}
|
||||
if (au >= 256) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */
|
||||
if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */
|
||||
if (opt & FM_FAT32) { /* FAT32 possible? */
|
||||
if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */
|
||||
fmt = FS_FAT32; break;
|
||||
@@ -5298,7 +5299,7 @@ FRESULT f_mkfs (
|
||||
|
||||
#if _FS_EXFAT
|
||||
if (fmt == FS_EXFAT) { /* Create an exFAT volume */
|
||||
DWORD sum, szb_bit, szb_case;
|
||||
DWORD szb_bit, szb_case, sum, nb, cl;
|
||||
WCHAR ch, si;
|
||||
UINT j, st;
|
||||
BYTE b;
|
||||
@@ -5311,20 +5312,19 @@ FRESULT f_mkfs (
|
||||
/* Determine FAT location, data location and number of clusters */
|
||||
if (!au) { /* au auto-selection */
|
||||
au = 8;
|
||||
if (sz_vol >= 0x80000) au = 64; /* >= 512KS */
|
||||
if (sz_vol >= 0x4000000) au = 256; /* >= 64MS */
|
||||
if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */
|
||||
if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */
|
||||
}
|
||||
b_fat = b_vol + 32; /* FAT start at offset 32 */
|
||||
sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Numbef of FAT sectors */
|
||||
sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */
|
||||
b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */
|
||||
if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */
|
||||
n_clst = (sz_vol - (b_data - b_vol)) / au; /* Nunber of clusters */
|
||||
n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */
|
||||
if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */
|
||||
if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */
|
||||
|
||||
szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */
|
||||
tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of bitmap clusters */
|
||||
tbl[2] = 1; /* Number of rootdir clusters */
|
||||
tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */
|
||||
|
||||
/* Create a compressed up-case table */
|
||||
sect = b_data + au * tbl[0]; /* Table start sector */
|
||||
@@ -5355,43 +5355,49 @@ FRESULT f_mkfs (
|
||||
sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
|
||||
i += 2; szb_case += 2;
|
||||
if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */
|
||||
ns = (i + ss - 1) / ss;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns; i = 0;
|
||||
n = (i + ss - 1) / ss;
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
|
||||
sect += n; i = 0;
|
||||
}
|
||||
} while (si);
|
||||
tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case clusters */
|
||||
tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */
|
||||
tbl[2] = 1; /* Number of root dir clusters */
|
||||
|
||||
/* Initialize the allocation bitmap */
|
||||
mem_set(buf, 0, szb_buf); /* Set in-use flags of bitmap, up-case and root dir */
|
||||
for (i = 0, n = tbl[0] + tbl[1] + tbl[2]; n >= 8; buf[i++] = 0xFF, n -= 8) ;
|
||||
for (b = 1; n; buf[i] |= b, b <<= 1, n--) ;
|
||||
sect = b_data; n = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of the sectors */
|
||||
do { /* Fill allocation bitmap sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
|
||||
nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */
|
||||
do {
|
||||
mem_set(buf, 0, szb_buf);
|
||||
for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ;
|
||||
for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ;
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
/* Initialize the FAT */
|
||||
st_qword(buf, 0xFFFFFFFFFFFFFFF8); /* Entry 0 and 1 */
|
||||
for (j = 0, i = 2; j < 3; j++) { /* Set entries of bitmap, up-case and root dir */
|
||||
for (n = tbl[j]; n; n--) {
|
||||
st_dword(buf + i * 4, (n >= 2) ? i + 1 : 0xFFFFFFFF);
|
||||
i++;
|
||||
sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */
|
||||
j = nb = cl = 0;
|
||||
do {
|
||||
mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */
|
||||
if (cl == 0) { /* Set entry 0 and 1 */
|
||||
st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
|
||||
st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
|
||||
}
|
||||
do { /* Create chains of bitmap, up-case and root dir */
|
||||
while (nb && i < szb_buf) { /* Create a chain */
|
||||
st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
|
||||
i += 4; cl++; nb--;
|
||||
}
|
||||
sect = b_fat; n = sz_fat; /* Start of FAT and number of the sectors */
|
||||
do { /* Fill FAT sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
if (!nb && j < 3) nb = tbl[j++]; /* Next chain */
|
||||
} while (nb && i < szb_buf);
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
/* Initialize the root directory */
|
||||
mem_set(buf, 0, ss);
|
||||
mem_set(buf, 0, szb_buf);
|
||||
buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */
|
||||
buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */
|
||||
st_dword(buf + SZDIRE * 1 + 20, 2);
|
||||
@@ -5400,13 +5406,13 @@ FRESULT f_mkfs (
|
||||
st_dword(buf + SZDIRE * 2 + 4, sum);
|
||||
st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]);
|
||||
st_dword(buf + SZDIRE * 2 + 24, szb_case);
|
||||
sect = b_data + au * (tbl[0] + tbl[1]); n = au; /* Start of directory and number of the sectors */
|
||||
do { /* Fill root direcotry sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns;
|
||||
sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */
|
||||
do { /* Fill root directory sectors */
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
|
||||
/* Create two set of the exFAT VBR blocks */
|
||||
sect = b_vol;
|
||||
@@ -5452,7 +5458,7 @@ FRESULT f_mkfs (
|
||||
}
|
||||
|
||||
} else
|
||||
#endif
|
||||
#endif /* _FS_EXFAT */
|
||||
{ /* Create an FAT12/16/32 volume */
|
||||
do {
|
||||
pau = au;
|
||||
@@ -5535,14 +5541,14 @@ FRESULT f_mkfs (
|
||||
st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
|
||||
buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
|
||||
st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
|
||||
buf[BPB_NumFATs] = n_fats; /* Number of FATs */
|
||||
buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */
|
||||
st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */
|
||||
if (sz_vol < 0x10000) {
|
||||
st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
|
||||
} else {
|
||||
st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 12-bit LBA */
|
||||
st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */
|
||||
}
|
||||
buf[BPB_Media] = 0xF8; /* Media descriptor */
|
||||
buf[BPB_Media] = 0xF8; /* Media descriptor byte */
|
||||
st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
|
||||
st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */
|
||||
st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */
|
||||
@@ -5579,8 +5585,8 @@ FRESULT f_mkfs (
|
||||
}
|
||||
|
||||
/* Initialize FAT area */
|
||||
mem_set(buf, 0, szb_buf);
|
||||
sect = b_fat; /* Start sector */
|
||||
mem_set(buf, 0, (UINT)szb_buf);
|
||||
sect = b_fat; /* FAT start sector */
|
||||
for (i = 0; i < n_fats; i++) { /* Initialize FATs each */
|
||||
if (fmt == FS_FAT32) {
|
||||
st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */
|
||||
@@ -5589,22 +5595,22 @@ FRESULT f_mkfs (
|
||||
} else {
|
||||
st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */
|
||||
}
|
||||
n = sz_fat; /* Sector count of a FAT */
|
||||
nsect = sz_fat; /* Number of FAT sectors */
|
||||
do { /* Fill FAT sectors */
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns;
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
|
||||
mem_set(buf, 0, ss);
|
||||
} while (n -= ns);
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
}
|
||||
|
||||
/* Initialize root directory (fill with zero) */
|
||||
n = (fmt == FS_FAT32) ? pau : sz_dir; /* Sector count of root directory */
|
||||
nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */
|
||||
do {
|
||||
ns = (n > sz_buf) ? sz_buf : n;
|
||||
if (disk_write(pdrv, buf, sect, ns) != RES_OK) return FR_DISK_ERR;
|
||||
sect += ns;
|
||||
} while (n -= ns);
|
||||
n = (nsect > sz_buf) ? sz_buf : nsect;
|
||||
if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR;
|
||||
sect += n; nsect -= n;
|
||||
} while (nsect);
|
||||
}
|
||||
|
||||
/* Determine system ID in the partition table */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*----------------------------------------------------------------------------/
|
||||
/ FatFs - Generic FAT file system module R0.12a /
|
||||
/ FatFs - Generic FAT file system module R0.12b /
|
||||
/-----------------------------------------------------------------------------/
|
||||
/
|
||||
/ Copyright (C) 2016, ChaN, all right reserved.
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
|
||||
#ifndef _FATFS
|
||||
#define _FATFS 80186 /* Revision ID */
|
||||
#define _FATFS 68020 /* Revision ID */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@@ -159,7 +159,7 @@ typedef struct {
|
||||
/* File object structure (FIL) */
|
||||
|
||||
typedef struct {
|
||||
_FDID obj; /* Object identifier */
|
||||
_FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */
|
||||
BYTE flag; /* File status flags */
|
||||
BYTE err; /* Abort flag (error code) */
|
||||
FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/ FatFs - FAT file system module configuration file
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 80186 /* Revision ID */
|
||||
#define _FFCONF 68020 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
@@ -204,14 +204,14 @@
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS bytes.
|
||||
/ At the tiny configuration, size of file object (FIL) is reduced _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. */
|
||||
|
||||
|
||||
#define _FS_EXFAT 0
|
||||
/* This option switches support of exFAT file system in addition to the traditional
|
||||
/ FAT file system. (0:Disable or 1:Enable) To enable exFAT, also LFN must be enabled.
|
||||
/* 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. */
|
||||
|
||||
|
||||
@@ -259,7 +259,9 @@
|
||||
/ 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
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
/ included somewhere in the scope of ff.h. */
|
||||
|
||||
/* #include <windows.h> // O/S definitions */
|
||||
|
||||
|
||||
/*--- End of configuration options ---*/
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "../../types.h"
|
||||
@@ -1,9 +1,5 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "../../types.h"
|
||||
|
||||
void ioDelay(u32 us);
|
||||
void waitcycles(u32 us);
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
.arm
|
||||
.global ioDelay
|
||||
.type ioDelay STT_FUNC
|
||||
.global waitcycles
|
||||
.type waitcycles STT_FUNC
|
||||
|
||||
@ioDelay ( u32 us )
|
||||
ioDelay:
|
||||
ldr r1, =0x18000000 @ VRAM
|
||||
1:
|
||||
@ Loop doing uncached reads from VRAM to make loop timing more reliable
|
||||
ldr r2, [r1]
|
||||
subs r0, #1
|
||||
bgt 1b
|
||||
bx lr
|
||||
@waitcycles ( u32 us )
|
||||
waitcycles:
|
||||
PUSH {R0-R2,LR}
|
||||
STR R0, [SP,#4]
|
||||
waitcycles_loop:
|
||||
LDR R3, [SP,#4]
|
||||
SUBS R2, R3, #1
|
||||
STR R2, [SP,#4]
|
||||
CMP R3, #0
|
||||
BNE waitcycles_loop
|
||||
POP {R0-R2,PC}
|
||||
|
||||
@@ -1,30 +1,55 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* Copyright (c) 2014-2015, Normmatt
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms
|
||||
* of the GNU General Public License Version 2, as described below:
|
||||
*
|
||||
* This file is free software: you may copy, redistribute and/or modify
|
||||
* it under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This file 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/.
|
||||
*/
|
||||
|
||||
#include "sdmmc.h"
|
||||
#include "delay.h"
|
||||
|
||||
struct mmcdevice handleNAND;
|
||||
struct mmcdevice handleSD;
|
||||
static struct mmcdevice handleNAND;
|
||||
static struct mmcdevice handleSD;
|
||||
|
||||
static inline u16 sdmmc_read16(u16 reg) {
|
||||
return *(vu16*)(SDMMC_BASE + reg);
|
||||
static inline u16 sdmmc_read16(u16 reg)
|
||||
{
|
||||
return *(vu16 *)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write16(u16 reg, u16 val) {
|
||||
*(vu16*)(SDMMC_BASE + reg) = val;
|
||||
static inline void sdmmc_write16(u16 reg, u16 val)
|
||||
{
|
||||
*(vu16 *)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline u32 sdmmc_read32(u16 reg) {
|
||||
return *(vu32*)(SDMMC_BASE + reg);
|
||||
static inline u32 sdmmc_read32(u16 reg)
|
||||
{
|
||||
return *(vu32 *)(SDMMC_BASE + reg);
|
||||
}
|
||||
|
||||
static inline void sdmmc_write32(u16 reg, u32 val) {
|
||||
*(vu32*)(SDMMC_BASE + reg) = val;
|
||||
static inline void sdmmc_write32(u16 reg, u32 val)
|
||||
{
|
||||
*(vu32 *)(SDMMC_BASE + reg) = val;
|
||||
}
|
||||
|
||||
static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set) {
|
||||
static inline void sdmmc_mask16(u16 reg, const u16 clear, const u16 set)
|
||||
{
|
||||
u16 val = sdmmc_read16(reg);
|
||||
val &= ~clear;
|
||||
val |= set;
|
||||
@@ -38,172 +63,215 @@ static inline void setckl(u32 data)
|
||||
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
|
||||
}
|
||||
|
||||
|
||||
mmcdevice *getMMCDevice(int drive)
|
||||
{
|
||||
if(drive==0) return &handleNAND;
|
||||
if(drive == 0) return &handleNAND;
|
||||
return &handleSD;
|
||||
}
|
||||
|
||||
static u32 __attribute__((noinline)) geterror(struct mmcdevice *ctx)
|
||||
static int geterror(struct mmcdevice *ctx)
|
||||
{
|
||||
return (ctx->error << 29) >> 31;
|
||||
return (int)((ctx->error << 29) >> 31);
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) inittarget(struct mmcdevice *ctx)
|
||||
static void inittarget(struct mmcdevice *ctx)
|
||||
{
|
||||
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
|
||||
sdmmc_mask16(REG_SDPORTSEL, 0x3, (u16)ctx->devicenumber);
|
||||
setckl(ctx->clk);
|
||||
if (ctx->SDOPT == 0) {
|
||||
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||
} else {
|
||||
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||
}
|
||||
|
||||
if(ctx->SDOPT == 0) sdmmc_mask16(REG_SDOPT, 0, 0x8000);
|
||||
else sdmmc_mask16(REG_SDOPT, 0x8000, 0);
|
||||
}
|
||||
|
||||
static void __attribute__((noinline)) sdmmc_send_command(struct mmcdevice *ctx, u32 cmd, u32 args)
|
||||
{
|
||||
bool getSDRESP = (cmd << 15) >> 31;
|
||||
u32 getSDRESP = (cmd << 15) >> 31;
|
||||
u16 flags = (cmd << 15) >> 31;
|
||||
const bool readdata = cmd & 0x20000;
|
||||
const bool writedata = cmd & 0x40000;
|
||||
const int readdata = cmd & 0x20000;
|
||||
const int writedata = cmd & 0x40000;
|
||||
|
||||
if (readdata || writedata)
|
||||
if(readdata || writedata)
|
||||
flags |= TMIO_STAT0_DATAEND;
|
||||
|
||||
ctx->error = 0;
|
||||
while (sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0,0);
|
||||
sdmmc_write16(REG_SDIRMASK1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
sdmmc_mask16(REG_SDDATACTL32,0x1800,0);
|
||||
|
||||
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1,args >> 16);
|
||||
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
|
||||
while((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
|
||||
sdmmc_write16(REG_SDIRMASK0, 0);
|
||||
sdmmc_write16(REG_SDIRMASK1, 0);
|
||||
sdmmc_write16(REG_SDSTATUS0, 0);
|
||||
sdmmc_write16(REG_SDSTATUS1, 0);
|
||||
sdmmc_mask16(REG_DATACTL32, 0x1800, 0);
|
||||
sdmmc_write16(REG_SDCMDARG0, args & 0xFFFF);
|
||||
sdmmc_write16(REG_SDCMDARG1, args >> 16);
|
||||
sdmmc_write16(REG_SDCMD, cmd & 0xFFFF);
|
||||
|
||||
u32 size = ctx->size;
|
||||
vu8 *dataPtr = ctx->data;
|
||||
u8 *rDataPtr = ctx->rData;
|
||||
const u8 *tDataPtr = ctx->tData;
|
||||
|
||||
bool useBuf = ( NULL != dataPtr );
|
||||
bool rUseBuf = rDataPtr != NULL;
|
||||
bool tUseBuf = tDataPtr != NULL;
|
||||
|
||||
u16 status0 = 0;
|
||||
while(true) {
|
||||
u16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
if (status1 & TMIO_STAT1_RXRDY) {
|
||||
if (readdata && useBuf) {
|
||||
while(true)
|
||||
{
|
||||
vu16 status1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
vu16 ctl32 = sdmmc_read16(REG_DATACTL32);
|
||||
if((ctl32 & 0x100))
|
||||
{
|
||||
if(readdata)
|
||||
{
|
||||
if(rUseBuf)
|
||||
{
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_RXRDY);
|
||||
if (size > 0x1FF) {
|
||||
for(int i = 0; i<0x200; i+=2) {
|
||||
u16 data = sdmmc_read16(REG_SDFIFO);
|
||||
*dataPtr++ = data & 0xFF;
|
||||
*dataPtr++ = data >> 8;
|
||||
if(size > 0x1FF)
|
||||
{
|
||||
//Gabriel Marcano: This implementation doesn't assume alignment.
|
||||
//I've removed the alignment check doen with former rUseBuf32 as a result
|
||||
for(int i = 0; i < 0x200; i += 4)
|
||||
{
|
||||
u32 data = sdmmc_read32(REG_SDFIFO32);
|
||||
*rDataPtr++ = data;
|
||||
*rDataPtr++ = data >> 8;
|
||||
*rDataPtr++ = data >> 16;
|
||||
*rDataPtr++ = data >> 24;
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status1 & TMIO_STAT1_TXRQ) {
|
||||
if (writedata && useBuf) {
|
||||
sdmmc_mask16(REG_DATACTL32, 0x800, 0);
|
||||
}
|
||||
}
|
||||
if(!(ctl32 & 0x200))
|
||||
{
|
||||
if(writedata)
|
||||
{
|
||||
if(tUseBuf)
|
||||
{
|
||||
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
|
||||
//sdmmc_write16(REG_SDSTATUS1,~TMIO_STAT1_TXRQ);
|
||||
if (size > 0x1FF) {
|
||||
for (int i = 0; i<0x200; i+=2) {
|
||||
u16 data = *dataPtr++;
|
||||
data |= *dataPtr++ << 8;
|
||||
sdmmc_write16(REG_SDFIFO, data);
|
||||
if(size > 0x1FF)
|
||||
{
|
||||
for(int i = 0; i < 0x200; i += 4)
|
||||
{
|
||||
u32 data = *tDataPtr++;
|
||||
data |= (u32)*tDataPtr++ << 8;
|
||||
data |= (u32)*tDataPtr++ << 16;
|
||||
data |= (u32)*tDataPtr++ << 24;
|
||||
sdmmc_write32(REG_SDFIFO32, data);
|
||||
}
|
||||
size -= 0x200;
|
||||
}
|
||||
}
|
||||
|
||||
sdmmc_mask16(REG_DATACTL32, 0x1000, 0);
|
||||
}
|
||||
if (status1 & TMIO_MASK_GW) {
|
||||
}
|
||||
if(status1 & TMIO_MASK_GW)
|
||||
{
|
||||
ctx->error |= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(status1 & TMIO_STAT1_CMD_BUSY)) {
|
||||
if(!(status1 & TMIO_STAT1_CMD_BUSY))
|
||||
{
|
||||
status0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
if(sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
|
||||
{
|
||||
ctx->error |= 0x1;
|
||||
if (status0 & TMIO_STAT0_DATAEND)
|
||||
}
|
||||
if(status0 & TMIO_STAT0_DATAEND)
|
||||
{
|
||||
ctx->error |= 0x2;
|
||||
}
|
||||
|
||||
if ((status0 & flags) == flags)
|
||||
if((status0 & flags) == flags)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
|
||||
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
|
||||
sdmmc_write16(REG_SDSTATUS0,0);
|
||||
sdmmc_write16(REG_SDSTATUS1,0);
|
||||
sdmmc_write16(REG_SDSTATUS0, 0);
|
||||
sdmmc_write16(REG_SDSTATUS1, 0);
|
||||
|
||||
if (getSDRESP != 0) {
|
||||
ctx->ret[0] = (u32)sdmmc_read16(REG_SDRESP0) | (u32)(sdmmc_read16(REG_SDRESP1) << 16);
|
||||
ctx->ret[1] = (u32)sdmmc_read16(REG_SDRESP2) | (u32)(sdmmc_read16(REG_SDRESP3) << 16);
|
||||
ctx->ret[2] = (u32)sdmmc_read16(REG_SDRESP4) | (u32)(sdmmc_read16(REG_SDRESP5) << 16);
|
||||
ctx->ret[3] = (u32)sdmmc_read16(REG_SDRESP6) | (u32)(sdmmc_read16(REG_SDRESP7) << 16);
|
||||
if(getSDRESP != 0)
|
||||
{
|
||||
ctx->ret[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16));
|
||||
ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16));
|
||||
ctx->ret[2] = (u32)(sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16));
|
||||
ctx->ret[3] = (u32)(sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16));
|
||||
}
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in)
|
||||
int __attribute__((noinline)) sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, const u8 *in)
|
||||
{
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
if(handleSD.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = in;
|
||||
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||
handleSD.tData = in;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x52C19,sector_no);
|
||||
sdmmc_send_command(&handleSD, 0x52C19, sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
int __attribute__((noinline)) sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, u8 *out)
|
||||
{
|
||||
if (handleSD.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
if(handleSD.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handleSD);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
handleSD.data = out;
|
||||
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||
handleSD.rData = out;
|
||||
handleSD.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleSD,0x33C12,sector_no);
|
||||
sdmmc_send_command(&handleSD, 0x33C12, sector_no);
|
||||
return geterror(&handleSD);
|
||||
}
|
||||
|
||||
u32 __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out)
|
||||
int __attribute__((noinline)) sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, u8 *out)
|
||||
{
|
||||
if (handleNAND.isSDHC == 0)
|
||||
sector_no <<= 9;
|
||||
if(handleNAND.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handleNAND);
|
||||
sdmmc_write16(REG_SDSTOP,0x100);
|
||||
|
||||
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
|
||||
|
||||
handleNAND.data = out;
|
||||
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||
handleNAND.rData = out;
|
||||
handleNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleNAND,0x33C12,sector_no);
|
||||
sdmmc_send_command(&handleNAND, 0x33C12, sector_no);
|
||||
inittarget(&handleSD);
|
||||
return geterror(&handleNAND);
|
||||
}
|
||||
|
||||
static u32 calcSDSize(u8* csd, int type)
|
||||
/*
|
||||
int __attribute__((noinline)) sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, const u8 *in) //experimental
|
||||
{
|
||||
if(handleNAND.isSDHC == 0) sector_no <<= 9;
|
||||
inittarget(&handleNAND);
|
||||
sdmmc_write16(REG_SDSTOP, 0x100);
|
||||
sdmmc_write16(REG_SDBLKCOUNT32, numsectors);
|
||||
sdmmc_write16(REG_SDBLKLEN32, 0x200);
|
||||
sdmmc_write16(REG_SDBLKCOUNT, numsectors);
|
||||
handleNAND.tData = in;
|
||||
handleNAND.size = numsectors << 9;
|
||||
sdmmc_send_command(&handleNAND, 0x52C19, sector_no);
|
||||
inittarget(&handleSD);
|
||||
return geterror(&handleNAND);
|
||||
}
|
||||
*/
|
||||
|
||||
static u32 calcSDSize(u8 *csd, int type)
|
||||
{
|
||||
u32 result = 0;
|
||||
if (type == -1) type = csd[14] >> 6;
|
||||
switch (type) {
|
||||
if(type == -1) type = csd[14] >> 6;
|
||||
switch(type)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
u32 block_len = csd[9] & 0xf;
|
||||
u32 block_len = csd[9] & 0xF;
|
||||
block_len = 1u << block_len;
|
||||
u32 mult = (u32)(csd[4] >> 7) | (u32)((csd[5] & 3) << 1);
|
||||
u32 mult = (u32)((csd[4] >> 7) | ((csd[5] & 3) << 1));
|
||||
mult = 1u << (mult + 2);
|
||||
result = csd[8] & 3;
|
||||
result = (result << 8) | csd[7];
|
||||
@@ -212,18 +280,42 @@ static u32 calcSDSize(u8* csd, int type)
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
result = csd[7] & 0x3f;
|
||||
result = csd[7] & 0x3F;
|
||||
result = (result << 8) | csd[6];
|
||||
result = (result << 8) | csd[5];
|
||||
result = (result + 1) * 1024;
|
||||
break;
|
||||
default:
|
||||
break; //Do nothing otherwise
|
||||
break; //Do nothing otherwise FIXME perhaps return some error?
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void InitSD()
|
||||
{
|
||||
*(vu16 *)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(vu16 *)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
*(vu16 *)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
*(vu16 *)0x100060D8 = (*(vu16 *)0x100060D8 & 0xFFDD) | 2;
|
||||
*(vu16 *)0x10006100 &= 0xFFFFu; //SDDATACTL32
|
||||
*(vu16 *)0x100060D8 &= 0xFFDFu; //SDDATACTL
|
||||
*(vu16 *)0x10006104 = 512; //SDBLKLEN32
|
||||
*(vu16 *)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(vu16 *)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(vu16 *)0x100060E0 |= 1u; //SDRESET
|
||||
*(vu16 *)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(vu16 *)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(vu16 *)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16 *)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16 *)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
*(vu16 *)0x10006024 = 0x20;
|
||||
*(vu16 *)0x10006028 = 0x40EE;
|
||||
*(vu16 *)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(vu16 *)0x10006026 = 512; //SDBLKLEN
|
||||
*(vu16 *)0x10006008 = 0; //SDSTOP
|
||||
}
|
||||
|
||||
static int Nand_Init()
|
||||
{
|
||||
//NAND
|
||||
handleNAND.isSDHC = 0;
|
||||
@@ -233,80 +325,50 @@ static void InitSD()
|
||||
handleNAND.clk = 0x80;
|
||||
handleNAND.devicenumber = 1;
|
||||
|
||||
//SD
|
||||
handleSD.isSDHC = 0;
|
||||
handleSD.SDOPT = 0;
|
||||
handleSD.res = 0;
|
||||
handleSD.initarg = 0;
|
||||
handleSD.clk = 0x80;
|
||||
handleSD.devicenumber = 0;
|
||||
|
||||
*(vu16*)0x10006100 &= 0xF7FFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 &= 0xEFFFu; //SDDATACTL32
|
||||
*(vu16*)0x10006100 |= 0x402u; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 = (*(vu16*)0x100060D8 & 0xFFDD) | 2;
|
||||
*(vu16*)0x10006100 &= 0xFFFDu; //SDDATACTL32
|
||||
*(vu16*)0x100060D8 &= 0xFFDDu; //SDDATACTL
|
||||
*(vu16*)0x10006104 = 0; //SDBLKLEN32
|
||||
*(vu16*)0x10006108 = 1; //SDBLKCOUNT32
|
||||
*(vu16*)0x100060E0 &= 0xFFFEu; //SDRESET
|
||||
*(vu16*)0x100060E0 |= 1u; //SDRESET
|
||||
*(vu16*)0x10006020 |= TMIO_MASK_ALL; //SDIR_MASK0
|
||||
*(vu16*)0x10006022 |= TMIO_MASK_ALL>>16; //SDIR_MASK1
|
||||
*(vu16*)0x100060FC |= 0xDBu; //SDCTL_RESERVED7
|
||||
*(vu16*)0x100060FE |= 0xDBu; //SDCTL_RESERVED8
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; //SDPORTSEL
|
||||
*(vu16*)0x10006024 = 0x40; //Nintendo sets this to 0x20
|
||||
*(vu16*)0x10006028 = 0x40EB; //Nintendo sets this to 0x40EE
|
||||
*(vu16*)0x10006002 &= 0xFFFCu; ////SDPORTSEL
|
||||
*(vu16*)0x10006026 = 512; //SDBLKLEN
|
||||
*(vu16*)0x10006008 = 0; //SDSTOP
|
||||
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
static int Nand_Init()
|
||||
{
|
||||
inittarget(&handleNAND);
|
||||
ioDelay(0xF000);
|
||||
waitcycles(0xF000);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0,0);
|
||||
sdmmc_send_command(&handleNAND, 0, 0);
|
||||
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleNAND,0x10701,0x100000);
|
||||
} while ( !(handleNAND.error & 1) );
|
||||
} while((handleNAND.ret[0] & 0x80000000) == 0);
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
sdmmc_send_command(&handleNAND, 0x10701, 0x100000);
|
||||
}
|
||||
while(!(handleNAND.error & 1));
|
||||
}
|
||||
while((handleNAND.ret[0] & 0x80000000) == 0);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10602,0x0);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10602, 0x0);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10403,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10403, handleNAND.initarg << 0x10);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10609,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10609, handleNAND.initarg << 0x10);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0],0);
|
||||
handleNAND.total_size = calcSDSize((u8*)&handleNAND.ret[0], 0);
|
||||
handleNAND.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10407,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10407, handleNAND.initarg << 0x10);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
handleNAND.SDOPT = 1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B70100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10506, 0x3B70100);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10506,0x3B90100);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10506, 0x3B90100);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x1040D,handleNAND.initarg << 0x10);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x1040D, handleNAND.initarg << 0x10);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleNAND,0x10410,0x200);
|
||||
if (handleNAND.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleNAND, 0x10410, 0x200);
|
||||
if((handleNAND.error & 0x4)) return -1;
|
||||
|
||||
handleNAND.clk |= 0x200;
|
||||
|
||||
@@ -317,113 +379,102 @@ static int Nand_Init()
|
||||
|
||||
static int SD_Init()
|
||||
{
|
||||
//SD
|
||||
handleSD.isSDHC = 0;
|
||||
handleSD.SDOPT = 0;
|
||||
handleSD.res = 0;
|
||||
handleSD.initarg = 0;
|
||||
handleSD.clk = 0x80;
|
||||
handleSD.devicenumber = 0;
|
||||
|
||||
inittarget(&handleSD);
|
||||
|
||||
ioDelay(1u << 18); //Card needs a little bit of time to be detected, it seems
|
||||
waitcycles(1u << 22); //Card needs a little bit of time to be detected, it seems FIXME test again to see what a good number is for the delay
|
||||
|
||||
//If not inserted
|
||||
if (!(*((vu16*)0x1000601c) & TMIO_STAT0_SIGSTATE)) return -1;
|
||||
if(!(*((vu16 *)(SDMMC_BASE + REG_SDSTATUS0)) & TMIO_STAT0_SIGSTATE)) return 5;
|
||||
|
||||
sdmmc_send_command(&handleSD,0,0);
|
||||
sdmmc_send_command(&handleSD,0x10408,0x1AA);
|
||||
//u32 temp = (handleSD.ret[0] == 0x1AA) << 0x1E;
|
||||
sdmmc_send_command(&handleSD, 0, 0);
|
||||
sdmmc_send_command(&handleSD, 0x10408, 0x1AA);
|
||||
u32 temp = (handleSD.error & 0x1) << 0x1E;
|
||||
|
||||
//int count = 0;
|
||||
u32 temp2 = 0;
|
||||
do {
|
||||
do {
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handleSD,0x10769,0x00FF8000 | temp);
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10);
|
||||
sdmmc_send_command(&handleSD, 0x10769, 0x00FF8000 | temp);
|
||||
temp2 = 1;
|
||||
} while ( !(handleSD.error & 1) );
|
||||
|
||||
} while((handleSD.ret[0] & 0x80000000) == 0);
|
||||
}
|
||||
while(!(handleSD.error & 1));
|
||||
}
|
||||
while((handleSD.ret[0] & 0x80000000) == 0);
|
||||
|
||||
if(!((handleSD.ret[0] >> 30) & 1) || !temp)
|
||||
temp2 = 0;
|
||||
|
||||
handleSD.isSDHC = temp2;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10602,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10602, 0);
|
||||
if((handleSD.error & 0x4)) return -1;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10403,0);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10403, 0);
|
||||
if((handleSD.error & 0x4)) return -2;
|
||||
handleSD.initarg = handleSD.ret[0] >> 0x10;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10609,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10609, handleSD.initarg << 0x10);
|
||||
if((handleSD.error & 0x4)) return -3;
|
||||
|
||||
handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0],-1);
|
||||
handleSD.total_size = calcSDSize((u8*)&handleSD.ret[0], -1);
|
||||
handleSD.clk = 1;
|
||||
setckl(1);
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10507,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10507, handleSD.initarg << 0x10);
|
||||
if((handleSD.error & 0x4)) return -4;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10437,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10437, handleSD.initarg << 0x10);
|
||||
if((handleSD.error & 0x4)) return -5;
|
||||
|
||||
handleSD.SDOPT = 1;
|
||||
sdmmc_send_command(&handleSD,0x10446,0x2);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10446, 0x2);
|
||||
if((handleSD.error & 0x4)) return -6;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x1040D,handleSD.initarg << 0x10);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x1040D, handleSD.initarg << 0x10);
|
||||
if((handleSD.error & 0x4)) return -7;
|
||||
|
||||
sdmmc_send_command(&handleSD,0x10410,0x200);
|
||||
if (handleSD.error & 0x4) return -1;
|
||||
sdmmc_send_command(&handleSD, 0x10410, 0x200);
|
||||
if((handleSD.error & 0x4)) return -8;
|
||||
handleSD.clk |= 0x200;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sdmmc_get_cid(bool isNand, u32 *info)
|
||||
{
|
||||
struct mmcdevice *device = isNand ? &handleNAND : &handleSD;
|
||||
|
||||
inittarget(device);
|
||||
|
||||
// use cmd7 to put sd card in standby mode
|
||||
// CMD7
|
||||
sdmmc_send_command(device, 0x10507, 0);
|
||||
|
||||
// get sd card info
|
||||
// use cmd10 to read CID
|
||||
sdmmc_send_command(device, 0x1060A, device->initarg << 0x10);
|
||||
|
||||
for(int i = 0; i < 4; ++i)
|
||||
info[i] = device->ret[i];
|
||||
|
||||
// put sd card back to transfer mode
|
||||
// CMD7
|
||||
sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
|
||||
}
|
||||
|
||||
void sdmmc_sdcard_init()
|
||||
{
|
||||
InitSD();
|
||||
Nand_Init();
|
||||
SD_Init();
|
||||
}
|
||||
|
||||
int sdmmc_get_cid(int isNand, uint32_t *info)
|
||||
{
|
||||
struct mmcdevice *device;
|
||||
if(isNand)
|
||||
device = &handleNAND;
|
||||
else
|
||||
device = &handleSD;
|
||||
|
||||
inittarget(device);
|
||||
// use cmd7 to put sd card in standby mode
|
||||
// CMD7
|
||||
{
|
||||
sdmmc_send_command(device,0x10507,0);
|
||||
//if((device->error & 0x4)) return -1;
|
||||
}
|
||||
|
||||
// get sd card info
|
||||
// use cmd10 to read CID
|
||||
{
|
||||
sdmmc_send_command(device,0x1060A,device->initarg << 0x10);
|
||||
//if((device->error & 0x4)) return -2;
|
||||
|
||||
for( int i = 0; i < 4; ++i ) {
|
||||
info[i] = device->ret[i];
|
||||
}
|
||||
}
|
||||
|
||||
// put sd card back to transfer mode
|
||||
// CMD7
|
||||
{
|
||||
sdmmc_send_command(device,0x10507,device->initarg << 0x10);
|
||||
//if((device->error & 0x4)) return -3;
|
||||
}
|
||||
|
||||
if(isNand)
|
||||
{
|
||||
inittarget(&handleSD);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,12 +1,8 @@
|
||||
// Copyright 2014 Normmatt
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "../../types.h"
|
||||
|
||||
#define SDMMC_BASE 0x10006000u
|
||||
#define SDMMC_BASE 0x10006000
|
||||
|
||||
#define REG_SDCMD 0x00
|
||||
#define REG_SDPORTSEL 0x02
|
||||
@@ -14,19 +10,19 @@
|
||||
#define REG_SDCMDARG0 0x04
|
||||
#define REG_SDCMDARG1 0x06
|
||||
#define REG_SDSTOP 0x08
|
||||
#define REG_SDBLKCOUNT 0x0a
|
||||
#define REG_SDBLKCOUNT 0x0A
|
||||
|
||||
#define REG_SDRESP0 0x0c
|
||||
#define REG_SDRESP1 0x0e
|
||||
#define REG_SDRESP0 0x0C
|
||||
#define REG_SDRESP1 0x0E
|
||||
#define REG_SDRESP2 0x10
|
||||
#define REG_SDRESP3 0x12
|
||||
#define REG_SDRESP4 0x14
|
||||
#define REG_SDRESP5 0x16
|
||||
#define REG_SDRESP6 0x18
|
||||
#define REG_SDRESP7 0x1a
|
||||
#define REG_SDRESP7 0x1A
|
||||
|
||||
#define REG_SDSTATUS0 0x1c
|
||||
#define REG_SDSTATUS1 0x1e
|
||||
#define REG_SDSTATUS0 0x1C
|
||||
#define REG_SDSTATUS1 0x1E
|
||||
|
||||
#define REG_SDIRMASK0 0x20
|
||||
#define REG_SDIRMASK1 0x22
|
||||
@@ -36,17 +32,17 @@
|
||||
#define REG_SDOPT 0x28
|
||||
#define REG_SDFIFO 0x30
|
||||
|
||||
#define REG_SDDATACTL 0xd8
|
||||
#define REG_SDRESET 0xe0
|
||||
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
|
||||
#define REG_DATACTL 0xD8
|
||||
#define REG_SDRESET 0xE0
|
||||
#define REG_SDPROTECTED 0xF6 //bit 0 determines if sd is protected or not?
|
||||
|
||||
#define REG_SDDATACTL32 0x100
|
||||
#define REG_DATACTL32 0x100
|
||||
#define REG_SDBLKLEN32 0x104
|
||||
#define REG_SDBLKCOUNT32 0x108
|
||||
#define REG_SDFIFO32 0x10C
|
||||
|
||||
#define REG_CLK_AND_WAIT_CTL 0x138
|
||||
#define REG_RESET_SDIO 0x1e0
|
||||
#define REG_RESET_SDIO 0x1E0
|
||||
|
||||
#define TMIO_STAT0_CMDRESPEND 0x0001
|
||||
#define TMIO_STAT0_DATAEND 0x0004
|
||||
@@ -70,31 +66,7 @@
|
||||
#define TMIO_STAT1_CMD_BUSY 0x4000
|
||||
#define TMIO_STAT1_ILL_ACCESS 0x8000
|
||||
|
||||
//Comes from TWLSDK mongoose.tef DWARF info
|
||||
#define SDMC_NORMAL 0x00000000
|
||||
#define SDMC_ERR_COMMAND 0x00000001
|
||||
#define SDMC_ERR_CRC 0x00000002
|
||||
#define SDMC_ERR_END 0x00000004
|
||||
#define SDMC_ERR_TIMEOUT 0x00000008
|
||||
#define SDMC_ERR_FIFO_OVF 0x00000010
|
||||
#define SDMC_ERR_FIFO_UDF 0x00000020
|
||||
#define SDMC_ERR_WP 0x00000040
|
||||
#define SDMC_ERR_ABORT 0x00000080
|
||||
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
|
||||
#define SDMC_ERR_PARAM 0x00000200
|
||||
#define SDMC_ERR_R1_STATUS 0x00000800
|
||||
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
|
||||
#define SDMC_ERR_RESET 0x00002000
|
||||
#define SDMC_ERR_ILA 0x00004000
|
||||
#define SDMC_ERR_INFO_DETECT 0x00008000
|
||||
|
||||
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
|
||||
#define SDMC_STAT_ERR_CC 0x00100000
|
||||
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
|
||||
#define SDMC_STAT_ERR_CRC 0x00800000
|
||||
#define SDMC_STAT_ERR_OTHER 0xf9c70008
|
||||
|
||||
#define TMIO_MASK_ALL 0x837f031d
|
||||
#define TMIO_MASK_ALL 0x837F031D
|
||||
|
||||
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
|
||||
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
|
||||
@@ -103,7 +75,8 @@
|
||||
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
|
||||
|
||||
typedef struct mmcdevice {
|
||||
vu8* data;
|
||||
u8 *rData;
|
||||
const u8 *tData;
|
||||
u32 size;
|
||||
u32 error;
|
||||
u16 stat0;
|
||||
@@ -118,12 +91,10 @@ typedef struct mmcdevice {
|
||||
u32 res;
|
||||
} mmcdevice;
|
||||
|
||||
mmcdevice *getMMCDevice(int drive);
|
||||
|
||||
void sdmmc_sdcard_init();
|
||||
u32 sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
u32 sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, vu8 *in);
|
||||
|
||||
u32 sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, vu8 *out);
|
||||
|
||||
int sdmmc_get_cid( int isNand, uint32_t *info);
|
||||
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);
|
||||
431
source/firm.c
431
source/firm.c
@@ -26,6 +26,7 @@
|
||||
#include "fs.h"
|
||||
#include "patches.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "cache.h"
|
||||
#include "emunand.h"
|
||||
#include "crypto.h"
|
||||
@@ -35,27 +36,27 @@
|
||||
#include "pin.h"
|
||||
#include "../build/injector.h"
|
||||
|
||||
extern u16 launchedFirmTIDLow[8]; //defined in start.s
|
||||
#ifdef DEV
|
||||
#include "exceptions.h"
|
||||
#endif
|
||||
|
||||
static firmHeader *const firm = (firmHeader *)0x24000000;
|
||||
extern u16 launchedFirmTidLow[8]; //Defined in start.s
|
||||
|
||||
static firmHeader *firm = (firmHeader *)0x24000000;
|
||||
static const firmSectionHeader *section;
|
||||
|
||||
u32 config,
|
||||
emuOffset;
|
||||
|
||||
u32 emuOffset;
|
||||
bool isN3DS,
|
||||
isDevUnit,
|
||||
isFirmlaunch;
|
||||
|
||||
CfgData configData;
|
||||
FirmwareSource firmSource;
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bool isA9lh;
|
||||
|
||||
u32 configTemp,
|
||||
emuHeader;
|
||||
|
||||
FirmwareType firmType;
|
||||
FirmwareSource nandType;
|
||||
ConfigurationStatus needConfig;
|
||||
@@ -69,41 +70,48 @@ void main(void)
|
||||
//Mount filesystems. CTRNAND will be mounted only if/when needed
|
||||
mountFs();
|
||||
|
||||
const char configPath[] = "/luma/config.bin";
|
||||
|
||||
//Attempt to read the configuration file
|
||||
needConfig = fileRead(&config, configPath) ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
||||
needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION;
|
||||
|
||||
#ifdef DEV
|
||||
detectAndProcessExceptionDumps();
|
||||
#endif
|
||||
|
||||
//Determine if this is a firmlaunch boot
|
||||
if(launchedFirmTIDLow[5] != 0)
|
||||
if(launchedFirmTidLow[5] != 0)
|
||||
{
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
||||
|
||||
isFirmlaunch = true;
|
||||
|
||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||
firmType = launchedFirmTIDLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTIDLow[5] - u'0');
|
||||
if(needConfig == CREATE_CONFIGURATION) mcuReboot();
|
||||
|
||||
nandType = (FirmwareSource)BOOTCONFIG(0, 3);
|
||||
firmSource = (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
isA9lh = BOOTCONFIG(3, 1) != 0;
|
||||
//'0' = NATIVE_FIRM, '1' = TWL_FIRM, '2' = AGB_FIRM
|
||||
firmType = launchedFirmTidLow[7] == u'3' ? SAFE_FIRM : (FirmwareType)(launchedFirmTidLow[5] - u'0');
|
||||
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
isA9lh = BOOTCFG_A9LH != 0;
|
||||
|
||||
#ifdef DEV
|
||||
if(isA9lh) installArm9Handlers();
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
isFirmlaunch = false;
|
||||
firmType = NATIVE_FIRM;
|
||||
|
||||
//Determine if booting with A9LH
|
||||
isA9lh = !PDN_SPI_CNT;
|
||||
|
||||
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
|
||||
bool useSysAsDefault = isA9lh ? CONFIG(1) : false;
|
||||
#ifdef DEV
|
||||
if(isA9lh) installArm9Handlers();
|
||||
#endif
|
||||
|
||||
//Get pressed buttons
|
||||
u32 pressed = HID_PAD;
|
||||
|
||||
//Save old options and begin saving the new boot configuration
|
||||
configTemp = (config & 0xFFFFFFC0) | ((u32)isA9lh << 3);
|
||||
configTemp = (configData.config & 0xFFFFFE00) | ((u32)isA9lh << 6);
|
||||
|
||||
//If it's a MCU reboot, try to force boot options
|
||||
if(isA9lh && CFG_BOOTENV)
|
||||
@@ -112,19 +120,19 @@ void main(void)
|
||||
if(CFG_BOOTENV == 7)
|
||||
{
|
||||
nandType = FIRMWARE_SYSNAND;
|
||||
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
firmSource = CONFIG(USESYSFIRM) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
|
||||
//Flag to prevent multiple boot options-forcing
|
||||
configTemp |= 1 << 4;
|
||||
configTemp |= 1 << 7;
|
||||
}
|
||||
|
||||
/* Else, force the last used boot options unless a button is pressed
|
||||
or the no-forcing flag is set */
|
||||
else if(!pressed && !BOOTCONFIG(4, 1))
|
||||
else if(needConfig != CREATE_CONFIGURATION && !pressed && !BOOTCFG_NOFORCEFLAG)
|
||||
{
|
||||
nandType = (FirmwareSource)BOOTCONFIG(0, 3);
|
||||
firmSource = (FirmwareSource)BOOTCONFIG(2, 1);
|
||||
nandType = (FirmwareSource)BOOTCFG_NAND;
|
||||
firmSource = (FirmwareSource)BOOTCFG_FIRM;
|
||||
needConfig = DONT_CONFIGURE;
|
||||
}
|
||||
}
|
||||
@@ -132,23 +140,14 @@ void main(void)
|
||||
//Boot options aren't being forced
|
||||
if(needConfig != DONT_CONFIGURE)
|
||||
{
|
||||
PINData pin;
|
||||
|
||||
bool pinExists = CONFIG(7) && readPin(&pin);
|
||||
|
||||
//If we get here we should check the PIN (if it exists) in all cases
|
||||
if(pinExists) verifyPin(&pin);
|
||||
bool pinExists = MULTICONFIG(PIN) != 0 && verifyPin();
|
||||
|
||||
//If no configuration file exists or SELECT is held, load configuration menu
|
||||
bool shouldLoadConfigurationMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
|
||||
bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & BUTTON_SELECT) && !(pressed & BUTTON_L1));
|
||||
|
||||
if(shouldLoadConfigurationMenu)
|
||||
if(shouldLoadConfigMenu)
|
||||
{
|
||||
configureCFW();
|
||||
|
||||
if(!pinExists && CONFIG(7)) newPin();
|
||||
|
||||
chrono(2);
|
||||
configMenu(pinExists);
|
||||
|
||||
//Update pressed buttons
|
||||
pressed = HID_PAD;
|
||||
@@ -160,74 +159,94 @@ void main(void)
|
||||
firmSource = FIRMWARE_SYSNAND;
|
||||
|
||||
//Flag to tell loader to init SD
|
||||
configTemp |= 1 << 5;
|
||||
configTemp |= 1 << 8;
|
||||
|
||||
//If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo
|
||||
if(pinExists && !shouldLoadConfigMenu)
|
||||
{
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
chrono(2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(CONFIG(6) && loadSplash()) pressed = HID_PAD;
|
||||
if(CONFIG(PAYLOADSPLASH) && loadSplash()) pressed = HID_PAD;
|
||||
|
||||
/* If L and R/A/Select or one of the single payload buttons are pressed,
|
||||
chainload an external payload (the PIN, if any, has been verified)*/
|
||||
bool shouldLoadPayload = (pressed & SINGLE_PAYLOAD_BUTTONS) || ((pressed & BUTTON_L1) && (pressed & L_PAYLOAD_BUTTONS));
|
||||
chainload an external payload */
|
||||
bool shouldLoadPayload = ((pressed & SINGLE_PAYLOAD_BUTTONS) && !(pressed & (BUTTON_L1 | BUTTON_R1 | BUTTON_A))) ||
|
||||
((pressed & L_PAYLOAD_BUTTONS) && (pressed & BUTTON_L1));
|
||||
|
||||
if(shouldLoadPayload) loadPayload(pressed);
|
||||
|
||||
if(!CONFIG(6)) loadSplash();
|
||||
if(!CONFIG(PAYLOADSPLASH)) loadSplash();
|
||||
|
||||
//Determine if the user chose to use the SysNAND FIRM as default for a R boot
|
||||
bool useSysAsDefault = isA9lh ? CONFIG(USESYSFIRM) : false;
|
||||
|
||||
//If R is pressed, boot the non-updated NAND with the FIRM of the opposite one
|
||||
if(pressed & BUTTON_R1)
|
||||
{
|
||||
nandType = (useSysAsDefault) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
firmSource = (useSysAsDefault) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||
nandType = useSysAsDefault ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
firmSource = useSysAsDefault ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND;
|
||||
}
|
||||
|
||||
/* Else, boot the NAND the user set to autoboot or the opposite one, depending on L,
|
||||
with their own FIRM */
|
||||
else
|
||||
{
|
||||
nandType = (CONFIG(0) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
nandType = (CONFIG(AUTOBOOTSYS) != !(pressed & BUTTON_L1)) ? FIRMWARE_EMUNAND : FIRMWARE_SYSNAND;
|
||||
firmSource = nandType;
|
||||
}
|
||||
|
||||
/* If we're booting emuNAND the second emuNAND is set as default and B isn't pressed,
|
||||
or vice-versa, boot the second emuNAND */
|
||||
if(nandType != FIRMWARE_SYSNAND && (CONFIG(2) == !(pressed & BUTTON_B))) nandType = FIRMWARE_EMUNAND2;
|
||||
//If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config
|
||||
if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND)
|
||||
{
|
||||
FirmwareSource temp;
|
||||
switch(pressed & EMUNAND_BUTTONS)
|
||||
{
|
||||
case BUTTON_UP:
|
||||
temp = FIRMWARE_EMUNAND;
|
||||
break;
|
||||
case BUTTON_RIGHT:
|
||||
temp = FIRMWARE_EMUNAND2;
|
||||
break;
|
||||
case BUTTON_DOWN:
|
||||
temp = FIRMWARE_EMUNAND3;
|
||||
break;
|
||||
case BUTTON_LEFT:
|
||||
temp = FIRMWARE_EMUNAND4;
|
||||
break;
|
||||
default:
|
||||
temp = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU));
|
||||
break;
|
||||
}
|
||||
|
||||
if(nandType == FIRMWARE_EMUNAND) nandType = temp;
|
||||
else firmSource = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If we need to boot emuNAND, make sure it exists
|
||||
//If we need to boot EmuNAND, make sure it exists
|
||||
if(nandType != FIRMWARE_SYSNAND)
|
||||
{
|
||||
locateEmuNAND(&emuOffset, &emuHeader, &nandType);
|
||||
locateEmuNand(&emuHeader, &nandType);
|
||||
if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND;
|
||||
}
|
||||
|
||||
//Same if we're using emuNAND as the FIRM source
|
||||
//Same if we're using EmuNAND as the FIRM source
|
||||
else if(firmSource != FIRMWARE_SYSNAND)
|
||||
locateEmuNAND(&emuOffset, &emuHeader, &firmSource);
|
||||
locateEmuNand(&emuHeader, &firmSource);
|
||||
|
||||
if(!isFirmlaunch)
|
||||
{
|
||||
configTemp |= (u32)nandType | ((u32)firmSource << 2);
|
||||
|
||||
/* If the configuration is different from previously, overwrite it.
|
||||
Just the no-forcing flag being set is not enough */
|
||||
if((configTemp & 0xFFFFFFEF) != config)
|
||||
{
|
||||
//Merge the new options and new boot configuration
|
||||
config = (config & 0xFFFFFFC0) | (configTemp & 0x3F);
|
||||
|
||||
if(!fileWrite(&config, configPath, 4))
|
||||
{
|
||||
createDirectory("luma");
|
||||
if(!fileWrite(&config, configPath, 4))
|
||||
error("Error writing the configuration file");
|
||||
}
|
||||
}
|
||||
configTemp |= (u32)nandType | ((u32)firmSource << 3);
|
||||
writeConfig(needConfig, configTemp);
|
||||
}
|
||||
|
||||
u32 firmVersion = loadFirm(firmType);
|
||||
u32 firmVersion = loadFirm(&firmType, firmSource);
|
||||
|
||||
switch(firmType)
|
||||
{
|
||||
@@ -235,7 +254,8 @@ void main(void)
|
||||
patchNativeFirm(firmVersion, nandType, emuHeader, isA9lh);
|
||||
break;
|
||||
case SAFE_FIRM:
|
||||
patchSafeFirm();
|
||||
case NATIVE_FIRM1X2X:
|
||||
if(isA9lh) patch1x2xNativeAndSafeFirm();
|
||||
break;
|
||||
default:
|
||||
//Skip patching on unsupported O3DS AGB/TWL FIRMs
|
||||
@@ -246,35 +266,93 @@ void main(void)
|
||||
launchFirm(firmType);
|
||||
}
|
||||
|
||||
static inline u32 loadFirm(FirmwareType firmType)
|
||||
#ifdef DEV
|
||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource)
|
||||
{
|
||||
section = firm->section;
|
||||
|
||||
//Load FIRM from CTRNAND, unless it's an O3DS and we're loading a pre-5.0 NATIVE FIRM
|
||||
u32 firmVersion = firmRead(firm, (u32)firmType);
|
||||
const char *firmwareFiles[4] = {
|
||||
"/luma/firmware.bin",
|
||||
"/luma/firmware_twl.bin",
|
||||
"/luma/firmware_agb.bin",
|
||||
"/luma/firmware_safe.bin"
|
||||
};
|
||||
|
||||
if(!isN3DS && firmType == NATIVE_FIRM && firmVersion < 0x25)
|
||||
//Load FIRM from CTRNAND
|
||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||
|
||||
bool loadFromSd = false;
|
||||
|
||||
if(!isN3DS && *firmType == NATIVE_FIRM)
|
||||
{
|
||||
//We can't boot < 3.x NANDs
|
||||
if(firmVersion < 0x18)
|
||||
error("An old unsupported NAND has been detected.\nLuma3DS is unable to boot it.");
|
||||
{
|
||||
//We can't boot < 3.x EmuNANDs
|
||||
if(firmSource != FIRMWARE_SYSNAND)
|
||||
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it");
|
||||
|
||||
//We can't boot a 4.x NATIVE_FIRM, load one from SD
|
||||
if(!fileRead(firm, "/luma/firmware.bin") || (((u32)section[2].address >> 8) & 0xFF) != 0x68)
|
||||
error("An old unsupported FIRM has been detected.\nCopy firmware.bin in /luma to boot");
|
||||
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM");
|
||||
|
||||
//No assumption regarding FIRM version
|
||||
firmVersion = 0xffffffff;
|
||||
*firmType = NATIVE_FIRM1X2X;
|
||||
}
|
||||
|
||||
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
|
||||
else if(firmVersion < 0x25) loadFromSd = true;
|
||||
}
|
||||
|
||||
//Check that the SD FIRM is right for the console from the ARM9 section address
|
||||
if(fileRead(firm, *firmType == NATIVE_FIRM1X2X ? firmwareFiles[0] : firmwareFiles[(u32)*firmType], 0x400000) &&
|
||||
((section[3].offset ? section[3].address : section[2].address) == (isN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800)))
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
else
|
||||
{
|
||||
if(loadFromSd) error("An old unsupported FIRM has been detected.\nCopy a valid firmware.bin in /luma to boot");
|
||||
decryptExeFs((u8 *)firm);
|
||||
}
|
||||
else decryptExeFs((u8 *)firm);
|
||||
|
||||
return firmVersion;
|
||||
}
|
||||
#else
|
||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource)
|
||||
{
|
||||
section = firm->section;
|
||||
|
||||
//Load FIRM from CTRNAND
|
||||
u32 firmVersion = firmRead(firm, (u32)*firmType);
|
||||
|
||||
if(!isN3DS && *firmType == NATIVE_FIRM)
|
||||
{
|
||||
if(firmVersion < 0x18)
|
||||
{
|
||||
//We can't boot < 3.x EmuNANDs
|
||||
if(firmSource != FIRMWARE_SYSNAND)
|
||||
error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it");
|
||||
|
||||
if(BOOTCFG_SAFEMODE != 0) error("SAFE_MODE is not supported on 1.x/2.x FIRM");
|
||||
|
||||
*firmType = NATIVE_FIRM1X2X;
|
||||
}
|
||||
|
||||
//We can't boot a 3.x/4.x NATIVE_FIRM, load one from SD
|
||||
else if(firmVersion < 0x25)
|
||||
{
|
||||
if(!fileRead(firm, "/luma/firmware.bin", 0x400000) || section[2].address != (u8 *)0x8006800)
|
||||
error("An old unsupported FIRM has been detected.\nCopy a valid firmware.bin in /luma to boot");
|
||||
|
||||
firmVersion = 0xFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if(firmVersion != 0xFFFFFFFF) decryptExeFs((u8 *)firm);
|
||||
|
||||
return firmVersion;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
u8 *arm11Section1 = (u8 *)firm + section[1].offset;
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset,
|
||||
*arm11Section1 = (u8 *)firm + section[1].offset;
|
||||
|
||||
if(isN3DS)
|
||||
{
|
||||
@@ -291,14 +369,28 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
||||
process9MemAddr;
|
||||
u8 *process9Offset = getProcess9(arm9Section + 0x15000, section[2].size - 0x15000, &process9Size, &process9MemAddr);
|
||||
|
||||
#ifdef DEV
|
||||
//Find Kernel11 SVC table and handler, exceptions page and free space locations
|
||||
u32 baseK11VA;
|
||||
u8 *freeK11Space;
|
||||
u32 *arm11SvcHandler,
|
||||
*arm11ExceptionsPage,
|
||||
*arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage);
|
||||
#else
|
||||
//Find Kernel11 SVC table and free space locations
|
||||
u32 baseK11VA;
|
||||
u8 *freeK11Space;
|
||||
u32 *arm11SvcTable = getKernel11Info(arm11Section1, section[1].size, &baseK11VA, &freeK11Space);
|
||||
#endif
|
||||
|
||||
//Apply signature patches
|
||||
patchSignatureChecks(process9Offset, process9Size);
|
||||
|
||||
//Apply emuNAND patches
|
||||
//Apply EmuNAND patches
|
||||
if(nandType != FIRMWARE_SYSNAND)
|
||||
{
|
||||
u32 branchAdditive = (u32)firm + section[2].offset - (u32)section[2].address;
|
||||
patchEmuNAND(arm9Section, section[2].size, process9Offset, process9Size, emuOffset, emuHeader, branchAdditive);
|
||||
patchEmuNand(arm9Section, section[2].size, process9Offset, process9Size, emuHeader, branchAdditive);
|
||||
}
|
||||
|
||||
//Apply FIRM0/1 writes patches on sysNAND to protect A9LH
|
||||
@@ -314,28 +406,63 @@ static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32
|
||||
patchTitleInstallMinVersionCheck(process9Offset, process9Size);
|
||||
|
||||
//Restore svcBackdoor
|
||||
reimplementSvcBackdoor(arm11Section1, section[1].size);
|
||||
reimplementSvcBackdoor(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||
}
|
||||
|
||||
implementSvcGetCFWInfo(arm11Section1, section[1].size);
|
||||
implementSvcGetCFWInfo(arm11Section1, arm11SvcTable, baseK11VA, &freeK11Space);
|
||||
|
||||
#ifdef DEV
|
||||
//Apply UNITINFO patch
|
||||
if(MULTICONFIG(DEVOPTIONS) == 1) patchUnitInfoValueSet(arm9Section, section[2].size);
|
||||
|
||||
if(isA9lh && MULTICONFIG(DEVOPTIONS) != 2)
|
||||
{
|
||||
//Install ARM11 exception handlers
|
||||
u32 codeSetOffset;
|
||||
u32 stackAddress = getInfoForArm11ExceptionHandlers(arm11Section1, section[1].size, &codeSetOffset);
|
||||
installArm11Handlers(arm11ExceptionsPage, stackAddress, codeSetOffset);
|
||||
|
||||
//Kernel9/Process9 debugging
|
||||
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
|
||||
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
|
||||
patchKernel9Panic(arm9Section, section[2].size);
|
||||
|
||||
//Stub svcBreak11 with "bkpt 65535"
|
||||
patchSvcBreak11(arm11Section1, arm11SvcTable);
|
||||
|
||||
//Stub kernel11Panic with "bkpt 65534"
|
||||
patchKernel11Panic(arm11Section1, section[1].size);
|
||||
}
|
||||
|
||||
if(CONFIG(PATCHACCESS))
|
||||
{
|
||||
patchArm11SvcAccessChecks(arm11SvcHandler);
|
||||
patchK11ModuleChecks(arm11Section1, section[1].size, &freeK11Space);
|
||||
patchP9AccessChecks(process9Offset, process9Size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void patchLegacyFirm(FirmwareType firmType)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[3].offset;
|
||||
|
||||
//On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
if(isN3DS)
|
||||
{
|
||||
//Decrypt ARM9Bin and patch ARM9 entrypoint to skip arm9loader
|
||||
arm9Loader((u8 *)firm + section[3].offset);
|
||||
arm9Loader(arm9Section);
|
||||
firm->arm9Entry = (u8 *)0x801301C;
|
||||
}
|
||||
|
||||
applyLegacyFirmPatches((u8 *)firm, firmType);
|
||||
|
||||
if(firmType == TWL_FIRM && CONFIG(8))
|
||||
patchTwlBg((u8 *)firm + section[1].offset);
|
||||
#ifdef DEV
|
||||
//Apply UNITINFO patch
|
||||
if(MULTICONFIG(DEVOPTIONS) == 1) patchUnitInfoValueSet(arm9Section, section[3].size);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void patchSafeFirm(void)
|
||||
static inline void patch1x2xNativeAndSafeFirm(void)
|
||||
{
|
||||
u8 *arm9Section = (u8 *)firm + section[2].offset;
|
||||
|
||||
@@ -347,42 +474,100 @@ static inline void patchSafeFirm(void)
|
||||
|
||||
patchFirmWrites(arm9Section, section[2].size);
|
||||
}
|
||||
else patchFirmWriteSafe(arm9Section, section[2].size);
|
||||
else patchOldFirmWrites(arm9Section, section[2].size);
|
||||
|
||||
#ifdef DEV
|
||||
if(MULTICONFIG(DEVOPTIONS) != 2)
|
||||
{
|
||||
//Kernel9/Process9 debugging
|
||||
patchArm9ExceptionHandlersInstall(arm9Section, section[2].size);
|
||||
patchSvcBreak9(arm9Section, section[2].size, (u32)section[2].address);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void copySection0AndInjectSystemModules(void)
|
||||
#ifdef DEV
|
||||
static inline void copySection0AndInjectSystemModules(FirmwareType firmType)
|
||||
{
|
||||
u8 *arm11Section0 = (u8 *)firm + section[0].offset;
|
||||
u32 srcModuleSize,
|
||||
dstModuleSize;
|
||||
|
||||
struct
|
||||
for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
|
||||
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
|
||||
{
|
||||
u32 size;
|
||||
const u8 *addr;
|
||||
} modules[5];
|
||||
srcModuleSize = *(u32 *)(src + 0x104) * 0x200;
|
||||
const char *moduleName = (char *)(src + 0x200);
|
||||
|
||||
u32 n = 0,
|
||||
loaderIndex;
|
||||
u8 *pos = arm11Section0;
|
||||
char fileName[30] = "/luma/sysmodules/";
|
||||
const char *ext = ".cxi";
|
||||
|
||||
for(u8 *end = pos + section[0].size; pos < end; pos += modules[n++].size)
|
||||
//Read modules from files if they exist
|
||||
concatenateStrings(fileName, moduleName);
|
||||
concatenateStrings(fileName, ext);
|
||||
|
||||
u32 fileSize = fileRead(dst, fileName, 2 * srcModuleSize);
|
||||
if(fileSize) dstModuleSize = fileSize;
|
||||
else
|
||||
{
|
||||
modules[n].addr = pos;
|
||||
modules[n].size = *(u32 *)(pos + 0x104) * 0x200;
|
||||
const void *module;
|
||||
|
||||
if(memcmp(modules[n].addr + 0x200, "loader", 7) == 0) loaderIndex = n;
|
||||
if(firmType == NATIVE_FIRM && memcmp(moduleName, "loader", 6) == 0)
|
||||
{
|
||||
module = injector;
|
||||
dstModuleSize = injector_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
module = src;
|
||||
dstModuleSize = srcModuleSize;
|
||||
}
|
||||
|
||||
modules[loaderIndex].addr = injector;
|
||||
modules[loaderIndex].size = injector_size;
|
||||
|
||||
pos = section[0].address;
|
||||
|
||||
for(u32 i = 0; i < n; pos += modules[i++].size)
|
||||
memcpy(pos, modules[i].addr, modules[i].size);
|
||||
memcpy(dst, module, dstModuleSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void copySection0AndInjectSystemModules(void)
|
||||
{
|
||||
u32 srcModuleSize,
|
||||
dstModuleSize;
|
||||
|
||||
for(u8 *src = (u8 *)firm + section[0].offset, *srcEnd = src + section[0].size, *dst = section[0].address;
|
||||
src < srcEnd; src += srcModuleSize, dst += dstModuleSize)
|
||||
{
|
||||
srcModuleSize = *(u32 *)(src + 0x104) * 0x200;
|
||||
const char *moduleName = (const char *)(src + 0x200);
|
||||
|
||||
const void *module;
|
||||
|
||||
if(memcmp(moduleName, "loader", 6) == 0)
|
||||
{
|
||||
module = injector;
|
||||
dstModuleSize = injector_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
module = src;
|
||||
dstModuleSize = srcModuleSize;
|
||||
}
|
||||
|
||||
memcpy(dst, module, dstModuleSize);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void launchFirm(FirmwareType firmType)
|
||||
{
|
||||
#ifdef DEV
|
||||
//Allow module injection and/or inject 3ds_injector on new NATIVE_FIRMs and LGY FIRMs
|
||||
u32 sectionNum;
|
||||
if(firmType != SAFE_FIRM && firmType != NATIVE_FIRM1X2X)
|
||||
{
|
||||
copySection0AndInjectSystemModules(firmType);
|
||||
sectionNum = 1;
|
||||
}
|
||||
else sectionNum = 0;
|
||||
#else
|
||||
//If we're booting NATIVE_FIRM, section0 needs to be copied separately to inject 3ds_injector
|
||||
u32 sectionNum;
|
||||
if(firmType == NATIVE_FIRM)
|
||||
@@ -391,9 +576,10 @@ static inline void launchFirm(FirmwareType firmType)
|
||||
sectionNum = 1;
|
||||
}
|
||||
else sectionNum = 0;
|
||||
#endif
|
||||
|
||||
//Copy FIRM sections to respective memory locations
|
||||
for(; sectionNum < 4 && section[sectionNum].size; sectionNum++)
|
||||
for(; sectionNum < 4 && section[sectionNum].size != 0; sectionNum++)
|
||||
memcpy(section[sectionNum].address, (u8 *)firm + section[sectionNum].offset, section[sectionNum].size);
|
||||
|
||||
//Determine the ARM11 entry to use
|
||||
@@ -408,7 +594,8 @@ static inline void launchFirm(FirmwareType firmType)
|
||||
//Set ARM11 kernel entrypoint
|
||||
*arm11 = (u32)firm->arm11Entry;
|
||||
|
||||
flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
//Ensure that all memory transfers have completed and that the caches have been flushed
|
||||
flushEntireDCache();
|
||||
flushEntireICache();
|
||||
|
||||
//Final jump to ARM9 kernel
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
|
||||
//FIRM Header layout
|
||||
typedef struct firmSectionHeader {
|
||||
u32 offset;
|
||||
@@ -42,16 +47,15 @@ typedef struct firmHeader {
|
||||
firmSectionHeader section[4];
|
||||
} firmHeader;
|
||||
|
||||
typedef enum ConfigurationStatus
|
||||
{
|
||||
DONT_CONFIGURE = 0,
|
||||
MODIFY_CONFIGURATION = 1,
|
||||
CREATE_CONFIGURATION = 2
|
||||
} ConfigurationStatus;
|
||||
|
||||
static inline u32 loadFirm(FirmwareType firmType);
|
||||
static inline u32 loadFirm(FirmwareType *firmType, FirmwareSource firmSource);
|
||||
static inline void patchNativeFirm(u32 firmVersion, FirmwareSource nandType, u32 emuHeader, bool isA9lh);
|
||||
static inline void patchLegacyFirm(FirmwareType firmType);
|
||||
static inline void patchSafeFirm(void);
|
||||
static inline void patch1x2xNativeAndSafeFirm(void);
|
||||
|
||||
#ifdef DEV
|
||||
static inline void copySection0AndInjectSystemModules(FirmwareType firmType);
|
||||
#else
|
||||
static inline void copySection0AndInjectSystemModules(void);
|
||||
#endif
|
||||
|
||||
static inline void launchFirm(FirmwareType firmType);
|
||||
110
source/fs.c
110
source/fs.c
@@ -22,6 +22,7 @@
|
||||
|
||||
#include "fs.h"
|
||||
#include "memory.h"
|
||||
#include "strings.h"
|
||||
#include "cache.h"
|
||||
#include "screen.h"
|
||||
#include "fatfs/ff.h"
|
||||
@@ -37,34 +38,35 @@ void mountFs(void)
|
||||
f_mount(&nandFs, "1:", 0);
|
||||
}
|
||||
|
||||
u32 fileRead(void *dest, const char *path)
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize)
|
||||
{
|
||||
FIL file;
|
||||
u32 size;
|
||||
u32 ret = 0;
|
||||
|
||||
if(f_open(&file, path, FA_READ) == FR_OK)
|
||||
{
|
||||
unsigned int read;
|
||||
size = f_size(&file);
|
||||
if(dest != NULL)
|
||||
f_read(&file, dest, size, &read);
|
||||
u32 size = f_size(&file);
|
||||
if(dest == NULL) ret = size;
|
||||
else if(!(maxSize > 0 && size > maxSize))
|
||||
f_read(&file, dest, size, (unsigned int *)&ret);
|
||||
f_close(&file);
|
||||
}
|
||||
else size = 0;
|
||||
|
||||
return size;
|
||||
return ret;
|
||||
}
|
||||
|
||||
u32 getFileSize(const char *path)
|
||||
{
|
||||
return fileRead(NULL, path);
|
||||
return fileRead(NULL, path, 0);
|
||||
}
|
||||
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
{
|
||||
FIL file;
|
||||
|
||||
if(f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS) == FR_OK)
|
||||
FRESULT result = f_open(&file, path, FA_WRITE | FA_OPEN_ALWAYS);
|
||||
|
||||
if(result == FR_OK)
|
||||
{
|
||||
unsigned int written;
|
||||
f_write(&file, buffer, size, &written);
|
||||
@@ -73,27 +75,42 @@ bool fileWrite(const void *buffer, const char *path, u32 size)
|
||||
return true;
|
||||
}
|
||||
|
||||
if(result == 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;
|
||||
f_mkdir(folder);
|
||||
}
|
||||
|
||||
return fileWrite(buffer, path, size);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void createDirectory(const char *path)
|
||||
void fileDelete(const char *path)
|
||||
{
|
||||
f_mkdir(path);
|
||||
f_unlink(path);
|
||||
}
|
||||
|
||||
void loadPayload(u32 pressed)
|
||||
{
|
||||
const char *pattern;
|
||||
|
||||
if(pressed & BUTTON_RIGHT) pattern = PATTERN("right");
|
||||
else if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
|
||||
if(pressed & BUTTON_LEFT) pattern = PATTERN("left");
|
||||
else if(pressed & BUTTON_RIGHT) pattern = PATTERN("right");
|
||||
else if(pressed & BUTTON_UP) pattern = PATTERN("up");
|
||||
else if(pressed & BUTTON_DOWN) pattern = PATTERN("down");
|
||||
else if(pressed & BUTTON_START) pattern = PATTERN("start");
|
||||
else if(pressed & BUTTON_B) pattern = PATTERN("b");
|
||||
else if(pressed & BUTTON_X) pattern = PATTERN("x");
|
||||
else if(pressed & BUTTON_Y) pattern = PATTERN("y");
|
||||
else if(pressed & BUTTON_R1) pattern = PATTERN("r");
|
||||
else if(pressed & BUTTON_A) pattern = PATTERN("a");
|
||||
else if(pressed & BUTTON_START) pattern = PATTERN("start");
|
||||
else pattern = PATTERN("select");
|
||||
|
||||
DIR dir;
|
||||
@@ -106,22 +123,28 @@ void loadPayload(u32 pressed)
|
||||
|
||||
if(result == FR_OK && info.fname[0])
|
||||
{
|
||||
initScreens();
|
||||
|
||||
u32 *const loaderAddress = (u32 *)0x24FFFF00;
|
||||
u32 *loaderAddress = (u32 *)0x24FFFF00;
|
||||
u8 *payloadAddress = (u8 *)0x24F00000;
|
||||
|
||||
memcpy(loaderAddress, loader, loader_size);
|
||||
|
||||
path[14] = '/';
|
||||
memcpy(&path[15], info.altname, 13);
|
||||
concatenateStrings(path, "/");
|
||||
concatenateStrings(path, info.altname);
|
||||
|
||||
loaderAddress[1] = fileRead((void *)0x24F00000, path);
|
||||
u32 payloadSize = fileRead(payloadAddress, path, (u8 *)loaderAddress - payloadAddress);
|
||||
|
||||
if(payloadSize > 0)
|
||||
{
|
||||
loaderAddress[1] = payloadSize;
|
||||
|
||||
initScreens();
|
||||
|
||||
flushDCacheRange(loaderAddress, loader_size);
|
||||
flushICacheRange(loaderAddress, loader_size);
|
||||
|
||||
((void (*)())loaderAddress)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
u32 firmRead(void *dest, u32 firmType)
|
||||
@@ -131,8 +154,9 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
{ "00000202", "20000202" },
|
||||
{ "00000003", "20000003" }};
|
||||
|
||||
char path[48] = "1:/title/00040138/00000000/content";
|
||||
memcpy(&path[18], firmFolders[firmType][isN3DS ? 1 : 0], 8);
|
||||
char path[48] = "1:/title/00040138/";
|
||||
concatenateStrings(path, firmFolders[firmType][isN3DS ? 1 : 0]);
|
||||
concatenateStrings(path, "/content");
|
||||
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
@@ -145,7 +169,7 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
while(f_readdir(&dir, &info) == FR_OK && info.fname[0])
|
||||
{
|
||||
//Not a cxi
|
||||
if(info.altname[9] != 'A') continue;
|
||||
if(info.fname[9] != 'a') continue;
|
||||
|
||||
//Convert the .app name to an integer
|
||||
u32 tempVersion = 0;
|
||||
@@ -162,21 +186,35 @@ u32 firmRead(void *dest, u32 firmType)
|
||||
f_closedir(&dir);
|
||||
|
||||
//Complete the string with the .app name
|
||||
memcpy(&path[34], "/00000000.app", 14);
|
||||
|
||||
//Last digit of the .app
|
||||
u32 i = 42;
|
||||
concatenateStrings(path, "/00000000.app");
|
||||
|
||||
//Convert back the .app name from integer to array
|
||||
u32 tempVersion = firmVersion;
|
||||
while(tempVersion)
|
||||
{
|
||||
static const char hexDigits[] = "0123456789ABCDEF";
|
||||
path[i--] = hexDigits[tempVersion & 0xF];
|
||||
tempVersion >>= 4;
|
||||
}
|
||||
hexItoa(firmVersion, &path[35], 8);
|
||||
|
||||
fileRead(dest, path);
|
||||
fileRead(dest, path, 0);
|
||||
|
||||
return firmVersion;
|
||||
}
|
||||
|
||||
#ifdef DEV
|
||||
void findDumpFile(const char *path, char *fileName)
|
||||
{
|
||||
DIR dir;
|
||||
FILINFO info;
|
||||
u32 n = 0;
|
||||
|
||||
while(f_findfirst(&dir, &info, path, fileName) == FR_OK && info.fname[0])
|
||||
{
|
||||
u32 i = 18,
|
||||
tmp = ++n;
|
||||
|
||||
while(tmp > 0)
|
||||
{
|
||||
fileName[i--] = '0' + (tmp % 10);
|
||||
tmp /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
f_closedir(&dir);
|
||||
}
|
||||
#endif
|
||||
@@ -29,9 +29,13 @@
|
||||
extern bool isN3DS;
|
||||
|
||||
void mountFs(void);
|
||||
u32 fileRead(void *dest, const char *path);
|
||||
u32 fileRead(void *dest, const char *path, u32 maxSize);
|
||||
u32 getFileSize(const char *path);
|
||||
bool fileWrite(const void *buffer, const char *path, u32 size);
|
||||
void createDirectory(const char *path);
|
||||
void fileDelete(const char *path);
|
||||
void loadPayload(u32 pressed);
|
||||
u32 firmRead(void *dest, u32 firmType);
|
||||
|
||||
#ifdef DEV
|
||||
void findDumpFile(const char *path, char *fileName);
|
||||
#endif
|
||||
21
source/i2c.c
21
source/i2c.c
@@ -20,6 +20,10 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Thanks to the everyone who contributed in the development of this file
|
||||
*/
|
||||
|
||||
#include "i2c.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -75,7 +79,7 @@ static inline void i2cWaitBusy(u8 bus_id)
|
||||
while (*i2cGetCntReg(bus_id) & 0x80);
|
||||
}
|
||||
|
||||
static inline u32 i2cGetResult(u8 bus_id)
|
||||
static inline bool i2cGetResult(u8 bus_id)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
|
||||
@@ -91,7 +95,7 @@ static void i2cStop(u8 bus_id, u8 arg0)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
static bool i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = dev_reg;
|
||||
@@ -100,7 +104,7 @@ static u32 i2cSelectDevice(u8 bus_id, u8 dev_reg)
|
||||
return i2cGetResult(bus_id);
|
||||
}
|
||||
|
||||
static u32 i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
static bool i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
{
|
||||
i2cWaitBusy(bus_id);
|
||||
*i2cGetDataReg(bus_id) = reg;
|
||||
@@ -111,10 +115,10 @@ static u32 i2cSelectRegister(u8 bus_id, u8 reg)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
{
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id);
|
||||
u8 dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
u8 bus_id = i2cGetDeviceBusId(dev_id),
|
||||
dev_addr = i2cGetDeviceRegAddr(dev_id);
|
||||
|
||||
for(u32 i = 0; i < 8; i++)
|
||||
{
|
||||
@@ -125,12 +129,11 @@ u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data)
|
||||
*i2cGetCntReg(bus_id) = 0xC1;
|
||||
i2cStop(bus_id, 0);
|
||||
|
||||
if(i2cGetResult(bus_id))
|
||||
return 1;
|
||||
if(i2cGetResult(bus_id)) return true;
|
||||
}
|
||||
*i2cGetCntReg(bus_id) = 0xC5;
|
||||
i2cWaitBusy(bus_id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
@@ -41,4 +41,4 @@
|
||||
#define I2C_DEV_GYRO 10
|
||||
#define I2C_DEV_IR 13
|
||||
|
||||
u32 i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
bool i2cWriteRegister(u8 dev_id, u8 reg, u8 data);
|
||||
@@ -20,9 +20,8 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
* Boyer-Moore Horspool algorithm adapted from http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180
|
||||
* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||
*/
|
||||
|
||||
@@ -41,19 +40,19 @@ void memset32(void *dest, u32 filler, u32 size)
|
||||
{
|
||||
u32 *dest32 = (u32 *)dest;
|
||||
|
||||
for (u32 i = 0; i < size / 4; i++)
|
||||
for(u32 i = 0; i < size / 4; i++)
|
||||
dest32[i] = filler;
|
||||
}
|
||||
|
||||
int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
{
|
||||
const u8 *buf1c = (const u8 *)buf1;
|
||||
const u8 *buf2c = (const u8 *)buf2;
|
||||
const u8 *buf1c = (const u8 *)buf1,
|
||||
*buf2c = (const u8 *)buf2;
|
||||
|
||||
for(u32 i = 0; i < size; i++)
|
||||
{
|
||||
int cmp = buf1c[i] - buf2c[i];
|
||||
if(cmp) return cmp;
|
||||
if(cmp != 0) return cmp;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -62,23 +61,22 @@ int memcmp(const void *buf1, const void *buf2, u32 size)
|
||||
u8 *memsearch(u8 *startPos, const void *pattern, u32 size, u32 patternSize)
|
||||
{
|
||||
const u8 *patternc = (const u8 *)pattern;
|
||||
|
||||
//Preprocessing
|
||||
u32 table[256];
|
||||
|
||||
for(u32 i = 0; i < 256; ++i)
|
||||
table[i] = patternSize + 1;
|
||||
for(u32 i = 0; i < patternSize; ++i)
|
||||
table[patternc[i]] = patternSize - i;
|
||||
//Preprocessing
|
||||
for(u32 i = 0; i < 256; i++)
|
||||
table[i] = patternSize;
|
||||
for(u32 i = 0; i < patternSize - 1; i++)
|
||||
table[patternc[i]] = patternSize - i - 1;
|
||||
|
||||
//Searching
|
||||
u32 j = 0;
|
||||
|
||||
while(j <= size - patternSize)
|
||||
{
|
||||
if(memcmp(patternc, startPos + j, patternSize) == 0)
|
||||
u8 c = startPos[j + patternSize - 1];
|
||||
if(patternc[patternSize - 1] == c && memcmp(pattern, startPos + j, patternSize - 1) == 0)
|
||||
return startPos + j;
|
||||
j += table[startPos[j + patternSize]];
|
||||
j += table[c];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
@@ -20,9 +20,8 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Quick Search algorithm adapted from http://igm.univ-mlv.fr/~lecroq/string/node19.html#SECTION00190
|
||||
* Boyer-Moore Horspool algorithm adapted from http://www-igm.univ-mlv.fr/~lecroq/string/node18.html#SECTION00180
|
||||
* memcpy, memset32 and memcmp adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c
|
||||
*/
|
||||
|
||||
|
||||
296
source/patches.c
296
source/patches.c
@@ -20,53 +20,19 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ARM11 modules patching code originally by Subv
|
||||
*/
|
||||
|
||||
#include "patches.h"
|
||||
#include "memory.h"
|
||||
#include "config.h"
|
||||
#include "../build/rebootpatch.h"
|
||||
#include "../build/svcGetCFWInfopatch.h"
|
||||
#include "../build/twl_k11modulespatch.h"
|
||||
|
||||
static u32 *arm11ExceptionsPage = NULL;
|
||||
static u32 *arm11SvcTable = NULL;
|
||||
static u32 *arm11SvcHandler = NULL;
|
||||
|
||||
static u8 *freeK11Space = NULL; //other than the one used for svcBackdoor
|
||||
|
||||
static void findArm11ExceptionsPageAndSvcHandlerAndTable(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 arm11ExceptionsPagePattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
||||
|
||||
if(arm11ExceptionsPage == NULL) arm11ExceptionsPage = (u32 *)memsearch(pos, arm11ExceptionsPagePattern, size, 4) - 0xB;
|
||||
if((arm11SvcTable == NULL || arm11SvcHandler == NULL) && arm11ExceptionsPage != NULL)
|
||||
{
|
||||
u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
arm11SvcHandler = arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + 0xFFFF0008 - svcOffset - 0xFFF00000 + 8) - 0xFFF00000); //SVC handler address
|
||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||
}
|
||||
}
|
||||
|
||||
static void findFreeK11Space(u8 *pos, u32 size)
|
||||
{
|
||||
if(freeK11Space == NULL)
|
||||
{
|
||||
const u8 bogus_pattern[] = { 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF, 0x2F, 0xE1, 0x1E, 0xFF,
|
||||
0x2F, 0xE1, 0x00, 0x10, 0xA0, 0xE3, 0x00, 0x10, 0xC0, 0xE5,
|
||||
0x1E, 0xFF, 0x2F, 0xE1 };
|
||||
|
||||
u32 *someSpace = (u32 *)memsearch(pos, bogus_pattern, size, 24);
|
||||
|
||||
// We couldn't find the place where to begin our search of an empty block
|
||||
if (someSpace == NULL)
|
||||
return;
|
||||
|
||||
// Advance until we reach the padding area (filled with 0xFF)
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = someSpace; *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
|
||||
freeK11Space = (u8 *)freeSpace;
|
||||
}
|
||||
}
|
||||
#ifdef DEV
|
||||
#include "../build/k11modulespatch.h"
|
||||
#endif
|
||||
|
||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
{
|
||||
@@ -79,6 +45,47 @@ u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr)
|
||||
return off - 0x204 + (*(u32 *)(off - 0x64) * 0x200) + 0x200;
|
||||
}
|
||||
|
||||
#ifdef DEV
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
||||
|
||||
*arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)) - 0xB;
|
||||
|
||||
u32 svcOffset = (-(((*arm11ExceptionsPage)[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||
*arm11SvcHandler = arm11SvcTable;
|
||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2)) + 1;
|
||||
|
||||
return arm11SvcTable;
|
||||
}
|
||||
#else
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space)
|
||||
{
|
||||
const u8 pattern[] = {0x00, 0xB0, 0x9C, 0xE5};
|
||||
|
||||
u32 *arm11ExceptionsPage = (u32 *)memsearch(pos, pattern, size, sizeof(pattern)) - 0xB;
|
||||
|
||||
u32 svcOffset = (-((arm11ExceptionsPage[2] & 0xFFFFFF) << 2) & (0xFFFFFF << 2)) - 8; //Branch offset + 8 for prefetch
|
||||
u32 pointedInstructionVA = 0xFFFF0008 - svcOffset;
|
||||
*baseK11VA = pointedInstructionVA & 0xFFFF0000; //This assumes that the pointed instruction has an offset < 0x10000, iirc that's always the case
|
||||
u32 *arm11SvcTable = (u32 *)(pos + *(u32 *)(pos + pointedInstructionVA - *baseK11VA + 8) - *baseK11VA); //SVC handler address
|
||||
while(*arm11SvcTable) arm11SvcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
const u8 pattern2[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
*freeK11Space = memsearch(pos, pattern2, size, sizeof(pattern2)) + 1;
|
||||
|
||||
return arm11SvcTable;
|
||||
}
|
||||
#endif
|
||||
|
||||
void patchSignatureChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 sigPatch[2] = {0x2000, 0x4770};
|
||||
@@ -87,8 +94,8 @@ void patchSignatureChecks(u8 *pos, u32 size)
|
||||
const u8 pattern[] = {0xC0, 0x1C, 0x76, 0xE7},
|
||||
pattern2[] = {0xB5, 0x22, 0x4D, 0x0C};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, 4),
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, 4) - 1);
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)),
|
||||
*off2 = (u16 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) - 1);
|
||||
|
||||
*off = sigPatch[0];
|
||||
off2[0] = sigPatch[0];
|
||||
@@ -98,9 +105,9 @@ void patchSignatureChecks(u8 *pos, u32 size)
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
{
|
||||
//Look for firmlaunch code
|
||||
const u8 pattern[] = {0xDE, 0x1F, 0x8D, 0xE2};
|
||||
const u8 pattern[] = {0xE2, 0x20, 0x20, 0x90};
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, 4) - 0x10;
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern)) - 0x13;
|
||||
|
||||
//Firmlaunch function offset - offset in BLX opcode (A4-16 - ARM DDI 0100E) + 1
|
||||
u32 fOpenOffset = (u32)(off + 9 - (-((*(u32 *)off & 0x00FFFFFF) << 2) & (0xFFFFFF << 2)) - pos + process9MemAddr);
|
||||
@@ -115,32 +122,28 @@ void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr)
|
||||
|
||||
void patchFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 writeBlock[2] = {0x2000, 0x46C0};
|
||||
|
||||
//Look for FIRM writing code
|
||||
u8 *const off1 = memsearch(pos, "exe:", size, 4);
|
||||
u8 *off1 = memsearch(pos, "exe:", size, 4);
|
||||
const u8 pattern[] = {0x00, 0x28, 0x01, 0xDA};
|
||||
|
||||
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, 4);
|
||||
u16 *off2 = (u16 *)memsearch(off1 - 0x100, pattern, 0x100, sizeof(pattern));
|
||||
|
||||
off2[0] = writeBlock[0];
|
||||
off2[1] = writeBlock[1];
|
||||
off2[0] = 0x2000;
|
||||
off2[1] = 0x46C0;
|
||||
}
|
||||
|
||||
void patchFirmWriteSafe(u8 *pos, u32 size)
|
||||
void patchOldFirmWrites(u8 *pos, u32 size)
|
||||
{
|
||||
const u16 writeBlockSafe[2] = {0x2400, 0xE01D};
|
||||
|
||||
//Look for FIRM writing code
|
||||
const u8 pattern[] = {0x04, 0x1E, 0x1D, 0xDB};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, 4);
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
off[0] = writeBlockSafe[0];
|
||||
off[1] = writeBlockSafe[1];
|
||||
off[0] = 0x2400;
|
||||
off[1] = 0xE01D;
|
||||
}
|
||||
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size)
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||
{
|
||||
//Official implementation of svcBackdoor
|
||||
const u8 svcBackdoor[40] = {0xFF, 0x10, 0xCD, 0xE3, //bic r1, sp, #0xff
|
||||
@@ -154,33 +157,26 @@ void reimplementSvcBackdoor(u8 *pos, u32 size)
|
||||
0x00, 0xD0, 0xA0, 0xE1, //mov sp, r0
|
||||
0x11, 0xFF, 0x2F, 0xE1}; //bx r1
|
||||
|
||||
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
|
||||
|
||||
if(!arm11SvcTable[0x7B])
|
||||
{
|
||||
u32 *freeSpace;
|
||||
for(freeSpace = arm11ExceptionsPage; *freeSpace != 0xFFFFFFFF; freeSpace++);
|
||||
memcpy(*freeK11Space, svcBackdoor, 40);
|
||||
|
||||
memcpy(freeSpace, svcBackdoor, 40);
|
||||
|
||||
arm11SvcTable[0x7B] = 0xFFFF0000 + ((u8 *)freeSpace - (u8 *)arm11ExceptionsPage);
|
||||
arm11SvcTable[0x7B] = baseK11VA + *freeK11Space - pos;
|
||||
*freeK11Space += 40;
|
||||
}
|
||||
}
|
||||
|
||||
void implementSvcGetCFWInfo(u8 *pos, u32 size)
|
||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space)
|
||||
{
|
||||
memcpy(*freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
|
||||
|
||||
CFWInfo *info = (CFWInfo *)memsearch(*freeK11Space, "LUMA", svcGetCFWInfo_size, 4);
|
||||
|
||||
const char *rev = REVISION;
|
||||
bool isRelease;
|
||||
|
||||
findArm11ExceptionsPageAndSvcHandlerAndTable(pos, size);
|
||||
findFreeK11Space(pos, size);
|
||||
|
||||
memcpy(freeK11Space, svcGetCFWInfo, svcGetCFWInfo_size);
|
||||
|
||||
CFWInfo *info = (CFWInfo *)memsearch(freeK11Space, "LUMA", svcGetCFWInfo_size, 4);
|
||||
|
||||
info->commitHash = COMMIT_HASH;
|
||||
info->config = config;
|
||||
info->config = configData.config;
|
||||
info->versionMajor = (u8)(rev[1] - '0');
|
||||
info->versionMinor = (u8)(rev[3] - '0');
|
||||
if(rev[4] == '.')
|
||||
@@ -188,20 +184,23 @@ void implementSvcGetCFWInfo(u8 *pos, u32 size)
|
||||
info->versionBuild = (u8)(rev[5] - '0');
|
||||
isRelease = rev[6] == 0;
|
||||
}
|
||||
else
|
||||
isRelease = rev[4] == 0;
|
||||
else isRelease = rev[4] == 0;
|
||||
|
||||
info->flags = 0 /* master branch */ | (((isRelease) ? 1 : 0) << 1) /* is release */;
|
||||
#ifdef DEV
|
||||
info->flags = 1 /* dev build */ | ((isRelease ? 1 : 0) << 1) /* is release */;
|
||||
#else
|
||||
info->flags = 0 /* regular build */ | ((isRelease ? 1 : 0) << 1) /* is release */;
|
||||
#endif
|
||||
|
||||
arm11SvcTable[0x2E] = 0xFFF00000 + freeK11Space - pos; //stubbed svc
|
||||
freeK11Space += svcGetCFWInfo_size;
|
||||
arm11SvcTable[0x2E] = baseK11VA + *freeK11Space - pos; //Stubbed svc
|
||||
*freeK11Space += svcGetCFWInfo_size;
|
||||
}
|
||||
|
||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x0A, 0x81, 0x42, 0x02};
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, 4);
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
if(off != NULL) off[4] = 0xE0;
|
||||
}
|
||||
@@ -227,7 +226,7 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
|
||||
/* Calculate the amount of patches to apply. Only count the boot screen patch for AGB_FIRM
|
||||
if the matching option was enabled (keep it as last) */
|
||||
u32 numPatches = firmType == TWL_FIRM ? (sizeof(twlPatches) / sizeof(patchData)) :
|
||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(5));
|
||||
(sizeof(agbPatches) / sizeof(patchData) - !CONFIG(SHOWGBABOOT));
|
||||
const patchData *patches = firmType == TWL_FIRM ? twlPatches : agbPatches;
|
||||
|
||||
//Patch
|
||||
@@ -247,20 +246,131 @@ void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType)
|
||||
}
|
||||
}
|
||||
|
||||
void patchTwlBg(u8 *pos)
|
||||
#ifdef DEV
|
||||
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size)
|
||||
{
|
||||
u8 *dst = pos + ((isN3DS) ? 0xFEA4 : 0xFCA0);
|
||||
u16 *src1 = (u16 *)(pos + ((isN3DS) ? 0xE38 : 0xE3C)), *src2 = (u16 *)(pos + ((isN3DS) ? 0xE54 : 0xE58));
|
||||
memcpy(dst, twl_k11modules, twl_k11modules_size); //install k11 hook
|
||||
const u8 pattern[] = {0x03, 0xA0, 0xE3, 0x18};
|
||||
|
||||
u32 *off;
|
||||
for(off = (u32 *)dst; *off != 0xABCDABCD; off++);
|
||||
*off = (isN3DS) ? 0xCDE88 : 0xCD5F8; //dev SRL launcher offset
|
||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) + 0x13);
|
||||
|
||||
//Construct BLX instructions:
|
||||
src1[0] = 0xF000 | ((((u32)dst - (u32)src1 - 4) & (0xFFF << 11)) >> 12);
|
||||
src1[1] = 0xE800 | ((((u32)dst - (u32)src1 - 4) & 0xFFF) >> 1);
|
||||
for(u32 r0 = 0x08000000; *off != 0xE3A01040; off++) //Until mov r1, #0x40
|
||||
{
|
||||
//Discard everything that's not str rX, [r0, #imm](!)
|
||||
if((*off & 0xFE5F0000) != 0xE4000000) continue;
|
||||
|
||||
src2[0] = 0xF000 | ((((u32)dst - (u32)src2 - 4) & (0xFFF << 11)) >> 12);
|
||||
src2[1] = 0xE800 | ((((u32)dst - (u32)src2 - 4) & 0xFFF) >> 1);
|
||||
u32 rD = (*off >> 12) & 0xF,
|
||||
offset = (*off & 0xFFF) * ((((*off >> 23) & 1) == 0) ? -1 : 1);
|
||||
bool writeback = ((*off >> 21) & 1) != 0,
|
||||
pre = ((*off >> 24) & 1) != 0;
|
||||
|
||||
u32 addr = r0 + ((pre || !writeback) ? offset : 0);
|
||||
if((addr & 7) != 0 && addr != 0x08000014 && addr != 0x08000004) *off = 0xE1A00000; //nop
|
||||
else *off = 0xE5800000 | (rD << 12) | (addr & 0xFFF); //Preserve IRQ and SVC handlers
|
||||
|
||||
if(!pre) addr += offset;
|
||||
if(writeback) r0 = addr;
|
||||
}
|
||||
}
|
||||
|
||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset)
|
||||
{
|
||||
//This function has to succeed. Crash if it doesn't (we'll get an exception dump of it anyways)
|
||||
|
||||
const u8 pattern[] = {0xE3, 0xDC, 0x05, 0xC0}, //Get TitleID from CodeSet
|
||||
pattern2[] = {0xE1, 0x0F, 0x00, 0xBD}; //Call exception dispatcher
|
||||
|
||||
u32 *loadCodeSet = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
|
||||
|
||||
*codeSetOffset = *loadCodeSet & 0xFFF;
|
||||
|
||||
return *(u32 *)(memsearch(pos, pattern2, size, sizeof(pattern2)) + 0xD);
|
||||
}
|
||||
|
||||
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address)
|
||||
{
|
||||
/* Stub svcBreak with "bkpt 65535" so we can debug the panic.
|
||||
Thanks @yellows8 and others for mentioning this idea on #3dsdev */
|
||||
|
||||
//Look for the svc handler
|
||||
const u8 pattern[] = {0x00, 0xE0, 0x4F, 0xE1}; //mrs lr, spsr
|
||||
|
||||
u32 *arm9SvcTable = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
while(*arm9SvcTable) arm9SvcTable++; //Look for SVC0 (NULL)
|
||||
|
||||
u32 *addr = (u32 *)(pos + arm9SvcTable[0x3C] - kernel9Address);
|
||||
*addr = 0xE12FFF7F;
|
||||
}
|
||||
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable)
|
||||
{
|
||||
//Same as above, for NATIVE_FIRM ARM11
|
||||
u32 *addr = (u32 *)(pos + arm11SvcTable[0x3C] - 0xFFF00000);
|
||||
*addr = 0xE12FFF7F;
|
||||
}
|
||||
|
||||
void patchKernel9Panic(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0xFF, 0xEA, 0x04, 0xD0};
|
||||
|
||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0x12);
|
||||
*off = 0xE12FFF7E;
|
||||
}
|
||||
|
||||
void patchKernel11Panic(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0x02, 0x0B, 0x44, 0xE2};
|
||||
|
||||
u32 *off = (u32 *)memsearch(pos, pattern, size, sizeof(pattern));
|
||||
*off = 0xE12FFF7E;
|
||||
}
|
||||
|
||||
void patchP9AccessChecks(u8 *pos, u32 size)
|
||||
{
|
||||
const u8 pattern[] = {0xE0, 0x00, 0x40, 0x39};
|
||||
|
||||
u16 *off = (u16 *)memsearch(pos, pattern, size, sizeof(pattern)) - 7;
|
||||
|
||||
off[0] = 0x2001; //mov r0, #1
|
||||
off[1] = 0x4770; //bx lr
|
||||
}
|
||||
|
||||
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler)
|
||||
{
|
||||
while(*arm11SvcHandler != 0xE11A0E1B) arm11SvcHandler++; //TST R10, R11,LSL LR
|
||||
*arm11SvcHandler = 0xE3B0A001; //MOVS R10, #1
|
||||
}
|
||||
|
||||
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space)
|
||||
{
|
||||
/* We have to detour a function in the ARM11 kernel because builtin modules
|
||||
are compressed in memory and are only decompressed at runtime */
|
||||
|
||||
//Check that we have enough free space
|
||||
if(*(u32 *)(*freeK11Space + k11modules_size - 4) == 0xFFFFFFFF)
|
||||
{
|
||||
//Inject our code into the free space
|
||||
memcpy(*freeK11Space, k11modules, k11modules_size);
|
||||
|
||||
//Look for the code that decompresses the .code section of the builtin modules
|
||||
const u8 pattern[] = {0xE5, 0x48, 0x00, 0x9D};
|
||||
|
||||
u32 *off = (u32 *)(memsearch(pos, pattern, size, sizeof(pattern)) - 0xB);
|
||||
|
||||
//Inject a jump (BL) instruction to our code at the offset we found
|
||||
*off = 0xEB000000 | (((((u32)*freeK11Space) - ((u32)off + 8)) >> 2) & 0xFFFFFF);
|
||||
|
||||
*freeK11Space += k11modules_size;
|
||||
}
|
||||
}
|
||||
|
||||
void patchUnitInfoValueSet(u8 *pos, u32 size)
|
||||
{
|
||||
//Look for UNITINFO value being set during kernel sync
|
||||
const u8 pattern[] = {0x01, 0x10, 0xA0, 0x13};
|
||||
|
||||
u8 *off = memsearch(pos, pattern, size, sizeof(pattern));
|
||||
|
||||
off[0] = isDevUnit ? 0 : 1;
|
||||
off[3] = 0xE3;
|
||||
}
|
||||
#endif
|
||||
@@ -20,6 +20,10 @@
|
||||
* Notices displayed by works containing it.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ARM11 modules patching code originally by Subv
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
@@ -47,16 +51,36 @@ typedef struct __attribute__((packed))
|
||||
u32 config;
|
||||
} CFWInfo;
|
||||
|
||||
extern bool isN3DS;
|
||||
extern u32 config;
|
||||
#ifdef DEV
|
||||
extern bool isDevUnit;
|
||||
#endif
|
||||
|
||||
u8 *getProcess9(u8 *pos, u32 size, u32 *process9Size, u32 *process9MemAddr);
|
||||
|
||||
#ifdef DEV
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space, u32 **arm11SvcHandler, u32 **arm11ExceptionsPage);
|
||||
#else
|
||||
u32 *getKernel11Info(u8 *pos, u32 size, u32 *baseK11VA, u8 **freeK11Space);
|
||||
#endif
|
||||
|
||||
void patchSignatureChecks(u8 *pos, u32 size);
|
||||
void patchTitleInstallMinVersionCheck(u8 *pos, u32 size);
|
||||
void patchFirmlaunches(u8 *pos, u32 size, u32 process9MemAddr);
|
||||
void patchFirmWrites(u8 *pos, u32 size);
|
||||
void patchFirmWriteSafe(u8 *pos, u32 size);
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 size);
|
||||
void implementSvcGetCFWInfo(u8 *pos, u32 size);
|
||||
void patchOldFirmWrites(u8 *pos, u32 size);
|
||||
void reimplementSvcBackdoor(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||
void implementSvcGetCFWInfo(u8 *pos, u32 *arm11SvcTable, u32 baseK11VA, u8 **freeK11Space);
|
||||
void applyLegacyFirmPatches(u8 *pos, FirmwareType firmType);
|
||||
void patchTwlBg(u8 *pos);
|
||||
|
||||
#ifdef DEV
|
||||
void patchArm9ExceptionHandlersInstall(u8 *pos, u32 size);
|
||||
u32 getInfoForArm11ExceptionHandlers(u8 *pos, u32 size, u32 *codeSetOffset);
|
||||
void patchSvcBreak9(u8 *pos, u32 size, u32 kernel9Address);
|
||||
void patchSvcBreak11(u8 *pos, u32 *arm11SvcTable);
|
||||
void patchKernel9Panic(u8 *pos, u32 size);
|
||||
void patchKernel11Panic(u8 *pos, u32 size);
|
||||
void patchP9AccessChecks(u8 *pos, u32 size);
|
||||
void patchArm11SvcAccessChecks(u32 *arm11SvcHandler);
|
||||
void patchK11ModuleChecks(u8 *pos, u32 size, u8 **freeK11Space);
|
||||
void patchUnitInfoValueSet(u8 *pos, u32 size);
|
||||
#endif
|
||||
156
source/pin.c
156
source/pin.c
@@ -21,11 +21,11 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* pin.c
|
||||
* Code to manage pin locking for 3ds. By reworks.
|
||||
* Code originally by reworks
|
||||
*/
|
||||
|
||||
#include "draw.h"
|
||||
#include "config.h"
|
||||
#include "screen.h"
|
||||
#include "utils.h"
|
||||
#include "memory.h"
|
||||
@@ -34,96 +34,121 @@
|
||||
#include "pin.h"
|
||||
#include "crypto.h"
|
||||
|
||||
bool readPin(PINData *out)
|
||||
static char pinKeyToLetter(u32 pressed)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) zeroes[16] = {0};
|
||||
u8 __attribute__((aligned(4))) tmp[32] = {0};
|
||||
|
||||
if(fileRead(out, "/luma/pin.bin") != sizeof(PINData)) return false;
|
||||
if(memcmp(out->magic, "PINF", 4) != 0) return false;
|
||||
|
||||
computePINHash(tmp, zeroes, 1);
|
||||
|
||||
return memcmp(out->testHash, tmp, 32) == 0; //test vector verification (SD card has (or hasn't) been used on another console)
|
||||
}
|
||||
|
||||
static inline char PINKeyToLetter(u32 pressed)
|
||||
{
|
||||
const char keys[] = "AB--------XY";
|
||||
const char keys[] = "AB--RLUD--XY";
|
||||
|
||||
u32 i;
|
||||
__asm__ volatile("clz %[i], %[pressed]" : [i] "=r" (i) : [pressed] "r" (pressed));
|
||||
for(i = 31; pressed > 1; i--) pressed /= 2;
|
||||
|
||||
return keys[31 - i];
|
||||
}
|
||||
|
||||
void newPin(void)
|
||||
void newPin(bool allowSkipping)
|
||||
{
|
||||
clearScreens();
|
||||
clearScreens(true, true);
|
||||
|
||||
drawString("Enter your NEW PIN: ", 10, 10, COLOR_WHITE);
|
||||
u8 length = 4 + 2 * (MULTICONFIG(PIN) - 1);
|
||||
|
||||
// Set the default value as 0x00 so we can check if there are any unentered characters.
|
||||
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0}; // pad to AES block length
|
||||
char *title = allowSkipping ? "Press START to skip or enter a new PIN" : "Enter a new PIN to proceed";
|
||||
drawString(title, true, 10, 10, COLOR_TITLE);
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
u32 cnt = 0;
|
||||
int charDrawPos = 20 * SPACING_X;
|
||||
//Pad to AES block length with zeroes
|
||||
u8 __attribute__((aligned(4))) enteredPassword[0x10] = {0};
|
||||
|
||||
while(cnt < PIN_LENGTH)
|
||||
u8 cnt = 0;
|
||||
u32 charDrawPos = 16 * SPACING_X;
|
||||
|
||||
while(cnt < length)
|
||||
{
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
pressed = waitInput();
|
||||
}
|
||||
while(!(pressed & PIN_BUTTONS & ~BUTTON_START));
|
||||
while(!(pressed & PIN_BUTTONS));
|
||||
|
||||
pressed &= PIN_BUTTONS & ~BUTTON_START;
|
||||
pressed &= PIN_BUTTONS;
|
||||
if(!allowSkipping) pressed &= ~BUTTON_START;
|
||||
|
||||
if(pressed & BUTTON_START) return;
|
||||
if(!pressed) continue;
|
||||
char key = PINKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; // add character to password.
|
||||
|
||||
// visualize character on screen.
|
||||
drawCharacter(key, 10 + charDrawPos, 10, COLOR_WHITE);
|
||||
char key = pinKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; //Add character to password
|
||||
|
||||
//Visualize character on screen
|
||||
drawCharacter(key, true, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
charDrawPos += 2 * SPACING_X;
|
||||
}
|
||||
|
||||
PINData pin = {0};
|
||||
u8 __attribute__((aligned(4))) tmp[32] = {0};
|
||||
u8 __attribute__((aligned(4))) zeroes[16] = {0};
|
||||
PinData pin;
|
||||
|
||||
memcpy(pin.magic, "PINF", 4);
|
||||
pin.formatVersionMajor = 1;
|
||||
pin.formatVersionMinor = 0;
|
||||
pin.formatVersionMajor = PIN_VERSIONMAJOR;
|
||||
pin.formatVersionMinor = PIN_VERSIONMINOR;
|
||||
pin.length = length;
|
||||
|
||||
computePINHash(tmp, zeroes, 1);
|
||||
memcpy(pin.testHash, tmp, 32);
|
||||
u8 __attribute__((aligned(4))) tmp[0x20];
|
||||
u8 __attribute__((aligned(4))) zeroes[0x10] = {0};
|
||||
|
||||
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
|
||||
memcpy(pin.hash, tmp, 32);
|
||||
computePinHash(tmp, zeroes);
|
||||
memcpy(pin.testHash, tmp, sizeof(tmp));
|
||||
|
||||
fileWrite(&pin, "/luma/pin.bin", sizeof(PINData));
|
||||
computePinHash(tmp, enteredPassword);
|
||||
memcpy(pin.hash, tmp, sizeof(tmp));
|
||||
|
||||
while(HID_PAD & PIN_BUTTONS);
|
||||
if(!fileWrite(&pin, PIN_PATH, sizeof(PinData)))
|
||||
error("Error writing the PIN file");
|
||||
}
|
||||
|
||||
void verifyPin(PINData *in)
|
||||
bool verifyPin(void)
|
||||
{
|
||||
PinData pin;
|
||||
|
||||
if(fileRead(&pin, PIN_PATH, sizeof(PinData)) != sizeof(PinData) ||
|
||||
memcmp(pin.magic, "PINF", 4) != 0 ||
|
||||
pin.formatVersionMajor != PIN_VERSIONMAJOR ||
|
||||
pin.formatVersionMinor != PIN_VERSIONMINOR ||
|
||||
pin.length != 4 + 2 * (MULTICONFIG(PIN) - 1))
|
||||
return false;
|
||||
|
||||
u8 __attribute__((aligned(4))) zeroes[0x10] = {0};
|
||||
u8 __attribute__((aligned(4))) tmp[0x20];
|
||||
|
||||
computePinHash(tmp, zeroes);
|
||||
|
||||
//Test vector verification (SD card has, or hasn't been used on another console)
|
||||
if(memcmp(pin.testHash, tmp, sizeof(tmp)) != 0) return false;
|
||||
|
||||
initScreens();
|
||||
|
||||
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE);
|
||||
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
//Pad to AES block length with zeroes
|
||||
u8 __attribute__((aligned(4))) enteredPassword[0x10] = {0};
|
||||
|
||||
// Set the default characters as 0x00 so we can check if there are any unentered characters.
|
||||
u8 __attribute__((aligned(4))) enteredPassword[16 * ((PIN_LENGTH + 15) / 16)] = {0};
|
||||
|
||||
u32 cnt = 0;
|
||||
bool unlock = false;
|
||||
int charDrawPos = 5 * SPACING_X;
|
||||
u8 cnt = 0;
|
||||
u32 charDrawPos = 16 * SPACING_X;
|
||||
|
||||
const char messagePath[] = "/luma/pinmessage.txt";
|
||||
|
||||
u32 messageSize = getFileSize(messagePath);
|
||||
if(messageSize > 0 && messageSize <= 800)
|
||||
{
|
||||
char message[messageSize + 1];
|
||||
fileRead(message, messagePath, 0);
|
||||
message[messageSize] = 0;
|
||||
drawString(message, false, 10, 10, COLOR_WHITE);
|
||||
}
|
||||
|
||||
while(!unlock)
|
||||
{
|
||||
drawString("Press START to shutdown or enter PIN to proceed", true, 10, 10, COLOR_TITLE);
|
||||
drawString("PIN ( digits): ", true, 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawCharacter('0' + pin.length, true, 10 + 5 * SPACING_X, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
u32 pressed;
|
||||
do
|
||||
{
|
||||
@@ -133,34 +158,33 @@ void verifyPin(PINData *in)
|
||||
|
||||
if(pressed & BUTTON_START) mcuPowerOff();
|
||||
|
||||
pressed &= PIN_BUTTONS & ~BUTTON_START;
|
||||
pressed &= PIN_BUTTONS;
|
||||
|
||||
if(!pressed) continue;
|
||||
|
||||
char key = PINKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; // add character to password.
|
||||
char key = pinKeyToLetter(pressed);
|
||||
enteredPassword[cnt++] = (u8)key; //Add character to password
|
||||
|
||||
// visualize character on screen.
|
||||
drawCharacter(key, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
//Visualize character on screen
|
||||
drawCharacter(key, true, 10 + charDrawPos, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
charDrawPos += 2 * SPACING_X;
|
||||
|
||||
if(cnt >= PIN_LENGTH)
|
||||
if(cnt >= pin.length)
|
||||
{
|
||||
u8 __attribute__((aligned(4))) tmp[32] = {0};
|
||||
|
||||
computePINHash(tmp, enteredPassword, (PIN_LENGTH + 15) / 16);
|
||||
unlock = memcmp(in->hash, tmp, 32) == 0;
|
||||
computePinHash(tmp, enteredPassword);
|
||||
unlock = memcmp(pin.hash, tmp, sizeof(tmp)) == 0;
|
||||
|
||||
if(!unlock)
|
||||
{
|
||||
charDrawPos = 5 * SPACING_X;
|
||||
charDrawPos = 16 * SPACING_X;
|
||||
cnt = 0;
|
||||
|
||||
clearScreens();
|
||||
clearScreens(true, false);
|
||||
|
||||
drawString("Press START to shutdown or enter pin to proceed.", 10, 10, COLOR_WHITE);
|
||||
drawString("Pin: ", 10, 10 + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawString("Wrong pin! Try again!", 10, 10 + 3 * SPACING_Y, COLOR_RED);
|
||||
drawString("Wrong PIN, try again", true, 10, 10 + 4 * SPACING_Y, COLOR_RED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
16
source/pin.h
16
source/pin.h
@@ -21,26 +21,26 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* pin.h
|
||||
*
|
||||
* Code to manage pin locking for 3ds. By reworks.
|
||||
* Code originally by reworks
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PIN_LENGTH 4
|
||||
#define PIN_PATH "/luma/pin.bin"
|
||||
#define PIN_VERSIONMAJOR 1
|
||||
#define PIN_VERSIONMINOR 2
|
||||
|
||||
typedef struct __attribute__((packed))
|
||||
{
|
||||
char magic[4];
|
||||
u16 formatVersionMajor, formatVersionMinor;
|
||||
|
||||
u8 length;
|
||||
u8 testHash[32];
|
||||
u8 hash[32];
|
||||
} PINData;
|
||||
} PinData;
|
||||
|
||||
bool readPin(PINData* out);
|
||||
void newPin(void);
|
||||
void verifyPin(PINData *in);
|
||||
void newPin(bool allowSkipping);
|
||||
bool verifyPin(void);
|
||||
@@ -21,8 +21,8 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others.
|
||||
* Screen deinit code by tiniVi.
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others
|
||||
* Screen deinit code by tiniVi
|
||||
*/
|
||||
|
||||
#include "screen.h"
|
||||
@@ -101,32 +101,45 @@ void updateBrightness(u32 brightnessIndex)
|
||||
invokeArm11Function(ARM11);
|
||||
}
|
||||
|
||||
void clearScreens(void)
|
||||
void clearScreens(bool clearTop, bool clearBottom)
|
||||
{
|
||||
static bool clearTopTmp,
|
||||
clearBottomTmp;
|
||||
clearTopTmp = clearTop;
|
||||
clearBottomTmp = clearBottom;
|
||||
|
||||
void __attribute__((naked)) ARM11(void)
|
||||
{
|
||||
//Disable interrupts
|
||||
__asm(".word 0xF10C01C0");
|
||||
|
||||
//Setting up two simultaneous memory fills using the GPU
|
||||
vu32 *REGs_PSC0 = (vu32 *)0x10400010;
|
||||
|
||||
vu32 *REGs_PSC0 = (vu32 *)0x10400010,
|
||||
*REGs_PSC1 = (vu32 *)0x10400020;
|
||||
|
||||
if(clearTopTmp)
|
||||
{
|
||||
REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address
|
||||
REGs_PSC0[1] = (u32)(fb->top_left + 0x46500) >> 3; //End address
|
||||
REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address
|
||||
REGs_PSC0[2] = 0; //Fill value
|
||||
REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start
|
||||
}
|
||||
|
||||
vu32 *REGs_PSC1 = (vu32 *)0x10400020;
|
||||
if(clearBottomTmp)
|
||||
{
|
||||
REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address
|
||||
REGs_PSC1[1] = (u32)(fb->bottom + 0x38400) >> 3; //End address
|
||||
REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address
|
||||
REGs_PSC1[2] = 0; //Fill value
|
||||
REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start
|
||||
}
|
||||
|
||||
while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2)));
|
||||
while(!((!clearTopTmp || (REGs_PSC0[3] & 2)) && (!clearBottomTmp || (REGs_PSC1[3] & 2))));
|
||||
|
||||
if(fb->top_right != fb->top_left)
|
||||
if(fb->top_right != fb->top_left && clearTopTmp)
|
||||
{
|
||||
REGs_PSC0[0] = (u32)fb->top_right >> 3; //Start address
|
||||
REGs_PSC0[1] = (u32)(fb->top_right + 0x46500) >> 3; //End address
|
||||
REGs_PSC0[1] = (u32)(fb->top_right + SCREEN_TOP_FBSIZE) >> 3; //End address
|
||||
REGs_PSC0[2] = 0; //Fill value
|
||||
REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start
|
||||
|
||||
@@ -136,6 +149,8 @@ void clearScreens(void)
|
||||
WAIT_FOR_ARM9();
|
||||
}
|
||||
|
||||
flushDCacheRange(&clearTopTmp, 1);
|
||||
flushDCacheRange(&clearBottomTmp, 1);
|
||||
flushDCacheRange((void *)fb, sizeof(struct fb));
|
||||
invokeArm11Function(ARM11);
|
||||
}
|
||||
@@ -147,7 +162,7 @@ void initScreens(void)
|
||||
//Disable interrupts
|
||||
__asm(".word 0xF10C01C0");
|
||||
|
||||
u32 brightnessLevel = brightness[MULTICONFIG(0)];
|
||||
u32 brightnessLevel = brightness[MULTICONFIG(BRIGHTNESS)];
|
||||
|
||||
*(vu32 *)0x10141200 = 0x1007F;
|
||||
*(vu32 *)0x10202014 = 0x00000001;
|
||||
@@ -242,18 +257,18 @@ void initScreens(void)
|
||||
|
||||
if(PDN_GPU_CNT == 1)
|
||||
{
|
||||
flushDCacheRange(&config, 4);
|
||||
flushDCacheRange(&configData, sizeof(CfgData));
|
||||
flushDCacheRange((void *)fb, sizeof(struct fb));
|
||||
invokeArm11Function(ARM11);
|
||||
|
||||
clearScreens();
|
||||
clearScreens(true, true);
|
||||
|
||||
//Turn on backlight
|
||||
i2cWriteRegister(I2C_DEV_MCU, 0x22, 0x2A);
|
||||
}
|
||||
else
|
||||
{
|
||||
clearScreens();
|
||||
updateBrightness(MULTICONFIG(0));
|
||||
clearScreens(true, true);
|
||||
updateBrightness(MULTICONFIG(BRIGHTNESS));
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,16 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others.
|
||||
* Screen deinit code by tiniVi.
|
||||
* Screen init code by dark_samus, bil1s, Normmatt, delebile and others
|
||||
* Screen deinit code by tiniVi
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
#define ARM11_STUB_ADDRESS (0x25000000 - 0x30) //It's currently only 0x28 bytes large. We're putting 0x30 just to be sure here
|
||||
#define WAIT_FOR_ARM9() *arm11Entry = 0; while(!*arm11Entry); ((void (*)())*arm11Entry)();
|
||||
|
||||
@@ -40,5 +42,5 @@ static volatile struct fb {
|
||||
|
||||
void deinitScreens(void);
|
||||
void updateBrightness(u32 brightnessIndex);
|
||||
void clearScreens(void);
|
||||
void clearScreens(bool clearTop, bool clearBottom);
|
||||
void initScreens(void);
|
||||
@@ -26,8 +26,8 @@
|
||||
_start:
|
||||
b start
|
||||
|
||||
.global launchedFirmTIDLow
|
||||
launchedFirmTIDLow:
|
||||
.global launchedFirmTidLow
|
||||
launchedFirmTidLow:
|
||||
.hword 0, 0, 0, 0, 0, 0, 0, 0
|
||||
|
||||
start:
|
||||
@@ -80,6 +80,7 @@ start:
|
||||
@ 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
|
||||
|
||||
55
source/strings.c
Normal file
55
source/strings.c
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 "strings.h"
|
||||
#include "memory.h"
|
||||
|
||||
u32 strlen(const char *string)
|
||||
{
|
||||
char *stringEnd = (char *)string;
|
||||
|
||||
while(*stringEnd) stringEnd++;
|
||||
|
||||
return stringEnd - string;
|
||||
}
|
||||
|
||||
void concatenateStrings(char *destination, const char *source)
|
||||
{
|
||||
u32 i = strlen(source),
|
||||
j = strlen(destination);
|
||||
|
||||
memcpy(&destination[j], source, i + 1);
|
||||
}
|
||||
|
||||
void hexItoa(u32 number, char *out, u32 digits)
|
||||
{
|
||||
const char hexDigits[] = "0123456789ABCDEF";
|
||||
u32 i = 0;
|
||||
|
||||
while(number > 0)
|
||||
{
|
||||
out[digits - 1 - i++] = hexDigits[number & 0xF];
|
||||
number >>= 4;
|
||||
}
|
||||
|
||||
while(i < digits) out[digits - 1 - i++] = '0';
|
||||
}
|
||||
29
source/strings.h
Normal file
29
source/strings.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
u32 strlen(const char *string);
|
||||
void concatenateStrings(char *destination, const char *source);
|
||||
void hexItoa(u32 number, char *out, u32 digits);
|
||||
@@ -26,13 +26,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define CFG_BOOTENV (*(vu32 *)0x10010000)
|
||||
#define CFG_UNITINFO (*(vu8 *)0x10010010)
|
||||
|
||||
#define PDN_MPCORE_CFG (*(vu32 *)0x10140FFC)
|
||||
#define PDN_SPI_CNT (*(vu32 *)0x101401C0)
|
||||
#define PDN_GPU_CNT (*(vu8 *)0x10141200)
|
||||
|
||||
//Common data types
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
@@ -43,18 +36,21 @@ typedef volatile u16 vu16;
|
||||
typedef volatile u32 vu32;
|
||||
typedef volatile u64 vu64;
|
||||
|
||||
//Used by multiple files:
|
||||
//Used by multiple files
|
||||
typedef enum FirmwareSource
|
||||
{
|
||||
FIRMWARE_SYSNAND = 0,
|
||||
FIRMWARE_EMUNAND = 1,
|
||||
FIRMWARE_EMUNAND2 = 2
|
||||
FIRMWARE_EMUNAND,
|
||||
FIRMWARE_EMUNAND2,
|
||||
FIRMWARE_EMUNAND3,
|
||||
FIRMWARE_EMUNAND4
|
||||
} FirmwareSource;
|
||||
|
||||
typedef enum FirmwareType
|
||||
{
|
||||
NATIVE_FIRM = 0,
|
||||
TWL_FIRM = 1,
|
||||
AGB_FIRM = 2,
|
||||
SAFE_FIRM = 3
|
||||
TWL_FIRM,
|
||||
AGB_FIRM,
|
||||
SAFE_FIRM,
|
||||
NATIVE_FIRM1X2X
|
||||
} FirmwareType;
|
||||
@@ -27,12 +27,10 @@
|
||||
#include "draw.h"
|
||||
#include "cache.h"
|
||||
|
||||
extern bool isFirmlaunch;
|
||||
|
||||
u32 waitInput(void)
|
||||
{
|
||||
u32 pressedKey = 0,
|
||||
key;
|
||||
bool pressedKey = false;
|
||||
u32 key;
|
||||
|
||||
//Wait for no keys to be pressed
|
||||
while(HID_PAD);
|
||||
@@ -45,10 +43,10 @@ u32 waitInput(void)
|
||||
key = HID_PAD;
|
||||
|
||||
//Make sure it's pressed
|
||||
for(u32 i = 0x13000; i; i--)
|
||||
for(u32 i = 0x13000; i > 0; i--)
|
||||
{
|
||||
if(key != HID_PAD) break;
|
||||
if(i == 1) pressedKey = 1;
|
||||
if(i == 1) pressedKey = true;
|
||||
}
|
||||
}
|
||||
while(!pressedKey);
|
||||
@@ -58,9 +56,10 @@ u32 waitInput(void)
|
||||
|
||||
void mcuReboot(void)
|
||||
{
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens();
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true);
|
||||
|
||||
flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
//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);
|
||||
@@ -68,19 +67,17 @@ void mcuReboot(void)
|
||||
|
||||
void mcuPowerOff(void)
|
||||
{
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens();
|
||||
if(!isFirmlaunch && PDN_GPU_CNT != 1) clearScreens(true, true);
|
||||
|
||||
flushEntireDCache(); //Ensure that all memory transfers have completed and that the data cache has been flushed
|
||||
//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);
|
||||
}
|
||||
|
||||
//TODO: add support for TIMER IRQ
|
||||
static inline void startChrono(u64 initialTicks)
|
||||
{
|
||||
//Based on a NATIVE_FIRM disassembly
|
||||
|
||||
REG_TIMER_CNT(0) = 0; //67MHz
|
||||
for(u32 i = 1; i < 4; i++) REG_TIMER_CNT(i) = 4; //Count-up
|
||||
|
||||
@@ -117,9 +114,9 @@ void error(const char *message)
|
||||
{
|
||||
initScreens();
|
||||
|
||||
drawString("An error has occurred:", 10, 10, COLOR_RED);
|
||||
int posY = drawString(message, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||
drawString("An error has occurred:", true, 10, 10, COLOR_RED);
|
||||
u32 posY = drawString(message, true, 10, 30, COLOR_WHITE);
|
||||
drawString("Press any button to shutdown", true, 10, posY + 2 * SPACING_Y, COLOR_WHITE);
|
||||
|
||||
waitInput();
|
||||
mcuPowerOff();
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#define REG_TIMER_CNT(i) *(vu16 *)(0x10003002 + 4 * i)
|
||||
#define REG_TIMER_VAL(i) *(vu16 *)(0x10003000 + 4 * i)
|
||||
|
||||
extern bool isFirmlaunch;
|
||||
|
||||
u32 waitInput(void);
|
||||
void mcuReboot(void);
|
||||
void mcuPowerOff(void);
|
||||
|
||||
Reference in New Issue
Block a user