Add support for Linksys EA6350 v3


Hi Escalion,

How did you get terminal access without having a serial console?



Hey robimarko.
I've made the improvements ref. the comments made and pushed the changes.

I have made some progress with factory image, that I can now flash a file from the Linksys UI with the generated factory bin with some extra steps:

Generate CRC with: printf "%08X" $(dd if=factory.bin bs=$(stat -c%s factory.bin) count=1|cksum| cut -d ' ' -f1)
Replace FFFFFFFF with generated CRC in file sig.txt (MUST BE 256 bytes long):

.LINKSYS.01000409EA6350v3       FFFFFFFF       K0000000F0246434

cat factory.bin sig.txt > factory.img

The resulting file can be flashed successfully, HOWEVER, I have the following issue (and I've been scratching my head for a couple hours)

main_loop@455: run_command(if test $boot_part = 1; then run bootpart1; else run bootpart2; fi, 0)

NAND read: device 1 offset 0x2800000, size 0x300000
 3145728 bytes read: OK
## Booting kernel from FIT Image at 82000000 ...
   Using 'config@1' configuration
   Trying 'kernel@1' kernel subimage
     Description:  ARM OpenWrt Linux-4.14.71
     Type:         Kernel Image
     Compression:  gzip compressed
     Data Start:   0x820000e4
     Data Size:    2922949 Bytes = 2.8 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x80208000
     Entry Point:  0x80208000
     Hash algo:    crc32
     Hash value:   bd0a1919
     Hash algo:    sha1
     Hash value:   9a1ae3798bb47d0b893f29d4e77633cc2f2bbc2f
   Verifying Hash Integrity ... crc32+ sha1+ OK
## Flattened Device Tree from FIT Image at 82000000
   Using 'config@1' configuration
   Trying 'fdt@1' FDT blob subimage
     Description:  ARM OpenWrt linksys_ea6350v3 device tree blob
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x822c9be8
     Data Size:    16164 Bytes = 15.8 KiB
     Architecture: ARM
     Hash algo:    crc32
     Hash value:   f6119e79
     Hash algo:    sha1
     Hash value:   2f996e5da4e4119af3f5099a33b64204d1ce60bb
   Verifying Hash Integrity ... crc32+ sha1+ OK
   Booting using the fdt blob at 0x822c9be8
   Uncompressing Kernel Image ... OK
   Loading Device Tree to 87228000, end 8722ef23 ... OK
ipq: fdt fixup unable to find compatible node
In ART, mac0=0xFFFFFFFFFFFF, mac1=0xFFFFFFFFFFFF, mac2=0x6038E08994A5, mac3=0x6038E08994A6
get_eth_mac_address@1407: the base hw_mac_addr='60:38:E0:89:94:A3' is valid!
Using machid 0x8010100 from environment

Starting kernel ...
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.14.71 (ryan@ryan-linux) (gcc version 7.3.0 (OpenWrt GCC 7.3.0 r8055-6e80dd5)) #0 SMP Fri Sep 21 17:33:36 2018
[    0.000000] CPU: ARMv7 Processor [410fc075] revision 5 (ARMv7), cr=10c5387d
[    0.000000] CPU: div instructions available: patching division code
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt: Machine model: Linksys EA6350v3
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] random: get_random_bytes called from start_kernel+0x88/0x3c0 with crng_init=0
[    0.000000] percpu: Embedded 15 pages/cpu @cfdaf000 s29260 r8192 d23988 u61440
[    0.000000] Built 1 zonelists, mobility grouping on.  Total pages: 64512
[    0.000000] Kernel command line: init=/sbin/init rootfstype=squashfs ubi.mtd=13,2048 ubi.block=0,0 root=/dev/ubiblock0_0 rootwait ro
[    0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
[    0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
[    0.000000] Memory: 249920K/260096K available (4196K kernel code, 137K rwdata, 1168K rodata, 1024K init, 256K bss, 10176K reserved, 0K cma-reserved, 0K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xd0800000 - 0xff800000   ( 752 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xd0000000   ( 256 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0208000 - 0xc0719060   (5189 kB)
[    0.000000]       .init : 0xc0900000 - 0xc0a00000   (1024 kB)
[    0.000000]       .data : 0xc0a00000 - 0xc0a22680   ( 138 kB)
[    0.000000]        .bss : 0xc0a24000 - 0xc0a64258   ( 257 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
[    0.000000] arch_timer: cp15 timer(s) running at 48.00MHz (virt).
[    0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0xb11fd3bfb, max_idle_ns: 440795203732 ns
[    0.000008] sched_clock: 56 bits at 48MHz, resolution 20ns, wraps every 4398046511096ns
[    0.000022] Switching to timer-based delay loop, resolution 20ns
[    0.000249] Calibrating delay loop (skipped), value calculated using timer frequency.. 96.00 BogoMIPS (lpj=480000)
[    0.000269] pid_max: default: 32768 minimum: 301
[    0.000400] Mount-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000419] Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes)
[    0.000946] CPU: Testing write buffer coherency: ok
[    0.001639] Setting up static identity map for 0x80300000 - 0x80300060
[    0.001779] Hierarchical SRCU implementation.
[    0.002412] smp: Bringing up secondary CPUs ...
[    0.005120] smp: Brought up 1 node, 4 CPUs
[    0.005141] SMP: Total of 4 processors activated (384.00 BogoMIPS).
[    0.005149] CPU: All CPU(s) started in SVC mode.
[    0.009357] VFP support v0.3: implementor 41 architecture 2 part 30 variant 7 rev 5
[    0.009505] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
[    0.009530] futex hash table entries: 1024 (order: 4, 65536 bytes)
[    0.009731] pinctrl core: initialized pinctrl subsystem
[    0.010684] NET: Registered protocol family 16
[    0.011008] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.012026] cpuidle: using governor ladder
[    0.012069] cpuidle: using governor menu
[    0.028844] usbcore: registered new interface driver usbfs
[    0.028916] usbcore: registered new interface driver hub
[    0.028996] usbcore: registered new device driver usb
[    0.029044] pps_core: LinuxPPS API ver. 1 registered
[    0.029053] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <>
[    0.029078] PTP clock support registered
[    0.030450] clocksource: Switched to clocksource arch_sys_counter
[    0.031249] NET: Registered protocol family 2
[    0.031876] TCP established hash table entries: 2048 (order: 1, 8192 bytes)
[    0.031918] TCP bind hash table entries: 2048 (order: 2, 16384 bytes)
[    0.031966] TCP: Hash tables configured (established 2048 bind 2048)
[    0.032070] UDP hash table entries: 256 (order: 1, 8192 bytes)
[    0.032107] UDP-Lite hash table entries: 256 (order: 1, 8192 bytes)
[    0.032313] NET: Registered protocol family 1
[    0.033338] No memory allocated for crashlog
[    0.033482] workingset: timestamp_bits=30 max_order=16 bucket_order=0
[    0.036658] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.036673] jffs2: version 2.2 (NAND) (SUMMARY) (LZMA) (RTIME) (CMODE_PRIORITY) (c) 2001-2006 Red Hat, Inc.
[    0.041620] io scheduler noop registered
[    0.041640] io scheduler deadline registered (default)
[    0.044253] bam-dma-engine 8e04000.dma: num-channels unspecified in dt
[    0.044273] bam-dma-engine 8e04000.dma: num-ees unspecified in dt
[    0.044960] tcsr 1949000.tcsr: setting wifi_glb_cfg = 41000000
[    0.045043] tcsr 194b000.tcsr: setting usb hs phy mode select = e700e7
[    0.045116] tcsr 1953000.ess_tcsr: setting ess interface select = 0
[    0.045191] tcsr 1957000.tcsr: setting wifi_noc_memtype_m0_m2 = 2222222
[    0.045409] Serial: 8250/16550 driver, 2 ports, IRQ sharing disabled
[    0.045961] msm_serial 78af000.serial: msm_serial: detected port #0
[    0.046007] msm_serial 78af000.serial: uartclk = 1843200
[    0.046058] 78af000.serial: ttyMSM0 at MMIO 0x78af000 (irq = 25, base_baud = 115200) is a MSM
[    0.046083] msm_serial: console setup on port #0
[    0.560068] console [ttyMSM0] enabled
[    0.564982] msm_serial: driver initialized
[    0.572466] loop: module loaded
[    0.573620] spi_qup 78b5000.spi: IN:block:16, fifo:64, OUT:block:16, fifo:64
[    0.576184] m25p80 spi0.0: mx25l1606e (2048 Kbytes)
[    0.582715] 10 fixed-partitions partitions found on MTD device spi0.0
[    0.587220] Creating 10 MTD partitions on "spi0.0":
[    0.593842] 0x000000000000-0x000000040000 : "SBL1"
[    0.599194] 0x000000040000-0x000000060000 : "MIBIB"
[    0.603958] 0x000000060000-0x0000000c0000 : "QSEE"
[    0.608678] 0x0000000c0000-0x0000000d0000 : "CDT"
[    0.613570] 0x0000000d0000-0x0000000e0000 : "APPSBLENV"
[    0.618323] 0x0000000e0000-0x000000160000 : "APPSBL"
[    0.623404] 0x000000160000-0x000000170000 : "ART"
[    0.628553] 0x000000170000-0x000000190000 : "u_env"
[    0.633192] 0x000000190000-0x0000001b0000 : "s_env"
[    0.637854] 0x0000001b0000-0x0000001c0000 : "devinfo"
[    0.643545] libphy: ipq40xx_mdio: probed
[    0.678173] ESS reset ok!
[    0.711154] ESS reset ok!
[    1.140851] libphy: Fixed MDIO Bus: probed
[    1.240589] i2c /dev entries driver
[    1.280661] nand: device found, Manufacturer ID: 0xef, Chip ID: 0xaa
[    1.280697] nand: Winbond W25N01GV 1G 3.3V 8-bit
[    1.286087] nand: 128 MiB, SLC, erase size: 128 KiB, page size: 2048, OOB size: 64
[    1.290752] Scanning device for bad blocks
[    1.308506] random: fast init done
[    1.588022] random: crng init done
[    1.682088] 6 fixed-partitions partitions found on MTD device spi0.1
[    1.682121] Creating 6 MTD partitions on "spi0.1":
[    1.687519] 0x000000000000-0x000002800000 : "kernel"
[    1.693365] 0x000000300000-0x000002800000 : "rootfs"
[    1.698280] mtd: device 11 (rootfs) set to be root filesystem
[    1.705241] mtdsplit: no squashfs found in "rootfs"
[    1.707847] 0x000002800000-0x000005000000 : "alt_kernel"
[    1.713358] 0x000002b00000-0x000005000000 : "alt_rootfs"
[    1.718846] 0x000005000000-0x000005100000 : "sysdiag"
[    1.723987] 0x000005100000-0x000008000000 : "syscfg"
[    1.730128] NET: Registered protocol family 10
[    1.734724] Segment Routing with IPv6
[    1.737664] NET: Registered protocol family 17
[    1.741701] 8021q: 802.1Q VLAN Support v1.8
[    1.745772] Registering SWP/SWPB emulation handler
[    1.753641] ubi0: attaching mtd13
[    2.556492] ubi0: scanning is finished
[    2.556545] ubi0 error: ubi_read_volume_table: the layout volume was not found
[    2.559238] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd13, error -22
[    2.566391] UBI error: cannot attach mtd13
[    2.573564] UBI: block: can't open volume on ubi0_0, err=-19
[    2.5775�[    2.585469] Waiting for root device /dev/ubiblock0_0...

Could I have defined something incorrectly?

Here is the env:

(EA6350v3) # printenv
bootcmd=if test $boot_part = 1; then run bootpart1; else run bootpart2; fi
bootpart1=set bootargs $partbootargs && nand read $loadaddr $prikern $kernsize && bootm $loadaddr
bootpart2=set bootargs $partbootargs2 && nand read $loadaddr $altkern $kernsize && bootm $loadaddr
flashimg=tftpboot $loadaddr $image && nand erase $prikern $imgsize && nand write $loadaddr $prikern $filesize
flashimg2=tftpboot $loadaddr $image && nand erase $altkern $imgsize && nand write $loadaddr $altkern $filesize
partbootargs=init=/sbin/init rootfstype=squashfs ubi.mtd=11,2048 ubi.block=0,0 root=/dev/ubiblock0_0 rootwait ro
partbootargs2=init=/sbin/init rootfstype=squashfs ubi.mtd=13,2048 ubi.block=0,0 root=/dev/ubiblock0_0 rootwait ro

Environment size: 1203/131068 bytes

mantisgb: I do have a serial console :slight_smile:


Hm, It looks like the bootloader is overriding partitions defined in DTS.
Are you sure that the partition layout matches the one you set in DTS?
If so, then you can use a chose node and pass bootargs simply like this to make kernel ignore ones by bootloader


The override seems to be acting correctly (it has a failsafe updater - which I need to find a way to update the env on successful update) and the partitions are in the correct place.
It seems I need to generate a UBIFS instead of merely appending the rootfs - which makes sense as UBI is better for NAND.
I'll have a play with a couple of options today.


No need for UBIFS, when you declare a ubi partition it will wrap everything inside a UBIFS.
You can use init.d folder and make a script to check if the model matches yours and then execute commands to modify U-boot env.
You can simply use u-boot-envtools


It seems odd as the factory image generated looks like this:

binwalk signed.img 

228           0xE4            gzip compressed data, maximum compression, from Unix, NULL date (1970-01-01 00:00:00)
3145728       0x300000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4781364 bytes, 1959 inodes, blocksize: 262144 bytes, created: 2018-09-21 17:33:36

Whereas the Linksys firnware looks like this:

binwalk FW_EA6350v3_3.1.9.182357_prod.img 

14119         0x3727          xz compressed data
14340         0x3804          xz compressed data
3145728       0x300000        UBI erase count header, version: 1, EC: 0x0, VID header offset: 0x800, data offset: 0x1000

Which makes sense as the key lines of failure are:
mtd: device 11 (rootfs) set to be root filesystem - The upgrade was writtin to alt_* (mtd12,13)

[    1.918039] ubi0: attaching mtd13
i0: scanning is finished
[    3.471654] ubi0 error: ubi_read_volume_table: the layout volume was not found
[    3.474378] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd13, error -22

If I generate a UBIFS for the factory binary I'm hoping that this should resolve the issue - but I may have to redefine its' sysupgrade image too for compatibility with the bootloader.


Well, you made a recipe for it and it looks to be incorrect.


From what I can tell Linksys had the great(or bad - can't tell from here efficiently without the device) idea to use the "zImage" option. This will make the kernel build code to add a "loader" that decompresses the kernel image and (relocated it to a different place).

Now, this can work just as well, but I would try to see if the u-boot on the device might support lzma packed images directly, this way there's no need to get creative. You should be able to test this relatively easily by switching from $(call Device/FitImage) in the image/Makefile code to $(call Device/FitImageLzma) and see if u-boot can boot the kernel or if u-boot refuses with a error message about "unsupported compression".

Yeah, I think for the factory image you just need to replace "append-rootfs" with "append-ubi".
(I don't think you don't need to do anything special for sysupgrade image creation, it should be fine as is.)

This is very bad. The error is caused by the bootargs set by your u-boot:

partbootargs=init=/sbin/init rootfstype=squashfs *ubi.mtd=11,2048 ubi.block=0,0* root=/dev/ubiblock0_0


partbootargs2=init=/sbin/init rootfstype=squashfs *ubi.mtd=13,2048 ubi.block=0,0* root=/dev/ubiblock0_0 rootwait ro

Problem is, unlike the "mtdparts= " hack from above, the ubi module can parse multiple "ubi.mtd=" parameters and a new ubi.mtd= will not overwrite the previous one . What even worse: the ubi-auto-attach feature will not work if a ubi.mtd= is specified. I don't see how you could easily work around this either without breaking a lot of eggs.

It might be possible to get this working "somehow". Do you know how the upgrade process switches between the primary and alternative kernel and rootfs locations? Does it flip-flop between boot_part=0 and boot_part=1 after a upgrade, or is the alt_kernel + alt_rootfs copied into the primary kernel + rootfs after a successful boot? Or is there something else going on there?

I'm asking because it would be fantastic if the boot_part value could always be 2.


@chunkeey I am hitting a somewhat similar issue on Netgear WAC510.
It uses a dual flash layout on NAND and bootloader will pass mtdargs and other parameters to cmdline to fill out the partition table.
But it sends 2 ubi parameters and kernel then claims that there is no squashfs in the partition.


(I hope OP and the moderators don't mind that drive-by :gun: )

As for the WAC510: Does it also use some convoluted u-boot env scripting to switch between images? Because if it does and the uboot has a working "saveenv" command, you can make the modifications to the uboot env scripts to just always load one of the partitions. We had this situation before when we were dealing with the Meraki MR24 and MX60. Sure, it's sub-optimal that the installation method requires serial access, so but it's not a show-stopper.

I can tell you from my experiences with the Meraki's the MR33 guide, that the users didn't stop trying, even though it's a complicated process.


Yes, its U-boot deletes and then generates a new u-boot-env before every boot and then passes that to the kernel cmdline.

Yes, you can manually set anything in the bootloader env and after saving it will not generate a new one on every boot.
I already had to generate a whole new factory image build recipe since stock firmware checks the tar(And only exclusively tar compressed image is accepted) contains metadata text file which includes supported devices, version to see if the version is higher and then it checks for md5sum text file to verify that the image is not modified.
Got that bloody thing working and now correct mtdargs are passed to the cmdline but ubi variables are wrong.

I hate to require serial because of something as dumb as this


True, the RT-AC58U is in a similar situation, it's possible to create a "easy flash" image, but it's not really working as intended. Question is: can you ssh or telnet into the WAC510 on the stock firmware? And if so, can you change the u-boot env there?


Well you can ssh, the issue is that Netgear gave permissions to run any command only to root user(I have not been able to figure out the password) and the user you create on the web ui cant execute any command other than reboot.
Even stupid ls does not work.

I gotta take a look at bypassing the ubi arguments in some way


Hm, I downloaded and extracted the vendor firmware But there isn't any password hashes in the /etc/shadow nor was there any obvious code that set it (for root). My best guess is that they just drop a sshkey into /etc/dropbear/authorized_keys on their dedicated development and test images.

You can, but from what I can tell with dealing with the Netgear WNDAP620 and all the Merakis: It's not really worth the effort for a one-time thing. It's more important to just get the image out to the public as-is and add that some u-boot shell/env settings need to be changed.


There is a patch for overriding bootloader cmdline-args in the mpc85xx target. This could maybe also replace the bootargs-append hack currently used for the NBG6617 and WRE6606.


Unfortunately the loader doesn't support LZMA - which in itself isn't too much of an issue.

The Linksys firmware swaps the boot_part value (stored in "s_env") and a state flag (to decide if the upgradfe was successful).
When the linksys firmware performs the upgrade it selects the 'unused' partition and flashes to there, so in this case the firmware always ends up in the correct place for u-boot to work.
There are 2 cases here which I can see, and that would be:

  • Override the cmdline with the patch mentioned by @blocktrron - however this will be an issue to identify what partition we have been flashed to (could be either if the device has been updated) and could prevent upgrades.
  • Make an intermediary image that will work in either partition, to flash full OpenWRT to the primary partition, which will allow upgrades. (Requires no serial console :hugs:)

I think I will explore the latter because, as pointed out - there are many eggs to be broken.


Hey guys,

So I've managed to get the factory image to work (I used cmdline override).

The next issue is that it breaks sysupgrade (as expected) - I attempted a recipe to fix this but to no avail.

I have added in the recipe for the linksys signature, so it generates flashable images without manually signing.

One caveat is that the active linksys firmware must be in mtd12 - if you flash a generated image and it returns to linksys you need to flash a linksys image first and then flash the openwrt factory image.

The other hurdle is within the s_env there is a boot count which needs to be updated so that u-boot doesn't flip back to the linksys firmware - working on this one today.



s_env is simple.
Simply do something like this:


Unfortunately in this application it is actually u_env that is the u-boot env as you quoted.

I already tried the boot_count variable in u-boot as shown here:

s_env in this attaches a repeating signature:

(No response from firmware)

11 08 11 20 00 00 00 00 11 08 11 20 FF FF FF FF
11 08 11 20 01 00 00 00 12 08 11 20 FF FF FF FF
11 08 11 20 02 00 00 00 13 08 11 20 FF FF FF FF

(response from firmware)

11 08 11 20 00 00 00 00 11 08 11 20 FF FF FF FF
11 08 11 20 01 00 00 00 12 08 11 20 FF FF FF FF
11 08 11 20 00 00 00 00 11 08 11 20 FF FF FF FF
11 08 11 20 01 00 00 00 12 08 11 20 FF FF FF FF
11 08 11 20 00 00 00 00 11 08 11 20 FF FF FF FF
11 08 11 20 01 00 00 00 12 08 11 20 FF FF FF FF
11 08 11 20 00 00 00 00 11 08 11 20 FF FF FF FF

and expects a functioning firmware to update it's boot count.

Unfilled section is:

Working on a small program to handle this now.

EDIT 17 Oct Been a bit busy with various other tasks - will update here when I get back on to it.


@mantisgb I think I'm pretty much there with the build - everything seems to be working - including updating :wink: - it's time for some testing if you could be so kind to help :slight_smile:

You can checkout the source from

I recommend building with LuCi - which can be added by doing the following from the openwrt directory:

./scripts/feeds update
./scripts/feeds install -a -p luci

Then run make menuconfig and select;

Target system - Qualcomm Atheros IPQ40XX
Target Profile - Linksys EA6350v3

For basic LuCi support Select:

LuCi -> Collections -> luci-ssl-nginx

Save the config and run make.
If you want to see what's going on use make V=s
Using make -jN (where N is the number of jobs to run concurrently (1 per core is best)) can fail sometimes, but speeds things up a lot.

Go grab a sandwich or a cup of tea and relax, compilation takes a while.

< disclaimer >
If you get this wrong you can brick your device and I accept no responsibility if you render your device useless, although I can offer advise on how to unbrick.
Code is under GNU GPLv2 and as such no warranty etc applies.
</ disclaimer >

To install:

  • Connect to the Linksys Smart WiFi Page (default
  • Select the connectivity tab on the left
  • In the manual update box on the right;
  • Select browse, and browse to openwrt/bin/targets/ipq40xx/generic/openwrt-ipq40xx-linksys_ea6350v3-squashfs-factory.bin
  • Click update.
  • Read and accept the warning (or not, if you don't want to take the risk)

The router LED will start blinking. When the router LED goes solid, you can now navigate to to your new OpenWRT installation.

Have fun and I look forward to your feedback :slight_smile: