First commit

This commit is contained in:
Lillian Skinner 2024-11-11 01:22:54 -05:00
commit 2787a3d5cf
No known key found for this signature in database
72 changed files with 11665 additions and 0 deletions

116
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,116 @@
name: Build TwlNandTool
on:
push:
branches: ["*"]
paths-ignore:
- 'README.md'
pull_request:
branches: ["*"]
paths-ignore:
- 'README.md'
release:
types: [created]
jobs:
build:
runs-on: ubuntu-latest
container: devkitpro/devkitarm
name: Build with Docker using devkitARM
steps:
- name: Checkout repo
uses: actions/checkout@v4
- name: Setup environment
run: git config --global safe.directory '*'
- name: Build TwlNandTool
run: make
- name: Upload prod SRL
uses: actions/upload-artifact@v4.3.6
with:
path: "TwlNandTool.prod.srl"
name: TwlNandTool-Prod
devsign:
runs-on: windows-latest
needs: [build]
name: Devsign TwlNandTool and build a TAD
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: TwlNandTool-Prod
path: TwlNandTool-Prod
- name: Set up Python 3.12
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Clone ntool
uses: GuillaumeFalourd/clone-github-repo-action@v2.1
with:
depth: 1
owner: 'xprism1'
repository: 'ntool'
- name: Devsign TwlNandTool
run: |
cp TwlNandTool-Prod\TwlNandTool.prod.srl ntool
pip install pycryptodome
python ntool/ntool.py srl_retail2dev ntool/TwlNandTool.prod.srl --out ntool/TwlNandTool.dev.srl
- name: Make a devsigned TAD
run: |
curl https://cdn.randommeaninglesscharacters.com/tools/maketad/maketad.zip -o maketad.zip
7z e maketad.zip
cp ntool/TwlNandTool.dev.srl .
.\maketad-20090604.exe TwlNandTool.dev.srl -s -o TwlNandTool.dev.tad
- name: Upload devsigned TAD
uses: actions/upload-artifact@v4.3.6
with:
path: "TwlNandTool.dev.tad"
name: TwlNandTool-Tad
- name: Upload devsigned SRL
uses: actions/upload-artifact@v4.3.6
with:
path: "ntool/TwlNandTool.dev.srl"
name: TwlNandTool-Dev
# Only run this for non-PR jobs.
publish_build:
runs-on: ubuntu-latest
name: Upload to release
if: ${{ success() && startsWith(github.ref, 'refs/tags') }}
needs: [devsign]
steps:
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: TwlNandTool-Prod
path: TwlNandTool-Prod
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: TwlNandTool-Dev
path: TwlNandTool-Dev
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: TwlNandTool-Tad
path: TwlNandTool-Tad
- name: Publish Build
run: |
ID=$(jq --raw-output '.release.id' $GITHUB_EVENT_PATH)
AUTH_HEADER="Authorization: token ${{ secrets.TOKEN }}"
CONTENT_TYPE="Content-Type: application/7z-x-compressed"
for file in ${{ github.workspace }}/TwlNandTool-Prod/*; do
CONTENT_LENGTH="Content-Length: $(stat -c%s $file)"
UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)"
curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL"
done
for file in ${{ github.workspace }}/TwlNandTool-Dev/*; do
CONTENT_LENGTH="Content-Length: $(stat -c%s $file)"
UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)"
curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL"
done
for file in ${{ github.workspace }}/TwlNandTool-Tad/*; do
CONTENT_LENGTH="Content-Length: $(stat -c%s $file)"
UPLOAD_URL="https://uploads.github.com/repos/${{ github.repository }}/releases/$ID/assets?name=$(basename $file)"
curl -XPOST -H "$AUTH_HEADER" -H "$CONTENT_LENGTH" -H "$CONTENT_TYPE" --upload-file "$file" "$UPLOAD_URL"
done

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
*.o
*.d
*.nds
*.dsi
TwlNandTool.*.srl
TwlNandTool.*.tad
*.elf
*.map
*build/
*.vscode
*.DS_Store
*._*
make.sh
NandFirmCheck.sublime-project
NandFirmCheck.sublime-workspace
# Non-essential NAND titles:
# HNAx = Launcher
# HNBx = Settings
# 4NFA = NandFiler
# 4TNA = TwlNmenu
nitrofiles/import/*/HNAA*
nitrofiles/import/*/HNBA*
nitrofiles/import/*/4NFA*
nitrofiles/import/*/4TNA*
ntool/
arm9/include/version.h

69
Makefile Normal file
View File

@ -0,0 +1,69 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
# specify a directory which contains the nitro filesystem
# this is relative to the Makefile
export NITRODATA := nitrofiles
# These set the information text in the nds file
GAME_TITLE := TwlNandTool
GAME_SUBTITLE1 := rmc
GAME_CODE := 4FXA
GAME_LABEL := TWLNANDTOOL
export TARGET := TwlNandTool
include $(DEVKITARM)/ds_rules
icons := $(wildcard *.bmp)
ifneq (,$(findstring $(TARGET).bmp,$(icons)))
export GAME_ICON := $(CURDIR)/$(TARGET).bmp
else
ifneq (,$(findstring icon.bmp,$(icons)))
export GAME_ICON := $(CURDIR)/icon.bmp
endif
endif
.PHONY: checkarm7 checkarm9 clean
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
all: checkarm7 checkarm9 $(TARGET).prod.srl
#---------------------------------------------------------------------------------
checkarm7:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
checkarm9:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
# System NAND app for dev unit TADs
$(TARGET).prod.srl : $(NITRO_FILES) arm7/$(TARGET).elf arm9/$(TARGET).elf
ndstool -c $(TARGET).prod.srl -7 arm7/$(TARGET).elf -9 arm9/$(TARGET).elf -d $(NITRODATA) \
-u "00030015" \
-g "$(GAME_CODE)" "00" "$(GAME_LABEL)" \
-b $(GAME_ICON) "$(GAME_TITLE);$(GAME_SUBTITLE1)" \
$(_ADDFILES)
#---------------------------------------------------------------------------------
arm7/$(TARGET).elf:
$(MAKE) -C arm7
#---------------------------------------------------------------------------------
arm9/$(TARGET).elf:
$(MAKE) -C arm9
#---------------------------------------------------------------------------------
clean:
$(MAKE) -C arm9 clean
$(MAKE) -C arm7 clean
rm -f $(TARGET).prod.srl $(TARGET).dev.srl $(TARGET).dev.tad $(TARGET).arm7 $(TARGET).arm9

127
arm7/Makefile Normal file
View File

@ -0,0 +1,127 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src
INCLUDES := include build
DATA :=
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb-interwork
CFLAGS := -g -Wall -O2\
-mcpu=arm7tdmi -mtune=arm7tdmi -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM7
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -fno-rtti
ASFLAGS := -g $(ARCH)
LDFLAGS = -specs=ds_arm7.specs -g $(ARCH) -Wl,--nmagic -Wl,-Map,$(notdir $*).map
#LIBS := -ldswifi7 -lmm7 -lnds7
LIBS := -lnds7
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM7ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM7ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

210
arm7/src/main.c Normal file
View File

@ -0,0 +1,210 @@
/*---------------------------------------------------------------------------------
default ARM7 core
Copyright (C) 2005 - 2010
Michael Noland (joat)
Jason Rogers (dovoto)
Dave Murphy (WinterMute)
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any
damages arising from the use of this software.
Permission is granted to anyone to use this software for any
purpose, including commercial applications, and to alter it and
redistribute it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you
must not claim that you wrote the original software. If you use
this software in a product, an acknowledgment in the product
documentation would be appreciated but is not required.
2. Altered source versions must be plainly marked as such, and
must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
---------------------------------------------------------------------------------*/
#include "my_sdmmc.h"
#include <nds.h>
#include <string.h>
//---------------------------------------------------------------------------------
void VcountHandler()
//---------------------------------------------------------------------------------
{
inputGetAndSend();
}
volatile bool exitflag = false;
volatile bool reboot = false;
// Custom POWER button handling, based on the default function:
// https://github.com/devkitPro/libnds/blob/154a21cc3d57716f773ff2b10f815511c1b8ba9f/source/common/interrupts.c#L51-L69
//---------------------------------------------------------------------------------
TWL_CODE void i2cIRQHandlerCustom()
//---------------------------------------------------------------------------------
{
int cause = (i2cReadRegister(I2C_PM, I2CREGPM_PWRIF) & 0x3) | (i2cReadRegister(I2C_GPIO, 0x02)<<2);
switch (cause & 3)
{
case 1:
reboot = true;
exitflag = true;
break;
case 2:
reboot = false;
exitflag = true;
break;
}
}
void set_ctr(u32* ctr)
{
for (int i = 0; i < 4; i++) REG_AES_IV[i] = ctr[3-i];
}
// 10 11 22 23 24 25
//this is sort of a bodged together dsi aes function adapted from this 3ds function
//https://github.com/TiniVi/AHPCFW/blob/master/source/aes.c#L42
//as long as the output changes when keyslot values change, it's good enough.
void aes(void* in, void* out, void* iv, u32 method)
{
REG_AES_CNT = ( AES_CNT_MODE(method) |
AES_WRFIFO_FLUSH |
AES_RDFIFO_FLUSH |
AES_CNT_KEY_APPLY |
AES_CNT_KEYSLOT(3) |
AES_CNT_DMA_WRITE_SIZE(2) |
AES_CNT_DMA_READ_SIZE(1)
);
if (iv != NULL) set_ctr((u32*)iv);
REG_AES_BLKCNT = (1 << 16);
REG_AES_CNT |= 0x80000000;
for (int j = 0; j < 0x10; j+=4) REG_AES_WRFIFO = *((u32*)(in+j));
while (((REG_AES_CNT >> 0x5) & 0x1F) < 0x4); //wait for every word to get processed
for (int j = 0; j < 0x10; j+=4) *((u32*)(out+j)) = REG_AES_RDFIFO;
//REG_AES_CNT &= ~0x80000000;
//if (method & (AES_CTR_DECRYPT | AES_CTR_ENCRYPT)) add_ctr((u8*)iv);
}
int my_sdmmc_nand_startup();
//---------------------------------------------------------------------------------
int main()
//---------------------------------------------------------------------------------
{
// clear sound registers
dmaFillWords(0, (void*)0x04000400, 0x100);
REG_SOUNDCNT |= SOUND_ENABLE;
writePowerManagement(PM_CONTROL_REG, ( readPowerManagement(PM_CONTROL_REG) & ~PM_SOUND_MUTE ) | PM_SOUND_AMP );
powerOn(POWER_SOUND);
readUserSettings();
ledBlink(0);
irqInit();
irqSetAUX(IRQ_I2C, i2cIRQHandlerCustom);
// Start the RTC tracking IRQ
initClockIRQ();
fifoInit();
touchInit();
if (isDSiMode() /*|| ((REG_SCFG_EXT & BIT(17)) && (REG_SCFG_EXT & BIT(18)))*/)
{
vu8 *out=(vu8*)0x02300000;
memset(out, 0, 16);
//#if USENATIVECONSOLEID
// first check whether we can read the console ID directly and it was not hidden by SCFG
if (((*(vu16*)0x04004000) & (1u << 10)) == 0 && ((*(vu8*)0x04004D08) & 0x1) == 0)
{
// The console id registers are readable, so use them!
memcpy(out, (vu8*)0x04004D00, 8);
}
if(out[0] == 0 || out[1] == 0) {
// For getting ConsoleID without reading from 0x4004D00...
u8 base[16]={0};
u8 in[16]={0};
u8 iv[16]={0};
u8 *scratch=(u8*)0x02300200;
u8 *key3=(u8*)0x40044D0;
aes(in, base, iv, 2);
//write consecutive 0-255 values to any byte in key3 until we get the same aes output as "base" above - this reveals the hidden byte. this way we can uncover all 16 bytes of the key3 normalkey pretty easily.
//greets to Martin Korth for this trick https://problemkaputt.de/gbatek.htm#dsiaesioports (Reading Write-Only Values)
for (int i=0;i<16;i++)
{
for (int j=0;j<256;j++)
{
*(key3+i)=j & 0xFF;
aes(in, scratch, iv, 2);
if (!memcmp(scratch, base, 16))
{
out[i]=j;
//hit++;
break;
}
}
}
}
my_sdmmc_nand_startup();
my_sdmmc_get_cid(true, (u32*)0x2FFD7BC); // Get eMMC CID
//sdmmc_nand_cid((u32*)0x2FFD7BC);
}
SetYtrigger(80);
installSoundFIFO();
installSystemFIFO();
irqSet(IRQ_VCOUNT, VcountHandler);
irqEnable( IRQ_VBLANK | IRQ_VCOUNT | IRQ_NETWORK);
// Keep the ARM7 mostly idle
while (!exitflag)
{
if ( 0 == (REG_KEYINPUT & (KEY_SELECT | KEY_START | KEY_L | KEY_R)))
{
exitflag = true;
}
int batteryStatus;
if (isDSiMode() || REG_SCFG_EXT != 0)
batteryStatus = i2cReadRegister(I2C_PM, I2CREGPM_BATTERY);
else
batteryStatus = (readPowerManagement(PM_BATTERY_REG) & 1) ? 0x3 : 0xF;
fifoSendValue32(FIFO_USER_03, batteryStatus);
fifoSendValue32(FIFO_USER_01, *(vu8*)0x04004024);
swiWaitForVBlank();
}
// Tell ARM9 to safely exit
fifoSendValue32(FIFO_USER_01, 0x54495845); // 'EXIT'
fifoWaitValue32(FIFO_USER_02);
fifoCheckValue32(FIFO_USER_02);
if (reboot)
{
i2cWriteRegister(I2C_PM, I2CREGPM_RESETFLAG, 1);
i2cWriteRegister(I2C_PM, I2CREGPM_PWRCNT, 1);
}
else
{
writePowerManagement(PM_CONTROL_REG,PM_SYSTEM_PWR);
}
return 0;
}

696
arm7/src/my_sdmmc.c Normal file
View File

