Embedded Linux Development Course - RootFS (pt. 8)

RootFS

Sistemas de Arquivo

Sistemas de Arquivo são utilizados para organizar dados, normalmente entre diretórios e arquivos, em dispositivos de armazenamento (locais ou remotos). Esta organização de diretórios e arquivos costuma ser feita de forma hierárquica, tal como foi utilizado ao longo de todo o treinamento.

Em sistemas Unix, aplicações e usuários exergam uma hierarquia global única. Isto significa, na prática, que podem ser compostos por diferentes sistemas de arquivo.

Ou seja, é uma grande árvore enraizada /, e mesmo assim temos sistema de arquivos em diferentes dispositivos e partições. Para evitar problemas de incompatibilidade o sistema de arquivos enraizados (/) é montado como parte do processo de inicialização. Cada um dos outros sistemas de arquivos que você criar não será utilizável pelo sistema Linux até que esteja montado em um ponto de montagem.

Um exemplo é o diretório /boot, o / e o swap, este são normalmente partições diferentes e utilizam sistemas de arquivos diferentes, normalmente.

  • /boot - fat
  • / - ext4
  • swap - swap

No conjunto atual de sistemas de arquivos montados, um ponto de montagem (mount point) é simplesmente um diretório no qual o sistema de arquivos em um dispositivo é inserido na árvore.

Montagem é o processo de tornar o sistema de arquivos no dispositivo accessível. Por exemplo, é possível montar sistemas de arquivos em partições de unidade de disco rígido como /boot, /tmp ou /home, é possível montar o sistema de arquivos em uma unidade de disquete como /mnt/floppy, ou em um CD-ROM como /media/cdrom1.

Como você pode ver, os pontos de montagem podem estar no diretório-raiz ou em um subdiretório bem abaixo da árvore.

Assim, um ou mais sitemas de arquivos são montados em localizações espcíficas nessa hierarquia de diretórios. Quando um sistema de arquivo é montado num diretório, o mount point, e o onteúdo do diretório irá refletir o conteúdo do dispositivo de armazenamento (físico). Quano o sistema de arquivo é desmontado, o mount point torna-se vazio novamente.

Essa funcionalidade permite que usuários e aplicações acessem diretórios e arquivos de forma fácil, independemente de sua localização física ou tipo de sistema de arquivo.

Root FileSystem

Conforme o que já foi apresentado, existe um sistema específico e este é montado na raiz principal da hierarquia, identificado por /. Este sistema em específico é chamado de Root Filesystem ou RootFS.

Os comandos mount e umount são programas (executáveis) e estão presentes em algum sistema de arquivo. Ao passo que o RootFS é o primeiro sistema de arquivo a ser montado em um sistema Linux e, portanto, não pode ser montado através de um comando mount convencional.

Isto decorre do fato de que quando o kernel é inicializado, ele realiza a montagem do RootFS. Lembre-se um dos parâmetros de command line passados para o kernel é justamento o root=. Este parâmetro especifica a localização do diretório onde será montado, pelo kernel, o RootFS.

Quando o RootFS não é devidamente definito através dos parâmetros de command line. Isto é, quando não é definido o parâmetro root= ou mesmo este é definido de forma errada. O sistema apresenta um erro, o famigerado Kernel Panic.

Please append a correct “root=”boot option Kernel panic - not syncing: VFS: Unable to mount root fs on unknown block(0,0)

Localização

O RootFS pode ser montado de diferentes localidades, por exemplo, da particção de um HD, pendrive, cartão SD, ou memória flash NAND. É possível també, ser montado da rede, através do protocolo NFS, outra opção é também ser montado a partir da memória RAM, previamente carregada pelo bootloader.

Cada caso tem sua especificidade, cabe ao desenvolvedor do sistema escolher como iniciá-lo, como realizar as configurações e etc.

Montando o RootFS

Para a montagem do RootFS de uma partição de HDD ou Pendrive:

root=/dev/sdXY

onde X é uma letra que dica o dispostivo, ao passo que Y é um número que indica a partição no dispositivo.

Por exemplo, root=/dev/sda2 significa que a segunda partição do segundo dispositivo.

Para a montagem do RootFS de uma partição de um cartão SD é similar ao HDD e pendrive, a principal diferença é o dispositivo. Para realizar a montagem em um cartão SD, faz-se:

root=/dev/mmcblkXpY

onde o X e Y são números que marcam, respectivamente, o dispositivo e a partição.

Um exemplo deste tipo de montagem é root=/dev/mmcblk0p2, ou seja, o RootFS será montado na segunda partição do primeiro dispositivo.

Caso a escolha seja montar de partição de uma memória flash:

root=/dev/mtdblockX

onde X é um número que indica a partição na memória.

Por exemplo, root=/dev/mtdblock3, indica que é a quarta partição de um chip NAND (lembre-se o primeiro dispositivo é o 0).

Montando o RootFS via Rede

Desde que sua rede esteja funcionando, entre o computador host e a plataforma target, seu RootFS pode ser um diretóriio na sua máquina de desenvolvimento, e ser exportado via NFS (Network File System).

Para tal, é necessário estabeler a comunicação entre as duas partes e além disso, é necessário a presença de um servidor NFS na máquina host e um cliente NFS na plataforma target.

Utilizando a montagem do RootFS através da rede facilita alguns processos. A tarefa de atualizar arquivos no RootFS se torna muito simples e rápida, não necessitando executar o comando de reset.

Existe também a possibilidade de ter um RootFS maior, na realidade, bem grande. Podendo invluir ferramentas e/ou binários que não aberiam no equipamento embarcado. Isso, pois a localização destes arquivos esta no computador host.

Além disso, há a possibilidade de gerar toolchains nativos, apesar de cross-compilação ser mais eficiente em questão de velocidade.

Como mencionado, a máquina de desenvolvimento precisa ter um servidor NFS, para tal, devemos instalar um NFS Server.

sudo apt install nfs-kernel-server

Além disso, também é necessário exportar o diretório RootFS no arquivo /etc/exports:

∼/dsle20/RootFS_NFS/ 10.1.1.100(rw, no_root_squash, no_subtree_check)

onde 10.1.1.100 é o IP do cliente NFS, no caso, este é o IP que configuramos na RPi durante as práticas anteriores. Ao passo que (rw, no_root_squash, no_subtree_check) são as opções do NFS Server para esse diretório.

Para re-iniciar o NFS Server na máquina de desenvolvimento precisar executar o seguinte comando.

sudo /etc/init.d/nfs-kernel-server restart

Agora, na plataforma target, o kernel Linux deve ser compilado com as seguintes opções habilitadas:

  • CONFIG_NFS_FS=y: Garante suporte como cliente NFS;
  • CONFIG_IP_PNP=y: Configura o IP durante o processo de boot;
  • CONFIG_ROOT_NFS=y: Garante suporte para o RootFS via NFS.

O kernel deve ser inicializado com os seguintes parâmetros:

  • root=/dev/nfs (RootFS via NFS);
  • ip=10.1.1.100 (IP do target);
  • nfsroot=/10.1.1.1:/dsle20/RootFS_NFS/ (Detalhes de IP e local do diretório no Server-_host_).

Montando o RootFS na Memória

Além disso, é possível também ter uma image de RootFS integrada na imagem do kernel, como mencionado anteriormente. Desta forma, o RootFS é carregado na memória RAM e assim como o kernel Linux.

O mecânismo utilizado para fazer este feito é chamado de initramfs . Lembre-se no momento em que estavamos configurando o U-Boot, através das linhas de comando, foi fornecido uma informação

bootz ${kernel_addr_r} - ${fdt_addr_r}

Onde o parâmetro central ‘ - ‘ era justamente o endereço do RootFS na memória RAM, o initramfs. Como não utilizamos, estava com um valor nulo.

Este mecanismo é capaz de integrar um arquivo comprimido do RootFS na imagem do kernel Linux ou mesmo carregar o arquivo comprimido de forma separada, pelo bootloader.

Este paradígma possui algumas características que podem ser encaradas como vantajosas e outras como desvantagens em relação aos outros métodos.

