SASE2015 - Booteando Linux en una CIAA NXP Ezequiel García Ariel D’Alessandro August 10, 2015 1 Entorno Antes de empezar, necesitamos activar el entorno que preparamos anteriormente. Para ello, simplemente ejecutamos ’ciaa_env’ en una consola. $ ciaa_env CIAA environment. ARCH=arm, CC=arm-cortexm3-uclinuxeabi- IMPORTANTE: Debemos ejecutar siempre ’ciaa_env’ al abrir una consola nueva. 2 U-Boot El primer paso es compilar el bootloader U-Boot. Para ello, debemos configurarlo con ’ciaa_lpc4337_config’ y luego compilarlo. $ cd u-boot/ $ make ciaa_lpc4337_config $ make Si todo salió bien, deberíamos ver: [..] tools/lpc178x_fcg u-boot.bin u-boot-lpc.bin File u-boot.bin loaded with size 66580 bytes Word 0: 0x1008a000 Word 1: 0x1a0002a1 Word 2: 0x1a000299 Word 3: 0x1a000299 Word 4: 0x1a000299 Word 5: 0x1a000299 Word 6: 0x1a000299 Word 7: 0x53f75062 (cksum total: 0x00000000) File u-boot-lpc.bin created with size 66580 bytes mv u-boot-lpc.bin u-boot.bin dd if=cpu/arm_cortexm3/lpc18xx/spifilib/spifilib-envm.bin of=u-boot.bin seek=112 bs=1024 8+1 records in 8+1 records out 8312 bytes (8.3 kB) copied, 0.000206441 s, 40.3 MB/s 1 3 Conectar la CIAA Conectamos el cable miniUSB<->USB. El conector miniUSB debe ir al puerto ”DBG” de la CIAA y el USB se conecta a nuestra PC o laptop. Este conector cumple tres funciones: 1) alimentación de la CIAA, 2) conexión JTAG, y 3) conexión al puerto serie UART2. Al conectar el cable veremos que se enciende el LED ”PWR” (rojo), el cuál indica que la placa está alimentada (rail de entrada de 5V energizado). Además, deberíamos ver que se crean dos nuevos dispositivos tty en nuestra PC o laptop: $ dmesg | tail | grep tty usb 2-1.2: FTDI USB Serial Device converter now attached to ttyUSB0 usb 2-1.2: FTDI USB Serial Device converter now attached to ttyUSB1 Esto indica que nuestra PC o laptop detectó correctamente el chip FTDI de la CIAA. IMPORTANTE: Si no se ven estos mensajes, y la PC o laptop no logra detectar a la CIAA, no es posible continuar. En ese caso, contactarse con los organizadores. 4 Flashear el bootloader en la CIAA Ahora que la CIAA se encuentra alimentada, podemos usar openocd para acceder a la flash interna y grabar el bootloader. Esto lo debemos hacer usando el openocd que compilamos e instalamos previamente: $ /usr/local/bin/openocd -f /path/to/buildroot/board/ciaa/lpc4337/openocd.cfg \ -c "init" \ -c "halt" \ -c "flash write_image erase unlock /path/to/u-boot/u-boot.bin 0x1A000000 bin" \ -c "exit" IMPORTANTE: No se recomienda usar otra versión de openocd. En particular, no estamos seguros que funcione el openocd provisto por la distribución Linux. IMPORTANTE: Si openocd termina con un error LIBUSB_ERROR_ACCESS será necesario ejecutarlo con privilegios. 5 Primer booteo de la CIAA Vamos a usar picocom, conectando al puerto UART2. El bootloader que acabamos de grabar, tiene configurada la salida de consola en el UART2. En nuestra PC o laptop este puerto el device ttyUSB1. $ picocom -b 115200 /dev/ttyUSB1 Reseteamos el SoC para verificar si la grabación fue exitosa: $ /usr/local/bin/openocd -f /path/to/buildroot/board/ciaa/lpc4337/openocd.cfg \ -c "init" \ 2 -c "reset" \ -c "exit" Si todo salió bien vemos: U-Boot 2010.03-00037-g6823c9c-dirty (Jul 29 2015 - 18:08:59) CPU : LPC43xx series (Cortex-M4/M0) Freqs: SYSTICK=144MHz,CCLK=144MHz Board: CIAA NXP DRAM: 8 MB Using default environment In: serial Out: serial Err: serial Net: LPC18XX_MAC Hit any key to stop autoboot: 0 6 Linux kernel Ahora que tenemos un bootloader listo, vamos a compilar el kernel para poder grabarlo en la flash. $ cd linux/ $ make ciaa_lpc4337_defconfig $ make zImage dtbs NOTA: Esto puede llevar un tiempo largo, dependiendo del sistema. En sistemas SMP se puede usar -jN para hacer una compilación paralela con N jobs. 7 Generación de uImage Ahora que ya tenemos la imagen zImage (kernel comprimido) y el binario del DeviceTree, debemos empaquetarlos usando un formato conocido por U-Boot. NOTA: Este paso es necesario para poder bootear Linux en un bootloader U-Boot ”antiguo”. Versiones modernas de U-Boot son capaces de bootear Linux usando directamente el par zImage, dtb. Primero generamos un archivo contiguo que contiene los dos binarios: $ cat ./arch/arm/boot/zImage ./arch/arm/boot/dts/lpc4337-ciaa.dtb > zImage.dtb Y luego, creamos una imagen para U-Boot. $ ./scripts/mkuboot.sh -A arm -O linux -T kernel -C none -a 0x28008000 -e 0x28008001 \ -n "CIAA-Linux" -d zImage.dtb uImage 3 8 Flashear el kernel en la CIAA $ /usr/local/bin/openocd -f /path/to/buildroot/board/ciaa/lpc4337/openocd.cfg \ -c "init" \ -c "reset" \ -c "halt" \ -c "flash write_image erase unlock /path/to/linux/uImage 0x14000000 bin" \ -c "exit" 9 Bootear kernel Volvemos a la terminal donde tenemos picocom corriendo, o lo iniciamos nuevamente: $ picocom -b 115200 /dev/ttyUSB1 Reseteamos el SoC para verificar si la grabación fue exitosa: $ /usr/local/bin/openocd -f /path/to/buildroot/board/ciaa/lpc4337/openocd.cfg \ -c "init" \ -c "reset" \ -c "exit" El bootloader debería iniciar automáticamente. Si esto no sucede, podemos bootear en forma manual: $ ciaa > run bootcmd ## Booting kernel from Legacy Image at 14000000 ... Image Name: CIAA-Linux Image Type: ARM Linux Kernel Image (uncompressed) Data Size: 1236942 Bytes = 1.2 MB Load Address: 28008000 Entry Point: 28008001 Verifying Checksum ... OK Loading Kernel Image ... OK OK Starting kernel ... Uncompressing Linux... done, booting the kernel. [ 0.000000] Booting Linux on physical CPU 0x0 [ etc ] EJERCICIO: ¿Por qué el load address es 0x28008000 y el entry point es 0x28008001? Este kernel aún no tiene un rootfs, y por lo tanto, veremos un mensaje indicando que no se ha podido encontrar un medio para montar un rootfs. Freeing unused kernel memory: 60K (28117000 - 28126000) Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance. ---[ end Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/init.txt for guidance. 4 10 Compilar rootfs La configuración por defecto ’ciaa_lpc4337_defconfig’ de buildroot, tiene habilitadas las opciones para compilar kernel y bootloader. Dado que esto ya lo hicimos en los pasos previos, desactivaremos esas opciones para ahorrar tiempo considerable durante la compilación. Lo primero que haremos es acceder al menú (basado en ncurses): $ cd buildroot/ $ make menuconfig Luego destildamos las opciones: Kernel ---> Linux Kernel Bootloaders ---> U-Boot Notar que dentro de cada opción figuran otros parámetros indicando por ej. qué versión y archivo de configuración utilizar, coincidiendo con los utilizados en los pasos previos. Se alienta a los asistentes a probar e investigar las distintas configuraciones que ofrece Buildroot. Finalmente compilamos el rootfs, siendo en este caso de tipo cpio: $ make rootfs-cpio Si todo salió bien, deberíamos tener una imagen del rootfs en: /path/to/buildroot/output/images/rootfs.cpio 11 Re-configurar Linux para agregar el rootfs Ahora que generamos el rootfs, vamos a modificar la configuración de linux para que se compile con el file system embebido. Para esto ejecutamos: $ cd linux/ $ make menuconfig Debemos asegurarnos que la siguiente opción esté tildada: General setup ---> Initial RAM filesystem and RAM disk (initramfs/initrd) support Luego, en la opción: General setup ---> Initramfs source file(s) insertamos la siguiente cadena de texto: /path/to/buildroot/output/images/rootfs.cpio Ahora podemos volver a compilar el kernel con: $ make zImage dtbs Por último, repetimos los pasos de la sección ’Generación de uImage’. 5 12 Flashear el kernel+rootfs en la CIAA $ /usr/local/bin/openocd -f /path/to/buildroot/board/ciaa/lpc4337/openocd.cfg \ -c "init" \ -c "reset" \ -c "halt" \ -c "flash write_image erase unlock /path/to/linux/uImage 0x14000000 bin" \ -c "exit" 13 Bootear el sistema! Finalmente, podemos bootear la CIAA y ver como bootea hasta un shell Linux típico. En este shell, podemos ingresar comandos Unix conocidos. Sin embargo, debemos tener en cuenta que es un entorno reducido y por lo tanto muchos comandos responden en forma limitada o bien no están disponibles. / # ls bin dev etc / # PID 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 22 24 ps USER root root root root root root root root root root root root root root root root root root root home init lib lib32 linuxrc media VSZ 220 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 232 228 STAT S SW SW SW SW< SW SW SW SW SW< SW SW< SW SW SW< SW SW S R mnt opt proc root run sbin sys tmp usr COMMAND init [kthreadd] [ksoftirqd/0] [kworker/0:0] [kworker/0:0H] [kworker/u2:0] [rcu_preempt] [rcu_sched] [rcu_bh] [khelper] [kdevtmpfs] [writeback] [kworker/0:1] [kswapd0] [deferwq] [kworker/u2:1] [spi32766] -/bin/sh ps / # grep sh: can't execute 'grep': No such file or directory EJERCICIO: Calcular el footprint de memoria RAM. 6 var 14 Bonus track! Para seguir investigando podés hacer los siguientes ejercicios. Recordá que dentro del menú de configuración podés buscar la ubicación de una opción determinada presionando la tecla / e insertando su nombre. 1. Re-compilar el kernel con las opciones CONFIG_HZ_PERIODIC y CONFIG_HZ_1000 habilitadas. ¿Se puede notar alguna diferencia de comportamiento? 2. Re-compilar el kernel eligiendo CONFIG_SLOB. ¿Se puede notar alguna diferencia de comportamiento? 3. Re-compilar el kernel eligiendo CONFIG_PRINTK_TIME. ¿Se puede notar alguna diferencia de comportamiento? 4. En buildroot, usando make busybox-menuconfig, seleccionar CONFIG_LESS y CONFIG_YES y re-compilar el kernel+initramfs. Comprobar que se pueden usar los comandos ”less” y ”yes”. 15 Layout de memoria Flash A continuación se describe el Layout de memoria Flash utilizado durante el taller a medida que se van realizando los distintos pasos. Bank 0x1a000000: (memoria flash interna) -----------| U-Boot | | | | ... | | | |----------| Bank 0x14000000: (memoria flash externa SPI NOR) -----------| Kernel | | | | ... | | | |----------| | RootFS | | appended | | ... | | | |----------| 7