How I Installed OpenWrt on a Linksys EA4500 and Recovered the Router After a Failed Flash

A practical walkthrough: Linksys factory firmware → failed OpenWrt installation → UART and U-Boot → booting OpenWrt from RAM via TFTP → restoring the UBI partition → successful boot of OpenWrt 23.05.6.

Scope. This describes a specific case involving a Linksys EA4500 on Marvell Kirkwood, which has two firmware slots and NAND with a bad block at 0x01DE0000. The instructions are not universal for all EA4500 units, especially hardware revision v3, which uses a different platform. Before writing to NAND, always verify the output of /proc/mtd and the actual layout. The wrong partition can leave the device without even its factory fallback firmware.

System status. Recovered and successfully booting OpenWrt 23.05.6. This OpenWrt series has already reached end of life and no longer receives security fixes. It is used here as a verified intermediate recovery point, not as a recommendation for long-term operation on an external WAN.

Starting Point

On hand was a Linksys EA4500 with the original Linksys Smart Wi-Fi firmware (2.1.41.162351). The goal was to replace the factory system with OpenWrt. This article is specifically about installing and recovering OpenWrt, not about configuring circumvention of blocks.

The device is equipped with a Marvell Kirkwood 88F6282 SoC (ARMv5, OpenWrt architecture arm_xscale), approximately 128 MiB of RAM, and 128 MiB of NAND with 128 KiB erase blocks. A UART serial console is available on the board. Power is 12 V; when working with UART, use 3.3 V logic levels, do not connect a 5 V TTL adapter to the board, and generally do not connect the USB–UART adapter’s VCC to the router. You need a common ground, TX↔RX, and RX↔TX. Terminal settings: 115200, 8N1.

An important feature of the EA4500 is dual-slot firmware: when updating through the standard interface, the new image goes into the inactive slot, after which the bootloader attempts to switch to it. This is useful during recovery, but it does not eliminate the need to protect the working slot.

Step 1. Attempting a Normal Installation from the Factory Interface

First, the official file was downloaded:

openwrt-23.05.6-kirkwood-generic-linksys_ea4500-squashfs-factory.bin

This is the correct file type for an initial installation through the Linksys web interface. sysupgrade.bin images are used for updating an already-running OpenWrt and are not interchangeable with factory.bin.

After flashing, the router did not boot: there were reboots and periods with no UART output. An attempt with OpenWrt 22.03 produced a similar result. This looked like a failed flash, but the cause turned out to be more specific.

At the same time, hardware quirks appeared: sometimes after a reboot the console was silent, and sometimes U-Boot started. The cause of this instability was not established; power or hardware problems are possible. This is a separate observation and must not be confused with the UBI mismatch diagnosed below. In cases of unstable boot, it is better to first check the power supply and the UART connection.

Step 2. Connecting UART and Gaining Access to U-Boot

After connecting UART and stopping autoboot, U-Boot 1.1.4 became available. Through it, the environment variables were checked:

text

printenv

Key values for our unit:

text

bootcmd=run altnandboot
boot_part=2
bootdelay=0
auto_recovery=yes
nandboot=nand read.e 0x2000000 0x200000 0x300000; setenv bootargs $(console) $(mtdparts) $(fs_bootargs_root) serial_number=$(sn) uuid=$(uuid) hw_version=$(hw) device_mac=$(mac) factory_date=$(date) wps_pin=$(wps); bootm 0x2000000;
altnandboot=nand read.e 0x2000000 0x1c00000 0x300000; setenv bootargs $(console) $(mtdparts) $(alt_fs_bootargs_root) serial_number=$(sn) uuid=$(uuid) hw_version=$(hw) device_mac=$(mac) factory_date=$(date) wps_pin=$(wps); bootm 0x2000000;

In this case, nandboot loads the kernel of the first slot from 0x00200000, and altnandboot loads the second from 0x01C00000. Therefore, U-Boot was trying to boot the second slot, which was corrupted from a boot standpoint.

The first slot was checked manually:

text

run nandboot

Result: the factory Linksys firmware booted. This means U-Boot, the main hardware components, and the first firmware copy were alive. This became a safety net throughout the recovery.

A Quirk of the Factory Firmware’s Network Settings

After entering the stock Linux system, it turned out that the WAN obtains an address via DHCP (in our case 192.168.1.148), while the LAN bridge br0 had the address 10.199.36.77/24; the usual DHCP on LAN did not work. This made network access difficult but did not mean the router was hardware-faulty. For further operations, UART and a separate Ethernet path to the PC were used.

Step 3. Reading NAND and Finding the Real Cause

Through U-Boot and Linux, the partition sizes, bad blocks, and kernel boot output were checked.

The following bad NAND blocks were found on this device:

text

Bad eraseblock 239 at 0x000001de0000
Bad eraseblock 692 at 0x000005680000
Bad eraseblock 985 at 0x000007b20000

The first of these is especially interesting: 0x01DE0000 is located inside the kernel area of the second slot.

At the same time, U-Boot successfully read the kernel with the command:

text

nand read.e 0x2000000 0x1c00000 0x300000

The nand read.e command skips marked bad blocks. Because of this, the physical and logical placement of data in the second slot requires special attention.

In the OpenWrt 23.05.6 logs, the kernel successfully passed the checksum verification and started, but then could not find UBI:

text

UBI error: no valid UBI magic found inside mtd6
VFS: Cannot open root device
Kernel panic

That is, the failure occurred not during kernel boot but when mounting the root filesystem.

Checking UBI Offsets

Next, in the factory Linux, the first bytes of consecutive blocks of the second slot were read (NAND erase block size is 0x20000, or 128 KiB). The following picture emerged:

Physical addressContents at the start of the block
0x01EE000000 00 ...
0x01F0000000 00 ...
0x01F2000055 42 49 23 = UBI#
0x01F40000UBI#
0x01F60000UBI#

But OpenWrt expected the start of the rootfs2 partition at address 0x01F00000, that is, exactly one eraseblock earlier than the first detected UBI# signature.

Working hypothesis: the stock firmware from the factory interface placed UBI with an additional offset of 128 KiB, probably because of the bad block in the kernel area of the second slot. This explains the mismatch with the OpenWrt layout. We did not investigate the implementation of the stock flasher down to the source code level, so the specific internal mechanism of the offset remains a hypothesis. The practical part is confirmed: after writing UBI directly to the correct MTD partition, the device worked.

Step 4. Why We Chose to Boot OpenWrt from RAM

Instead of manually modifying the device tree or rewriting U-Boot, we decided to use the official OpenWrt initramfs. It runs entirely in RAM, does not require a working root filesystem on NAND, and contains the ubiformat, ubiattach, and ubinfo utilities.

This made it possible to write the root filesystem precisely to the rootfs2 MTD partition without touching the first Linksys slot.

Two official OpenWrt 23.05.6 files are needed:

  1. openwrt-23.05.6-kirkwood-generic-linksys_ea4500-initramfs-uImage — for loading into RAM via TFTP;
  2. openwrt-23.05.6-kirkwood-generic-linksys_ea4500-squashfs-factory.bin — for extracting the UBI part.

They are available in the OpenWrt 23.05.6 release archive for kirkwood/generic.

Verified official SHA256:

FileSHA256
...linksys_ea4500-squashfs-factory.binfc368d0f70d4f23b5f84af0a54fab90253dd35f9c20b3fe20d28caa30920191a
...linksys_ea4500-initramfs-uImage7ae7bc6b9f057c614b9011ccd82803fb10ae01deaa16e2ba89c486dcb539b0c2

The attached archive contains scripts/prepare_firmware.py, which downloads the files, verifies SHA256, and extracts rootfs.ubi. The binary images themselves are not included in the archive, since during its preparation the official server was not accessible from the environment where the files were created.

Step 5. Booting initramfs via TFTP

On a Windows computer, the Ethernet interface was configured with static IPv4 192.168.1.2/24 and a TFTP server was started. The specific TFTP program does not matter: it is enough for the server to serve initramfs-uImage from its directory. The Python HTTP server, which will be needed below, does not replace the TFTP server for the tftpboot command.

At the U-Boot prompt:

text

setenv ipaddr 192.168.1.1
setenv serverip 192.168.1.2
ping 192.168.1.2
tftpboot 0x2000000 openwrt-23.05.6-kirkwood-generic-linksys_ea4500-initramfs-uImage
iminfo 0x2000000
setenv bootargs 'console=ttyS0,115200 root=/dev/ram rw'
bootm 0x2000000

iminfo is needed to make sure the image loaded and its header/checksum are correct. setenv here changes only the values in the U-Boot runtime environment; we did not run the saveenv command.

Result: OpenWrt 23.05.6 booted successfully from RAM. On the console:

text

DISTRIB_ID='OpenWrt'
DISTRIB_RELEASE='23.05.6'
DISTRIB_TARGET='kirkwood/generic'
DISTRIB_ARCH='arm_xscale'

Step 6. Verifying the Memory Map in RAM OpenWrt

This is the most important step before any NAND write. We ran:

sh

cat /proc/mtd
dmesg | grep -E 'Creating|kernel2|ubi|0x000001f00000|Bad eraseblock'
command -v ubiformat
command -v ubiattach
command -v ubinfo
mount | grep ubi
free

The actual memory map:

text

dev:    size   erasesize  name
mtd0: 00080000 00020000 "u-boot"
mtd1: 00020000 00020000 "u_env"
mtd2: 00020000 00020000 "s_env"
mtd3: 01a00000 00020000 "kernel1"
mtd4: 01700000 00020000 "rootfs1"
mtd5: 01a00000 00020000 "kernel2"
mtd6: 01700000 00020000 "rootfs2"
mtd7: 04a00000 00020000 "syscfg"
mtd8: 00140000 00020000 "unused"

The kernel additionally confirmed:

text

0x000001c00000-0x000003600000 : "kernel2"
0x000001f00000-0x000003600000 : "rootfs2"

Here kernel2 (/dev/mtd5) is a large overlapping area of the second slot, and rootfs2 (/dev/mtd6) is its filesystem, 23 MiB in size, starting at 0x01F00000. For recovery, you must write only to /dev/mtd6, not to /dev/mtd5, /dev/mtd3, /dev/mtd4, or U-Boot.

The tools ubiformat, ubiattach, ubinfo, and wget turned out to be already available in the initramfs. This made it possible to avoid installing additional packages.

Step 7. Extracting the Correct UBI Image

For this specific OpenWrt 23.05.6 factory image, the first 0x300000 bytes (3 MiB) are allocated to the kernel; the UBI image begins after that. This means it is enough to remove the first 3 MiB from the factory.bin file. Do not use this offset for OpenWrt 24.10 and newer without additional verification: the kernel area and boot parameters have been changed there.

The package includes a verification script:

powershell

py -3 scripts\prepare_firmware.py

It verifies the hashes of the two source files and requires that:

  • the UBI# signature be present at offset 0x300000;
  • the extracted file have a size of 4,456,448 bytes (34 NAND blocks);
  • the size be a multiple of 0x20000.

Output file: firmware/rootfs.ubi.

For reproducibility, the minimal version of the operation looks like this:

python

from pathlib import Path

factory = Path('openwrt-23.05.6-kirkwood-generic-linksys_ea4500-squashfs-factory.bin').read_bytes()
ubi = factory[0x300000:]
assert len(ubi) == 4456448
assert ubi[:4] == b'UBI#'
assert len(ubi) % 0x20000 == 0
Path('rootfs.ubi').write_bytes(ubi)

Unlike the minimal example, the attached script first verifies the official SHA256 of the image, which is necessary before flashing.

Step 8. Transferring rootfs.ubi to the Router

On Windows, from the folder containing the file, a simple HTTP server was started:

powershell

