Adding OpenWrt Support for Netgear RAX120 (Nighthawk AX12)

I figured it wasn't that easy :slight_smile:.

I was just rebasing under the -pr branch last night and finalizing the led / button stuff so I just haven't had a chance to dig into it much. -pr is running good though.

I dont think there is help in the tool so, command is just:
aq-fw-download fw-path eth-name phy-addr

1 Like
  • Flashing steps / Wiki updates
    I believe @wangyu is working on this

I have added the steps of building and using the firmware onto the rax120 wiki.

At the moment, there is not really steps for "flashing", we only have steps of running the firmware as initramfs from a usb stick.

Some info might be helpful, here is the output of binwalk of stock image:

root@zong:/# binwalk RAX120-V1.2.3.28.img

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
128           0x80            device tree image (dtb)
360           0x168           gzip compressed data, maximum compression, has original file name: "Image", from Unix, last modified: 2021-09-22 04:08:18
4905536       0x4ADA40        device tree image (dtb)
4986596       0x4C16E4        device tree image (dtb)
5071104       0x4D6100        device tree image (dtb)
5151500       0x4E9B0C        device tree image (dtb)
5231144       0x4FD228        device tree image (dtb)
5312008       0x510E08        device tree image (dtb)
5396504       0x525818        device tree image (dtb)
5475892       0x538E34        device tree image (dtb)
5557188       0x54CBC4        device tree image (dtb)
5638248       0x560868        device tree image (dtb)
5718912       0x574380        device tree image (dtb)
5804320       0x589120        device tree image (dtb)
5888352       0x59D960        device tree image (dtb)
5968144       0x5B1110        device tree image (dtb)
6053416       0x5C5E28        device tree image (dtb)
6133028       0x5D9524        device tree image (dtb)
6212820       0x5ECCD4        device tree image (dtb)
6297332       0x6016F4        device tree image (dtb)
6422592       0x620040        uImage header, header size: 64 bytes, header CRC: 0xB72F253C, created: 2021-09-22 04:12:54, image size: 56625152 bytes, Data Address: 0x40908000, Entry Point: 0x40908000, data CRC: 0x8C7F2917, OS: Linux, image type: OS Kernel Image, compression type: lzma, image name: "Linux-4.4.60"
6422656       0x620080        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 56614315 bytes, 4587 inodes, blocksize: 262144 bytes, created: 2021-09-22 04:12:38

Here is the dumpimage of stock firmware after removed a 128 byte header:

root@zong:/# dd if=./RAX120-V1.2.3.28.img of=./stock.img.128byte_header_removed bs=128 skip=1
492560+1 records in
492560+1 records out
63047681 bytes (63 MB, 60 MiB) copied, 3.1987 s, 19.7 MB/s