@ -0,0 +1,696 @@
#include <nds/system.h>
#include <nds/bios.h>
#include "my_sdmmc.h"
#include <nds/interrupts.h>
#include <nds/fifocommon.h>
#include <nds/fifomessages.h>
#include <stddef.h>
static struct mmcdevice deviceSD;
static struct mmcdevice deviceNAND;
/*mmcdevice *getMMCDevice(int drive)
{
if (drive==0) return &deviceNAND;
return &deviceSD;
}
*/
//---------------------------------------------------------------------------------
int my_geterror(struct mmcdevice *ctx)
//---------------------------------------------------------------------------------
{
//if (ctx->error == 0x4) return -1;
//else return 0;
return (ctx->error << 29) >> 31;
}
//---------------------------------------------------------------------------------
void my_setTarget(struct mmcdevice *ctx)
//---------------------------------------------------------------------------------
{
sdmmc_mask16(REG_SDPORTSEL,0x3,(u16)ctx->devicenumber);
setckl(ctx->clk);
if (ctx->SDOPT == 0)
{
sdmmc_mask16(REG_SDOPT, 0, 0x8000);
}
else
{
sdmmc_mask16(REG_SDOPT, 0x8000, 0);
}
}
//---------------------------------------------------------------------------------
void my_sdmmc_send_command(struct mmcdevice *ctx, uint32_t cmd, uint32_t args)
//---------------------------------------------------------------------------------
{
const bool getSDRESP = (cmd << 15) >> 31;
u16 flags = (cmd << 15) >> 31;
const bool readdata = cmd & 0x20000;
const bool writedata = cmd & 0x40000;
if (readdata || writedata)
{
flags |= TMIO_STAT0_DATAEND;
}
ctx->error = 0;
while ((sdmmc_read16(REG_SDSTATUS1) & TMIO_STAT1_CMD_BUSY)); //mmc working?
sdmmc_write16(REG_SDIRMASK0,0);
sdmmc_write16(REG_SDIRMASK1,0);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
sdmmc_mask16(REG_SDDATACTL32,0x1800,0x400); // Disable TX32RQ and RX32RDY IRQ. Clear fifo.
sdmmc_write16(REG_SDCMDARG0,args &0xFFFF);
sdmmc_write16(REG_SDCMDARG1,args >> 16);
sdmmc_write16(REG_SDCMD,cmd &0xFFFF);
u32 size = ctx->size;
const u16 blkSize = sdmmc_read16(REG_SDBLKLEN32);
#ifdef DATA32_SUPPORT
u32 *rDataPtr32 = (u32*)ctx->rData;
#else
u16 *rDataPtr16 = (u16*)ctx->rData;
#endif
u8 *rDataPtr8 = ctx->rData;
#ifdef DATA32_SUPPORT
const u32 *tDataPtr32 = (u32*)ctx->tData;
#else
const u16 *tDataPtr16 = (u16*)ctx->tData;
#endif
const u8 *tDataPtr8 = ctx->tData;
#ifdef DATA32_SUPPORT
bool rUseBuf = ( NULL != rDataPtr32 );
bool tUseBuf = ( NULL != tDataPtr32 );
#else
bool rUseBuf = ( NULL != rDataPtr16 );
bool tUseBuf = ( NULL != tDataPtr16 );
#endif
u16 status0 = 0;
while (1)
{
volatile u16 status1 = sdmmc_read16(REG_SDSTATUS1);
#ifdef DATA32_SUPPORT
volatile u16 ctl32 = sdmmc_read16(REG_SDDATACTL32);
if (ctl32 & 0x100)
#else
if (status1 & TMIO_STAT1_RXRDY)
#endif
{
if (readdata)
{
if (rUseBuf)
{
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_RXRDY, 0);
if (size >= blkSize)
{
#ifdef DATA32_SUPPORT
if (!((u32)rDataPtr32 & 3))
{
for (u32 i = 0; i < blkSize; i += 4)
{
*rDataPtr32++ = sdmmc_read32(REG_SDFIFO32);
}
}
else
{
for (u32 i = 0; i < blkSize; i += 4)
{
u32 data = sdmmc_read32(REG_SDFIFO32);
*rDataPtr8++ = data;
*rDataPtr8++ = data >> 8;
*rDataPtr8++ = data >> 16;
*rDataPtr8++ = data >> 24;
}
}
#else
if (!((u16)rDataPtr16 & 1))
{
for (u16 i = 0; i < blkSize; i += 2)
{
*rDataPtr16++ = sdmmc_read16(REG_SDFIFO);
}
}
else
{
for (u16 i = 0; i < blkSize; i += 2)
{
u16 data = sdmmc_read16(REG_SDFIFO);
*rDataPtr8++ = data;
*rDataPtr8++ = data >> 8;
}
}
#endif
size -= blkSize;
}
}
sdmmc_mask16(REG_SDDATACTL32, 0x800, 0);
}
}
#ifdef DATA32_SUPPORT
if (!(ctl32 & 0x200))
#else
if ((status1 & TMIO_STAT1_TXRQ))
#endif
{
if (writedata)
{
if (tUseBuf)
{
sdmmc_mask16(REG_SDSTATUS1, TMIO_STAT1_TXRQ, 0);
if (size >= blkSize)
{
#ifdef DATA32_SUPPORT
if (!((u32)tDataPtr32 & 3))
{
for (u32 i = 0; i < blkSize; i += 4)
{
sdmmc_write32(REG_SDFIFO32, *tDataPtr32++);
}
}
else
{
for (u32 i = 0; i < blkSize; i += 4)
{
u32 data = *tDataPtr8++;
data |= (u32)*tDataPtr8++ << 8;
data |= (u32)*tDataPtr8++ << 16;
data |= (u32)*tDataPtr8++ << 24;
sdmmc_write32(REG_SDFIFO32, data);
}
}
#else
if (!((u16)tDataPtr16 & 1))
{
for (u16 i = 0; i < blkSize; i += 2)
{
sdmmc_write16(REG_SDFIFO, *tDataPtr16++);
}
}
else
{
for (u16 i = 0; i < blkSize; i += 2)
{
u16 data = *tDataPtr8++;
data |= (u16)(*tDataPtr8++ << 8);
sdmmc_write16(REG_SDFIFO, data);
}
}
#endif
size -= blkSize;
}
}
sdmmc_mask16(REG_SDDATACTL32, 0x1000, 0);
}
}
if (status1 & TMIO_MASK_GW)
{
ctx->error |= 4;
break;
}
if (!(status1 & TMIO_STAT1_CMD_BUSY))
{
status0 = sdmmc_read16(REG_SDSTATUS0);
if (sdmmc_read16(REG_SDSTATUS0) & TMIO_STAT0_CMDRESPEND)
{
ctx->error |= 0x1;
}
if (status0 & TMIO_STAT0_DATAEND)
{
ctx->error |= 0x2;
}
if ((status0 & flags) == flags)
break;
}
}
ctx->stat0 = sdmmc_read16(REG_SDSTATUS0);
ctx->stat1 = sdmmc_read16(REG_SDSTATUS1);
sdmmc_write16(REG_SDSTATUS0,0);
sdmmc_write16(REG_SDSTATUS1,0);
if (getSDRESP != 0)
{
ctx->ret[0] = (u32)(sdmmc_read16(REG_SDRESP0) | (sdmmc_read16(REG_SDRESP1) << 16));
ctx->ret[1] = (u32)(sdmmc_read16(REG_SDRESP2) | (sdmmc_read16(REG_SDRESP3) << 16));
ctx->ret[2] = (u32)(sdmmc_read16(REG_SDRESP4) | (sdmmc_read16(REG_SDRESP5) << 16));
ctx->ret[3] = (u32)(sdmmc_read16(REG_SDRESP6) | (sdmmc_read16(REG_SDRESP7) << 16));
}
}
//---------------------------------------------------------------------------------
int my_sdmmc_cardinserted()
//---------------------------------------------------------------------------------
{
return 1; //my_sdmmc_cardready;
}
static bool my_sdmmc_controller_initialised = false;
//---------------------------------------------------------------------------------
void my_sdmmc_controller_init( bool force_init )
//---------------------------------------------------------------------------------
{
if (!force_init && my_sdmmc_controller_initialised) return;
deviceSD.isSDHC = 0;
deviceSD.SDOPT = 0;
deviceSD.res = 0;
deviceSD.initarg = 0;
deviceSD.clk = 0x80;
deviceSD.devicenumber = 0;
deviceNAND.isSDHC = 0;
deviceNAND.SDOPT = 0;
deviceNAND.res = 0;
deviceNAND.initarg = 1;
deviceNAND.clk = 0x80;
deviceNAND.devicenumber = 1;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xF7FFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xEFFFu;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
#else
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) |= 0x402u;
#endif
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) = (*(vu16*)(SDMMC_BASE + REG_SDDATACTL) & 0xFFDD) | 2;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFFu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDFu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 512;
#else
*(vu16*)(SDMMC_BASE + REG_SDDATACTL32) &= 0xFFFDu;
*(vu16*)(SDMMC_BASE + REG_SDDATACTL) &= 0xFFDDu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN32) = 0;
#endif
*(vu16*)(SDMMC_BASE + REG_SDBLKCOUNT32) = 1;
*(vu16*)(SDMMC_BASE + REG_SDRESET) &= 0xFFFEu;
*(vu16*)(SDMMC_BASE + REG_SDRESET) |= 1u;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK0) |= TMIO_MASK_ALL;
*(vu16*)(SDMMC_BASE + REG_SDIRMASK1) |= TMIO_MASK_ALL>>16;
*(vu16*)(SDMMC_BASE + 0x0fc) |= 0xDBu; //SDCTL_RESERVED7
*(vu16*)(SDMMC_BASE + 0x0fe) |= 0xDBu; //SDCTL_RESERVED8
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
#ifdef DATA32_SUPPORT
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x20;
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EE;
#else
*(vu16*)(SDMMC_BASE + REG_SDCLKCTL) = 0x40; //Nintendo sets this to 0x20
*(vu16*)(SDMMC_BASE + REG_SDOPT) = 0x40EB; //Nintendo sets this to 0x40EE
#endif
*(vu16*)(SDMMC_BASE + REG_SDPORTSEL) &= 0xFFFCu;
*(vu16*)(SDMMC_BASE + REG_SDBLKLEN) = 512;
*(vu16*)(SDMMC_BASE + REG_SDSTOP) = 0;
my_sdmmc_controller_initialised = true;
my_setTarget(&deviceSD);
}
//---------------------------------------------------------------------------------
static u32 calcSDSize(u8* csd, int type)
//---------------------------------------------------------------------------------
{
u32 result = 0;
if (type == -1) type = csd[14] >> 6;
switch (type)
{
case 0:
{
u32 block_len = csd[9] & 0xf;
block_len = 1 << block_len;
u32 mult = (csd[4] >> 7) | ((csd[5] & 3) << 1);
mult = 1 << (mult + 2);
result = csd[8] & 3;
result = (result << 8) | csd[7];
result = (result << 2) | (csd[6] >> 6);
result = (result + 1) * mult * block_len / 512;
}
break;
case 1:
result = csd[7] & 0x3f;
result = (result << 8) | csd[6];
result = (result << 8) | csd[5];
result = (result + 1) * 1024;
break;
}
return result;
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_init()
//---------------------------------------------------------------------------------
{
// We need to send at least 74 clock pulses.
my_setTarget(&deviceSD);
swiDelay(0x1980); // ~75-76 clocks
// card reset
my_sdmmc_send_command(&deviceSD,0,0);
// CMD8 0x1AA
my_sdmmc_send_command(&deviceSD,0x10408,0x1AA);
u32 temp = (deviceSD.error & 0x1) << 0x1E;
u32 temp2 = 0;
do {
do {
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
// ACMD41
my_sdmmc_send_command(&deviceSD,0x10769,0x00FF8000 | temp);
temp2 = 1;
}
while ( !(deviceSD.error & 1) );
}
while ((deviceSD.ret[0] & 0x80000000) == 0);
if (!((deviceSD.ret[0] >> 30) & 1) || !temp)
temp2 = 0;
deviceSD.isSDHC = temp2;
my_sdmmc_send_command(&deviceSD,0x10602,0);
if (deviceSD.error & 0x4) return -1;
my_sdmmc_send_command(&deviceSD,0x10403,0);
if (deviceSD.error & 0x4) return -1;
deviceSD.initarg = deviceSD.ret[0] >> 0x10;
my_sdmmc_send_command(&deviceSD,0x10609,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// Command Class 10 support
const bool cmd6Supported = ((u8*)deviceSD.ret)[10] & 0x40;
deviceSD.total_size = calcSDSize((u8*)&deviceSD.ret[0],-1);
setckl(0x201); // 16.756991 MHz
my_sdmmc_send_command(&deviceSD,0x10507,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -1;
// ACMD42
my_sdmmc_send_command(&deviceSD,0x1076A,0x0);
if (deviceSD.error & 0x4) return -1;
// CMD55
my_sdmmc_send_command(&deviceSD,0x10437,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -7;
deviceSD.SDOPT = 1;
my_sdmmc_send_command(&deviceSD,0x10446,0x2);
if (deviceSD.error & 0x4) return -8;
sdmmc_mask16(REG_SDOPT, 0x8000, 0); // Switch to 4 bit mode.
// TODO: CMD6 to switch to high speed mode.
if (cmd6Supported)
{
sdmmc_write16(REG_SDSTOP,0);
sdmmc_write16(REG_SDBLKLEN32,64);
sdmmc_write16(REG_SDBLKLEN,64);
deviceSD.rData = NULL;
deviceSD.size = 64;
my_sdmmc_send_command(&deviceSD,0x31C06,0x80FFFFF1);
sdmmc_write16(REG_SDBLKLEN,512);
if (deviceSD.error & 0x4) return -9;
deviceSD.clk = 0x200; // 33.513982 MHz
setckl(0x200);
}
else deviceSD.clk = 0x201; // 16.756991 MHz
my_sdmmc_send_command(&deviceSD,0x1040D,deviceSD.initarg << 0x10);
if (deviceSD.error & 0x4) return -9;
my_sdmmc_send_command(&deviceSD,0x10410,0x200);
if (deviceSD.error & 0x4) return -10;
return 0;
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_init()
//---------------------------------------------------------------------------------
{
my_setTarget(&deviceNAND);
swiDelay(0xF000);
my_sdmmc_send_command(&deviceNAND,0,0);
do {
do {
my_sdmmc_send_command(&deviceNAND,0x10701,0x100000);
}
while ( !(deviceNAND.error & 1) );
}
while ((deviceNAND.ret[0] & 0x80000000) == 0);
my_sdmmc_send_command(&deviceNAND,0x10602,0x0);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10403,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10609,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.total_size = calcSDSize((uint8_t*)&deviceNAND.ret[0],0);
deviceNAND.clk = 1;
setckl(1);
my_sdmmc_send_command(&deviceNAND,0x10407,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.SDOPT = 1;
my_sdmmc_send_command(&deviceNAND,0x10506,0x3B70100);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10506,0x3B90100);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x1040D,deviceNAND.initarg << 0x10);
if ((deviceNAND.error & 0x4))return -1;
my_sdmmc_send_command(&deviceNAND,0x10410,0x200);
if ((deviceNAND.error & 0x4))return -1;
deviceNAND.clk |= 0x200;
my_setTarget(&deviceSD);
return 0;
}
//---------------------------------------------------------------------------------
int my_sdmmc_readsectors(struct mmcdevice *device, u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
if (device->isSDHC == 0) sector_no <<= 9;
my_setTarget(device);
sdmmc_write16(REG_SDSTOP,0x100);
#ifdef DATA32_SUPPORT
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
sdmmc_write16(REG_SDBLKLEN32,0x200);
#endif
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
device->rData = out;
device->size = numsectors << 9;
my_sdmmc_send_command(device,0x33C12,sector_no);
my_setTarget(&deviceSD);
return my_geterror(device);
}
//---------------------------------------------------------------------------------
int my_sdmmc_writesectors(struct mmcdevice *device, u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
if (device->isSDHC == 0)
sector_no <<= 9;
my_setTarget(device);
sdmmc_write16(REG_SDSTOP,0x100);
#ifdef DATA32_SUPPORT
sdmmc_write16(REG_SDBLKCOUNT32,numsectors);
sdmmc_write16(REG_SDBLKLEN32,0x200);
#endif
sdmmc_write16(REG_SDBLKCOUNT,numsectors);
device->tData = in;
device->size = numsectors << 9;
my_sdmmc_send_command(device,0x52C19,sector_no);
my_setTarget(&deviceSD);
return my_geterror(device);
}
//---------------------------------------------------------------------------------
void my_sdmmc_get_cid(int devicenumber, u32 *cid)
//---------------------------------------------------------------------------------
{
struct mmcdevice *device = (devicenumber == 1 ? &deviceNAND : &deviceSD);
int oldIME = enterCriticalSection();
my_setTarget(device);
// use cmd7 to put sd card in standby mode
// CMD7
my_sdmmc_send_command(device, 0x10507, 0);
// get sd card info
// use cmd10 to read CID
my_sdmmc_send_command(device, 0x1060A, device->initarg << 0x10);
for (int i = 0; i < 4; ++i)
cid[i] = device->ret[i];
// put sd card back to transfer mode
// CMD7
my_sdmmc_send_command(device, 0x10507, device->initarg << 0x10);
leaveCriticalSection(oldIME);
}
//---------------------------------------------------------------------------------
void my_sdmmcMsgHandler(int bytes, void *user_data)
//---------------------------------------------------------------------------------
{
FifoMessage msg;
int retval = 0;
fifoGetDatamsg(FIFO_SDMMC, bytes, (u8*)&msg);
int oldIME = enterCriticalSection();
switch (msg.type)
{
case SDMMC_SD_READ_SECTORS:
retval = my_sdmmc_readsectors(&deviceSD, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_SD_WRITE_SECTORS:
retval = my_sdmmc_writesectors(&deviceSD, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_NAND_READ_SECTORS:
retval = my_sdmmc_readsectors(&deviceNAND, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
case SDMMC_NAND_WRITE_SECTORS:
retval = my_sdmmc_writesectors(&deviceNAND, msg.sdParams.startsector, msg.sdParams.numsectors, msg.sdParams.buffer);
break;
}
leaveCriticalSection(oldIME);
fifoSendValue32(FIFO_SDMMC, retval);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_startup()
//---------------------------------------------------------------------------------
{
my_sdmmc_controller_init(false);
return my_sdmmc_nand_init();
}
//---------------------------------------------------------------------------------
int my_sdmmc_sd_startup()
//---------------------------------------------------------------------------------
{
my_sdmmc_controller_init(false);
return my_sdmmc_sdcard_init();
}
//---------------------------------------------------------------------------------
void my_sdmmcValueHandler(u32 value, void* user_data)
//---------------------------------------------------------------------------------
{
int result = 0;
int sdflag = 0;
int oldIME = enterCriticalSection();
switch (value)
{
case SDMMC_HAVE_SD:
result = sdmmc_read16(REG_SDSTATUS0);
break;
case SDMMC_SD_START:
sdflag = 1;
/* Falls through. */
case SDMMC_NAND_START:
if (sdmmc_read16(REG_SDSTATUS0) == 0)
{
result = 1;
}
else
{
result = (sdflag == 1 ) ? my_sdmmc_sd_startup() : my_sdmmc_nand_startup();
}
break;
case SDMMC_SD_IS_INSERTED:
result = my_sdmmc_cardinserted();
break;
case SDMMC_SD_STOP:
break;
case SDMMC_NAND_SIZE:
result = deviceNAND.total_size;
break;
}
leaveCriticalSection(oldIME);
fifoSendValue32(FIFO_SDMMC, result);
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
return my_sdmmc_readsectors(&deviceSD, sector_no, numsectors, out);
}
//---------------------------------------------------------------------------------
int my_sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
return my_sdmmc_writesectors(&deviceSD, sector_no, numsectors, in);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out)
//---------------------------------------------------------------------------------
{
return my_sdmmc_readsectors(&deviceNAND, sector_no, numsectors, out);
}
//---------------------------------------------------------------------------------
int my_sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in)
//---------------------------------------------------------------------------------
{
return my_sdmmc_writesectors(&deviceNAND, sector_no, numsectors, in);
}

205
arm7/src/my_sdmmc.h Normal file
View File

@ -0,0 +1,205 @@
#ifndef __SDMMC_H__
#define __SDMMC_H__
#include <nds/ndstypes.h>
#define DATA32_SUPPORT
#define SDMMC_BASE 0x04004800
#define REG_SDCMD 0x00
#define REG_SDPORTSEL 0x02
#define REG_SDCMDARG 0x04
#define REG_SDCMDARG0 0x04
#define REG_SDCMDARG1 0x06
#define REG_SDSTOP 0x08
#define REG_SDRESP 0x0c
#define REG_SDBLKCOUNT 0x0a
#define REG_SDRESP0 0x0c
#define REG_SDRESP1 0x0e
#define REG_SDRESP2 0x10
#define REG_SDRESP3 0x12
#define REG_SDRESP4 0x14
#define REG_SDRESP5 0x16
#define REG_SDRESP6 0x18
#define REG_SDRESP7 0x1a
#define REG_SDSTATUS0 0x1c
#define REG_SDSTATUS1 0x1e
#define REG_SDIRMASK0 0x20
#define REG_SDIRMASK1 0x22
#define REG_SDCLKCTL 0x24
#define REG_SDBLKLEN 0x26
#define REG_SDOPT 0x28
#define REG_SDFIFO 0x30
#define REG_SDDATACTL 0xd8
#define REG_SDRESET 0xe0
#define REG_SDPROTECTED 0xf6 //bit 0 determines if sd is protected or not?
#define REG_SDDATACTL32 0x100
#define REG_SDBLKLEN32 0x104
#define REG_SDBLKCOUNT32 0x108
#define REG_SDFIFO32 0x10C
#define REG_CLK_AND_WAIT_CTL 0x138
#define REG_RESET_SDIO 0x1e0
//The below defines are from linux kernel drivers/mmc tmio_mmc.h.
/* Definitions for values the CTRL_STATUS register can take. */
#define TMIO_STAT0_CMDRESPEND 0x0001
#define TMIO_STAT0_DATAEND 0x0004
#define TMIO_STAT0_CARD_REMOVE 0x0008
#define TMIO_STAT0_CARD_INSERT 0x0010
#define TMIO_STAT0_SIGSTATE 0x0020
#define TMIO_STAT0_WRPROTECT 0x0080
#define TMIO_STAT0_CARD_REMOVE_A 0x0100
#define TMIO_STAT0_CARD_INSERT_A 0x0200
#define TMIO_STAT0_SIGSTATE_A 0x0400
#define TMIO_STAT1_CMD_IDX_ERR 0x0001
#define TMIO_STAT1_CRCFAIL 0x0002
#define TMIO_STAT1_STOPBIT_ERR 0x0004
#define TMIO_STAT1_DATATIMEOUT 0x0008
#define TMIO_STAT1_RXOVERFLOW 0x0010
#define TMIO_STAT1_TXUNDERRUN 0x0020
#define TMIO_STAT1_CMDTIMEOUT 0x0040
#define TMIO_STAT1_RXRDY 0x0100
#define TMIO_STAT1_TXRQ 0x0200
#define TMIO_STAT1_ILL_FUNC 0x2000
#define TMIO_STAT1_CMD_BUSY 0x4000
#define TMIO_STAT1_ILL_ACCESS 0x8000
#define SDMC_NORMAL 0x00000000
#define SDMC_ERR_COMMAND 0x00000001
#define SDMC_ERR_CRC 0x00000002
#define SDMC_ERR_END 0x00000004
#define SDMC_ERR_TIMEOUT 0x00000008
#define SDMC_ERR_FIFO_OVF 0x00000010
#define SDMC_ERR_FIFO_UDF 0x00000020
#define SDMC_ERR_WP 0x00000040
#define SDMC_ERR_ABORT 0x00000080
#define SDMC_ERR_FPGA_TIMEOUT 0x00000100
#define SDMC_ERR_PARAM 0x00000200
#define SDMC_ERR_R1_STATUS 0x00000800
#define SDMC_ERR_NUM_WR_SECTORS 0x00001000
#define SDMC_ERR_RESET 0x00002000
#define SDMC_ERR_ILA 0x00004000
#define SDMC_ERR_INFO_DETECT 0x00008000
#define SDMC_STAT_ERR_UNKNOWN 0x00080000
#define SDMC_STAT_ERR_CC 0x00100000
#define SDMC_STAT_ERR_ECC_FAILED 0x00200000
#define SDMC_STAT_ERR_CRC 0x00800000
#define SDMC_STAT_ERR_OTHER 0xf9c70008
#define TMIO_MASK_ALL 0x837f031d
#define TMIO_MASK_GW (TMIO_STAT1_ILL_ACCESS | TMIO_STAT1_CMDTIMEOUT | TMIO_STAT1_TXUNDERRUN | TMIO_STAT1_RXOVERFLOW | \
TMIO_STAT1_DATATIMEOUT | TMIO_STAT1_STOPBIT_ERR | TMIO_STAT1_CRCFAIL | TMIO_STAT1_CMD_IDX_ERR)
#define TMIO_MASK_READOP (TMIO_STAT1_RXRDY | TMIO_STAT1_DATAEND)
#define TMIO_MASK_WRITEOP (TMIO_STAT1_TXRQ | TMIO_STAT1_DATAEND)
typedef struct mmcdevice {
u8* rData;
const u8* tData;
u32 size;
u32 startOffset;
u32 endOffset;
u32 error;
u16 stat0;
u16 stat1;
u32 ret[4];
u32 initarg;
u32 isSDHC;
u32 clk;
u32 SDOPT;
u32 devicenumber;
u32 total_size; //size in sectors of the device
u32 res;
} mmcdevice;
enum {
MMC_DEVICE_SDCARD,
MMC_DEVICE_NAND,
};
void my_sdmmc_controller_init(bool force_init);
void my_sdmmc_initirq();
int my_sdmmc_cardinserted();
int my_sdmmc_sdcard_init();
int my_sdmmc_nand_init();
void my_sdmmc_get_cid(int devicenumber, u32 *cid);
static inline void sdmmc_nand_cid( u32 *cid)
{
my_sdmmc_get_cid(MMC_DEVICE_NAND, cid);
}
static inline void sdmmc_sdcard_cid( u32 *cid)
{
my_sdmmc_get_cid(MMC_DEVICE_SDCARD, cid);
}
int my_sdmmc_sdcard_readsectors(u32 sector_no, u32 numsectors, void *out);
int my_sdmmc_sdcard_writesectors(u32 sector_no, u32 numsectors, void *in);
int my_sdmmc_nand_readsectors(u32 sector_no, u32 numsectors, void *out);
int my_sdmmc_nand_writesectors(u32 sector_no, u32 numsectors, void *in);
extern u32 sdmmc_cid[];
extern int sdmmc_curdevice;
//---------------------------------------------------------------------------------
static inline u16 sdmmc_read16(u16 reg)
//---------------------------------------------------------------------------------
{
return *(vu16*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write16(u16 reg, u16 val)
//---------------------------------------------------------------------------------
{
*(vu16*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline u32 sdmmc_read32(u16 reg)
//---------------------------------------------------------------------------------
{
return *(vu32*)(SDMMC_BASE + reg);
}
//---------------------------------------------------------------------------------
static inline void sdmmc_write32(u16 reg, u32 val)
//---------------------------------------------------------------------------------
{
*(vu32*)(SDMMC_BASE + reg) = val;
}
//---------------------------------------------------------------------------------
static inline void sdmmc_mask16(u16 reg, u16 clear, u16 set)
//---------------------------------------------------------------------------------
{
u16 val = sdmmc_read16(reg);
val &= ~clear;
val |= set;
sdmmc_write16(reg, val);
}
//---------------------------------------------------------------------------------
static inline void setckl(u32 data)
//---------------------------------------------------------------------------------
{
sdmmc_mask16(REG_SDCLKCTL, 0x100, 0);
sdmmc_mask16(REG_SDCLKCTL, 0x2FF, data & 0x2FF);
sdmmc_mask16(REG_SDCLKCTL, 0x0, 0x100);
}
#endif

166
arm9/Makefile Normal file
View File

@ -0,0 +1,166 @@
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITARM)),)
$(error "Please set DEVKITARM in your environment. export DEVKITARM=<path to>devkitARM")
endif
include $(DEVKITARM)/ds_rules
# If on a tagged commit, use just tag
ifneq ($(shell echo $(shell git tag -l --points-at HEAD) | head -c 1),)
GIT_VER := $(shell git tag -l --points-at HEAD)
else
GIT_VER := $(shell git describe --abbrev=0 --tags)-$(shell git rev-parse --short=7 HEAD)
endif
# Ensure version.h exists
ifeq (,$(wildcard include/version.h))
$(shell mkdir -p include)
$(shell touch include/version.h)
endif
ifeq (,$(wildcard ../../nitrofiles/version_twlnandtool))
$(shell mkdir -p ../nitrofiles)
$(shell touch ../nitrofiles/version_twlnandtool)
endif
# Print new version if changed
ifeq (,$(findstring $(GIT_VER), $(shell cat include/version.h)))
$(shell printf "\"$(GIT_VER)\"" > ../../nitrofiles/version_twlnandtool)
$(shell printf "#ifndef VERSION_H\n#define VERSION_H\n\n#define VERSION \"$(GIT_VER)\"\n\n#endif // VERSION_H\n" > include/version.h)
endif
#---------------------------------------------------------------------------------
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
# DATA is a list of directories containing binary files
# all directories are relative to this makefile
#---------------------------------------------------------------------------------
BUILD := build
SOURCES := src src/nand src/nand/polarssl src/nand/twltool
INCLUDES := include src/nand
DATA := ../data
GRAPHICS := ../gfx
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
ARCH := -mthumb -mthumb-interwork
CFLAGS := -g -Wall -O2\
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math \
$(ARCH)
CFLAGS += $(INCLUDE) -DARM9
CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions
ASFLAGS := -g $(ARCH) -march=armv5te -mtune=arm946e-s
LDFLAGS = -specs=ds_arm9.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lfilesystem -lfat -lnds9
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(LIBNDS)
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export ARM9ELF := $(CURDIR)/$(TARGET).elf
export DEPSDIR := $(CURDIR)/$(BUILD)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
#---------------------------------------------------------------------------------
export LD := $(CC)
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
export LD := $(CXX)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(BMPFILES:.bmp=.o) \
$(PNGFILES:.png=.o) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o)
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD)
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) *.elf *.srl* *.bin
#---------------------------------------------------------------------------------
else
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(ARM9ELF) : $(OFILES)
@echo linking $(notdir $@)
@$(LD) $(LDFLAGS) $(OFILES) $(LIBPATHS) $(LIBS) -o $@
#---------------------------------------------------------------------------------
# you need a rule like this for each extension you use as binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
#---------------------------------------------------------------------------------
# This rule creates assembly source files using grit
# grit takes an image file and a .grit describing how the file is to be processed
# add additional rules like this for each image extension
# you use in the graphics folders
#---------------------------------------------------------------------------------
%.s %.h : %.bmp %.grit
#---------------------------------------------------------------------------------
grit $< -fts -o$*
#---------------------------------------------------------------------------------
%.s %.h : %.png %.grit
#---------------------------------------------------------------------------------
grit $< -fts -o$*
-include $(DEPSDIR)/*.d
#---------------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------------

324
arm9/src/main.c Normal file
View File

@ -0,0 +1,324 @@
#include "main.h"
#include "menu.h"
#include "message.h"
#include "nand/nandio.h"
#include "storage.h"
#include "version.h"
#include <dirent.h>
#include <time.h>
#include "nand/filesystem.h"
#include "nand/nandfirm.h"
#include "video.h"
#include "nitrofs.h"
#include <stdlib.h>
#include <stdio.h>
/*
TODO:
- Write good NAND I/O routine
- Import NandFirm
- Detect debugger vs dev
- Create FAT
Okay so this might be a bad idea in my hands lol
Find an FS lib? No. Fuck no.
Paste this (encrypted) into sector 0x877 (0x10EE00).
unsigned char twlMain[56] = {
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x34, 0x00,
0x20, 0x00, 0x10, 0x00, 0x77, 0x08, 0x00, 0x00, 0x89, 0x6F, 0x06, 0x00,
0x00, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00
};
Pad to 0x200b and make sure to have 0x55AA (CRINGE I HATE MBR I HATE MBR I HATE MBR) <-- I don't even remember writing this.
Now fill 0x200*688b (total 0x171000) afterwards with zerobytes.
Congrats. This is a formatted file system. Please don't hurt me.
Okay but what about twl_photo? Uhhh nobody likes that.... oh fine.
Paste this (encrypted) into sector 0x6784D (0xCF09A00)
unsigned char twlPhoto[56] = {
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x09, 0x00,
0x20, 0x00, 0x10, 0x00, 0x4D, 0x78, 0x06, 0x00, 0xB3, 0x05, 0x01, 0x00,
0x01, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00
};
Same thing but fill 0x200*13Ab (total 0x27400) afterwards with zerobytes.
Okay lol how do I do the VBR padding to 0x200
- Dump VBR start into a variable
- for loop to add the rest of the zero bytes (0x1C8b)
- end with 0x55AA
Awesome!
- Fancy windows like TWL EVA
- System transfer (way later on)
*/
bool programEnd = false;
bool sdnandMode = true;
bool unlaunchFound = false;
bool unlaunchPatches = false;
bool arm7Exiting = false;
bool charging = false;
u8 batteryLevel = 0;
u8 region = 0;
u32 consoleSign;
char consoleSignName[9];
PrintConsole topScreen;
PrintConsole bottomScreen;
typedef enum {
MENUSTATE_CHECK_NANDFIRM,
MENUSTATE_FS_MENU,
MENUSTATE_CHECK_NANDINFO,
MENUSTATE_TEST,
MENUSTATE_EXIT
} MenuState;
static int _mainMenu(int cursor)
{
//top screen
clearScreen(cMAIN);
printf("\n\x1B[40mTwlNandTool Ver%s", VERSION);
printf("\nRun on: %s (%02lX)", consoleSignName, consoleSign);
printf("\n\nNAND repair tool by RMC/RVTR");
printf("\n\nMode: Main Menu");
//menu
Menu* m = newMenu();
setMenuHeader(m, "TwlNandTool");
char modeStr[32];
addMenuItem(m, "Check NandFirm", NULL, 0);
addMenuItem(m, "FileSystem Menu", NULL, 0);
addMenuItem(m, "CID Info", NULL, 0);
addMenuItem(m, "Debug1", NULL, 0);
addMenuItem(m, "Exit", NULL, 0);
m->cursor = cursor;
//bottom screen
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_A)
break;
}
int result = m->cursor;
freeMenu(m);
return result;
}
void fifoHandlerPower(u32 value32, void* userdata)
{
if (value32 == 0x54495845) // 'EXIT'
{
programEnd = true;
arm7Exiting = true;
}
}
void fifoHandlerBattery(u32 value32, void* userdata)
{
batteryLevel = value32 & 0xF;
charging = (value32 & BIT(7)) != 0;
}
int main(int argc, char **argv)
{
fifoWaitValue32(FIFO_USER_01);
consoleSign = fifoGetValue32(FIFO_USER_01);
if (consoleSign == 0x00) {
strcpy(consoleSignName, "RETAIL");
} else {
strcpy(consoleSignName, "PANDA");
}
videoInit();
srand(time(0));
keysSetRepeat(25, 5);
//_setupScreens();
fifoSetValue32Handler(FIFO_USER_01, fifoHandlerPower, NULL);
fifoSetValue32Handler(FIFO_USER_03, fifoHandlerBattery, NULL);
//DSi check
if (!isDSiMode()) {
messageBox("\x1B[31mError:\x1B[33m This app is only for DSi.");
return 0;
}
//setup sd card access
if (!fatInitDefault()) {
messageBox("fatInitDefault()...\x1B[31mFailed\n\x1B[40m\n\nSome features will not work.");
//return 0;
}
//setup sd card access
if(!nitroFSInit(argv[0])) {
if(!nitroFSInit("TwlNandTool.prod.srl") || !nitroFSGood()) {
if(!nitroFSInit("TwlNandTool.dev.srl") || !nitroFSGood()) {
if(!nitroFSInit("ntrboot.nds") || !nitroFSGood()) {
messageBox("nitroFSInit()...\x1B[31mFailed\n\x1B[40m\nSome features will not work.\n\nTry placing the SRL for your DSion your SD card root like this:\n\nSDMC:/TwlNandTool.prod.srl\nSDMC:/TwlNandTool.dev.srl\nSDMC:/ntrboot.nds\n\n");
}
}
}
//return 0;
}
//setup nand access
if (!fatMountSimple("nand", &io_dsi_nand)) {
messageBox("nand init \x1B[31mfailed\n\x1B[40m\n\nNAND must be repaired.");
}
int cursor = 0;
while (!programEnd)
{
cursor = _mainMenu(cursor);
switch (cursor)
{
case MENUSTATE_CHECK_NANDFIRM:
nandFirmRead();
break;
case MENUSTATE_FS_MENU:
fsMain();
break;
case MENUSTATE_CHECK_NANDINFO:
nandPrintInfo();
break;
case MENUSTATE_TEST:
testRoutine();
break;
case MENUSTATE_EXIT:
programEnd = true;
break;
}
}
clearScreen(cSUB);
printf("Unmounting NAND...\n");
fatUnmount("nand:");
printf("Merging stages...\n");
nandio_shutdown();
fifoSendValue32(FIFO_USER_02, 0x54495845); // 'EXIT'
while (arm7Exiting)
swiWaitForVBlank();
return 0;
}
int testRoutine(void) {
clearScreen(cSUB);
iprintf("\n>> Test Area");
iprintf("\n NAND R/W test ");
iprintf("\n--------------------------------");
int fail=0;
int byteAddress = 0x4E400;
int inputLength = 0x401;
int byteOffset = byteAddress % SECTOR_SIZE;
int sectorNum = byteAddress / SECTOR_SIZE;
int byteEndOffset = (byteAddress+inputLength) % SECTOR_SIZE;
int sectorEndNum = (byteAddress+inputLength) / SECTOR_SIZE;
iprintf("\nInput address : %02X", byteAddress);
iprintf("\nInput length : %02X", inputLength);
iprintf("\nInput sector : %02X", sectorNum);
iprintf("\nSector offset : %02X", byteOffset);
iprintf("\nEnd sector : %02X", sectorEndNum);
iprintf("\nSector offset : %02X", byteEndOffset);
iprintf("\n");
int i;
if (sectorNum == sectorEndNum) {
// Handle a single sector write differently since it is unpredictable
//nand_ReadSectors(sectorNum, 1, sector_buf);
//memcpy(sector_buf + inputLength, file_buf, inputLength);
//nand_WriteSectors(1, 1, sector_buf);
iprintf("\n%02X to %02X of %02X", byteOffset, byteEndOffset, sectorNum);
iprintf("\n%02X to %02X of file_buf", 0, inputLength);
} else {
for (i = sectorNum; i < sectorEndNum + 1;) {
// Back up sector
//nand_ReadSectors(i, 1, sector_buf);
if (i == sectorNum) {
// Handle the first sector differently since we'll only be writing a partial amount of data
//memcpy(sector_buf + byteOffset, file_buf, SECTOR_SIZE - byteOffset);
iprintf("\n%02X to %02X of %02X", byteOffset, (SECTOR_SIZE), i);
iprintf("\n0 to %02X of file_buf", (SECTOR_SIZE - byteOffset), i);
} else if (i == sectorEndNum) {
// Handle the last sector differently since we'll only be writing a partial amount of data
//memcpy(sector_buf, file_buf + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), byteEndOffset);
iprintf("\n0 to %02X of %02X (end)", byteEndOffset, i);
iprintf("\n%02X to %02X of file_buf", ((i - sectorNum) * SECTOR_SIZE) - byteOffset, (((i - sectorNum) * SECTOR_SIZE) + byteEndOffset) - byteOffset);
} else {
// Handle the middle sectors the same because they'll always be the full sector
//memcpy(sector_buf, file_buf + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), SECTOR_SIZE);
iprintf("\n0 to %02X of %02X", SECTOR_SIZE, i);
iprintf("\n%02X to %02X of file_buf", ((i - sectorNum) * SECTOR_SIZE) - byteOffset, (((i - sectorNum) * SECTOR_SIZE) - byteOffset) + SECTOR_SIZE);
}
i++;
// I need to do a cmp here
// Write sector
//nand_WriteSectors(i, 1, sector_buf);
}
}
iprintf("\n\n\n Please Push Select To Return ");
// Do some check to make sure it is not outside of NAND range.
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}
void clearScreen(enum console c)
{
consoleSet(c);
iprintf("\x1b[2J");
}

36
arm9/src/main.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef MAIN_H
#define MAIN_H
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include "nand/nandfirm.h"
#include "nand/nandio.h"
#include "nand/filesystem.h"
#include "video.h"
#include "nitrofs.h"
extern bool programEnd;
extern bool sdnandMode;
extern bool unlaunchFound;
extern bool unlaunchPatches;
extern bool charging;
extern u8 batteryLevel;
extern u8 region;
void installMenu();
void titleMenu();
void backupMenu();
void testMenu();
int testRoutine(void);
extern PrintConsole topScreen;
extern PrintConsole bottomScreen;
void clearScreen(enum console c);
#define abs(X) ( (X) < 0 ? -(X): (X) )
#define sign(X) ( ((X) > 0) - ((X) < 0) )
#define repeat(X) for (int _I_ = 0; _I_ < (X); _I_++)
#endif

228
arm9/src/menu.c Normal file
View File

@ -0,0 +1,228 @@
#include "menu.h"
#include "main.h"
#include "video.h"
Menu* newMenu()
{
Menu* m = (Menu*)malloc(sizeof(Menu));
m->cursor = 0;
m->page = 0;
m->itemCount = 0;
m->nextPage = false;
m->changePage = 0;
m->header[0] = '\0';
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
m->items[i].directory = false;
m->items[i].label = NULL;
m->items[i].value = NULL;
}
return m;
}
void freeMenu(Menu* m)
{
if (!m) return;
clearMenu(m);
free(m);
m = NULL;
}
void addMenuItem(Menu* m, char const* label, char const* value, bool directory)
{
if (!m) return;
int i = m->itemCount;
if (i >= ITEMS_PER_PAGE) return;
m->items[i].directory = directory;
if (label)
{
m->items[i].label = (char*)malloc(32);
sprintf(m->items[i].label, "%.31s", label);
}
if (value)
{
m->items[i].value = (char*)malloc(strlen(value)+1);
sprintf(m->items[i].value, "%s", value);
}
m->itemCount += 1;
}
static int alphabeticalCompare(const void* a, const void* b)
{
const Item* itemA = (const Item*)a;
const Item* itemB = (const Item*)b;
if (itemA->directory && !itemB->directory)
return -1;
else if (!itemA->directory && itemB->directory)
return 1;
else
return strcasecmp(itemA->label, itemB->label);
}
void sortMenuItems(Menu* m)
{
qsort(m->items, m->itemCount, sizeof(Item), alphabeticalCompare);
}
void setMenuHeader(Menu* m, char* str)
{
if (!m) return;
if (!str)
{
m->header[0] = '\0';
return;
}
char* strPtr = str;
if (strlen(strPtr) > 30)
strPtr = str + (strlen(strPtr) - 30);
sprintf(m->header, "%.30s", strPtr);
}
void resetMenu(Menu* m)
{
m->cursor = 0;
m->page = 0;
m->changePage = 0;
m->nextPage = 0;
}
void clearMenu(Menu* m)
{
if (!m) return;
for (int i = 0; i < ITEMS_PER_PAGE; i++)
{
if (m->items[i].label)
{
free(m->items[i].label);
m->items[i].label = NULL;
}
if (m->items[i].value)
{
free(m->items[i].value);
m->items[i].value = NULL;
}
}
m->itemCount = 0;
}
void printMenu(Menu* m)
{
clearScreen(cSUB);
if (!m) return;
//header
iprintf("\x1B[42m"); //green
iprintf("%.30s\n\n", m->header);
iprintf("\x1B[40m"); //white
if (m->itemCount <= 0)
{
iprintf("Back - [B]\n");
return;
}
//items
for (int i = 0; i < m->itemCount; i++)
{
if (m->items[i].label)
{
if (m->items[i].directory)
iprintf(" [%.28s]\n", m->items[i].label);
else
iprintf(" %.30s\n", m->items[i].label);
}
else
iprintf(" \n");
}
//cursor
iprintf("\x1b[%d;0H>", 2 + m->cursor);
//scroll arrows
if (m->page > 0)
iprintf("\x1b[2;31H^");
if (m->nextPage)
iprintf("\x1b[21;31Hv");
}
static void _moveCursor(Menu* m, int dir)
{
if (m->changePage != 0)
return;
m->cursor += sign(dir);
if (m->cursor < 0)
{
if (m->page <= 0)
m->cursor = 0;
else
{
m->changePage = -1;
m->cursor = ITEMS_PER_PAGE - 1;
}
}
else if (m->cursor > m->itemCount-1)
{
if (m->nextPage && m->cursor >= ITEMS_PER_PAGE)
{
m->changePage = 1;
m->cursor = 0;
}
else
{
m->cursor = m->itemCount-1;
}
}
}
bool moveCursor(Menu* m)
{
if (!m) return false;
m->changePage = 0;
int lastCursor = m->cursor;
u32 down = keysDownRepeat();
if (down & KEY_DOWN)
_moveCursor(m, 1);
else if (down & KEY_UP)
_moveCursor(m, -1);
if (down & KEY_RIGHT)
{
repeat(10)
_moveCursor(m, 1);
}
else if (down & KEY_LEFT)
{
repeat(10)
_moveCursor(m, -1);
}
return !(lastCursor == m->cursor);
}

37
arm9/src/menu.h Normal file
View File

@ -0,0 +1,37 @@
#ifndef MENU_H
#define MENU_H
#include <nds/ndstypes.h>
#define ITEMS_PER_PAGE 20
typedef struct {
bool directory;
char* label;
char* value;
} Item;
typedef struct {
int cursor;
int page;
int itemCount;
bool nextPage;
int changePage;
char header[32];
Item items[ITEMS_PER_PAGE];
} Menu;
Menu* newMenu();
void freeMenu(Menu* m);
void addMenuItem(Menu* m, char const* label, char const* value, bool directory);
void sortMenuItems(Menu* m);
void setMenuHeader(Menu* m, char* str);
void resetMenu(Menu* m);
void clearMenu(Menu* m);
void printMenu(Menu* m);
bool moveCursor(Menu* m);
#endif

162
arm9/src/message.c Normal file
View File

@ -0,0 +1,162 @@
#include "message.h"
#include "main.h"
#include "video.h"
void keyWait(u32 key)
{
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & key)
break;
}
}
bool choiceBox(char* message)
{
const int choiceRow = 10;
int cursor = 0;
clearScreen(cSUB);
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", message);
iprintf("\x1B[40m"); //white
iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
//Clear cursor
iprintf("\x1b[%d;0H ", choiceRow + cursor);
if (keysDown() & (KEY_UP | KEY_DOWN))
cursor = !cursor;
//Print cursor
iprintf("\x1b[%d;0H>", choiceRow + cursor);
if (keysDown() & (KEY_A | KEY_START))
break;
if (keysDown() & KEY_B)
{
cursor = 1;
break;
}
}
scanKeys();
return (cursor == 0)? YES: NO;
}
bool choicePrint(char* message)
{
bool choice = NO;
iprintf("\x1B[34m"); //yellow
iprintf("\n%s\n", message);
iprintf("\x1B[40m"); //white
iprintf("Yes - [A]\nNo - [B]\n");
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_A)
{
choice = YES;
break;
}
else if (keysDown() & KEY_B)
{
choice = NO;
break;
}
}
scanKeys();
return choice;
}
const static u16 keys[] = {KEY_UP, KEY_DOWN, KEY_RIGHT, KEY_LEFT, KEY_A, KEY_B, KEY_X, KEY_Y};
const static char *keysLabels[] = {"\x18", "\x19", "\x1A", "\x1B", "<A>", "<B>", "<X>", "<Y>"};
bool randomConfirmBox(char* message)
{
const int choiceRow = 10;
int sequencePosition = 0;
u8 sequence[8];
for (int i = 0; i < sizeof(sequence); i++)
{
sequence[i] = rand() % (sizeof(keys) / sizeof(keys[0]));
}
clearScreen(cSUB);
iprintf("\x1B[43m"); //yellow
iprintf("%s\n", message);
iprintf("\x1B[40m"); //white
iprintf("\n<START> cancel\n");
while (!programEnd && sequencePosition < sizeof(sequence))
{
swiWaitForVBlank();
scanKeys();
//Print sequence
iprintf("\x1b[%d;0H", choiceRow);
for (int i = 0; i < sizeof(sequence); i++)
{
iprintf("\x1B[%0om", i < sequencePosition ? 032 : 047);
iprintf("%s ", keysLabels[sequence[i]]);
}
if (keysDown() & (KEY_UP | KEY_DOWN | KEY_RIGHT | KEY_LEFT | KEY_A | KEY_B | KEY_X | KEY_Y))
{
if (keysDown() & keys[sequence[sequencePosition]])
sequencePosition++;
else
sequencePosition = 0;
}
if (keysDown() & KEY_START)
{
sequencePosition = 0;
break;
}
}
scanKeys();
return sequencePosition == sizeof(sequence);
}
void messageBox(char* message)
{
clearScreen(cSUB);
messagePrint(message);
}
void messagePrint(char* message)
{
iprintf("%s\n", message);
iprintf("\nOkay - [A]\n");
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & (KEY_A | KEY_B | KEY_START))
break;
}
scanKeys();
}

19
arm9/src/message.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef MESSAGE_H
#define MESSAGE_H
#include <nds/ndstypes.h>
#include "video.h"
enum {
YES = true,
NO = false
};
void keyWait(u32 key);
bool choiceBox(char* message);
bool choicePrint(char* message);
bool randomConfirmBox(char* message);
void messageBox(char* message);
void messagePrint(char* message);
#endif

6
arm9/src/message/main.c Normal file
View File

@ -0,0 +1,6 @@
void death(char *message, u8 *buffer){
iprintf("%s\n", message);
iprintf("Hold Power to exit\n");
free(buffer);
while(1)swiWaitForVBlank();
}

1
arm9/src/message/main.h Normal file
View File

@ -0,0 +1 @@
void death(char *message, u8 *buffer);

138
arm9/src/nand/crypto.c Normal file
View File

@ -0,0 +1,138 @@
#include <stdint.h>
#include "crypto.h"
#include "u128_math.h"
#include "f_xy.h"
#include "twltool/dsi.h"
// more info:
// https://github.com/Jimmy-Z/TWLbf/blob/master/dsi.c
// https://github.com/Jimmy-Z/bfCL/blob/master/dsi.h
// ported back to 32 bit for ARM9
static dsi_context nand_ctx;
static dsi_context boot2_ctx;
static dsi_es_context es_ctx;
static uint8_t nand_ctr_iv[16];
static uint8_t boot2_ctr[16];
static void generate_key(uint8_t *generated_key, const uint32_t *console_id, const key_mode_t mode)
{
uint32_t key[4];
switch (mode)
{
case NAND:
key[0] = console_id[0];
key[1] = console_id[0] ^ KEYSEED_DSI_NAND_0;
key[2] = console_id[1] ^ KEYSEED_DSI_NAND_1;
key[3] = console_id[1];
break;
case NAND_3DS:
key[0] = (console_id[0] ^ KEYSEED_3DS_NAND_0) | 0x80000000;
key[1] = KEYSEED_3DS_NAND_1;
key[2] = KEYSEED_3DS_NAND_2;
key[3] = console_id[1] ^ KEYSEED_3DS_NAND_3;
break;
case ES:
key[0] = KEYSEED_ES_0;
key[1] = KEYSEED_ES_1;
key[2] = console_id[1] ^ KEYSEED_ES_2;
key[3] = console_id[0];
break;
default:
break;
}
u128_xor((uint8_t *)key, mode == ES ? getDSiEsKeyY() : DSi_NAND_KEY_Y);
u128_add((uint8_t *)key, DSi_KEY_MAGIC);
u128_lrot((uint8_t *)key, 42);
memcpy(generated_key, key, 16);
}
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len)
{
uint8_t digest[SHA1_LEN];
swiSHA1Calc(digest, data, len);
return memcmp(digest, digest_verify, SHA1_LEN);
}
void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS)
{
uint32_t console_id[2];
GET_UINT32_BE(console_id[0], console_id_be, 4);
GET_UINT32_BE(console_id[1], console_id_be, 0);
uint8_t key[16];
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
dsi_set_key(&nand_ctx, key);
u32 normalkey[4];
u32 tadsrl_keyX[4] = {0x4E00004A, 0x4A00004E, 0, 0};
tadsrl_keyX[2] = console_id[1] ^ 0xC80C4B72;
tadsrl_keyX[3] = console_id[0];
F_XY((u8 *)normalkey, (u8 *)tadsrl_keyX, getDSiEsKeyY());
dsi_es_init(&es_ctx, (u8*)normalkey);
dsi_set_key(&boot2_ctx, DSi_BOOT2_KEY);
swiSHA1Calc(nand_ctr_iv, emmc_cid, 16);
}
// crypt one block, in/out must be aligned to 32 bit(restriction induced by xor_128)
// offset as block offset, block as AES block
void dsi_nand_crypt_1(uint8_t* out, const uint8_t* in, uint32_t offset)
{
uint8_t ctr[16];
memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv));
u128_add32(ctr, offset);
dsi_set_ctr(&nand_ctx, ctr);
dsi_crypt_ctr(&nand_ctx, in, out, 16);
}
void dsi_nand_crypt(uint8_t* out, const uint8_t* in, uint32_t offset, unsigned count)
{
uint8_t ctr[16];
memcpy(ctr, nand_ctr_iv, sizeof(nand_ctr_iv));
u128_add32(ctr, offset);
for (unsigned i = 0; i < count; ++i)
{
dsi_set_ctr(&nand_ctx, ctr);
dsi_crypt_ctr(&nand_ctx, in, out, 16);
out += AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
u128_add32(ctr, 1);
}
}
int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode)
{
if (mode == DECRYPT)
return dsi_es_decrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
else
dsi_es_encrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
return 0;
}
void dsi_boot2_crypt_set_ctr(uint32_t size_r)
{
for (int i=0;i<4;i++)
{
boot2_ctr[i] = (size_r) >> (8*i);
boot2_ctr[i+4] = (-size_r) >> (8*i);
boot2_ctr[i+8] = (~size_r) >> (8*i);
boot2_ctr[i+12] = 0;
}
}
void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count)
{
for (unsigned i = 0; i < count; ++i)
{
dsi_set_ctr(&boot2_ctx, boot2_ctr);
dsi_crypt_ctr(&boot2_ctx, in, out, 16);
out += AES_BLOCK_SIZE;
in += AES_BLOCK_SIZE;
u128_add32(boot2_ctr, 1);
}
}

70
arm9/src/nand/crypto.h Normal file
View File

@ -0,0 +1,70 @@
#pragma once
#include <nds.h>
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
#define AES_BLOCK_SIZE 16
#define SHA1_LEN 20
#define KEYSEED_DSI_NAND_0 0x24ee6906
#define KEYSEED_DSI_NAND_1 0xe65b601d
#define KEYSEED_3DS_NAND_0 0xb358a6af
#define KEYSEED_3DS_NAND_1 0x544e494e
#define KEYSEED_3DS_NAND_2 0x4f444e45
#define KEYSEED_3DS_NAND_3 0x08c267b7
#define KEYSEED_ES_0 0x4e00004a
#define KEYSEED_ES_1 0x4a00004e
#define KEYSEED_ES_2 0xc80c4b72
#define GET_UINT32_BE(n, b, i) \
((uint8_t*)&(n))[0] = (b)[i + 3]; \
((uint8_t*)&(n))[1] = (b)[i + 2]; \
((uint8_t*)&(n))[2] = (b)[i + 1]; \
((uint8_t*)&(n))[3] = (b)[i + 0]
#define PUT_UINT32_BE(n, b, i) \
(b)[i + 0] = ((uint8_t*)&(n))[3]; \
(b)[i + 1] = ((uint8_t*)&(n))[2]; \
(b)[i + 2] = ((uint8_t*)&(n))[1]; \
(b)[i + 3] = ((uint8_t*)&(n))[0]
typedef enum {
ENCRYPT,
DECRYPT
} crypt_mode_t;
typedef enum {
NAND,
NAND_3DS,
ES
} key_mode_t;
// don't want to include nds.h just for this
void swiSHA1Calc(void *digest, const void *buf, size_t len);
int dsi_sha1_verify(const void *digest_verify, const void *data, unsigned len);
void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int is3DS);
void dsi_nand_crypt_1(uint8_t *out, const uint8_t* in, u32 offset);
void dsi_nand_crypt(uint8_t *out, const uint8_t* in, u32 offset, unsigned count);
int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode);
void dsi_boot2_crypt_set_ctr(uint32_t size_r);
void dsi_boot2_crypt(uint8_t* out, const uint8_t* in, unsigned count);
#ifdef __cplusplus
}
#endif

86
arm9/src/nand/f_xy.c Normal file
View File

@ -0,0 +1,86 @@
/* f_xy.c
*
* This file was imported from godmode9i, but it is liely not the
* original source. twltool uses the same file.
*
* If you happen to know whom to credit I'd love to add the name
*
* Refactored to reduce the pointer casts and remove the dependency
* from tonccpy.
*/
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include "u128_math.h"
#include "f_xy.h"
/************************ Constants / Defines *********************************/
const uint8_t DSi_KEY_MAGIC[16] = {
0x79, 0x3e, 0x4f, 0x1a, 0x5f, 0x0f, 0x68, 0x2a,
0x58, 0x02, 0x59, 0x29, 0x4e, 0xfb, 0xfe, 0xff
};
const uint8_t DSi_NAND_KEY_Y[16] = {
0x76, 0xdc, 0xb9, 0x0a, 0xd3, 0xc4, 0x4d, 0xbd,
0x1d, 0xdd, 0x2d, 0x20, 0x05, 0x00, 0xa0, 0xe1
};
const uint8_t DSi_ES_KEY_Y_PROD[16] = {
0xe5, 0xcc, 0x5a, 0x8b, 0x56, 0xd0, 0xc9,0x72,
0x9c, 0x17, 0xe8, 0xdc, 0x39, 0x12, 0x36, 0xa9
};
const uint8_t DSi_ES_KEY_Y_DEV[16] = {
0x2D, 0xD4, 0x03, 0x98, 0xA7, 0x6B, 0x03, 0x28,
0xCE, 0x61, 0x04, 0xBB, 0x0A, 0xBB, 0x03, 0x5B,
};
const uint8_t DSi_BOOT2_KEY[16] = {
0x98, 0xee, 0x80, 0x80, 0x00, 0x6c, 0xb4, 0xf6,
0x3a, 0xc2, 0x6e, 0x62, 0xf9, 0xec, 0x34, 0xad
};
/************************ Functions *******************************************/
const uint8_t* getDSiEsKeyY(void)
{
// Check SCFG_OP for dev/prod unit
unsigned char* ptr = (unsigned char*)0x4004024;
char scfg_op = *ptr;
bool devUnit;
devUnit = bit_test(7, scfg_op);
// My cat wanted to say "dfrolfv"
if(!devUnit) {
return DSi_ES_KEY_Y_PROD;
} else {
return DSi_ES_KEY_Y_DEV;
}
}
void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y)
{
uint8_t key_xy[16];
for (int i=0; i<16; i++)
key_xy[i] = key_x[i] ^ key_y[i];
memcpy(key, DSi_KEY_MAGIC, sizeof(DSi_KEY_MAGIC));
u128_add(key, key_xy);
u128_lrot(key, 42);
}
//F_XY_reverse does the reverse of F(X^Y): takes (normal)key, and does F in reverse to generate the original X^Y key_xy.
void F_XY_reverse(const uint8_t *key, uint8_t *key_xy)
{
memcpy(key_xy, key, 16);
u128_rrot(key_xy, 42);
u128_sub(key_xy, DSi_KEY_MAGIC);
}
int bit_test(char bit, char byte)
{
bit = 1 << bit;
return(bit & byte);
}

38
arm9/src/nand/f_xy.h Normal file
View File

@ -0,0 +1,38 @@
/* f_xy.h
*
* This file was imported from godmode9i, but it is liely not the
* original source. twltool uses the same file.
*
* If you happen to know whom to credit I'd love to add the name
*
* Refactored to reduce the pointer casts and remove the dependency
* from tonccpy.
*/
#ifndef _H_F_XY
#define _H_F_XY
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
extern const uint8_t DSi_KEY_MAGIC[16];
extern const uint8_t DSi_NAND_KEY_Y[16];
extern const uint8_t DSi_ES_KEY_Y_PROD[16];
extern const uint8_t DSi_ES_KEY_Y_DEV[16];
extern const uint8_t DSi_BOOT2_KEY[16];
/************************ Function Protoypes **********************************/
const uint8_t* getDSiEsKeyY(void);
void F_XY(uint8_t *key, const uint8_t *key_x, const uint8_t *key_y);
void F_XY_reverse(const uint8_t *key, uint8_t *key_xy);
int bit_test(char bit, char byte);
#ifdef __cplusplus
}
#endif
#endif

258
arm9/src/nand/filesystem.c Normal file
View File

@ -0,0 +1,258 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "nandfirm.h"
#include "sector0.h"
#include "crypto.h"
#include "filesystem.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
#include "../menu.h"
// Master Boot Records
u8 mbrSamsung[68] = {
0x00, 0x00, 0x00, 0x03, 0x18, 0x04, 0x06, 0x0F, 0xE0, 0x3B, 0x77, 0x08,
0x00, 0x00, 0x89, 0x6F, 0x06, 0x00, 0x00, 0x02, 0xCE, 0x3C, 0x06, 0x0F,
0xE0, 0xBE, 0x4D, 0x78, 0x06, 0x00, 0xB3, 0x05, 0x01, 0x00, 0x00, 0x02,
0xDE, 0xBF, 0x01, 0x0F, 0xE0, 0xBF, 0x5D, 0x7E, 0x07, 0x00, 0xA3, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
};
u8 mbrSt[68] = {
0x00, 0x00, 0x00, 0x03, 0x18, 0x04, 0x06, 0x0F, 0xE0, 0x3B, 0x77, 0x08,
0x00, 0x00, 0x89, 0x6F, 0x06, 0x00, 0x00, 0x02, 0xCE, 0x3C, 0x06, 0x0F,
0xE0, 0xBE, 0x4D, 0x78, 0x06, 0x00, 0xB3, 0x05, 0x01, 0x00, 0x00, 0x02,
0xDC, 0xBF, 0x01, 0x0F, 0xE0, 0xD5, 0x5B, 0x7E, 0x07, 0x00, 0xA5, 0x2D,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
};
// Volume Boot Records
u8 vbrMain[54] = {
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x34, 0x00,
0x20, 0x00, 0x10, 0x00, 0x77, 0x08, 0x00, 0x00, 0x89, 0x6F, 0x06, 0x00,
0x00, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};
u8 vbrPhoto[54] = {
0xE9, 0x00, 0x00, 0x54, 0x57, 0x4C, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00,
0x02, 0x20, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0xF8, 0x09, 0x00,
0x20, 0x00, 0x10, 0x00, 0x4D, 0x78, 0x06, 0x00, 0xB3, 0x05, 0x01, 0x00,
0x01, 0x00, 0x29, 0x78, 0x56, 0x34, 0x12, 0x20, 0x20, 0x20, 0x20, 0x20,
0x20, 0x20, 0x20, 0x20, 0x20, 0x20
};
static size_t i;
enum {
READ_MBR,
REPAIR_MBR,
FORMAT_MAIN,
FORMAT_PHOTO,
BACK
};
static int _fsMenu(int cursor)
{
//top screen
clearScreen(cMAIN);
printf("\n\x1B[40mTwlNandTool Ver0.0");
printf("\n\nNAND repair tool by RMC/RVTR");
printf("\n\nMode: FileSystem");
//menu
Menu* m = newMenu();
setMenuHeader(m, "TwlNandTool");
char modeStr[32];
addMenuItem(m, "Read MBR", NULL, 0);
addMenuItem(m, "Repair MBR", NULL, 0);
addMenuItem(m, "Format TWL_MAIN", NULL, 0);
addMenuItem(m, "Format TWL_PHOTO", NULL, 0);
addMenuItem(m, "Back", NULL, 0);
m->cursor = cursor;
//bottom screen
printMenu(m);
while (!programEnd)
{
swiWaitForVBlank();
scanKeys();
if (moveCursor(m))
printMenu(m);
if (keysDown() & KEY_A)
break;
}
int result = m->cursor;
freeMenu(m);
return result;
}
int fsMain(void)
{
int cursor = 0;
while (!programEnd)
{
cursor = _fsMenu(cursor);
switch (cursor)
{
case READ_MBR:
readMbr();
break;
case REPAIR_MBR:
repairMbr();
break;
case FORMAT_MAIN:
formatMain();
break;
case FORMAT_PHOTO:
repairMbr();
break;
case BACK:
programEnd = true;
break;
}
}
programEnd = false;
return 0;
}
int readMbr(void) {
clearScreen(cSUB);
nand_ReadSectors(0, 1, sector_buf);
dsi_crypt_init((const u8*)consoleIDfixed, (const u8*)0x2FFD7BC, is3DS);
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
iprintf("\n>> MBR (Master Boot Record) ");
iprintf("\n--------------------------------");
printf("\n ");
for (i = 444; i < SECTOR_SIZE; i++) {
printf("%02X", sector_buf[i]);
if ((i + 1) % 2 == 0) {
printf(" ");
}
if ((i - 443) % 8 == 0 && i != 444) {
printf("\n ");
}
}
if(parse_mbr(sector_buf, is3DS)) {
iprintf("\n\n \x1B[31mERROR!\x1B[40m MBR is corrupted.");
}
iprintf("\n\n Please Push Select To Return ");
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}
int repairMbr(void) {
clearScreen(cSUB);
iprintf("\n>> Repair Corrupted MBR ");
iprintf("\n--------------------------------");
if(!parse_mbr(sector_buf, is3DS)) {
if (choicePrint("NAND does not look corrupt.\nRepair anyway?") == NO) {
return 0;
}
}
iprintf("\nNAND type : %s (0x%02X)", nandInfo.NAND_PNM, nandInfo.NAND_MID);
memset(sector_buf, 0, 444);
if (nandInfo.NAND_MID == 0x15) {
memcpy(sector_buf + 444, mbrSamsung, sizeof(mbrSamsung));
} else if (nandInfo.NAND_MID == 0xFE) {
memcpy(sector_buf + 444, mbrSt, sizeof(mbrSt));
}
// Write new MBR, encrypt it, then save it.
// Afterwards we read it back from NAND and decrypt to confirm it works.
//
// No need to do safety checks before writing since corrupted MBR is already broken.
iprintf("\nWriting new MBR...");
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
nand_WriteSectors(0, 1, sector_buf);
iprintf("\nTesting new MBR...");
nand_ReadSectors(0, 1, sector_buf);
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
if(parse_mbr(sector_buf, is3DS)) {
iprintf("\n\n \x1B[31mERROR!\x1B[40m Failed to fix MBR.");
}
iprintf("\n\x1B[32mThe new MBR passed!\x1B[40m");
iprintf("\n\n Please Push Select To Return ");
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}
int formatMain(void) {
clearScreen(cSUB);
iprintf("\n>> Write TWL_MAIN VBR ");
iprintf("\n--------------------------------");
memset(file_buf, 0, 0x200);
// Write the first 54 bytes, then pad to 0x1FE. Finally write 0x55AA
memcpy(file_buf, vbrMain, sizeof(vbrMain));
memset(file_buf + sizeof(vbrMain), 0, (BUFFER_SIZE - sizeof(vbrMain) - 2));
memset(file_buf + SECTOR_SIZE - 2, 0x55, 1);
memset(file_buf + SECTOR_SIZE - 1, 0xAA, 1);
good_nandio_write(0x10EE00, 0x200, file_buf, false);
iprintf("\n\nDone! Please confirm VBR is correct.");
iprintf("\n\n Please Push Select To Return ");
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}

View File

@ -0,0 +1,21 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "nandfirm.h"
#include "sector0.h"
#include "crypto.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
#include "../menu.h"
int fsMain(void);
int readMbr(void);
int repairMbr(void);
int formatMain(void);

116
arm9/src/nand/nandfirm.c Normal file
View File

@ -0,0 +1,116 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "nandfirm.h"
#include "sector0.h"
#include "crypto.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
u32 done=0;
size_t i;
void death(char *message, u8 *buffer){
iprintf("\n%s\n", message);
free(buffer);
while(1)swiWaitForVBlank();
}
int nandFirmRead(void) {
clearScreen(cSUB);
iprintf("\n>> NandFirm Version Checker ");
iprintf("\n--------------------------------");
int fail=0;
// Sectors are 0x200 each, this is sector 626 OR offset 0x4E400
// nand_ReadSectors(<start at sector>, <number of sectors to read>, <save sectors to>)
if(nand_ReadSectors(626, 1, sector_buf) == false){
iprintf("Nand read error");
}
printf("\n Ver: ");
for (i = 0; i < 9; i++) {
if (sector_buf[i] == 0x0A) {
printf("-");
} else {
printf("%c", sector_buf[i]);
}
}
printf("\n");
iprintf("\n\n\n Please Push Select To Return ");
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}
int nandPrintInfo(void) {
extern nandData nandInfo;
clearScreen(cSUB);
iprintf("\n>> CID (Card IDentification)");
iprintf("\n Brand : %s", nandInfo.NAND_MID_NAME);
iprintf("\n--------------------------------");
iprintf("\nManufacturer ID : 0x%02X", nandInfo.NAND_MID);
iprintf("\nOEM/Application ID : 0x%02X%02X", nandInfo.NAND_OID[0], nandInfo.NAND_OID[1]);
iprintf("\nProduct name : %s", nandInfo.NAND_PNM);
iprintf("\nProduct revision : %02X", nandInfo.NAND_PRV);
iprintf("\nProduct S/N : %02X%02X%02X%02X", nandInfo.NAND_PSN[0], nandInfo.NAND_PSN[1], nandInfo.NAND_PSN[2], nandInfo.NAND_PSN[3]);
iprintf("\nManufacturing date : %02X(%d 20%d)",nandInfo.NAND_MDT, nandInfo.NAND_MDT_MONTH, nandInfo.NAND_MDT_YEAR);
printf("\n\n ");
for (i = 16; i > 0;) {
i--;
if ((i + 1) % 2 == 0) {
printf(" ");
}
printf("%02X", CID[i]);
if (i == 8) {
printf("\n ");
}
}
iprintf("\n\n\n Please Push Select To Return ");
while (true)
{
swiWaitForVBlank();
scanKeys();
if (keysDown() & KEY_SELECT )
break;
}
}
/*
I'd actually like to make a good RAW NAND R/W routine.
nandRead(byteAddress, amount, infile)
I want to find the sector (626) and sector offset (80) for the hex address below:
Offset: 320592
Sector size: 512
byteOffset = byteAddress % sectorSize and sector = byteAddress / sectorSize
*/

