From 8084f01c55393a362313d0b52547f8d2d4e93057 Mon Sep 17 00:00:00 2001 From: Augusto Daniele <12686734+tasken@users.noreply.github.com> Date: Mon, 5 Oct 2026 00:35:30 -0300 Subject: [PATCH 1/2] Initialize flashcards on ARM7 first --- arm7/Makefile | 8 +- arm7/source/card_init.c | 151 ++++++++++++++++++++++++++++++++ arm7/source/card_init.h | 8 ++ arm7/source/main.c | 10 ++- arm9/source/driveOperations.cpp | 24 ++++- arm9/source/flashcard_init.h | 9 ++ arm9/source/read_card.c | 13 ++- arm9/source/read_card.h | 1 + 8 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 arm7/source/card_init.c create mode 100644 arm7/source/card_init.h create mode 100644 arm9/source/flashcard_init.h diff --git a/arm7/Makefile b/arm7/Makefile index ba531ad..2de8709 100644 --- a/arm7/Makefile +++ b/arm7/Makefile @@ -20,7 +20,7 @@ include $(DEVKITARM)/ds_rules TARGET := GodMode9i BUILD := build SOURCES := source -INCLUDES := include build +INCLUDES := include build ../arm9/source #--------------------------------------------------------------------------------- # options for code generation @@ -50,6 +50,8 @@ LIBS := -lnds7 #--------------------------------------------------------------------------------- LIBDIRS := $(LIBNDS) +vpath encryption.c $(SHARED_ARM9_SOURCE) +vpath tonccpy.c $(SHARED_BOOTLOADER_SOURCE) #--------------------------------------------------------------------------------- # no real need to edit anything past this point unless you need to add additional @@ -59,8 +61,10 @@ ifneq ($(BUILD),$(notdir $(CURDIR))) #--------------------------------------------------------------------------------- export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) +export SHARED_ARM9_SOURCE := $(CURDIR)/../arm9/source +export SHARED_BOOTLOADER_SOURCE := $(CURDIR)/../bootloader/source -CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) encryption.c tonccpy.c CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) diff --git a/arm7/source/card_init.c b/arm7/source/card_init.c new file mode 100644 index 0000000..b6539d0 --- /dev/null +++ b/arm7/source/card_init.c @@ -0,0 +1,151 @@ +/* + NitroHax -- Cheat tool for the Nintendo DS + Copyright (C) 2008 Michael "Chishm" Chisholm + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +*/ + +#include "card_init.h" +#include "encryption.h" +#include "flashcard_init.h" +#include +#include + +static struct { + u32 iii, jjj, kkkkk, llll, mmm, nnn; +} key1data; + +static void initKey1Command(u8* command) { + key1data.iii = rand() & 0xFFF; + key1data.jjj = rand() & 0xFFF; + key1data.kkkkk = rand() & 0xFFFFF; + key1data.llll = rand() & 0xFFFF; + key1data.mmm = rand() & 0xFFF; + key1data.nnn = rand() & 0xFFF; + + command[7] = CARD_CMD_ACTIVATE_BF; + command[6] = key1data.iii >> 4; + command[5] = (key1data.iii << 4) | (key1data.jjj >> 8); + command[4] = key1data.jjj; + command[3] = key1data.kkkkk >> 16; + command[2] = key1data.kkkkk >> 8; + command[1] = key1data.kkkkk; + command[0] = rand(); +} + +static void encryptedCommand(u8 code, u8* command, u32 block) { + u32 iii = key1data.iii; + u32 jjj = key1data.jjj; + if (code != CARD_CMD_SECURE_READ) + block = key1data.llll; + if (code == CARD_CMD_ACTIVATE_SEC) { + iii = key1data.mmm; + jjj = key1data.nnn; + } + + command[7] = code | (block >> 12); + command[6] = block >> 4; + command[5] = (block << 4) | (iii >> 8); + command[4] = iii; + command[3] = jjj >> 4; + command[2] = (jjj << 4) | (key1data.kkkkk >> 16); + command[1] = key1data.kkkkk >> 8; + command[0] = key1data.kkkkk; + crypt_64bit_up((u32*)command); + key1data.kkkkk++; +} + +static void cardDelay(u16 timeout) { + TIMER_DATA(0) = 0 - ((timeout & 0x3FFF) + 3); + TIMER_CR(0) = TIMER_DIV_256 | TIMER_ENABLE; + while (TIMER_DATA(0) != 0xFFFF); + TIMER_CR(0) = 0; + TIMER_DATA(0) = 0; +} + +u32 initFlashcardArm7(void) { + static const u8 seedBytes[] = {0xE8, 0x4D, 0x5A, 0xB1, 0x17, 0x8F, 0x99, 0xD5}; + static u32 headerData[0x200 / sizeof(u32)]; + tNDSHeader* header = (tNDSHeader*)headerData; + u8 command[8] __attribute__((aligned(4))); + + cardReadHeader((u8*)headerData); + if (header->headerCRC16 != swiCRC16(0xFFFF, headerData, 0x15E)) + return FLASHCARD_INIT_BAD_HEADER; + + u32 chipId = cardReadID(CARD_CLK_SLOW); + while (REG_ROMCTRL & CARD_BUSY); + bool normalChip = (chipId & BIT(31)) != 0; + u32 gameCode; + memcpy(&gameCode, header->gameCode, sizeof(gameCode)); + init_keycode(gameCode, 2, 8, NTR_CARD_KEY); + + u32 key1Flags = CARD_ACTIVATE | CARD_nRESET | + (header->cardControl13 & (CARD_WR | CARD_CLK_SLOW)) | + ((header->cardControlBF & (CARD_CLK_SLOW | CARD_DELAY1(0x1FFF))) + + ((header->cardControlBF & CARD_DELAY2(0x3F)) >> 16)); + if (!normalChip) + key1Flags |= CARD_SEC_LARGE; + + initKey1Command(command); + cardPolledTransfer((header->cardControl13 & (CARD_WR | CARD_nRESET | CARD_CLK_SLOW)) | + CARD_ACTIVATE, NULL, 0, command); + encryptedCommand(CARD_CMD_ACTIVATE_SEC, command, 0); + if (normalChip) { + cardPolledTransfer(key1Flags, NULL, 0, command); + cardDelay(header->readTimeout); + } + cardPolledTransfer(key1Flags, NULL, 0, command); + + REG_ROMCTRL = 0; + REG_CARD_1B0 = seedBytes[header->deviceType & 7] | + (key1data.nnn << 15) | (key1data.mmm << 27) | 0x6000; + REG_CARD_1B4 = 0x879B9B05; + REG_CARD_1B8 = key1data.mmm >> 5; + REG_CARD_1BA = 0x5C; + REG_ROMCTRL = CARD_nRESET | CARD_SEC_SEED | CARD_SEC_EN | CARD_SEC_DAT; + key1Flags |= CARD_SEC_EN | CARD_SEC_DAT; + + encryptedCommand(CARD_CMD_SECURE_CHIPID, command, 0); + if (normalChip) { + cardPolledTransfer(key1Flags, NULL, 0, command); + cardDelay(header->readTimeout); + } + cardPolledTransfer(key1Flags | CARD_BLK_SIZE(7), NULL, 0, command); + + u32 secureFlags = (header->cardControlBF & + (CARD_CLK_SLOW | CARD_DELAY1(0x1FFF) | CARD_DELAY2(0x3F))) | + CARD_ACTIVATE | CARD_nRESET | CARD_SEC_EN | CARD_SEC_DAT; + for (int block = 4; block < 8; block++) { + encryptedCommand(CARD_CMD_SECURE_READ, command, block); + if (normalChip) { + cardPolledTransfer(secureFlags, NULL, 0, command); + cardDelay(header->readTimeout); + for (int i = 0; i < 8; i++) + cardPolledTransfer(secureFlags | CARD_BLK_SIZE(1), NULL, 0, command); + } else { + cardPolledTransfer(secureFlags | CARD_BLK_SIZE(4) | CARD_SEC_LARGE, + NULL, 0, command); + } + } + + encryptedCommand(CARD_CMD_DATA_MODE, command, 0); + if (normalChip) { + cardPolledTransfer(key1Flags, NULL, 0, command); + cardDelay(header->readTimeout); + } + cardPolledTransfer(key1Flags, NULL, 0, command); + return FLASHCARD_INIT_OK; +} diff --git a/arm7/source/card_init.h b/arm7/source/card_init.h new file mode 100644 index 0000000..a0eed1f --- /dev/null +++ b/arm7/source/card_init.h @@ -0,0 +1,8 @@ +#ifndef ARM7_CARD_INIT_H +#define ARM7_CARD_INIT_H + +#include + +u32 initFlashcardArm7(void); + +#endif diff --git a/arm7/source/main.c b/arm7/source/main.c index aa85bde..3769d59 100644 --- a/arm7/source/main.c +++ b/arm7/source/main.c @@ -31,6 +31,8 @@ #include #include "gba.h" +#include "card_init.h" +#include "flashcard_init.h" #define SD_IRQ_STATUS (*(vu32*)0x400481C) @@ -262,6 +264,13 @@ int main() { } resyncClock(); + if (fifoCheckValue32(FLASHCARD_INIT_CHANNEL)) { + u32 command = fifoGetValue32(FLASHCARD_INIT_CHANNEL); + if (command == FLASHCARD_INIT_REQUEST) { + fifoSendValue32(FLASHCARD_INIT_CHANNEL, initFlashcardArm7()); + } + } + // Send SD status if(isDSiMode() || *(u16*)(0x4004700) != 0) fifoSendValue32(FIFO_USER_04, SD_IRQ_STATUS); @@ -282,4 +291,3 @@ int main() { } return 0; } - diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp index dfc9830..b8a4209 100644 --- a/arm9/source/driveOperations.cpp +++ b/arm9/source/driveOperations.cpp @@ -25,6 +25,7 @@ #include "io_sc_common.h" #include "exptools.h" #include "read_card.h" +#include "flashcard_init.h" static sNDSHeader nds; @@ -421,6 +422,23 @@ TWL_CODE bool twl_flashcardMount(void) { slot1Enabled = true; } + // Follow HBMenu Deluxe's ARM7-first initialization order, without a later slot reset: + // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm7/source/main.c + // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm9/include/read_card.c + sysSetCardOwner(BUS_OWNER_ARM7); + if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) { + sysSetCardOwner(BUS_OWNER_ARM9); + printf("Failed to request ARM7 card initialization.\n"); + return false; + } + fifoWaitValue32(FLASHCARD_INIT_CHANNEL); + u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL); + sysSetCardOwner(BUS_OWNER_ARM9); + if (cardInitResult != FLASHCARD_INIT_OK) { + printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult); + return false; + } + nds.gameCode[0] = 0; nds.gameTitle[0] = 0; char gamename[13]; @@ -467,7 +485,11 @@ TWL_CODE bool twl_flashcardMount(void) { }*/ if (doCardInit) { - cardInit((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work. + int result = cardInitWithoutSlotReset((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work. + if (result != 0) { + printf("ARM9 card initialization failed: %d\n", result); + return false; + } for (int i = 0; i < 30; i++) swiWaitForVBlank(); if (isDSPico) picoInit(false); } diff --git a/arm9/source/flashcard_init.h b/arm9/source/flashcard_init.h new file mode 100644 index 0000000..2d00b47 --- /dev/null +++ b/arm9/source/flashcard_init.h @@ -0,0 +1,9 @@ +#ifndef FLASHCARD_INIT_H +#define FLASHCARD_INIT_H + +#define FLASHCARD_INIT_CHANNEL FIFO_USER_03 +#define FLASHCARD_INIT_REQUEST 0x54494E49 // 'INIT' +#define FLASHCARD_INIT_OK 0 +#define FLASHCARD_INIT_BAD_HEADER 0x16 + +#endif diff --git a/arm9/source/read_card.c b/arm9/source/read_card.c index f72ebb9..7f527ce 100644 --- a/arm9/source/read_card.c +++ b/arm9/source/read_card.c @@ -337,7 +337,7 @@ void cardTwlBlowfishInit(sNDSHeaderExt* ndsHeader) { } -int cardInit (sNDSHeaderExt* ndsHeader) +static int cardInitInternal (sNDSHeaderExt* ndsHeader, bool resetSlot) { u32 portFlagsKey1, portFlagsSecRead; normalChip = false; // As defined by GBAtek, normal chip secure area and header are accessed in blocks of 0x200, other chip in blocks of 0x1000 @@ -351,7 +351,8 @@ int cardInit (sNDSHeaderExt* ndsHeader) twlBlowfish = false; sysSetCardOwner (BUS_OWNER_ARM9); // Allow arm9 to access NDS cart - cardDSiSlot1Reset(); + if (resetSlot) + cardDSiSlot1Reset(); REG_ROMCTRL=0; REG_AUXSPICNT=0; @@ -520,6 +521,14 @@ int cardInit (sNDSHeaderExt* ndsHeader) return ERR_NONE; } +int cardInit (sNDSHeaderExt* ndsHeader) { + return cardInitInternal(ndsHeader, true); +} + +int cardInitWithoutSlotReset (sNDSHeaderExt* ndsHeader) { + return cardInitInternal(ndsHeader, false); +} + u32 cardGetId() { return iCardId; } diff --git a/arm9/source/read_card.h b/arm9/source/read_card.h index 9fbb615..e6f4fc3 100644 --- a/arm9/source/read_card.h +++ b/arm9/source/read_card.h @@ -42,6 +42,7 @@ extern u32 cardNandRomEnd; extern u32 cardNandRwStart; int cardInit (sNDSHeaderExt* ndsHeader); +int cardInitWithoutSlotReset (sNDSHeaderExt* ndsHeader); void cardRead (u32 src, void* dest, bool nandSave); From 793feb18e5e40ff74a10833782662a96e14feb59 Mon Sep 17 00:00:00 2001 From: Augusto Daniele <12686734+tasken@users.noreply.github.com> Date: Mon, 5 Oct 2026 01:30:43 -0300 Subject: [PATCH 2/2] Only use ARM7 card setup when the SD card was mounted --- arm9/source/driveOperations.cpp | 42 ++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/arm9/source/driveOperations.cpp b/arm9/source/driveOperations.cpp index b8a4209..b0f4f57 100644 --- a/arm9/source/driveOperations.cpp +++ b/arm9/source/driveOperations.cpp @@ -29,6 +29,8 @@ static sNDSHeader nds; +static bool sdMountAttempted = false; + static bool slot1Enabled = true; bool nandMounted = false; @@ -167,6 +169,7 @@ void nandUnmount(void) { } bool sdMount(void) { + sdMountAttempted = true; fatMountSimple("sd", __my_io_dsisd()); if (sdFound()) { sdMountedDone = true; @@ -425,18 +428,20 @@ TWL_CODE bool twl_flashcardMount(void) { // Follow HBMenu Deluxe's ARM7-first initialization order, without a later slot reset: // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm7/source/main.c // https://github.com/ApacheThunder/ntr-hb-menu/blob/deluxe/arm9/include/read_card.c - sysSetCardOwner(BUS_OWNER_ARM7); - if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) { + if (sdMountAttempted) { + sysSetCardOwner(BUS_OWNER_ARM7); + if (!fifoSendValue32(FLASHCARD_INIT_CHANNEL, FLASHCARD_INIT_REQUEST)) { + sysSetCardOwner(BUS_OWNER_ARM9); + printf("Failed to request ARM7 card initialization.\n"); + return false; + } + fifoWaitValue32(FLASHCARD_INIT_CHANNEL); + u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL); sysSetCardOwner(BUS_OWNER_ARM9); - printf("Failed to request ARM7 card initialization.\n"); - return false; - } - fifoWaitValue32(FLASHCARD_INIT_CHANNEL); - u32 cardInitResult = fifoGetValue32(FLASHCARD_INIT_CHANNEL); - sysSetCardOwner(BUS_OWNER_ARM9); - if (cardInitResult != FLASHCARD_INIT_OK) { - printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult); - return false; + if (cardInitResult != FLASHCARD_INIT_OK) { + printf("ARM7 card initialization failed: %lu\n", (unsigned long)cardInitResult); + return false; + } } nds.gameCode[0] = 0; @@ -484,11 +489,16 @@ TWL_CODE bool twl_flashcardMount(void) { fatMountSimple("fat", dldiGet()); }*/ - if (doCardInit) { - int result = cardInitWithoutSlotReset((sNDSHeaderExt*)((u32*)0x02FFC000)); // Certain flashcarts require card init before DLDI will work. - if (result != 0) { - printf("ARM9 card initialization failed: %d\n", result); - return false; + if (doCardInit) { // Certain flashcarts require card init before DLDI will work. + sNDSHeaderExt* cardHeader = (sNDSHeaderExt*)((u32*)0x02FFC000); + if (sdMountAttempted) { + int result = cardInitWithoutSlotReset(cardHeader); + if (result != 0) { + printf("ARM9 card initialization failed: %d\n", result); + return false; + } + } else { + cardInit(cardHeader); } for (int i = 0; i < 30; i++) swiWaitForVBlank(); if (isDSPico) picoInit(false);