An Emotional Roller Coaster on 2DS

I wasn’t prepared obviously!
on 2026-02-26, at 01:30 CET, it was a Thursday

Back to postsHiro Lynx by Muzz, facing you and pressing his handpaws beans on the inside of your screen

I bought a 2DS on the second-hand market, it’s in really good shape and seemed to work well! It came with a 4GB SD card with Mario Kart 7 on it, which was all official and sold with the console, I learned.

A black 2DS on the left, and its Pokémon-themed carrying case on the left
I had never bought a Nintendo console myself in my whole life actually

I also learned that the loss of the SD card or the reinitialization of the console would make me lose the ability to play this game on this console. Obviously such limitations shall not stand!

So I proceeded to install a custom firmware because, of course, that’s what I do. I used this guide recommended by my friends.

And then it didn’t want to boot, failing with a cryptic error. I was scared, did I just brick it?

The 2DS upper screen showing an error
Ominous

Not to worry though, there was a guide to work through this problem. It was more complicated and also seemed more dangerous. But I was reassured by the fact that:

  1. The custom bootloader was working and I could load and run an alternative firmware, GodMode9, which is a Swiss-army knife for making changes to the console’s NAND and extract data from it too;
  2. The guide immediately worked me through backing up the firmware that I was about to alter.

Unfortunately that didn’t work either. After basically overwriting the firmware with a known good version and clearing the settings, I was left with the same error than before. Quite depressing…

The same error than before but zoomed in with some motion blur applied
CTRNAND intensifies

But it was working fine before when I had just bought it, what is it that I did wrong? I double-checked steps in the guide and couldn’t find anything that I had missed.

I must mention than while testing to boot the console in roundabout ways, it did actually boot two times to the Home screen (I must have had tried to boot it more than 50 times…), which gave me some hope that I could make it work despite everything.

After reluctantly joining the appropriate Discord support server for troubleshooting, I searched for posts from people having the same problem as me. There were quite a few.

One of the things that knowledgeable people from the support server advised to do in this case was to run a firmware that tests the hardware. Easy enough! I put the 3ds_hw_test.firm on my SD card, asked Luma to launch it, and let it run for half-an-hour or so.

The 2DS showing on both screens a different log describing the tests that are currently being made
The bigger CPU gets the bigger screen

The results were grim. 41 errors! That seems like a lot… but at least that’s something actionable. Those errors were conveniently written to a log file on the SD card, which revealed that in fact the 41 errors were all related to one specific, litteral bit in FCRAM (Fast Cycle Random Access Memory) at address 0x20018350. That bit was toast, always zero.

...
[ARM11] Setting up Bit Fade Test...
[ARM11]
[ARM11] Testing FCRAM ...
[ARM11] Running Walking Ones Test...
[ARM11] Running Own Address Test...
[ARM11] 20018350: 20018350 -> 18350
[ARM11] Running Moving Inversions Test, Ones & Zeros...
[ARM9] Processing bit fade pattern 0 for ARM9 Internal Memory...
[ARM11] 20018350: ffffffff -> dfffffff
...
[ARM11] 20018350: ffffffff -> dfffffff
[ARM11] Processing bit fade pattern 0 for DSP Memory...
[ARM11] Processing bit fade pattern 0 for AXI Work RAM...
[ARM11] Running Moving inversions Test, 8 Bit Patterns...
[ARM9] Processing bit fade pattern ffffffff for ARM9 Internal Memory...
[ARM11] 20018350: fefefefe -> defefefe
...
[ARM11] 20018350: fdfdfdfd -> ddfdfdfd
...
[ARM11] 20018350: fbfbfbfb -> dbfbfbfb
...
[ARM11] 20018350: f7f7f7f7 -> d7f7f7f7
...
[ARM11] 20018350: efefefef -> cfefefef
...

The good people of the support server, when presented with my case, deemed it lost. I should try and refund the console.

But I bought it second hand with cash and it was working just fine when I got it? How was I supposed to ask for a refund if I had borked the console with a custom firmware. I couldn’t ask for a refund either if I restored Nintendo’s bootloader, which made the console work fine. Also uninstalling the custom firmware was very not recommended because it could brick the console, and the people on the support server wouldn’t advise to do it either, except as very last resort.

I still read the guide to uninstall the custom firmware and there was an interesting bit in there. There was a way to boot the vanilla firmware from GodMode9. It was recommended to do it once before overwriting Luma3DS’s bootloader with the original bootloader from Nintendo, as it was a way to confirm that the vanilla firmware was still working. There are many things you can do with a custom firmware and some of those will be frowned upon by the original firmware so hard that it would crash. And if you don’t have a custom bootloader anymore at that point, the console is doomed.

So I used this bit of information to extract the original stage-2 bootloader so Luma3DS could use it to boot into the original firmware. Yep, that worked! And reliably at that.

So Nintendo’s bootloader did something different from Luma3DS… and maybe I could coerce Luma3DS into doing things differently too.

After searching for my error message in Luma3DS’s source code, I found out that a few different things could trigger its display:

  1. Impossible to find the firmware in NAND or SD card;
  2. The firmware has an invalid header or size;
  3. The firmware’s hash is invalid.

I also found out that the firmware was loaded at address 0x20001000. Oh my. One byte of the firmware is stored in my bad memory address, and the whole firmware is then checked for corruption. That cannot possibly work!

