u-boot es un bootloader libre para dispositivos empotrados de bajo poder computacional. Para los no versados en el tema, es algo así como el grub de tu PDA o de tu navegador GPS, pero con notables diferencias, claro está. En ésta receta hablaré sobre como instalarlo y como usarlo para crear distintas particiones en la memoria Flash de tu handheld para, de este modo, poder instalar un sistema arrancable. Para ello, pondré como ejemplo la tarjeta mini2440 que compré hace unos días.

Introducción

Como he comentado anteriormente, u-boot es un bootloader totalmente libre para dispositivos empotrados de bajo poder computacional. Entre sus múltiples posibilidades, están las de particionar la memoría flash del dispostivo en el que se encuentre corriendo, asignar nombre a dichas particiones, cargar imágenes en RAM desde distintas fuentes, volcarlas a flash, y un largo etcétera.... Aquí hablaré de las opciones que a mi juicio pueden ser mas socorridas para la instalación de un Sistema Operativo en un dispositivo empotrado, en mi caso, tomando como ejemplo la tarjeta de desarrollo mini2440 comentada anteriormente, aunque como es de suponer, las mismas operaciones pueden ser realizadas en distintas plataformas hardware. Así pues vamos a lo divertido del asunto ¡¡Empezamos!!

Instalando