Como vantagens é possível levantar algumas características como, um tempo de boot mais rápido, já que normalmente o tamanho do RootFS é menor. Uma eficiência mais elevada, pois as aplicações já estão na memória. Além disso, pode atuar como um passo intermediário para montar o verdadeiro e maior RootFS. Normalmente isto é utilizado em servidores e desktops. Em contra partitada, uma vez que o initramfs é montando na memória RAM, o armazenamento é volátil.

O conteúdo de uma imagem initramfs é definada nna configuração do kernel, com a opção CONFIG_INITRAMFS_SOURCE. Seu valor pode ser o caminho para um diretório contendo o RootFS ou o caminho para um arquivo comprimido cpio, arquivo de texto descrevendo o conteúdo do initramfs.

O processo de build do kernel Linux irá integrar automaticamente o conteúdo da configuração acima na imagem do kernel.

Mais informações podem ser encontradas em: KernelDoc/ramfs-rootfs-initramfs e KernelDoc/early-userspace/README

Organização do RootFS

A organização do RootFS no Linux, em termos de diretórios, é padronizada pelo FileSystem Hierarchy Standard.

A maioria dos sistemas Linux corroboram com esse padrão e também estão organizados desta forma. As aplicações, em geral, esperam esse tipo de organização do sistema. Isso facilita o trabalho de usuários e desenvolvedores. Uma vez quea estrutura organizacional do RootFS é parecida em diferentes distribuições Linux.

Diretórios mais Importantes

Os principais diretórios do sistemas são:

  • /bin - Programas básicos;
  • /sbin - Programas para administração do sistema;
  • /boot - Imagens de boot (bootloader, kernel, initramfs e etc.);
  • /home - Home do usuário root;
  • /lib - Bibliotecas básicas do sistema;
  • /media - Ponto de montagem para mídias removíveis;
  • /mnt - Ponto de montagem para mídias estáticas;
  • /dev - Arquivos de dispositivos;
  • /sys - Ponto de montagem do sistema de arquivos virtual sysfs;
  • /proc - Ponto de montagem do sistema de arquivo virtual “proc”;
  • /tmp - Arquivos temporários;
  • /usr - Aplicações e dados do usuário;
    • /usr/bin - Aplicações básicas do usuário;
    • /usr/lib - Bibliotecas do usuário;
    • /usr/sbin - Aplicações de administração do usuário;
  • /var - Arquivos de dados variáveis (logs, branco de dados e arquivos temporários).

Separação de Programas e Bibliotecas

Os programas, programas de sistemas e bibliotecas básicas são instalados nos diretórios, mencionados acima, /bin, /sbin e /lib respectivamente.

Os programas como ls, ifconfig, cp, grep e etc, utilizados durante o treinamento, estão disponíveis nos diretórios /bin e /sbin enquanto as bilbiotecas C por exemplo, estão no diretório /lib.

Todos os outros programas, aqueles que vem junto com a distribuição Linux instalada, como no caso do Ubuntu. Estão disponíveis em /usb/bin, /usr/sbin e /usr/lib.

Arquivos de Dispositivos

Uma das regras mais importantes quando se trabalha com o kernel Linux é que as aplicações do usuário podem acessar dispositivos de hardware através de uma API comum (as já mencionadas System Calls).

Desta forma, todos os dispositivos de hardware são representados através de arquivos no sistemas. Chamamos estes de arquivos de dispositivos, e encontram-se no diretório /dev.

Internamente, o kernel Linux identifica cada dispositivo por meio de três informações básicas:

  • Major number: que indica a categoria deste dispositivo
  • Minor number: indica o número do dispositivo
  • Tipo:
    • Dispositivos de bloco: Composto por blocos de dados de tamanho fixo, que podem ser lidos e escritos, endereçáveis e de acesso aleatório. Por exemplo, HD, pendrive, cartão SD;
    • Dispositivos de caractere: Permite acesso sequencial de bytes, sem começo, sem fim, sem tamanho fixo! Exemplo clássico: a porta serial. Além disso, placas de som, rede, câmeras.

A grande maioria dos dispositivos que não são de blocos, são representados como dispositivos de caractere no kernel Linux.

Exemplos de Arquivos de Dispositivos

Podemos listas os arquivos de dispositivos dentro do diretório \dev.

ls -la /dev/ttyS* /dev/sda1
brw-rw---- 1 root disk    8,  1 Jan  8 10:39 /dev/sda1
crw-rw---- 1 root dialout 4, 64 Jan  8 10:39 /dev/ttyS0
crw-rw---- 1 root dialout 4, 65 Jan  8 10:39 /dev/ttyS1
crw-rw---- 1 root dialout 4, 74 Jan  8 10:39 /dev/ttyS10
crw-rw---- 1 root dialout 4, 75 Jan  8 10:39 /dev/ttyS11
crw-rw---- 1 root dialout 4, 76 Jan  8 10:39 /dev/ttyS12
crw-rw---- 1 root dialout 4, 77 Jan  8 10:39 /dev/ttyS13
  • Campo 1: é o tipo (b: dispositivo de bloco, c: dispositivo de caracter etc).
  • Campo 2-4: permissões de dono, grupo e todos os usuários;
  • Campo 5: número de links ou diretórios dentro deste diretório/arquivos.
  • Campo 6: usuário dono deste arquivo;
  • Campo 7: grupo que o arquivo pertence;
  • Campo 8: tamanho em bytes.

Para escrever em um arquivo de dispositivo, como por exemplo, uma porta serial:

int fd;
fd = open("/dev/ttyS0", O_RDWR);
write(fd, "Hello", 5);
close(fd);

Inicializamos um file descriptor e utilizamos as funções open, write e close para a sua manipulação. Lembre-se essas funções são proporcionadas pela API comum da biblioteca padrão C.

ProcFS

O sistema de arquivo virtual, mencionado na seção de características do kernel Linux, é chamado de procfs. Ele existe desde o inívio do desenvolvimento do Linux.

O procfs permite o kernel exportar estatísticas dos processos em execução do sistema, através do diretório /proc. Além disso, permite que o usuário altere parâmetros do kernel em tempo de execução. Algumas aplicações como ps, top e htop utilizam justamente o /proc para extrair e exibir as informações sobre os processos em execução.

Para realizar a montagem do procfs:

mount -t proc nodev /proc

E para consutar mais informações é sempre possível utilizar o comando man proc.

SysFS

O sitema de arquivo virtual sysfs, diferentemente do procfs, exporta informações de drivers e dispositivos de hardware conectados ao sistema. Este, permite somente representar em espaço de usuário a “visão” do kernel sobre barramento, dispositivos e drivers.

Este sistema é muito útil para aplicações que desejam listar informações de hardware. Além disos, as aplicações esperam que o sysfs seja monado em /sys.

ls /sys
block  class  devices   fs          kernel  power
bus    dev    firmware  hypervisor  module

BusyBox

Um sistema Linux normalmente precisa de um conjunto básico de programas para funcionar, como por exemplo, init, shell, utilizarios para manipulação de arquivos e etc.

Para compor um sistema Linux normal, é necessário adquirir essas ferramentas. Contudo, elas são fornecidas por diferentes projetos e é trabalhoso integrá-las manualmente. Não obstante, alguns programas como bash, wget, grep e tar, por exemplo, não foram desenvolvidos para sistemas embarcados.

Para solucionar esses problemas o BusyBox é uma solução alteranativa para Sistemas Embarcados.

O BusyBox é popularmente referido como canivete suíço dos Sistemas Linux Embarcados, ele faz a combinação de diversas ferramentas de uso comum e utilizadas em sistemas Linux. Não somente, são apresentadas com tamanho reduzido e outras características ideiais para um sistema embarcado.

