TL;DR
While porting the Ruckus H510 to OpenWrt, I wrote 4 bytes to what I
thought was the U-Boot environment partition (mtd8). It was actually
0:CDT — the Qualcomm Configuration Data Table. The device hard-bricked
before U-Boot even loaded. Recovery required an external SPI programmer
and a SOIC-8 clip.
The setup
The H510 (like most embedded Qualcomm boards) has a 4 MiB SPI NOR
that holds the boot chain: SBL1, MIBIB, QSEE, CDT, DDRPARAMS,
U-Boot, and the ART partition. Each is exposed as a /dev/mtdN block
device from the running kernel — and on the stock Ruckus firmware,
every one of them is writable.
I was diagnosing why fw_setenv-style writes to mtd10
(0:APPSBLENV) weren’t persisting on unit B. As a test, I ran a
“surgical” 4-byte dd write to mtd8 to see if other SPI NOR
partitions were writable.
mtd8 happened to be 0:CDT.
The mistake
# The command that bricked the device
dd if=/tmp/4bytes of=/dev/mtd8 bs=1 count=4
A bare dd to a NOR mtd device performs the write, but the
underlying flash AND-merges the new bytes into the existing bits.
Flash can flip 1-bits to 0-bits, but not vice versa. Even a
“harmless” 4-byte write therefore mangles the partition’s first 32
bits.
For 0:CDT, which SBL1 validates by magic number + length, those
first 32 bits are enough.
The crash
On the next power cycle, the board didn’t reach U-Boot. The serial console showed:
B - boot_config_data_table_init, Start
B - Error: CDT is not programmed
B - Boot error occurred!. Error code: 302a
SBL1 (Stage 1 Boot Loader) runs from mask ROM. It validates CDT before loading anything else. Invalid CDT = no U-Boot, no recovery shell, no second chances.
Why this is more dangerous than it looks
No kernel-side write protection. The Ruckus kernel exposes every
SPI NOR partition (SBL1, MIBIB, QSEE, CDT, DDRPARAMS, APPSBL)
as a writable /dev/mtdN. The JEDEC-standard block-protection bits on
the MX25L3233F could be set to write-protect these partitions, but
Ruckus ships them unset.
MTD_POWERUP_LOCK doesn’t help. The stock SPI NOR driver reports
MTD_POWERUP_LOCK in the flags, but it doesn’t implement the
lock/unlock ioctl. flash_unlock /dev/mtd10 returns ENOTSUP
(error 95). Despite the flag suggesting otherwise, flash_erase and
dd write successfully without any unlock step.
MTD numbering is unstable. mtd8 was 0:CDT on the stock
kernel. On a different kernel build, or on OpenWrt, the numbering
shifts:
| Partition | Stock Ruckus mtd | OpenWrt mtd |
|---|---|---|
0:APPSBLENV | mtd10 | mtd8 |
0:CDT | mtd8 | mtd6 |
Board Data | mtd3 | mtd11 |
The one stable anchor is 0:APPSBLENV = mtd10 on both stock
and OpenWrt (by partition name, not number).
Recovery
With SBL1 refusing to boot, the only path is external: CH341A programmer + SOIC-8 clip on the SPI NOR chip.
0:CDT contains DK04.1-C1 reference-design configuration — no
per-unit data — so cross-unit copy is safe. Restore the 64 KiB at
offset 0xc0000 from a backup of another H510 (or the SPI NOR
backup you definitely made before writing to any flash partition).
0:APPSBLENV can be left blank; U-Boot falls back to
compiled-in defaults.
Flash lock investigation
After the recovery, I investigated whether flash_unlock is
needed before writing to SPI NOR or NAND from the stock firmware.
NAND (mtd0, mtd1, mtd2): flags = 0x400 (MTD_WRITEABLE).
No powerup lock. ubidetach + ubiformat work without any
unlock step.
SPI NOR (mtd8–mtd12): flags = 0xc00 (MTD_WRITEABLE | MTD_POWERUP_LOCK). flash_unlock returns ENOTSUP — the stock
kernel’s SPI NOR driver doesn’t implement the lock/unlock ioctl.
Despite the flag, flash_erase and dd write successfully without
any unlock step.
Conclusion: flash_unlock is neither required nor functional
in the no-UART install path. The install sequence is simply
flash_erase → dd.
Rules going forward
Never
dd/cat/echoto any/dev/mtdNwhose partition has a0:prefix, except0:APPSBLENV. Usemtd_debug erasemtd_debug writeif you must, never the raw block device.
Verify mtd numbers against
/proc/mtdbefore every write. The numbering can shift between kernel builds.Capture a full SPI NOR backup before any NOR write.
dd if=/dev/mtd0ro of=/tmp/full-nor.bin bs=4096from the OEM kernel gives you a 4 MiB recovery image you can flash back externally if things go wrong.
What this teaches
“Read-only” and “write-protected” are different things. The Ruckus kernel exposes boot-critical partitions as writable block devices with no enforcement. A typo on the mtd number and the device is hard-bricked before any bootloader can help you.
The CDT brick is a reminder that “no UART” doesn’t mean “no risk” — the OEM kernel gives you enough flash access to brick the device beyond its own bootloader’s reach.