19
arm9/src/nand/nandfirm.h Normal file
View File

@ -0,0 +1,19 @@
#include <nds.h>
#include <fat.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <nds/arm9/nand.h>
#include "f_xy.h"
#include "twltool/dsi.h"
#include "nandio.h"
#include "sector0.h"
#include "crypto.h"
#include "../message.h"
#include "../main.h"
#include "../video.h"
void wait(int ticks);
void death(char *message, u8 *buffer);
int nandFirmRead(void);
int nandPrintInfo(void);

364
arm9/src/nand/nandio.c Normal file
View File

@ -0,0 +1,364 @@
#include <nds.h>
#include <nds/disc_io.h>
#include <malloc.h>
#include <stdio.h>
#include "crypto.h"
#include "sector0.h"
#include "f_xy.h"
#include "nandio.h"
#include "u128_math.h"
/************************ Function Protoypes **********************************/
bool nandio_startup();
bool nandio_is_inserted();
bool nandio_read_sectors(sec_t offset, sec_t len, void *buffer);
bool nandio_write_sectors(sec_t offset, sec_t len, const void *buffer);
bool nandio_clear_status();
bool nandio_shutdown();
/************************ Constants / Defines *********************************/
u8 consoleID[8];
u8 CID[16];
u8 consoleIDfixed[8];
nandData nandInfo = {0};
const DISC_INTERFACE io_dsi_nand = {
NAND_DEVICENAME,
FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE,
nandio_startup,
nandio_is_inserted,
nandio_read_sectors,
nandio_write_sectors,
nandio_clear_status,
nandio_shutdown
};
bool is3DS;
static bool writingLocked = true;
static bool nandWritten = false;
extern bool nand_Startup();
static u8* crypt_buf = 0;
static u32 fat_sig_fix_offset = 0;
static u32 sector_buf32[SECTOR_SIZE/sizeof(u32)];
extern u8 *sector_buf = (u8*)sector_buf32;
static u32 file_buf32[BUFFER_SIZE/sizeof(u32)];
extern u8 *file_buf = (u8*)file_buf32;
void nandio_set_fat_sig_fix(u32 offset)
{
fat_sig_fix_offset = offset;
}
void getCID(u8 *CID){
memcpy(CID,(u8*)0x02FFD7BC,16); //arm9 location
}
void getConsoleID(u8 *consoleID)
{
u8 *fifo=(u8*)0x02300000; //shared mem address that has our computed key3 stuff
u8 key[16]; //key3 normalkey - keyslot 3 is used for DSi/twln NAND crypto
u8 key_x[16];////key3_x - contains a DSi console id (which just happens to be the LFCS on 3ds)
u8 empty_buff[8] = {0};
memcpy(key, fifo, 16); //receive the goods from arm7
if(memcmp(key + 8, empty_buff, 8) == 0)
{
//we got the consoleid directly or nothing at all, don't treat this as key3 output
memcpy(consoleID, key, 8);
return;
}
F_XY_reverse(key, key_x); //work backwards from the normalkey to get key_x that has the consoleID
u128_xor(key_x, DSi_NAND_KEY_Y);
memcpy(&consoleID[0], &key_x[0], 4);
memcpy(&consoleID[4], &key_x[0xC], 4);
}
void nandGetInfo(void) {
// Copy over all NAND data from the CID
nandInfo.NAND_MID = CID[14];
strcpy(nandInfo.NAND_MID_NAME, "UNKNOWN");
if (nandInfo.NAND_MID == 0x15) {
strcpy(nandInfo.NAND_MID_NAME, "SAMSUNG");
} else if (nandInfo.NAND_MID == 0xFE) {
strcpy(nandInfo.NAND_MID_NAME, "ST");
}
nandInfo.NAND_OID[0] = CID[13];
nandInfo.NAND_OID[1] = CID[12];
nandInfo.NAND_PNM[0] = CID[11];
nandInfo.NAND_PNM[1] = CID[10];
nandInfo.NAND_PNM[2] = CID[9];
nandInfo.NAND_PNM[3] = CID[8];
nandInfo.NAND_PNM[4] = CID[7];
nandInfo.NAND_PNM[5] = CID[6];
nandInfo.NAND_PNM[6] = '\0';
nandInfo.NAND_PRV = CID[5];
memcpy(nandInfo.NAND_PSN, &CID[4], 4);
nandInfo.NAND_MDT = CID[0];
nandInfo.NAND_MDT_MONTH = (CID[0] & 0xF0) >> 4;
nandInfo.NAND_MDT_YEAR = (CID[0] & 0x0F) - 3;
return;
}
bool nandio_startup()
{
if (!nand_Startup())
{
return false;
}
nand_ReadSectors(0, 1, sector_buf);
is3DS = parse_ncsd(sector_buf) == 0;
//if (is3DS) return false;
// Get ConsoleID
getConsoleID(consoleID);
getCID(CID);
for (int i = 0; i < 8; i++)
{
consoleIDfixed[i] = consoleID[7-i];
}
nandGetInfo();
// iprintf("sector 0 is %s\n", is3DS ? "3DS" : "DSi");
dsi_crypt_init((const u8*)consoleIDfixed, (const u8*)0x2FFD7BC, is3DS);
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
parse_mbr(sector_buf, is3DS);
mbr_t *mbr = (mbr_t*)sector_buf;
nandio_set_fat_sig_fix(is3DS ? 0 : mbr->partitions[0].offset);
if (crypt_buf == 0)
{
crypt_buf = (u8*)memalign(32, SECTOR_SIZE * CRYPT_BUF_LEN);
}
return crypt_buf != 0;
}
bool nandio_is_inserted()
{
return true;
}
// len is guaranteed <= CRYPT_BUF_LEN
static bool read_sectors(sec_t start, sec_t len, void *buffer)
{
if (nand_ReadSectors(start, len, crypt_buf))
{
dsi_nand_crypt(buffer, crypt_buf, start * SECTOR_SIZE / AES_BLOCK_SIZE, len * SECTOR_SIZE / AES_BLOCK_SIZE);
if (fat_sig_fix_offset &&
start == fat_sig_fix_offset
&& ((u8*)buffer)[0x36] == 0
&& ((u8*)buffer)[0x37] == 0
&& ((u8*)buffer)[0x38] == 0)
{
((u8*)buffer)[0x36] = 'F';
((u8*)buffer)[0x37] = 'A';
((u8*)buffer)[0x38] = 'T';
}
return true;
}
else
{
return false;
}
}
// len is guaranteed <= CRYPT_BUF_LEN
static bool write_sectors(sec_t start, sec_t len, const void *buffer)
{
static u8 writeCopy[SECTOR_SIZE*16];
memcpy(writeCopy, buffer, len * SECTOR_SIZE);
dsi_nand_crypt(crypt_buf, writeCopy, start * SECTOR_SIZE / AES_BLOCK_SIZE, len * SECTOR_SIZE / AES_BLOCK_SIZE);
if (nand_WriteSectors(start, len, crypt_buf))
{
return true;
}
else
{
return false;
}
}
bool nandio_read_sectors(sec_t offset, sec_t len, void *buffer)
{
while (len >= CRYPT_BUF_LEN)
{
if (!read_sectors(offset, CRYPT_BUF_LEN, buffer))
{
return false;
}
offset += CRYPT_BUF_LEN;
len -= CRYPT_BUF_LEN;
buffer = ((u8*)buffer) + SECTOR_SIZE * CRYPT_BUF_LEN;
}
if (len > 0)
{
return read_sectors(offset, len, buffer);
} else
{
return true;
}
}
bool nandio_write_sectors(sec_t offset, sec_t len, const void *buffer)
{
if (writingLocked)
return false;
nandWritten = true;
while (len >= CRYPT_BUF_LEN)
{
if (!write_sectors(offset, CRYPT_BUF_LEN, buffer))
{
return false;
}
offset += CRYPT_BUF_LEN;
len -= CRYPT_BUF_LEN;
buffer = ((u8*)buffer) + SECTOR_SIZE * CRYPT_BUF_LEN;
}
if (len > 0)
{
return write_sectors(offset, len, buffer);
} else
{
return true;
}
}
bool good_nandio_write(int inputAddress, int inputLength, u8 *buffer, bool crypt) {
// Sorry lol I just don't want to deal with sector calculation
int byteOffset = inputAddress % SECTOR_SIZE;
int sectorNum = inputAddress / SECTOR_SIZE;
int byteEndOffset = (inputAddress+inputLength) % SECTOR_SIZE;
int sectorEndNum = (inputAddress+inputLength) / SECTOR_SIZE;
int i;
if (inputLength <= 0x200) {
// Handle a single sector write differently since it is unpredictable
nand_ReadSectors(sectorNum, 1, sector_buf);
memcpy(sector_buf, buffer, inputLength);
if (crypt == true) {
dsi_nand_crypt(sector_buf, sector_buf, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
}
nand_WriteSectors(sectorNum, 1, sector_buf);
//iprintf("\n%02X to %02X of %02X", byteOffset, inputLength, sectorNum);
//iprintf("\n%02X to %02X of buffer", 0, inputLength);
} else {
for (i = sectorNum; i < sectorEndNum + 1;) {
// Back up sector
nand_ReadSectors(i, 1, sector_buf);
if (i == sectorNum) {
// Handle the first sector differently since we'll only be writing a partial amount of data
memcpy(sector_buf + byteOffset, buffer, SECTOR_SIZE - byteOffset);
//iprintf("\n%02X to %02X of %02X", byteOffset, (SECTOR_SIZE), i);
//iprintf("\n0 to %02X of buffer", (SECTOR_SIZE - byteOffset), i);
} else if (i == sectorEndNum) {
// Handle the last sector differently since we'll only be writing a partial amount of data
memcpy(sector_buf, buffer + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), byteEndOffset);
//iprintf("\n0 to %02X of %02X (end)", byteEndOffset, i);
//iprintf("\n%02X to %02X of buffer", ((i - sectorNum) * SECTOR_SIZE) - byteOffset, (((i - sectorNum) * SECTOR_SIZE) + byteEndOffset) - byteOffset);
} else {
// Handle the middle sectors the same because they'll always be the full sector
memcpy(sector_buf, buffer + (((i - sectorNum) * SECTOR_SIZE) - byteOffset), SECTOR_SIZE);
//iprintf("\n0 to %02X of %02X", SECTOR_SIZE, i);
//iprintf("\n%02X to %02X of buffer", ((i - sectorNum) * SECTOR_SIZE) - byteOffset, (((i - sectorNum) * SECTOR_SIZE) - byteOffset) + SECTOR_SIZE);
}
// I need to do a cmp here
// Write sector
if (crypt == true) {
dsi_nand_crypt(buffer, buffer, 0, SECTOR_SIZE / AES_BLOCK_SIZE);
}
nand_WriteSectors(i, 1, sector_buf);
i++;
}
}
return true;
}
bool nandio_clear_status()
{
return true;
}
bool nandio_shutdown()
{
if (nandWritten)
{
// at cleanup we synchronize the FAT statgings
// A FatFS might have multiple copies of the FAT.
// we will get them back synchonized as we just worked on the first copy
// this allows us to revert changes in the FAT if we did not properly finish
// and did not push the changes to the other copies
// to do this we read the first partition sector
nandio_read_sectors(fat_sig_fix_offset, 1, sector_buf);
u8 stagingLevels = sector_buf[0x10];
u8 reservedSectors = sector_buf[0x0E];
u16 sectorsPerFatCopy = sector_buf[0x16] | ((u16)sector_buf[0x17] << 8);
/*
iprintf("[i] Staging for %i FAT copies\n",stagingLevels);
iprintf("[i] Stages starting at %i\n",reservedSectors);
iprintf("[i] %i sectors per stage\n",sectorsPerFatCopy);
*/
if (stagingLevels > 1)
{
for (u32 sector = 0;sector < sectorsPerFatCopy; sector++)
{
// read fat sector
nandio_read_sectors(fat_sig_fix_offset + reservedSectors + sector, 1, sector_buf);
// write to each copy, except the source copy
writingLocked = false;
for (int stage = 1;stage < stagingLevels;stage++)
{
nandio_write_sectors(fat_sig_fix_offset + reservedSectors + sector + (stage *sectorsPerFatCopy), 1, sector_buf);
}
writingLocked = true;
}
}
nandWritten = false;
}
free(crypt_buf);
crypt_buf = 0;
return true;
}
bool nandio_lock_writing()
{
writingLocked = true;
return writingLocked;
}
bool nandio_unlock_writing()
{
writingLocked = false;
return !writingLocked;
}
bool nandio_force_fat_fix()
{
if (!writingLocked)
nandWritten = true;
return true;
}

59
arm9/src/nand/nandio.h Normal file
View File

@ -0,0 +1,59 @@
#pragma once
#include <stdint.h>
#include <nds/disc_io.h>
#include "sector0.h"
#ifdef __cplusplus
extern "C" {
#endif
/************************ Constants / Defines *********************************/
#define CRYPT_BUF_LEN 64
#define NAND_DEVICENAME (('N' << 24) | ('A' << 16) | ('N' << 8) | 'D')
extern const DISC_INTERFACE io_dsi_nand;
/************************ Function Protoypes **********************************/
void nandio_set_fat_sig_fix(uint32_t offset);
void getCID(u8 *CID);
void getConsoleID(uint8_t *consoleID);
void nandGetInfo(void);
typedef struct {
uint8_t NAND_MID;
char NAND_MID_NAME[10];
uint8_t NAND_OID[2];
uint8_t NAND_PNM[7];
uint8_t NAND_PRV;
uint8_t NAND_PSN[4];
uint8_t NAND_MDT;
uint8_t NAND_MDT_MONTH;
uint8_t NAND_MDT_YEAR;
} nandData;
extern u8 *sector_buf;
extern u8 *file_buf;
extern bool is3DS;
extern nandData nandInfo;
extern u8 consoleID[8];
extern u8 CID[16];
extern u8 consoleIDfixed[8];
extern bool good_nandio_write(int inputAddress, int inputLength, u8 *buffer, bool crypt);
extern bool nandio_startup();
extern bool nandio_shutdown();
extern bool nandio_lock_writing();
extern bool nandio_unlock_writing();
extern bool nandio_force_fat_fix();
#ifdef __cplusplus
}
#endif