root@zong:/# dumpimage -l stock.img.128byte_header_removed
FIT description: ARM64 OpenWrt FIT (Flattened Image Tree)
Created:         Wed Sep 22 04:12:03 2021
 Image 0 (kernel@1)
  Description:  ARM64 OpenWrt Linux-4.4.60
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Kernel Image
  Compression:  gzip compressed
  Data Size:    4904846 Bytes = 4789.89 KiB = 4.68 MiB
  Architecture: AArch64
  OS:           Linux
  Load Address: 0x41080000
  Entry Point:  0x41080000
  Hash algo:    crc32
  Hash value:   ea7cd3eb
  Hash algo:    sha1
  Hash value:   f36cb32b5310a923d0ec4773aabc8894053c8c32
 Image 1 (fdt@hk09)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80781 Bytes = 78.89 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   a07c0e09
  Hash algo:    sha1
  Hash value:   8adc0e81f4c6f26f4041c1951bfb0efafb0b1663
 Image 2 (fdt@oak02)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    84232 Bytes = 82.26 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   23ccc53e
  Hash algo:    sha1
  Hash value:   bf2011c4e904cb54451fb3b6147de719d51280b6
 Image 3 (fdt@ac01)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80120 Bytes = 78.24 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   f56603db
  Hash algo:    sha1
  Hash value:   f162fe78f771a3dce1d0e0f7349ed06dcee041eb
 Image 4 (fdt@db.hk01)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    79368 Bytes = 77.51 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   9871cd43
  Hash algo:    sha1
  Hash value:   02a7cd8fdd564b676fdcbcc7316e71775c83071a
 Image 5 (fdt@hk02)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80586 Bytes = 78.70 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   3c46cf7d
  Hash algo:    sha1
  Hash value:   572a13380dcd98839930813f932cbd1d0e5c99af
 Image 6 (fdt@hk01.c4)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    84219 Bytes = 82.25 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   a7c758b5
  Hash algo:    sha1
  Hash value:   a89dd2b54299fafe6312edc51a309af7dc8fcd05
 Image 7 (fdt@hk06)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    79112 Bytes = 77.26 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   438b566c
  Hash algo:    sha1
  Hash value:   9fdfb776489ebc2076036d821cb0db83a808604e
 Image 8 (fdt@hk08)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    81018 Bytes = 79.12 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   77415c01
  Hash algo:    sha1
  Hash value:   d8d24689d7a64cc206c6baef059463c6a662c1b4
 Image 9 (fdt@hk10)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80781 Bytes = 78.89 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   8f768994
  Hash algo:    sha1
  Hash value:   541c0950cc07f8e6536f0e9abef56e828c69b5c0
 Image 10 (fdt@hk07)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80385 Bytes = 78.50 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   747b38a0
  Hash algo:    sha1
  Hash value:   67d0a6e79604eaed538828f031b28f4375deed3b
 Image 11 (fdt@hk01.c3)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    85130 Bytes = 83.13 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   2602bd37
  Hash algo:    sha1
  Hash value:   21c1a61497da27f901c8babe5abe9c3f5aa2ead5
 Image 12 (fdt@hk01.c2)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    83754 Bytes = 81.79 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   b84f417e
  Hash algo:    sha1
  Hash value:   a8f2a67d4de3e9c35b4b7c529d0f405607d2d210
 Image 13 (fdt@ac04)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    79516 Bytes = 77.65 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   deaa9d0c
  Hash algo:    sha1
  Hash value:   0f0d442842b69dc55d17901b6d22aef0829187c8
 Image 14 (fdt@hk01)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    84994 Bytes = 83.00 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   d21e8ca4
  Hash algo:    sha1
  Hash value:   f8344005b4d3cf5e2a0c9431cbe8541152728a08
 Image 15 (fdt@db.hk02)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    79333 Bytes = 77.47 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   1776e701
  Hash algo:    sha1
  Hash value:   3a6160721038b97c5ffc88d78b20bbda0d959158
 Image 16 (fdt@ac03)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    79516 Bytes = 77.65 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   824ee63f
  Hash algo:    sha1
  Hash value:   6bc4b05c95cd9cd5896d30e25884612de3e79ab2
 Image 17 (fdt@oak03)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    84236 Bytes = 82.26 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   1eaee8f0
  Hash algo:    sha1
  Hash value:   b5ce8f81272a6c99d0e274d5fc3c56888db4389b
 Image 18 (fdt@ac02)
  Description:  ARM64 OpenWrt qcom-ipq807x-hkxx device tree blob
  Created:      Wed Sep 22 04:12:03 2021
  Type:         Flat Device Tree
  Compression:  uncompressed
  Data Size:    80120 Bytes = 78.24 KiB = 0.08 MiB
  Architecture: AArch64
  Hash algo:    crc32
  Hash value:   1b469a41
  Hash algo:    sha1
  Hash value:   2ce9ea3dc0068a34834ded3d419b8753b5d5cbef
 Default Configuration: 'config@ac02'
 Configuration 0 (config@hk09)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk09
 Configuration 1 (config@oak02)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@oak02
 Configuration 2 (config@ac01)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@ac01
 Configuration 3 (config@db.hk01)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@db.hk01
 Configuration 4 (config@hk02)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk02
 Configuration 5 (config@hk01.c4)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk01.c4
 Configuration 6 (config@hk06)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk06
 Configuration 7 (config@hk08)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk08
 Configuration 8 (config@hk10)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk10
 Configuration 9 (config@hk07)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk07
 Configuration 10 (config@hk01.c3)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk01.c3
 Configuration 11 (config@hk01.c2)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk01.c2
 Configuration 12 (config@ac04)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@ac04
 Configuration 13 (config@hk01)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@hk01
 Configuration 14 (config@db.hk02)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@db.hk02
 Configuration 15 (config@ac03)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@ac03
 Configuration 16 (config@oak03)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@oak03
 Configuration 17 (config@ac02)
  Description:  OpenWrt
  Kernel:       kernel@1
  FDT:          fdt@ac02