addgroup, adduser, adjtimex, ar, arp, arping, ash, awk, basename, bbconfig, bbsh, brctl, bunzip2, busy- box, bzcat, bzip2, cal, cat, catv, chat, chattr, chcon, chgrp, chmod, chown, chpasswd, chpst, chroot, chrt, chvt, cksum, clear, cmp, comm, cp, cpio, crond, crontab, cryptpw, cttyhack, cut, date, dc, dd, deallocvt, del- group, deluser, depmod, devfsd, df, dhcprelay, diff, dirname, dmesg, dnsd, dos2unix, dpkg, dpkg_deb, du, dumpkmap, dumpleases, e2fsck, echo, ed, egrep, eject, env, envdir, envuidgid, ether_wake, expand, expr, fakeidentd, false, fbset, fbsplash, fdflush, fdformat, fdisk, fetchmail, fgrep, find, findfs, fold, free, freeram- disk, fsck, fsck_minix, ftpget, ftpput, fuser, getenforce, getopt, getsebool, getty, grep, gunzip, gzip, halt, hd, hdparm, head, hexdump, hostid, hostname, httpd, hush, hwclock, id, ifconfig, ifdown, ifenslave, ifup, inetd, init, inotifyd, insmod, install, ip, ipaddr, ipcalc, ipcrm, ipcs, iplink, iproute, iprule, iptunnel, kbd_mode, kill, killall, killall5, klogd, lash, last, length, less, linux32, linux64, linuxrc, ln, load_policy, loadfont, loadkmap, logger, login, logname, logread, losetup, lpd, lpq, lpr, ls, lsattr, lsmod, lzmacat, makedevs, man, match- pathcon, md5sum, mdev, mesg, microcom, mkdir, mke2fs, mkfifo, mkfs_minix, mknod, mkswap, mktemp, modprobe, more, mount, mountpoint, msh, mt, mv, nameif, nc, netstat, nice, nmeter, nohup, nslookup, od, openvt, parse, passwd, patch, pgrep, pidof, ping, ping6, pipe_progress, pivot_root, pkill, poweroff, printenv, printf, ps, pscan, pwd, raidautorun, rdate, rdev, readahead, readlink, readprofile, realpath, reboot, renice, reset, resize, restorecon, rm, rmdir, rmmod, route, rpm, rpm2cpio, rtcwake, run_parts, runcon, runlevel, runsv, runsvdir, rx, script, sed, selinuxenabled, sendmail, seq, sestatus, setarch, setconsole, setenforce, setfiles, setfont, setkeycodes, setlogcons, setsebool, setsid, setuidgid, sh, sha1sum, showkey, slattach, sleep, softlimit, sort, split, start_stop_daemon, stat, strings, stty, su, sulogin, sum, sv, svlogd, swapoff, swapon, switch_root, sync, sysctl, syslogd, tac, tail, tar, taskset, tcpsvd, tee, telnet, telnetd, test, tftp, tftpd, time, top, touch, tr, traceroute, true, tty, ttysize, tune2fs, udhcpc, udhcpd, udpsvd, umount, uname, uncom- press, unexpand, uniq, unix2dos, unlzma, unzip, uptime, usleep, uudecode, uuencode, vconfig, vi, vlock, watch, watchdog, wc, wget, which, who, whoami, xargs, yes, zcat, zcip

Este é um pequeno exemplo das ferramentas disponibilizadas pelo BusyBox.

[LAB] RootFS

Apesar de não ter sido apresentado ainda, utilizares a ferramenta de System Build chamada Buildroot para esta prática. O motivo de se utilizar este tipo de ferramenta ainda será explicado no treinamento.

Nesta atividade será gerado um sistema de arquivos simples (RootFS) baseado no BusyBox, porém como mencionado, utilizaremos a ferramenta Buildroot.

Baixando o Buildroot

A página oficial do projeto é https://buildroot.org/ e seu repositório oficial é https://github.com/buildroot/buildroot. É possível baixá-lo dos dois lugares.

A versão utilizada no curso é uma das últimas, sendo uma versão estável, e encontra-se no diretório ∼/dsle20/dl/rootfs/buildroot-2019.02.8.tar.gz. Crie o diretório ∼/dsle20/rootfs e extraia o Buildroot neste diretório:

cd
cd dsle20
mkdir rootfs
cd rootfs
tar xvf ~/dsle20/dl/rootfs/buildroot-2019.02.8.tar.gz