1164
arm9/src/nand/polarssl/aes.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
/**
* \file aes.h
*
* Copyright (C) 2006-2010, Brainspark B.V.
*
* This file is part of PolarSSL (http://www.polarssl.org)
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef POLARSSL_AES_H
#define POLARSSL_AES_H
#define AES_ENCRYPT 1
#define AES_DECRYPT 0
#define POLARSSL_ERR_AES_INVALID_KEY_LENGTH -0x0800
#define POLARSSL_ERR_AES_INVALID_INPUT_LENGTH -0x0810
/**
* \brief AES context structure
*/
typedef struct
{
int nr; /*!< number of rounds */
unsigned long *rk; /*!< AES round keys */
unsigned long buf[68]; /*!< unaligned data */
}
aes_context;
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief AES key schedule (encryption)
*
* \param ctx AES context to be initialized
* \param key encryption key
* \param keysize must be 128, 192 or 256
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_enc( aes_context *ctx, const unsigned char *key, int keysize );
/**
* \brief AES key schedule (decryption)
*
* \param ctx AES context to be initialized
* \param key decryption key
* \param keysize must be 128, 192 or 256
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_KEY_LENGTH
*/
int aes_setkey_dec( aes_context *ctx, const unsigned char *key, int keysize );
/**
* \brief AES-ECB block encryption/decryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if successful
*/
int aes_crypt_ecb( aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] );
/**
* \brief AES-CBC buffer encryption/decryption
* Length should be a multiple of the block
* size (16 bytes)
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful, or POLARSSL_ERR_AES_INVALID_INPUT_LENGTH
*/
int aes_crypt_cbc( aes_context *ctx,
int mode,
int length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
/**
* \brief AES-CFB128 buffer encryption/decryption.
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv_off offset in IV (updated after use)
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if successful
*/
int aes_crypt_cfb128( aes_context *ctx,
int mode,
int length,
int *iv_off,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int aes_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* aes.h */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,761 @@
/**
* \file bignum.h
*
* \brief Multi-precision integer library
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
#ifndef MBEDTLS_BIGNUM_H
#define MBEDTLS_BIGNUM_H
#if !defined(MBEDTLS_CONFIG_FILE)
#include "config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#include <stddef.h>
#include <stdint.h>
#if defined(MBEDTLS_FS_IO)
#include <stdio.h>
#endif
#define MBEDTLS_ERR_MPI_FILE_IO_ERROR -0x0002 /**< An error occurred while reading from or writing to a file. */
#define MBEDTLS_ERR_MPI_BAD_INPUT_DATA -0x0004 /**< Bad input parameters to function. */
#define MBEDTLS_ERR_MPI_INVALID_CHARACTER -0x0006 /**< There is an invalid character in the digit string. */
#define MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL -0x0008 /**< The buffer is too small to write to. */
#define MBEDTLS_ERR_MPI_NEGATIVE_VALUE -0x000A /**< The input arguments are negative or result in illegal output. */
#define MBEDTLS_ERR_MPI_DIVISION_BY_ZERO -0x000C /**< The input argument for division is zero, which is not allowed. */
#define MBEDTLS_ERR_MPI_NOT_ACCEPTABLE -0x000E /**< The input arguments are not acceptable. */
#define MBEDTLS_ERR_MPI_ALLOC_FAILED -0x0010 /**< Memory allocation failed. */
#define MBEDTLS_MPI_CHK(f) do { if( ( ret = f ) != 0 ) goto cleanup; } while( 0 )
/*
* Maximum size MPIs are allowed to grow to in number of limbs.
*/
#define MBEDTLS_MPI_MAX_LIMBS 10000
#if !defined(MBEDTLS_MPI_WINDOW_SIZE)
/*
* Maximum window size used for modular exponentiation. Default: 6
* Minimum value: 1. Maximum value: 6.
*
* Result is an array of ( 2 << MBEDTLS_MPI_WINDOW_SIZE ) MPIs used
* for the sliding window calculation. (So 64 by default)
*
* Reduction in size, reduces speed.
*/
#define MBEDTLS_MPI_WINDOW_SIZE 6 /**< Maximum windows size used. */
#endif /* !MBEDTLS_MPI_WINDOW_SIZE */
#if !defined(MBEDTLS_MPI_MAX_SIZE)
/*
* Maximum size of MPIs allowed in bits and bytes for user-MPIs.
* ( Default: 512 bytes => 4096 bits, Maximum tested: 2048 bytes => 16384 bits )
*
* Note: Calculations can results temporarily in larger MPIs. So the number
* of limbs required (MBEDTLS_MPI_MAX_LIMBS) is higher.
*/
#define MBEDTLS_MPI_MAX_SIZE 1024 /**< Maximum number of bytes for usable MPIs. */
#endif /* !MBEDTLS_MPI_MAX_SIZE */
#define MBEDTLS_MPI_MAX_BITS ( 8 * MBEDTLS_MPI_MAX_SIZE ) /**< Maximum number of bits for usable MPIs. */
/*
* When reading from files with mbedtls_mpi_read_file() and writing to files with
* mbedtls_mpi_write_file() the buffer should have space
* for a (short) label, the MPI (in the provided radix), the newline
* characters and the '\0'.
*
* By default we assume at least a 10 char label, a minimum radix of 10
* (decimal) and a maximum of 4096 bit numbers (1234 decimal chars).
* Autosized at compile time for at least a 10 char label, a minimum radix
* of 10 (decimal) for a number of MBEDTLS_MPI_MAX_BITS size.
*
* This used to be statically sized to 1250 for a maximum of 4096 bit
* numbers (1234 decimal chars).
*
* Calculate using the formula:
* MBEDTLS_MPI_RW_BUFFER_SIZE = ceil(MBEDTLS_MPI_MAX_BITS / ln(10) * ln(2)) +
* LabelSize + 6
*/
#define MBEDTLS_MPI_MAX_BITS_SCALE100 ( 100 * MBEDTLS_MPI_MAX_BITS )
#define MBEDTLS_LN_2_DIV_LN_10_SCALE100 332
#define MBEDTLS_MPI_RW_BUFFER_SIZE ( ((MBEDTLS_MPI_MAX_BITS_SCALE100 + MBEDTLS_LN_2_DIV_LN_10_SCALE100 - 1) / MBEDTLS_LN_2_DIV_LN_10_SCALE100) + 10 + 6 )
/*
* Define the base integer type, architecture-wise.
*
* 32 or 64-bit integer types can be forced regardless of the underlying
* architecture by defining MBEDTLS_HAVE_INT32 or MBEDTLS_HAVE_INT64
* respectively and undefining MBEDTLS_HAVE_ASM.
*
* Double-width integers (e.g. 128-bit in 64-bit architectures) can be
* disabled by defining MBEDTLS_NO_UDBL_DIVISION.
*/
#if !defined(MBEDTLS_HAVE_INT32)
#if defined(_MSC_VER) && defined(_M_AMD64)
/* Always choose 64-bit when using MSC */
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* !MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#elif defined(__GNUC__) && ( \
defined(__amd64__) || defined(__x86_64__) || \
defined(__ppc64__) || defined(__powerpc64__) || \
defined(__ia64__) || defined(__alpha__) || \
( defined(__sparc__) && defined(__arch64__) ) || \
defined(__s390x__) || defined(__mips64) )
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
/* mbedtls_t_udbl defined as 128-bit unsigned int */
typedef unsigned int mbedtls_t_udbl __attribute__((mode(TI)));
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#elif defined(__ARMCC_VERSION) && defined(__aarch64__)
/*
* __ARMCC_VERSION is defined for both armcc and armclang and
* __aarch64__ is only defined by armclang when compiling 64-bit code
*/
#if !defined(MBEDTLS_HAVE_INT64)
#define MBEDTLS_HAVE_INT64
#endif /* !MBEDTLS_HAVE_INT64 */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
/* mbedtls_t_udbl defined as 128-bit unsigned int */
typedef __uint128_t mbedtls_t_udbl;
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#elif defined(MBEDTLS_HAVE_INT64)
/* Force 64-bit integers with unknown compiler */
typedef int64_t mbedtls_mpi_sint;
typedef uint64_t mbedtls_mpi_uint;
#endif
#endif /* !MBEDTLS_HAVE_INT32 */
#if !defined(MBEDTLS_HAVE_INT64)
/* Default to 32-bit compilation */
#if !defined(MBEDTLS_HAVE_INT32)
#define MBEDTLS_HAVE_INT32
#endif /* !MBEDTLS_HAVE_INT32 */
typedef int32_t mbedtls_mpi_sint;
typedef uint32_t mbedtls_mpi_uint;
#if !defined(MBEDTLS_NO_UDBL_DIVISION)
typedef uint64_t mbedtls_t_udbl;
#define MBEDTLS_HAVE_UDBL
#endif /* !MBEDTLS_NO_UDBL_DIVISION */
#endif /* !MBEDTLS_HAVE_INT64 */
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief MPI structure
*/
typedef struct
{
int s; /*!< integer sign */
size_t n; /*!< total # of limbs */
mbedtls_mpi_uint *p; /*!< pointer to limbs */
}
mbedtls_mpi;
/**
* \brief Initialize one MPI (make internal references valid)
* This just makes it ready to be set or freed,
* but does not define a value for the MPI.
*
* \param X One MPI to initialize.
*/
void mbedtls_mpi_init( mbedtls_mpi *X );
/**
* \brief Unallocate one MPI
*
* \param X One MPI to unallocate.
*/
void mbedtls_mpi_free( mbedtls_mpi *X );
/**
* \brief Enlarge to the specified number of limbs
*
* \param X MPI to grow
* \param nblimbs The target number of limbs
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_grow( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Resize down, keeping at least the specified number of limbs
*
* \param X MPI to shrink
* \param nblimbs The minimum number of limbs to keep
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shrink( mbedtls_mpi *X, size_t nblimbs );
/**
* \brief Copy the contents of Y into X
*
* \param X Destination MPI
* \param Y Source MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_copy( mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Swap the contents of X and Y
*
* \param X First MPI value
* \param Y Second MPI value
*/
void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y );
/**
* \brief Safe conditional assignement X = Y if assign is 1
*
* \param X MPI to conditionally assign to
* \param Y Value to be assigned
* \param assign 1: perform the assignment, 0: keep X's original value
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
*
* \note This function is equivalent to
* if( assign ) mbedtls_mpi_copy( X, Y );
* except that it avoids leaking any information about whether
* the assignment was done or not (the above code may leak
* information through branch prediction and/or memory access
* patterns analysis).
*/
int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign );
/**
* \brief Safe conditional swap X <-> Y if swap is 1
*
* \param X First mbedtls_mpi value
* \param Y Second mbedtls_mpi value
* \param assign 1: perform the swap, 0: keep X and Y's original values
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
*
* \note This function is equivalent to
* if( assign ) mbedtls_mpi_swap( X, Y );
* except that it avoids leaking any information about whether
* the assignment was done or not (the above code may leak
* information through branch prediction and/or memory access
* patterns analysis).
*/
int mbedtls_mpi_safe_cond_swap( mbedtls_mpi *X, mbedtls_mpi *Y, unsigned char assign );
/**
* \brief Set value from integer
*
* \param X MPI to set
* \param z Value to use
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_lset( mbedtls_mpi *X, mbedtls_mpi_sint z );
/**
* \brief Get a specific bit from X
*
* \param X MPI to use
* \param pos Zero-based index of the bit in X
*
* \return Either a 0 or a 1
*/
int mbedtls_mpi_get_bit( const mbedtls_mpi *X, size_t pos );
/**
* \brief Set a bit of X to a specific value of 0 or 1
*
* \note Will grow X if necessary to set a bit to 1 in a not yet
* existing limb. Will not grow if bit should be set to 0
*
* \param X MPI to use
* \param pos Zero-based index of the bit in X
* \param val The value to set the bit to (0 or 1)
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if val is not 0 or 1
*/
int mbedtls_mpi_set_bit( mbedtls_mpi *X, size_t pos, unsigned char val );
/**
* \brief Return the number of zero-bits before the least significant
* '1' bit
*
* Note: Thus also the zero-based index of the least significant '1' bit
*
* \param X MPI to use
*/
size_t mbedtls_mpi_lsb( const mbedtls_mpi *X );
/**
* \brief Return the number of bits up to and including the most
* significant '1' bit'
*
* Note: Thus also the one-based index of the most significant '1' bit
*
* \param X MPI to use
*/
size_t mbedtls_mpi_bitlen( const mbedtls_mpi *X );
/**
* \brief Return the total size in bytes
*
* \param X MPI to use
*/
size_t mbedtls_mpi_size( const mbedtls_mpi *X );
/**
* \brief Import from an ASCII string
*
* \param X Destination MPI
* \param radix Input numeric base
* \param s Null-terminated string buffer
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code
*/
int mbedtls_mpi_read_string( mbedtls_mpi *X, int radix, const char *s );
/**
* \brief Export into an ASCII string
*
* \param X Source MPI
* \param radix Output numeric base
* \param buf Buffer to write the string to
* \param buflen Length of buf
* \param olen Length of the string written, including final NUL byte
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code.
* *olen is always updated to reflect the amount
* of data that has (or would have) been written.
*
* \note Call this function with buflen = 0 to obtain the
* minimum required buffer size in *olen.
*/
int mbedtls_mpi_write_string( const mbedtls_mpi *X, int radix,
char *buf, size_t buflen, size_t *olen );
#if defined(MBEDTLS_FS_IO)
/**
* \brief Read MPI from a line in an opened file
*
* \param X Destination MPI
* \param radix Input numeric base
* \param fin Input file handle
*
* \return 0 if successful, MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if
* the file read buffer is too small or a
* MBEDTLS_ERR_MPI_XXX error code
*
* \note On success, this function advances the file stream
* to the end of the current line or to EOF.
*
* The function returns 0 on an empty line.
*
* Leading whitespaces are ignored, as is a
* '0x' prefix for radix 16.
*
*/
int mbedtls_mpi_read_file( mbedtls_mpi *X, int radix, FILE *fin );
/**
* \brief Write X into an opened file, or stdout if fout is NULL
*
* \param p Prefix, can be NULL
* \param X Source MPI
* \param radix Output numeric base
* \param fout Output file handle (can be NULL)
*
* \return 0 if successful, or a MBEDTLS_ERR_MPI_XXX error code
*
* \note Set fout == NULL to print X on the console.
*/
int mbedtls_mpi_write_file( const char *p, const mbedtls_mpi *X, int radix, FILE *fout );
#endif /* MBEDTLS_FS_IO */
/**
* \brief Import X from unsigned binary data, big endian
*
* \param X Destination MPI
* \param buf Input buffer
* \param buflen Input buffer size
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_read_binary( mbedtls_mpi *X, const unsigned char *buf, size_t buflen );
/**
* \brief Export X into unsigned binary data, big endian.
* Always fills the whole buffer, which will start with zeros
* if the number is smaller.
*
* \param X Source MPI
* \param buf Output buffer
* \param buflen Output buffer size
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL if buf isn't large enough
*/
int mbedtls_mpi_write_binary( const mbedtls_mpi *X, unsigned char *buf, size_t buflen );
/**
* \brief Left-shift: X <<= count
*
* \param X MPI to shift
* \param count Amount to shift
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shift_l( mbedtls_mpi *X, size_t count );
/**
* \brief Right-shift: X >>= count
*
* \param X MPI to shift
* \param count Amount to shift
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_shift_r( mbedtls_mpi *X, size_t count );
/**
* \brief Compare unsigned values
*
* \param X Left-hand MPI
* \param Y Right-hand MPI
*
* \return 1 if |X| is greater than |Y|,
* -1 if |X| is lesser than |Y| or
* 0 if |X| is equal to |Y|
*/
int mbedtls_mpi_cmp_abs( const mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Compare signed values
*
* \param X Left-hand MPI
* \param Y Right-hand MPI
*
* \return 1 if X is greater than Y,
* -1 if X is lesser than Y or
* 0 if X is equal to Y
*/
int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y );
/**
* \brief Compare signed values
*
* \param X Left-hand MPI
* \param z The integer value to compare to
*
* \return 1 if X is greater than z,
* -1 if X is lesser than z or
* 0 if X is equal to z
*/
int mbedtls_mpi_cmp_int( const mbedtls_mpi *X, mbedtls_mpi_sint z );
/**
* \brief Unsigned addition: X = |A| + |B|
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Unsigned subtraction: X = |A| - |B|
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B is greater than A
*/
int mbedtls_mpi_sub_abs( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed addition: X = A + B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed subtraction: X = A - B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_sub_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Signed addition: X = A + b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The integer value to add
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_add_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Signed subtraction: X = A - b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The integer value to subtract
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_sub_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Baseline multiplication: X = A * B
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_mul_mpi( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Baseline multiplication: X = A * b
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param b The unsigned integer value to multiply with
*
* \note b is unsigned
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_mul_int( mbedtls_mpi *X, const mbedtls_mpi *A, mbedtls_mpi_uint b );
/**
* \brief Division by mbedtls_mpi: A = Q * B + R
*
* \param Q Destination MPI for the quotient
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0
*
* \note Either Q or R can be NULL.
*/
int mbedtls_mpi_div_mpi( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Division by int: A = Q * b + R
*
* \param Q Destination MPI for the quotient
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param b Integer to divide by
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0
*
* \note Either Q or R can be NULL.
*/
int mbedtls_mpi_div_int( mbedtls_mpi *Q, mbedtls_mpi *R, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Modulo: R = A mod B
*
* \param R Destination MPI for the rest value
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if B == 0,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if B < 0
*/
int mbedtls_mpi_mod_mpi( mbedtls_mpi *R, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Modulo: r = A mod b
*
* \param r Destination mbedtls_mpi_uint
* \param A Left-hand MPI
* \param b Integer to divide by
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_DIVISION_BY_ZERO if b == 0,
* MBEDTLS_ERR_MPI_NEGATIVE_VALUE if b < 0
*/
int mbedtls_mpi_mod_int( mbedtls_mpi_uint *r, const mbedtls_mpi *A, mbedtls_mpi_sint b );
/**
* \brief Sliding-window exponentiation: X = A^E mod N
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param E Exponent MPI
* \param N Modular MPI
* \param _RR Speed-up MPI used for recalculations
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is negative or even or
* if E is negative
*
* \note _RR is used to avoid re-computing R*R mod N across
* multiple calls, which speeds up things a bit. It can
* be set to NULL if the extra performance is unneeded.
*/
int mbedtls_mpi_exp_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *E, const mbedtls_mpi *N, mbedtls_mpi *_RR );
/**
* \brief Fill an MPI X with size bytes of random
*
* \param X Destination MPI
* \param size Size in bytes
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_fill_random( mbedtls_mpi *X, size_t size,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Greatest common divisor: G = gcd(A, B)
*
* \param G Destination MPI
* \param A Left-hand MPI
* \param B Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed
*/
int mbedtls_mpi_gcd( mbedtls_mpi *G, const mbedtls_mpi *A, const mbedtls_mpi *B );
/**
* \brief Modular inverse: X = A^-1 mod N
*
* \param X Destination MPI
* \param A Left-hand MPI
* \param N Right-hand MPI
*
* \return 0 if successful,
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if N is <= 1,
MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if A has no inverse mod N.
*/
int mbedtls_mpi_inv_mod( mbedtls_mpi *X, const mbedtls_mpi *A, const mbedtls_mpi *N );
/**
* \brief Miller-Rabin primality test
*
* \param X MPI to check
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful (probably prime),
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_NOT_ACCEPTABLE if X is not prime
*/
int mbedtls_mpi_is_prime( const mbedtls_mpi *X,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Prime number generation
*
* \param X Destination MPI
* \param nbits Required size of X in bits
* ( 3 <= nbits <= MBEDTLS_MPI_MAX_BITS )
* \param dh_flag If 1, then (X-1)/2 will be prime too
* \param f_rng RNG function
* \param p_rng RNG parameter
*
* \return 0 if successful (probably prime),
* MBEDTLS_ERR_MPI_ALLOC_FAILED if memory allocation failed,
* MBEDTLS_ERR_MPI_BAD_INPUT_DATA if nbits is < 3
*/
int mbedtls_mpi_gen_prime( mbedtls_mpi *X, size_t nbits, int dh_flag,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng );
/**
* \brief Checkup routine
*
* \return 0 if successful, or 1 if the test failed
*/
int mbedtls_mpi_self_test( int verbose );
#ifdef __cplusplus
}
#endif
#endif /* bignum.h */

View File

@ -0,0 +1,887 @@
/**
* \file bn_mul.h
*
* \brief Multi-precision integer library
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
* Multiply source vector [s] with b, add result
* to destination vector [d] and set carry c.
*
* Currently supports:
*
* . IA-32 (386+) . AMD64 / EM64T
* . IA-32 (SSE2) . Motorola 68000
* . PowerPC, 32-bit . MicroBlaze
* . PowerPC, 64-bit . TriCore
* . SPARC v8 . ARM v3+
* . Alpha . MIPS32
* . C, longlong . C, generic
*/
#ifndef MBEDTLS_BN_MUL_H
#define MBEDTLS_BN_MUL_H
#include "bignum.h"
#if defined(MBEDTLS_HAVE_ASM)
#ifndef asm
#define asm __asm
#endif
/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */
#if defined(__GNUC__) && \
( !defined(__ARMCC_VERSION) || __ARMCC_VERSION >= 6000000 )
#if defined(__i386__)
#define MULADDC_INIT \
asm( \
"movl %%ebx, %0 \n\t" \
"movl %5, %%esi \n\t" \
"movl %6, %%edi \n\t" \
"movl %7, %%ecx \n\t" \
"movl %8, %%ebx \n\t"
#define MULADDC_CORE \
"lodsl \n\t" \
"mull %%ebx \n\t" \
"addl %%ecx, %%eax \n\t" \
"adcl $0, %%edx \n\t" \
"addl (%%edi), %%eax \n\t" \
"adcl $0, %%edx \n\t" \
"movl %%edx, %%ecx \n\t" \
"stosl \n\t"
#if defined(MBEDTLS_HAVE_SSE2)
#define MULADDC_HUIT \
"movd %%ecx, %%mm1 \n\t" \
"movd %%ebx, %%mm0 \n\t" \
"movd (%%edi), %%mm3 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd (%%esi), %%mm2 \n\t" \
"pmuludq %%mm0, %%mm2 \n\t" \
"movd 4(%%esi), %%mm4 \n\t" \
"pmuludq %%mm0, %%mm4 \n\t" \
"movd 8(%%esi), %%mm6 \n\t" \
"pmuludq %%mm0, %%mm6 \n\t" \
"movd 12(%%esi), %%mm7 \n\t" \
"pmuludq %%mm0, %%mm7 \n\t" \
"paddq %%mm2, %%mm1 \n\t" \
"movd 4(%%edi), %%mm3 \n\t" \
"paddq %%mm4, %%mm3 \n\t" \
"movd 8(%%edi), %%mm5 \n\t" \
"paddq %%mm6, %%mm5 \n\t" \
"movd 12(%%edi), %%mm4 \n\t" \
"paddq %%mm4, %%mm7 \n\t" \
"movd %%mm1, (%%edi) \n\t" \
"movd 16(%%esi), %%mm2 \n\t" \
"pmuludq %%mm0, %%mm2 \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd 20(%%esi), %%mm4 \n\t" \
"pmuludq %%mm0, %%mm4 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd 24(%%esi), %%mm6 \n\t" \
"pmuludq %%mm0, %%mm6 \n\t" \
"movd %%mm1, 4(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd 28(%%esi), %%mm3 \n\t" \
"pmuludq %%mm0, %%mm3 \n\t" \
"paddq %%mm5, %%mm1 \n\t" \
"movd 16(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm2 \n\t" \
"movd %%mm1, 8(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm7, %%mm1 \n\t" \
"movd 20(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm4 \n\t" \
"movd %%mm1, 12(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm2, %%mm1 \n\t" \
"movd 24(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm6 \n\t" \
"movd %%mm1, 16(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm4, %%mm1 \n\t" \
"movd 28(%%edi), %%mm5 \n\t" \
"paddq %%mm5, %%mm3 \n\t" \
"movd %%mm1, 20(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm6, %%mm1 \n\t" \
"movd %%mm1, 24(%%edi) \n\t" \
"psrlq $32, %%mm1 \n\t" \
"paddq %%mm3, %%mm1 \n\t" \
"movd %%mm1, 28(%%edi) \n\t" \
"addl $32, %%edi \n\t" \
"addl $32, %%esi \n\t" \
"psrlq $32, %%mm1 \n\t" \
"movd %%mm1, %%ecx \n\t"
#define MULADDC_STOP \
"emms \n\t" \
"movl %4, %%ebx \n\t" \
"movl %%ecx, %1 \n\t" \
"movl %%edi, %2 \n\t" \
"movl %%esi, %3 \n\t" \
: "=m" (t), "=m" (c), "=m" (d), "=m" (s) \
: "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \
: "eax", "ecx", "edx", "esi", "edi" \
);
#else
#define MULADDC_STOP \
"movl %4, %%ebx \n\t" \
"movl %%ecx, %1 \n\t" \
"movl %%edi, %2 \n\t" \
"movl %%esi, %3 \n\t" \
: "=m" (t), "=m" (c), "=m" (d), "=m" (s) \
: "m" (t), "m" (s), "m" (d), "m" (c), "m" (b) \
: "eax", "ecx", "edx", "esi", "edi" \
);
#endif /* SSE2 */
#endif /* i386 */
#if defined(__amd64__) || defined (__x86_64__)
#define MULADDC_INIT \
asm( \
"xorq %%r8, %%r8 \n\t"
#define MULADDC_CORE \
"movq (%%rsi), %%rax \n\t" \
"mulq %%rbx \n\t" \
"addq $8, %%rsi \n\t" \
"addq %%rcx, %%rax \n\t" \
"movq %%r8, %%rcx \n\t" \
"adcq $0, %%rdx \n\t" \
"nop \n\t" \
"addq %%rax, (%%rdi) \n\t" \
"adcq %%rdx, %%rcx \n\t" \
"addq $8, %%rdi \n\t"
#define MULADDC_STOP \
: "+c" (c), "+D" (d), "+S" (s) \
: "b" (b) \
: "rax", "rdx", "r8" \
);
#endif /* AMD64 */
#if defined(__mc68020__) || defined(__mcpu32__)
#define MULADDC_INIT \
asm( \
"movl %3, %%a2 \n\t" \
"movl %4, %%a3 \n\t" \
"movl %5, %%d3 \n\t" \
"movl %6, %%d2 \n\t" \
"moveq #0, %%d0 \n\t"
#define MULADDC_CORE \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"moveq #0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"addxl %%d4, %%d3 \n\t"
#define MULADDC_STOP \
"movl %%d3, %0 \n\t" \
"movl %%a3, %1 \n\t" \
"movl %%a2, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "d0", "d1", "d2", "d3", "d4", "a2", "a3" \
);
#define MULADDC_HUIT \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d4:%%d1 \n\t" \
"addxl %%d3, %%d1 \n\t" \
"addxl %%d0, %%d4 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"movel %%a2@+, %%d1 \n\t" \
"mulul %%d2, %%d3:%%d1 \n\t" \
"addxl %%d4, %%d1 \n\t" \
"addxl %%d0, %%d3 \n\t" \
"addl %%d1, %%a3@+ \n\t" \
"addxl %%d0, %%d3 \n\t"
#endif /* MC68000 */
#if defined(__powerpc64__) || defined(__ppc64__)
#if defined(__MACH__) && defined(__APPLE__)
#define MULADDC_INIT \
asm( \
"ld r3, %3 \n\t" \
"ld r4, %4 \n\t" \
"ld r5, %5 \n\t" \
"ld r6, %6 \n\t" \
"addi r3, r3, -8 \n\t" \
"addi r4, r4, -8 \n\t" \
"addic r5, r5, 0 \n\t"
#define MULADDC_CORE \
"ldu r7, 8(r3) \n\t" \
"mulld r8, r7, r6 \n\t" \
"mulhdu r9, r7, r6 \n\t" \
"adde r8, r8, r5 \n\t" \
"ld r7, 8(r4) \n\t" \
"addze r5, r9 \n\t" \
"addc r8, r8, r7 \n\t" \
"stdu r8, 8(r4) \n\t"
#define MULADDC_STOP \
"addze r5, r5 \n\t" \
"addi r4, r4, 8 \n\t" \
"addi r3, r3, 8 \n\t" \
"std r5, %0 \n\t" \
"std r4, %1 \n\t" \
"std r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#else /* __MACH__ && __APPLE__ */
#define MULADDC_INIT \
asm( \
"ld %%r3, %3 \n\t" \
"ld %%r4, %4 \n\t" \
"ld %%r5, %5 \n\t" \
"ld %%r6, %6 \n\t" \
"addi %%r3, %%r3, -8 \n\t" \
"addi %%r4, %%r4, -8 \n\t" \
"addic %%r5, %%r5, 0 \n\t"
#define MULADDC_CORE \
"ldu %%r7, 8(%%r3) \n\t" \
"mulld %%r8, %%r7, %%r6 \n\t" \
"mulhdu %%r9, %%r7, %%r6 \n\t" \
"adde %%r8, %%r8, %%r5 \n\t" \
"ld %%r7, 8(%%r4) \n\t" \
"addze %%r5, %%r9 \n\t" \
"addc %%r8, %%r8, %%r7 \n\t" \
"stdu %%r8, 8(%%r4) \n\t"
#define MULADDC_STOP \
"addze %%r5, %%r5 \n\t" \
"addi %%r4, %%r4, 8 \n\t" \
"addi %%r3, %%r3, 8 \n\t" \
"std %%r5, %0 \n\t" \
"std %%r4, %1 \n\t" \
"std %%r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#endif /* __MACH__ && __APPLE__ */
#elif defined(__powerpc__) || defined(__ppc__) /* end PPC64/begin PPC32 */
#if defined(__MACH__) && defined(__APPLE__)
#define MULADDC_INIT \
asm( \
"lwz r3, %3 \n\t" \
"lwz r4, %4 \n\t" \
"lwz r5, %5 \n\t" \
"lwz r6, %6 \n\t" \
"addi r3, r3, -4 \n\t" \
"addi r4, r4, -4 \n\t" \
"addic r5, r5, 0 \n\t"
#define MULADDC_CORE \
"lwzu r7, 4(r3) \n\t" \
"mullw r8, r7, r6 \n\t" \
"mulhwu r9, r7, r6 \n\t" \
"adde r8, r8, r5 \n\t" \
"lwz r7, 4(r4) \n\t" \
"addze r5, r9 \n\t" \
"addc r8, r8, r7 \n\t" \
"stwu r8, 4(r4) \n\t"
#define MULADDC_STOP \
"addze r5, r5 \n\t" \
"addi r4, r4, 4 \n\t" \
"addi r3, r3, 4 \n\t" \
"stw r5, %0 \n\t" \
"stw r4, %1 \n\t" \
"stw r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#else /* __MACH__ && __APPLE__ */
#define MULADDC_INIT \
asm( \
"lwz %%r3, %3 \n\t" \
"lwz %%r4, %4 \n\t" \
"lwz %%r5, %5 \n\t" \
"lwz %%r6, %6 \n\t" \
"addi %%r3, %%r3, -4 \n\t" \
"addi %%r4, %%r4, -4 \n\t" \
"addic %%r5, %%r5, 0 \n\t"
#define MULADDC_CORE \
"lwzu %%r7, 4(%%r3) \n\t" \
"mullw %%r8, %%r7, %%r6 \n\t" \
"mulhwu %%r9, %%r7, %%r6 \n\t" \
"adde %%r8, %%r8, %%r5 \n\t" \
"lwz %%r7, 4(%%r4) \n\t" \
"addze %%r5, %%r9 \n\t" \
"addc %%r8, %%r8, %%r7 \n\t" \
"stwu %%r8, 4(%%r4) \n\t"
#define MULADDC_STOP \
"addze %%r5, %%r5 \n\t" \
"addi %%r4, %%r4, 4 \n\t" \
"addi %%r3, %%r3, 4 \n\t" \
"stw %%r5, %0 \n\t" \
"stw %%r4, %1 \n\t" \
"stw %%r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4", "r5", "r6", "r7", "r8", "r9" \
);
#endif /* __MACH__ && __APPLE__ */
#endif /* PPC32 */
/*
* The Sparc(64) assembly is reported to be broken.
* Disable it for now, until we're able to fix it.
*/
#if 0 && defined(__sparc__)
#if defined(__sparc64__)
#define MULADDC_INIT \
asm( \
"ldx %3, %%o0 \n\t" \
"ldx %4, %%o1 \n\t" \
"ld %5, %%o2 \n\t" \
"ld %6, %%o3 \n\t"
#define MULADDC_CORE \
"ld [%%o0], %%o4 \n\t" \
"inc 4, %%o0 \n\t" \
"ld [%%o1], %%o5 \n\t" \
"umul %%o3, %%o4, %%o4 \n\t" \
"addcc %%o4, %%o2, %%o4 \n\t" \
"rd %%y, %%g1 \n\t" \
"addx %%g1, 0, %%g1 \n\t" \
"addcc %%o4, %%o5, %%o4 \n\t" \
"st %%o4, [%%o1] \n\t" \
"addx %%g1, 0, %%o2 \n\t" \
"inc 4, %%o1 \n\t"
#define MULADDC_STOP \
"st %%o2, %0 \n\t" \
"stx %%o1, %1 \n\t" \
"stx %%o0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "g1", "o0", "o1", "o2", "o3", "o4", \
"o5" \
);
#else /* __sparc64__ */
#define MULADDC_INIT \
asm( \
"ld %3, %%o0 \n\t" \
"ld %4, %%o1 \n\t" \
"ld %5, %%o2 \n\t" \
"ld %6, %%o3 \n\t"
#define MULADDC_CORE \
"ld [%%o0], %%o4 \n\t" \
"inc 4, %%o0 \n\t" \
"ld [%%o1], %%o5 \n\t" \
"umul %%o3, %%o4, %%o4 \n\t" \
"addcc %%o4, %%o2, %%o4 \n\t" \
"rd %%y, %%g1 \n\t" \
"addx %%g1, 0, %%g1 \n\t" \
"addcc %%o4, %%o5, %%o4 \n\t" \
"st %%o4, [%%o1] \n\t" \
"addx %%g1, 0, %%o2 \n\t" \
"inc 4, %%o1 \n\t"
#define MULADDC_STOP \
"st %%o2, %0 \n\t" \
"st %%o1, %1 \n\t" \
"st %%o0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "g1", "o0", "o1", "o2", "o3", "o4", \
"o5" \
);
#endif /* __sparc64__ */
#endif /* __sparc__ */
#if defined(__microblaze__) || defined(microblaze)
#define MULADDC_INIT \
asm( \
"lwi r3, %3 \n\t" \
"lwi r4, %4 \n\t" \
"lwi r5, %5 \n\t" \
"lwi r6, %6 \n\t" \
"andi r7, r6, 0xffff \n\t" \
"bsrli r6, r6, 16 \n\t"
#define MULADDC_CORE \
"lhui r8, r3, 0 \n\t" \
"addi r3, r3, 2 \n\t" \
"lhui r9, r3, 0 \n\t" \
"addi r3, r3, 2 \n\t" \
"mul r10, r9, r6 \n\t" \
"mul r11, r8, r7 \n\t" \
"mul r12, r9, r7 \n\t" \
"mul r13, r8, r6 \n\t" \
"bsrli r8, r10, 16 \n\t" \
"bsrli r9, r11, 16 \n\t" \
"add r13, r13, r8 \n\t" \
"add r13, r13, r9 \n\t" \
"bslli r10, r10, 16 \n\t" \
"bslli r11, r11, 16 \n\t" \
"add r12, r12, r10 \n\t" \
"addc r13, r13, r0 \n\t" \
"add r12, r12, r11 \n\t" \
"addc r13, r13, r0 \n\t" \
"lwi r10, r4, 0 \n\t" \
"add r12, r12, r10 \n\t" \
"addc r13, r13, r0 \n\t" \
"add r12, r12, r5 \n\t" \
"addc r5, r13, r0 \n\t" \
"swi r12, r4, 0 \n\t" \
"addi r4, r4, 4 \n\t"
#define MULADDC_STOP \
"swi r5, %0 \n\t" \
"swi r4, %1 \n\t" \
"swi r3, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r3", "r4" "r5", "r6", "r7", "r8", \
"r9", "r10", "r11", "r12", "r13" \
);
#endif /* MicroBlaze */
#if defined(__tricore__)
#define MULADDC_INIT \
asm( \
"ld.a %%a2, %3 \n\t" \
"ld.a %%a3, %4 \n\t" \
"ld.w %%d4, %5 \n\t" \
"ld.w %%d1, %6 \n\t" \
"xor %%d5, %%d5 \n\t"
#define MULADDC_CORE \
"ld.w %%d0, [%%a2+] \n\t" \
"madd.u %%e2, %%e4, %%d0, %%d1 \n\t" \
"ld.w %%d0, [%%a3] \n\t" \
"addx %%d2, %%d2, %%d0 \n\t" \
"addc %%d3, %%d3, 0 \n\t" \
"mov %%d4, %%d3 \n\t" \
"st.w [%%a3+], %%d2 \n\t"
#define MULADDC_STOP \
"st.w %0, %%d4 \n\t" \
"st.a %1, %%a3 \n\t" \
"st.a %2, %%a2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "d0", "d1", "e2", "d4", "a2", "a3" \
);
#endif /* TriCore */
/*
* gcc -O0 by default uses r7 for the frame pointer, so it complains about our
* use of r7 below, unless -fomit-frame-pointer is passed. Unfortunately,
* passing that option is not easy when building with yotta.
*
* On the other hand, -fomit-frame-pointer is implied by any -Ox options with
* x !=0, which we can detect using __OPTIMIZE__ (which is also defined by
* clang and armcc5 under the same conditions).
*
* So, only use the optimized assembly below for optimized build, which avoids
* the build error and is pretty reasonable anyway.
*/
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
#define MULADDC_CANNOT_USE_R7
#endif
#if defined(__arm__) && !defined(MULADDC_CANNOT_USE_R7)
#if defined(__thumb__) && !defined(__thumb2__)
#pragma message "using ARM THUMB MULADDC"
#define MULADDC_INIT \
asm( \
"ldr r0, %3 \n\t" \
"ldr r1, %4 \n\t" \
"ldr r2, %5 \n\t" \
"ldr r3, %6 \n\t" \
"lsr r7, r3, #16 \n\t" \
"mov r9, r7 \n\t" \
"lsl r7, r3, #16 \n\t" \
"lsr r7, r7, #16 \n\t" \
"mov r8, r7 \n\t"
#define MULADDC_CORE \
"ldmia r0!, {r6} \n\t" \
"lsr r7, r6, #16 \n\t" \
"lsl r6, r6, #16 \n\t" \
"lsr r6, r6, #16 \n\t" \
"mov r4, r8 \n\t" \
"mul r4, r6 \n\t" \
"mov r3, r9 \n\t" \
"mul r6, r3 \n\t" \
"mov r5, r9 \n\t" \
"mul r5, r7 \n\t" \
"mov r3, r8 \n\t" \
"mul r7, r3 \n\t" \
"lsr r3, r6, #16 \n\t" \
"add r5, r5, r3 \n\t" \
"lsr r3, r7, #16 \n\t" \
"add r5, r5, r3 \n\t" \
"add r4, r4, r2 \n\t" \
"mov r2, #0 \n\t" \
"adc r5, r2 \n\t" \
"lsl r3, r6, #16 \n\t" \
"add r4, r4, r3 \n\t" \
"adc r5, r2 \n\t" \
"lsl r3, r7, #16 \n\t" \
"add r4, r4, r3 \n\t" \
"adc r5, r2 \n\t" \
"ldr r3, [r1] \n\t" \
"add r4, r4, r3 \n\t" \
"adc r2, r5 \n\t" \
"stmia r1!, {r4} \n\t"
#define MULADDC_STOP \
"str r2, %0 \n\t" \
"str r1, %1 \n\t" \
"str r0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r0", "r1", "r2", "r3", "r4", "r5", \
"r6", "r7", "r8", "r9", "cc" \
);
#else
#define MULADDC_INIT \
asm( \
"ldr r0, %3 \n\t" \
"ldr r1, %4 \n\t" \
"ldr r2, %5 \n\t" \
"ldr r3, %6 \n\t"
#define MULADDC_CORE \
"ldr r4, [r0], #4 \n\t" \
"mov r5, #0 \n\t" \
"ldr r6, [r1] \n\t" \
"umlal r2, r5, r3, r4 \n\t" \
"adds r7, r6, r2 \n\t" \
"adc r2, r5, #0 \n\t" \
"str r7, [r1], #4 \n\t"
#define MULADDC_STOP \
"str r2, %0 \n\t" \
"str r1, %1 \n\t" \
"str r0, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "r0", "r1", "r2", "r3", "r4", "r5", \
"r6", "r7", "cc" \
);
#endif /* Thumb */
#endif /* ARMv3 */
#if defined(__alpha__)
#define MULADDC_INIT \
asm( \
"ldq $1, %3 \n\t" \
"ldq $2, %4 \n\t" \
"ldq $3, %5 \n\t" \
"ldq $4, %6 \n\t"
#define MULADDC_CORE \
"ldq $6, 0($1) \n\t" \
"addq $1, 8, $1 \n\t" \
"mulq $6, $4, $7 \n\t" \
"umulh $6, $4, $6 \n\t" \
"addq $7, $3, $7 \n\t" \
"cmpult $7, $3, $3 \n\t" \
"ldq $5, 0($2) \n\t" \
"addq $7, $5, $7 \n\t" \
"cmpult $7, $5, $5 \n\t" \
"stq $7, 0($2) \n\t" \
"addq $2, 8, $2 \n\t" \
"addq $6, $3, $3 \n\t" \
"addq $5, $3, $3 \n\t"
#define MULADDC_STOP \
"stq $3, %0 \n\t" \
"stq $2, %1 \n\t" \
"stq $1, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "$1", "$2", "$3", "$4", "$5", "$6", "$7" \
);
#endif /* Alpha */
#if defined(__mips__) && !defined(__mips64)
#define MULADDC_INIT \
asm( \
"lw $10, %3 \n\t" \
"lw $11, %4 \n\t" \
"lw $12, %5 \n\t" \
"lw $13, %6 \n\t"
#define MULADDC_CORE \
"lw $14, 0($10) \n\t" \
"multu $13, $14 \n\t" \
"addi $10, $10, 4 \n\t" \
"mflo $14 \n\t" \
"mfhi $9 \n\t" \
"addu $14, $12, $14 \n\t" \
"lw $15, 0($11) \n\t" \
"sltu $12, $14, $12 \n\t" \
"addu $15, $14, $15 \n\t" \
"sltu $14, $15, $14 \n\t" \
"addu $12, $12, $9 \n\t" \
"sw $15, 0($11) \n\t" \
"addu $12, $12, $14 \n\t" \
"addi $11, $11, 4 \n\t"
#define MULADDC_STOP \
"sw $12, %0 \n\t" \
"sw $11, %1 \n\t" \
"sw $10, %2 \n\t" \
: "=m" (c), "=m" (d), "=m" (s) \
: "m" (s), "m" (d), "m" (c), "m" (b) \
: "$9", "$10", "$11", "$12", "$13", "$14", "$15" \
);
#endif /* MIPS */
#endif /* GNUC */
#if (defined(_MSC_VER) && defined(_M_IX86)) || defined(__WATCOMC__)
#define MULADDC_INIT \
__asm mov esi, s \
__asm mov edi, d \
__asm mov ecx, c \
__asm mov ebx, b
#define MULADDC_CORE \
__asm lodsd \
__asm mul ebx \
__asm add eax, ecx \
__asm adc edx, 0 \
__asm add eax, [edi] \
__asm adc edx, 0 \
__asm mov ecx, edx \
__asm stosd
#if defined(MBEDTLS_HAVE_SSE2)
#define EMIT __asm _emit
#define MULADDC_HUIT \
EMIT 0x0F EMIT 0x6E EMIT 0xC9 \
EMIT 0x0F EMIT 0x6E EMIT 0xC3 \
EMIT 0x0F EMIT 0x6E EMIT 0x1F \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x6E EMIT 0x16 \
EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \
EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x04 \
EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \
EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x08 \
EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \
EMIT 0x0F EMIT 0x6E EMIT 0x7E EMIT 0x0C \
EMIT 0x0F EMIT 0xF4 EMIT 0xF8 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCA \
EMIT 0x0F EMIT 0x6E EMIT 0x5F EMIT 0x04 \
EMIT 0x0F EMIT 0xD4 EMIT 0xDC \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x08 \
EMIT 0x0F EMIT 0xD4 EMIT 0xEE \
EMIT 0x0F EMIT 0x6E EMIT 0x67 EMIT 0x0C \
EMIT 0x0F EMIT 0xD4 EMIT 0xFC \
EMIT 0x0F EMIT 0x7E EMIT 0x0F \
EMIT 0x0F EMIT 0x6E EMIT 0x56 EMIT 0x10 \
EMIT 0x0F EMIT 0xF4 EMIT 0xD0 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x6E EMIT 0x66 EMIT 0x14 \
EMIT 0x0F EMIT 0xF4 EMIT 0xE0 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x6E EMIT 0x76 EMIT 0x18 \
EMIT 0x0F EMIT 0xF4 EMIT 0xF0 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x04 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x6E EMIT 0x5E EMIT 0x1C \
EMIT 0x0F EMIT 0xF4 EMIT 0xD8 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCD \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x10 \
EMIT 0x0F EMIT 0xD4 EMIT 0xD5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x08 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCF \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x14 \
EMIT 0x0F EMIT 0xD4 EMIT 0xE5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x0C \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCA \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x18 \
EMIT 0x0F EMIT 0xD4 EMIT 0xF5 \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x10 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCC \
EMIT 0x0F EMIT 0x6E EMIT 0x6F EMIT 0x1C \
EMIT 0x0F EMIT 0xD4 EMIT 0xDD \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x14 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCE \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x18 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0xD4 EMIT 0xCB \
EMIT 0x0F EMIT 0x7E EMIT 0x4F EMIT 0x1C \
EMIT 0x83 EMIT 0xC7 EMIT 0x20 \
EMIT 0x83 EMIT 0xC6 EMIT 0x20 \
EMIT 0x0F EMIT 0x73 EMIT 0xD1 EMIT 0x20 \
EMIT 0x0F EMIT 0x7E EMIT 0xC9
#define MULADDC_STOP \
EMIT 0x0F EMIT 0x77 \
__asm mov c, ecx \
__asm mov d, edi \
__asm mov s, esi \
#else
#define MULADDC_STOP \
__asm mov c, ecx \
__asm mov d, edi \
__asm mov s, esi \
#endif /* SSE2 */
#endif /* MSVC */
#endif /* MBEDTLS_HAVE_ASM */
#if !defined(MULADDC_CORE)
#if defined(MBEDTLS_HAVE_UDBL)
#define MULADDC_INIT \
{ \
mbedtls_t_udbl r; \
mbedtls_mpi_uint r0, r1;
#define MULADDC_CORE \
r = *(s++) * (mbedtls_t_udbl) b; \
r0 = (mbedtls_mpi_uint) r; \
r1 = (mbedtls_mpi_uint)( r >> biL ); \
r0 += c; r1 += (r0 < c); \
r0 += *d; r1 += (r0 < *d); \
c = r1; *(d++) = r0;
#define MULADDC_STOP \
}
#else
#define MULADDC_INIT \
{ \
mbedtls_mpi_uint s0, s1, b0, b1; \
mbedtls_mpi_uint r0, r1, rx, ry; \
b0 = ( b << biH ) >> biH; \
b1 = ( b >> biH );
#define MULADDC_CORE \
s0 = ( *s << biH ) >> biH; \
s1 = ( *s >> biH ); s++; \
rx = s0 * b1; r0 = s0 * b0; \
ry = s1 * b0; r1 = s1 * b1; \
r1 += ( rx >> biH ); \
r1 += ( ry >> biH ); \
rx <<= biH; ry <<= biH; \
r0 += rx; r1 += (r0 < rx); \
r0 += ry; r1 += (r0 < ry); \
r0 += c; r1 += (r0 < c); \
r0 += *d; r1 += (r0 < *d); \
c = r1; *(d++) = r0;
#define MULADDC_STOP \
}
#endif /* C (generic) */
#endif /* C (longlong) */
#endif /* bn_mul.h */

View File

@ -0,0 +1,5 @@
#define MBEDTLS_BIGNUM_C
#define POLARSSL_AES_C
#define MBEDTLS_HAVE_ASM

View File

@ -0,0 +1,98 @@
/**
* \file padlock.h
*
* Copyright (C) 2006-2010, Brainspark B.V.
*
* This file is part of PolarSSL (http://www.polarssl.org)
* Lead Maintainer: Paul Bakker <polarssl_maintainer at polarssl.org>
*
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef POLARSSL_PADLOCK_H
#define POLARSSL_PADLOCK_H
#include "aes.h"
#if defined(POLARSSL_HAVE_ASM) && defined(__GNUC__) && defined(__i386__)
#ifndef POLARSSL_HAVE_X86
#define POLARSSL_HAVE_X86
#endif
#define PADLOCK_RNG 0x000C
#define PADLOCK_ACE 0x00C0
#define PADLOCK_PHE 0x0C00
#define PADLOCK_PMM 0x3000
#define PADLOCK_ALIGN16(x) (unsigned long *) (16 + ((long) x & ~15))
#define POLARSSL_ERR_PADLOCK_DATA_MISALIGNED -0x08E0
#ifdef __cplusplus
extern "C" {
#endif
/**
* \brief PadLock detection routine
*
* \param The feature to detect
*
* \return 1 if CPU has support for the feature, 0 otherwise
*/
int padlock_supports( int feature );
/**
* \brief PadLock AES-ECB block en(de)cryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param input 16-byte input block
* \param output 16-byte output block
*
* \return 0 if success, 1 if operation failed
*/
int padlock_xcryptecb( aes_context *ctx,
int mode,
const unsigned char input[16],
unsigned char output[16] );
/**
* \brief PadLock AES-CBC buffer en(de)cryption
*
* \param ctx AES context
* \param mode AES_ENCRYPT or AES_DECRYPT
* \param length length of the input data
* \param iv initialization vector (updated after use)
* \param input buffer holding the input data
* \param output buffer holding the output data
*
* \return 0 if success, 1 if operation failed
*/
int padlock_xcryptcbc( aes_context *ctx,
int mode,
int length,
unsigned char iv[16],
const unsigned char *input,
unsigned char *output );
#ifdef __cplusplus
}
#endif
#endif /* HAVE_X86 */
#endif /* padlock.h */

107
arm9/src/nand/sector0.c Normal file
View File

@ -0,0 +1,107 @@
/* sector0.c
* This code was imported from https://github.com/DS-Homebrew/GodMode9i
*
* Changes against the source:
* - Documentation added
* - clean up formatting
* - moved magic numbers to defines from sector0.h
* - fixed parse_mbr to return valid even when signature was invalid but
* the bootstrap region was not all zero. (? Why was this code there ?)
* - removed verbose output (must be handled on application level, not
* within the helpers
*/
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "sector0.h"
/************************ Constants / Defines *********************************/
static const mbr_partition_t ptable_DSi[MBR_PARTITIONS] = {
{0u, {3u, 24u, 4u}, 6u, {15u, 224u, 59u}, 0x00000877u, 0x00066f89u},
{0u, {2u, 206u, 60u}, 6u, {15u, 224u, 190u}, 0x0006784u, 0x000105b3u},
{0u, {2u, 222u, 191u}, 1u, {15u, 224u, 191u}, 0x00077e5u, 0x000001a3u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u}
};
static const mbr_partition_t ptable_3DS[MBR_PARTITIONS] = {
{0u, {4u, 24u, 0u}, 6u, {1u, 160u, 63u}, 0x0000009u, 0x00047da9u},
{0u, {4u, 142u, 64u}, 6u, {1u, 160u, 195u}, 0x0004808u, 0x000105b3u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u},
{0u, {0u, 0u, 0u}, 0u, {0u, 0u, 0u}, 0u, 0u}
};
/************************ Functions *******************************************/
/*! \brief Sanity check of the NCSD
*
* The ncsd is checked for
* - the signature magic
* - the partition types
* to ensure a valid 3DS ncsd is present
*
* Return values:
* 0: NCSD is a valid
* -1: the signature/magic is invalid
* -2: at least one unknown partition type was found
*/
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE])
{
const ncsd_header_t * h = (ncsd_header_t *)sector0;
if (NCSD_MAGIC != h->magic)
{
return -1;
}
for (unsigned i = 0; i < NCSD_PARTITIONS; ++i)
{
unsigned fs_type = h->fs_types[i];
if (fs_type == 0)
{
break;
}
switch (fs_type)
{
case 1:
case 3:
case 4:
break;
default:
return -2;
}
}
return 0;
}
/*! \brief Sanity check of the MBR
*
* The master boot record is checked for
* - the signature
* - the partition0 values
* to ensure a valid DSi main partition can be found
*
* Return values:
* 0: MBR is a valid DSi partition
* -1: the signature is invalid
* -2: the first partition does not match expected values
*/
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], const int is3DS)
{
const mbr_t *m = (mbr_t*)sector0;
const mbr_partition_t *ref_ptable; // reference partition table
if ((MBR_SIGNATURE_0 != m->boot_signature[0]) || (MBR_SIGNATURE_1 != m->boot_signature[1]))
{
// if the signature is invalid, the bootsector shall not be used!
return -1;
}
ref_ptable = is3DS?ptable_3DS:ptable_DSi;
// only test the 1st partition now, we've seen variations on the 3rd partition
// and after all we only care about the 1st partition
if (memcmp(ref_ptable, m->partitions, sizeof(mbr_partition_t)))
{
return -2;
}
return 0;
}

165
arm9/src/nand/sector0.h Normal file
View File

@ -0,0 +1,165 @@
/* sector0.h
* This code was imported from https://github.com/DS-Homebrew/GodMode9i
*
* Changes against the source:
* - Documentation added
* - clean up / reorder
* - removed verbose output (must be handled on application level, not
* within the helpers */
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <assert.h>
/************************ Constants / Defines *********************************/
#define SECTOR_SIZE 0x200
#define BUFFER_SIZE 0x1000
#define MBR_PARTITIONS 4
#define MBR_BOOTSTRAP_SIZE (SECTOR_SIZE - (2 + MBR_PARTITIONS * 16))
#define MBR_SIGNATURE_0 0x55
#define MBR_SIGNATURE_1 0xAA
// https://3dbrew.org/wiki/NCSD#NCSD_header
#define NCSD_PARTITIONS 8
#define NCSD_SIGNATURESIZE 0x100
#define NCSD_HEADERSIZE (NCSD_SIGNATURESIZE + 0x060)
#define NCSD_MAGIC 0x4453434e
/************************ Structures / Datatypes ******************************/
#ifdef _MSC_VER
#pragma pack(push, 1)
#define __PACKED
#elif defined __GNUC__
#define __PACKED __attribute__ ((__packed__))
#endif
typedef struct {
uint32_t offset, length;
} __PACKED ncsd_partition_t;
typedef struct {
uint8_t digest[NCSD_SIGNATURESIZE];
} __PACKED ncsd_sginature;
typedef struct {
ncsd_sginature signature;
uint32_t magic, size;
uint64_t media_id;
uint8_t fs_types[NCSD_PARTITIONS], crypt_types[NCSD_PARTITIONS];
ncsd_partition_t partitions[NCSD_PARTITIONS];
} __PACKED ncsd_header_t;
/*
* CHS Sector Address Format
* See https://en.wikipedia.org/wiki/Master_boot_record#PTE
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 1 | 3 | CHS Address of first sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t head;
uint8_t sectorAndCylHigh;
uint8_t cylinderLow;
} __PACKED chs_t;
/*
* Partition table entries
* See https://en.wikipedia.org/wiki/Master_boot_record#PTE
*
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 0 | 1 | Bit 7: Bootable |
* | | | Bit 0..6: Reserved (0) |
* ----------------------------------------------------------
* | 1 | 3 | CHS Address of first sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
* | 4 | 1 | Partition Type |
* ----------------------------------------------------------
* | 5 | 3 | CHS Address of last sector |
* | | | Bit 0..7: Head |
* | | | Bit 8..13: Sector |
* | | | Bit 14..23: Cylinder |
* ----------------------------------------------------------
* | 8 | 4 | LBA Address of first sector |
* ----------------------------------------------------------
* | 12 | 4 | Number of sectors |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t status;
chs_t chs_first;
uint8_t type;
chs_t chs_last;
uint32_t offset;
uint32_t length;
} __PACKED mbr_partition_t;
/*
* Master Boot Record
* https://en.wikipedia.org/wiki/Master_boot_record#Sector_layout
*
* ==========================================================
* | Offset | Field Length | Description |
* ==========================================================
* | 0 | 446 | Boot code |
* ----------------------------------------------------------
* | 446 | 16 | Partition 0 |
* ----------------------------------------------------------
* | 462 | 16 | Partition 1 |
* ----------------------------------------------------------
* | 478 | 16 | Partition 2 |
* ----------------------------------------------------------
* | 494 | 16 | Partition 3 |
* ----------------------------------------------------------
* | 510 | 2 | Signature ( 55 aa ) |
* ----------------------------------------------------------
*/
typedef struct {
uint8_t bootstrap[MBR_BOOTSTRAP_SIZE];
mbr_partition_t partitions[MBR_PARTITIONS];
uint8_t boot_signature[2];
} __PACKED mbr_t;
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef __PACKED
/************************ Function Protoypes **********************************/
int parse_ncsd(const uint8_t sector0[SECTOR_SIZE]);
int parse_mbr(const uint8_t sector0[SECTOR_SIZE], const int is3DS);
/************************ static code verification ****************************/
static_assert(sizeof(ncsd_header_t) == NCSD_HEADERSIZE, "sizeof(ncsd_header_t) should equal 0x160");
static_assert(sizeof(mbr_t) == SECTOR_SIZE, "sizeof(mbr_t) should equal 0x200");
#ifdef __cplusplus
}
#endif

132
arm9/src/nand/ticket0.h Normal file
View File

@ -0,0 +1,132 @@
#pragma once
#include <assert.h>
#include <stdint.h>
#ifdef _MSC_VER
#pragma pack(push, 1)
#define PACKED
#elif ! defined PACKED
#define PACKED __attribute__ ((__packed__))
#endif
#define RSA_2048_LEN (2048/8)
// most, if not all, are big endian
// http://problemkaputt.de/gbatek.htm#dsisdmmcdsiwareticketsandtitlemetadata
// http://dsibrew.org/wiki/Ticket
// http://wiibrew.org/wiki/Ticket
typedef struct {
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
char issuer[0x40];
uint8_t ecdh[0x3c];
uint8_t padding1[3];
uint8_t encrypted_title_key[0x10];
uint8_t unknown0;
uint8_t ticket_id[8];
uint8_t console_id[4];
uint8_t title_id[8];
uint8_t unknown1[2];
uint8_t version[2];
uint8_t permitted_titles_mask[4];
uint8_t permit_mask[4];
uint8_t title_export_allowed;
uint8_t common_key_index;
uint8_t unknown[0x30];
uint8_t content_access_permissions[0x40];
uint8_t padding2[2];
uint8_t time_limits[2 * 8 * sizeof(uint32_t)];
} PACKED ticket_v0_t;
static_assert(sizeof(ticket_v0_t) == 0x2a4, "invalid sizeof(ticket_v0_t)");
// http://dsibrew.org/wiki/Tmd
// http://wiibrew.org/wiki/Title_metadata
typedef struct {
uint8_t sig_type[4];
uint8_t sig[RSA_2048_LEN];
uint8_t padding0[0x3c];
char issuer[0x40];
uint8_t version;
uint8_t ca_crl_version;
uint8_t signer_crl_version;
uint8_t padding1;
uint8_t system_version[8];
uint8_t title_id[8];
uint8_t title_type[4];
uint8_t group_id[2];
uint8_t public_save_size[4];
uint8_t private_save_size[4];
uint8_t padding2[8];
uint8_t parent_control[0x10];
uint8_t padding3[0x1e];
uint8_t access_rights[4];
uint8_t title_version[2];
uint8_t num_content[2];
uint8_t boot_index[2];
uint8_t padding4[2];
} PACKED tmd_header_v0_t;
static_assert(sizeof(tmd_header_v0_t) == 0x1e4, "invalid sizeof(tmd_header_v0_t)");
typedef struct {
uint8_t content_id[4];
uint8_t index[2];
uint8_t type[2];
uint8_t size[8];
uint8_t sha1[20];
} PACKED tmd_content_v0_t;
static_assert(sizeof(tmd_content_v0_t) == 0x24, "invalid sizeof(tmd_contend_v0_t)");
// used in ticket encryption
// http://problemkaputt.de/gbatek.htm#dsiesblockencryption
#define AES_CCM_MAC_LEN 0x10
#define AES_CCM_NONCE_LEN 0x0c
typedef struct {
uint8_t ccm_mac[AES_CCM_MAC_LEN];
union {
struct {
uint8_t fixed_3a;
uint8_t nonce[AES_CCM_NONCE_LEN];
uint8_t len24be[3];
};
struct {
uint8_t padding[AES_CCM_NONCE_LEN];
// defined for convenience, it's still big endian with only 24 effective bits
// read it as 32 bit big endian and discard the highest 8 bits
uint8_t len32be[4];
};
uint8_t encrypted[0x10];
};
} PACKED es_block_footer_t;
static_assert(sizeof(es_block_footer_t) == 0x20, "invalid sizeof(es_block_footer_t)");
// used in cert.sys
// http://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaredevkpandcertsyscertificatefiles
// "DSi SD/MMC Firmware dev.kp and cert.sys Certificate Files"
typedef struct {
uint32_t signature_type;
uint8_t signature[RSA_2048_LEN];
uint8_t padding0[0x3c];
char signature_name[0x40];
uint32_t key_type;
char key_name[0x40];
uint32_t key_flags;
uint8_t rsa_key[RSA_2048_LEN];
uint8_t rsa_exp[4];
uint8_t padding1[0x34];
} PACKED cert_t;
static_assert(sizeof(cert_t) == 0x300, "invalid sizeof(cert_t)");
#ifdef _MSC_VER
#pragma pack(pop)
#endif
#undef PACKED

343
arm9/src/nand/twltool/dsi.c Normal file
View File

@ -0,0 +1,343 @@
#include "dsi.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "u128_math.h"
void dsi_set_key(dsi_context* ctx, const unsigned char key[16])
{
unsigned char keyswap[16];
u128_swap(keyswap, key);
aes_setkey_enc(&ctx->aes, keyswap, 128);
}
void dsi_add_ctr(dsi_context* ctx, unsigned int carry)
{
unsigned int counter[4];
unsigned char *outctr = (unsigned char*)ctx->ctr;
int sum;
signed int i;
for (i = 0; i < 4; i++)
counter[i] = (outctr[i * 4 + 0] << 24) | (outctr[i * 4 + 1] << 16) |
(outctr[i * 4 + 2] << 8) | (outctr[i * 4 + 3] << 0);
for (i = 3; i >= 0; i--)
{
sum = counter[i] + carry;
if (sum < counter[i])
carry = 1;
else
carry = 0;
counter[i] = sum;
}
for (i = 0; i < 4; i++)
{
outctr[i * 4 + 0] = counter[i] >> 24;
outctr[i * 4 + 1] = counter[i] >> 16;
outctr[i * 4 + 2] = counter[i] >> 8;
outctr[i * 4 + 3] = counter[i] >> 0;
}
}
void dsi_set_ctr(dsi_context* ctx, const unsigned char ctr[16])
{
int i;
for (i=0; i<16; i++)
ctx->ctr[i] = ctr[15-i];
}
void dsi_init_ctr(dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[12])
{
dsi_set_key(ctx, key);
dsi_set_ctr(ctx, ctr);
}
void dsi_crypt_ctr(dsi_context* ctx, const void* in, void* out, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i += 0x10)
{
dsi_crypt_ctr_block(ctx, in+i, out+i);
}
}
void dsi_crypt_ctr_block(dsi_context* ctx, const unsigned char input[16], unsigned char output[16])
{
int i;
unsigned char stream[16];
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->ctr, stream);
if (input)
{
for (i=0; i<16; i++)
{
output[i] = stream[15-i] ^ input[i];
}
}
else
{
for (i=0; i<16; i++)
output[i] = stream[15-i];
}
dsi_add_ctr(ctx, 1);
}
void dsi_init_ccm(dsi_context* ctx, unsigned char key[16], unsigned int maclength,
unsigned int payloadlength, unsigned int assoclength, unsigned char nonce[12])
{
int i;
dsi_set_key(ctx, key);
ctx->maclen = maclength;
maclength = (maclength-2)/2;
payloadlength = (payloadlength+15) & ~15;
// CCM B0 block:
// [1-byte flags] [12-byte nonce] [3-byte size]
ctx->mac[0] = (maclength<<3) | 2;
if (assoclength)
ctx->mac[0] |= (1<<6);
for (i=0; i<12; i++)
ctx->mac[1+i] = nonce[11-i];
ctx->mac[13] = payloadlength>>16;
ctx->mac[14] = payloadlength>>8;
ctx->mac[15] = payloadlength>>0;
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
// CCM CTR:
// [1-byte flags] [12-byte nonce] [3-byte ctr]
ctx->ctr[0] = 2;
for (i=0; i<12; i++)
ctx->ctr[1+i] = nonce[11-i];
ctx->ctr[13] = 0;
ctx->ctr[14] = 0;
ctx->ctr[15] = 0;
dsi_crypt_ctr_block(ctx, 0, ctx->S0);
}
void dsi_encrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac)
{
int i;
for (i=0; i<16; i++)
ctx->mac[i] ^= input[15-i];
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
if (mac)
{
for (i=0; i<16; i++)
mac[i] = ctx->mac[15-i] ^ ctx->S0[i];
}
if (output)
dsi_crypt_ctr_block(ctx, input, output);
}
void dsi_decrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac)
{
int i;
if (output)
{
dsi_crypt_ctr_block(ctx, input, output);
for (i=0; i<16; i++)
ctx->mac[i] ^= output[15-i];
}
else
{
for (i=0; i<16; i++)
ctx->mac[i] ^= input[15-i];
}
aes_crypt_ecb(&ctx->aes, AES_ENCRYPT, ctx->mac, ctx->mac);
if (mac)
{
for (i=0; i<16; i++)
mac[i] = ctx->mac[15-i] ^ ctx->S0[i];
}
}
void dsi_decrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac)
{
unsigned char block[16];
unsigned char ctr[16];
while (size > 16)
{
dsi_decrypt_ccm_block(ctx, input, output, mac);
if (input)
input += 16;
if (output)
output += 16;
size -= 16;
}
memcpy(ctr, ctx->ctr, 16);
memset(block, 0, 16);
dsi_crypt_ctr_block(ctx, block, block);
memcpy(ctx->ctr, ctr, 16);
memcpy(block, input, size);
dsi_decrypt_ccm_block(ctx, block, block, mac);
memcpy(output, block, size);
}
void dsi_encrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac)
{
unsigned char block[16];
while (size > 16)
{
dsi_encrypt_ccm_block(ctx, input, output, mac);
if (input)
input += 16;
if (output)
output += 16;
size -= 16;
}
memset(block, 0, 16);
memcpy(block, input, size);
dsi_encrypt_ccm_block(ctx, block, block, mac);
memcpy(output, block, size);
}
void dsi_es_init(dsi_es_context* ctx, unsigned char key[16])
{
memcpy(ctx->key, key, 16);
ctx->randomnonce = 1;
}
void dsi_es_set_nonce(dsi_es_context* ctx, unsigned char nonce[12])
{
memcpy(ctx->nonce, nonce, 12);
ctx->randomnonce = 0;
}
void dsi_es_set_random_nonce(dsi_es_context* ctx)
{
ctx->randomnonce = 1;
}
int dsi_es_decrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size)
{
unsigned char ctr[16];
unsigned char nonce[12];
unsigned char scratchpad[16];
unsigned char chkmac[16];
unsigned char genmac[16];
dsi_context cryptoctx;
unsigned int chksize;
memcpy(chkmac, metablock, 16);
memcpy(ctr, metablock + 16, 16);
ctr[0] = 0;
ctr[13] = 0;
ctr[14] = 0;
ctr[15] = 0;
dsi_init_ctr(&cryptoctx, ctx->key, ctr);
dsi_crypt_ctr_block(&cryptoctx, metablock+16, scratchpad);
chksize = (scratchpad[13]<<16) | (scratchpad[14]<<8) | (scratchpad[15]<<0);
if (scratchpad[0] != 0x3A)
{
return -1;
}
if (chksize != size)
{
return -2;
}
memcpy(nonce, metablock + 17, 12);
dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce);
dsi_decrypt_ccm(&cryptoctx, buffer, buffer, size, genmac);
if (memcmp(genmac, chkmac, 16) != 0)
{
return -3;
}
return 0;
}
void dsi_es_encrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size)
{
int i;
unsigned char nonce[12];
unsigned char mac[16];
unsigned char ctr[16];
unsigned char scratchpad[16];
dsi_context cryptoctx;
if (ctx->randomnonce)
{
srand((unsigned int)time(0));
for (i=0; i<12; i++)
nonce[i] = rand();
}
else
{
memcpy(nonce, ctx->nonce, 12);
}
dsi_init_ccm(&cryptoctx, ctx->key, 16, size, 0, nonce);
dsi_encrypt_ccm(&cryptoctx, buffer, buffer, size, mac);
memset(scratchpad, 0, 16);
scratchpad[0] = 0x3A;
scratchpad[13] = size >> 16;
scratchpad[14] = size >> 8;
scratchpad[15] = size >> 0;
memset(ctr, 0, 16);
memcpy(ctr+1, nonce, 12);
dsi_init_ctr(&cryptoctx, ctx->key, ctr);
dsi_crypt_ctr_block(&cryptoctx, scratchpad, metablock+16);
memcpy(metablock+17, nonce, 12);
memcpy(metablock, mac, 16);
}