Looks like the stock firmware is: 128byte header(in text format) + FIT (Flattened Image Tree) + a uimage header (which contains the checksum of the squashfs) +a squashfs image(rootfs)

Checking Image at 4449ffc0 ...
Unknown image format!
rootfs checksum error

Maybe that means the checksum in the uimage header doesn't match with the squashfs image? Can we mimic the format of stock firmware?

We should be able to mimic the OEM image hierarchy. It's pretty close currently except I'm not sure how to tell the OuenWRT build to include that uImage header.

This is how Netgear builds their OEM image. I assume that header comes from mkdniimg.

define Image/Build/RAX120
	echo "Generating $(2) Firmware"
	$(STAGING_DIR_HOST)/../../tools/checksize $(BIN_DIR)/$(KERNEL_IMG) $(shell expr $(KERNEL_SIZE) - 64) $(shell expr $(KERNEL_SIZE) - 64 - 131072)
#	test `cat $(BIN_DIR)/$(KERNEL_IMG) |wc -c` -le $(KERNEL_SIZE)
	test `cat $(BIN_DIR)/$(ROOTFS_IMG) |wc -c` -le $(ROOTFS_SIZE)
	
	rm -f $(ONE_IMAGE)
	(\
		dd if=$(BIN_DIR)/$(KERNEL_IMG) bs=$(KERNEL_SIZE) conv=sync; \
		dd if=$(BIN_DIR)/$(ROOTFS_IMG) bs=64k; \
	) > $(ONE_IMAGE)
	
	$(STAGING_DIR_HOST)/bin/rax120 $(BIN_DIR)/$(KERNEL_IMG) $(BIN_DIR)/$(KERNEL_IMG).normal
	
	dd if=$(BIN_DIR)/$(KERNEL_IMG) bs=$(shell expr $(KERNEL_SIZE) - 64) conv=sync of=$(BIN_DIR)/$(KERNEL_IMG).final
	
	$(call MkImageLzma,$(BIN_DIR)/$(ROOTFS_IMG),$(BIN_DIR)/$(ROOTFS_IMG).uImage)
	$(STAGING_DIR_HOST)/bin/rax120 $(BIN_DIR)/$(ROOTFS_IMG).uImage $(BIN_DIR)/$(ROOTFS_IMG).tmp
	
	dd if=$(BIN_DIR)/$(ROOTFS_IMG).tmp bs=64 count=1 >> $(BIN_DIR)/$(KERNEL_IMG).final
	(\
		dd if=$(BIN_DIR)/$(KERNEL_IMG).final bs=$(KERNEL_SIZE) conv=sync; \
		dd if=$(BIN_DIR)/$(ROOTFS_IMG) bs=64k; \
	) > $(ONE_IMAGE_FINAL)
	
	$(STAGING_DIR_HOST)/bin/mkdniimg \
		-B $(MODULE_NAME) -v $(FW_VERSION) -r $(FW_REGION) -H $(HW_ID) \
		-i $(ONE_IMAGE_FINAL) \
		-o $(DNI_IMAGE)

