Modificando un WRT300n V2

Tengo abandonados en el trastero un par de routers inalámbricos WTR300N V2 (draft N) y voy a hacerles una actualización…
La verdad es que son unos chismes majos, procesador ARM de 266 Mhz, 16Mb de SDRAM, 4Mb de FLASH, wireless atheros draft N de 300Mbps etc, pero el firmware que monta Linksys es muy limitado para las posibilidades que presenta este hardware.
Montaré un puerto serie, un conector JTAG, ampliaré la memoria e instalaré OpenWRT.
Estas modificaciones son aplicables sólo al WRT300n V2, que lleva un chip intel XScale IXP423 de 266, Mhz, el WRT300 V1 usa uno broadcom y es totalmente distinto. El V2 es intel/arm y el V1 es broadcom/mips.
En primer lugar instalo un puerto serie con niveles TTL para la cónsola de depuración. Es imprescindible para tener acceso a él si no esta correctamente configurada la red.
En este foro puede verse el conexionado del puerto serie.

Son 4 pines en línea, el que esta encerrado en un cuadrado de serigrafía es +3,3V, el siguiente RX, TX y el último GND.

En este conector uso mi convertidor RS232-TTL que, aunque usa un MAX232 a 5V trabaja bien con los niveles de este puerto de 3,3V. He aprovechado los conectores de audio de 4 pines de viejos CD-ROM para esta tarea:

Puerto serie

A continuación monto un conector JTAG para asegurarme que podré cambiar el firmware de la flash aunque estropee el bootloader que lleva de fábrica.

Jtag

Pinout del conector JTAG.

He construido un cable tipo wiggler para comunicarme por el JTAG con el router, visto aquí (cable wiggler). Yo he usado un conector de 12 pines en lugar de uno de 14, porque es lo que tenía a mano, aprovechando que 2 no se usaban.
Por último he cambiado la memoria, para ampliarlo de 16Mb a 32Mb. Este router viene con 2 memorias SDRAM a 133Mhz de 1Mb x 16 bits x 4 bancos. Yo las he sustituido por unas de 2Mb x 16bits x 4 bancos tipo K4S281632d que he obtenido de viejos módulos SDRAM (de los que montaban los PIII) de 256Mb (8 chips de 32 Mb). También sirven unas HY57V281620h

SDRAM

Ahora enciendo el router y compruebo la salida de la cónsola mediante el cable serie y un programa de terminal como realterm configurado a 115200 baudios 8n1. Para sacar el máximo partido a la cónsola de programación mejor usar un programa de telnet con posibilidad de puerto serie como PuTTY. Comienzan los problemas: el linux solo reconoce 16Mb de RAM:

RedBoot(tm) bootstrap and debug environment [ROMRAM]
Red Hat certified release, version 2.02 - built 14:59:11, Aug 16 2006

Platform: Intel Generic Residential Gateway (IXP42X 266MHz) BE
Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.

RAM: 0x00000000-0x01000000, [0x00066480-0x00ff4000] available
FLASH: 0x50000000 - 0x50400000, 64 blocks of 0x00010000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort
RedBoot> boot;exe
eRcOmM signature found
Copying kernel. Size: 11c190
Using base address 0x00400000 and length 0x0011c190
Uncompressing Linux.............................................................................. done, booting the kernel.
Linux version 2.6.13.2 (root@5c10-187-9-1) (gcc version 3.4.3) #1 Thu Oct 12 14: 53:29 CST 2006
CPU: XScale-IXP42x Family [690541f1] revision 1 (ARMv5TE)
Machine: ADI Engineering Coyote
Memory policy: ECC disabled, Data cache writeback
CPU0: D VIVT undefined 5 cache
CPU0: I cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
CPU0: D cache: 32768 bytes, associativity 32, 32 byte lines, 32 sets
Built 1 zonelists
Kernel command line: console=ttyS0,115200 root=/dev/mtdblock2 noinitrd rootfstyp e=squashfs mem=16M@0x00000000 init=/sbin/init
PID hash table entries: 128 (order: 7, 2048 bytes)
Console: colour dummy device 80x30
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 16MB = 16MB total
Memory: 13316KB available (1686K code, 838K data, 324K init)

Buscando en foros deduzco que el problema es del BootLoader (RedBoot) que es el que se encarga de inicializar los periféricos del procesador (incluido el controlador de SDRAM). El kernel de linux lee la configuración que esta usando el RedBoot. Necesito un RedBoot que reconozca los 32Mb de RAM.

