├── Makefile ├── README.md ├── include ├── types.h └── unprotboot9_sdmmc.h └── source ├── unprotboot9_sdmmc.c └── unprotboot9_sdmmc_asm.s /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITARM)),) 6 | $(error "Please set DEVKITARM in your environment. export DEVKITARM=devkitARM") 7 | endif 8 | 9 | include $(DEVKITARM)/3ds_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # BUILD is the directory where object files & intermediate files will be placed 14 | # SOURCES is a list of directories containing source code 15 | # DATA is a list of directories containing data files 16 | # INCLUDES is a list of directories containing header files 17 | #--------------------------------------------------------------------------------- 18 | TARGET := unprotboot9_sdmmc 19 | BUILD := build 20 | SOURCES := source 21 | DATA := data 22 | INCLUDES := include 23 | 24 | #--------------------------------------------------------------------------------- 25 | # options for code generation 26 | #--------------------------------------------------------------------------------- 27 | ARCH := -marm -fpic 28 | 29 | CFLAGS := -g -Wall -Os\ 30 | -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\ 31 | -ffast-math -std=c99 \ 32 | $(ARCH) 33 | 34 | CFLAGS += $(INCLUDE) -DARM9 $(DEFINES) 35 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 36 | 37 | ASFLAGS := -g $(ARCH) 38 | 39 | #--------------------------------------------------------------------------------- 40 | # list of directories containing libraries, this must be the top level containing 41 | # include and lib 42 | #--------------------------------------------------------------------------------- 43 | LIBDIRS := $(CTRULIB) 44 | 45 | #--------------------------------------------------------------------------------- 46 | # no real need to edit anything past this point unless you need to add additional 47 | # rules for different file extensions 48 | #--------------------------------------------------------------------------------- 49 | ifneq ($(BUILD),$(notdir $(CURDIR))) 50 | #--------------------------------------------------------------------------------- 51 | 52 | export OUTPUT := $(CURDIR)/lib/lib$(TARGET).a 53 | 54 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 55 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 56 | 57 | export DEPSDIR := $(CURDIR)/$(BUILD) 58 | 59 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 60 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 61 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 62 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 63 | 64 | #--------------------------------------------------------------------------------- 65 | # use CXX for linking C++ projects, CC for standard C 66 | #--------------------------------------------------------------------------------- 67 | ifeq ($(strip $(CPPFILES)),) 68 | #--------------------------------------------------------------------------------- 69 | export LD := $(CC) 70 | #--------------------------------------------------------------------------------- 71 | else 72 | #--------------------------------------------------------------------------------- 73 | export LD := $(CXX) 74 | #--------------------------------------------------------------------------------- 75 | endif 76 | #--------------------------------------------------------------------------------- 77 | 78 | export OFILES := $(addsuffix .o,$(BINFILES)) \ 79 | $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 80 | 81 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 82 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 83 | -I$(CURDIR)/$(BUILD) 84 | 85 | .PHONY: $(BUILD) clean all 86 | 87 | #--------------------------------------------------------------------------------- 88 | all: $(BUILD) 89 | 90 | lib: 91 | @[ -d $@ ] || mkdir -p $@ 92 | 93 | $(BUILD): lib 94 | @[ -d $@ ] || mkdir -p $@ 95 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 96 | 97 | #--------------------------------------------------------------------------------- 98 | clean: 99 | @echo clean ... 100 | @rm -fr $(BUILD) lib 101 | 102 | #--------------------------------------------------------------------------------- 103 | else 104 | 105 | DEPENDS := $(OFILES:.o=.d) 106 | 107 | #--------------------------------------------------------------------------------- 108 | # main targets 109 | #--------------------------------------------------------------------------------- 110 | $(OUTPUT) : $(OFILES) 111 | 112 | #--------------------------------------------------------------------------------- 113 | %.bin.o : %.bin 114 | #--------------------------------------------------------------------------------- 115 | @echo $(notdir $<) 116 | @$(bin2o) 117 | 118 | 119 | -include $(DEPENDS) 120 | 121 | #--------------------------------------------------------------------------------------- 122 | endif 123 | #--------------------------------------------------------------------------------------- 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a library for using the mmc code in the 3DS arm9-bootrom. This only uses the code in the unprotected arm9-bootrom. Boot9 only supports sector reading, not writing. Also note that boot9 reads each sector with command 18 not 25: each sector is read seperately with a dedicated command. Here the data is read from FIFO to the output buffer via CPU mem-copy. No crypto is used with this. 2 | 3 | SD(HC) and NAND are all supported by this arm9-bootrom sdmmc code. 4 | 5 | -------------------------------------------------------------------------------- /include/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | typedef uint8_t u8; 7 | typedef uint16_t u16; 8 | typedef uint32_t u32; 9 | typedef uint64_t u64; 10 | 11 | typedef int32_t s32; 12 | typedef int64_t s64; 13 | 14 | typedef volatile uint8_t vu8; 15 | typedef volatile uint16_t vu16; 16 | typedef volatile uint32_t vu32; 17 | -------------------------------------------------------------------------------- /include/unprotboot9_sdmmc.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //These return 0 for success, non-zero for error. 4 | 5 | //Using this requires that the DTCM at 0xfff00000 is accessible. You can either disable MPU, or setup a MPU region for it. 6 | //Additionally, reading the NAND requires ITCM to be accessible at 0x07ff8000. 7 | 8 | typedef enum { 9 | unprotboot9_sdmmc_deviceid_sd = 0x200, 10 | unprotboot9_sdmmc_deviceid_nand = 0x201 11 | } unprotboot9_sdmmc_deviceid; 12 | 13 | typedef s32 (*unprotboot9_sdmmc_dma_prepare_cb)(u32 fifoAddr); 14 | typedef void (*unprotboot9_sdmmc_dma_abort_cb)(void); 15 | typedef s32 (*unprotboot9_sdmmc_sector_cb)(u32 fifoAddr); 16 | 17 | s32 unprotboot9_sdmmc_initialize(void);//This must be used first. 18 | s32 unprotboot9_sdmmc_initdevice(unprotboot9_sdmmc_deviceid deviceid);//This must be used before doing anything with the device. 19 | s32 unprotboot9_sdmmc_selectdevice(unprotboot9_sdmmc_deviceid deviceid);//Only use this if the device was already initialized, and if the device isn't already selected(the latter is checked for by the boot9 code anyway). 20 | 21 | //Sets up a raw sector read from the currently selected and previously initialized device (in a way you can use DMA). 22 | //If the callback is set, the read is interrupted every sector and you are supposed to empty the FIFO in less than 650ms 23 | s32 unprotboot9_sdmmc_readrawsectors_setup(u32 sector, u32 numsectors, unprotboot9_sdmmc_sector_cb cb, unprotboot9_sdmmc_dma_prepare_cb dmaprep_cb, unprotboot9_sdmmc_dma_abort_cb dmaabt_cb); 24 | 25 | //Read raw sectors using the CPU 26 | s32 unprotboot9_sdmmc_readrawsectors(u32 sector, u32 numsectors, void *buf); 27 | -------------------------------------------------------------------------------- /source/unprotboot9_sdmmc.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "types.h" 6 | #include "unprotboot9_sdmmc.h" 7 | 8 | void unprotboot9_sdmmc_initialize_asm(); 9 | 10 | s32 unprotboot9_sdmmc_initialize() 11 | { 12 | void (*funcptr_cleardtcm)() = (void*)0xffff01b0; 13 | void (*funcptr_boot9init)() = (void*)0xffff1ff9; 14 | s32 (*funcptr_mmcinit)() = (void*)0xffff56c9; 15 | 16 | *((u16*)0x10000020) |= 0x200;//If not set, the hardware will not detect any inserted card on the sdbus. 17 | *((u16*)0x10000020) &= ~0x1;//If set while bitmask 0x200 is set, a sdbus command timeout error will occur during sdbus init. 18 | 19 | unprotboot9_sdmmc_initialize_asm(); 20 | 21 | funcptr_cleardtcm(); 22 | *((u32*)(0xfff0009c+0x1c)) = 1;//Initialize the sdmmc busid. 23 | 24 | funcptr_boot9init();//General boot9 init function. 25 | 26 | return funcptr_mmcinit(); 27 | } 28 | 29 | s32 unprotboot9_sdmmc_initdevice(unprotboot9_sdmmc_deviceid deviceid) 30 | { 31 | s32 (*funcptr)(u32) = (void*)0xffff5775; 32 | return funcptr(deviceid); 33 | } 34 | 35 | s32 unprotboot9_sdmmc_selectdevice(unprotboot9_sdmmc_deviceid deviceid) 36 | { 37 | s32 (*funcptr)(u32) = (void*)0xffff5c11; 38 | return funcptr(deviceid); 39 | } 40 | 41 | s32 unprotboot9_sdmmc_readrawsectors_setup(u32 sector, u32 numsectors, unprotboot9_sdmmc_sector_cb cb, unprotboot9_sdmmc_dma_prepare_cb dmaprep_cb, unprotboot9_sdmmc_dma_abort_cb dmaabt_cb) 42 | { 43 | u32 original_deviceid; 44 | s32 ret; 45 | s32 (*funcptr)(u32, u32) = (void*)0xffff2e39; 46 | 47 | original_deviceid = *(u32*)0xfff000bc; 48 | 49 | //The boot9 sector reading code calls the deviceselect code with deviceid=nand. Overwrite the current_deviceid value in memory with the nand value, so that the deviceselect function does nothing there. 50 | *(u32*)0xfff000bc = (u32)unprotboot9_sdmmc_deviceid_nand; 51 | 52 | //Prepare the arguments 53 | *(u32*)0xfff000a8 = (u32)dmaprep_cb; 54 | *(u32*)0xfff000ac = (u32)cb; 55 | *(u32*)0xfff000b0 = (u32)dmaabt_cb; 56 | //*0xfff000bc is 0 for Port1, 1 for Port3. BootROM always uses Port1 (interrupt ID is harcoded, anyway). 57 | 58 | ret = funcptr(sector, numsectors); 59 | 60 | *(u32*)0xfff000bc = original_deviceid; 61 | return ret; 62 | } 63 | 64 | s32 unprotboot9_sdmmc_readrawsectors(u32 sector, u32 numsectors, void *buf) 65 | { 66 | //This mimics the function @ 0xffff55f8 67 | *(u32*)0xfff00198 = (u32)buf; 68 | return unprotboot9_sdmmc_readrawsectors_setup(sector, numsectors, (unprotboot9_sdmmc_sector_cb)0xffff3bd5, NULL, NULL); 69 | } 70 | -------------------------------------------------------------------------------- /source/unprotboot9_sdmmc_asm.s: -------------------------------------------------------------------------------- 1 | .arm 2 | .text 3 | 4 | .global unprotboot9_sdmmc_initialize_asm 5 | 6 | unprotboot9_sdmmc_initialize_asm: 7 | @ Setup the DTCM region register. 8 | ldr r0, =0xfff0000a 9 | mcr 15, 0, r0, cr9, cr1, 0 10 | 11 | @ Enable DTCM. 12 | mrc 15, 0, r0, cr1, cr0, 0 @ 13 | orr r0, r0, #0x10000 14 | mcr 15, 0, r0, cr1, cr0, 0 15 | bx lr 16 | .pool 17 | 18 | --------------------------------------------------------------------------------