cd firmware
py -3 -m http.server 8000 --bind 192.168.1.2

The package includes scripts/start_http_server.cmd for this purpose.

From OpenWrt booted in RAM:

sh

cd /tmp
wget http://192.168.1.2:8000/rootfs.ubi
ls -l /tmp/rootfs.ubi
sha256sum /tmp/rootfs.ubi

In our case:

text

rootfs.ubi  4456448 bytes

The first four bytes were also checked. BusyBox did not have the od command, so a simpler method was used:

sh

dd if=/tmp/rootfs.ubi bs=4 count=1 2>/dev/null
echo

Expected:

text

UBI#

The package includes scripts/check_ram_openwrt.sh and scripts/check_rootfs_ubi.sh — additional checks of the MTD map, tools, signature, and size. They can be sent to the router via the same HTTP server or copied manually.

Before writing, be sure to verify that the SHA256 of rootfs.ubi matches on the PC and the router.

Step 9. Restoring the rootfs2 Partition

Dangerous command. The following involves rewriting NAND. It is provided as a record of the operation actually performed, not as an unconditional recommendation for any device. Before running it, make sure you are working from initramfs, that /dev/mtd6 on your device is indeed rootfs2 with size 0x01700000, that the rootfs.ubi file has been verified, and that power is stable. If the layout is different — stop.

We ran:

sh

ubiformat /dev/mtd6 -f /tmp/rootfs.ubi -y

Key lines of the actual output:

text

ubiformat: mtd6 (nand), size 24117248 bytes (23.0 MiB), 184 eraseblocks of 131072 bytes (128.0 KiB), min. I/O size 2048 bytes
libscan: scanning eraseblock 183 -- 100 % complete
ubiformat: 35 eraseblocks have valid erase counter, mean value is 0
ubiformat: 148 eraseblocks are supposedly empty
ubiformat: warning!: 1 of 184 eraseblocks contain non-UBI data
ubiformat: warning!: only 35 of 184 eraseblocks have valid erase counter
ubiformat: erase counter 0 will be used for all eraseblocks
ubiformat: flashing eraseblock 33 -- 100 % complete
ubiformat: formatting eraseblock 183 -- 100 % complete

The warnings about old contents and erase counters did not lead to an error in this case. All of mtd6 was formatted, and UBI was written from the beginning of the required area.

Verification via ubiattach

After a successful ubiformat:

sh

ubiattach -m 6
ubinfo -a

Key lines from the result:

text

ubi0: attaching mtd6
UBI: EOF marker found, PEBs from 29 will be erased
ubi0: volume 1 ("rootfs_data") re-sized from 9 to 133 LEBs
ubi0: attached mtd6 (name "rootfs2", size 23 MiB)
ubi0: PEB size: 131072 bytes (128 KiB), LEB size: 129024 bytes
ubi0: min./max. I/O unit sizes: 2048/2048, sub-page size 512
ubi0: VID header offset: 512 (aligned 512), data offset: 2048
ubi0: good PEBs: 184, bad PEBs: 0, corrupted PEBs: 0
ubi0: user volume: 2, internal volumes: 1

And ubinfo -a:

UBI volumeSizeState
rootfs27 LEB / 3,483,648 bytes (~3.3 MiB)OK
rootfs_data133 LEB / 17,160,192 bytes (~16.3 MiB)OK

This confirmed the correct UBI structure. The automatic enlargement of rootfs_data is normal. Zero available LEBs was also expected: the free space is distributed between the volumes and the reserve for bad blocks.

Note: ubinfo confirms the correctness of the UBI metadata and the state of the volumes, but by itself it does not guarantee that all files inside rootfs are readable. The final check is provided by booting OpenWrt from NAND.

Step 10. Reboot — and a Successful Boot!

After checking the filesystem, UBI was detached from the RAM system (if ubidetach was available) and a normal boot of the second slot was checked:

sh

ubidetach -m 6

The router was then rebooted. U-Boot used the previously existing altnandboot; we did not touch the factory slot.

This time OpenWrt booted fully:

text

BusyBox v1.36.1 (2025-08-14 18:27:40 UTC) built-in shell (ash)

  _______                     ________        __
 |       |.-----.-----.-----.|  |  |  |.----.|  |_
 |   -   ||  _  |  -__|     ||  |  |  ||   _||   _|
 |_______||   __|_____|__|__||________||__|  |____|
          |__| W I R E L E S S   F R E E D O M
 -----------------------------------------------------
 OpenWrt 23.05.6, r24232-539228933c
 -----------------------------------------------------
=== WARNING! =====================================
There is no root password defined on this device!
Use the "passwd" command to set up a new password
in order to prevent unauthorized SSH logins.
--------------------------------------------------
root@OpenWrt:/#

This is no longer initramfs but an installed OpenWrt that has booted from NAND.

The first action after recovery should be to set a password:

sh

passwd

Then check the persistent overlay, filesystems, and network:

sh

cat /etc/openwrt_release
mount | grep overlay
df -h
ip addr

Why It All Worked: A Brief Diagram

text

Factory Linksys (slot 1) ────────────────┐
                                          │ remained working
Web flash of factory.bin                  │
          │                               │
          ▼                               │
Slot 2: kernel2 + rootfs2                 │
          │                               │
          ├─ kernel boots                  │
          └─ UBI not found                │
                    │                     │
      bad block 0x01DE0000                │
      rootfs2 expected at 0x01F00000      │
      UBI# found from 0x01F20000          │
                    │                     │
                    ▼                     │
           UART → U-Boot                  │
                    │                     │
           TFTP → initramfs (RAM)         │
                    │                     │
           verify /proc/mtd               │
                    │                     │
        extract rootfs.ubi                │
                    │                     │
      ubiformat /dev/mtd6 -f ...          │
                    │                     │
           ubiattach + ubinfo             │
                    │                     │
                    ▼                     │
      OpenWrt 23.05.6 boots      ◄────────┘

Key Takeaways

  1. Do not equate a kernel failing to boot with a damaged kernel. Here U-Boot loaded the kernel successfully, and the failure occurred at the UBI stage.
  2. Read the actual addresses and NAND state. A bad block inside the kernel area could have affected the layout when the firmware was written. The match between the assumed and actual addressing must be verified, not guessed.
  3. Use initramfs as a rescue tool. This made it possible to work with MTD directly without overwriting the working first slot.
  4. Do not confuse mtd5 and mtd6. In our layout, mtd5 covers the second slot, while mtd6 is the area of its rootfs needed for UBI.
  5. Verify SHA256 and the UBI signature. A wrong image during direct NAND writing is significantly more dangerous than a standard installation via the GUI.
  6. Do not change U-Boot variables unnecessarily. Recovering OpenWrt 23.05.6 did not require running saveenv or replacing the bootloader.
  7. Do not blindly transfer instructions between OpenWrt versions. For OpenWrt 24.10+ on the EA4500, the documentation describes increasing the kernel read size from 0x300000 to 0x400000; this separate step was not performed in our case.

Files and Scripts Attached to the Article

text

ea4500_openwrt_recovery/
├── EA4500_OpenWrt_Recovery.md          ← this article
├── README.md                           ← package contents and quick start
├── firmware/
│   └── README_DOWNLOAD.md             ← official links and SHA256
├── scripts/
│   ├── prepare_firmware.py             ← download, verify SHA256, and extract UBI
│   ├── start_http_server.cmd           ← HTTP server for transferring UBI
│   ├── check_ram_openwrt.sh            ← safe MTD check from initramfs
│   └── check_rootfs_ubi.sh             ← safe rootfs.ubi check
└── logs/
    └── recovery_key_outputs.txt       ← saved key diagnostic logs

The binary firmware files and the generated rootfs.ubi appear in firmware/ after running prepare_firmware.py. The article intentionally does not include a universal automatic ubiformat script: the dangerous operation must be performed manually only after verifying the MTD map.

Useful Links