En primer lugar necesito un PC con Linux, yo estoy usando OpenSuse 11.1 pero podrían usarse otras versiones o «sabores».

A continuación descargo los fuentes OpenSource del firmware de Linksys: fuentes GPL del wrt300nV2 o Fuentes desde Cisco (Linksys). Lo descomprimo en ~/WRT300N-GPL-v2.00.21
Hacer «cd ~/WRT300N-GPL-v2.00.21» y ejecutar «./build» como root para que compile, de esta forma probamos que todo esta bien configurado para compilar los fuentes originales. Los binarios generados quedan en «~/WRT300N-GPL-v2.00.21/binfile». «wrt300n.bin» es el bootloader+linux completo. Sin embargo esto no compila el bootloader, sino que usa un binario que ya existía en /binfile.

Para compilar el bootloader RedBoot, que esta incluido en los fuentes originales dentro del directorio «/wrt300n_redboot_GPL» es necesario descargar la toolchain «i686-pc-linux-gnulibc2.2-x-xscale-elf». Extraigo el fichero «Install» y ejecuto como root «./Install — file=i686-pc-linux-gnulibc2.2-x-xscale-elf.tar.Z». Esto descomprime el fichero y lo instala en «/opt/redhat/xscale-030422»

Pruebo a compilar el RedBoot:
«export PATH=/opt/redhat/xscale-030422/H-i686-pc-linux-gnulibc2.2/bin:$PATH»
(Para que makefile encuentre una utilidad llamada «ecosconfig» de la toolchain que se instaló en /opt/redhat/xscale-030422/H-i686-pc-linux-gnulibc2.2)
Como estoy usando una versión «moderna» de linux, en comparación a la que se usó para crear la toolchain, obtengo un error cuando make ejecuta ecosconfig, porque busca una librería antigua: «libstdc++.so.5». La consigo instalando el paquete «libstdc++33» con yast como root.
Compilar el RedBoot puede hacerse como usuario normal:
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL»
«make»
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/build/wrt300n/install/bin»
Copio el RedBoot recién construido para evitar confundirlo:
«mv redboot.bin a redboot.bin.old»

Ahora ya he probado que todo va bien y que puedo compilar los fuentes que he descargado. Tengo que modificar el RedBoot para que reconozca los 32Mb de mi placa:
En primer lugar he de cambiar un par de switches en la configuración del RedBoot. Tal y como viene configurado en los fuentes descargados no permite usar todas los comandos disponibles.
En «~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/options/startup_ROMRAM.ecm» modifico estas dos líneas:
cdl_component CYGSEM_REDBOOT_FLASH_CONFIG {
user_value 1
};
cdl_option CYGOPT_REDBOOT_FIS {
user_value 1
};

Anteriormente tenían «user_value 0″.

Cambio los defines adecuados para que reconozca los 32Mb:
En:»~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/packages/hal/arm/xscale/grg/current/include/grg.h»
Me aseguro de que NO este definido «WRT300N-16MB» para que defina 32Mb de RAM.
#undef WRT300N-16MB
// #define WRT300N-16MB

Este paso es MUY IMPORTANTE, yo dejé «bricked» mi router antes de descubrirlo. El RedBoot, tal y como viene configurado en los fuentes, solo soporta un tipo de memoria FLASH de 8Mb. Si se compila y se instala tal cual, al arrancar, no detectará la FLASH y será imposible cargar el linux ni sustituir el RedBoot. Esto es debido a que los routers comerciales suelen llevar una FLASH de sólo 4Mb tipo MX29LV320, compatible con AM29LV320D.
Existen unos defines que permiten configurar el RedBoot con los drivers adecuados para varios tipos de FLASH. Comprobar los tipos de FLASH soportadas en «~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPLpackagesdevsflashamdam29xxxxxv2_0includeflash_am29xxxxx_parts.inl»
Para que soporte la flash MX29LV320 y AM29LV320D yo añado en:
«~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/packages/hal/arm/xscale/grg/current/misc/redboot_ROMRAM.ecm»
y en
«~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/packages/hal/arm/xscale/grg/current/misc/redboot_RAM.ecm»

cdl_option CYGHWR_DEVS_FLASH_AMD_AM29LV320D {
inferred_value 1
};

Añado una cadena de texto para que cuando se ejecute el RedBoot pueda identificar que es el que yo he modificado:
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/packages/redboot/current/src/»
En version.c añado «compiLed by helitp@arrakis.es»

