Flash image with firmaware version 2.10 flash.bin
Encoding: little
Info from Chris Windibank, 01/Mar/04
The firmware file is always 917,504 (0xe0000) bytes in length, presumably because it's a 1MB flash and the bootloader, backup firmware and settings area is 131,072 (0x20000) which leaves exacly that amount!
It is checksummed by the attached code. They decided to xor all bytes together and check it against a know value. I'm not really an expert in checksums so I can't say if this is unusual or not but it's certainly not a mainstream algorithm.
The first part of the file is the actual firmware in ARJ format using the -m1 compression mode and the archive consists of a single file named NML.MEM. The unused area after the firmware is zeroed and finally a signature and checksum is attached at the end. The signature is constant and the checksum can be calculated by xoring all the bytes in the file minus the checksum area and then xoring the result with 0xaabbbbaa.
The attached file can be used to generate a valid flash binary. I am a WindowsXP user and it was tested with Visual Studio 6.0 but it only contains C standard library calls so it should compile fine with GCC. It's usage is as follows:
mkflash outfile
- where outfile is the name of the output file (I use di604_firmware_ucl.bin)
It expects to find a file name NML.ARJ in the current directory (the first version took and input file but I got tired of typing it). I have been renaming zImage NML.MEM and then using the following to make the archive for mkflash.
arj32 a -m1 NML.ARJ NML.MEM
I'm not sure if the firmware checks to see if the arjed file in the archive is actually name NML.MEM but that is the way DLink does it so I did the same.
The bootloader expands the archived firmware sitting at 0x20000 in flash ROM to 0x20000000 in RAM. It then flips the RAM/ROM address spaces and jumps to address 0 which is the begginning of RAM after the flip.
At this point the router will start to execute zImage which is a compressed kernal and ramdisk. It should decompress the kernal and ramdisk and then jump to the entry point of the decompressed kernal. I chose to go with the compressed kernal because it's an easy way to get the ramdisk into memory from a single arjed archive. It may be better to try something different later when the kernal is actually working!
Address | Description | Calls | Called by |
0x0000 | Exceptions vectors. |
Reset/IRQ | - |
0x0048 - 0x007c | Switch between 0 address from Flash mem to SDRAM. |
- | 0x2E0 |
0x0080 - 0x0254 |
Initialisation:
Pass contorol to 0x500 |
0x500 | 0x0000 |
0x0268 - 0x0268 | Endless loop | - | 0x324 |
0x026C - 0x028C | read input value of the GPIO[6] (CONFIG RESET TO FACTORY). return 1 if input 0; |
- | 0x324 |
0x0290 - 0x02DC | XOR starting from start_addr(r0) for bytes(r1) length Return 0 if ==0xaabbbbaa else return -1 |
- | 0x324 |
0x02E0 - 0x320 | memcpy(src,dst,len) | - | 0x324 |
0x0324 - 0x03D0 | check SRC of the main firware (0x2000) check reset buttom load uncompression code to SDRAM if RESET load from 0x03000 if no RESET load from 0x20000 if uncompression return 1 - then switch memory and start from 0 |
- | 0x0874 |
0x03D4 - 0x042c | UART | - | 0x46C |
0x0430 - 0x043c | Read UART0 flag register for "receive FIFO full" | - | 0x46C |
0x0440 - 0x0468 | - | - | 0x46C |
0x046c - 0x04dc | no direct call | - | call itself ?? |
0x04E0 - 0x04fc | no direct call!! | - | - |
0x0500 - 0x0870 | Calculates relocated addreses Clean mem region 0x20380000(0x1477B bytes) memcpy(0x11a9,0x1145,0x24) copy to flash???? call 0x0874 |
indirect 0x0874 | 0x80 |
0x0874 - 0x09c0 | do nothing; call 0x324; call 0xA08; |
0x09c4 0x0e20 0x0db0 0x0324 0x0a08 | indirect call 0x0500 |
0x09C4 - 0x09E0 | void f_0x9c4() { f_0xebc(16,-1); return; } |
- | 0x0874 |
0x09E4 - 0x0A04 | - | - | - |
0x0A08 - 0x0AC8 | - | - | - |
0xacc - 0xae4 | - | - | no direct call |
0xae8 - 0xd24 | memcpy(src,dst,len) | - | indirect call 0x500 |
0xd28 - 0xdac | memset(addres, char , len) - set memory region with a specific byte | - | indirect call 0x500 |
0x0db0 - | return 20383884; | - | indirect call 0xEDC |
0x0ebc - 0x0ed8 | *20383884 = r0; return -1; |
- | 0x09c4 |
0x0edc - 0x0f20 | if(!f_0x00db0) return 20383964; else return f_0x0db0(); // 20383884 |
- | 0x0f24 |
0x0f24 - 0x0f44 | void f_0x0f24(word val){ { word *addr; addr = f_0x0edc(); if(addr!=0) *addr = val; return; } |
- | 0x0ebc |
0x0f48 - | - | - | - |
0x1134 | indirect function call (call addr in IP register | IP defined address | multiple |
0x1138 | function to do nothing???? OR first parameter to itself and exit. What the big deal?? | - | 0x0ebc |
- | - | - | - |
0x29C4 - 0x2F9C | last function | - | - |
indirectly called functions
0x4e0
0xacc - 0xae4
0xae8 - 0xd24
0xd28 - 0xdac