Unmasking the NETGEAR WGR614L Bootloader and Flash

After a week or so of hacking the WGR614L, I thought it would be a good idea to share some of the information that I have learned along the way. This is by no means a definitive guide; rather, it is more of a collection of some interesting facts and background about the hardware and the bootloader, as well as providing some assistance with using my improved flash utility for the router.

Checksums

Before the bootloader (which is a modified version of CFE) will execute an image out of flash, it first verifies a checksum of that image. The checksum is performed over a variable number of bytes starting at KERNEL_FLASH_ADDR (0xBC020000). The number of bytes, and the expected checksum, are stored in a non-standard location in flash. The boot loader loads both of these values, and then calculates the checksum over the specified number of bytes. If the checksum calculated by the bootloader and the checksum stored in flash are equal, then the image will be booted.

Generally, Broadcom based routers use a TRX header located at the KERNEL_FLASH_ADDR. The TRX header, shown below, contains a length and a CRC. It also contains three offsets used by the kernel to work out where itself and its filing systems are. The TRX also contains a CRC which ensures integrity of the image. The Linux kernel understands the TRX header, and how to update the checksum (if for example it changes the offsets).

typedef struct {
uint32_t magic;         /* HDR0 */
uint32_t length;
uint32_t crc32;
uint32_t flag_version;
uint32_t offsets[TRX_MAX_OFFSET];
} trx_t;

The WGR614L, however, does not use this method. On the WGR614L, the checksum and length is stored outside of the TRX. This means that standard tools, such as mtd, used to flash the router will not work; as the checksum is not updated.

Fear not, as there is a solution: My Flash Utility for the WGR614L. This allows you to modify the checksum values. It also has a nice "fudge" that sets the checksum length to 4, the magic of the TRX which is always HDR0; this essentially disables the check. Then, you can use the standard tools without any problem!

To upgrade, there is a two step process. First, disable the checksums, then reflash:

$ ngr-flash -a
$ mtd -r write your-new-image.trx linux 

Booting

After checking the checksum, the bootloader will decompress an LZMA image located in the first part of the TRX. Now, the bootloader makes some assumptions about the image that it's going to load. To save you the trouble, here is the code:

static int boot_lzma(void)
{
unsigned long in        = KERNEL_FLASH_ADDR + sizeof(struct trx_header);
unsigned long out       = KERNEL_RAM_ADDR;
unsigned long in_len    = 0x3A0000;
unsigned long out_len   = 0x3A0000;
unsigned long start_addr = 0x80001400;  /* addr for 'kernel_entry' */
if (!lzma_decode((unsigned char *)in, (unsigned char *)out,
(unsigned int )in_len, (unsigned int *)&out_len))
cfe_start(start_addr);
printf("LZMA boot failed\n");
return 0;
}

Now the main "gotcha" here is that the start_addr is fixed to 0x80001400! (There is another in that the length is fixed). So, after decompressing the LZMA image into RAM, the bootloader will jump to start_addr. This makes the assumption, as the comment says, that the kernel_entry is at that address; well, it doesn't have to be, it just happened to be on the 2.4 kernel that NETGEAR ships with the device.

My first few days were spent trying to figure out why my new shiny kernel would not boot. The answer was that the boot loader was jumping into a random location, not the kernel_entry as it should. It's worth pointing out that - located at the start of the Linux kernel, in memory (KERNEL_RAM_ADDR or 0x80001000), is a jump instruction to the real location of kernel_entry. Thus, a bootloader should just jump to this first address.

As such, f you're building a kernel, I suggest you check your System.map for the location of kernel_entry. If it's not been booting, there is a good chance you had the same problem that I did! For those running OpenWRT, you may find the thread on Booting OpenWRT - From the correct address interesting reading.

CHK Files

Finally it's worth mentioning a little about CHK files. I suggest reading Nachi's WGR614L Firmware Image Creation. A CHK file glues together a kernel image, and root fs, along with a header, into a single file.

The CHK file must match the "board_id" of your router. The accepted value is "U12H072T00_NETGEAR". The bootloader will verify this value when you tftp an image onto the router. The bootloader gets its board_id value from flash, and my Flash Utility for the WGR614L can help you read it. The following CFE command can also be used to reset it.

CFE> setenv BOARD_ID U12H072T00_NETGEAR

The bootloader also stores a copy of the kernel checksum and length into flash. This is used as described above for checking the flash has a valid image.

One final thing worth mentioning is the mkchkimg tool. This works a little like the "packet" tool. It will take a TRX and fs_image and create a CHK file. I created it after I found that the packet tool I had was appending the fs_image before the kernel image, you can guess how well that worked!

Final Words

I would like to thank NETGEAR for their efforts with the WGR614L! There are areas for improvement, but this router is an excellent step into the world of open source networking. Please feel free to comment if you have any questions or input!

MikeK
MikeK's picture
Dave,

Dave,

Thank you,

Good article

MikeK