Oops, my device doesn't have pins soldered in advance (as shown in the pictures on openwrt wiki and devwiki):

I think I have to stay with no TTL access for a while, before getting the tool and some time to get it soldered...

This is how Netgear builds their OEM image. I assume that header comes from mkdniimg.

From the content you posted, the uImage header is generated by the (STAGING_DIR_HOST)/bin/rax120.

Here is the source code of the tool:

yancey@yancey-ubuntu:~/rax120v2/RAX120-V1.2.3.28_gpl_src$ cat tools/firmware-utils/src/rax120.c
/*
 * R7800.c - partially based on OpenWrt's add_header.c
 *
 * Copyright (C) 2009 Anael Orlinski  <naouel@naouel.org>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License,
 * version 2 as published by the Free Software Foundation.
 *
 * 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

/*
 * The add_header utility used by various vendors preprends the buf
 * image with a header containing a CRC32 value which is generated for the
 * model id + reserved space for CRC32 + buf, then replaces the reserved
 * area with the actual CRC32. This replacement tool mimics this behavior.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <netinet/in.h>
#include <inttypes.h>

#define BPB 8 /* bits/byte */

static uint32_t crc32[1<<BPB];

static void init_crc32()
{
	const uint32_t poly = ntohl(0x2083b8ed);
	int n;

	for (n = 0; n < 1<<BPB; n++) {
		uint32_t crc = n;
		int bit;

		for (bit = 0; bit < BPB; bit++)
			crc = (crc & 1) ? (poly ^ (crc >> 1)) : (crc >> 1);
		crc32[n] = crc;
	}
}

static uint32_t crc32buf(unsigned char *buf, size_t len)
{
	uint32_t crc = 0xFFFFFFFF;

	for (; len; len--, buf++)
		crc = crc32[(uint8_t)crc ^ *buf] ^ (crc >> BPB);
	return ~crc;
}

struct header {
    uint32_t magic;
    uint32_t crc;
    unsigned char stuff[56];
};

static void usage(const char *) __attribute__ (( __noreturn__ ));

static void usage(const char *mess)
{
	fprintf(stderr, "Error: %s\n", mess);
	fprintf(stderr, "Usage: ax6000 input_file output_file\n");
	fprintf(stderr, "\n");
	exit(1);
}

int main(int argc, char **argv)
{
	off_t len;			// of original buf
	off_t buflen;			// of the output file
	int fd;
	void *input_file;		// pointer to the input file (mmmapped)
	struct header header;
	unsigned char *buf;	// pointer to prefix + copy of original buf

	// verify parameters

	if (argc != 3)
		usage("wrong number of arguments");

	// mmap input_file
	if ((fd = open(argv[1], O_RDONLY))  < 0
	|| (len = lseek(fd, 0, SEEK_END)) < 0
	|| (input_file = mmap(0, len, PROT_READ, MAP_SHARED, fd, 0)) == (void *) (-1)
	|| close(fd) < 0)
	{
		fprintf(stderr, "Error loading file %s: %s\n", argv[1], strerror(errno));
		exit(1);
	}

	buflen = len;

	init_crc32();

        // preload header
        memcpy(&header, input_file, sizeof(header));

        header.magic = htonl(0x27051956); /* To sync with u-boot to change it to default 0x27051956 */
	header.crc = 0;

	// create a firmware image in memory and copy the input_file to it
	buf = malloc(buflen);
	memcpy(buf, input_file, len);

	// CRC of temporary header
	header.crc = htonl(crc32buf((unsigned char*)&header, sizeof(header)));

	memcpy(buf, &header, sizeof(header));

	// write the buf
	if ((fd = open(argv[2], O_CREAT|O_WRONLY|O_TRUNC,0644)) < 0
	|| write(fd, buf, buflen) != buflen
	|| close(fd) < 0)
	{
		fprintf(stderr, "Error storing file %s: %s\n", argv[2], strerror(errno));
		exit(2);
 	}

	free(buf);

	munmap(input_file,len);

	return 0;
}