El proceso de instalación de u-boot consiste básicamente en cargar una imagen de u-boot en la RAM de nuestrá máquina, bien a través de su propio bootloader, bien a través de un JTAG. Una vez cargado en RAM, se lanza y se le dice que se (auto)guarde en la flash de nuestra tarjeta. En este caso y dependiendo de nuestro hardware, puede sobreescribir a su bootloader original o bien coexistir con él. En cualquier caso, deberás consultar el manual de tu tarjeta para comprobarlo. Para el caso de la MINI2440, ésta viene con dos memorias Flash distintas: por un lado hay una flash NOR de 2MB en la que se encuentra almacenado el bootloader específico para los micros de Samsung (mirco de mi tarjeta de desarrollo): supervivi. Por otro lado, tenemos una memoria flash NAND de 128MB en la que se supone que irá nuestro sistema. En ésta memoria será en la que instalaremos u-boot, configurando posteriormente nuestra mini2440 para que arranque desde ahí (utilizando un conmutador que a tal efecto lleva instalado). Para comenzar, necesitaremos descargarnos tanto el código fuente de u-boot como el software necesario para poder comunicarnos con supervivi y un compilador cruzado para ARM: * u-boot:
javieralso@rigoberto:~$ mkdir uboot; cd uboot
javierlaso@rigoberto:~$ git clone git://repo.or.cz/u-boot-openmoko/mini2440.git
* toolchain: Compiladores cruzados para ARM los hay muchos en esto de GNU. Yo, que seguí al dedillo todo lo que leí en montónes de blogs, usé el siguiente:
javieralso@rigoberto:~$ wget http://www.codesourcery.com/sgpp/lite/arm/portal/package3696/public/arm-none-linux-gnueabi/arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
javieralso@rigoberto:~$ unp  arm-none-linux-gnueabi/arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu.tar.bz2
javieralso@rigoberto:~$ export PATH=$PATH:/home/rigoberto/arm-2008q3/bin
* Friendlyarm package: Se trata de un paquete con un uploader ya compilado para comunicarse con supervivi y de una imagen ya compilada de u-boot, que deberemos utilizar para cargar la imagen que compilemos nosotros. El por qué de usar dos imágenes de u-boot se explicará mas adelante.
javieralso@rigoberto:~$ wget http://www.kernelconcepts.de/~fuchs/micro2440/friendlyarm2440-package.tar.bz2
javieralso@rigoberto:~$ unp friendlyarm2440-package.tar.bz2
Una vez que lo tenemos todo descargado, empezaremos por compilar el bootloader, para ello no debemos olvidar añadir a nuestro PATH la ruta en la que se encuentran los binarios de la toolchain que usemos (como hicimos anteriormente). Una vez hecho esto, podemos pasar a compilar:
javieralso@rigoberto:~$ cd uboot/mini2440
javieralso@rigoberto:uboot/mini2440$ export CROSS_COMPILE=arm-none-linux-gnueabi-
javieralso@rigoberto:uboot/mini2440$ make mini2440_config; make
Después de no mucho tiempo y si todo ha ido bien, deberemos tener un archivo llamado u-boot.bin en la carpeta donde hemos llevado a cabo la compilación. Esa es la imagen de u-boot que se almacenará en la flash de nuestra tarjeta, pero para poder llevar a cabo dicho proceso, necesitaremos otra versión de u-boot (sé que suena bastante raro, y de hecho lo es. Mas adelante explicaré el por qué de ello). Bueno, llegados a este punto, cogemos nuestra mini2440, deslizamos el conmutador superior hacia la izquierda (para configurar el arranque desde la NOR), conectamos un módem nulo desde su puerto serie a un puerto serie de nuestra máquina y el device USB a un puerto USB libre. Tras ésto, arramcamos minicom y configuramos el puerto serie a 115200 Baudios, 8N1 y sin ningún tipo de contol de flujo. Hecho ésto, solo queda encender nuestra tarjeta y comprobar que supervivi arranca:
##### FriendlyARM BIOS for 2440 #####
[x] bon part 0 320k 2368k
[v] Download vivi
[k] Download linux kernel
[y] Download root_yaffs image
[c] Download root_cramfs image
[a] Absolute User Application
[n] Download Nboot
[e] Download Eboot
[i] Download WinCE NK.nb0
[w] Download WinCE NK.bin
[d] Download & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the nand flash
[p] Partition for Linux
[b] Boot the system
[s] Set the boot parameters
[t] Print the TOC struct of wince
[u] Backup NAND Flash to HOST through USB(upload)
[r] Restore NAND Flash from HOST through USB
[q] Goto shell of vivi
Enter your selection:
Seleccionamos la opción q para acceder a la shell de superivi y después le diremos que queremos cargar una imagen en la memoria y arrancar, pero para ello, antes deberemos saber cuando ocupa el bootloader que vamos a cargar (el que ya viene compilado), así que volvemos a la carpeta en la que lo descomprimimos y lo comprobamos:
javieralso@rigoberto:friendlyarm2440-package$ ls -l
.......................
.......................
-rwxr-xr-x   1 javieralso javieralso 239016 oct 24 11:38 u-boot.bin
.......................
.......................
Podemos ver que ocupa 239016 bytes. Guardamos este dato, nos será util para lo que comentamos anteriormente.
Enter your selection:q
Supervivi> load flash 0 239016 u
USB host is connected. Waiting a download.
Con esto le hemos dicho a supervivi que queremos cargar 239016 bytes de datos (observad que es la misma cantidad de bytes que ocupa la imagen de u-boot) en la RAM de la tarjeta. Ahora supervivi se ha quedado esperando los datos, que deberemos envíarselos vía USB. Habiendo conectado el cable USB como se indicó antes ejecutamos el uploader:
javieralso@rigoberto: friendlyarm2440-package$ sudo ./s3c2410_boot_usb u-boot.bin
que deberá ser llamado desde la carpeta friendlyarm2440-package y con sudo para poder tener acceso al puerto USB. Si todo ha ido bien, supervivi debería mostar algo como esto:
Now, Downloading [ADDRESS:30000000h,TOTAL:232706]
RECEIVED FILE SIZE:  239016 (28KB/S, 8S)
Downloaded file at 0x30000000, size = 232696 bytes
Found block size = 0x0003c000
Erasing...    ... done
Writing...    ... done
Written 239016 bytes
Supervivi>
Ahora, arrancamos u-boot:
Supervivi> go 0x30000000
y ya está, nuestra tarjeta arrancará u-boot:
U-Boot 1.3.2-mini2440 (May 15 2009 - 21:24:49)
I2C:   ready
DRAM:  64 MB
Flash:  2 MB
NAND:  Bad block table not found for chip 0
Bad block table not found for chip 0
64 MiB
*** Warning - bad CRC or NAND, using default environment
USB:   S3C2410 USB Deviced
In:    serial
Out:   serial
Err:   serial
MAC: 08:08:11:18:12:27
Hit any key to stop autoboot:  0
Lo primero que ha hecho u-boot al arrancar, ha sido darnos un mensaje de alerta indicando que el contenido de la NAND no es el que debiera, ya que no está formateada, así que éste será el siguiente paso:
MINI2440 # nand scrub
NAND scrub: device 0 whole chip
Warning: scrub option will erase all factory set bad blocks!
         There is no reliable way to recover them.
         Use this command only for testing purposes if you
         are sure of what you are doing!