Configurando e Compilando o RootFS

Novamente, o sistema de configuração do Buildroot é idêntico às ferramentas anteriores. Todas as configurações realizadas são salvas em um arquivo .config no diretório principal. Para listar as opções de placas/plataformas disponíveis, execute o seguinte comando:

cd buildroot-2019.02.8
make list-defconfigs
Built-in configs:
aarch64_efi_defconfig               - Build for aarch64_efi
acmesystems_aria_g25_128mb_defconfig - Build for acmesystems_aria_g25_128mb
acmesystems_aria_g25_256mb_defconfig - Build for acmesystems_aria_g25_256mb
acmesystems_arietta_g25_128mb_defconfig - Build for acmesystems_arietta_g25_128mb
...

Após analisar com calma os arquivos, perceba que o da Raspberry Pi 3 B é o raspberrypi3_defconfig. Faça o load deste arquivo da mesma maneira como anteriormente:

make raspberrypi3_defconfig

Como já possuímos: Toolchain, Bootloader e Kernel configurados, precisamos passar tais informações ao Buildroot para que essas ferramentas não sejam baixadas novamente. Inicie o aplicativo menuconfig e faça as seguintes alterações, cautelosamente:

make menuconfig

No menu Toolchain options, iremos realizar praticamente as mesmas configurações do nosso toolchain:

  • Toolchain Type: External toolchain;
  • Toolchain: Custom Toolchain;
  • Toolchain origin: Pre-isntalled toolchain;
  • Toolchain path (ATENÇÃO): Digite o path completo do seu toolchain, não utilize o path relativo ∼/.
    • Ex:/home/gbs/dsle20/toolchains/x-tools/armv8-rpi3-linux-gnueabihf/

Uma dica, durante esse tipo de processo é necessário utilizar o caminho absoluto dos arquivos/diretórios. Para facilitar e evitar typos é interessante abrir outro terminal (ctrl+t) e navegar até o diretório e em seguinda execute o comando pwd. Ele retornará o caminho absoluto até a localização que você esta, então basta copiar e colar no outro terminal.

  • External toolchain gcc version: 6.x;
  • External toolchain kernel headers series: 4.10.x;
  • External toolchain C library: glibc/eglibc;
  • Toolchain has C++ Support: selecionar;
  • Copy gdb server to the Target: selecionar;
  • System hostname: Pode colocar o nome que desejar;
  • System banner: Idem. Será a mensagem de boas-vindas assim que o terminal é exibido;
  • Init system: BusyBox;
  • /dev management: Dynamic using devtmpfs only;
  • Enable root login with password: * selecione. Nota: ao criar este RootFs, somente o usuário root estará disponível;
  • Kernel Version: Custom version;
  • Kernel version (2a opção logo abaixo): 4.14.y. Esta é a mesma versão dos fontes baixados do repositório da RPi;
  • Linux Kernel Tools:
    • cpupower: algumas ferramentas que permitem gerenciar o desempenho / consumo da cpu no kernel;
    • gpio: ferramentas para manipulação de gpio;
    • perf: ferramenta utilizada para profilling do sistema;

O menu Target packages é responsável por fornecer a maioria das aplicações de usuário. Por enquanto, selecionaremos algumas básicas. No entanto, caso necessitemos durante o desenvolvimento de softwares, podemos adicionar alguma aplicação específica no futuro:

  • Audio and Video Applications: bluez-alsa, hcitop;
  • Compressors and Decompressors: bzip2, zip;
  • Debugging, profilling and benchmark: gdb->full debuger, memstat, rt-tests;
  • Development Tools: binutils, git, make, tree;
  • Filesystems and Flash Utilities: sshfs, ntfs-3g;
  • Hardware handling
    • Firmware: rpi-bt-firmware, rpi-firmware, Install DTB overlays, rpi-wifi-firmware;
    • Interpreter languages and scripting: php, python;
  • Libraries->Hardware Handling: bcm2835, WiringPi;
  • Netwok Applications: bluez-utils 5.x, dhcpcd, lightttpd, openssh, wpa_supplicant, wpa_cli binary;
  • Shell and utilities: sudo;
  • System Tools: htop;
  • Text edtior and viewers: nano;