Especially, look at the line header.magic = htonl(0x27051956); it's exactly the magic inside the uImage header (offset 0061ffc0 after the 128byte header is removed):

0061ffc0: 2705 1956 b72f 253c 614a ad46 0360 0800  '..V./%<aJ.F.`..
0061ffd0: 4090 8000 4090 8000 8c7f 2917 0516 0203  @...@.....).....
0061ffe0: 4c69 6e75 782d 342e 342e 3630 0000 0000  Linux-4.4.60....
0061fff0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00620000: 6873 7173 eb11 0000 36ad 4a61 0000 0400  hsqs....6.Ja....
00620010: 0701 0000 0400 1200 c006 0100 0400 0000  ................
00620020: 9e14 1094 0000 0000 abdd 5f03 0000 0000  .........._.....
00620030: a3dd 5f03 0000 0000 ffff ffff ffff ffff  .._.............
````

Looks like we are getting that value in the header after adding uImage none except it's at 00000000. I'm not sure how to force the OpenWRT build to use 0061ffc0?

Looks like we are getting that value in the header after adding uImage none except it's at 00000000. I'm not sure how to force the OpenWRT build to use 0061ffc0?

It seems easier to generate seperately and append together, as what is done in the netgear gpl code. (maybe the general openwrt code can't really handle such a format yet)

I added some comments to the netgear's firmware generate makefile:

Looks like the stock firmware is: 128byte header(in text format) + FIT (Flattened Image Tree) + a uimage header (which contains the checksum of the squashfs) +a squashfs image(rootfs)

It matches with the previous guess above.

Note: the KERNEL_IMAGE should be the FIT part of the firmware structure, it should be already zero-padded to multiple of 64k bytes in some previous steps of the makefile. The tail 64 byte of KERNEL_IMAGE is removed, and replaced with the 64 byte uImage header. After KERNEL_IMAGE and ROOTFS_IMAGE are combined togher, ROOTFS_IMAGE is aligned at 64kb and the 64byte header is right before the ROOTFS_IMAGE.

I'm not sure how to force the OpenWRT build to use 0061ffc0?

I think for our case, it might not be 0x0061ffc0. From the hex dump of firmware, the ROOTFS_IMAGE is at 0x00620000 (without the 128byte header) which is the exact next 64kb block of the end of the FIT. And 0061ffc0 is 0x00620000 minus 64byte(size of uImage header). Looks like 0061ffc0 is not hardcoded, it's based on the size of FIT.

1 Like

For the 128byte header, the source code of tool is at tools/firmware-utils/src/mkdniimg.c.

Maybe no need to use the tool at all, it's just plaintext seperated by \n, and padded to 128 bytes with zeros:

00000000: 6465 7669 6365 3a52 4158 3132 300a 7665  device:RAX120.ve
00000010: 7273 696f 6e3a 5631 2e32 2e33 2e32 380a  rsion:V1.2.3.28.
00000020: 7265 6769 6f6e 3a0a 6864 5f69 643a 3239  region:.hd_id:29
00000030: 3736 3535 3839 2b30 2b35 3132 2b31 3032  765589+0+512+102
00000040: 342b 3478 342b 3878 380a 0000 0000 0000  4+4x4+8x8.......
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: d00d feed 0061 5b38 0000 0038 0061 56ac  .....a[8...8.aV.
00000090: 0000 0028 0000 0011 0000 0010 0000 0000  ...(............

There is no checksum inside, we can even hardcode this header.

Looks like the stock firmware is: 128byte header(in text format) + FIT (Flattened Image Tree) + a uimage header (which contains the checksum of the squashfs) +a squashfs image(rootfs)

I just checked the stock firmware again, to see if there is something appended after the squashfs image. There is indeed something, but all of them are 0xff or 0x00, except two interesting magic of dead c0de and a c4 at the very end of file:

03c1dd20: 87cb 17cb 587d 5c98 3d58 ef36 aee8 a67d  ....X}\.=X.6...}
03c1dd30: 3388 d52a 45dd 4b5f 8491 0f14 48d5 2e2a  3..*E.K_....H..*
03c1dd40: e1d0 b6d4 4f81 b256 9455 5aab da28 323d  ....O..V.UZ..(2=
03c1dd50: 4085 ca95 5e00 0000 009d 5618 8800 01b9  @...^.....V.....
03c1dd60: 07d8 1e00 007c 55e8 b93e 300d 8b02 0000  .....|U..>0.....
03c1dd70: 0000 0159 5a0f b95f 0300 0000 0071 c15f  ...YZ.._.....q._
03c1dd80: 0300 0000 0067 c95f 0300 0000 0091 d15f  .....g._......._
03c1dd90: 0300 0000 0093 d95f 0300 0000 0004 8000  ......._........
03c1dda0: 0000 009d dd5f 0300 0000 00ff ffff ffff  ....._..........
03c1ddb0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1ddc0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1ddd0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1dde0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1ddf0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1de00: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1de10: ffff ffff ffff ffff ffff ffff ffff ffff  ................
...............................
03c1dfb0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1dfc0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1dfd0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1dfe0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1dff0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e000: dead c0de ffff ffff ffff ffff ffff ffff  ................
03c1e010: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e020: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e030: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e040: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e050: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1e060: ffff ffff ffff ffff ffff ffff ffff ffff  ................
.....................................
03c1ffc0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1ffd0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1ffe0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c1fff0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
03c20000: dead c0de 0000 0000 0000 0000 0000 0000  ................
03c20010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
.................................
03c207c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c207d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c207e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c207f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
03c20800: c4

(128 byte header has been removed)

Where 0x3c1ddab is the exact end of squashfs (by binwalk). The tail doesn't have meaningful data, it might doesn't matter at all.

I think that's probably just the jffs2 marker, telling it to erase from there to the end of the partition.

See https://openwrt.org/docs/techref/filesystems#jffs2

Did they use a jffs2 overlay in stock? I don't know what the 0xc4 mark is off the top of my head. Either way, it won't look like that in custom images because you'd most likely use a ubi overlay anyway.

No there is no jffs2 used in stock.

The deadc0de seems pretty common in the code base:

yancey@yancey-ubuntu:~/rax120v2/testopenwrt$ grep -r "deadc0de" .
./scripts/functions.sh:		"deadc0de")
./build_dir/host/firmware-utils-2022-02-28-002cfaf0/src/imagetag.c:	  /* Align image to specified erase block size and append deadc0de */
./build_dir/host/firmware-utils-2022-02-28-002cfaf0/src/imagetag.c:	  printf("Data alignment to %dk with 'deadc0de' appended\n", block_size/1024);
Binary file ./build_dir/host/firmware-utils-2022-02-28-002cfaf0/CMakeFiles/imagetag.dir/src/imagetag.c.o matches
Binary file ./build_dir/host/firmware-utils-2022-02-28-002cfaf0/imagetag matches
./build_dir/toolchain-aarch64_cortex-a53_gcc-11.2.0_musl/linux-5.15.34/tools/testing/selftests/arm64/fp/sve-test.S://	ldr	w0, =0xdeadc0de
./build_dir/toolchain-aarch64_cortex-a53_gcc-11.2.0_musl/linux-5.15.34/tools/testing/selftests/arm64/fp/sve-test.S://	ldr	w0, =0xdeadc0de
./build_dir/toolchain-aarch64_cortex-a53_gcc-11.2.0_musl/linux-5.15.34/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c:#define UCALL_PR_HCALL 0xdeadc0de
./build_dir/toolchain-aarch64_cortex-a53_gcc-11.2.0_musl/linux-5.15.34/tools/testing/selftests/kvm/aarch64/psci_cpu_on_test.c:#define CPU_ON_CONTEXT_ID 0xdeadc0deul
./build_dir/toolchain-aarch64_cortex-a53_gcc-11.2.0_musl/linux-5.15.34/lib/test_bitmap.c:	{  0,  8, 32, { 0xdeadc0deUL, }, { 0x00deadc0UL, }, },

And it might doesn't matter whether have it or not.

Here is a rough depiction of the oem flash layout.

By default they set the kernel size to 6.4m but it's a level 3 partition so we can use any size as long as we stay within the boundaries of mtd24/firmware.

I defined the following in DT to ensure we have enough kernel space. This allocates roughly 30m to kernel and 74m to rootfs. rootfs could be about 300m larger if we purge language, cert, mtdoops, router_analytics, ntgrdata, and reserved (padding, rootfs_1,...).

	.
	.
	.
	partition@1980000 {
		label = "firmware";
		reg = <0x1980000 0x6400000>;
	};
	kernel@1980000 {
		label = "kernel";
		reg = <0x1980000 0x01D00000>;
	};
	rootfs@3680000 {
		label = "rootfs";
		reg = <0x3680000 0x4700000>;
	};
	.
	.
	.

UART/TFTP flashing:

  1. Stop autoboot
  2. Run
    setenv bootcmd "nand read 0x44000000 0x1980000 0x01D00000; bootm"
    saveenv
    fw_recovery
  3. start tftp on host machine and run
    connect 192.168.1.1
    binary
    put openwrt-ipq807x-generic-netgear_rax120-squashfs-factory.bin

rootfs could be about 300m larger if we purge language, cert, mtdoops, router_analytics, ntgrdata, and reserved (padding, rootfs_1,...).

We can use rootfs/mtd26 as squashfs or ubifs, and the 290mb reserved as an overlay. Then we get 300+mb spaces without purging the existing parititons. In this way, we can switch between stock and openwrt easier.

That may be an optimal route actually. The only thing I'm concerned with towards the end of flash is the Aquantia firmware. exroot is not a bad option over USB 3 also.

I have figured out a hacky way of generating a firmware that is mostly compatible with the stock format. It's flash-able via the router webpage or tftp, doesn't need serial access or change u-boot environment.

The script is here:

(based on @jewest's repo)

The main trick is,a dummy uimage header with an empty squashfs image is inserted, so that the uboot is cheated and no longer complains. From the binwalk result, you can see the dummy uimage header clearly:

yancey@yancey-ubuntu:~/rax120v2/openwrt_jewwest/rax120_image_gen$ binwalk final.img
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
128           0x80            device tree image (dtb)
360           0x168           gzip compressed data, maximum compression, from Unix, last modified: 1970-01-01 00:00:00 (null date)
4714460       0x47EFDC        device tree image (dtb)
4849728       0x4A0040        uImage header, header size: 64 bytes, header CRC: 0xD99B0130, created: 2022-06-03 07:14:04, image size: 4096 bytes, Data Address: 0x40908000, Entry Point: 0x40908000, data CRC: 0x4ED4CFE6, OS: Linux, image type: OS Kernel Image, compression type: lzma, image name: "Linux-5.15"
4849792       0x4A0080        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 162 bytes, 1 inodes, blocksize: 262144 bytes, created: 2022-06-03 07:14:04
30408832      0x1D00080       Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4841214 bytes, 972 inodes, blocksize: 262144 bytes, created: 2022-06-03 05:11:57

Flashing steps:

  1. clone the repo, run:
./scripts/feeds update packages luci
./scripts/feeds install -a -p luci

./scripts/feeds update packages nss_packages
./scripts/feeds install -a -p nss_packages
  1. run make menconfig, select:
target-system ---> Qualcomm Atheros IPQ807X
target-profile ---> Netgear Rax120
firmware ---> aq-fw_download
firmware ---> nss-firmware-ipq8074
kernel-module ---> network-device ---> kmod-qca-nss-drv
luci ---> collections ---> luci
  1. run make -j

  2. goto rax120_image_gen folder and run gen.sh, then you will get final.img in the folder

  3. then flash final.img via the router webpage,
    or
    with the tftp recover method on the wiki:
    https://openwrt.org/inbox/toh/netgear/netgear_rax120_nighthawk_ax12#tftp_method

After flashing it boots, the rootfs is automatically mounts:

root@OpenWrt:/# df
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/root                 4864      4864         0 100% /rom
tmpfs                   468296        48    468248   0% /tmp
tmpfs                   468296       172    468124   0% /tmp/root
overlayfs:/tmp/root     468296       172    468124   0% /
tmpfs                      512         0       512   0% /dev

But there are still many problems, like the all the network adapters are not even brought up by default. You still need serial to verify it's flashed successfully....

UPDATE: it turns out the reason was just I didn't feed and select the qca-nss driver and firmware packages, after it's fixed the problem is gone. And the instructions has been updated.

1 Like

Here is the bootcmd of uboot:

bootcmd=
mii write 0x4 0x0 0x800; sleep 1; nmrp; 
echo Loading DNI firmware for checking...; 
loadn_dniimg 0 0x1980000 0x44000000; 
calc_rootadd 0x1980000 0x44000000; 
iminfo 0x44000000; 
if test $? -ne 0; then echo linux checksum error; fw_recovery; fi;
iminfo $rootfs_addr_for_fw_checking; 
if test $? -ne 0; then echo rootfs checksum error; fw_recovery; fi;
nand read 0x44000000 0x1980000 0x06400000; dnibootm

What worths most attention is the calc_rootadd 0x1980000 0x44000000, it calculates the offset of rootfs, and let the following iminfo to verify the rootfs (by the uimage header maybe?).

In the gpl code, the logic of calc_rootadd is at RAX120-V1.2.3.28_gpl_src/git_home/u-boot.git/common/cmd_dni_nand.c

hdr = (image_header_t *)addr;

rootfs_addr = (ntohl(hdr->ih_size)/CONFIG_SYS_FLASH_SECTOR_SIZE+1)*CONFIG_SYS_FLASH_SECTOR_SIZE+2*sizeof(image_header_t)-sizeof(image_header_t);

where the imge_header_t is:

typedef struct image_header {
	uint32_t	ih_magic;	/* Image Header Magic Number	*/
	uint32_t	ih_hcrc;	/* Image Header CRC Checksum	*/
	uint32_t	ih_time;	/* Image Creation Timestamp	*/
	uint32_t	ih_size;	/* Image Data Size		*/
	uint32_t	ih_load;	/* Data	 Load  Address		*/
	uint32_t	ih_ep;		/* Entry Point Address		*/
...

So the rootfs_addr is calculated based on the 4th uint32 inside the FIT header, in other words the 4th uint32 inside FIT header need to be matched with the offset of rootfs.

But the offset of rootfs (if in usual way) also has to be matched with the rootfs partition table of mtd of linux, which is hardcoded inside ipq8074-rax120.dts at the moment.

The image_header is checksum protected, I don't know how to generate a FIT with arbitrary ih_size at the moment, so I used the trick of dummy uimage header to cheat the uboot to pass the check.

Isnt the DNI image recipe that other Netgear targets use exactly doing this?

It could be a problem with the way I set netgear-dni to append maybe?

I never actually got past this issue.. I just cleaned up to bootcmd to eliminate all the extra checks. Updating bootcmd was also necessary to boot a kernel larger then 0x06400000.

Updating bootcmd was also necessary to boot a kernel larger then 0x06400000.

If I understand correctly, this 0x06400000 means 100mb. It couldn't be a practical problem.