View File

@ -0,0 +1,68 @@
#ifndef _DSI_H_
#define _DSI_H_
#include "polarssl/aes.h"
typedef struct
{
unsigned char ctr[16];
unsigned char mac[16];
unsigned char S0[16];
unsigned int maclen;
aes_context aes;
}
dsi_context;
typedef struct
{
unsigned char key[16];
unsigned char nonce[12];
int randomnonce;
} dsi_es_context;
#ifdef __cplusplus
extern "C" {
#endif
void dsi_set_key(dsi_context* ctx, const unsigned char key[16]);
void dsi_add_ctr(dsi_context* ctx, unsigned int carry);
void dsi_set_ctr(dsi_context* ctx, const unsigned char ctr[16]);
void dsi_init_ctr(dsi_context* ctx, const unsigned char key[16], const unsigned char ctr[12]);
void dsi_crypt_ctr(dsi_context* ctx, const void* in, void* out, unsigned int len);
void dsi_crypt_ctr_block(dsi_context* ctx, const unsigned char input[16], unsigned char output[16]);
void dsi_init_ccm(dsi_context* ctx, unsigned char key[16], unsigned int maclength,
unsigned int payloadlength, unsigned int assoclength, unsigned char nonce[12]);
void dsi_encrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac);
void dsi_decrypt_ccm_block(dsi_context* ctx, unsigned char input[16], unsigned char output[16], unsigned char* mac);
void dsi_decrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac);
void dsi_encrypt_ccm(dsi_context* ctx, unsigned char* input, unsigned char* output, unsigned int size, unsigned char* mac);
void dsi_es_init(dsi_es_context* ctx, unsigned char key[16]);
void dsi_es_set_nonce(dsi_es_context* ctx, unsigned char nonce[12]);
void dsi_es_set_random_nonce(dsi_es_context* ctx);
int dsi_es_decrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size);
void dsi_es_encrypt(dsi_es_context* ctx, unsigned char* buffer, unsigned char metablock[32], unsigned int size);
#ifdef __cplusplus
}
#endif
#endif // _DSI_H_