Really scrub this NAND flash? <y/N>
Erasing at 0x33d4000 --  81% complete.
NAND 64MiB 3,3V 8-bit: MTD Erase failure: -5
Erasing at 0x3ffc000 -- 100% complete.
Bad block table not found for chip 0
Bad block table not found for chip 0
OK
MINI2440 #
La NAND tiene almacenada (o debería tener) una lista de bloques defectuosos, pero nosotros limpiaremos completamente la NAND borrando dicha lista y reconstruyéndola después con los bloques que nosotros detectemos como defectuosos. El comando nand scrub se encarga precisamente de limpiar la NAND. Después de haber hecho la limpieza, creamos nuestra propia BBT:
MINI2440 # nand createbbt
Create BBT and erase everything ? <y/N>
Skipping bad block at  0x03410000
Skipping bad block at  0x03ff0000
Skipping bad block at  0x03ff4000
Skipping bad block at  0x03ff8000
Skipping bad block at  0x03ffc000
Creating BBT. Please wait ...Bad block table not found for chip 0
Bad block table not found for chip 0
Bad block table written to 0x03ffc000, version 0x01
Bad block table written to 0x03ff8000, version 0x01
Éste proceso es relativamente lento, por lo que habrá que esperar que esperar (3-4 minutillos), así que mientras tanto, busca una tarjeta SD y copia el binario u-boot.bin QUE COMPILASTE en dicha tarjeta. La cargaremos desde ahí. Una vez que tenemos nuestra SD con el binario cargado y que ya se ha generado la nueva BBT, introducimos la tarjeta en el lector de la mini2440 y le decimos a u-boot que la detecte:
MINI2440 # mmcinit
******** FIXME: Poner aquí la salida de mmcinit **************
Con la tarjeta ya detectada, cargamos u-boot.bin en la RAM:
MINI2440 # fatload mmc 0:1 0x32000000 u-boot.bin
******** FIXME: Poner aquí la salida de fatload **************
¿Qué ha pasado? pues que le hemos dicho a u-boot que cargue el archivo binario u-boot.bin desde un sistema de archivos FAT (fatload), ubicado en la SD (mmc), dispositivo 1, primera partición (0:1), a partir de la dirección de memoria 0x32000000. Una vez que tenemos el archivo en RAM, lo volcamos a la NAND:
MINI2440 # nand write.e 0x32000000 0x0 0x38fc4
NAND write: device 0 offset 0x0, size 0x38fc4
Writing data at 0x38c00 -- 100% complete.
 233412 bytes written: OK
Bueno, en este caso, hemos escrito 0x38fc4 bytes (233412, para que nos entendamos), al comienzo de la NAND. El numerajo hexadecimal, como hemos visto corresponde al peso de u-boot.bin pero ésta vez del que hemos compilado NOSOTROS, no del que cargamos en RAM al principio. Importante no confundir ésto. Un apunte importante es que en ésta parte del proceso, la longitud deberá darse en hexadecimal, no en decimal, ya que ello puede dar lugar a errores. Por lo que he leído por ahí, el modificador .e del comando write se utiliza para generar códigos de correción de errores y evitar así lo posibles bloques defectuosos que pueda haber en la NAND (recordad el comando createbbt de antes). Una vez hecho esto, podemos reiniciar tranquilos nuestra mini2440: u-boot la hará arrancar. Aunque no hará mucho mas ya que la NAND está completamente borrada y no hay sistema. Antes comenté que explicaría el por qué de usar dos versiones distintas de uboot. El motivo es que para instalar uboot, primero debe residir en la RAM y poderse ejecutar desde ahí, ya que hay que hacer una limpieza de la NAND antes de llevar a cabo la instalación propiamente dicha. Lo que observé entonces fue que las versiones de uboot que conseguían arrancar desde la RAM, no eran capaces de hacerlo luego una vez que se instalaban en NAND (en efecto, según los manuales que leí, una vez creada la BBT, se escribía el contenido de la RAM a la NAND, ya que allí es donde está uboot) y viceversa, las imágenes que si que eran capaces de arrancar desde la NAND no lo podían hacer desde la RAM. No sé si esto es por algún bug en uboot (que deberían ser entonces bugs distintos en cada una de las versiones) o porque yo hice algo mal (repetí montones de veces lo que vi en tutos, blogs y demás paso por paso). El caso es que después de varios intentos sin mucho éxito, decidí hacerlo tal y como lo he explicado aquí.

En el futuro....

En próximas recetas, explicaré qué comandos básicos utilizar en u-boot para cargar imágenes en RAM o NAND, configurar el arranque automático desde distintas fuentes, particionar la NAND, configurar opciones de arranque para el S.O. que tengamos y alguna cosilla mas. Evito poner esa información aquí para no sobrecargar demasíado esta receta, que se supone que tienen que ser breves y concisas.

Referencias

Muchas y muy variadas, aunque las mas importantes son éstas.


blog comments powered by Disqus