Hacking TP-Link MR3220, openWRT and Debian in chroot

This page describes hacking of TP-Link MR3220 and running Debian on it. The procedure of running Debian can be used on other similar devices too.

TP-Link MR3220 is a very cheap (500 CZK or $30) device featuring

(more detailed info…)

With original firmware, it is usable as a soho wifi router, however, you can run full-featured linux distro (Debian) on it!

Flashing openWRT

It's easy, just download the prebuilt firmware (local mirror of a tested build from 2011-10-27), go to web admin, upload and wait a minute.

Installing necessary packages

opkg install ip
opkg install usbutils
opkg install kmod-usb-storage
opkg install kmod-fs-ext4 

Soldering serial port

If you brick your system, you can always debug it with a serial port or boot fresh working image.

Serial port communicates on 3.3V TTL levels and the pinout is described here. It is a bit difficult to solder GND, because that small pin is directly connected with the big groundplate, so take GND somewhere else.

Configuration of Minicom is

pu port             /dev/ttyUSB0
pu baudrate         115200
pu rtscts           No

Then power on your device. When Autobooting in 1 seconds appears, type tpl as fast as possilbe (try it multiple times :-). Hooray, you are in an U-Boot prompt!

Booting custom image from TFTP

Connect to LAN interface and set up a TFTP server on Enter to U-Boot:

tftpboot 0x81000000 filename_on_the_tftp_server  # download file to specified address in RAM
bootm 0x81000000                                 # fire it!

If you want to write this new firmware to flash, use

erase 0x9f020000 +0x3c0000                       # erase old firmware
tftpboot 0x81000000 filename_on_the_tftp_server  # download file to specified address in RAM
cp.b 0x81000000 0x9f020000 0x3c0000              # copy it from RAM to flash

Running Debian

We can connect mass-storage device with installed Debian and run it on this device!

Debian-MIPS binaries require CONFIG_MIPS_FPU_EMU in Linux kernel. However, default openWRT kernels don't have this support. You can either build your own image (make kernel_menuconfig and Enable FPU emulation on the first screen) or download my prebuilt image (warning: it is compiled without IPv6 and kernel crypto support, so you probably don't want to use it in production environment!).

Then flash your firmware and install "must-have" packages specified above (ip usbutils kmod-usb-storage kmod-fs-ext4). Create an ext2/3/4 partition on your USB storage (300 MB for minimal system, 1 GB recommended) and try to mount it on your WRT device.

Bootstrapping foreign-architecture Debian system

You can also download my system image. Use it this way: tar xvf debian_mips.tar.bz2 --strip-components=2

We are going to prepare the system on an Intel 486 desktop machine. However, our WRT device is MIPS.

Start with debootstrap as usual

debootstrap --foreign --arch=mips squeeze /some/where http://debian.sh.cvut.cz/debian/

Debootstrap will unpack some packages and then fail on chroot. Now mount the /some/where dir on your target machine and

mount -o bind /dev /mnt/dev
mount -t proc none /mnt/proc
chroot /mnt/some/where /bin/bash
for f in /var/cache/apt/archives/*.deb; do dpkg --unpack --force-all $f; done
dpkg --configure -a

After editing sources.list and apt-get update, you should be able to install Debian packages as usual. Install a SSH server. But remember, we are still chrooted.

Automating the chroot

I decided to mod the openWRT init system do automatically chroot to Debian after booting. I then created my own init in the Debian chroot.

First disable all services in openWRT. The /etc/rc.d/ dir should look like this

root@rubisco:/# ls /etc/rc.d/                                                                       
K40network    K50dropbear   K90network    K99umount     S39usb        S97watchdog                   
K45firewall   K50telnet     K98boot       S05defconfig  S95done       S99sysctl                     
K50cron       K60dnsmasq    K98sysntpd    S10boot       S96led        

Then edit /etc/rc.local

# Put your custom commands here that should be executed once                                        
# the system init finished. By default this file does nothing.                                      
date -s @1320792150                                                                                 
while [ ! -b /dev/sda1 ]; do                                                                        
  date >> /tmp/log                                                                                  
  sleep 1                                                                                           
mount /dev/sda1 /mnt                                                                                
mount -o bind /dev /mnt/dev                                                                         
mount -t proc none /mnt/proc                                                                        
chroot /mnt /myinit                                                                                 
exit 0

We don't want root automatically logged on ttyS0, so create a login script and put appropriate line to inittab

while read foo; do
  if [ "x$foo" == xbrm ]; then
    /bin/ash --login
  sleep 1


I use homebrew init /myinit in the target Debian system.


mount -t sysfs none /sys
/etc/init.d/udev start
sleep 3
hostname rubisco.hrach.eu
dhclient eth1
/etc/init.d/ssh start
mount -a
/etc/init.d/cron start
mount /dev/pts

ntpdate europe.pool.ntp.org
if problems with /dev/pts/ appear when you try to SSH in, try ssh root@machine "/bin/bash -i"

Flash storage considerations

Maybe /var/log, lock, tmp to ramdisk? ext2/3 commit=1200?

There we go…

Blinking LEDs ;-)

echo 0 > /sys/class/leds/tl-mr3x20\:green\:qss/brightness
echo 1 > /sys/class/leds/tl-mr3x20\:green\:qss/brightness

factory NVRAM factory firmware

--Jenda, Nov 2011