Day: October 21, 2012

How to transfer files to a Linux embedded system over serial

Imagine a scenario where you have a device running an embedded Linux system, but you flashed a bad firmware which doesn’t let you to have access to Ethernet/WiFi on this device. You only have access to it is using serial cable.

First thing you think is to use U-Boot transfer support over serial, but unfortunately you don’t have this option:

U-Boot 1.1.4 (Feb 27 2009 - 12:06:20)

AP94 (ar7100) U-boot 0.0.12
In:    serial
Out:   serial
Err:   serial
Net:   eth0, eth1
### main_loop entered: bootdelay=1

### main_loop: bootcmd="bootm 0xbf050000"
Hit any key to stop autoboot:  0 
### main_loop: got key string, abort boot
### main_loop: no define CONFIG_AUTOBOOT_KEYED
### main_loop: CFG_HUSH_PARSER
### main_loop: parse_string_outer
## Booting image at bf050000 ...
   Image Name:   MIPS OpenWrt Linux-
   Image Type:   MIPS Linux Kernel Image (lzma  compressed)
   Data Size:    873389 Bytes = 852.9 kB
   Load Address: 80060000
   Entry Point:  80060000
   Verifying Checksum ... OK
   LZMA Umcompressing Kernel Image ...    Image loaded from 80060000-802e82d8

As you can see it is not entering on U-Boot prompt because CONFIG_AUTOBOOT_KEYED is not defined.

Then we need to find another alternative.

Unfortunately we are not lucky, the linux system on this router doesn’t have “lrz” program installed.

But all is not lost, I have an idea!!!

The “lrz” program is relatively small, about 60KB, then I can convert it to text and past on Linux terminal running inside de box (using echo and pasting text). Unfortunately I cannot use base64 program, because it also is not install on this Linux system.

The remaining option is using hexdump (xxd) to convert the binary on text bytes and the recover it, as I suggested some times ago (link in Portuguese).

So, let to do it, first thing to do is download the package containing the lrz program (lrzsz_0.12.20-1_ar71xx.ipk).

Hmm, this is 59792 bytes long, no problem I hope.

Now let to convert it from binary to text:

$ xxd -g1 lrz > output.txt

We need to remove the “00xxxxx: ” from beginning of each line and the 16 chars representation at end of each line:

$ sed -i 's/^\(.\)\{9\}//g' output.txt
$ sed -i 's/\(.\)\{16\}$//g' output.txt

Now let to convert this text back to binary to confirm it is working:

$ for i in $(cat output.txt) ; do printf "\x$i" ; done > mylrz

Let to check if they are have same binary sequence:

$ md5sum mylrz 
08334803ce751a1f021a4de0c9c0e849  mylrz
$ md5sum lrz 
08334803ce751a1f021a4de0c9c0e849  lrz

Now just let to copy and past this content of text to linux box terminal.

I suggest you to use picocom instead minicom to do it:

$ picocom -b 115200 -l -r /dev/ttyUSB0

I started trying to send line by line this way:

root@OpenWrt:/# echo "7f 45 4c 46 01 02 01 00 01 00 00 00 00 00 00 00" >> mylrz.txt

In few seconds it became a very boring task and very prone to fail.

Then I decide to send commands directly to terminal over serial using shell script.

Leave picocom…

After some tests I noticed it is not possible to send many characters to terminal (over /dev/ttyUSB0), no more 2 bytes by time.

Then the command became this long line:

$ j=0; echo -n -e "ec" > /dev/ttyUSB0; echo -n -e "ho" > /dev/ttyUSB0; echo -n -e " \"" > /dev/ttyUSB0; for i in $(cat output.txt); do j=$(($j+1)); echo -n -e "$i" > /dev/ttyUSB0; echo -n -e " " > /dev/ttyUSB0; if [ $(expr $j % 16) -eq 0 ]; then echo -n -e "\" " > /dev/ttyUSB0; echo -n -e ">>" > /dev/ttyUSB0; echo -n -e "lr" > /dev/ttyUSB0; echo -n -e "z" > /dev/ttyUSB0; echo " " > /dev/ttyUSB0; echo -n -e "ec" > /dev/ttyUSB0; echo -n -e "ho" > /dev/ttyUSB0; echo -n -e " \"" > /dev/ttyUSB0; fi; done; echo -n -e "ec" > /dev/ttyUSB0; echo -n -e "ho" > /dev/ttyUSB0; echo "\"" > /dev/ttyUSB0

Verify if file lrz file was created:

root@OpenWrt:/# ls -l lrz
-rw-r--r--    1 root     root        183113 Jan  1 01:19 lrz

Rename it to lrz.txt:

root@OpenWrt:/# mv lrz lrz.txt

Convert the text file to binary:

root@OpenWrt:/# for i in $(cat lrz.txt) ; do printf "\x$i" ; done > lrz
root@OpenWrt:/# md5sum lrz
08334803ce751a1f021a4de0c9c0e849  lrz

Give it permission to execute and move to /usr/bin:

root@OpenWrt:/# chmod 555 lrz
root@OpenWrt:/# mv lrz /usr/bin/

Execute the lrz command to receive file:

root@OpenWrt:/# lrz -Z

Leave picocom program (Ctrl+A+X)

In you computer execute this command to transfer firmware to your router:

$ sz --zmodem /tmp/openwrt-ar71xx-dir825.bin > /dev/ttyUSB0 < /dev/ttyUSB0

Check with md5sum to verify if it is correct:

root@OpenWrt:/tmp# md5sum openwrt-ar71xx-dir825.bin 
90e938ed4a5aa825495c2b0b099655e7  openwrt-ar71xx-dir825.bin

alan@aureo:~/Desktop/serial$ md5sum /tmp/openwrt-ar71xx-dir825.bin 
90e938ed4a5aa825495c2b0b099655e7  /tmp/openwrt-ar71xx-dir825.bin

Now just update your system using this new firmware:

root@OpenWrt:/# sysupgrade /tmp/openwrt-ar71xx-dir825.bin