106
arm9/src/nand/u128_math.c Normal file
View File

@ -0,0 +1,106 @@
// u128_math
#include "u128_math.h"
#include <string.h>
// rotate a 128bit, little endian by shift bits in direction of increasing significance.
void u128_lrot(uint8_t *num, uint32_t shift)
{
uint8_t tmp[16];
for (int i=0;i<16;i++)
{
// rot: rotate to more significant.
// LSB is tmp[0], MSB is tmp[15]
const uint32_t byteshift = shift / 8;
const uint32_t bitshift = shift % 8;
tmp[(i+byteshift) % 16] = (num[i] << bitshift)
| ((num[(i+16-1) % 16] >> (8-bitshift)) & 0xff);
}
memcpy(num, tmp, 16);
}
// rotate a 128bit, little endian by shift bits in direction of decreasing significance.
void u128_rrot(uint8_t *num, uint32_t shift)
{
uint8_t tmp[16];
for (int i=0;i<16;i++)
{
// rot: rotate to less significant.
// LSB is tmp[0], MSB is tmp[15]
const uint32_t byteshift = shift / 8;
const uint32_t bitshift = shift % 8;
tmp[i] = (num[(i+byteshift) % 16] >> bitshift)
| ((num[(i+byteshift+1) % 16] << (8-bitshift)) & 0xff);
}
memcpy(num, tmp, 16);
}
// xor two 128bit, little endian values and store the result into the first
void u128_xor(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] ^ b[i];
}
}
// or two 128bit, little endian values and store the result into the first
void u128_or(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] | b[i];
}
}
// and two 128bit, little endian values and store the result into the first
void u128_and(uint8_t *a, const uint8_t *b)
{
for (int i=0;i<16;i++)
{
a[i] = a[i] & b[i];
}
}
// add two 128bit, little endian values and store the result into the first
void u128_add(uint8_t *a, const uint8_t *b)
{
uint8_t carry = 0;
for (int i=0;i<16;i++)
{
uint16_t sum = a[i] + b[i] + carry;
a[i] = sum & 0xff;
carry = sum >> 8;
}
}
// add two 128bit, little endian values and store the result into the first
void u128_add32(uint8_t *a, const uint32_t b)
{
uint8_t _b[16];
memset(_b, 0, sizeof(_b));
for (int i=0;i<4;i++)
_b[i] = b >> (i*8);
u128_add(a, _b);
}
// sub two 128bit, little endian values and store the result into the first
void u128_sub(uint8_t *a, const uint8_t *b)
{
uint8_t carry = 0;
for (int i=0;i<16;i++)
{
uint16_t sub = a[i] - b[i] - (carry & 1);
a[i] = sub & 0xff;
carry = sub >> 8;
}
}
void u128_swap(uint8_t *out, const uint8_t *in)
{
for (int i=0;i<16;i++)
{
out[15-i] = in[i];
}
}

38
arm9/src/nand/u128_math.h Normal file
View File

@ -0,0 +1,38 @@
// u128_math
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// left rotate by (shift % 128) bits
void u128_lrot(uint8_t *num, uint32_t shift);
// right rotate by (shift % 128) bits
void u128_rrot(uint8_t *num, uint32_t shift);
// bitwise xor strored to a
void u128_xor(uint8_t *a, const uint8_t *b);
// bitwise or strored to a
void u128_or(uint8_t *a, const uint8_t *b);
// bitwise and strored to a
void u128_and(uint8_t *a, const uint8_t *b);
// add b to a and store in a
void u128_add(uint8_t *a, const uint8_t *b);
// add 32 bit value b to a and store in a
void u128_add32(uint8_t *a, const uint32_t b);
// substract b from a and store in a
void u128_sub(uint8_t *a, const uint8_t *b);
// swap byte order
void u128_swap(uint8_t *out, const uint8_t *in);
#ifdef __cplusplus
}
#endif

455
arm9/src/nitrofs.c Normal file
View File

@ -0,0 +1,455 @@
/*
nitrofs.c - eris's wai ossum nitro filesystem device driver
Based on information found at http://frangoassado.org/ds/rom_spec.txt and from the #dsdev ppls
Kallisti (K) 2008-01-26 All rights reversed.
2008-05-19 v0.2 - New And Improved!! :DDD
* fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/)
* also thx to wintermute's input realized:
* if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode
* you then dont need the gba's LOADEROFFSET, so it was set to 0x000
2008-05-21 v0.3 - newer and more improved
* fixed some issues with ftell() (again was fseek's fault u_u;;)
* fixed possible error in detecting sc.gba files when using dldi
* readded support for .gba files in addition to .nds emu
* added stat() support for completedness :)
2008-05-22 v0.3.1 - slight update
* again fixed fseek(), this time SEEK_END oddly i kinda forgot about it >_> sry
* also went ahead and inlined the functions, makes slight proformance improvement
2008-05-26 v0.4 - added chdir
* added proper chdir functionality
2008-05-30 v0.5.Turbo - major speed improvement
* This version uses a single filehandle to access the .nds file when not in GBA mode
improving the speed it takes to open a .nds file by around 106ms. This is great for
situations requiring reading alot of seperate small files. However it does take a little
bit longer when reading from multiple files simultainously
(around 122ms over 10,327 0x100 byte reads between 2 files).
2008-06-09
* Fixed bug with SEEK_END where it wouldnt utilize the submitted position..
(now can fseek(f,-128,SEEK_END) to read from end of file :D)
2008-06-18 v0.6.Turbo - . and .. :D
* Today i have added full "." and ".." support.
dirnext() will return . and .. first, and all relevent operations will
support . and .. in pathnames.
2009-05-10 v0.7.Turbo - small changes @_@?!
2009-08-08 v0.8.Turbo - fix fix fix
* fixed problem with some cards where the header would be loaded to GBA ram even if running
in NDS mode causing nitroFSInit() to think it was a valid GBA cart header and attempt to
read from GBA SLOT instead of SLOT 1. Fixed this by making it check that filename is not NULL
and then to try FAT/SLOT1 first. The NULL option allows forcing nitroFS to use gba.
2018-09-05 v0.9 - modernize devoptab (by RonnChyran)
* Updated for libsysbase change in devkitARM r46 and above.
2020-08-20 v0.10 - modernize GBA SLOT support (by RocketRobz)
* Updated GBA SLOT detection to check for game code and header CRC.
2020-08-24 v0.11 - SDNAND support (by RocketRobz)
* Added support for being mounted from SDNAND, if app is launched by hiyaCFW.
*/
#include "nitrofs.h"
#include "version.h"
#include "tonccpy.h"
#include <errno.h>
#include <nds.h>
#include <string.h>
#define __itcm __attribute__((section(".itcm")))
// Globals!
u32 fntOffset; // offset to start of filename table
u32 fatOffset; // offset to start of file alloc table
bool hasLoader; // single global nds filehandle (is null if not in dldi/fat mode)
u16 chdirpathid; // default dir path id...
FILE *ndsFile;
off_t ndsFileLastpos; // Used to determine need to fseek or not
devoptab_t nitroFSdevoptab = {
"nitro", // const char *name;
sizeof(struct nitroFSStruct), // int structSize;
&nitroFSOpen, // int (*open_r)(struct _reent *r, void *fileStruct, const char *path,int flags,int mode);
&nitroFSClose, // int (*close_r)(struct _reent *r,void* fd);
NULL, // int (*write_r)(struct _reent *r,void* fd,const char *ptr,int len);
&nitroFSRead, // int (*read_r)(struct _reent *r,void* fd,char *ptr,int len);
&nitroFSSeek, // int (*seek_r)(struct _reent *r,void* fd,int pos,int dir);
&nitroFSFstat, // int (*fstat_r)(struct _reent *r,void* fd,struct stat *st);
&nitroFSstat, // int (*stat_r)(struct _reent *r,const char *file,struct stat *st);
NULL, // int (*link_r)(struct _reent *r,const char *existing, const char *newLink);
NULL, // int (*unlink_r)(struct _reent *r,const char *name);
&nitroFSChdir, // int (*chdir_r)(struct _reent *r,const char *name);
NULL, // int (*rename_r) (struct _reent *r, const char *oldName, const char *newName);
NULL, // int (*mkdir_r) (struct _reent *r, const char *path, int mode);
sizeof(struct nitroDIRStruct), // int dirStateSize;
&nitroFSDirOpen, // DIR_ITER* (*diropen_r)(struct _reent *r, DIR_ITER *dirState, const char *path);
&nitroDirReset, // int (*dirreset_r)(struct _reent *r, DIR_ITER *dirState);
&nitroFSDirNext, // int (*dirnext_r)(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
&nitroFSDirClose // int (*dirclose_r)(struct _reent *r, DIR_ITER *dirState);
};
// K, i decided to inline these, improves speed slightly..
// these 2 'sub' functions deal with actually reading from either gba rom or .nds file :)
// what i rly rly rly wanna know is how an actual nds cart reads from itself, but it seems no one can tell me ~_~
// so, instead we have this weird weird haxy try gbaslot then try dldi method. If i (or you!!) ever do figure out
// how to read the proper way can replace these 4 functions and everything should work normally :)
// reads from rom image either gba rom or dldi
inline ssize_t nitroSubRead(off_t *npos, void *ptr, size_t len) {
if(ndsFile != NULL) { // read from ndsfile
if(ndsFileLastpos != *npos)
fseek(ndsFile, *npos, SEEK_SET); // if we need to, move! (might want to verify this succeed)
len = fread(ptr, 1, len, ndsFile);
} else { // reading from gbarom
tonccpy(ptr,
*npos + (void *)GBAROM,
len); // len isnt checked here because other checks exist in the callers (hopefully)
}
if(len > 0)
*npos += len;
ndsFileLastpos = *npos; // save the current file nds pos
return (len);
}
// seek around
inline void nitroSubSeek(off_t *npos, int pos, int dir) {
if((dir == SEEK_SET) || (dir == SEEK_END)) // otherwise just set the pos :)
*npos = pos;
else if(dir == SEEK_CUR)
*npos += pos; // see ez!
}
// Figure out if its gba or ds, setup stuff
int __itcm nitroFSInit(const char *ndsfile) {
off_t pos = 0;
char romstr[0x10];
chdirpathid = NITROROOT;
ndsFileLastpos = 0;
ndsFile = NULL;
bool noCashGba = (strncmp((const char *)0x4FFFA00, "no$gba", 6) == 0);
if(!isDSiMode() || noCashGba) {
sysSetCartOwner(BUS_OWNER_ARM9); // give us gba slot ownership
// We has gba rahm
// printf("yes i think this is GBA?!\n");
if(strncmp(((const char *)GBAROM) + LOADERSTROFFSET, LOADERSTR, strlen(LOADERSTR)) ==
0) { // Look for second magic string, if found its a sc.nds or nds.gba
// printf("sc/gba\n");
fntOffset = ((u32) * (u32 *)(((const char *)GBAROM) + FNTOFFSET + LOADEROFFSET)) + LOADEROFFSET;
fatOffset = ((u32) * (u32 *)(((const char *)GBAROM) + FATOFFSET + LOADEROFFSET)) + LOADEROFFSET;
hasLoader = true;
AddDevice(&nitroFSdevoptab);
return (1);
} else if(noCashGba) { // Ok, its not a .gba build, so must be emulator
// printf("gba, must be emu\n");
fntOffset = ((u32) * (u32 *)(((const char *)GBAROM) + FNTOFFSET));
fatOffset = ((u32) * (u32 *)(((const char *)GBAROM) + FATOFFSET));
hasLoader = false;
AddDevice(&nitroFSdevoptab);
return (1);
}
}
if(isDSiMode() && ndsfile == NULL) {
// Try SDNAND path
char fileName[64];
sprintf(fileName,
"sd:/title/%08x/%08x/content/000000%02x.app",
*(unsigned int *)0x02FFE234,
*(unsigned int *)0x02FFE230,
*(u8 *)0x02FFE01E);
ndsfile = fileName;
}
if(ndsfile != NULL) {
if((ndsFile = fopen(ndsfile, "rb"))) {
nitroSubRead(&pos, romstr, strlen(LOADERSTR));
if(strncmp(romstr, LOADERSTR, strlen(LOADERSTR)) == 0) {
nitroSubSeek(&pos, LOADEROFFSET + FNTOFFSET, SEEK_SET);
nitroSubRead(&pos, &fntOffset, sizeof(fntOffset));
nitroSubSeek(&pos, LOADEROFFSET + FATOFFSET, SEEK_SET);
nitroSubRead(&pos, &fatOffset, sizeof(fatOffset));
fatOffset += LOADEROFFSET;
fntOffset += LOADEROFFSET;
hasLoader = true;
} else {
nitroSubSeek(&pos, FNTOFFSET, SEEK_SET);
nitroSubRead(&pos, &fntOffset, sizeof(fntOffset));
nitroSubSeek(&pos, FATOFFSET, SEEK_SET);
nitroSubRead(&pos, &fatOffset, sizeof(fatOffset));
hasLoader = false;
}
setvbuf(ndsFile, NULL, _IONBF, 0); // we dont need double buffs u_u
AddDevice(&nitroFSdevoptab);
return (1);
}
}
return (0);
}
// Directory functs
DIR_ITER *nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path) {
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; // this makes it lots easier!
struct stat st;
char dirname[NITRONAMELENMAX];
char *cptr;
char mydirpath[NITROMAXPATHLEN]; // to hold copy of path string
char *dirpath = mydirpath;
bool pathfound;
if((cptr = strchr(path, ':')))
path = cptr + 1; // move path past any device names (if it was nixy style wouldnt need this step >_>)
strncpy(dirpath, path, sizeof(mydirpath) - 1); // copy the string (as im gonna mutalate it)
dirStruct->pos = 0;
if(*dirpath == '/') // if first character is '/' use absolute root path plz
dirStruct->cur_dir_id = NITROROOT; // first root dir
else
dirStruct->cur_dir_id = chdirpathid; // else use chdirpath
nitroDirReset(r, dirState); // set dir to current path
do {
while((cptr = strchr(dirpath, '/')) == dirpath) {
dirpath++; // move past any leading / or // together
}
if(cptr)
*cptr = 0; // erase /
if(*dirpath ==
0) { // are we at the end of the path string?? if so there is nothing to search for we're already here !
pathfound = true; // mostly this handles searches for root or / or no path specified cases
break;
}
pathfound = false;
while(nitroFSDirNext(r, dirState, dirname, &st) == 0) {
if((st.st_mode == S_IFDIR) && !(strcmp(dirname, dirpath))) { // if its a directory and name matches dirpath
dirStruct->cur_dir_id = dirStruct->dir_id; // move us to the next dir in tree
nitroDirReset(r, dirState); // set dir to current path we just found...
pathfound = true;
break;
}
};
if(!pathfound)
break;
dirpath = cptr + 1; // move to right after last / we found
} while(cptr); // go till after the last /
if(pathfound) {
return (dirState);
} else {
r->_errno = ENOENT;
return (NULL);
}
}
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState) { return (0); }
/*Consts containing relative system path strings*/
const char *syspaths[2] = {".", ".."};
// reset dir to start of entry selected by dirStruct->cur_dir_id which should be set in dirOpen okai?!
int nitroDirReset(struct _reent *r, DIR_ITER *dirState) {
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; // this makes it lots easier!
struct ROM_FNTDir dirsubtable;
off_t *pos = &dirStruct->pos;
nitroSubSeek(pos, fntOffset + ((dirStruct->cur_dir_id & NITRODIRMASK) * sizeof(struct ROM_FNTDir)), SEEK_SET);
nitroSubRead(pos, &dirsubtable, sizeof(dirsubtable));
dirStruct->namepos = dirsubtable.entry_start; // set namepos to first entry in this dir's table
dirStruct->entry_id = dirsubtable.entry_file_id; // get number of first file ID in this branch
dirStruct->parent_id = dirsubtable.parent_id; // save parent ID in case we wanna add ../ functionality
dirStruct->spc = 0; // system path counter, first two dirnext's deliver . and ..
return (0);
}
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st) {
unsigned char next;
struct nitroDIRStruct *dirStruct = (struct nitroDIRStruct *)dirState->dirStruct; // this makes it lots easier!
off_t *pos = &dirStruct->pos;
if(dirStruct->spc <= 1) {
if(st)
st->st_mode = S_IFDIR;
if((dirStruct->spc == 0) || (dirStruct->cur_dir_id == NITROROOT)) { // "." or its already root (no parent)
dirStruct->dir_id = dirStruct->cur_dir_id;
} else { // ".."
dirStruct->dir_id = dirStruct->parent_id;
}
strcpy(filename, syspaths[dirStruct->spc++]);
return (0);
}
nitroSubSeek(pos, fntOffset + dirStruct->namepos, SEEK_SET);
nitroSubRead(pos, &next, sizeof(next));
// next: high bit 0x80 = entry isdir.. other 7 bits r size, the 16 bits following name are dir's entryid (starts
// with f000)
// 00 = endoftable //
if(next) {
if(next & NITROISDIR) {
if(st)
st->st_mode = S_IFDIR;
next &= NITROISDIR ^ 0xff; // invert bits and mask off 0x80
nitroSubRead(pos, filename, next);
nitroSubRead(
&dirStruct->pos,
&dirStruct->dir_id,
sizeof(
dirStruct->dir_id)); // read the dir_id
// grr cant get the struct member size?, just wanna test it so moving on...
// nitroSubRead(pos,&dirStruct->dir_id,sizeof(u16)); //read the dir_id
dirStruct->namepos += next + sizeof(u16) + 1; // now we points to next one plus dir_id size:D
} else {
if(st)
st->st_mode = 0;
nitroSubRead(pos, filename, next);
dirStruct->namepos += next + 1; // now we points to next one :D
// read file info to get filesize (and for fileopen)
nitroSubSeek(pos, fatOffset + (dirStruct->entry_id * sizeof(struct ROM_FAT)), SEEK_SET);
nitroSubRead(pos,
&dirStruct->romfat,
sizeof(dirStruct->romfat)); // retrieve romfat entry (contains filestart and end positions)
dirStruct->entry_id++; // advance ROM_FNTStrFile ptr
if(st)
st->st_size = dirStruct->romfat.bottom - dirStruct->romfat.top; // calculate filesize
}
filename[(int)next] = 0; // zero last char
return (0);
} else {
r->_errno = EIO;
return (-1);
}
}
// fs functs
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fileStruct;
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
dirState.dirStruct = &dirStruct; // create a temp dirstruct
struct _reent dre;
struct stat st; // all these are just used for reading the dir ~_~
char dirfilename[NITROMAXPATHLEN]; // to hold a full path (i tried to avoid using so much stack but blah :/)
char *filename; // to hold filename
char *cptr; // used to string searching and manipulation
cptr = (char *)path + strlen(path); // find the end...
filename = NULL;
do {
if((*cptr == '/') || (*cptr == ':')) { // split at either / or : (whichever comes first form the end!)
cptr++;
strncpy(dirfilename, path, cptr - path); // copy string up till and including/ or : zero rest
dirfilename[cptr - path] = 0; // it seems strncpy doesnt always zero?!
filename = cptr; // filename = now remainder of string
break;
}
} while(cptr-- != path); // search till start
if(!filename) { // we didnt find a / or : ? shouldnt realyl happen but if it does...
filename = (char *)path; // filename = complete path
dirfilename[0] = 0; // make directory path ""
}
if(nitroFSDirOpen(&dre, &dirState, dirfilename)) {
fatStruct->start = 0;
while(nitroFSDirNext(&dre, &dirState, dirfilename, &st) == 0) {
if(!(st.st_mode & S_IFDIR) && (strcmp(dirfilename, filename) == 0)) { // Found the *file* youre looking
// for!!
fatStruct->start = dirStruct.romfat.top;
fatStruct->end = dirStruct.romfat.bottom;
if(hasLoader) {
fatStruct->start += LOADEROFFSET;
fatStruct->end += LOADEROFFSET;
}
break;
}
}
if(fatStruct->start) {
nitroSubSeek(&fatStruct->pos, fatStruct->start, SEEK_SET); // seek to start of file
return (0); // woot!
}
nitroFSDirClose(&dre, &dirState);
}
if(r->_errno == 0) {
r->_errno = ENOENT;
}
return (-1); // teh fail
}
int nitroFSClose(struct _reent *r, void *fd) { return (0); }
ssize_t nitroFSRead(struct _reent *r, void *fd, char *ptr, size_t len) {
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
off_t *npos = &fatStruct->pos;
if(*npos + len > fatStruct->end)
len = fatStruct->end - *npos; // dont let us read past the end plz!
if(*npos > fatStruct->end)
return (0); // hit eof
return (nitroSubRead(npos, ptr, len));
}
off_t nitroFSSeek(struct _reent *r, void *fd, off_t pos, int dir) {
// need check for eof here...
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
off_t *npos = &fatStruct->pos;
if(dir == SEEK_SET)
pos += fatStruct->start; // add start from .nds file offset
else if(dir == SEEK_END)
pos += fatStruct->end; // set start to end of file (useless?)
if(pos > fatStruct->end)
return (-1); // dont let us read past the end plz!
nitroSubSeek(npos, pos, dir);
return (*npos - fatStruct->start);
}
int nitroFSFstat(struct _reent *r, void *fd, struct stat *st) {
struct nitroFSStruct *fatStruct = (struct nitroFSStruct *)fd;
st->st_size = fatStruct->end - fatStruct->start;
return (0);
}
int nitroFSstat(struct _reent *r, const char *file, struct stat *st) {
struct nitroFSStruct fatStruct;
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
if(nitroFSOpen(NULL, &fatStruct, file, 0, 0) >= 0) {
st->st_mode = S_IFREG;
st->st_size = fatStruct.end - fatStruct.start;
return (0);
}
dirState.dirStruct = &dirStruct;
if((nitroFSDirOpen(r, &dirState, file) != NULL)) {
st->st_mode = S_IFDIR;
nitroFSDirClose(r, &dirState);
return (0);
}
r->_errno = ENOENT;
return (-1);
}
int nitroFSChdir(struct _reent *r, const char *name) {
struct nitroDIRStruct dirStruct;
DIR_ITER dirState;
dirState.dirStruct = &dirStruct;
if((name != NULL) && (nitroFSDirOpen(r, &dirState, name) != NULL)) {
chdirpathid = dirStruct.cur_dir_id;
nitroFSDirClose(r, &dirState);
return (0);
} else {
r->_errno = ENOENT;
return (-1);
}
}
// From pkmn-chest
bool nitroFSGood(void) {
bool nitroFSGood = false;
FILE *file = fopen("nitro:/version_twlnandtool", "r");
if(file) {
fseek(file, 0, SEEK_END);
int length = ftell(file);
fseek(file, 0, SEEK_SET);
char *version = (char *)malloc((length + 1) * sizeof(char));
fread(version, 1, length, file);
nitroFSGood = (strcmp(version, VERSION) == 0);
fclose(file);
}
return nitroFSGood;
}

132
arm9/src/nitrofs.h Normal file
View File