Para compilar de nuevo el RedBoot:
«export PATH=/opt/redhat/xscale-030422/H-i686-pc-linux-gnulibc2.2/bin:$PATH»
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL»
«make»
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/build/wrt300n/install/bin»
«mv redboot.bin redbot_mio.bin»

Ahora habría que probarlo, pero ¿y si falla y el router queda inutilizado?. Pues existe una forma de probar el RedBoot sin tener que flashearlo. Puede cargarse en RAM usando el redboot original y ejecutarlo. Si no va como esperamos, al rearrancar el router usará de nuevo el de la flash. Si todo va bien podemos flashearlo definitivamente.
Para preparar un RedBoot que corra en ram:
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL»
«cp Makefile Makefile.romram»
cambio en Makefile
ecosconfig import ${ECOS_REPOSITORY}/hal/arm/xscale/grg/current/misc/redboot_ROMRAM.ecm &&
por
ecosconfig import ${ECOS_REPOSITORY}/hal/arm/xscale/grg/current/misc/redboot_RAM.ecm &&
«cp Makefile Makefile.ram»
«make»
«cd ~/WRT300N-GPL-v2.00.21/wrt300n_redboot_GPL/build/wrt300n/install/bin»
«mv redboot.img redboot_mio.img»
Llegado a este punto tendremos un «redboot_mio.bin» que creamos que es el que corre desde flash y un «redboot_mio.img» que es el que corre desde ram. Para construir uno u otro copiar «cp Makefile.romram Makefile» o «cp Makefile.ram Makefile» y luego ejecutar «make». Dejo aquí mis dos RedBoot ya compilados.

Ahora usaré otro ordenador con Windows XP para comunicarme con el router. Descargo tftpd32. Es un pequeño programa servidor de tftp que enviará los ficheros al router cuando se los pidamos a través del RedBoot.
Conecto un cable ethernet entre el wrt300n y el PC, configurando la terjeta de red en IP:192.168.0.3 NETMASK:255.255.255.0. Esta es la dirección IP que usa por defecto el RedBoot para conectar con el tftp. Arranco tftpd32 desde el directorio donde tengo los binarios del RedBoot y escojo en ua pestaña de IP la de la tarjeta ethernet 192.168.0.3
El RedBoot configurará la IP del wrt300n en 192.168.0.10 unos segundos durante el arranque, mientras tiene el control. Puede comunicarse por TELNET con Putty configurando telnet, servidor en 192.168.0.3, puero 9000 pero es mas fiable el cable serie.

Arranco en Putty una conexión serie a 115200 baudios y conecto el convertidor rs232-TTL. Conecto el router y podré ver en el Putty:

RedBoot(tm) bootstrap and debug environment [ROMRAM]
Red Hat certified release, version 2.02 - built 14:59:11, Aug 16 2006

Platform: Intel Generic Residential Gateway (IXP42X 266MHz) BE
Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.

RAM: 0x00000000-0x01000000, [0x00066480-0x00ff4000] available
FLASH: 0x50000000 - 0x50400000, 64 blocks of 0x00010000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort

En ese momento pulso CTRL-C para abortar el arranque del linux y comenzar a usar el RedBoot.
Ejecutamos el comando «fis» (Flash Image System):
fis
solo debera mostrar disponibles erase y write, es el redboot original
ether (esto activa la ethernet)
ip -h 192.168.0.3 (esto configura la ip del host, solo es necesario si esta en una ip distinta a la predefinida 192.168.0.3)
load redboot_mio.img (esto cargará en la RAM del wrt300n el fichero por ftpd)
go (ejecuta el programa cargado)
arrancará un nuevo redboot, parar el arranque de linux con CTRL-C
Comprobar que muestra la cadena «compiLed by helitp@arrakis.es» incluida en la modificacion del redboot
Comprobar que detecta la flash! si no es asi comprobar el tipo de flash montada en el wrt300n y añadir los modelos correctos en redboot_RAM.ecm o redboot_RAMROM.ecm
Ejecutar:
fis
debera mostrar los comandos init, load, list etc ademas de los erase y write, es el redboot modificado
Una vez comprobado que todo va bien podemos cargar el RedBoot definitivo y grabarlo en la FLASH, reiniciamos el router y pulsamos CTRL-C:
ether
ip -h 192.168.0.3
load -r -b 0x70000 -m tftp redboot_mio.bin (esto carga el fichero binario en la dirección 0x70000)
fis write -f 0x50000000 -b 0x70000 -l 0x4f978 (graba el el cotenido de la dirección 0x70000 longitud 0x4f978 enla flash)
reset (reinicia el router)
Es importante saber que en la dirección de memoria 0xCFFA0 se guarda la MAC del router. Si se sobreescribe por error la MAC se perderá. Puede recuperarse preparando un fichero binario de 6 bytes con la MAC en mac.bin y subiéndolo al router con
load -r -b 0xCFFA0 -m tftp mac.bin
luego subir el RedBoot y grabarlo TODO junto:
load -r -b 0x70000 -m tftp redboot_mio.bin
fis write -f 0x50000000 -b 0x70000 -l 0x60000
También es interesante saber que los parámetros por defecto del RedBoot pueden modificarse con
fconfig -i
Run script at boot: true
Es script de inicio para autoarranque del linux debe ser:
Boot script:
ether
fis load kernel
exec