Perceba o quanto de personalização, entre drivers, ferramentas, softwares, é possível selecionar ou não, no intuito de customizar um sistema Linux embarcado.

  • Exact Size: 1g (opcional o tamanho).

Infelizmente o buildroot não estima o menor tamanho possível da imagem final do Rootfs. Até porque, considerando sistemas embarcados, a ideia é sempre utilizar o máximo do espaço disponível que a placa oferece. Assim, sinta-se à vontade para selecionar o tamanho final da sua imagem do RootFS. Se der algum erro no final da compilação, dizendo que a imagem não cabe no tamanho selecionado, não se preocupe. Apenas abra o menuconfig novamente, aumente o tamanho e recompile. A menos que você dê um clean, o buildroot não irá apagar tudo que já foi compilado.

Certifique-se de que não haja nenhum Bootloader selecionado no menu Bootloader. Pois já compilamos o U-Boot. Saia e SALVE as configurações.

ATENÇÃO: antes de compilar, ainda é necessário definir o local dos fontes do kernel (que você já baixou). Crie e edite o arquivo local.mk

nano local.mk

Insira a seguinte linha, lembre-se de utilizar o caminho absoluto

LINUX_OVERRIDE_SRCDIR=/home/gbs/dsle20/kernel/linux-rpi-4.14.y

indicando o path para os fontes do kernel.

Note que é necessário utilizar o caminho completo e altere o nome de usuário (gbs) de acordo com seu usuário.

Por fim, compile as imagens:

make -j4

Assim que a compilação terminar, verifique os arquivos gerados dentro da pasta output/image:

  • bcm2710-rpi-3-b.dtb: Device Tree compilado para a RPi 3 Model B;
  • bcm2710-rpi-3-b-plus.dtb: Device Tree compilado para a RPi 3 Model B Plus;
  • bcm2710-rpi-cm3.dtb: Device Tree compilado para a RPi Compute Module (ComputeModule);
  • boot.vfat: Partição de boot com todos os arquivos necessários para bootar o sistema recém-criado;
  • rootfs.ext2: Partição de montagem do RootFS; Contém todos os arquivos, programas, bibliotecas que o kernel Linux precisa para executar, bem como as aplicações e ferramentas extras selecionadas;
  • rootfs.ext4: Cópia da partição acima, no entanto esta utiliza o sistema de arquivos ext4;
  • rpi-firmware: Pasta com todos os binários e arquivos de configuração necessários para bootar a RPi;
  • sdcard.img: Imagem pronta com todos arquivos gerados pelo Buildroot. Contém ambas partições, boot e rootfs. Pronta para gravar num cartão SD ou pendrive ou HD;
  • zImage: Imagem compilada do kernel Linux;

_Obs: Caso o U-Boot fosse selecionado antes do processo de build, nessa mesma pasta existiria um arquivo u-boot.bin, imagem do U-B_oot.

[LAB] Fazendo o Primeiro Boot do Sistema

Após gravar a imagem completa do SD card, abra o arquivo config.txt.

Uma forma fácil de fazer a gravação através do Ubuntu é dar um duplo clique no sdcard.img e selecionar a opção de restaurar a imagem em um disco. Desta forma, já será aberto o aplicativo de manipulação de discos do próprio Ubuntu e basta seguir o processo.

Este arquivo esta presente na partição boot e faça as seguintes alterações:

[...]
#fixes rpi3 ttyAMA0 serial console
#dtoverlay=pi3-miniuart-bt
enable_uart=1

isso habilitará o boot via serial (conforme explicado nas seções anteriores). Ao carregar seu sistema pela primeira vez, será necessário realizar algumas configurações básicas para ativar o servidor SSH e criar uma nova conta de usuário, por exemplo.

Criando uma senha de root e nova conta de usuário

De acordo com a configuração realizada no Buildroot, somente o usuário root foi criado no sistema. Na tela de login, entre como root e senha em branco. Note que o conteúdo da mensagem de boas vindas será exibido de acordo com sua configuração no Buildroot:

Welcome to DSLE20
dsle20 login: root
#

Após logar no sistema, crie os diretórios /home e /boot. Em seguida adicione um segundo usuário no sistema:

mkdir /home
mkdir /boot
adduser -h /home/gbs gbs

Nota: substitua o nome gbs pelo nome/nick de seu usuário desejado. Será necessário definir uma nova senha para o usuário. Aproveite e defina também uma senha para o root:

Changing password for gbs
New password:
Bad password: too weak
Retype password:
passwd: password for gbs changed by root

Feito isso, altere o arquivo /etc/sudoers para dar permissões de root ao seu usuário por meio do comando sudo. Encontre as linhas referentes a permissões de usuário dos grupos wheel e sudo e remova seus respectivos comentários:

nano /etc/sudoers
[...]
## Uncomment to allow members of group wheel to execute any command## Uncomment to allow members of group wheel to execute any command
%wheel ALL=(ALL) ALL

[...]
## Uncomment to allow members of group sudo to execute any command
%sudo ALL=(ALL) ALL

[...]

Em seguida, crie o grupo sudo e adicione seu usuário nos grupos dialout, root, sudo e wheel:

 addgroup sudo
 nano /etc/group
root :x:0:gbs
[...]
wheel:x:10:root, gbs
[...]
dialout :x:18:gbs
[...]
sudo:x:1003:gbs

Nota: é possível utilizar esse arquivo para adicionar usuários em qualquer grupo do sistema. Basta encontrar o grupo desejado e adicionar o usuário no final da linha.

Caso já exista algum usuário definido naquele grupo (como no caso do grupo wheel acima, adicione uma vírgula, espaço e o novo usuário).

Configurando um Endereço IP estático

Para configurar um IP estático no novo sistema, edite o arquivo /etc/network/interfaces da seguinte maneira:

nano /etc/network/interfaces
[...]
auto eth0
iface eth0 inet static
    address 10.1.1.100
    netmask 255.255.255.0
    gateway 10.1.1.1
    pre-up /etc/network/nfs_check
    wait-delay 15
[...]

Habilitando o SSH

Inicialmente, altere as configurações do arquivo/etc/ssh/sshd_config:

nano /etc/ssh/sshd_config
[...]
#LoginGraceTime 2m
PermitRootLogin yes
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10
[...]
# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes
PermitEmptyPasswords yes
[...]

As configurações acima permitem

  • Logar como root via SSH;
  • Possibilita logar através de login e senha (caso contrário somente com chaves públicas);
  • Permite logar somente com login, sem a necessidade de digitar a senha. Este último será útil para copiar automaticamente o binário do seu programa pelo comando scp sem precisar digitar a senha.

Em seguida, altere o arquivo /etc/shadow. Este arquivo é responsável por armazenar informações seguras de contas de usuário.

Os campos são separados por “:”. O primeiro campo representa o nome de usuário, o segundo representa a senha de usuário encriptada e o terceiro, que devemos alterar, representa o número de dias que se passaram, desde a última alteração de senha para a respectiva conta de usuário. É necessário alterar esse campo de 0 para um número qualquer (10 por exemplo) para que tal usuário possa logar na RPi via SSH:

nano /etc/shadow
root :$1$8N/irElt$uJ8THhDt.c2plt8cU336j/:10:0:99999:7:::
[...]
gbs:$1$cmF9.YCp$ATeyqkHK..4sDEq9jkbq71:10:0:99999:7:::

Para maiores informações sobre o arquivo shadow: https://www.cyberciti.biz/faq/understanding-etcshadow-file/.

Reinicie o sistema e teste o SSH, tanto com root quanto seu usuário. Caso apareça alguma mensagem de warning relacionada à mudança de identificação de Host, apague o arquivo ∼/.ssh/known_hosts:

reboot
ssh root@10.1.1.100
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
It is also possible that a host key has just been changed.
[...]
rm ~/.ssh/known_hosts

Após remover o arquivo, será pedido a autorização para gerar uma nova key para o IP 10.1.1.100. Digite yes e prossiga com o login.