@ -0,0 +1,132 @@
/*
nitrofs.hpp - eris's wai ossum nitro filesystem device driver header
Based on information found at http://frangoassado.org/ds/rom_spec.txt and from the #dsdev ppls
Kallisti (K) 2008-01-26 All rights reversed.
2008-05-19 v0.2 - New And Improved!! :DDD
* fix'd the fseek SEEK_CUR issue (my fseek funct should not have returned a value :/)
* also thx to wintermute's input realized:
* if you dont give ndstool the -o wifilogo.bmp option it will run on emulators in gba mode
* you then dont need the gba's LOADEROFFSET, so it was set to 0x000
2008-05-21 v0.3 - newer and more improved
* fixed some issues with ftell() (again was fseek's fault u_u;;)
* fixed possible error in detecting sc.gba files when using dldi
* readded support for .gba files in addition to .nds emu
* added stat() support for completedness :)
2008-05-22 v0.3.1 - slight update
* again fixed fseek(), this time SEEK_END oddly i kinda forgot about it >_> sry
* also went ahead and inlined the functions, makes slight proformance improvement
2008-05-26 v0.4 - added chdir
* added proper chdir functionality
2008-05-30 v0.5.Turbo - major speed improvement
* This version uses a single filehandle to access the .nds file when not in GBA mode
improving the speed it takes to open a .nds file by around 106ms. This is great for
situations requiring reading alot of seperate small files. However it does take a little
bit longer when reading from multiple files simultainously
(around 122ms over 10,327 0x100 byte reads between 2 files).
2008-06-09
* Fixed bug with SEEK_END where it wouldnt utilize the submitted position..
(now can fseek(f,-128,SEEK_END) to read from end of file :D)
2008-06-18 v0.6.Turbo - . and .. :D
* Today i have added full "." and ".." support.
dirnext() will return . and .. first, and all relevent operations will
support . and .. in pathnames.
2009-05-10 v0.7.Turbo - small changes @_@?!
2009-08-08 v0.8.Turbo - fix fix fix
* fixed problem with some cards where the header would be loaded to GBA ram even if running
in NDS mode causing nitroFSInit() to think it was a valid GBA cart header and attempt to
read from GBA SLOT instead of SLOT 1. Fixed this by making it check that filename is not NULL
and then to try FAT/SLOT1 first. The NULL option allows forcing nitroFS to use gba.
2018-09-05 v0.9 - modernize devoptab (by RonnChyran)
* Updated for libsysbase change in devkitARM r46 and above.
2020-08-20 v0.10 - modernize GBA SLOT support (by RocketRobz)
* Updated GBA SLOT detection to check for game code and header CRC.
2020-08-24 v0.11 - SDNAND support (by RocketRobz)
* Added support for being mounted from SDNAND, if app is launched by hiyaCFW.
*/
#ifndef NITROFS_H
#define NITROFS_H
#include <nds.h>
#include <stdio.h>
#include <sys/dir.h>
#include <sys/iosupport.h>
#include "version.h"
#ifdef __cplusplus
extern "C" {
#endif
int nitroFSInit(const char *ndsfile);
DIR_ITER *nitroFSDirOpen(struct _reent *r, DIR_ITER *dirState, const char *path);
int nitroDirReset(struct _reent *r, DIR_ITER *dirState);
int nitroFSDirNext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *st);
int nitroFSDirClose(struct _reent *r, DIR_ITER *dirState);
int nitroFSOpen(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
int nitroFSClose(struct _reent *r, void *fd);
ssize_t nitroFSRead(struct _reent *r, void *fd, char *ptr, size_t len);
off_t nitroFSSeek(struct _reent *r, void *fd, off_t pos, int dir);
int nitroFSFstat(struct _reent *r, void *fd, struct stat *st);
int nitroFSstat(struct _reent *r, const char *file, struct stat *st);
int nitroFSChdir(struct _reent *r, const char *name);
bool nitroFSGood(void);
#define LOADERSTR "PASS" //look for this
#define LOADERSTROFFSET 0xac
#define LOADEROFFSET 0x0200
#define FNTOFFSET 0x40
#define FATOFFSET 0x48
#define NITRONAMELENMAX 0x80 //max file name is 127 +1 for zero byte :D
#define NITROMAXPATHLEN 0x100 //256 bytes enuff?
#define NITROROOT 0xf000 //root entry_file_id
#define NITRODIRMASK 0x0fff //remove leading 0xf
#define NITROISDIR 0x80 //mask to indicate this name entry is a dir, other 7 bits = name length
//Directory filename subtable entry structure
struct ROM_FNTDir {
u32 entry_start;
u16 entry_file_id;
u16 parent_id;
};
//Yo, dis table is fat (describes the structures
struct ROM_FAT {
u32 top; //start of file in rom image
u32 bottom; //end of file in rom image
};
struct nitroFSStruct {
off_t pos; //where in the file am i?
off_t start; //where in the rom this file starts
off_t end; //where in the rom this file ends
};
struct nitroDIRStruct {
off_t pos; //where in the file am i?
off_t namepos; //ptr to next name to lookup in list
struct ROM_FAT romfat;
u16 entry_id; //which entry this is (for files only) incremented with each new file in dir?
u16 dir_id; //which directory entry this is.. used ofc for dirs only
u16 cur_dir_id; //which directory entry we are using
u16 parent_id; //who is the parent of the current directory (this can be used to easily ../ )
u8 spc; //system path count.. used by dirnext, when 0=./ 1=../ >=2 actual dirs
};
#ifdef __cplusplus
}
#endif
#endif

566
arm9/src/storage.c Normal file
View File

@ -0,0 +1,566 @@
#include "storage.h"
#include "main.h"
#include "message.h"
#include <errno.h>
#include <dirent.h>
#include "video.h"
#define TITLE_LIMIT 39
//printing
void printBytes(unsigned long long bytes)
{
if (bytes < 1024)
iprintf("%dB", (unsigned int)bytes);
else if (bytes < 1024 * 1024)
printf("%.2fKB", (float)bytes / 1024.f);
else if (bytes < 1024 * 1024 * 1024)
printf("%.2fMB", (float)bytes / 1024.f / 1024.f);
else
printf("%.2fGB", (float)bytes / 1024.f / 1024.f / 1024.f);
}
//progress bar
static int lastBars = 0;
void printProgressBar(float percent)
{
if (percent < 0.f) percent = 0.f;
if (percent > 1.f) percent = 1.f;
int bars = (int)(30.f * percent);
//skip redundant prints
if (bars != lastBars)
{
consoleSet(cMAIN);
iprintf("\x1B[42m"); //green
//Print frame
if (lastBars <= 0)
{
iprintf("\x1b[23;0H[");
iprintf("\x1b[23;31H]");
}
//Print bars
if (bars > 0)
{
for (int i = 0; i < bars; i++)
iprintf("\x1b[23;%dH|", 1 + i);
}
lastBars = bars;
iprintf("\x1B[47m"); //white
}
}
void clearProgressBar()
{
lastBars = 0;
consoleSet(cMAIN);
iprintf("\x1b[23;0H ");
}
//files
bool fileExists(char const* path)
{
if (!path) return false;
FILE* f = fopen(path, "rb");
if (!f)
return false;
fclose(f);
return true;
}
int copyFile(char const* src, char const* dst)
{
if (!src) return 1;
unsigned long long size = getFileSizePath(src);
return copyFilePart(src, 0, size, dst);
}
int copyFilePart(char const* src, u32 offset, u32 size, char const* dst)
{
if (!src) return 1;
if (!dst) return 2;
FILE* fin = fopen(src, "rb");
if (!fin)
{
fclose(fin);
return 3;
}
else
{
if (fileExists(dst))
remove(dst);
FILE* fout = fopen(dst, "wb");
if (!fout)
{
fclose(fin);
fclose(fout);
return 4;
}
else
{
fseek(fin, offset, SEEK_SET);
consoleSet(cMAIN);
int bytesRead;
unsigned long long totalBytesRead = 0;
#define BUFF_SIZE 128 //Arbitrary. A value too large freezes the ds.
char* buffer = (char*)malloc(BUFF_SIZE);
while (!programEnd)
{
unsigned int toRead = BUFF_SIZE;
if (size - totalBytesRead < BUFF_SIZE)
toRead = size - totalBytesRead;
bytesRead = fread(buffer, 1, toRead, fin);
fwrite(buffer, bytesRead, 1, fout);
totalBytesRead += bytesRead;
printProgressBar( ((float)totalBytesRead / (float)size) );
if (bytesRead != BUFF_SIZE)
break;
}
clearProgressBar();
consoleSet(cSUB);
free(buffer);
}
fclose(fout);
}
fclose(fin);
return 0;
}
unsigned long long getFileSize(FILE* f)
{
if (!f) return 0;
fseek(f, 0, SEEK_END);
unsigned long long size = ftell(f);
fseek(f, 0, SEEK_SET);
return size;
}
unsigned long long getFileSizePath(char const* path)
{
if (!path) return 0;
FILE* f = fopen(path, "rb");
unsigned long long size = getFileSize(f);
fclose(f);
return size;
}
bool padFile(char const* path, int size)
{
if (!path) return false;
FILE* f = fopen(path, "ab");
if (!f)
{
return false;
}
else
{
for (int i = 0; i < size; i++)
fputc('\0', f);
}
fclose(f);
return true;
}
//directories
bool dirExists(char const* path)
{
if (!path) return false;
DIR* dir = opendir(path);
if (!dir)
return false;
closedir(dir);
return true;
}
bool copyDir(char const* src, char const* dst)
{
if (!src || !dst) return false;
// iprintf("copyDir\n%s\n%s\n\n", src, dst);
bool result = true;
DIR* dir = opendir(src);
struct dirent* ent;
if (!dir)
{
return false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char* dsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(dsrc, "%s/%s", src, ent->d_name);
char* ddst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(ddst, "%s/%s", dst, ent->d_name);
mkdir(ddst, 0777);
if (!copyDir(dsrc, ddst))
result = false;
free(ddst);
free(dsrc);
}
else
{
char* fsrc = (char*)malloc(strlen(src) + strlen(ent->d_name) + 4);
sprintf(fsrc, "%s/%s", src, ent->d_name);
char* fdst = (char*)malloc(strlen(dst) + strlen(ent->d_name) + 4);
sprintf(fdst, "%s/%s", dst, ent->d_name);
// iprintf("%s\n%s\n\n", fsrc, fdst);
iprintf("%s -> \n%s...", fsrc, fdst);
int ret = copyFile(fsrc, fdst);
if (ret != 0)
{
iprintf("\x1B[31m"); //red
iprintf("Fail\n");
iprintf("\x1B[33m"); //yellow
iprintf("%s\n", strerror(errno));
/*
switch (ret)
{
case 1:
iprintf("Empty input path.\n");
break;
case 2:
iprintf("Empty output path.\n");
break;
case 3:
iprintf("Error opening input file.\n");
break;
case 4:
iprintf("Error opening output file.\n");
break;
}
*/
iprintf("\x1B[47m"); //white
result = false;
}
else
{
iprintf("\x1B[42m"); //green
iprintf("Done\n");
iprintf("\x1B[47m"); //white
}
free(fdst);
free(fsrc);
}
}
}
closedir(dir);
return result;
}
bool deleteDir(char const* path)
{
if (!path) return false;
if (strcmp("/", path) == 0)
{
//oh fuck no
return false;
}
bool result = true;
DIR* dir = opendir(path);
struct dirent* ent;
if (!dir)
{
result = false;
}
else
{
while ( (ent = readdir(dir)) )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
//Delete directory
char subpath[512];
sprintf(subpath, "%s/%s", path, ent->d_name);
if (!deleteDir(subpath))
result = false;
}
else
{
//Delete file
char fpath[512];
sprintf(fpath, "%s/%s", path, ent->d_name);
iprintf("%s...", fpath);
if (remove(fpath) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
}
}
}
closedir(dir);
iprintf("%s...", path);
if (remove(path) != 0)
{
iprintf("\x1B[31m");
iprintf("Fail\n");
iprintf("\x1B[47m");
result = false;
}
else
{
iprintf("\x1B[42m");
iprintf("Done\n");
iprintf("\x1B[47m");
}
return result;
}
unsigned long long getDirSize(const char* path, u32 blockSize)
{
if (!path) return 0;
unsigned long long size = 0;
DIR* dir = opendir(path);
struct dirent* ent;
if (dir)
{
while ((ent = readdir(dir)))
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
{
char fullpath[512];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getDirSize(fullpath, blockSize);
}
else
{
char fullpath[260];
sprintf(fullpath, "%s/%s", path, ent->d_name);
size += getFileSizePath(fullpath);
// If we've specified a block size, round up to it
if ((size % blockSize) != 0)
size += blockSize - (size % blockSize);
}
}
}
closedir(dir);
return size;
}
//home menu
int getMenuSlots()
{
//Assume the home menu has a hard limit on slots
//Find a better way to do this
return TITLE_LIMIT;
}
int getMenuSlotsFree()
{
//Get number of open menu slots by subtracting the number of directories in the title folders
//Find a better way to do this
const int NUM_OF_DIRS = 4;
const char* dirs[] = {
"00030004",
"00030005",
"0003000f",
"00030015",
"00030017"
};
int freeSlots = getMenuSlots();
DIR* dir;
struct dirent* ent;
for (int i = 0; i < NUM_OF_DIRS; i++)
{
char path[256];
sprintf(path, "%s:/title/%s", sdnandMode ? "sd" : "nand", dirs[i]);
dir = opendir(path);
if (dir)
{
while ( (ent = readdir(dir)) != NULL )
{
if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0)
continue;
if (ent->d_type == DT_DIR)
freeSlots -= 1;
}
}
closedir(dir);
}
return freeSlots;
}
//SD card
bool sdIsInserted()
{
//Find a better way to do this.
return true;
}
unsigned long long getSDCardSize()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_blocks;
}
return 0;
}
unsigned long long getSDCardFree()
{
if (sdIsInserted())
{
struct statvfs st;
if (statvfs("sd:/", &st) == 0)
return st.f_bsize * st.f_bavail;
}
return 0;
}
//internal storage
unsigned long long getDsiSize()
{
//The DSi has 256MB of internal storage. Some is unavailable and used by other things.
//An empty DSi reads 1024 open blocks
return 1024 * BYTES_PER_BLOCK;
}
unsigned long long getDsiFree()
{
u32 blockSize = getDsiClusterSize();
//Get free space by subtracting file sizes in nand folders
unsigned long long size = getDsiSize();
unsigned long long appSize = getDirSize(sdnandMode ? "sd:/title/00030004" : "nand:/title/00030004", blockSize);
//subtract, but don't go under 0
if (appSize > size)
{
size = 0;
}
else
{
size -= appSize;
}
unsigned long long realFree = getDsiRealFree();
return (realFree < size) ? realFree : size;
}
unsigned long long getDsiRealSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_blocks;
return 0;
}
unsigned long long getDsiRealFree()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize * st.f_bavail;
return 0;
}
u32 getDsiClusterSize()
{
struct statvfs st;
if (statvfs(sdnandMode ? "sd:/" : "nand:/", &st) == 0)
return st.f_bsize;
return 0;
}

51
arm9/src/storage.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef STORAGE_H
#define STORAGE_H
#include <nds/ndstypes.h>
#include <stdio.h>
#include "video.h"
#define BACKUP_PATH "sd:/_nds/ntm/backup"
#define BYTES_PER_BLOCK (1024*128)
//printing
void printBytes(unsigned long long bytes);
//progress bar
void printProgressBar(float percent);
void clearProgressBar();
//Files
bool fileExists(char const* path);
int copyFile(char const* src, char const* dst);
int copyFilePart(char const* src, u32 offset, u32 size, char const* dst);
unsigned long long getFileSize(FILE* f);
unsigned long long getFileSizePath(char const* path);
bool padFile(char const* path, int size);
//Directories
bool dirExists(char const* path);
bool copyDir(char const* src, char const* dst);
bool deleteDir(char const* path);
unsigned long long getDirSize(char const* path, u32 blockSize);
//home menu
int getMenuSlots();
int getMenuSlotsFree();
#define getMenuSlotsUsed() (getMenuSlots() - getMenuSlotsFree())
//SD card
bool sdIsInserted();
unsigned long long getSDCardSize();
unsigned long long getSDCardFree();
#define getSDCardUsedSpace() (getSDCardSize() - getSDCardFree())
//internal storage
unsigned long long getDsiSize();
unsigned long long getDsiFree();
unsigned long long getDsiRealSize();
unsigned long long getDsiRealFree();
u32 getDsiClusterSize();
#define getDsiUsed() (getDSIStorageSize() - getDSIStorageFree())
#endif

136
arm9/src/tonccpy.c Normal file
View File

@ -0,0 +1,136 @@
#include "tonccpy.h"
//# tonccpy.c
//! VRAM-safe cpy.
/*! This version mimics memcpy in functionality, with
the benefit of working for VRAM as well. It is also
slightly faster than the original memcpy, but faster
implementations can be made.
\param dst Destination pointer.
\param src Source pointer.
\param size Fill-length in bytes.
\note The pointers and size need not be word-aligned.
*/
void tonccpy(void *dst, const void *src, uint size) {
if(size == 0 || dst == NULL || src == NULL)
return;
uint count;
u16 *dst16; // hword destination
u8 *src8; // byte source
// Ideal case: copy by 4x words. Leaves tail for later.
if(((u32)src | (u32)dst) % 4 == 0 && size >= 4) {
u32 *src32 = (u32 *)src, *dst32 = (u32 *)dst;
count = size / 4;
uint tmp = count & 3;
count /= 4;
// Duff's Device, good friend!
switch(tmp) {
do {
*dst32++ = *src32++;
case 3:
*dst32++ = *src32++;
case 2:
*dst32++ = *src32++;
case 1:
*dst32++ = *src32++;
case 0:;
} while(count--);
}
// Check for tail
size &= 3;
if(size == 0)
return;
src8 = (u8 *)src32;
dst16 = (u16 *)dst32;
} else { // Unaligned.
uint dstOfs = (u32)dst & 1;
src8 = (u8 *)src;
dst16 = (u16 *)(dst - dstOfs);
// Head: 1 byte.
if(dstOfs != 0) {
*dst16 = (*dst16 & 0xFF) | *src8++ << 8;
dst16++;
if(--size == 0)
return;
}
}
// Unaligned main: copy by 2x byte.
count = size / 2;
while(count--) {
*dst16++ = src8[0] | src8[1] << 8;
src8 += 2;
}
// Tail: 1 byte.
if(size & 1)
*dst16 = (*dst16 & ~0xFF) | *src8;
}
//# toncset.c
//! VRAM-safe memset, internal routine.
/*! This version mimics memset in functionality, with
the benefit of working for VRAM as well. It is also
slightly faster than the original memset.
\param dst Destination pointer.
\param fill Word to fill with.
\param size Fill-length in bytes.
\note The \a dst pointer and \a size need not be
word-aligned. In the case of unaligned fills, \a fill
will be masked off to match the situation.
*/
void __toncset(void *dst, u32 fill, uint size) {
if(size == 0 || dst == NULL)
return;
uint left = (u32)dst & 3;
u32 *dst32 = (u32 *)(dst - left);
u32 count, mask;
// Unaligned head.
if(left != 0) {
// Adjust for very small stint.
if(left + size < 4) {
mask = BIT_MASK(size * 8) << (left * 8);
*dst32 = (*dst32 & ~mask) | (fill & mask);
return;
}
mask = BIT_MASK(left * 8);
*dst32 = (*dst32 & mask) | (fill & ~mask);
dst32++;
size -= 4 - left;
}
// Main stint.
count = size / 4;
uint tmp = count & 3;
count /= 4;
switch(tmp) {
do {
*dst32++ = fill;
case 3:
*dst32++ = fill;
case 2:
*dst32++ = fill;
case 1:
*dst32++ = fill;
case 0:;
} while(count--);
}
// Tail
size &= 3;
if(size) {
mask = BIT_MASK(size * 8);
*dst32 = (*dst32 & ~mask) | (fill & mask);
}
}

36
arm9/src/tonccpy.h Normal file
View File

@ -0,0 +1,36 @@
//# Stuff you may not have yet.
#ifndef TONCCPY_H
#define TONCCPY_H
#ifdef __cplusplus
extern "C" {
#endif
#include <nds.h>
typedef unsigned int uint;
#define BIT_MASK(len) ((1<<(len))-1)
static inline u32 quad8(u32 x) { x |= x<<8; return x | x<<16; }
//# Declarations and inlines.
void tonccpy(void *dst, const void *src, uint size);
void __toncset(void *dst, u32 fill, uint size);
//! VRAM-safe memset, byte version. Size in bytes.
static inline void toncset(void *dst, u8 src, uint size) { __toncset(dst, quad8(src), size); }
//! VRAM-safe memset, halfword version. Size in hwords.
static inline void toncset16(void *dst, u16 src, uint size) { __toncset(dst, src|src<<16, size*2); }
//! VRAM-safe memset, word version. Size in words.
static inline void toncset32(void *dst, u32 src, uint size) { __toncset(dst, src, size*4); }
#ifdef __cplusplus
}
#endif
#endif

127
arm9/src/video.c Executable file
View File

@ -0,0 +1,127 @@
#include <stdlib.h>
#include <stdio.h>
#include <nds.h>
// From https://code.google.com/archive/p/sundevos/
// common headers
#include "video.h"
#define SUB ((u16 *)BG_BMP_RAM_SUB(2))
#define MAIN ((u16 *)BG_BMP_RAM(2))
PrintConsole console, consoleSub;
/*
* map base : 2Ko
* tile base : 16Ko
* bmp base : 16Ko
*/
void videoInit() {
consoleDebugInit(DebugDevice_CONSOLE); // debug to console
videoSetMode(MODE_5_3D);
vramSetBankA(VRAM_A_MAIN_BG_0x06000000); // allocate 128Ko at 0x06000000
// <-- console (debug console for binaries loaded)
consoleInit(
&console, // the console to be initted
1, // bgLayer
BgType_Text4bpp, // bg type
BgSize_T_256x256, // bg size
4, // map base
0, // tile base
true, // main display
true // load graphics
);
iprintf("\x1b[32;1m"); // Set the color to green
// --> <-- 2D (96Ko at 0x06008000)
bgInit(
3, // bg layer
BgType_Bmp16, // bg type
BgSize_B16_256x256, // bg size
2, // map base (here it's the bmp base)
0 // tile base (here it's useless)
);
screenFill(MAIN, RGB15(31, 31, 31)|BIT(15));
// --> <-- 3D
/* <-- old
glInit();
glClearColor(0,0,0,0); // make the BG transparent
glClearDepth(0x7FFF);
glViewport(0,0,255,191); // Set our viewport to be the same size as the screen
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70, 256.0 / 192.0, 0.1, 100);
--> */
#define MIN_X (0.0f)
#define MAX_X (4.0f)
#define MIN_Y (0.0f)
#define MAX_Y (3.0f)
glInit();
glClearColor(0,0,0,0); // BG must be opaque for AA to work
//glClearPolyID(63); // BG must have a unique polygon ID for AA to work
glClearDepth(0x7FFF);
glViewport(0,0,255,191);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrthof32(floattof32(MIN_X), floattof32(MAX_X), floattof32(MIN_Y), floattof32(MAX_Y), floattof32(0.1), floattof32(10));
glMatrixMode(GL_MODELVIEW);
glMaterialf(GL_AMBIENT, RGB15(31,31,31));
glMaterialf(GL_DIFFUSE, RGB15(31,31,31));
glMaterialf(GL_SPECULAR, BIT(15) | RGB15(31,31,31));
glMaterialf(GL_EMISSION, RGB15(31,31,31));
glMaterialShinyness();
glMatrixMode(GL_MODELVIEW);
// -->
videoSetModeSub(MODE_5_2D);
vramSetBankC(VRAM_C_SUB_BG_0x06200000); // allocate 128Ko at 0x06200000
// <-- console (debug console for SunOS)
consoleInit(
&consoleSub, // the console to be initted
1, // bgLayer
BgType_Text4bpp, // bg type
BgSize_T_256x256, // bg size
4, // map base
0, // tile base
false, // main display
true // load graphics
);
iprintf("\x1b[31;1m"); // Set the color to red
// --> <-- 2D (96Ko at 0x06208000)
bgInitSub(
3, // bg layer
BgType_Bmp16, // bg type
BgSize_B16_256x256, // bg size
2, // map base (here it's the bmp base)
0 // tile base (here it's useless)
);
screenFill(SUB, RGB15(31, 31, 31)|BIT(15));
// -->
lcdMainOnBottom();
consoleSet(cSUB);
}
void consoleHide(enum console c) {
if(c & cMAIN) bgHide(console.bgId);
if(c & cSUB) bgHide(consoleSub.bgId);
}
void consoleShow(enum console c) {
if(c & cMAIN) bgShow(console.bgId);
if(c & cSUB) bgShow(consoleSub.bgId);
}
void consoleSet(enum console c) {
if(c & cMAIN) consoleSelect(&console);
if(c & cSUB) consoleSelect(&consoleSub);
}
void screenFill(u16 *buf, u16 color) {
u16 i;
for(i = 0; i < 256*192; i++) buf[i] = color;
}

16
arm9/src/video.h Executable file
View File

@ -0,0 +1,16 @@
#ifndef _VIDEO_H
#define _VIDEO_H
extern enum console {
cMAIN= 1<<0,
cSUB= 1<<1
};
void videoInit ();
void consoleHide (enum console c);
void consoleShow (enum console c);
void consoleSet (enum console c);
void screenFill (u16 *buf, u16 color);
#endif

80
hwinfo.py Normal file
View File

@ -0,0 +1,80 @@
import os, sys, struct, argparse, hashlib, hmac
def readle(b):
return int.from_bytes(b, 'little')
def readbe(b):
return int.from_bytes(b, 'big')
def hextobytes(s):
return bytes.fromhex(s)
# RSA keys, (retail, dev)
rsa_key_mod = (bytes.fromhex('BAF198A49F2E78F81DCBDCE57DB54FB77C6A158FA3F10DC19E1B95345CA6E714C93F44F04CD2D71F4E89EBEE2ED5BCFCA2E63F6B821D883C0098E5F67B7D217EDC77A1BBBD4C624D362BF2C6BE3D300E3ED3B8BE336047029E131D50C56EB67C195F968760915CAFDE38CA49F1D332DDA845D660FDCF4872E19CC5635488D5D7'), bytes.fromhex('E51CBFC7630B9DD166598D0F1AADB73A7DA8E94330B57017FF77A13E06F10856EBC63779DEC0CE485CBE81603D36FFBF9F97BFA43C98955C9CDA2BEB31BE72E3CAEBB2ECB0606A809446C6E0A47E52AD1A3F45906471B92020F7E7A3C9C85C9ECF5414F4B2921D61253631D81FC1FC009AC6EAE828F9CC73E738BF36F4A80FF9'))
rsa_key_priv = (None, bytes.fromhex('B57CC285E4F56CBC554116B62241FD64BDE9B16D620637972AECCEB35DB84D0CDD93949A5B538B9452B32DB4D888DAAA2677847D4AEAEB560381E74C55893163B4C5B95919A1CC46B571AFC176825BADB416B875BEF5A559CB3AE2C5784520F2C20674B151D94E904D2B7B85E481C30780A5941B239BAC7E5E8A16018B1EE569'))
def verify(consoleID, hwinfo, dev):
h = hashlib.sha1()
h.update(consoleID)
hmac_key = h.digest()
hm = hmac.new(key=hmac_key, digestmod=hashlib.sha1)
hm.update(hwinfo[0x88:0xA4])
hmac_digest = hm.digest()
dec = pow(readbe(hwinfo[:0x80]), 0x10001, readbe(rsa_key_mod[dev])).to_bytes(0x80, 'big')
if dec[-20:] == hmac_digest:
print('Signature is valid')
else:
print('Signature is invalid')
def resign(consoleID, hwinfo, dev):
if not dev:
print('Not supported for retail')
return
h = hashlib.sha1()
h.update(consoleID)
hmac_key = h.digest()
hm = hmac.new(key=hmac_key, digestmod=hashlib.sha1)
hm.update(hwinfo[0x88:0xA4])
hmac_digest = hm.digest()
hmac_digest_padded = b'\x00\x01' + b'\xff' * 105 + b'\x00' + hmac_digest
enc = pow(readbe(hmac_digest_padded), readbe(rsa_key_priv[dev]), readbe(rsa_key_mod[dev])).to_bytes(0x80, 'big')
enc += b'\x00' * (0x80 - len(enc))
out = 'HWINFO_S_resigned.dat'
with open(out, 'wb') as f:
f.write(enc)
f.write(hwinfo[0x80:])
print(f'Wrote to {out}')
parser = argparse.ArgumentParser()
parser.add_argument('--consoleid', required=True,
help='console id in hex')
parser.add_argument('--hwinfo', required=True,
help='path to the HWINFO_S.dat file')
parser.add_argument('mode', nargs=1,
help='verify or resign')
parser.add_argument('--dev', action='store_true',
help='use dev key')
args = parser.parse_args()
mode = args.mode[0]
if mode not in ['verify', 'resign']:
raise Exception('Invalid mode')
if args.dev:
dev = 1
else:
dev = 0
consoleID = hextobytes(args.consoleid)
with open(args.hwinfo, 'rb') as f:
hwinfo = f.read()
if mode == 'verify':
verify(consoleID, hwinfo, dev)
elif mode == 'resign':
resign(consoleID, hwinfo, dev)

BIN
icon.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
nitrofiles/import/dev/cert.sys Executable file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
"-"