├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── ccpp.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── Makefile ├── README.md ├── arm11 ├── Makefile ├── linker.ld └── source │ ├── main.c │ ├── memory.c │ ├── memory.h │ ├── start.s │ └── types.h ├── arm9 ├── Makefile ├── linker.ld └── source │ ├── firm.c │ ├── main.c │ ├── menu.h │ ├── patch.s │ └── start.s ├── bootloader ├── Makefile ├── linker.ld └── source │ ├── main.c │ └── start.s ├── docs ├── _config.yml ├── css │ └── style.css ├── index.md └── js │ └── main.js ├── linker.ld ├── logo.png └── streaming.py /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Multimegamander] 4 | patreon: ctrbrew 5 | custom: ['https://paypal.me/TaylorGenn/15'] 6 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Run a one-line script 13 | run: echo Hello, world! 14 | - name: Run a multi-line script 15 | run: | 16 | echo Add other actions to build, 17 | echo test, and deploy your project. 18 | -------------------------------------------------------------------------------- /.github/workflows/ccpp.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: configure 13 | run: ./configure 14 | - name: make 15 | run: make 16 | - name: make check 17 | run: make check 18 | - name: make distcheck 19 | run: make distcheck 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at multimegamander@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ctrbrew 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) 2 | 3 | ifeq ($(strip $(DEVKITARM)),) 4 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 5 | endif 6 | 7 | ifneq ($(strip $(shell firmtool -v 2>&1 | grep usage)),) 8 | $(error "Please install firmtool v1.1 or greater") 9 | endif 10 | 11 | include $(DEVKITARM)/base_tools 12 | 13 | name := MultiCTR 14 | revision := $(shell git describe --tags --match v[0-9]* --abbrev=8 | sed 's/-[0-9]*-g/-/') 15 | version_major := $(shell git describe --tags --match v[0-9]* | cut -c2- | cut -f1 -d- | cut -f1 -d.) 16 | version_minor := $(shell git describe --tags --match v[0-9]* | cut -c2- | cut -f1 -d- | cut -f2 -d.) 17 | version_build := $(shell git describe --tags --match v[0-9]* | cut -c2- | cut -f1 -d- | cut -f3 -d.) 18 | commit := $(shell git rev-parse --short=8 HEAD) 19 | is_release := 0 20 | 21 | ifeq ($(strip $(revision)),) 22 | revision := v0.0.0-0 23 | version_major := 0 24 | version_minor := 0 25 | version_build := 0 26 | endif 27 | 28 | ifeq ($(strip $(commit)),) 29 | commit := 0 30 | endif 31 | 32 | ifeq ($(strip $(version_build)),) 33 | version_build := 0 34 | endif 35 | 36 | ifeq ($(strip $(shell git describe --tags --match v[0-9]* | grep -)),) 37 | is_release := 1 38 | endif 39 | 40 | dir_arm9 := arm9 41 | dir_arm11 := arm11 42 | dir_bootloader := bootloader 43 | dir_exceptions := exceptions 44 | dir_arm9_exceptions := $(dir_exceptions)/arm9 45 | dir_k11_extension := k11_extension 46 | dir_sysmodules := sysmodules 47 | dir_loader := $(dir_sysmodules)/loader 48 | dir_ntr := $(dir_sysmodules)/ntr 49 | dir_build := build 50 | dir_out := out 51 | 52 | ASFLAGS := -mcpu=arm946e-s 53 | CFLAGS := -Wall -Wextra $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math 54 | LDFLAGS := -nostartfiles -Wl,--nmagic 55 | 56 | objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ 57 | $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ 58 | $(call rwildcard, $(dir_source), *.s *.c))) 59 | 60 | bundled = $(dir_build)/boorloader.bin.o $(dir_build)/arm9_exceptions.bin.o 61 | 62 | modules = $(dir_build)/loader.cxi $(dir_build)/ntr.cxi 63 | 64 | define bin2o 65 | bin2s $< | $(AS) -o $(@) 66 | endef 67 | 68 | .PHONY: all 69 | all: firm 70 | 71 | .PHONY: release 72 | release: $(dir_out)/$(name)$(revision).zip 73 | 74 | .PHONY: firm 75 | firm: $(dir_out)/MultiCTR.firm 76 | 77 | .PHONY: clean 78 | clean: 79 | @$(MAKE) -C $(dir_arm11) clean 80 | @$(MAKE) -C $(dir_bootloader) clean 81 | @$(MAKE) -C $(dir_arm9_exceptions) clean 82 | @$(MAKE) -C $(dir_k11_extension) clean 83 | @$(MAKE) -C $(dir_loader) clean 84 | @$(MAKE) -C $(dir_ntr) clean 85 | @rm -rf $(dir_out) $(dir_build) 86 | 87 | .PRECIOUS: $(dir_build)/%.bin 88 | 89 | .PHONY: $(dir_arm11) 90 | .PHONY: $(dir_bootloader) 91 | .PHONY: $(dir_arm9_exceptions) 92 | .PHONY: $(dir_k11_extension) 93 | .PHONY: $(dir_loader) 94 | .PHONY: $(dir_ntr) 95 | 96 | $(dir_out)/$(name)$(revision).zip: all 97 | @mkdir -p "$(@D)" 98 | @7z a -mx $@ ./$(@D)/* ./$(dir_exceptions)/exception_dump_parser.py 99 | 100 | $(dir_out)/MultiCTR.firm: $(dir_build)/arm11.elf $(dir_build)/arm9.elf $(dir_build)/k11_extension.bin 101 | @mkdir -p "$(@D)" 102 | @firmtool build $@ -D $^ -A 0x18180000 0x18000000 -C XDMA XDMA NDMA XDMA 103 | 104 | $(dir_build)/arm11.elf: $(dir_arm11) 105 | @mkdir -p "$(@D)" 106 | @$(MAKE) -C $< 107 | 108 | $(dir_build)/arm9.elf: $(bundled) $(objects) 109 | $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ 110 | 111 | $(dir_build)/k11_extension.bin: $(dir_k11_extension) 112 | @mkdir -p "$(@D)" 113 | @$(MAKE) -C $< 114 | 115 | $(dir_build)/loader.cxi: $(dir_loader) 116 | @mkdir -p "$(@D)" 117 | @$(MAKE) -C $< 118 | 119 | $(dir_build)/ntr.cxi: $(dir_rosalina) 120 | @mkdir -p "$(@D)" 121 | @$(MAKE) -C $< 122 | 123 | $(dir_build)/%.bin.o: $(dir_build)/%.bin 124 | @$(bin2o) 125 | 126 | $(dir_build)/bootloader.bin: $(dir_chainloader) 127 | @mkdir -p "$(@D)" 128 | @$(MAKE) -C $< 129 | 130 | $(dir_build)/arm9_exceptions.bin: $(dir_arm9_exceptions) 131 | @mkdir -p "$(@D)" 132 | @$(MAKE) -C $< 133 | 134 | $(dir_build)/%.bin: $(dir_patches)/%.s 135 | @mkdir -p "$(@D)" 136 | @armips $< 137 | 138 | $(dir_build)/memory.o $(dir_build)/strings.o: CFLAGS += -O3 139 | $(dir_build)/config.o: CFLAGS += -DCONFIG_TITLE="\"$(name) $(revision) configuration\"" 140 | 141 | $(dir_build)/bundled.h: $(bundled) 142 | @$(foreach f, $(bundled),\ 143 | echo "extern const u8" `(echo $(basename $(notdir $(f))) | sed -e 's/^\([0-9]\)/_\1/' | tr . _)`"[];" >> $@;\ 144 | echo "extern const u32" `(echo $(basename $(notdir $(f)))| sed -e 's/^\([0-9]\)/_\1/' | tr . _)`_size";" >> $@;\ 145 | ) 146 | 147 | $(dir_build)/%.o: $(dir_source)/%.c $(dir_build)/bundled.h 148 | @mkdir -p "$(@D)" 149 | $(COMPILE.c) $(OUTPUT_OPTION) $< 150 | 151 | $(dir_build)/%.o: $(dir_source)/%.s 152 | @mkdir -p "$(@D)" 153 | $(COMPILE.s) $(OUTPUT_OPTION) $< 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

11 | 12 | MultiCTR is a fully fledged 3DS CFW/bootloader that's mean to be versatile, flexable, and easy to build upon. 13 | 14 | 15 | ## Getting Started 16 | 17 | 18 | 19 | ### Prerequisites 20 | 21 | 22 | ``` 23 | DevkitARM 24 | ctrulib 25 | makefirm 26 | ``` 27 | 28 | ### Installing 29 | 30 | ## Built With 31 | 32 | ## Contributing 33 | 34 | Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests to us. 35 | 36 | ## Versioning 37 | 38 | We use [SemVer](http://semver.org/) for versioning. For the versions available, see the [tags on this repository](https://github.com/PurpleBooth/a-good-readme-template/tags). 39 | 40 | ## Authors 41 | 42 | * **[Multimegamander](https://github.com/Multimegamander)** - *Main dev* - 43 | * **[Aurora Wright](https://github.com/AuroraWright)** - *Luma3DS Creator* - 44 | 45 | See also the list of [contributors](https://github.com/ctrbrew/MultiCTR/contributors) who participated in this project. 46 | 47 | ## License 48 | 49 | This project is licensed under the [Massachusetts Institute of Technology](LICENSE.md) Creative Commons License - see the [LICENSE](LICENSE) file for details 50 | 51 | ## Acknowledgments 52 | 53 | 54 | -------------------------------------------------------------------------------- /arm11/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | 3 | ifeq ($(strip $(DEVKITARM)),) 4 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 5 | endif 6 | 7 | TOPDIR ?= $(CURDIR) 8 | include $(DEVKITARM)/base_rules 9 | 10 | TARGET := $(notdir $(CURDIR)) 11 | BUILD := build 12 | SOURCES := source source/svc 13 | DATA := data 14 | INCLUDES := include include/svc 15 | 16 | ARCH := -march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft 17 | DEFINES := -DARM11 -D_3DS 18 | 19 | CFLAGS := -g -std=gnu11 -Wall -Wextra -Werror -O2 -mword-relocations \ 20 | -fomit-frame-pointer -ffunction-sections -fdata-sections \ 21 | -Wno-main -fno-builtin $(ARCH) $(DEFINES) 22 | 23 | CFLAGS += $(INCLUDE) 24 | 25 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++11 26 | 27 | ASFLAGS := -g $(ARCH) 28 | LDFLAGS = -specs=$(TOPDIR)/linker.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 29 | 30 | LIBS := 31 | 32 | LIBDIRS := 33 | 34 | 35 | ifneq ($(BUILD),$(notdir $(CURDIR))) 36 | 37 | export OUTPUT := $(CURDIR)/$(TARGET) 38 | export TOPDIR := $(CURDIR) 39 | 40 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 41 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 42 | 43 | export DEPSDIR := $(CURDIR)/$(BUILD) 44 | 45 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 46 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 47 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 48 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 49 | 50 | ifeq ($(strip $(CPPFILES)),) 51 | export LD := $(CC) 52 | else 53 | export LD := $(CXX) 54 | endif 55 | 56 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 57 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 58 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 59 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 60 | 61 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 62 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 63 | -I$(CURDIR)/$(BUILD) 64 | 65 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 66 | 67 | .PHONY: $(BUILD) clean all 68 | 69 | all: $(BUILD) 70 | 71 | $(BUILD): 72 | @[ -d $@ ] || mkdir -p $@ 73 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 74 | 75 | clean: 76 | @echo clean ... 77 | @rm -fr $(BUILD) $(TARGET).bin $(TARGET).elf 78 | 79 | 80 | else 81 | .PHONY: all 82 | 83 | DEPENDS := $(OFILES:.o=.d) 84 | 85 | all : $(OUTPUT).bin 86 | 87 | $(OUTPUT).bin : $(OUTPUT).elf 88 | $(OBJCOPY) -S -O binary $< $@ 89 | @echo built ... $(notdir $@) 90 | 91 | $(OUTPUT).elf : $(OFILES) 92 | 93 | %.elf: $(OFILES) 94 | @echo linking $(notdir $@) 95 | @$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@ 96 | @$(NM) -CSn $@ > $(notdir $*.lst) 97 | 98 | $(OFILES_SRC) : $(HFILES_BIN) 99 | 100 | %.bin.o %_bin.h : %.bin 101 | @echo $(notdir $<) 102 | @$(bin2o) 103 | 104 | -include $(DEPENDS) 105 | 106 | endif 107 | -------------------------------------------------------------------------------- /arm11/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | ENTRY(_start) 5 | SECTIONS 6 | { 7 | . = 0x23F00000; 8 | 9 | .text.start : { *(.text.start) } 10 | .text : { *(.text) } 11 | .data : { *(.data) } 12 | .bss : { *(.bss COMMON) } 13 | .rodata : { *(.rodata) } 14 | 15 | . = ALIGN(4); 16 | } 17 | -------------------------------------------------------------------------------- /arm11/source/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Luma3DS 3 | * Copyright (C) 2016-2017 Aurora Wright, TuxSH 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | /* 28 | * Screen init code by dark_samus, bil1s, Normmatt, delebile and others 29 | * LCD deinit code by tiniVi 30 | */ 31 | 32 | #include "types.h" 33 | #include "memory.h" 34 | 35 | void prepareForFirmlaunch(void); 36 | extern u32 prepareForFirmlaunchSize; 37 | 38 | extern volatile Arm11Op operation; 39 | 40 | // *((vu32*)0x10202204) = 1<<24 | 0xFF; 41 | 42 | static void initScreens(u32 brightnessLevel, struct fb *fbs) 43 | { 44 | *(vu32 *)0x10141200 = 0x1007F; 45 | *(vu32 *)0x10202014 = 0x00000001; 46 | *(vu32 *)0x1020200C &= 0xFFFEFFFE; 47 | *(vu32 *)0x10202240 = brightnessLevel; 48 | *(vu32 *)0x10202A40 = brightnessLevel; 49 | *(vu32 *)0x10202244 = 0x1023E; 50 | *(vu32 *)0x10202A44 = 0x1023E; 51 | 52 | //Top screen 53 | *(vu32 *)0x10400400 = 0x000001c2; 54 | *(vu32 *)0x10400404 = 0x000000d1; 55 | *(vu32 *)0x10400408 = 0x000001c1; 56 | *(vu32 *)0x1040040c = 0x000001c1; 57 | *(vu32 *)0x10400410 = 0x00000000; 58 | *(vu32 *)0x10400414 = 0x000000cf; 59 | *(vu32 *)0x10400418 = 0x000000d1; 60 | *(vu32 *)0x1040041c = 0x01c501c1; 61 | *(vu32 *)0x10400420 = 0x00010000; 62 | *(vu32 *)0x10400424 = 0x0000019d; 63 | *(vu32 *)0x10400428 = 0x00000002; 64 | *(vu32 *)0x1040042c = 0x00000192; 65 | *(vu32 *)0x10400430 = 0x00000192; 66 | *(vu32 *)0x10400434 = 0x00000192; 67 | *(vu32 *)0x10400438 = 0x00000001; 68 | *(vu32 *)0x1040043c = 0x00000002; 69 | *(vu32 *)0x10400440 = 0x01960192; 70 | *(vu32 *)0x10400444 = 0x00000000; 71 | *(vu32 *)0x10400448 = 0x00000000; 72 | *(vu32 *)0x1040045C = 0x00f00190; 73 | *(vu32 *)0x10400460 = 0x01c100d1; 74 | *(vu32 *)0x10400464 = 0x01920002; 75 | *(vu32 *)0x10400468 = (u32)fbs[0].top_left; 76 | *(vu32 *)0x1040046C = (u32)fbs[1].top_left; 77 | *(vu32 *)0x10400470 = 0x80341; 78 | *(vu32 *)0x10400474 = 0x00010501; 79 | *(vu32 *)0x10400478 = 0; 80 | *(vu32 *)0x10400494 = (u32)fbs[0].top_right; 81 | *(vu32 *)0x10400498 = (u32)fbs[1].top_right; 82 | *(vu32 *)0x10400490 = 0x000002D0; 83 | *(vu32 *)0x1040049C = 0x00000000; 84 | 85 | //Disco register 86 | for(u32 i = 0; i < 256; i++) 87 | *(vu32 *)0x10400484 = 0x10101 * i; 88 | 89 | //Bottom screen 90 | *(vu32 *)0x10400500 = 0x000001c2; 91 | *(vu32 *)0x10400504 = 0x000000d1; 92 | *(vu32 *)0x10400508 = 0x000001c1; 93 | *(vu32 *)0x1040050c = 0x000001c1; 94 | *(vu32 *)0x10400510 = 0x000000cd; 95 | *(vu32 *)0x10400514 = 0x000000cf; 96 | *(vu32 *)0x10400518 = 0x000000d1; 97 | *(vu32 *)0x1040051c = 0x01c501c1; 98 | *(vu32 *)0x10400520 = 0x00010000; 99 | *(vu32 *)0x10400524 = 0x0000019d; 100 | *(vu32 *)0x10400528 = 0x00000052; 101 | *(vu32 *)0x1040052c = 0x00000192; 102 | *(vu32 *)0x10400530 = 0x00000192; 103 | *(vu32 *)0x10400534 = 0x0000004f; 104 | *(vu32 *)0x10400538 = 0x00000050; 105 | *(vu32 *)0x1040053c = 0x00000052; 106 | *(vu32 *)0x10400540 = 0x01980194; 107 | *(vu32 *)0x10400544 = 0x00000000; 108 | *(vu32 *)0x10400548 = 0x00000011; 109 | *(vu32 *)0x1040055C = 0x00f00140; 110 | *(vu32 *)0x10400560 = 0x01c100d1; 111 | *(vu32 *)0x10400564 = 0x01920052; 112 | *(vu32 *)0x10400568 = (u32)fbs[0].bottom; 113 | *(vu32 *)0x1040056C = (u32)fbs[1].bottom; 114 | *(vu32 *)0x10400570 = 0x80301; 115 | *(vu32 *)0x10400574 = 0x00010501; 116 | *(vu32 *)0x10400578 = 0; 117 | *(vu32 *)0x10400590 = 0x000002D0; 118 | *(vu32 *)0x1040059C = 0x00000000; 119 | 120 | //Disco register 121 | for(u32 i = 0; i < 256; i++) 122 | *(vu32 *)0x10400584 = 0x10101 * i; 123 | } 124 | 125 | static void setupFramebuffers(struct fb *fbs) 126 | { 127 | *(vu32 *)0x10400468 = (u32)fbs[0].top_left; 128 | *(vu32 *)0x1040046c = (u32)fbs[1].top_left; 129 | *(vu32 *)0x10400494 = (u32)fbs[0].top_right; 130 | *(vu32 *)0x10400498 = (u32)fbs[1].top_right; 131 | *(vu32 *)0x10400568 = (u32)fbs[0].bottom; 132 | *(vu32 *)0x1040056c = (u32)fbs[1].bottom; 133 | } 134 | 135 | static void clearScreens(struct fb *fb) 136 | { 137 | //Setting up two simultaneous memory fills using the GPU 138 | 139 | vu32 *REGs_PSC0 = (vu32 *)0x10400010, 140 | *REGs_PSC1 = (vu32 *)0x10400020; 141 | 142 | REGs_PSC0[0] = (u32)fb->top_left >> 3; //Start address 143 | REGs_PSC0[1] = (u32)(fb->top_left + SCREEN_TOP_FBSIZE) >> 3; //End address 144 | REGs_PSC0[2] = 0; //Fill value 145 | REGs_PSC0[3] = (2 << 8) | 1; //32-bit pattern; start 146 | 147 | REGs_PSC1[0] = (u32)fb->bottom >> 3; //Start address 148 | REGs_PSC1[1] = (u32)(fb->bottom + SCREEN_BOTTOM_FBSIZE) >> 3; //End address 149 | REGs_PSC1[2] = 0; //Fill value 150 | REGs_PSC1[3] = (2 << 8) | 1; //32-bit pattern; start 151 | 152 | while(!((REGs_PSC0[3] & 2) && (REGs_PSC1[3] & 2))); 153 | } 154 | 155 | static void swapFramebuffers(bool isAlternate) 156 | { 157 | u32 isAlternateTmp = isAlternate ? 1 : 0; 158 | *(vu32 *)0x10400478 = (*(vu32 *)0x10400478 & 0xFFFFFFFE) | isAlternateTmp; 159 | *(vu32 *)0x10400578 = (*(vu32 *)0x10400478 & 0xFFFFFFFE) | isAlternateTmp; 160 | } 161 | 162 | static void deinitScreens(void) 163 | { 164 | //Shutdown LCDs 165 | *(vu32 *)0x10202A44 = 0; 166 | *(vu32 *)0x10202244 = 0; 167 | *(vu32 *)0x10202014 = 0; 168 | } 169 | 170 | void main(void) 171 | { 172 | operation = ARM11_READY; 173 | 174 | while(true) 175 | { 176 | switch(operation) 177 | { 178 | case ARM11_READY: 179 | continue; 180 | case INIT_SCREENS: 181 | initScreens(*(vu32 *)ARM11_PARAMETERS_ADDRESS, (struct fb *)(ARM11_PARAMETERS_ADDRESS + 4)); 182 | break; 183 | case SETUP_FRAMEBUFFERS: 184 | setupFramebuffers((struct fb *)ARM11_PARAMETERS_ADDRESS); 185 | break; 186 | case CLEAR_SCREENS: 187 | clearScreens((struct fb *)ARM11_PARAMETERS_ADDRESS); 188 | break; 189 | case SWAP_FRAMEBUFFERS: 190 | swapFramebuffers(*(volatile bool *)ARM11_PARAMETERS_ADDRESS); 191 | break; 192 | case DEINIT_SCREENS: 193 | deinitScreens(); 194 | break; 195 | case PREPARE_ARM11_FOR_FIRMLAUNCH: 196 | memcpy((void*)0x1FFFFC00, (void*)prepareForFirmlaunch, prepareForFirmlaunchSize); 197 | *(vu32*)0x1FFFFFFC = 0; 198 | ((void(*)(u32, volatile Arm11Op*))0x1FFFFC00)(ARM11_READY, &operation); 199 | } 200 | operation = ARM11_READY; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /arm11/source/memory.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Luma3DS 3 | * Copyright (C) 2016-2017 Aurora Wright, TuxSH 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | /* 28 | * memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c 29 | */ 30 | 31 | #include "memory.h" 32 | 33 | void memcpy(void *dest, const void *src, u32 size) 34 | { 35 | u8 *destc = (u8 *)dest; 36 | const u8 *srcc = (const u8 *)src; 37 | 38 | for(u32 i = 0; i < size; i++) 39 | destc[i] = srcc[i]; 40 | } 41 | 42 | void memset(void *dest, u32 filler, u32 size) 43 | { 44 | u8 *destc = (u8 *)dest; 45 | 46 | for(u32 i = 0; i < size; i++) 47 | destc[i] = (u8)filler; 48 | } 49 | 50 | void memset32(void *dest, u32 filler, u32 size) 51 | { 52 | u32 *dest32 = (u32 *)dest; 53 | 54 | for(u32 i = 0; i < size / 4; i++) 55 | dest32[i] = filler; 56 | } 57 | -------------------------------------------------------------------------------- /arm11/source/memory.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Luma3DS 3 | * Copyright (C) 2016-2017 Aurora Wright, TuxSH 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | /* 28 | * memcpy adapted from https://github.com/mid-kid/CakesForeveryWan/blob/557a8e8605ab3ee173af6497486e8f22c261d0e2/source/memfuncs.c 29 | */ 30 | 31 | #pragma once 32 | 33 | #include "types.h" 34 | 35 | void memcpy(void *dest, const void *src, u32 size); 36 | void memset(void *dest, u32 value, u32 size) __attribute__((used)); 37 | void memset32(void *dest, u32 filler, u32 size); 38 | -------------------------------------------------------------------------------- /arm11/source/start.s: -------------------------------------------------------------------------------- 1 | @ This file is part of Luma3DS 2 | @ Copyright (C) 2016-2017 Aurora Wright, TuxSH 3 | @ 4 | @ This program is free software: you can redistribute it and/or modify 5 | @ it under the terms of the GNU General Public License as published by 6 | @ the Free Software Foundation, either version 3 of the License, or 7 | @ (at your option) any later version. 8 | @ 9 | @ This program is distributed in the hope that it will be useful, 10 | @ but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | @ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | @ GNU General Public License for more details. 13 | @ 14 | @ You should have received a copy of the GNU General Public License 15 | @ along with this program. If not, see . 16 | @ 17 | @ Additional Terms 7.b and 7.c of GPLv3 apply to this file: 18 | @ * Requiring preservation of specified reasonable legal notices or 19 | @ author attributions in that material or in the Appropriate Legal 20 | @ Notices displayed by works containing it. 21 | @ * Prohibiting misrepresentation of the origin of that material, 22 | @ or requiring that modified versions of such material be marked in 23 | @ reasonable ways as different from the original version. 24 | 25 | .section .text.start 26 | .align 4 27 | .global _start 28 | .type _start, %function 29 | _start: 30 | b start 31 | 32 | .global operation 33 | operation: 34 | .word 0 35 | 36 | start: 37 | @ Disable interrupts and switch to supervisor mode 38 | cpsid aif, #0x13 39 | 40 | @ Set the control register to reset default: everything disabled 41 | ldr r0, =0x54078 42 | mcr p15, 0, r0, c1, c0, 0 43 | 44 | @ Set the auxiliary control register to reset default. 45 | @ Enables instruction folding, static branch prediction, 46 | @ dynamic branch prediction, and return stack. 47 | mov r0, #0xF 48 | mcr p15, 0, r0, c1, c0, 1 49 | 50 | @ Invalidate both caches, flush the prefetch buffer then DSB 51 | mov r0, #0 52 | mcr p15, 0, r0, c7, c5, 4 53 | mcr p15, 0, r0, c7, c10, 4 54 | mcr p15, 0, r0, c7, c7, 0 55 | 56 | @ Clear BSS 57 | ldr r0, =__bss_start 58 | mov r1, #0 59 | ldr r2, =__bss_end 60 | sub r2, r0 61 | bl memset32 62 | 63 | ldr sp, =__stack_top__ 64 | b main 65 | 66 | 67 | .global prepareForFirmlaunch 68 | .type prepareForFirmlaunch, %function 69 | prepareForFirmlaunch: 70 | str r0, [r1] @ tell ARM9 we're done 71 | mov r0, #0x20000000 72 | 73 | _wait_for_core0_entrypoint_loop: 74 | ldr r1, [r0, #-4] @ check if core0's entrypoint is 0 75 | cmp r1, #0 76 | beq _wait_for_core0_entrypoint_loop 77 | 78 | bx r1 @ jump to core0's entrypoint 79 | prepareForFirmlaunchEnd: 80 | 81 | .global prepareForFirmlaunchSize 82 | prepareForFirmlaunchSize: .word prepareForFirmlaunchEnd - prepareForFirmlaunch 83 | -------------------------------------------------------------------------------- /arm11/source/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Luma3DS 3 | * Copyright (C) 2016-2017 Aurora Wright, TuxSH 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #pragma once 28 | 29 | #include 30 | #include 31 | 32 | //Common data types 33 | typedef uint8_t u8; 34 | typedef uint16_t u16; 35 | typedef uint32_t u32; 36 | typedef uint64_t u64; 37 | typedef volatile u8 vu8; 38 | typedef volatile u16 vu16; 39 | typedef volatile u32 vu32; 40 | typedef volatile u64 vu64; 41 | 42 | #define SCREEN_TOP_WIDTH 400 43 | #define SCREEN_BOTTOM_WIDTH 320 44 | #define SCREEN_HEIGHT 240 45 | #define SCREEN_TOP_FBSIZE (3 * SCREEN_TOP_WIDTH * SCREEN_HEIGHT) 46 | #define SCREEN_BOTTOM_FBSIZE (3 * SCREEN_BOTTOM_WIDTH * SCREEN_HEIGHT) 47 | #define ARM11_PARAMETERS_ADDRESS 0x1FFFF000 48 | 49 | struct fb { 50 | u8 *top_left; 51 | u8 *top_right; 52 | u8 *bottom; 53 | } __attribute__((packed)); 54 | 55 | typedef enum 56 | { 57 | INIT_SCREENS = 0, 58 | SETUP_FRAMEBUFFERS, 59 | CLEAR_SCREENS, 60 | SWAP_FRAMEBUFFERS, 61 | DEINIT_SCREENS, 62 | PREPARE_ARM11_FOR_FIRMLAUNCH, 63 | ARM11_READY, 64 | } Arm11Op; 65 | -------------------------------------------------------------------------------- /arm9/Makefile: -------------------------------------------------------------------------------- 1 | .SUFFIXES: 2 | -------------------------------------------------------------------------------- /arm9/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | ENTRY(_start) 5 | SECTIONS 6 | { 7 | . = 0x23F00000; 8 | 9 | .text.start : { *(.text.start) } 10 | .text : { *(.text) } 11 | .data : { *(.data) } 12 | .bss : { *(.bss COMMON) } 13 | .rodata : { *(.rodata) } 14 | 15 | . = ALIGN(4); 16 | } 17 | -------------------------------------------------------------------------------- /arm9/source/firm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Luma3DS 3 | * Copyright (C) 2016-2019 Aurora Wright, TuxSH 4 | * 5 | * This program is free software: you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation, either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program. If not, see . 17 | * 18 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 19 | * * Requiring preservation of specified reasonable legal notices or 20 | * author attributions in that material or in the Appropriate Legal 21 | * Notices displayed by works containing it. 22 | * * Prohibiting misrepresentation of the origin of that material, 23 | * or requiring that modified versions of such material be marked in 24 | * reasonable ways as different from the original version. 25 | */ 26 | 27 | #include "firm.h" 28 | #include "config.h" 29 | #include "utils.h" 30 | #include "fs.h" 31 | #include "exceptions.h" 32 | #include "patches.h" 33 | #include "memory.h" 34 | #include "cache.h" 35 | #include "emunand.h" 36 | #include "crypto.h" 37 | #include "screen.h" 38 | #include "fmt.h" 39 | #include "bootloader/bootloader.h" 40 | 41 | static Firm *firm = (Firm *)0x20001000; 42 | 43 | static __attribute__((noinline)) bool overlaps(u32 as, u32 ae, u32 bs, u32 be) 44 | { 45 | if(as <= bs && bs <= ae) 46 | return true; 47 | if(bs <= as && as <= be) 48 | return true; 49 | return false; 50 | } 51 | 52 | static __attribute__((noinline)) bool inRange(u32 as, u32 ae, u32 bs, u32 be) 53 | { 54 | if(as >= bs && ae <= be) 55 | return true; 56 | return false; 57 | } 58 | 59 | static bool checkFirm(u32 firmSize) 60 | { 61 | if(memcmp(firm->magic, "FIRM", 4) != 0 || firm->arm9Entry == NULL) //Allow for the ARM11 entrypoint to be zero in which case nothing is done on the ARM11 side 62 | return false; 63 | 64 | bool arm9EpFound = false, 65 | arm11EpFound = false; 66 | 67 | u32 size = 0x200; 68 | for(u32 i = 0; i < 4; i++) 69 | size += firm->section[i].size; 70 | 71 | if(firmSize < size) return false; 72 | 73 | for(u32 i = 0; i < 4; i++) 74 | { 75 | FirmSection *section = &firm->section[i]; 76 | 77 | //Allow empty sections 78 | if(section->size == 0) 79 | continue; 80 | 81 | if((section->offset < 0x200) || 82 | (section->address + section->size < section->address) || //Overflow check 83 | ((u32)section->address & 3) || (section->offset & 0x1FF) || (section->size & 0x1FF) || //Alignment check 84 | (overlaps((u32)section->address, (u32)section->address + section->size, (u32)firm, (u32)firm + size)) || 85 | ((!inRange((u32)section->address, (u32)section->address + section->size, 0x08000000, 0x08000000 + 0x00100000)) && 86 | (!inRange((u32)section->address, (u32)section->address + section->size, 0x18000000, 0x18000000 + 0x00600000)) && 87 | (!inRange((u32)section->address, (u32)section->address + section->size, 0x1FF00000, 0x1FFFFC00)) && 88 | (!inRange((u32)section->address, (u32)section->address + section->size, 0x20000000, 0x20000000 + 0x8000000)))) 89 | return false; 90 | 91 | __attribute__((aligned(4))) u8 hash[0x20]; 92 | 93 | sha(hash, (u8 *)firm + section->offset, section->size, SHA_256_MODE); 94 | 95 | if(memcmp(hash, section->hash, 0x20) != 0) 96 | return false; 97 | 98 | if(firm->arm9Entry >= section->address && firm->arm9Entry < (section->address + section->size)) 99 | arm9EpFound = true; 100 | 101 | if(firm->arm11Entry >= section->address && firm->arm11Entry < (section->address + section->size)) 102 | arm11EpFound = true; 103 | } 104 | 105 | return arm9EpFound && (firm->arm11Entry == NULL || arm11EpFound); 106 | } 107 | 108 | static inline u32 loadFirmFromStorage(FirmwareType firmType) 109 | { 110 | static const char *firmwareFiles[] = { 111 | "native.firm", 112 | "twl.firm", 113 | "agb.firm", 114 | "safe.firm", 115 | "sysupdater.firm" 116 | }, 117 | *cetkFiles[] = { 118 | "cetk", 119 | "cetk_twl", 120 | "cetk_agb", 121 | "cetk_safe", 122 | "cetk_sysupdater" 123 | }; 124 | 125 | u32 firmSize = fileRead(firm, firmwareFiles[(u32)firmType], 0x400000 + sizeof(Cxi) + 0x200); 126 | 127 | if(!firmSize) return 0; 128 | 129 | static const char *extFirmError = "The external FIRM is not valid."; 130 | 131 | if(firmSize <= sizeof(Cxi) + 0x200) error(extFirmError); 132 | 133 | if(memcmp(firm, "FIRM", 4) != 0) 134 | { 135 | if(firmSize <= sizeof(Cxi) + 0x400) error(extFirmError); 136 | 137 | u8 cetk[0xA50]; 138 | 139 | if(fileRead(cetk, cetkFiles[(u32)firmType], sizeof(cetk)) != sizeof(cetk)) 140 | error("The cetk is missing or corrupted."); 141 | 142 | firmSize = decryptNusFirm((Ticket *)(cetk + 0x140), (Cxi *)firm, firmSize); 143 | 144 | if(!firmSize) error("Unable to decrypt the external FIRM."); 145 | } 146 | 147 | if(!checkFirm(firmSize)) error("The external FIRM is invalid or corrupted."); 148 | 149 | return firmSize; 150 | } 151 | 152 | u32 loadNintendoFirm(FirmwareType *firmType, FirmwareSource nandType, bool loadFromStorage, bool isSafeMode) 153 | { 154 | u32 firmVersion, 155 | firmSize; 156 | 157 | bool ctrNandError = isSdMode && !mountFs(false, false); 158 | 159 | if(!ctrNandError) 160 | { 161 | //Load FIRM from CTRNAND 162 | firmVersion = firmRead(firm, (u32)*firmType); 163 | 164 | if(firmVersion == 0xFFFFFFFF) ctrNandError = true; 165 | else 166 | { 167 | firmSize = decryptExeFs((Cxi *)firm); 168 | 169 | if(!firmSize || !checkFirm(firmSize)) ctrNandError = true; 170 | } 171 | } 172 | 173 | bool loadedFromStorage = false; 174 | 175 | if(loadFromStorage || ctrNandError) 176 | { 177 | u32 result = loadFirmFromStorage(*firmType); 178 | 179 | if(result != 0) 180 | { 181 | loadedFromStorage = true; 182 | firmSize = result; 183 | } 184 | else if(ctrNandError) error("Unable to mount CTRNAND or load the CTRNAND FIRM.\nPlease use an external one."); 185 | } 186 | 187 | //Check that the FIRM is right for the console from the ARM9 section address 188 | if((firm->section[3].offset != 0 ? firm->section[3].address : firm->section[2].address) != (ISN3DS ? (u8 *)0x8006000 : (u8 *)0x8006800)) 189 | error("The %s FIRM is not for this console.", loadedFromStorage ? "external" : "CTRNAND"); 190 | 191 | if(!ISN3DS && *firmType == NATIVE_FIRM && firm->section[0].address == (u8 *)0x1FF80000) 192 | { 193 | //We can't boot < 3.x EmuNANDs 194 | if(nandType != FIRMWARE_SYSNAND) error("An old unsupported EmuNAND has been detected.\nLuma3DS is unable to boot it."); 195 | 196 | if(isSafeMode) error("SAFE_MODE is not supported on 1.x/2.x FIRM."); 197 | 198 | *firmType = NATIVE_FIRM1X2X; 199 | } 200 | 201 | if(loadedFromStorage || ISDEVUNIT) 202 | { 203 | firmVersion = 0xFFFFFFFF; 204 | 205 | if(!ISN3DS && *firmType == NATIVE_FIRM) 206 | { 207 | __attribute__((aligned(4))) static const u8 hashes[3][0x20] = { 208 | {0x39, 0x75, 0xB5, 0x28, 0x24, 0x5E, 0x8B, 0x56, 0xBC, 0x83, 0x79, 0x41, 0x09, 0x2C, 0x42, 0xE6, 209 | 0x26, 0xB6, 0x80, 0x59, 0xA5, 0x56, 0xF9, 0xF9, 0x6E, 0xF3, 0x63, 0x05, 0x58, 0xDF, 0x35, 0xEF}, 210 | {0x81, 0x9E, 0x71, 0x58, 0xE5, 0x44, 0x73, 0xF7, 0x48, 0x78, 0x7C, 0xEF, 0x5E, 0x30, 0xE2, 0x28, 211 | 0x78, 0x0B, 0x21, 0x23, 0x94, 0x63, 0xE8, 0x4E, 0x06, 0xBB, 0xD6, 0x8D, 0xA0, 0x99, 0xAE, 0x98}, 212 | {0x1D, 0xD5, 0xB0, 0xC2, 0xD9, 0x4A, 0x4A, 0xF3, 0x23, 0xDD, 0x2F, 0x65, 0x21, 0x95, 0x9B, 0x7E, 213 | 0xF2, 0x71, 0x7E, 0xB6, 0x7A, 0x3A, 0x74, 0x78, 0x0D, 0xE3, 0xB5, 0x0C, 0x2B, 0x7F, 0x85, 0x37} 214 | }; 215 | 216 | u32 i; 217 | for(i = 0; i < 3; i++) if(memcmp(firm->section[1].hash, hashes[i], 0x20) == 0) break; 218 | 219 | switch(i) 220 | { 221 | case 0: 222 | firmVersion = 0x18; 223 | break; 224 | case 1: 225 | firmVersion = 0x1D; 226 | break; 227 | case 2: 228 | firmVersion = 0x1F; 229 | break; 230 | } 231 | } 232 | } 233 | 234 | return firmVersion; 235 | } 236 | 237 | void loadHomebrewFirm(u32 pressed) 238 | { 239 | char path[10 + 255]; 240 | bool found = !pressed ? payloadMenu(path) : findPayload(path, pressed); 241 | 242 | if(!found) return; 243 | 244 | u32 maxPayloadSize = (u32)((u8 *)0x27FFE000 - (u8 *)firm), 245 | payloadSize = fileRead(firm, path, maxPayloadSize); 246 | 247 | if(payloadSize <= 0x200 || !checkFirm(payloadSize)) error("The payload is invalid or corrupted."); 248 | 249 | char absPath[24 + 255]; 250 | 251 | if(isSdMode) sprintf(absPath, "sdmc:/multi/%s", path); 252 | else sprintf(absPath, "nand:/rw/multi/%s", path); 253 | 254 | char *argv[2] = {absPath, (char *)fbs}; 255 | 256 | initScreens(); 257 | 258 | launchFirm((firm->reserved2[0] & 1) ? 2 : 1, argv); 259 | } 260 | 261 | static inline void mergeSection0(FirmwareType firmType, u32 firmVersion, bool loadFromStorage) 262 | { 263 | u32 srcModuleSize, 264 | nbModules = 0; 265 | 266 | struct 267 | { 268 | char name[8]; 269 | u8 *src; 270 | u32 size; 271 | } moduleList[6]; 272 | 273 | //1) Parse info concerning Nintendo's modules 274 | for(u8 *src = (u8 *)firm + firm->section[0].offset, *srcEnd = src + firm->section[0].size; src < srcEnd; src += srcModuleSize, nbModules++) 275 | { 276 | memcpy(moduleList[nbModules].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8); 277 | moduleList[nbModules].src = src; 278 | srcModuleSize = moduleList[nbModules].size = ((Cxi *)src)->ncch.contentSize * 0x200; 279 | } 280 | 281 | if(firmType == NATIVE_FIRM && (ISN3DS || firmVersion >= 0x1D)) 282 | { 283 | //2) Merge that info with our own modules' 284 | for(u8 *src = (u8 *)0x18180000; memcmp(((Cxi *)src)->ncch.magic, "NCCH", 4) == 0; src += srcModuleSize) 285 | { 286 | const char *name = ((Cxi *)src)->exHeader.systemControlInfo.appTitle; 287 | 288 | u32 i; 289 | 290 | for(i = 0; i < 5 && memcmp(name, moduleList[i].name, 8) != 0; i++); 291 | 292 | if(i == 5) 293 | { 294 | nbModules++; 295 | memcpy(moduleList[i].name, ((Cxi *)src)->exHeader.systemControlInfo.appTitle, 8); 296 | } 297 | 298 | moduleList[i].src = src; 299 | srcModuleSize = moduleList[i].size = ((Cxi *)src)->ncch.contentSize * 0x200; 300 | } 301 | } 302 | 303 | //3) Read or copy the modules 304 | u8 *dst = firm->section[0].address; 305 | const char *extModuleSizeError = "The external FIRM modules are too large."; 306 | for(u32 i = 0, dstModuleSize, maxModuleSize = firmType == NATIVE_FIRM ? 0x80000 : 0x600000; i < nbModules; i++, dst += dstModuleSize, maxModuleSize -= dstModuleSize) 307 | { 308 | if(loadFromStorage) 309 | { 310 | char fileName[24]; 311 | 312 | //Read modules from files if they exist 313 | sprintf(fileName, "sysmodules/%.8s.cxi", moduleList[i].name); 314 | 315 | dstModuleSize = getFileSize(fileName); 316 | 317 | if(dstModuleSize != 0) 318 | { 319 | if(dstModuleSize > maxModuleSize) error(extModuleSizeError); 320 | 321 | if(dstModuleSize <= sizeof(Cxi) + 0x200 || 322 | fileRead(dst, fileName, dstModuleSize) != dstModuleSize || 323 | memcmp(((Cxi *)dst)->ncch.magic, "NCCH", 4) != 0 || 324 | memcmp(moduleList[i].name, ((Cxi *)dst)->exHeader.systemControlInfo.appTitle, sizeof(((Cxi *)dst)->exHeader.systemControlInfo.appTitle)) != 0) 325 | error("An external FIRM module is invalid or corrupted."); 326 | 327 | continue; 328 | } 329 | } 330 | 331 | dstModuleSize = moduleList[i].size; 332 | 333 | if(dstModuleSize > maxModuleSize) error(extModuleSizeError); 334 | 335 | memcpy(dst, moduleList[i].src, dstModuleSize); 336 | } 337 | 338 | //4) Patch NATIVE_FIRM if necessary 339 | if(nbModules == 6) 340 | { 341 | if(patchK11ModuleLoading(firm->section[0].size, dst - firm->section[0].address, (u8 *)firm + firm->section[1].offset, firm->section[1].size) != 0) 342 | error("Failed to inject custom sysmodule"); 343 | } 344 | } 345 | 346 | u32 patchNativeFirm(u32 firmVersion, FirmwareSource nandType, bool loadFromStorage, bool isFirmProtEnabled, bool needToInitSd, bool doUnitinfoPatch) 347 | { 348 | u8 *arm9Section = (u8 *)firm + firm->section[2].offset, 349 | *arm11Section1 = (u8 *)firm + firm->section[1].offset; 350 | 351 | if(ISN3DS) 352 | { 353 | //Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader 354 | kernel9Loader((Arm9Bin *)arm9Section); 355 | firm->arm9Entry = (u8 *)0x801B01C; 356 | } 357 | 358 | //Find the Process9 .code location, size and memory address 359 | u32 process9Size, 360 | process9MemAddr; 361 | u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr); 362 | 363 | //Find the Kernel11 SVC table and handler, exceptions page and free space locations 364 | u32 baseK11VA; 365 | u8 *freeK11Space; 366 | u32 *arm11SvcHandler, 367 | *arm11ExceptionsPage, 368 | *arm11SvcTable = getKernel11Info(arm11Section1, firm->section[1].size, &baseK11VA, &freeK11Space, &arm11SvcHandler, &arm11ExceptionsPage); 369 | 370 | u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200, 371 | ret = 0; 372 | 373 | //Skip on FIRMs < 4.0 374 | if(ISN3DS || firmVersion >= 0x1D) 375 | { 376 | ret += installK11Extension(arm11Section1, firm->section[1].size, needToInitSd, baseK11VA, arm11ExceptionsPage, &freeK11Space); 377 | ret += patchKernel11(arm11Section1, firm->section[1].size, baseK11VA, arm11SvcTable, arm11ExceptionsPage); 378 | } 379 | 380 | //Apply signature patches 381 | ret += patchSignatureChecks(process9Offset, process9Size); 382 | 383 | //Apply EmuNAND patches 384 | if(nandType != FIRMWARE_SYSNAND) ret += patchEmuNand(arm9Section, kernel9Size, process9Offset, process9Size, firm->section[2].address, firmVersion); 385 | 386 | //Apply FIRM0/1 writes patches on SysNAND to protect A9LH 387 | else if(isFirmProtEnabled) ret += patchFirmWrites(process9Offset, process9Size); 388 | 389 | //Apply firmlaunch patches 390 | ret += patchFirmlaunches(process9Offset, process9Size, process9MemAddr); 391 | 392 | //Apply dev unit check patches related to NCCH encryption 393 | if(!ISDEVUNIT) 394 | { 395 | ret += patchZeroKeyNcchEncryptionCheck(process9Offset, process9Size); 396 | ret += patchNandNcchEncryptionCheck(process9Offset, process9Size); 397 | } 398 | 399 | //Apply anti-anti-DG patches on 11.0+ 400 | if(firmVersion >= (ISN3DS ? 0x21 : 0x52)) ret += patchTitleInstallMinVersionChecks(process9Offset, process9Size, firmVersion); 401 | 402 | //Patch P9 AM ticket wrapper on 11.8+ to use 0 Key and IV, only with UNITINFO patch on to prevent NIM from actually sending any 403 | if(doUnitinfoPatch && firmVersion >= (ISN3DS ? 0x35 : 0x64)) ret += patchP9AMTicketWrapperZeroKeyIV(process9Offset, process9Size, firmVersion); 404 | 405 | //Apply UNITINFO patches 406 | if(doUnitinfoPatch) 407 | { 408 | ret += patchUnitInfoValueSet(arm9Section, kernel9Size); 409 | if(!ISDEVUNIT) ret += patchCheckForDevCommonKey(process9Offset, process9Size); 410 | } 411 | 412 | //ARM9 exception handlers 413 | ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size); 414 | ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address); 415 | ret += patchKernel9Panic(arm9Section, kernel9Size); 416 | 417 | ret += patchP9AccessChecks(process9Offset, process9Size); 418 | 419 | mergeSection0(NATIVE_FIRM, firmVersion, loadFromStorage); 420 | firm->section[0].size = 0; 421 | 422 | return ret; 423 | } 424 | 425 | u32 patchTwlFirm(u32 firmVersion, bool loadFromStorage, bool doUnitinfoPatch) 426 | { 427 | u8 *arm9Section = (u8 *)firm + firm->section[3].offset; 428 | 429 | //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader 430 | if(ISN3DS) 431 | { 432 | kernel9Loader((Arm9Bin *)arm9Section); 433 | firm->arm9Entry = (u8 *)0x801301C; 434 | } 435 | 436 | //Find the Process9 .code location, size and memory address 437 | u32 process9Size, 438 | process9MemAddr; 439 | u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr); 440 | 441 | u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200, 442 | ret = 0; 443 | 444 | ret += patchLgySignatureChecks(process9Offset, process9Size); 445 | ret += patchTwlInvalidSignatureChecks(process9Offset, process9Size); 446 | ret += patchTwlNintendoLogoChecks(process9Offset, process9Size); 447 | ret += patchTwlWhitelistChecks(process9Offset, process9Size); 448 | if(ISN3DS || firmVersion > 0x11) ret += patchTwlFlashcartChecks(process9Offset, process9Size, firmVersion); 449 | else if(!ISN3DS && firmVersion == 0x11) ret += patchOldTwlFlashcartChecks(process9Offset, process9Size); 450 | ret += patchTwlShaHashChecks(process9Offset, process9Size); 451 | 452 | //Apply UNITINFO patch 453 | if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size); 454 | 455 | if(loadFromStorage) 456 | { 457 | mergeSection0(TWL_FIRM, 0, true); 458 | firm->section[0].size = 0; 459 | } 460 | 461 | return ret; 462 | } 463 | 464 | u32 patchAgbFirm(bool loadFromStorage, bool doUnitinfoPatch) 465 | { 466 | u8 *arm9Section = (u8 *)firm + firm->section[3].offset; 467 | 468 | //On N3DS, decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader 469 | if(ISN3DS) 470 | { 471 | kernel9Loader((Arm9Bin *)arm9Section); 472 | firm->arm9Entry = (u8 *)0x801301C; 473 | } 474 | 475 | //Find the Process9 .code location, size and memory address 476 | u32 process9Size, 477 | process9MemAddr; 478 | u8 *process9Offset = getProcess9Info(arm9Section, firm->section[3].size, &process9Size, &process9MemAddr); 479 | 480 | u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200, 481 | ret = 0; 482 | 483 | ret += patchLgySignatureChecks(process9Offset, process9Size); 484 | if(CONFIG(SHOWGBABOOT)) ret += patchAgbBootSplash(process9Offset, process9Size); 485 | 486 | //Apply UNITINFO patch 487 | if(doUnitinfoPatch) ret += patchUnitInfoValueSet(arm9Section, kernel9Size); 488 | 489 | if(loadFromStorage) 490 | { 491 | mergeSection0(AGB_FIRM, 0, true); 492 | firm->section[0].size = 0; 493 | } 494 | 495 | return ret; 496 | } 497 | 498 | u32 patch1x2xNativeAndSafeFirm(void) 499 | { 500 | u8 *arm9Section = (u8 *)firm + firm->section[2].offset; 501 | 502 | if(ISN3DS) 503 | { 504 | //Decrypt ARM9Bin and patch ARM9 entrypoint to skip kernel9loader 505 | kernel9Loader((Arm9Bin *)arm9Section); 506 | firm->arm9Entry = (u8 *)0x801B01C; 507 | } 508 | 509 | //Find the Process9 .code location, size and memory address 510 | u32 process9Size, 511 | process9MemAddr; 512 | u8 *process9Offset = getProcess9Info(arm9Section, firm->section[2].size, &process9Size, &process9MemAddr); 513 | 514 | u32 kernel9Size = (u32)(process9Offset - arm9Section) - sizeof(Cxi) - 0x200, 515 | ret = 0; 516 | 517 | ret += ISN3DS ? patchFirmWrites(process9Offset, process9Size) : patchOldFirmWrites(process9Offset, process9Size); 518 | 519 | ret += ISN3DS ? patchSignatureChecks(process9Offset, process9Size) : patchOldSignatureChecks(process9Offset, process9Size); 520 | 521 | //ARM9 exception handlers 522 | ret += patchArm9ExceptionHandlersInstall(arm9Section, kernel9Size); 523 | ret += patchSvcBreak9(arm9Section, kernel9Size, (u32)firm->section[2].address); 524 | 525 | return ret; 526 | } 527 | 528 | void launchFirm(int argc, char **argv) 529 | { 530 | prepareArm11ForFirmlaunch(); 531 | bootload(argc, argv, firm); 532 | } 533 | -------------------------------------------------------------------------------- /arm9/source/main.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * This file is part of Luma3DS 4 | * Copyright (C) 2016-2019 Aurora Wright, TuxSH 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | * 19 | * Additional Terms 7.b and 7.c of GPLv3 apply to this file: 20 | * * Requiring preservation of specified reasonable legal notices or 21 | * author attributions in that material or in the Appropriate Legal 22 | * Notices displayed by works containing it. 23 | * * Prohibiting misrepresentation of the origin of that material, 24 | * or requiring that modified versions of such material be marked in 25 | * reasonable ways as different from the original version. 26 | */ 27 | 28 | #include 29 | #include 30 | #include "menu.h" 31 | 32 | void main() { 33 | bool isFirmProtEnabled, 34 | isSafeMode = false, 35 | needToInitSd = false, 36 | isNoForceFlagSet = false, 37 | isInvalidLoader = false, 38 | isNtrBoot; 39 | FirmwareType firmType; 40 | FirmwareSource nandType; 41 | const vu8 *bootMediaStatus = (const vu8 *)0x1FFFE00C; 42 | const vu32 *bootPartitionsStatus = (const vu32 *)0x1FFFE010; 43 | u32 firmlaunchTidLow = 0; 44 | 45 | //Shell closed, no error booting NTRCARD, NAND paritions not even considered 46 | isNtrBoot = bootMediaStatus[3] == 2 && !bootMediaStatus[1] && !bootPartitionsStatus[0] && !bootPartitionsStatus[1]; 47 | 48 | if((magicWord & 0xFFFF) == 0xBEEF && argc >= 1) //Normal (B9S) boot 49 | { 50 | bootType = isNtrBoot ? B9SNTR : B9S; 51 | 52 | u32 i; 53 | for(i = 0; i < sizeof(launchedPath)/2 - 1 && argv[0][i] != 0; i++) //Copy and convert the path to UTF-16 54 | launchedPath[i] = argv[0][i]; 55 | launchedPath[i] = 0; 56 | } 57 | else if(magicWord == 0xBABE && argc == 2) //Firmlaunch 58 | { 59 | bootType = FIRMLAUNCH; 60 | 61 | u32 i; 62 | u16 *p = (u16 *)argv[0]; 63 | for(i = 0; i < sizeof(launchedPath)/2 - 1 && p[i] != 0; i++) 64 | launchedPath[i] = p[i]; 65 | launchedPath[i] = 0; 66 | 67 | for(i = 0; i < 8; i++) 68 | firmlaunchTidLow = (argv[1][2 * i] > '9' ? argv[1][2 * i] - 'a' + 10 : argv[1][2 * i] - '0') | (firmlaunchTidLow << 4); 69 | } 70 | else if(magicWord == 0xB002) //FIRM/NTRCARD boot 71 | { 72 | if(isNtrBoot) bootType = NTR; 73 | else 74 | { 75 | const char *path; 76 | if(!((vu8 *)bootPartitionsStatus)[2]) 77 | { 78 | bootType = FIRM0; 79 | path = "firm0:"; 80 | } 81 | else 82 | { 83 | bootType = FIRM1; 84 | path = "firm1:"; 85 | } 86 | 87 | for(u32 i = 0; i < 7; i++) //Copy and convert the path to UTF-16 88 | launchedPath[i] = path[i]; 89 | } 90 | 91 | setupKeyslots(); 92 | } 93 | else isInvalidLoader = true; 94 | 95 | // Set up the additional sections, overwrites argc 96 | memcpy(__itcm_start__, __itcm_lma__, __itcm_bss_start__ - __itcm_start__); 97 | memset(__itcm_bss_start__, 0, __itcm_end__ - __itcm_bss_start__); 98 | I2C_init(); 99 | if(isInvalidLoader) error("Launched using an unsupported loader."); 100 | 101 | installArm9Handlers(); 102 | 103 | if(memcmp(launchedPath, u"sdmc", 8) == 0) 104 | { 105 | if(!mountFs(true, false)) error("Failed to mount SD."); 106 | isSdMode = true; 107 | } 108 | else if(memcmp(launchedPath, u"nand", 8) == 0) 109 | { 110 | if(!mountFs(false, true)) error("Failed to mount CTRNAND."); 111 | isSdMode = false; 112 | } 113 | else if(bootType == NTR || memcmp(launchedPath, u"firm", 8) == 0) 114 | { 115 | if(mountFs(true, false)) isSdMode = true; 116 | else if(mountFs(false, true)) isSdMode = false; 117 | else error("Failed to mount SD and CTRNAND."); 118 | 119 | if(bootType == NTR) 120 | { 121 | while(HID_PAD & NTRBOOT_BUTTONS); 122 | loadHomebrewFirm(0); 123 | mcuPowerOff(); 124 | } 125 | } 126 | else 127 | { 128 | char mountPoint[5]; 129 | 130 | u32 i; 131 | for(i = 0; i < 4 && launchedPath[i] != u':'; i++) 132 | mountPoint[i] = (char)launchedPath[i]; 133 | mountPoint[i] = 0; 134 | 135 | error("Launched from an unsupported location: %s.", mountPoint); 136 | } 137 | 138 | detectAndProcessExceptionDumps(); 139 | 140 | //Attempt to read the configuration file 141 | needConfig = readConfig() ? MODIFY_CONFIGURATION : CREATE_CONFIGURATION; 142 | 143 | //Determine if this is a firmlaunch boot 144 | if(bootType == FIRMLAUNCH) 145 | { 146 | if(needConfig == CREATE_CONFIGURATION) mcuPowerOff(); 147 | 148 | switch(firmlaunchTidLow & 0xF) 149 | { 150 | case 2: 151 | firmType = (FirmwareType)((firmlaunchTidLow >> 8) & 0xF); 152 | break; 153 | case 3: 154 | firmType = SAFE_FIRM; 155 | break; 156 | case 1: 157 | firmType = SYSUPDATER_FIRM; 158 | break; 159 | } 160 | 161 | nandType = (FirmwareSource)BOOTCFG_NAND; 162 | firmSource = (FirmwareSource)BOOTCFG_FIRM; 163 | isFirmProtEnabled = !BOOTCFG_NTRCARDBOOT; 164 | 165 | goto boot; 166 | } 167 | 168 | firmType = NATIVE_FIRM; 169 | isFirmProtEnabled = bootType != NTR; 170 | 171 | //Get pressed buttons 172 | u32 pressed = HID_PAD; 173 | 174 | //If it's a MCU reboot, try to force boot options 175 | if(CFG_BOOTENV && needConfig != CREATE_CONFIGURATION) 176 | { 177 | //Always force a SysNAND boot when quitting AGB_FIRM 178 | if(CFG_BOOTENV == 7) 179 | { 180 | nandType = FIRMWARE_SYSNAND; 181 | firmSource = (BOOTCFG_NAND != 0) == (BOOTCFG_FIRM != 0) ? FIRMWARE_SYSNAND : (FirmwareSource)BOOTCFG_FIRM; 182 | 183 | //Prevent multiple boot options-forcing 184 | if(nandType != BOOTCFG_NAND || firmSource != BOOTCFG_FIRM) isNoForceFlagSet = true; 185 | 186 | goto boot; 187 | } 188 | 189 | //Account for DSiWare soft resets if exiting TWL_FIRM 190 | if(CFG_BOOTENV == 3) 191 | { 192 | static const u8 TLNC[] = {0x54, 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x43}; 193 | if(memcmp((void *)0x20000C00, TLNC, 10) == 0) needToInitSd = true; 194 | } 195 | 196 | /* Force the last used boot options if autobooting a TWL title, or unless a button is pressed 197 | or the no-forcing flag is set */ 198 | if(needToInitSd || memcmp((void *)0x20000300, "TLNC", 4) == 0 || (!pressed && !BOOTCFG_NOFORCEFLAG)) 199 | { 200 | nandType = (FirmwareSource)BOOTCFG_NAND; 201 | firmSource = (FirmwareSource)BOOTCFG_FIRM; 202 | 203 | goto boot; 204 | } 205 | } 206 | 207 | u32 pinMode = MULTICONFIG(PIN); 208 | bool pinExists = pinMode != 0 && verifyPin(pinMode); 209 | 210 | //If no configuration file exists or SELECT is held or if booted from NTRCARD, load configuration menu 211 | bool shouldLoadConfigMenu = needConfig == CREATE_CONFIGURATION || ((pressed & (BUTTON_SELECT | BUTTON_L1)) == BUTTON_SELECT); 212 | 213 | if(shouldLoadConfigMenu) 214 | { 215 | configMenu(pinExists, pinMode); 216 | 217 | //Update pressed buttons 218 | pressed = HID_PAD; 219 | } 220 | 221 | if(!CFG_BOOTENV && pressed == SAFE_MODE) 222 | { 223 | nandType = FIRMWARE_SYSNAND; 224 | firmSource = FIRMWARE_SYSNAND; 225 | 226 | isSafeMode = true; 227 | needToInitSd = true; 228 | 229 | //If the PIN has been verified, wait to make it easier to press the SAFE_MODE combo 230 | if(pinExists && !shouldLoadConfigMenu) 231 | { 232 | while(HID_PAD & PIN_BUTTONS); 233 | wait(2000ULL); 234 | } 235 | 236 | goto boot; 237 | } 238 | 239 | u32 splashMode = MULTICONFIG(SPLASH); 240 | 241 | if(splashMode == 1 && loadSplash()) pressed = HID_PAD; 242 | 243 | bool autoBootEmu = CONFIG(AUTOBOOTEMU); 244 | 245 | if((pressed & (BUTTON_START | BUTTON_L1)) == BUTTON_START) 246 | { 247 | loadHomebrewFirm(0); 248 | pressed = HID_PAD; 249 | } 250 | else if((((pressed & SINGLE_PAYLOAD_BUTTONS) || (!autoBootEmu && (pressed & DPAD_BUTTONS))) && !(pressed & (BUTTON_L1 | BUTTON_R1))) || 251 | (((pressed & L_PAYLOAD_BUTTONS) || (autoBootEmu && (pressed & DPAD_BUTTONS))) && (pressed & BUTTON_L1))) loadHomebrewFirm(pressed); 252 | 253 | if(splashMode == 2) loadSplash(); 254 | 255 | //If booting from CTRNAND, always use SysNAND 256 | if(!isSdMode) nandType = FIRMWARE_SYSNAND; 257 | 258 | //If R is pressed, boot the non-updated NAND with the FIRM of the opposite one 259 | else if(pressed & BUTTON_R1) 260 | { 261 | if(CONFIG(USEEMUFIRM)) 262 | { 263 | nandType = FIRMWARE_SYSNAND; 264 | firmSource = FIRMWARE_EMUNAND; 265 | } 266 | else 267 | { 268 | nandType = FIRMWARE_EMUNAND; 269 | firmSource = FIRMWARE_SYSNAND; 270 | } 271 | } 272 | 273 | /* Else, boot the NAND the user set to autoboot or the opposite one, depending on L, 274 | with their own FIRM */ 275 | else firmSource = nandType = (autoBootEmu == ((pressed & BUTTON_L1) == BUTTON_L1)) ? FIRMWARE_SYSNAND : FIRMWARE_EMUNAND; 276 | 277 | //If we're booting EmuNAND or using EmuNAND FIRM, determine which one from the directional pad buttons, or otherwise from the config 278 | if(nandType == FIRMWARE_EMUNAND || firmSource == FIRMWARE_EMUNAND) 279 | { 280 | FirmwareSource tempNand; 281 | switch(pressed & DPAD_BUTTONS) 282 | { 283 | case BUTTON_UP: 284 | tempNand = FIRMWARE_EMUNAND; 285 | break; 286 | case BUTTON_RIGHT: 287 | tempNand = FIRMWARE_EMUNAND2; 288 | break; 289 | case BUTTON_DOWN: 290 | tempNand = FIRMWARE_EMUNAND3; 291 | break; 292 | case BUTTON_LEFT: 293 | tempNand = FIRMWARE_EMUNAND4; 294 | break; 295 | default: 296 | tempNand = (FirmwareSource)(1 + MULTICONFIG(DEFAULTEMU)); 297 | break; 298 | } 299 | 300 | if(nandType == FIRMWARE_EMUNAND) nandType = tempNand; 301 | else firmSource = tempNand; 302 | } 303 | boot: 304 | 305 | //If we need to boot EmuNAND, make sure it exists 306 | if(nandType != FIRMWARE_SYSNAND) 307 | { 308 | locateEmuNand(&nandType); 309 | if(nandType == FIRMWARE_SYSNAND) firmSource = FIRMWARE_SYSNAND; 310 | else if((*(vu16 *)(SDMMC_BASE + REG_SDSTATUS0) & TMIO_STAT0_WRPROTECT) == 0) //Make sure the SD card isn't write protected 311 | error("The SD card is locked, EmuNAND can not be used.\nPlease turn the write protection switch on the side of the SD Card off."); 312 | } 313 | 314 | //Same if we're using EmuNAND as the FIRM source 315 | else if(firmSource != FIRMWARE_SYSNAND) 316 | locateEmuNand(&firmSource); 317 | 318 | if(bootType != FIRMLAUNCH) 319 | { 320 | configData.bootConfig = ((bootType == NTR ? 1 : 0) << 7) | ((u32)isNoForceFlagSet << 6) | ((u32)firmSource << 3) | (u32)nandType; 321 | writeConfig(false); 322 | } 323 | 324 | bool loadFromStorage = CONFIG(LOADEXTFIRMSANDMODULES); 325 | u32 firmVersion = loadNintendoFirm(&firmType, firmSource, loadFromStorage, isSafeMode); 326 | 327 | bool doUnitinfoPatch = CONFIG(PATCHUNITINFO); 328 | u32 res = 0; 329 | switch(firmType) 330 | { 331 | case NATIVE_FIRM: 332 | res = patchNativeFirm(firmVersion, nandType, loadFromStorage, isFirmProtEnabled, needToInitSd, doUnitinfoPatch); 333 | break; 334 | case TWL_FIRM: 335 | res = patchTwlFirm(firmVersion, loadFromStorage, doUnitinfoPatch); 336 | break; 337 | case AGB_FIRM: 338 | res = patchAgbFirm(loadFromStorage, doUnitinfoPatch); 339 | break; 340 | case SAFE_FIRM: 341 | case SYSUPDATER_FIRM: 342 | case NATIVE_FIRM1X2X: 343 | res = patch1x2xNativeAndSafeFirm(); 344 | break; 345 | } 346 | 347 | if(res != 0) error("Failed to apply %u FIRM patch(es)! Go to Discord for help.", res); 348 | 349 | if(bootType != FIRMLAUNCH) deinitScreens(); 350 | launchFirm(0, NULL); 351 | } 352 | -------------------------------------------------------------------------------- /arm9/source/menu.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multimegamander/MultiCTR/52280211953baf98269c3c4fd0d77e741c0d985f/arm9/source/menu.h -------------------------------------------------------------------------------- /arm9/source/patch.s: -------------------------------------------------------------------------------- 1 | .section .large_patch.emunand, "aw", %progbits 2 | .arm 3 | .align 4 4 | 5 | @ Code originally by Normmatt 6 | 7 | .global emunandPatch 8 | emunandPatch: 9 | @ Original code that still needs to be executed 10 | mov r4, r0 11 | mov r5, r1 12 | mov r7, r2 13 | mov r6, r3 14 | @ End 15 | 16 | @ If we're already trying to access the SD, return 17 | ldr r2, [r0, #4] 18 | ldr r1, emunandPatchSdmmcStructPtr 19 | cmp r2, r1 20 | beq out 21 | 22 | str r1, [r0, #4] @ Set object to be SD 23 | ldr r2, [r0, #8] @ Get sector to read 24 | cmp r2, #0 @ For GW compatibility, see if we're trying to read the ncsd header (sector 0) 25 | 26 | ldr r3, emunandPatchNandOffset 27 | add r2, r3 @ Add the offset to the NAND in the SD 28 | 29 | ldreq r3, emunandPatchNcsdHeaderOffset 30 | addeq r2, r3 @ If we're reading the ncsd header, add the offset of that sector 31 | 32 | str r2, [r0, #8] @ Store sector to read 33 | 34 | out: 35 | @ Restore registers. 36 | mov r1, r5 37 | mov r2, r7 38 | mov r3, r6 39 | 40 | @ Return 4 bytes behind where we got called, 41 | @ due to the offset of this function being stored there 42 | mov r0, lr 43 | add r0, #4 44 | bx r0 45 | 46 | .pool 47 | 48 | .global emunandPatchSdmmcStructPtr 49 | .global emunandPatchNandOffset 50 | .global emunandPatchNcsdHeaderOffset 51 | 52 | emunandPatchSdmmcStructPtr: .word 0 @ Pointer to sdmmc struct 53 | emunandPatchNandOffset: .word 0 @ For rednand this should be 1 54 | emunandPatchNcsdHeaderOffset: .word 0 @ Depends on nand manufacturer + emunand type (GW/RED) 55 | 56 | .pool 57 | .balign 4 58 | 59 | _emunandPatchEnd: 60 | 61 | .global emunandPatchSize 62 | emunandPatchSize: 63 | .word _emunandPatchEnd - emunandPatch 64 | 65 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 66 | 67 | @ Code originally from delebile and mid-kid 68 | 69 | .section .large_patch.reboot, "aw", %progbits 70 | .arm 71 | .align 4 72 | 73 | #define copy_launch_stub_stack_top 0x01FFB800 74 | #define copy_launch_stub_stack_bottom 0x01FFA800 75 | #define copy_launch_stub_addr 0x01FF9000 76 | 77 | #define argv_addr (copy_launch_stub_stack_bottom - 0x100) 78 | #define fname_addr (copy_launch_stub_stack_bottom - 0x200) 79 | #define low_tid_addr (copy_launch_stub_stack_bottom - 0x300) 80 | 81 | #define firm_addr 0x20001000 82 | #define firm_maxsize 0x07FFF000 83 | 84 | .global rebootPatch 85 | rebootPatch: 86 | @ Interesting registers and locations to keep in mind, set just before this code is ran: 87 | @ - r1: FIRM path in exefs. 88 | @ - r7 (or r8): pointer to file object 89 | @ - *r7: vtable 90 | @ - *(vtable + 0x28): fread function 91 | @ - *(r7 + 8): file handle 92 | 93 | sub r7, r0, #8 94 | mov r8, r1 95 | 96 | pxi_wait_recv: 97 | ldr r2, =0x44846 98 | ldr r0, =0x10008000 99 | readPxiLoop1: 100 | ldrh r1, [r0, #4] 101 | lsls r1, #0x17 102 | bmi readPxiLoop1 103 | ldr r0, [r0, #0xC] 104 | cmp r0, r2 105 | bne pxi_wait_recv 106 | 107 | @ Open file 108 | add r0, r7, #8 109 | adr r1, rebootPatchFileName 110 | mov r2, #1 111 | adr r6, rebootPatchFopenPtr 112 | ldr r6, [r6] 113 | orr r6, #1 114 | blx r6 115 | cmp r0, #0 116 | bne panic 117 | 118 | @ Read file 119 | mov r0, r7 120 | adr r1, bytes_read 121 | ldr r2, =firm_addr 122 | ldr r3, =firm_maxsize 123 | ldr r6, [r7] 124 | ldr r6, [r6, #0x28] 125 | blx r6 126 | 127 | @ Copy the low TID (in UTF-16) of the wanted firm 128 | ldr r0, =low_tid_addr 129 | add r1, r8, #0x1A 130 | mov r2, #0x10 131 | bl memcpy16 132 | 133 | @ Copy argv[0] 134 | ldr r0, =fname_addr 135 | adr r1, rebootPatchFileName 136 | mov r2, #82 137 | bl memcpy16 138 | 139 | ldr r0, =argv_addr 140 | ldr r1, =fname_addr 141 | ldr r2, =low_tid_addr 142 | stmia r0, {r1, r2} 143 | 144 | @ Set kernel state 145 | mov r0, #0 146 | mov r1, #0 147 | mov r2, #0 148 | mov r3, #0 149 | svc 0x7C 150 | 151 | goto_reboot: 152 | @ Jump to reboot code 153 | ldr r0, kernel_func_displ 154 | add r0, pc @ pc is two instructions ahead of the instruction being executed (12 = 2*4 + 4) 155 | svc 0x7B 156 | 157 | die: 158 | b die 159 | 160 | memcpy16: 161 | cmp r2, #0 162 | bxeq lr 163 | add r2, r0, r2 164 | copy_loop16: 165 | ldrh r3, [r1], #2 166 | strh r3, [r0], #2 167 | cmp r0, r2 168 | blo copy_loop16 169 | bx lr 170 | 171 | panic: 172 | mov r1, r0 @ unused register 173 | mov r0, #0 174 | svc 0x3C @ svcBreak(USERBREAK_PANIC) 175 | b die 176 | 177 | kernel_func_displ: 178 | .word kernelcode_start - goto_reboot - 12 179 | 180 | bytes_read: 181 | .word 0 182 | 183 | .global rebootPatchFopenPtr 184 | rebootPatchFopenPtr: 185 | .word 0 186 | 187 | .pool 188 | 189 | .global rebootPatchFileName 190 | rebootPatchFileName: 191 | .skip 2*(80+1) 192 | 193 | .balign 4 194 | kernelcode_start: 195 | 196 | msr cpsr_cxsf, #0xD3 @ disable interrupts and clear flags 197 | 198 | ldr sp, =copy_launch_stub_stack_top 199 | 200 | ldr r0, =copy_launch_stub_addr 201 | adr r1, copy_launch_stub 202 | mov r2, #(copy_launch_stub_end - copy_launch_stub) 203 | bl memcpy32 204 | 205 | @ Disable MPU 206 | ldr r0, =0x42078 @ alt vector select, enable itcm 207 | mcr p15, 0, r0, c1, c0, 0 208 | 209 | bl flushCaches 210 | 211 | ldr r0, =copy_launch_stub_addr 212 | bx r0 213 | 214 | copy_launch_stub: 215 | 216 | ldr r4, =firm_addr 217 | 218 | mov r5, #0 219 | load_section_loop: 220 | @ Such checks. Very ghetto. Wow. 221 | add r3, r4, #0x40 222 | add r3, r5,lsl #5 223 | add r3, r5,lsl #4 224 | ldmia r3, {r6-r8} 225 | cmp r8, #0 226 | movne r0, r7 227 | addne r1, r4, r6 228 | movne r2, r8 229 | blne memcpy32 230 | add r5, #1 231 | cmp r5, #4 232 | blo load_section_loop 233 | 234 | mov r0, #2 @ argc 235 | ldr r1, =argv_addr @ argv 236 | ldr r2, =0xBABE @ magic word 237 | 238 | mov r5, #0x20000000 239 | ldr r6, [r4, #0x08] 240 | str r6, [r5, #-4] @ store arm11 entrypoint 241 | 242 | ldr lr, [r4, #0x0c] 243 | bx lr 244 | 245 | memcpy32: 246 | add r2, r0, r2 247 | copy_loop32: 248 | ldr r3, [r1], #4 249 | str r3, [r0], #4 250 | cmp r0, r2 251 | blo copy_loop32 252 | bx lr 253 | 254 | .pool 255 | 256 | copy_launch_stub_end: 257 | 258 | flushCaches: 259 | 260 | @ Clean and flush data cache 261 | mov r1, #0 @ segment counter 262 | outer_loop: 263 | mov r0, #0 @ line counter 264 | 265 | inner_loop: 266 | orr r2, r1, r0 @ generate segment and line address 267 | mcr p15, 0, r2, c7, c14, 2 @ clean and flush the line 268 | add r0, #0x20 @ increment to next line 269 | cmp r0, #0x400 270 | bne inner_loop 271 | 272 | add r1, #0x40000000 273 | cmp r1, #0 274 | bne outer_loop 275 | 276 | @ Drain write buffer 277 | mcr p15, 0, r1, c7, c10, 4 278 | 279 | @ Flush instruction cache 280 | mcr p15, 0, r1, c7, c5, 0 281 | 282 | bx lr 283 | 284 | .pool 285 | .balign 4 286 | 287 | _rebootPatchEnd: 288 | 289 | .global rebootPatchSize 290 | rebootPatchSize: 291 | .word _rebootPatchEnd - rebootPatch 292 | -------------------------------------------------------------------------------- /arm9/source/start.s: -------------------------------------------------------------------------------- 1 | .section .text.start 2 | .align 4 3 | .global _start 4 | _start: 5 | @ Change the stack pointer 6 | mov sp, #0x08100000 7 | 8 | @ Disable caches / MPU 9 | mrc p15, 0, r0, c1, c0, 0 @ read control register 10 | bic r0, #(1<<12) @ - instruction cache disable 11 | bic r0, #(1<<2) @ - data cache disable 12 | bic r0, #(1<<0) @ - mpu disable 13 | mcr p15, 0, r0, c1, c0, 0 @ write control register 14 | 15 | @ Give read/write access to all the memory regions 16 | ldr r5, =0x33333333 17 | mcr p15, 0, r5, c5, c0, 2 @ write data access 18 | mcr p15, 0, r5, c5, c0, 3 @ write instruction access 19 | 20 | @ Sets MPU permissions and cache settings 21 | ldr r0, =0xFFFF001D @ ffff0000 32k 22 | ldr r1, =0x01FF801D @ 01ff8000 32k 23 | ldr r2, =0x08000027 @ 08000000 1M 24 | ldr r3, =0x10000021 @ 10000000 128k 25 | ldr r4, =0x10100025 @ 10100000 512k 26 | ldr r5, =0x20000035 @ 20000000 128M 27 | ldr r6, =0x1FF00027 @ 1FF00000 1M 28 | ldr r7, =0x1800002D @ 18000000 8M 29 | mov r10, #0x25 30 | mov r11, #0x25 31 | mov r12, #0x25 32 | mcr p15, 0, r0, c6, c0, 0 33 | mcr p15, 0, r1, c6, c1, 0 34 | mcr p15, 0, r2, c6, c2, 0 35 | mcr p15, 0, r3, c6, c3, 0 36 | mcr p15, 0, r4, c6, c4, 0 37 | mcr p15, 0, r5, c6, c5, 0 38 | mcr p15, 0, r6, c6, c6, 0 39 | mcr p15, 0, r7, c6, c7, 0 40 | mcr p15, 0, r10, c3, c0, 0 @ Write bufferable 0, 2, 5 41 | mcr p15, 0, r11, c2, c0, 0 @ Data cacheable 0, 2, 5 42 | mcr p15, 0, r12, c2, c0, 1 @ Inst cacheable 0, 2, 5 43 | 44 | @ Enable caches 45 | mrc p15, 0, r4, c1, c0, 0 @ read control register 46 | orr r4, r4, #(1<<18) @ - itcm enable 47 | orr r4, r4, #(1<<12) @ - instruction cache enable 48 | orr r4, r4, #(1<<2) @ - data cache enable 49 | orr r4, r4, #(1<<0) @ - mpu enable 50 | mcr p15, 0, r4, c1, c0, 0 @ write control register 51 | 52 | @ Flush caches 53 | mov r5, #0 54 | mcr p15, 0, r5, c7, c5, 0 @ flush I-cache 55 | mcr p15, 0, r5, c7, c6, 0 @ flush D-cache 56 | mcr p15, 0, r5, c7, c10, 4 @ drain write buffer 57 | 58 | @ Fixes mounting of SDMC 59 | ldr r0, =0x10000020 60 | mov r1, #0x340 61 | str r1, [r0] 62 | 63 | bl main 64 | 65 | .die: 66 | b .die 67 | -------------------------------------------------------------------------------- /bootloader/Makefile: -------------------------------------------------------------------------------- 1 | rwildcard = $(foreach d, $(wildcard $1*), $(filter $(subst *, %, $2), $d) $(call rwildcard, $d/, $2)) 2 | 3 | ifeq ($(strip $(DEVKITARM)),) 4 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 5 | endif 6 | 7 | include $(DEVKITARM)/base_tools 8 | 9 | name := $(shell basename $(CURDIR)) 10 | 11 | dir_source := source 12 | dir_build := build 13 | dir_out := ../$(dir_build) 14 | 15 | ASFLAGS := -mcpu=arm946e-s 16 | CFLAGS := -Wall -Wextra -marm $(ASFLAGS) -fno-builtin -std=c11 -Wno-main -O2 -flto -ffast-math 17 | LDFLAGS := -nostartfiles -Wl,--nmagic 18 | 19 | objects = $(patsubst $(dir_source)/%.s, $(dir_build)/%.o, \ 20 | $(patsubst $(dir_source)/%.c, $(dir_build)/%.o, \ 21 | $(call rwildcard, $(dir_source), *.s *.c))) 22 | 23 | .PHONY: all 24 | all: $(dir_out)/$(name).bin 25 | 26 | .PHONY: clean 27 | clean: 28 | @rm -rf $(dir_build) 29 | 30 | $(dir_out)/$(name).bin: $(dir_build)/$(name).elf 31 | $(OBJCOPY) -S -O binary $< $@ 32 | 33 | $(dir_build)/$(name).elf: $(objects) 34 | $(LINK.o) -T linker.ld $(OUTPUT_OPTION) $^ 35 | 36 | $(dir_build)/memory.o: CFLAGS += -O3 37 | 38 | $(dir_build)/%.o: $(dir_source)/%.c 39 | @mkdir -p "$(@D)" 40 | $(COMPILE.c) $(OUTPUT_OPTION) $< 41 | 42 | $(dir_build)/%.o: $(dir_source)/%.s 43 | @mkdir -p "$(@D)" 44 | $(COMPILE.s) $(OUTPUT_OPTION) $< 45 | -------------------------------------------------------------------------------- /bootloader/linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | ENTRY(_start) 5 | SECTIONS 6 | { 7 | . = 0x23F00000; 8 | 9 | __start__ = ABSOLUTE(.); 10 | 11 | .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } 12 | .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } 13 | .data : ALIGN(4) { *(.data*); . = ALIGN(8); *(.bss* COMMON); . = ALIGN(8); } 14 | 15 | . = ALIGN(4); 16 | 17 | __end__ = ABSOLUTE(.); 18 | 19 | __stack_top__ = 0x01FFB800; 20 | __stack_bottom__ = 0x01FFA800; 21 | } 22 | -------------------------------------------------------------------------------- /bootloader/source/main.c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multimegamander/MultiCTR/52280211953baf98269c3c4fd0d77e741c0d985f/bootloader/source/main.c -------------------------------------------------------------------------------- /bootloader/source/start.s: -------------------------------------------------------------------------------- 1 | @ This file is part of Luma3DS 2 | @ Copyright (C) 2016-2017 Aurora Wright, TuxSH 3 | @ 4 | @ This program is free software: you can redistribute it and/or modify 5 | @ it under the terms of the GNU General Public License as published by 6 | @ the Free Software Foundation, either version 3 of the License, or 7 | @ (at your option) any later version. 8 | @ 9 | @ This program is distributed in the hope that it will be useful, 10 | @ but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | @ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | @ GNU General Public License for more details. 13 | @ 14 | @ You should have received a copy of the GNU General Public License 15 | @ along with this program. If not, see . 16 | @ 17 | @ Additional Terms 7.b and 7.c of GPLv3 apply to this file: 18 | @ * Requiring preservation of specified reasonable legal notices or 19 | @ author attributions in that material or in the Appropriate Legal 20 | @ Notices displayed by works containing it. 21 | @ * Prohibiting misrepresentation of the origin of that material, 22 | @ or requiring that modified versions of such material be marked in 23 | @ reasonable ways as different from the original version. 24 | .arm 25 | 26 | .section .text.start 27 | .align 4 28 | .global _start 29 | _start: 30 | ldr sp, =__stack_top__ 31 | b main 32 | 33 | .text 34 | .balign 4 35 | .global disableMpuAndJumpToEntrypoints 36 | .type disableMpuAndJumpToEntrypoints, %function 37 | disableMpuAndJumpToEntrypoints: 38 | mov r4, r0 39 | mov r5, r1 40 | mov r6, r2 41 | mov r7, r3 42 | 43 | bl flushCaches 44 | 45 | @ Disable caches / MPU 46 | mrc p15, 0, r0, c1, c0, 0 @ read control register 47 | bic r0, #(1<<12) @ - instruction cache disable 48 | bic r0, #(1<<2) @ - data cache disable 49 | bic r0, #(1<<0) @ - MPU disable 50 | mcr p15, 0, r0, c1, c0, 0 @ write control register 51 | 52 | @ Set the ARM11 entrypoint 53 | mov r0, #0x20000000 54 | str r7, [r0, #-4] 55 | 56 | @ Jump to the ARM9 entrypoint 57 | mov r0, r4 58 | mov r1, r5 59 | ldr r2, =0x3BEEF 60 | bx r6 61 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /docs/css/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multimegamander/MultiCTR/52280211953baf98269c3c4fd0d77e741c0d985f/docs/css/style.css -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: post 3 | title: "Welcome to Jekyll!" 4 | --- 5 | 6 | # Welcome 7 | 8 | **Hello world**, this is my first Jekyll blog post. 9 | 10 | I hope you like it! 11 | -------------------------------------------------------------------------------- /docs/js/main.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multimegamander/MultiCTR/52280211953baf98269c3c4fd0d77e741c0d985f/docs/js/main.js -------------------------------------------------------------------------------- /linker.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm", "elf32-littlearm") 2 | OUTPUT_ARCH(arm) 3 | 4 | ENTRY(_start) 5 | SECTIONS 6 | { 7 | . = 0x08006000; 8 | 9 | .text : ALIGN(4) { *(.text.start) *(.text*); . = ALIGN(4); } 10 | .rodata : ALIGN(4) { *(.rodata*); . = ALIGN(4); } 11 | .data : ALIGN(4) { *(.data*); . = ALIGN(4); } 12 | .bss : ALIGN(8) { __bss_start = .; *(.bss* COMMON); . = ALIGN(8); __bss_end = .; } 13 | 14 | . = ALIGN(4); 15 | } 16 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Multimegamander/MultiCTR/52280211953baf98269c3c4fd0d77e741c0d985f/logo.png -------------------------------------------------------------------------------- /streaming.py: -------------------------------------------------------------------------------- 1 | # For use with the NTR sysmodule 2 | # Written by Byebyesky 3 | 4 | #!/usr/bin/env python3 5 | import socket, struct, sys, pygame, os, numpy 6 | from PIL import Image 7 | from enum import Enum 8 | import binascii 9 | import time 10 | 11 | WELCOME = "Connection was accepted!\x00" 12 | DSSCREENHEIGHT = 240 13 | TOPWIDTH = 400 14 | BOTTOMWIDTH = 320 15 | TOPSIZE = TOPWIDTH*DSSCREENHEIGHT 16 | BOTSIZE = BOTTOMWIDTH*DSSCREENHEIGHT 17 | IMGTOP = (DSSCREENHEIGHT,TOPWIDTH) 18 | IMGBOT = (DSSCREENHEIGHT,BOTTOMWIDTH) 19 | IP = "192.168.178.28" 20 | PORT = 4957 21 | HEADERSIZE = 9 22 | MAGIC = b'\x13\x37' 23 | 24 | 25 | class BrokenPacket(Exception): 26 | pass 27 | 28 | class GSPGPU_FramebufferFormats(Enum): 29 | GSP_RGBA8_OES=0, #///< RGBA8 (4 bytes) 30 | GSP_BGR8_OES=1, #///< BGR8 (3 bytes) 31 | GSP_RGB565_OES=2, #///< RGB565 (2 bytes) 32 | GSP_RGB5_A1_OES=3, #///< RGB5A1 (2 bytes) 33 | GSP_RGBA4_OES=4 #///< RGBA4 (2 bytes) 34 | 35 | def getBytesPerPixel(pixelFormat): 36 | if pixelFormat == GSPGPU_FramebufferFormats.GSP_RGBA8_OES: 37 | return 4 38 | if pixelFormat == GSPGPU_FramebufferFormats.GSP_BGR8_OES: 39 | return 3 40 | if pixelFormat == 5: 41 | return 1 42 | else: 43 | return 2 44 | 45 | def getPixelFormat(pixelFormat): 46 | if pixelFormat == 0: 47 | return GSPGPU_FramebufferFormats.GSP_RGBA8_OES 48 | if pixelFormat == 1: 49 | return GSPGPU_FramebufferFormats.GSP_BGR8_OES 50 | if pixelFormat == 2: 51 | return GSPGPU_FramebufferFormats.GSP_RGB565_OES 52 | if pixelFormat == 3: 53 | return GSPGPU_FramebufferFormats.GSP_RGB5_A1_OES 54 | if pixelFormat == 4: 55 | return GSPGPU_FramebufferFormats.GSP_RGBA4_OES 56 | else: 57 | return pixelFormat 58 | 59 | def getScreen(sock, oldFrame, lastbpp): 60 | headerSize = HEADERSIZE 61 | header = bytearray(headerSize) 62 | magic = b'' 63 | isTop = b'' 64 | unused = b'' 65 | pixelFormat = b'' 66 | size = 0 67 | fullsize = 0 68 | 69 | while headerSize: 70 | try: 71 | nbytes = sock.recv_into(header, headerSize) 72 | headerSize -= nbytes 73 | time.sleep(5/1000000.0) 74 | except socket.timeout: 75 | print("Timeout!!! Try again...") 76 | break 77 | 78 | magic = bytes([header[0],header[1]]) 79 | isTop = header[2] 80 | currentBlocksize = header[3] 81 | pixelFormat = getPixelFormat(header[4]) 82 | bytesPerPixel = getBytesPerPixel(pixelFormat) 83 | fbsize = struct.unpack(">I", bytes([header[5], header[6], header[7],header[8]]))[0] 84 | 85 | 86 | if magic == MAGIC: 87 | size = fbsize 88 | fullsize = size 89 | else: 90 | print("Broken or invalid Packet!") 91 | print("Magic: ", header[:2], "Expected: ", MAGIC) 92 | raise BrokenPacket 93 | 94 | data = bytearray(size) 95 | view = memoryview(data) 96 | while size: 97 | try: 98 | nbytes = sock.recv_into(view, size) 99 | view = view[nbytes:] 100 | size -= nbytes 101 | except socket.timeout: 102 | print("Timeout!!! Try again...") 103 | break 104 | 105 | #print("Top: ", isTop, "Format: ", pixelFormat) 106 | #print("Fbsize: ", fbsize) 107 | #print(header) 108 | #print("BPP: ", bytesPerPixel) 109 | #print("First 10 bytes: ", data[:4*4+4]) 110 | #print("BlockSize: ", currentBlocksize) 111 | #print("Data length: ", len(data)) 112 | 113 | #compressed 114 | if pixelFormat == 6: 115 | oldFrame = bytearray(oldFrame) 116 | view = memoryview(oldFrame) 117 | #if data != b'': 118 | # print(data[:20]) 119 | for i in range(0, fbsize, currentBlocksize+4): 120 | offset = struct.unpack("= 1: 170 | os.environ['SDL_VIDEO_CENTERED'] = '1' 171 | pygame.init() 172 | FPS = 60 173 | DISPLAY = pygame.display.Info() 174 | WIDTH = TOPWIDTH 175 | HEIGHT = DSSCREENHEIGHT*2 176 | #For Debug, start in large screen 177 | WIDTH = WIDTH*2 178 | HEIGHT = HEIGHT*2 179 | NAME = "3DS View" 180 | SCALED = True 181 | ROTATED = False 182 | 183 | screen = pygame.display.set_mode((WIDTH, HEIGHT)) 184 | pygame.display.set_caption(NAME) 185 | clock = pygame.time.Clock() 186 | palette = [] 187 | for i in range(255): 188 | palette.append((i,i,i)) 189 | 190 | font = pygame.font.Font('freesansbold.ttf', 32) 191 | 192 | try: 193 | RED = 255 194 | GREEN = 0 195 | BLUE = 255 196 | ENABLE = 1 197 | COLOR = (ENABLE << 23) + (BLUE << 15) + (GREEN << 7) + RED 198 | BINCOL = struct.pack(" 500 and doShow: 325 | printTimer = pygame.time.get_ticks() 326 | fps = int(clock.get_fps()) 327 | txt = '' 328 | #txt += "Time: {} ".format(str(printTimer)) 329 | txt += "FPS: {}".format(str(fps)) 330 | text = font.render(txt, True, (255,255,255), (128,128,128)) 331 | #print( ) 332 | #pygame.display.set_caption(NAME + " FPS: " + str(fps)) 333 | 334 | screen.blit(text, (0,0)) 335 | pygame.display.update() 336 | 337 | elif MESSAGE == b'\x03': 338 | sock.send(MESSAGE+BINCOL) 339 | elif MESSAGE == b'\x05': 340 | sock.send(MESSAGE) 341 | data = b'' 342 | while True: 343 | data += sock.recv(1024) 344 | print(binascii.hexlify(data)) 345 | else: 346 | sock.send(MESSAGE) 347 | sock.recv(1024) 348 | if MESSAGE == b'\x02': 349 | data = sock.recv(1024) 350 | print(data.decode()) 351 | 352 | sock.close() 353 | 354 | except KeyboardInterrupt: 355 | print("Closing socket...") 356 | sock.close() 357 | 358 | #except ValueError: 359 | # print("Not enough data!") 360 | # print("Closing socket...") 361 | # sock.close() 362 | 363 | else: 364 | print(len(sys.argv)) 365 | print("No MESSAGE was given!") 366 | --------------------------------------------------------------------------------