Project Overview¶
What This Project Is¶
This project installs OpenWrt on a Cisco Meraki MR18 802.11n access point via JTAG, replacing the factory Cisco firmware with a fully open Linux distribution. It also provides a persistent fix for a hardware bug in the AR8035 Ethernet PHY that prevents RX traffic from reaching the SoC.
Why It Exists¶
The Meraki MR18 is cloud-locked by Cisco. Without an active Meraki dashboard license:
- There is no local web UI—the device phones home to Cisco's cloud or does nothing.
- There is no TFTP recovery mode accessible without the Meraki bootloader password.
- There is no serial console shell—the Meraki Nandloader drops straight into a locked-down Linux environment.
- The bootloader disables JTAG probing within ~2 seconds of power-on.
JTAG is the only way in. This project exploits a narrow timing window during the Nandloader's early boot to halt the CPU via EJTAG, load an OpenWrt initramfs kernel directly into RAM, boot it, trigger failsafe mode, and flash a sysupgrade image to NAND. The entire process is automated with Python.
After OpenWrt is installed, a separate bare-metal MIPS binary (ar8035-fix) corrects the AR8035 PHY's RGMII RX clock delay bug, restoring bidirectional Ethernet. A hotplug script makes this fix persistent across reboots.
Status¶
Complete. OpenWrt 25.12.0 (ath79/nand) runs from NAND. Ethernet is fully bidirectional.
Hardware Summary¶
| Component | Part | Details |
|---|---|---|
| SoC | QCA9557 / AR9344 | MIPS 74Kc, 560 MHz, big-endian |
| Flash | 128 MB NAND | Meraki Nandloader + OpenWrt sysupgrade |
| RAM | 128 MB DDR | Physical 0x00000000--0x07FFFFFF |
| Wireless | 2x 3x3 MIMO radios | 2.4 GHz + 5 GHz (802.11n) |
| Ethernet | AR8035 PHY | RGMII, single GbE port, PHY addr 3 |
| Power | 802.3af PoE or 12 V barrel | Project uses 12 V / 1.5 A bench supply |
| JTAG adapter | ESP-Prog (FT2232H) | Channel 0: JTAG, Channel 1 (UART): console + EN |
Firmware¶
| Image | File | Size | MD5 |
|---|---|---|---|
| Initramfs | openwrt-25.12.0-ath79-nand-meraki_mr18-initramfs-kernel.bin |
6,931,053 bytes | db191ecf0224f030365d604aa3919da9 |
| Sysupgrade | openwrt-25.12.0-ath79-nand-meraki_mr18-squashfs-sysupgrade.bin |
- | 53e272bed2041616068c6958fe28a197 |
Repository Layout¶
mr18-openwrt/
+-- README.md Quick-start summary and directory index
+-- docs/ Full project documentation
| +-- overview.md This file—project overview
| +-- guides/
| | +-- prerequisites.md Hardware BOM, software deps, firmware downloads
| +-- reference/
| | +-- address-map.md Every address constant with derivation
| | +-- script-reference.md CLI usage for every script
| +-- bugs/ Bug investigation write-ups
| +-- technical/ Deep-dive technical documents
+-- firmware/
| +-- openwrt-...-initramfs-kernel.bin RAM-bootable kernel (JTAG load target)
| +-- openwrt-...-squashfs-sysupgrade.bin NAND flash image
+-- jtag/
| +-- mr18_flash.py Main automation: PSU, OpenOCD, JTAG halt, RAM load, boot
| +-- verify_asm.py Validates hand-encoded MIPS machine code via Capstone
| +-- mr18.cfg OpenOCD target config for AR9344 / QCA9557
| +-- esp-prog.cfg OpenOCD interface config for ESP-Prog (FT2232H)
+-- ar8035-fix/
+-- Makefile Cross-compilation build (make / make docker / make clean)
+-- ar8035_start.S MIPS O32 assembly _start (no libc, raw syscalls)
+-- ar8035.c AR8035 MDIO register fix (RX clock delay + hibernation)
+-- ar8035-fix Pre-built MIPS32 ELF binary (5592 bytes)
+-- send_binary.py Transfers ar8035-fix to MR18 over UART and runs it
+-- uart_transfer.py Transfers sysupgrade image over UART, auto-runs sysupgrade
Documentation Index¶
See docs/guides/prerequisites.md for hardware and software requirements. See docs/reference/address-map.md for the memory map and address derivations. See docs/reference/script-reference.md for script CLI usage and configuration.