Como desenvolver um sistema Linux do zero para a Raspberry Pi
- por Sergio Prado
A Raspberry Pi já possui diversas distribuições Linux disponíveis, para diversos gostos e preferências, dentre elas Raspbian, Raspbmc, Debian “wheezy” e Arch Linux (eu escrevi sobre algumas delas no meu artigo introdutório sobre a Raspberry Pi).
E o uso de uma distribuição Linux facilita bastante o trabalho, principalmente para quem esta começando seus estudos. Mas junto com a facilidade de uso, vem a falta de flexibilidade. E se você quiser um tempo de boot menor? E se você quiser apenas um conjunto específico de aplicações? E se você quiser ter controle sobre os pacotes open source usados na distribuição e suas respectivas licenças (atividade essencial em um produto comercial)?
Tudo isso fica mais difícil de atingir quando usamos uma distribuição Linux pronta. Neste caso, o ideal é construir um sistema Linux customizado para as necessidades do projeto. E é isso que faremos neste artigo.
Todos os testes foram realizados em um PC com a distribuição Ubuntu 12.04 rodando nativamente. Se necessário, adapte os comandos utilizados à sua distribuição.
Então, vamos começar?
UM SISTEMA LINUX
Você precisa basicamente de 4 componentes para construir um sistema Linux completo (para qualquer plataforma de hardware):
- Toolchain
- Bootloader
- Kernel
- Rootfs
Vamos então estudar estes componentes mais detalhadamente.
TOOLCHAIN
Já escrevi bastante sobre toolchains, incluindo os artigos “Desmistificando toolchains em Linux embarcado” e “Gerando e usando toolchains em Linux embarcado“. Se você não souber ou quiser conhecer mais sobre o que é um toolchain e o seu uso em Linux embarcado, dê uma olhada nestes artigos.
Nossa plataforma de hardware é um ARM11 com suporte à ponto flutuante. Portanto, precisamos de um toolchain com estas características para trabalhar com Linux. E não precisamos ir muito longe. O projeto da Raspberry Pi já fornece um toolchain pronto para usarmos, baseado no toolchain da Linaro. Este toolchain esta disponível no repositório tools da Raspberry Pi no github.
Antes de começar, vamos criar um diretório de trabalho:
$ mkdir ~/raspberrypi |
Agora vamos baixar o toolchain:
$ cd ~/raspberrypi $ git clone https://github.com/raspberrypi/tools.git |
E instalar em um diretório na máquina de desenvolvimento:
$ sudo mkdir -p /opt/toolchain $ sudo chown $USER:$USER /opt/toolchain/ $ cp -a tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian /opt/toolchain/ |
Antes de usá-lo, vamos adicionar o diretório dos binários do toolchain no PATH:
$ echo -e '\nexport PATH="/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:$PATH"\n' >> ~/.bashrc $ source ~/.bashrc |
Se sua máquina for de 64 bits, você precisa instalar os bibliotecas de 32 bits:
$ sudo apt-get install ia32-libs libc6-dev-i386 |
Se você estiver usando uma versão mais nova do Ubuntu, o comando acima pode não funcionar. Tente então instalar as bibliotecas de 32 bits com o comando abaixo:
$ sudo apt-get install lib32stdc++6 libc6-dev-i386 |
Agora teste o toolchain:
$ arm-linux-gnueabihf-gcc -v Using built-in specs. COLLECT_GCC=arm-linux-gnueabihf-gcc COLLECT_LTO_WRAPPER=/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/../libexec/gcc/arm-linux-gnueabihf/4.7.2/lto-wrapper Target: arm-linux-gnueabihf Configured with: /cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/src/gcc-linaro-4.7-2012.08/configure --build=i686-build_pc-linux-gnu --host=i686-build_pc-linux-gnu --target=arm-linux-gnueabihf --prefix=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install --with-sysroot=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install/arm-linux-gnueabihf/libc --enable-languages=c,c++,fortran --disable-multilib --with-arch=armv6 --with-tune=arm1176jz-s --with-fpu=vfp --with-float=hard --with-pkgversion='crosstool-NG linaro-1.13.1+bzr2458 - Linaro GCC 2012.08' --with-bugurl=https://bugs.launchpad.net/gcc-linaro --enable-__cxa_atexit --enable-libmudflap --enable-libgomp --enable-libssp --with-gmp=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-mpfr=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-mpc=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-ppl=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-cloog=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-libelf=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static --with-host-libstdcxx='-L/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/.build/arm-linux-gnueabihf/build/static/lib -lpwl' --enable-threads=posix --disable-libstdcxx-pch --enable-linker-build-id --enable-plugin --enable-gold --with-local-prefix=/cbuild/slaves/oort61/crosstool-ng/builds/arm-linux-gnueabihf-raspbian-linux/install/arm-linux-gnueabihf/libc --enable-c99 --enable-long-long Thread model: posix gcc version 4.7.2 20120731 (prerelease) (crosstool-NG linaro-1.13.1+bzr2458 - Linaro GCC 2012.08) |
Com as ferramentas de desenvolvimento instaladas, podemos começar a desenvolver o nosso sistema Linux.
BOOTLOADER
O processo de boot da Raspberry Pi é um pouco diferente quando comparada à outras plataformas ARM disponíveis no mercado. Quando você energiza a placa, quem assume o controle é a GPU do SoC (o núcleo ARM11 esta desabilitado neste momento). A GPU procura na primeira partição do cartão SD um código de boot, e com mais alguns passo adicionais, irá carregar e executar o kernel Linux. Para uma descrição mais completa do processo de boot da Raspberry Pi , leia o artigo “Raspberry Pi e o processo de boot“.
Precisamos então preparar um cartão SD com 2 partições:
- Partição 1 (FAT32): partição de 64M para armazenar o código de boot, arquivos de configuração da GPU e a imagem do kernel Linux.
- Partição 2 (EXT2): partição para armazenar o sistema de arquivos (aqui você pode usar o resto do espaço disponível no cartão SD).
Para formatar o cartão SD você pode usar o gparted, conforme abaixo (altere o sdX pelo nome do arquivo de dispositivo do seu cartão SD):
$ sudo gparted /dev/sdX |
O particionamento do cartão SD deve ficar assim:
Perceba que criamos a primeira partição após os primeiros 2MB do cartão SD. Aparentemente isso é essencial para que a GPU consiga carregar o código de boot da primeira partição do cartão (as distros que testei gravam o cartão assim, e sem esta configuração não funcionou nos meus testes).
Com o cartão formatado, é só baixar os binários do bootloader e gravá-los na primeira partição do cartão (altere o ponto de montagem do cartão SD se necessário):
$ cd ~/raspberrypi $ git clone git://github.com/raspberrypi/firmware.git $ cd firmware/boot $ cp bootcode.bin start.elf fixup.dat /media/BOOT/ $ echo "gpu_mem=32" > /media/BOOT/config.txt |
KERNEL
No momento em que escrevo este artigo, o kernel da Raspberry Pi esta disponível no repositório linux do projeto no github.
Baixe então os fontes para sua máquina de desenvolvimento:
$ cd ~/raspberrypi $ git clone git://github.com/raspberrypi/linux.git -b rpi-3.2.27 $ cd linux |
Vamos configurar o kernel para ser compilado para a Raspberry Pi:
$ make ARCH=arm bcmrpi_cutdown_defconfig |
Antes de compilar, abra o menu de configuração:
$ make ARCH=arm menuconfig |
E habilite o sistema de arquivos ext2:
File systems ---> <*> Second extended fs support |
Agora compile:
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- |
E grave no cartão SD (altere o ponto de montagem do cartão SD se necessário):
$ cp -av arch/arm/boot/zImage /media/BOOT/kernel.img $ echo "console=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext2 rootwait" > /media/BOOT/cmdline.txt |
ROOTFS
Este é o nosso último passo. Vamos construir um sistema de arquivos simples baseado no Busybox. E para esta tarefa, usaremos o Buildroot.
O Buildroot é um sistema de build completo, capaz de gerar o toolchain, bootloader, kernel e rootfs. Mas para nossos testes, usaremos ele apenas para gerar o rootfs.
Baixe então o Buildroot para a sua máquina:
$ cd ~/raspberrypi $ wget http://buildroot.net/downloads/buildroot-2012.08.tar.bz2 $ tar xjfv buildroot-2012.08.tar.bz2 $ cd buildroot-2012.08/ |
E inicie o processo de configuração:
$ make menuconfig |
Primeiro configure a arquitetura:
Target Architecture (ARM (little endian)) Target Architecture Variant (arm1176jzf-s) |
Depois configure o toolchain (apesar do Buildroot ter a capacidade de gerar seu próprio toolchain, vamos configurá-lo para usar o toolchain que instalamos no começo deste artigo):
Toolchain ---> Toolchain type (External toolchain) Toolchain (Custom toolchain) Toolchain origin (Pre-installed toolchain) (/opt/toolchain/gcc-linaro-arm-linux-gnueabihf-raspbian) Toolchain path ($(ARCH)-linux-gnueabihf) Toolchain prefix External toolchain C library (glibc) [*] Toolchain has C++ support? |
Configure o sistema a ser gerado:
System configuration ---> (My Raspberry Pi is alive!) System banner /dev management (Dynamic using devtmpfs only) (ttyAMA0) Port to run a getty (login prompt) on |
Sinta-se livre para acessar o menu “Package Selection for the target” e habilitar as aplicações que achar interessante. Minha sugestão é que você não habilite muitos pacotes neste primeiro teste, nem nada muito grande ou complexo como o X11. Você evitará eventuais problemas ou um tempo de compilação muito grande. E de qualquer forma, depois dos primeiros testes você poderá habilitar outras aplicações e bibliotecas e recompilar o Buildroot.
Agora é só iniciar o processo de compilação:
$ make |
No fim da compilação, o Buildroot irá gerar a imagem do rootfs no diretório abaixo:
$ ls -l output/images/ -rw-rw-r-- 1 sprado sprado 4843520 Nov 3 20:20 rootfs.tar |
Por último, é só extrair a imagem para a partição do rootfs no cartão SD (altere o ponto de montagem do cartão SD se necessário):
$ sudo cp -av output/images/rootfs.tar /media/ROOTFS/ $ cd /media/ROOTFS/ $ sudo tar xfv rootfs.tar && sudo rm rootfs.tar |
TESTANDO
Para testar, você vai precisar de uma conexão com a console da Raspberry Pi. Na dúvida, dê uma olhada no artigo “Acessando a console serial na Raspberry Pi“.
Depois é só curtir o boot do sistema (para o login, o usuário é “root” e não tem senha):
Uncompressing Linux... done, booting the kernel. Initializing cgroup subsys cpu Linux version 3.2.27-cutdown+ (sprado@sergio-office-notebook) (gcc version 4.7.2 20120731 (prerelease) (crosstool-NG linaro-1.13.1+bzr2458 - Linaro GCC 2012.08) ) #1 PREEMPT Sat Nov 3 16:23:41 BRST 2012 CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache Machine: BCM2708 Memory policy: ECC disabled, Data cache writeback Built 1 zonelists in Zone order, mobility grouping on. Total pages: 56896 Kernel command line: dma.dmachans=0x7f35 bcm2708_fb.fbwidth=1280 bcm2708_fb.fbheight=1024 bcm2708.boardrev=0x2 bcm2708.serial=0xff0782fa smsc95xx.macaddr=B8:27:EB:07:82:FA sdhci-bcm2708.emmc_clock_freq=10t PID hash table entries: 1024 (order: 0, 4096 bytes) Dentry cache hash table entries: 32768 (order: 5, 131072 bytes) Inode-cache hash table entries: 16384 (order: 4, 65536 bytes) Memory: 224MB = 224MB total Memory: 222856k/222856k available, 6520k reserved, 0K highmem Virtual kernel memory layout: vector : 0xffff0000 - 0xffff1000 ( 4 kB) fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) vmalloc : 0xce800000 - 0xe8000000 ( 408 MB) lowmem : 0xc0000000 - 0xce000000 ( 224 MB) modules : 0xbf000000 - 0xc0000000 ( 16 MB) .text : 0xc0008000 - 0xc03cc000 (3856 kB) .init : 0xc03cc000 - 0xc03eac80 ( 124 kB) .data : 0xc03ec000 - 0xc04121e0 ( 153 kB) .bss : 0xc0412204 - 0xc045f8cc ( 310 kB) NR_IRQS:330 sched_clock: 32 bits at 1000kHz, resolution 1000ns, wraps every 4294967ms timer_set_mode: unhandled mode:1 timer_set_mode: unhandled mode:3 Console: colour dummy device 80x30 Calibrating delay loop... 697.95 BogoMIPS (lpj=3489792) pid_max: default: 32768 minimum: 301 Mount-cache hash table entries: 512 CPU: Testing write buffer coherency: ok devtmpfs: initialized NET: Registered protocol family 16 bcm2708.uart_clock = 0 mailbox: Broadcom VideoCore Mailbox driver bcm2708_vcio: mailbox at f200b880 bcm_power: Broadcom power driver bcm_power_open() -> 0 bcm_power_request(0, 8) bcm_mailbox_read -> 00000080, 0 bcm_power_request -> 0 Serial: AMBA PL011 UART driver dev:f1: ttyAMA0 at MMIO 0x20201000 (irq = 83) is a PL011 rev3 console [ttyAMA0] enabled bio: create slab at 0 SCSI subsystem initialized usbcore: registered new interface driver usbfs usbcore: registered new interface driver hub usbcore: registered new device driver usb Switching to clocksource stc FS-Cache: Loaded CacheFiles: Loaded NET: Registered protocol family 2 IP route cache hash table entries: 2048 (order: 1, 8192 bytes) TCP established hash table entries: 8192 (order: 4, 65536 bytes) TCP bind hash table entries: 8192 (order: 3, 32768 bytes) TCP: Hash tables configured (established 8192 bind 8192) TCP reno registered UDP hash table entries: 256 (order: 0, 4096 bytes) UDP-Lite hash table entries: 256 (order: 0, 4096 bytes) NET: Registered protocol family 1 RPC: Registered named UNIX socket transport module. RPC: Registered udp transport module. RPC: Registered tcp transport module. RPC: Registered tcp NFSv4.1 backchannel transport module. bcm2708_dma: DMA manager at ce808000 bcm2708_gpio: bcm2708_gpio_probe c03f1a78 vc-mem: phys_addr:0x00000000 mem_base=0x0e000000 mem_size:0x10000000(256 MiB) VFS: Disk quotas dquot_6.5.2 Dquot-cache hash table entries: 1024 (order 0, 4096 bytes) FS-Cache: Netfs 'nfs' registered for caching msgmni has been set to 435 io scheduler noop registered io scheduler deadline registered io scheduler cfq registered (default) Console: switching to colour frame buffer device 160x64 brd: module loaded loop: module loaded vcos: [1]: vchiq_init_state: slot_zero = 0xffd80000, is_master = 0 vcos: [1]: vchiq_init_state: called vcos: [1]: vchiq: initialised - version 2 (min 2), device 253.0 usbcore: registered new interface driver smsc95xx cdc_ncm: 04-Aug-2011 usbcore: registered new interface driver cdc_ncm dwc_otg: version 3.00a 10-AUG-2012 (platform bus) Core Release: 2.80a Setting default values for core params Finished setting default values for core params Using Buffer DMA mode Periodic Transfer Interrupt Enhancement - disabled Multiprocessor Interrupt Enhancement - disabled OTG VER PARAM: 0, OTG VER FLAG: 0 Dedicated Tx FIFOs mode dwc_otg bcm2708_usb: DWC OTG Controller dwc_otg bcm2708_usb: new USB bus registered, assigned bus number 1 dwc_otg bcm2708_usb: irq 32, io mem 0x00000000 Init: Port Power? op_state=1 Init: Power Port (0) usb usb1: New USB device found, idVendor=1d6b, idProduct=0002 usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1 usb usb1: Product: DWC OTG Controller usb usb1: Manufacturer: Linux 3.2.27-cutdown+ dwc_otg_hcd usb usb1: SerialNumber: bcm2708_usb hub 1-0:1.0: USB hub found hub 1-0:1.0: 1 port detected usbcore: registered new interface driver uas Initializing USB Mass Storage driver... usbcore: registered new interface driver usb-storage USB Mass Storage support registered. usbcore: registered new interface driver libusual mousedev: PS/2 mouse device common for all mice cpuidle: using governor ladder cpuidle: using governor menu sdhci: Secure Digital Host Controller Interface driver sdhci: Copyright(c) Pierre Ossman sdhci: Enable low-latency mode bcm_power_open() -> 1 mmc0: SDHCI controller on BCM2708_Arasan [platform] using platform's DMA mmc0: BCM2708 SDHC host at 0x20300000 DMA 2 IRQ 77 sdhci-pltfm: SDHCI platform and OF driver helper usbcore: registered new interface driver usbhid usbhid: USB HID core driver TCP cubic registered Initializing XFRM netlink socket NET: Registered protocol family 17 Registering the dns_resolver key type VFP support v0.3: implementor 41 architecture 1 part 20 variant b rev 5 Waiting for root device /dev/mmcblk0p2... mmc0: problem reading SD Status register. mmc0: new high speed SD card at address 0007 mmcblk0: mmc0:0007 SD2GB 1.84 GiB mmcblk0: p1 p2 EXT2-fs (mmcblk0p2): warning: mounting unchecked fs, running e2fsck is recommended VFS: Mounted root (ext2 filesystem) on device 179:2. devtmpfs: mounted Freeing init memory: 120K Starting logging: OK Initializing random number generator... done. Starting network... My Raspberry Pi is alive! buildroot login: root # |
Para ter acesso completo à todos os recursos da Raspberry Pi, só faltou incluir no nosso sistema as bibliotecas de acesso à GPU. Estas bibliotecas possibilitam a utilização da GPU para tarefas pesadas como decodificação de vídeo e execução de aplicações 3D. Este será o foco do nosso próximo artigo.
Happy Pi hacking!
Sergio Prado