OpenWRT on the Experia Box v8 (Arcadyan Astoria Networks VGV7519)

Above: my upstairs neighbour happens to run a stock Experia Box v8 in our stairwell.

February 6, 2016

This beauty cost me 4 euros at the local thrift shop. I picked it up in the hope that it had OpenWRT support, and because I liked the looks of the two USB3.0 ports, the gigabit Ethernet switch, and the phone modem. I knew this was a recent model, so I hoped the wireless chip would also probably be up to the latest standards. And indeed, it's 802.11n. (Colleagues later told me that this model is known among consumers for having poor wireless connectivity, something that web reviewers also note – oh well.)

Checking out the OpenWRT wiki, this router seems to be supported by OpenWRT-the-software, but the instructions are really unclear on how to actually load it onto the router. Though I did manage to get it working, it required a Raspberry Pi, extra pin headers, squid cables, JTAG tomfoolery and some software patching. Here's my experiment log.

Before we start...

I'll describe the process I used to flash OpenWRT on a second-hand, unbranded unit that I bought fair and square. I'm not liable for anything you do with this information. This will void your warranty. Use at your own risk. If you plan to follow along, make sure you actually own your unit and not your provider. OK, disclaimer aside...

Opening the case

The case is held together by two screws underneath the rubber feet and eight click fingers. It's possible to open the case without structually damaging the plastic, but probably not without bruising the case. The click fingers are very big and held in place by very deep wedges. Expect to use a fair amount of force to wiggle everything free. The plastic is quite flexible though. The first click finger is a freebie and unlatches by pressing the hole in the bottom with a screwdriver. The other ones eventually come loose if you prod and twist with a screwdriver long enough. I had the most luck with a kind of twisting motion.

Getting a serial console

It's possible to get a serial console on this device, because the header is there and even has pins, but in stock configuration it's all but useless. Other people have figured out that the serial console just drops you in a password-protected bootloader, from which there is no escape. The factory password remains uncracked.

JTAG and the Raspberry Pi

Time for a more serious approach: accessing the JTAG debug port. The PCB has an empty row of pins for a 14-pin EJTAG header. Solder in a row of pin headers:

Now we need to find a way of setting up a JTAG connection. I didn't have any JTAG adapters lying around, but like pretty much everyone, I do have a Raspberry Pi 2. It turns out that OpenOCD, an open-source on-chip debugger which we will use, can actually bitbang the JTAG protocol using the Raspberry Pi's standard set of GPIO pins! So all we need is to hook up the Pi to the JTAG header on the router with some squid cables. Here is a pinout diagram, showing which headers to connect:

Don't connect VREF, feed the router off its own power supply. In the end you'll get something like the cable soup below. Note that I also attached the serial header to the Raspberry Pi with a cheap serial-to-USB converter. This let me monitor the serial console through Minicom. The serial console is 115200 baud, 8N1.

OpenOCD

Unfortunately, the OpenOCD available in the Raspbian package manager is version 0.5.0, which doesn't support GPIO bitbanging yet. We'll need to download the latest version and build it ourselves. I followed this excellent description and just copypasted their commands.

Incidentally, I had to patch OpenOCD to avoid getting a register "a0" not found error when trying to flash the image. It turns out that the a and t registers are just aliases of the r registers, so it was enough to change the names. (It looks like upstream has already fixed this.)

We're extremely lucky that some anonymous hero already figured out the OpenOCD config for this board. Clone the Git repo:

# git clone https://github.com/openwrt-vgv7519/openocd-lantiq

Create an empty project directory and copy openocd.cfg to it. Edit the file to use the Raspberry Pi GPIO interface as the debug port:

source [find interface/sysfsgpio-raspberrypi.cfg]

We can now start OpenOCD inside our work dir and confirm that we have a connection to the board:

# openocd -s ../openocd-lantiq  # the path to the Git repo we just cloned
Open On-Chip Debugger 0.10.0-dev-00196-g1919dbb (2016-01-24-21:12)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
SysfsGPIO nums: tck = 11, tms = 25, tdi = 10, tdo = 9
SysfsGPIO num: trst = 7
trst_only separate trst_push_pull
adapter_nsrst_delay: 100
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
jtag_ntrst_delay: 100
force hard breakpoints
flash_file
Info : SysfsGPIO JTAG/SWD bitbang driver
Info : JTAG only mode enabled (specify swclk and swdio gpio to add SWD mode)
Info : This adapter doesn't support configurable speed
Info : JTAG tap: vrx200.cpu0 tap/device found: 0x00001183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0001, ver: 0x0)
Info : JTAG tap: vrx200.cpu1 tap/device found: 0x00000183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0000, ver: 0x0)

Awesome!

Uploading a bootloader to RAM

Our ultimate goal is to overwrite the locked bootloader in flash memory, so that we can install an OpenWRT image. But before we do that, we can try to load a copy of the bootloader in volatile RAM and run it non-destructively, to test if it works. Creating a bootloader image would normally be a nontrivial task, but thankfully prebuilt images are available from the same source as the OpenOCD configs. Clone the uboot-bin repository:

# git clone https://github.com/openwrt-vgv7519/uboot-bin

This will give you two U-Boot images, a RAM and a flash version. What the repo doesn't tell you is that you need to byte-swap the images before loading them into the router, or they won't work. (The router will go into an infinite loop on the serial console.) Well, they do mention it, but it's hidden in the comments to a commit.

There's more. On the Raspberry Pi at least, a simple byteswap is not enough. You need to first swap all bytes in each 32-bit word, and then swap individual bytes. (Same as swapping each block of 2 bytes.) So 0xAABBCCDD first becomes 0xDDCCBBAA and then 0xCCDDAABB. I figured this out by comparing flash dumps to the data I thought I'd written, and noticing that they were not the same. Let's copy the RAM image to our workdir and do a byteswap on the fly with this terrible oneliner:

# perl -e 'while (sysread(STDIN,$d,4)){print pack("N",unpack("V",$d));}' < ../uboot-bin/ram/u-boot.bin | dd conv=swab > u-boot.bin

Now we can start OpenOCD again and attach through Telnet at localhost port 4444. The board configuration includes a predefined vgv7519_uboot_ram procedure, which will upload u-boot.bin from the work directory and run it from RAM:

# telnet localhost 4444
telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> vgv7519_uboot_ram
VGV7519: reset init
JTAG tap: vrx200.cpu0 tap/device found: 0x00001183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0001, ver: 0x0)
JTAG tap: vrx200.cpu1 tap/device found: 0x00000183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0000, ver: 0x0)
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to debug-request, pc: 0xbfc00000
VGV7519: reset init
VRX200: HRST
VRX200: CGU init
VRX200: MC init prepare
VGV7519: MC setup
VRX200: MC init finish
257056 bytes written at address 0x80100000
downloaded 257056 bytes in 33.677269s (7.454 KiB/s)

Through Minicom, I could follow U-Boot's output. The predefined command resumes code execution at the image's entry point, and sure enough, after it was done, Minicom flashed the U-Boot prompt!

Overwriting the bootloader

Now that we can connect to the router and load our own version of U-Boot, let's overwrite the stock bootloader in flash memory to make our changes permanent. We could use the RAM-resident U-Boot itself to flash a new bootloader, but since we already have a JTAG connection, let's use what we have. Create a byteswapped version of the prebuilt flash image:

perl -e 'while (sysread(STDIN,$d,4)){print pack("N",unpack("V",$d));}' < ../uboot-bin/flash/u-boot.bin | dd conv=swab > u-boot.bin

Connect to the board and execute the predefined flash_file u-boot.bin command:

# telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> flash_file u-boot.bin
vrx200: flash_init
JTAG tap: vrx200.cpu0 tap/device found: 0x00001183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0001, ver: 0x0)
JTAG tap: vrx200.cpu1 tap/device found: 0x00000183 (mfg: 0x0c1 (V3 Semiconductor), part: 0x0000, ver: 0x0)
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to debug-request, pc: 0xbfc00000
VRX200: EBU init
VRX200: EBU swap enable
Flash Manufacturer/Device: 0x00c2 0x22cb
flash 'cfi' found at 0xb0000000
auto erase enabled
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
fast_data (0x1e22008c) is within write area (0x1e22010c-0x1e22210c).
Change work-area-phys or load_image address!
Falling back to non-bulk write
vrx200.cpu1: target state: halted
target halted in MIPS32 mode due to target-not-halted, pc: 0x1e220088
wrote 262144 bytes from file u-boot.bin in 925.382263s (0.277 KiB/s)
VRX200: EBU swap disable
vrx200: flashing done

This takes about 15 minutes. Reset or restart the router. On the serial console, you should now be getting an U-Boot prompt:

ROM VER: 1.1.4
CFG 01


U-Boot 2013.04-rc1-g9d05354-dirty (Jul 14 2013 - 13:19:30) VGV7519

Board: Arcadyan VGV7519 VRX288 Board
SoC:   Lantiq VRX288 v1.2
CPU:   500 MHz
IO:    250 MHz
BUS:   250 MHz
BOOT:  NOR
DRAM:  64 MiB
Flash: 16 MiB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   ltq-eth
VGV7519 # 

Finally... flashing OpenWRT

My Raspberry Pi also doubles as a TFTP server. First I downloaded the OpenWRT squashfs image for this board and placed it into /srv/tftp on the Pi. Then I set the tftp connection parameters on the switch and saved them to flash to make them persistent:

VGV7519 # setenv ipaddr 192.168.6.102
VGV7519 # setenv serverip 192.168.6.101
VGV7519 # set ethaddr 00:00:DE:AD:BE:EF
VGV7519 # saveenv
Saving Environment to Flash...
. done
Un-Protected 1 sectors
Erasing Flash...
. done
Erased 1 sectors
Writing to Flash... done
. done
Protected 1 sectors

Time to fetch the boot image. I had some timeout errors at first, but replugging the ethernet plug (into the red WAN port, obviously) made for a smooth connection:

VGV7519 # tftp 0x80700000 openwrt-15.05-lantiq-xrx200-VGV7519NOR-squashfs.image
ltq_phy: addr 0, link 0, speed 10, duplex 0
ltq_phy: addr 1, link 0, speed 10, duplex 0
ltq_phy: addr 17, link 0, speed 100, duplex 0
ltq_phy: addr 19, link 0, speed 100, duplex 0
ltq_phy: addr 5, link 1, speed 1000, duplex 1
Using ltq-eth device
TFTP from server 192.168.6.101; our IP address is 192.168.6.102
Filename 'openwrt-15.05-lantiq-xrx200-VGV7519NOR-squashfs.image'.
Load address: 0x80700000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         ##########################
done
Bytes transferred = 4194308 (400004 hex)

Then at long last, erase flash, write the image to memory and reset:

VGV7519 # erase 0xB0080000 +${filesize}

................................................................. done
Erased 65 sectors
VGV7519 # cp.b 0x80700000 0xB0080000 ${filesize}
Copy to Flash... done
VGV7519 # reset

After resetting, we're back in the U-Boot menu. Now we should be able to manually boot OpenWRT:

VGV7519 # bootm 0xB0080000

Nice, OpenWRT is running! Now let's configure U-Boot to boot the image automatically after a two-second timeout:

VGV7519 # setenv bootdelay 2
VGV7519 # setenv bootcmd bootm 0xB0080000
VGV7519 # saveenv
Saving Environment to Flash...
. done
Un-Protected 1 sectors
Erasing Flash...
. done
Erased 1 sectors
Writing to Flash... done
. done
Protected 1 sectors

And that's it! The Experia Box v8 is now running OpenWRT. You can configure it in the usual way by logging in via Telnet, changing root's password, and then configuring your setup in a browser using lUci. If you ever want to update the OpenWRT image, you can simply TFTP a new image using the new resident bootloader.