MR18 OpenWrt Documentation
This project installs OpenWrt on a Cisco Meraki MR18 access point by exploiting a 2-second JTAG window during boot to halt the CPU, load a kernel into RAM, and flash NAND—bypassing Cisco's cloud lock entirely. JTAG is necessary because the MR18 has no serial bootloader console, no network recovery mode, and no way to interrupt the boot process without physical debug access. The project is complete: OpenWrt 25.12.0 runs from NAND with full bidirectional Ethernet, including a persistent fix for the AR8035 PHY RX clock bug.
Reading Paths
Practical Path—I want to reproduce this
Technical Path—I want to understand how it works
| Step |
Document |
What you learn |
| 1 |
Overview |
Why the MR18 needs JTAG, project scope, boot sequence |
| 2 |
MIPS Memory Model |
KSEG0/KSEG1 segments, D-cache coherency, cached vs uncached access |
| 3 |
JTAG Timing Attack |
The 2-second boot window before Linux disables JTAG |
| 4 |
Image Loading |
Load-verify-fix-launch pipeline over PRACC at ~97 KB/s |
| 5 |
MIPS Assembly |
Hand-encoded 32-bit trampolines, bit-level instruction derivation |
| 6 |
Failsafe Trigger |
Five failed approaches and the working solution |
| 7 |
AR8035 PHY Fix |
RGMII RX clock delay via bare-metal MDIO register write |
| 8 |
UART Transfer |
Hex-over-serial protocol decoded by busybox awk |
| 9 |
OpenOCD Config |
Adapter/target configuration and telnet command interface |
Reference
| Document |
Contents |
| Address Map |
Every address constant with hardware-spec derivation |
| Script Reference |
CLI usage and configuration for all scripts |
| Bug Index |
All 23 bugs catalogued from first power-on to successful boot |
Reading Path Diagram
flowchart TD
START([docs/index.md]) --> CHOOSE{Reading path?}
CHOOSE -->|Practical| P1[Prerequisites]
P1 --> P2[Hardware Setup]
P2 --> P3[Quick Start]
P3 --> P4[Troubleshooting]
CHOOSE -->|Technical| T1[Overview]
T1 --> T2[MIPS Memory Model]
T2 --> T3[JTAG Timing Attack]
T3 --> T4[Image Loading]
T4 --> T5[MIPS Assembly]
T5 --> T6[Failsafe Trigger]
T6 --> T7[AR8035 PHY Fix]
T7 --> T8[UART Transfer]
T8 --> T9[OpenOCD Config]
P4 --> REF[Reference docs]
T9 --> REF
REF --> ADDR[Address Map]
REF --> SCRIPT[Script Reference]
REF --> BUGS[Bug Index—23 bugs]
Quick Links
| Document |
Description |
| Overview |
Project context: why the MR18 needs JTAG and what the project delivers |
| Prerequisites |
Hardware bill of materials, host software, and firmware image downloads |
| Hardware Setup |
JTAG header soldering, ESP-Prog wiring diagram, UART pinout |
| Quick Start |
End-to-end flash procedure from power-off to working OpenWrt |
| Troubleshooting |
Diagnosis steps for JTAG failures, stalled boots, and network issues |
| MIPS Memory Model |
KSEG0/KSEG1 virtual segments, AR9344 D-cache, cached vs uncached coherency |
| JTAG Timing Attack |
Exploiting the 2-second window before Linux disables EJTAG |
| Image Loading |
6.9 MB kernel transfer over PRACC with verify-and-repair loop |
| MIPS Assembly |
Bit-level derivation of every hand-encoded trampoline instruction |
| Failsafe Trigger |
Entering OpenWrt failsafe mode after initramfs boot |
| AR8035 PHY Fix |
Fixing RX silence by writing the RGMII clock delay register over MDIO |
| UART Transfer |
Hex-encoded serial file transfer protocol for sysupgrade images |
| OpenOCD Config |
ESP-Prog adapter config, AR9344 target config, telnet interface |
| Address Map |
Every memory address and register constant used in the project |
| Script Reference |
CLI usage, options, and default values for all Python scripts |
| Bug Index |
All 23 bugs: wrong binaries, PRACC bit-flips, cache incoherence, and more |