I decided that maybe Nintendo didn’t bother checking the firmware for corruption after loading it and that the bad address was only affecting unimportant data like a texture or some text, and not binary code that could potentially run at some point.

So I removed the hash checks from Luma3DS, compiled it and put it on my SD card. It worked!

  sha(hash, (u8 *)firm + section->offset, section->size, SHA_256_MODE);

  /*if(memcmp(hash, section->hash, 0x20) != 0)
    return false;*/

But only for a short time. As I had cleared the settings, I now had to setup the console again, and that didn’t go well. The console, as soon as I set the time and date, rebooted me into the setup to start from the beginning, again and again, which was not getting me anywhere.

I decided that maybe I should restore the NAND from the backup I made earlier, using GodMode9 again. So I wouldn’t have to use that buggy setup program!

And that worked! Now I was booting into the Home screen! Ok so the bad bit was probably related to the setup program, I can live without that, I have a backup of the NAND if I ever need to fix things again.

the word FORESHADOWING in fait text

At this point, the console is in the state that it would have been in before the last section of the guide I initially followed to install the custom firmware. So I continued from there. And at some point the console stopped booting.

The bootloader was fine, but the firmware would not run. My bad memory address seemed to affect more than just the setup…

So I tried to restore the NAND from the backup, and I could boot again. This time I got a little further but not much. Games would crash, the console would stop booting, I had to restore the NAND several times. The game I managed to install from the homebrew channel didn’t work.

At some point the guide advises you to update the firmware while running the custom firmware, I tried a few times but every time I got an error.

And then suddenly, after fiddling some more, it became stable. What the hell!? I could install and run games! What a relief! But what happened?

After some research, I found that my bad FCRAM bit was still there, but also that one of my attempts at updating the firmware did actually work. The console was running the latest firmware now, despite every attempt having reportedly failed. Sooo… maybe the new firmware had a byte at the bad memory address that was not affected by the bit that was stuck to zero?

The system settings screen of the 2DS in French, showing firmware version Sys 11.17.0-50E
Error: Success!

And… most probably yeah. I tried booting with the original Luma3DS bootloader, that does the corruption check, and now that was working fine.

Well I count that as a win! I could finally play Rhythm Heaven Megamix, which was the whole point since the beginning! Yay!

But… Megamix was… not as good as I expected. The levels are short, they are too easy, there are no remixes but instead a shit-ton on dialogue, that is not inherently unpalatable, except for the fact that there is way too much of it!

I realized that what I really wanted was actually Rhythm Paradise, a DS game. That’s what I had watched being played on DaftHusky’s channel on Twitch and what I wanted to play. I thought Megamix would have the same levels along with new ones, but those were watered down. And the dialogue. Too much dialogue.

So I thought, no problem, I can just get that game and run it on the 2DS, it is retro-compatible!

Alas. To play DS games, the 2DS has to reboot in DS mode, which it does by, as I understand, using a different firmware. And that firmware doesn’t work.

Still the same CTRNAND error but fried
I have seen this CTRNAND error so many times by now

I got the same error message I had before, telling me the firmware was corrupted. And this time, not checking the hash didn’t do the trick, not even a little: the screen stayed black.

Damn, I’d have to make do with Megamix.

But I had been thinking. What if I tell Luma3DS to load the firmware somewhere else in FCRAM? Someone on the support server had suggested that it might be possible too. If Luma3DS loads the firmware at, say, 0x20020000, about 128k further than what it does by default, the firmware would be safe in healthy memory.

It couldn’t be that simple, even the person on the support server that had suggested that it might be possible seemed to imply that it was not that simple. But I knew where were the constants in Luma3DS and I could try, I had nothing to lose. Maybe the firmware is relocatable and is a modern OS with virtual memory and all the bells and whistles that would make its location in FCRAM irrelevant. Surely 128k of missing FCRAM would not bother the vast majority of the games.

So I changed the constants, recompiled, put the Luma3DS bootloader on the SD card.

-#define firm_addr       0x20001000
-#define firm_maxsize    0x07FFF000
+#define firm_addr       0x20020000
+#define firm_maxsize    0x07FE0000

And the console booted to the Home screen. I was surprised that it was so easy. 3DS games ran just fine… But the real test was to switch to DS mode. And would you look at that, it worked too!

I had a fully functionnal console! Yay! That is quite a relief, after quite a few unforeseen developments.

Ten-dez le cou! Quack Quack!

I can’t ignore the fact that all RAM is bound to fail eventually, and that one bit failing is a bad sign. It could mean that soon others will follow. Maybe it’s bad luck and the other ones are gonna be fine for many years, but it could also mean that my chip is from a bad batch of RAM that will fail sooner than expected.

So I gotta enjoy the console while it lasts. It’s really sad because the console is in really good shape except for that one bit of FCRAM that won’t switch, except very rarely. It could be possible to change the RAM chip but that would mean sourcing a good one, desoldering the old chip, and even more annoying, soldering the new chip.

I want to thank my friends Siph for providing advice on choosing which kind of console to buy, the guides to install the custom firmware and support when things went south, and Kooda who provided a few cartridges for testing purpose and sharing general knowledge about the DS consoles!

Back to posts