DS Firmware Information

The firmware is contained in a SPI data flash chip (usually 256 KB). It contains the menu and pictochat. The encrypted firmware code is decrypted by the BIOS as it gets copied to RAM. The first 64 KB of firmware on all 256 KB model DSes is write protected by SL1, although Nintendo was dumb enough to not fit a working system in 64 KB, allowing things like the DS bricker to occur. However, a FlashMe'd DS contains a recovery routine that prevents total bricking.

There are at least five revisions of the firmware, three older versions that work with passme/wifime and changed small details, and two newer versions that do not work immediately. PassMe2 is currently the only way to run code on a DS with the newer firmware. There is now a new version of FlashMe for the new-type firmware, but it still requires a PassMe2 to install.

Known versions (based on checksum at offset 0x6 in the firmware):

  • v1 (checksum 0x2C7A, Freezes on removal)
  • v2 (checksum 0xE0CE, Grayish Blue)
  • v3 (checksum 0xBFBA, Dark Green (00,15,00))
  • v4 (checksum 0xDFC7, %bgcolor=#FFFF00 color=black% (31,31,0))
  • v5 (checksum unknown, Magenta)
  • DS Lite / v6 (checksum 0xE843, %color=black bgcolor=#DE00EE% (27,0,29))
  • iQue (checksum 0xF96D, Dark Green (00,15,00))
  • Flashme (Dark Green, based on v3 firmware)

v1-v3 are the original firmware supported by old PassMe/WifiMe
v4 is the firmware of recent DSes that blocks old PassMe and current WifiMe
v5 has been spotted but not dumped yet, also blocks
v6 / ds-lite works with passme 2

To test your firmware version without using a flash cartridge or passme, you can use the color trick discovered by MaHe. This can be invoked by ejecting a game from pictochat, or by waving a magnet near the start+select buttons on a DS (to simulate hinge close). If you get yellow or magenta, you probably need a PassMe2. If you get any other colors besides those listed above, please let us know.

The firmware on the iQue DS is 512 KB and includes additional range-checking code to prevent passme. The other range-checked firmwares (v4) are still 256 KB and was first found in the Japanese red DS, although it is present in all newly released DSes (as of writing), including the pink/powder blue nintendogs bundles, the teal DS in all regions, and newer blue DSes. For more information on the spread of the new firmware (and to add your serial number), see the DS firmware tracker. It appears that newly purchased Silver DSes, DSes returned for repair, and refurbished DSes also contain the new firmware.

The user settings are also stored in the firmware.

FlashMe versioning

FlashMe v1 to v4 do not work with WFC games, as the WFC settings data overwrites part of FlashMe's extension code. The recovery routine is still intact, so an affected DS can be reflashed with a GBA cartridge.

  • 0x17C:16 = 1 for FlashMe v1..v4
  • 0x17C:16 = 2 for FlashMe v5+
    • 0x3F7FC:16 + 3 = 5 for FlashMe v5, 6 for FlashMe v6

Firmware header

FW offset Size Description
0x00000 2 Part 3 source / 8
0x00002 2 Part 4 source / 8
0x00004 2 CRC-16 of parts 3 and 4
0x00006 2 CRC-16 of parts 1 and 2
0x00008 4 "MACP" or "MACh"
0x0000C 2 Part 1 source info
0x0000E 2 Part 1 destination info
0x00010 2 Part 2 source info
0x00012 2 Part 2 destination info
0x00014 2 Part 1+2 shift data
0x00016 2 Part 5 source / 8
0x00018 8 ?? ?? ?? ??
0x0001C 4 ?? ?? FF FF
0x00020 2 User settings offset / 8
0x00022 2 ?? ??
0x00024 2 ?? ??
0x00026 2 CRC16 of part 5
0x00028 2 ?? ??
0x0002A 2 CRC16 of remaining header
0x0002C 2 Size of remaining header
0x0002E 2 ?? ??
0x00030 6 ? 00 00 00 00 00 00 ?
0x00036 6 MAC address
0x0003C 4 Enabled channels (FE 3F FF FF)
0x00040 1 RF init flag (picks modes, 00, 02 spotted)
0x00041 1 Bits per RF init transfer
0x00042 1 Number of RF init entries
0x00043 1 ? 01 ?
0x00044 2 Wifi init 0x146
0x00046 2 Wifi init 0x148
0x00048 2 Wifi init 0x14A
0x0004A 2 Wifi init 0x14C
0x0004C 2 Wifi init 0x120
0x0004E 2 Wifi init 0x122
0x00050 2 Wifi init 0x154
0x00052 2 Wifi init 0x144
0x00054 2 Wifi init 0x130
0x00056 2 Wifi init 0x132
0x00058 2 Wifi init 0x140
0x0005A 2 Wifi init 0x142
0x0005C 2 Wifi init 0x038
0x0005E 2 Wifi init 0x124
0x00060 2 Wifi init 0x128
0x00062 2 Wifi init 0x150
0x00064 105 Baseband init data
0x000CE 36 (Num Bits+7)/8*(Num Entries) of RF init data
0x000F2 6 Channel 1 RF data
0x000F8 6 Channel 2 RF data
0x000FE 6 Channel 3 RF data
0x00104 6 Channel 4 RF data
0x0010A 6 Channel 5 RF data
0x00110 6 Channel 6 RF data
0x00116 6 Channel 7 RF data
0x0011C 6 Channel 8 RF data
0x00122 6 Channel 9 RF data
0x00128 6 Channel 10 RF data
0x0012E 6 Channel 11 RF data
0x00134 6 Channel 12 RF data
0x0013A 6 Channel 13 RF data
0x00140 6 Channel 14 RF data
0x00146 14 Channel 1..14 BB data
0x00154 14 ? (each 0x10)
0x00162 1 ? (0x1C)
0x00163 157 0xFF padding

Notes:

  • Part 1 (ARM 9 boot firmware)
    • Source = sourceInfo * (4 << ((shiftData>>0) & 7))
    • Destination = 0x02800000-destInfo * (4 << ((shiftData>>3) & 7))
  • Part 2 (ARM 7 boot firmware)
    • Source = sourceInfo * (4 << ((shiftData>>6) & 7))
    • Destination = (shiftData & 0x1000 ? 0x02800000 : 0x03810000) - destInfo*(4 << ((shiftData>>9) & 7))
  • Part 3 is the ARM 9 secondary firmware portion.
  • Part 4 is the ARM 7 secondary firmware portion.
  • Part 5 is language data, etc..., loaded by the ARM9.
  • In the US, you should only use channels 1 .. 13
  • CRC16 of remaining header has an initial value of 0, starting at 0x2C and continuing on to the end of the remaining header
  • size of remaining header is size including the 0x2A/0x2C fields, i.e. (0x138 means 0x2A+0x138=0x162 is the final byte, and 0x163 to 0x1FF is FF filled)

See for information on the custom firmware project.

For most purposes, you do not need to read the firmware, as the BIOS leaves the important settings in memory for you (as discussed above). However, initializing the Wireless system requires reading a number of configuration variables from the firmware. So, here is code to read it (will only operate on the ARM7):

#define FW_READ_ID 0x9F
#define FW_READ 0x03
#define FW_READ_STATUS 0x05

void readFirmware(uint32 address, uint32 size, uint8 * buffer) {
uint32 index;

// Read command
while (SERIAL_CR & SPI_BUSY);
SERIAL_CR = SPI_ENABLE | SPI_NOT_LAST | SPI_DEVICE_FIRMWARE;
SERIAL_DATA = FW_READ;
while (SERIAL_CR & SPI_BUSY);

// Set the address
SERIAL_DATA = (address>>16) & 0xFF;
while (SERIAL_CR & SPI_BUSY);
SERIAL_DATA = (address>>8) & 0xFF;
while (SERIAL_CR & SPI_BUSY);
SERIAL_DATA = (address) & 0xFF;
while (SERIAL_CR & SPI_BUSY);

for (index = 0; index < size; index++) {
SERIAL_DATA = 0;
while (SERIAL_CR & SPI_BUSY);
buffer[index] = SERIAL_DATA & 0xFF;
}
SERIAL_CR = 0;
}

// Returns a word containing information about the firmware chip:
// byte 0: Manuf. ID (Sanyo: 0x62, ST: 0x20)
// byte 1: Device ID (Sanyo: 0x42?, ST: 0x40)
// byte 2: Device capacity (only on ST chips, 0x12)
// byte 3: status register (contains two bits specifying protection size)
uint32 getFirmwareType(void) {
uint32 result;

// Get ID
while (SERIAL_CR & SPI_BUSY);
SERIAL_CR = SPI_ENABLE | SPI_NOT_LAST | SPI_DEVICE_FIRMWARE;
SERIAL_DATA = FW_READ_ID;
while (SERIAL_CR & SPI_BUSY);

result = 0;
for (int i = 0; i < 3; i++) {
SERIAL_DATA = 0;
while (SERIAL_CR & SPI_BUSY);
result = (SERIAL_DATA & 0xFF) | (result<<8);
}

// Get status
while (SERIAL_CR & SPI_BUSY);
SERIAL_CR = SPI_ENABLE | SPI_NOT_LAST | SPI_DEVICE_FIRMWARE;
SERIAL_DATA = FW_READ_STATUS;
while (SERIAL_CR & SPI_BUSY);

SERIAL_DATA = 0;
while (SERIAL_CR & SPI_BUSY);
result = ((SERIAL_DATA & 0xFF) << 24) | result;
}