Ahora, en el PC con Linux, descargo las fuentes últimas de OpenWrt
cd ~/
svn co svn://svn.openwrt.org/openwrt/trunk
svn checkout svn://svn.openwrt.org/openwrt/trunk/ ~/trunk/
cd ~/trunk/
Descargo tambien la última versión de LUCI, el interfaz gráfico basado en web para la configuración de OpenWrt:
./scripts/feeds update packages luci
./scripts/feeds install -a -p luci

Para seleccionar opciones adecuadas para el wrt300n:
make menuconfig
Esto generará un fichero .config con la configuración deseada. Este es el mio.

Para compilar:
make

en «~trunk/bin/ixp4xx/» quedan las imagenes binarias. Copio «openwrt-wrt300nv2-zImage» y «openwrt-ixp4xx-generic-squashfs.img» al PC con Windows, al directorio donde tengo corriendo el tftpd.
Desde el RedBoot hago:
fis init (formatea la flash)
load -r -b 0x6b000 -m tftp openwrt-wrt300nv2-zImage (sube al router el kernel de linux)
fis create kernel (graba el kernel en una particoión de flash)
load -r -b 0x6b000 -m tftp openwrt-ixp4xx-generic-squashfs.img (sube el sistema de ficheros al router)
fis create rootfs (graba el sistema de ficheros en otra partición)
fis load kernel (carga el kernel en RAM)
exec (lo ejecuta)

RedBoot(tm) bootstrap and debug environment, compiLed by helitp@arrakis.es [ROMRAM]
Red Hat certified release, version 2.02 - built 21:54:28, Apr 4 2011

Platform: Intel Generic Residential Gateway (IXP42X 266MHz) BE
Copyright (C) 2000, 2001, 2002, 2003, 2004 Red Hat, Inc.

RAM: 0x00000000-0x02000000, [0x0006ac98-0x01fe1000] available
FLASH: 0x50000000 - 0x50400000, 64 blocks of 0x00010000 bytes each.
== Executing boot script in 2.000 seconds - enter ^C to abort
RedBoot> ether

Trying NPE-B...success. Using NPE-B with PHY 16.
Ethernet eth0: MAC address 00:18:39:2f:fa:74
IP: 192.168.0.10/255.255.255.0, Gateway: 0.0.0.0
Default server: 192.168.0.125
RedBoot> fis load kernel
RedBoot> exec
Using base address 0x0006b000 and length 0x000d5724
Uncompressing Linux... done, booting the kernel.
Linux version 2.6.37.6 (heli@asus) (gcc version 4.5.2 (Linaro GCC 4.5-2011.02-0) ) #7 Fri Apr 8 06:50:08 CEST 2011
CPU: XScale-IXP42x Family [690541f1] revision 1 (ARMv5TE), cr=000039ff
CPU: VIVT data cache, VIVT instruction cache
Machine: Linksys WRT300N v2
Memory policy: ECC disabled, Data cache writeback
Built 1 zonelists in Zone order, mobility grouping on. Total pages: 8128
Kernel command line: root=/dev/mtdblock2 rootfstype=squashfs,jffs2 noinitrd console=ttyS0,115200
PID hash table entries: 128 (order: -3, 512 bytes)
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 32MB = 32MB total
Memory: 30016k/30016k available, 2752k reserved, 0K highmem

Ahora ya detecta los 32Mb correctamente. Ya esta todo. Este kernel con LUCI va muy bien con 32Mb de RAM pero iba muy lento y se podía colgar al usar LUCI en un router original con 16Mb de ram.