Table of Contents

Créer un système complet avec Buildroot

Le projet Buildroot permet de construire facilement un système embarqué et d’en ajuster finement le contenu. Pour produire des systèmes autonomes robustes, résilients et reproductibles, on préfère cette approche à l'utilisation d'une distribution pré-compilée dont le contenu est assez mal maîtrisé. On va utiliser Buildroot pour construire un système personnalisé pour Raspberry Pi.

Les développeurs de Buildroot fournissent une nouvelle version chaque trimestre. Celle de février est une version maintenue sur le long terme pendant toute l'année, alors que celles de mai, août et novembre sont des versions intermédiaires marquant les évolutions du projet.

Environnement de travail

Commencer par créer une arborescence de travail qui contiendra tous les fichiers personnalisés, les répertoires de construction, les archives des projets compilés par Buildroot, etc.

mkdir br-lab
cd br-lab

Espace d'échange

Pour les configurations aux ressources limitées, il faut allouer un swapfile conséquent (32Go pour un sbc raspberry disposant de 4Go de mémoire)

Au sein de cet environnement, commencer par créer et initialiser le fichier pour contenir le swap. Par exemple, pour créer un fichier d'échange de 32 Go, on peut utiliser la commande :

sudo dd if=/dev/zero of=swapfile bs=1M count=32768 status=progress

Fichiers d'échange sur BTRFS: À partir du noyau 5.0 et supérieur, les fichiers d'échange sont pris en charge sur btfrs. Ils nécessitent encore une manipulation spéciale en plus des étapes ci-dessus.

sudo truncate -s 0 /swapfile
sudo chattr +C /swapfile
sudo btrfs property set /swapfile compression none
\\Ces commandes créent un fichier d'échange vide, désactivent COW pour ce fichier et garantissent que la compression est désactivée.

Définir les autorisations appropriées sur le fichier. Il doit être accessible en lecture et en écriture uniquement par root. Cela peut être fait avec la commande :

sudo chmod 600 swapfile

Ensuite, formater le fichier d'échange :

sudo mkswap swapfile

Configuration par défaut

Télécharger l'archive de la dernière version de Buildroot, et la décompresser:

wget http://www.buildroot.org/downloads/buildroot-2020.02.tar.bz2 
tar xf buildroot-2020.02.tar.bz2

On va travailler dans un répertoire de compilation indépendant des sources de Buildroot. Ainsi on peut enchaîner plusieurs builds successifs pour différentes architectures ou différents projets en conservant les sources originales intactes. Par acquit de conscience on peut s'assurer qu'aucune modification n'interviendra dans les sources de Buildroot d'y interdire l'accès en écriture :

chmod -R -w buildroot-2020.02

Cela garantit qu'aucun fichier de configuration ne pourra être sauvegardé par erreur dans cette arborescence dans le cas d'une faute de frappe dans le nom d'une variable d'environnement sur une ligne de commande.

On va demander à Buildroot de préparer une configuration par défaut pour Raspberry Pi 4. Puis on va la modifier très légèrement dans un premier temps, et l'affinerons de manière plus importante ensuite.

La liste des configurations disponibles est à la racine du répertoire configs/

Buildroot intègre un nombre assez conséquent de configurations toutes prêtes pour des plateformes variées. Même si on ne trouve pas celle qui correspond exactement à la cible, il est souvent possible de choisir une configuration pour un system-on-chip proche et de l'ajuster assez facilement.

On veut construire un système pour Raspberry Pi 4, aussi on demande à Buildroot de préparer une configuration pour cette cible :

[buildroot-2020.02]$ make O=../build-pi4 raspberrypi4_defconfig 
  [...]
#
# configuration written to /home/cpb/br-lab/build-pi4/.config
#

Maintenant que la configuration a été préparée dans le répertoire de compilation, on peut s'y rendre pour continuer le travail, on n'aura plus besoin de revenir dans les sources de Buildroot.

[buildroot-2020.02]$ cd ../build-pi4/
ls
build  Makefile
ls -a
.                       .br2-external.in.openssl     build
..                      .br2-external.in.paths       .config
.br2-external.in.jpeg   .br2-external.in.toolchains  Makefile 
.br2-external.in.menus  .br2-external.mk

La configuration ainsi préparée est enregistrée dans le fichier «.config» (dont le nom commence par un point ce qui le rend invisible à la commande «ls» sauf si elle est suivie de l'option «-a»).

Le fichier .config contient les définitions de constantes pour «make» :

#
# Automatically generated file; DO NOT EDIT.
# Buildroot 2020.02 Configuration
#
BR2_HAVE_DOT_CONFIG=y
BR2_HOST_GCC_AT_LEAST_4_9=y
BR2_HOST_GCC_AT_LEAST_5=y
BR2_HOST_GCC_AT_LEAST_6=y
BR2_HOST_GCC_AT_LEAST_7=y
 [...]
# BR2_ELF2FLT is not set
# BR2_VFP_FLOAT is not set
# BR2_PACKAGE_GCC_TARGET is not set
# BR2_HAVE_DEVFILES is not set
[build-pi4]$

Modification de la configuration

Dans un premier temps on va faire quelques personnalisations rapides, on y reviendra plus en détail ultérieurement.

Pour cela on demande le menu de configuration de Buildroot. Comme on l'a indiqué, on fait appel à «make» pour interagir avec Buildroot :

make menuconfig
  [...]

Un menu de configuration s'ouvre.

Le menu est affiché en utilisant la bibliothèque Ncurses. Il existe des commandes équivalentes pour afficher le menu avec les bibliothèques Qt («make xconfig») ou Gtk («make gconfig»).

Le premier sous-menu «Target options» contient essentiellement des paramètres correspondant à certaines options du compilateur.

Dans le cas, il n'y a pas de raison de modifier le contenu de ce sous-menu mais on peut en profiter pour jeter un œil à la variété des processeurs supportés directement par Buildroot.

Le sous-menu «Build options» contient de nombreux paramètres permettant de configurer la manière de compiler le système. Une modification est conseillée : celle de l'option «Download dir». Il s'agit de l'emplacement où les packages téléchargés sont stockés avant d'être extraits et compilés. Par défaut il s'agit du sous-répertoire «dl» dans le répertoire de Buildroot. Mais on a supprimé les droits d'écriture sur ce dernier. Il faut donc extraire le répertoire «dl» en le remontant d'un cran. Ceci permet en outre de mutualiser les téléchargements entre les différents builds que l'on est amené à réaliser.

Editer le champ «Download dir» pour insérer un «../» avant «dl».

Le sous-menu «Toolchain» permet de configurer des paramètres de la chaîne de cross-compilation. Tout d'abord on peut choisir d'utiliser une chaîne de compilation produite par Buildroot (le cas par défaut) ou une toolchain externe, précompilée et téléchargée depuis certains sites de références (Linaro par exemple). Bien que cette dernière solution soit plus rapide, on déconseille pour des raisons de pérennité et de reproductibilité du système de compilation.

Un second choix important concerne la bibliothèque C à employer pendant la production du compilateur et à installer sur la cible. C’est une décision qui dépend beaucoup du code métier. La bibliothèque C est un point-clé du système ; c’est elle qui permet d’entrer dans le noyau pour bénéficier de ses services (les appels-système). Le choix par défaut est celui de la «uclibc-ng», une bibliothèque spécialement dédiée aux systèmes embarqués restreints. C'est la solution qu'on va conserver ici, mais les alternatives «glibc» et «musl» sont tout aussi acceptables.

On va modifier deux options:

Enfin, activer l'option «Build cross gdb for the host» comme on le voit sur la figure 6, pour produire tout de suite le débogueur qui fonctionnera sur la machine hote (le PC).

Pour le moment, on se limite à ces modifications, on examinera le contenu des autres menus ultérieurement. on peut quitter en sauvegardant la configuration.

Compilation et test

Compilation de l'image standard

Pour lancer la production de cette image, rien de plus simple :

make
 [...]

La première compilation est plus longue que les suivantes, du fait de la génération de la toolchain et de la compilation du noyau. La durée exacte dépend beaucoup de la machine hôte et de la connexion Internet, mais on peut considérer environ une heure sur une machine d'entrée de gamme. La compilation se termine ainsi :

INFO: vfat(boot.vfat): cmd: "MTOOLS_SKIP_CHECK=1 mcopy -bsp -i '/home/cpb/br-lab/build-pi4/images/boot.vfat' '/home/cpb/br-lab/build-pi4/images/rpi-firmware/overlays' '::'" (stderr):
INFO: vfat(boot.vfat): adding file 'zImage' as 'zImage' ...
INFO: vfat(boot.vfat): cmd: "MTOOLS_SKIP_CHECK=1 mcopy -bsp -i '/home/cpb/br-lab/build-pi4/images/boot.vfat' '/home/cpb/br-lab/build-pi4/images/zImage' '::'" (stderr):
INFO: hdimage(sdcard.img): adding partition 'boot' (in MBR) from 'boot.vfat' ...
INFO: hdimage(sdcard.img): adding partition 'rootfs' (in MBR) from 'rootfs.ext4' ...
INFO: hdimage(sdcard.img): writing MBR
[build-pi4]$

De nouveaux sous-répertoires sont apparus dans le répertoire de travail.

ls
build  host  images  Makefile  staging  target
ls target/
bin  lib      media  proc  sbin                              tmp
dev  lib32    mnt    root  sys                               usr
etc  linuxrc  opt    run   THIS_IS_NOT_YOUR_ROOT_FILESYSTEM  var

Pour le moment on s'intéresse au répertoire «images»

ls images/
bcm2711-rpi-4-b.dtb  rootfs.ext2  rpi-firmware  zImage
boot.vfat            rootfs.ext4  sdcard.img
[build-pi4]$

Les fichiers qu'il contient sont les suivants :

Préparation de la carte micro-SD

Exécuter la commande suivante:

lsblk
NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda              8:0    0 465,8G  0 disk  
├─sda1           8:1    0   400G  0 part  /home/testing
└─sda2           8:2    0     8G  0 part  [SWAP]
sdb              8:16   0 931,5G  0 disk  
└─sdb1           8:17   0   512G  0 part  /media/cpb/USB-EXT
nvme0n1        259:0    0 232,9G  0 disk  
├─nvme0n1p1    259:1    0   512M  0 part  /boot/efi
├─nvme0n1p2    259:2    0 224,6G  0 part  /
└─nvme0n1p3    259:3    0   7,8G  0 part  
  └─cryptswap1 253:0    0   7,8G  0 crypt 

On voit tous les périphériques «blocks» présents : «nvme0n1» le SSD principal du système, «sda» un disque dur SATA interne de travail, «sdb» un disque externe USB. Connecter ensuite une carte micro-SD insérée dans un adpateur USB, puis relancer la même commande après quelques secondes d'attente pour que l'auto-monteur ait terminé son travail :

lsblk
NAME           MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda              8:0    0 465,8G  0 disk  
├─sda1           8:1    0   400G  0 part  /home/testing
└─sda2           8:2    0     8G  0 part  [SWAP]
sdb              8:16   0 931,5G  0 disk  
└─sdb1           8:17   0   512G  0 part  /media/cpb/USB-EXT
sdc              8:32   1   3,8G  0 disk  
├─sdc1           8:33   1    32M  0 part  /media/cpb/0B09-D1D7
└─sdc2           8:34   1   256M  0 part  /media/cpb/0af348dc-34fe-4482-b341-a87
nvme0n1        259:0    0 232,9G  0 disk  
├─nvme0n1p1    259:1    0   512M  0 part  /boot/efi
├─nvme0n1p2    259:2    0 224,6G  0 part  /
└─nvme0n1p3    259:3    0   7,8G  0 part  
  └─cryptswap1 253:0    0   7,8G  0 crypt

Le périphérique apparu est «/dev/sdc/». Démonter les partitions montées automatiquement et copier l'image produite par Buildroot sur ce périphérique. Cette étape est potentiellement dangereuse (comme tout ce qu'on préfixe par «sudo»), il faut être attentif pour ne pas écraser le disque système par mégarde.

umount  /dev/sdc?
sudo  cp  images/sdcard.img  /dev/sdc

Une fois la copie terminée, insérer la carte SD dans le Raspberry Pi 4 et le démarrer.

Test de l'image standard

Connecter un écran sur l'une des sorties mini-HDMI, pour voir les traces de boot avec les fameuses framboises emblèmatiques du Raspberry Pi.

On peut connecter un terminal («minicom») sur le port série du Raspberry Pi avec un câble USB-Série (ne pas connecter le fil rouge).

On peut se connecter avec l'identité «root» (pas de mot de passe pour le moment), et passer quelques commandes.

le clavier est configuré en «Qwerty» ce qui nécessite une petite gymnastique si on ne dispose que d'un clavier «Azerty».

Welcome to Buildroot
buildroot login: root
# uname -a
Linux buildroot 4.19.97-v7l #1 SMP Sun Mar 8 23:43:48 CET 2020 armv7l GNU/Linux 
# cat /proc/device-tree/model
Raspberry Pi 4 Model B Rev 1.1# 
# free
              total        used        free      shared  buff/cache   available
Mem:        1962164       20664     1938580          48        2920     1929232
Swap:             0           0           0
# 

On voie ici Buildroot fonctionner sur un Raspberry Pi 4 avec 2 Gb de RAM. La commande «ps» affiche la liste des processus présents :

# ps
PID   USER     COMMAND
    1 root     init
    2 root     [kthreadd]
    3 root     [rcu_gp]
    4 root     [rcu_par_gp]
    6 root     [kworker/0:0H-kb]
    7 root     [kworker/u8:0-ev]
    8 root     [mm_percpu_wq]
    9 root     [ksoftirqd/0]
   10 root     [rcu_sched]
   11 root     [rcu_bh]
   12 root     [migration/0]
   13 root     [cpuhp/0]
   14 root     [cpuhp/1]
   15 root     [migration/1]
   16 root     [ksoftirqd/1]
   17 root     [kworker/1:0-eve]
   18 root     [kworker/1:0H-kb]
   19 root     [cpuhp/2]
   20 root     [migration/2]
   21 root     [ksoftirqd/2]
   22 root     [kworker/2:0-eve]
   24 root     [cpuhp/3]
   25 root     [migration/3]
   26 root     [ksoftirqd/3]
   29 root     [kdevtmpfs]
   30 root     [netns]
   32 root     [khungtaskd]
   33 root     [oom_reaper]
   34 root     [writeback]
   35 root     [kcompactd0]
   36 root     [crypto]
   37 root     [kblockd]
   38 root     [watchdogd]
   39 root     [kworker/2:1-ipv]
   40 root     [rpciod]
   41 root     [kworker/u9:0]
   42 root     [xprtiod]
   43 root     [kswapd0]
   44 root     [nfsiod]
   55 root     [kthrotld]
   56 root     [iscsi_eh]
   57 root     [kworker/u8:1]
   59 root     [kworker/1:1-mm_]
   60 root     [DWC Notificatio]
   61 root     [vchiq-slot/0]
   62 root     [vchiq-recy/0]
   63 root     [vchiq-sync/0]
   64 root     [vchiq-keep/0]
   65 root     [SMIO]
   66 root     [kworker/0:2-eve]
   67 root     [irq/37-brcmstb_]
   68 root     [irq/38-mmc1]
   69 root     [irq/38-mmc0]
   70 root     [kworker/0:3-eve]
   72 root     [mmc_complete]
   73 root     [kworker/0:1H-mm]
   74 root     [kworker/3:1H-kb]
   75 root     [kworker/3:2H]
   76 root     [jbd2/mmcblk0p2-]
   77 root     [ext4-rsv-conver]
   79 root     [kworker/2:1H-kb]
   80 root     [kworker/1:1H-kb]
   94 root     /sbin/syslogd -n
   98 root     /sbin/klogd -n
  123 root     [kworker/2:2H]
  124 root     [ipv6_addrconf]
  145 root     -sh
  164 root     /sbin/getty -L tty1 0 vt100
  169 root     [kworker/3:0-eve]
  170 root     [kworker/3:1-mm_]
  171 root     [kworker/3:2]
  172 root     ps
# 

Hormis les threads internes du noyau (toutes les tâches avec des noms entre crochets), on observe la présence de seulement six processus :

Voilà un système dont le contenu est bien sous contrôle !

Cette première image est très toutefois limitée, elle ne comprend guère que Busybox, et le compte «root» est même accessible sans mot de passe. on va améliorer sa configuration.

Affinement de la configuration

on peut faire une toute une série d’améliorations, afin d’obtenir un système un peu plus convivial, accueillant un autre utilisateur que «root» par exemple ou renforçant la partition principale contre les risques de coupures d’alimentation intempestives.

On relance :

make  menuconfig

Et on s'intéresse au menu «System configuration».

Intervenons sur quelques options de ce menu :

On va devoir fournir un fichier pour les utilisateurs supplémentaires (autres que «root»). Ce sera le premier fichier de configuration personnalisé mais on en ajoutera d'autres par la suite. La méthode la plus adéquate à mon sens consiste à ajouter une arborescence externe (external tree) regroupant tous les fichiers personnels.

On va donc quitter quelques instants le menu de configuration (en sauvegardant les modifications) pour créer le fichier des utilisateurs dans un external tree.

External Tree

Une arborescence externe est un répertoire placé en-dehors des sources de Buildroot et en-dehors du répertoire de compilation «build-pi4» dans lequel on peut ajouter des recettes supplémentaires (pour intégrer des packages absents de ceux connus par Buildroot, ou pour intégrer du code métier applicatif), des paramètres de configuration pour Busybox ou pour le noyau, ou encore des fichiers personnalisés à ajouter dans l'arborescence de la cible. on va commencer par un external tree très simple.

Remonter d'un cran dans le système de fichiers, puis créer un répertoire. Par exemple «pi4-config».

cd  ..
mkdir  pi4-config
ls
build-pi4          buildroot-2020.02.tar.bz2  pi4-config 
buildroot-2020.02  dl
cd  pi4-config/

Pour que cette arborescence soit valide pour Buildroot, on doit y créer trois fichiers :

echo  "name: PI4_CONFIG"  >  external.desc
echo  "desc: Custom configuration for Pi 4"  >>  external.desc 
cat external.desc
name: PI4_CONFIG
desc: Custom configuration for Pi 4

touch  external.mk
touch  Config.in

Ajouter un répertoire dans lequel on stockera les éléments de configuration, à commencer par la table des utilisateurs.

mkdir configs

Table des utilisateurs

on va remplir «Path to the users tables» avec le nom d’un fichier qui contient la liste des utilisateurs. Il doit y avoir un compte par ligne. Les champs, séparés par une espace, sont les suivants :

On crée donc un fichier «configs/users.tbl» :

echo  "rpi 1000 rpi 1000 =rpi /home/rpi /bin/sh - Raspberry Pi User"  >  configs/users.tbl 

Comme on le voit, on a ajouté un utilisateur «rpi» appartenant à son propre groupe, et dont le mot de passe est… «rpi» !

Le stockage du mot de passe en clair dans ce fichier de configuration est acceptable pour une expérimentation comme celle-ci mais doit être proscrit en environnement de production. En effet si la sécurité de la machine utilisée pour la compilation est compromise, c'est l'ensemble des systèmes déployés qui sont vulnérables

Intégration de l'arborescence externe et test

Pour que l'arborescence externe soit prise en considération, il va falloir l'indiquer une fois sur la ligne de commande de «make menuconfig» puis elle sera mémorisée automatiquement.

cd ../build-pi4/
export BR2_EXTERNAL=../pi4-config/
make menuconfig

Une nouvelle ligne est apparue au bas du menu de configuration: sous-menu «External options»

Comme on n'a pas indiqué de menu ou de packages supplémentaires, le sous-menu concerné indique seulement la description de cette option.

Pour le moment, il faut renseigner la table des utilisateurs. on peut donc donner le chemin dans le menu «System configuration». On utilise une variable qui va être automatiquement créée par Buildroot : «BR2_EXTERNAL_PI4_CONFIG_PATH». Elle représente la racine de l'arborescence externe. Le nom de la variable a été composé à partir du champ «name» que on a inscrit dans le fichier «external.desc». Le nom de l'external tree était «PI4_CONFIG», celui de la variable est donc «BR2_EXTERNAL_PI4_CONFIG_PATH».

Cette approche donne de la souplesse, il sera possible d'utiliser le même external tree dans d'autres builds. on verra même comment extraire la configuration de Buildroot qu'on réalise pour la réutiliser avec une version ultérieure par exemple.

On a donc modifié l'option «Path to the users tables» du menu «System configuration» pour y inscrire la chaîne : «$(BR2_EXTERNAL_PI4_CONFIG_PATH)/configs/users.tbl».

Après sauvegarde, on relance la compilation du système :

make

La durée est très courte, car il n'y a que quelques fichiers de paramétrage système à modifier.

Après réécriture de la carte micro-SD comme on l'a fait plus haut, on peut démarrer le Raspberry Pi :

Welcome on board!
R-Pi login:

On peut déjà remarquer la bonne prise en compte du message de bienvenue et du nom d'hôte («R-Pi» au lieu de «Buildroot» précédemment). On se connecte sous la nouvelle identité ajoutée :

R-Pi login: rpi 
Password: (rpi)
$ pwd
/home/rpi
$ exit

La connexion sur le nouveau compte a fonctionné, vérifier si «root» est bien muni d'un mot de passe cette fois :

Welcome on board!
R-Pi login: root
Password: (root)
# pwd
/root
#

on a placé le système de fichiers en lecture seulement, vérifions cela :

# echo  HELLO  >  file.txt
-sh: can't create file.txt: Read-only file system
#

L'écriture est bien interdite. on peut toutefois remonter le système de fichiers en lecture-écriture :

# mount  /  -o  remount,rw
[   73.376221] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) 
# echo  HELLO  >  file.txt
# ls
file.txt
#

Le message du kernel qui apparaît sur cette console après la commande «mount» est normal, il annonce que la partition a été remontée.

on peut aussi remettre le système de fichiers en lecture seule :

# mount  /  -o  remount,ro
[   89.070566] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) 
# rm  -f  file.txt 
rm: can't remove 'file.txt': Read-only file system
# 

Le système est bien en lecture seule à nouveau. Le système n'est plus vulnérable aux coupures d'alimentation électrique.

Ajout de contenu

Ajout de packages supportés par Buildroot

L'ajout de logiciels proposés par Buildroot est très simple, il suffit de se rendre dans le menu «Target packages» visible sur la figure 14, d'explorer ses sous-menus et de piocher dans les applications proposées pour composer le contenu de le système.

Par exemple, dans le menu «Networking applications» on ajoute le serveur SSH Dropbear :

Cocher également l'option «disable reverse DNS lookups» pour accélérer les connexions.

Ajouter l'éditeur Gnu Nano se trouvant dans le sous-menu «Text editors and viewers».

Ajout de scripts

On a précédemment vu comment remonter le système de fichiers, initialement en lecture seule, pour y accéder en lecture-écriture puis le replacer en lecture seule à nouveau. Ces commandes ne sont pas compliquées, mais durant la phase de mise au point on peut être amené à les taper plusieurs dizaines de fois par jour. On a donc l'habitude de créer deux petits scripts, qu'on appelle simplement «rw» et «ro», qui sont installés sur la cible et permettent très rapidement de changer le mode d'accès à l'arborescence.

On va placer les deux scripts dans une arborescence spécifique au sein de l'external tree créé plus haut. Ensuite on indiquera à Buildroot de venir ajouter le contenu de cette arborescence dans le système de fichiers de la cible.

Créer un premier répertoire dans l'external tree, correspondant à la racine de mon arborescence :

cd  ../pi4-config/
mkdir  custom-rootfs 

On souhaite placer mes scripts dans le répertoire «/usr/bin» de ma cible. On crée donc ce repertoire dans mon arborescence :

mkdir  -p  custom-rootfs/usr/bin/ 

Puis y créer le script »rw»

nano  custom-rootfs/usr/bin/rw 

Avec le contenu suivant :

#!/bin/sh

mount / -o remount,rw 

Et le script «ro» :

nano  custom-rootfs/usr/bin/ro 

contenant :

#!/bin/sh

mount / -o remount,ro 

Il faut rendre les scripts exécutables :

chmod  +x  custom-rootfs/usr/bin/* 

Il faut indiquer à Buildroot la présence de cette nouvelle arborescence à venir ajouter, superposer à celle d'origine. Il s'agit de l'entrée «Root filesystem overlay directories» du menu «System configuration». Comme précédemment, on fait référence au répertoire de l'external tree.

cd  ../build-pi4/ 
make  menuconfig

On remplis donc l'entrée «Root filesystem overlay directories» ainsi :

$(BR2_EXTERNAL_PI4_CONFIG_PATH)/custom-rootfs

Lancer la nouvelle production d’image (qui ne dure que quelques dizaines de secondes, le temps de compiler Dropbear, Nano et de reconstruire l'image).

Welcome on board!
R-Pi login: root
Password: (root) 

Vérifier si le serveur «dropbear» est bien démarré et si la commande «nano» est présente :

# ps |  grep  drop
  144 root     /usr/sbin/dropbear -R
  156 root     grep drop
# nano  --version
 GNU nano, version 4.7
 (C) 1999-2011, 2013-2019 Free Software Foundation, Inc.
 (C) 2014-2019 the contributors to nano
 Email: nano@nano-editor.org    Web: https://nano-editor.org/
 Compiled options: --enable-tiny --disable-nls --disable-utf8 

Les packages qu'on a demandés sont bien présents. Le système de fichiers doit toujours être en lecture seulement, vérifions si le script de passage en lecture-écriture fonctionne :

# touch  my-file
touch: my-file: Read-only file system
# rw
[  244.951926] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) 
# touch  my-file
# ls
my-file
# 

Parfait, on peut revenir en lecture seule :

# ro
[  266.057973] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null) 
# rm  -f  my-file
rm: can't remove 'my-file': Read-only file system
#

Le système est bien sécurisé contre les coupures d'alimentation et il offre suffisament de souplesse pour intervenir dans le système de fichiers si besoin.

Surcharge de fichiers de configuration

On n'a pas encore utilisé le réseau. Si on connecte un câble Ethernet avant de démarrer le Raspberry Pi, celui-ci peut obtenir directement une adresse IP si un serveur DHCP est présent sur le réseau local :

Welcome on board!
R-Pi login: root
Password: (root)
# ip  addr
1: lo: >LOOPBACK,UP,LOWER_UP< mtu 65536 qdisc noqueue qlen 1000 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0: >BROADCAST,MULTICAST,UP,LOWER_UP< mtu 1500 qdisc mq qlen 1000 
    link/ether dc:a6:32:02:57:3d brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.11/24 brd 192.168.3.255 scope global eth0 
       valid_lft forever preferred_lft forever
    inet6 2a01:e0a:22:ea90:dea6:32ff:fe02:573d/64 scope global dynamic 
       valid_lft 86366sec preferred_lft 86366sec
    inet6 fe80::dea6:32ff:fe02:573d/64 scope link 
       valid_lft forever preferred_lft forever
#

Il est toutefois des situations où l'on préfère utiliser une adresse IP fixe. Pour cela, il faut modifier le fichier /etc/network/interfaces. De même, le serveur DHCP fournit ici l'adresse du serveur DNS local. on peut en fixer un statiquement dans /etc/resolv.conf.

Le plus simple consiste à surcharger ces deux fichiers dans l'arborescence external tree :

cd  ../pi4-config/
mkdir  -p  custom-rootfs/etc/network/
nano  custom-rootfs/etc/network/interfaces 

Contenu du fichier «interfaces» (bien entendu il faut l'ajuster selon la situation) :

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
  address 192.168.3.200
  netmask 255.255.255.0
  gateway 192.168.3.254 
nano  custom-rootfs/etc/resolv.conf 

Contenu du fichier «resolv.conf» :

nameserver 1.1.1.1 

Après avoir regénéré l'image, l'avoir copiée sur carte micro-SD et démarré le Raspberry Pi, se connecter en SSH depuis mon PC :

ssh  root@192.168.3.200
The authenticity of host '192.168.3.200 (192.168.3.200)' can't be established. 
ECDSA key fingerprint is SHA256:sUFGwSyZCG/gWkEO4r9OmGgMFnZgs1LuT9fQgESIdTs.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.3.200' (ECDSA) to the list of known hosts.
root@192.168.3.200's password: (root)
# uname  -a
Linux R-Pi 4.19.97-v7l #1 SMP Sun Mar 8 23:43:48 CET 2020 armv7l GNU/Linux
# 

On peut vérifier que le Raspberry Pi accède également à Internet :

# ping  www.kernel.org
PING www.kernel.org (136.144.49.103): 56 data bytes
64 bytes from 136.144.49.103: seq=0 ttl=53 time=17.657 ms
64 bytes from 136.144.49.103: seq=1 ttl=53 time=17.413 ms
64 bytes from 136.144.49.103: seq=2 ttl=53 time=17.844 ms 
^C
--- www.kernel.org ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 17.413/17.638/17.844 ms
# wget  https://www.kernel.org
wget: not an http or ftp url: https://www.kernel.org
# exit
Connection to 192.168.3.200 closed.

Le cible accède bien à Internet, mais on ne peut pas télécharger une page en utilisant le protocole HTTPS, la commande «wget» n'acceptant que HTTP ou FTP.

Utilitaires implémentés par Busybox

La commande «wget» est implémentée en utilisant l'utilitaire Busybox. on peut modifier la configuration de ce dernier avec une commande particulière de Buildroot :

make  busybox-menuconfig 

Un écran de configuration ressemblant à celui de Buildroot apparaît.

Se rendre dans le menu «Networking Utilities», et valider l'option «Support HTTPS using internal TLS code» pour l'application «wget».

Bien entendu, on en profitera pour regarder les commandes implémentées dans Busybox, et en ajouter — ou en retirer — quelques unes suivant la destination du système embarqué.

Après compilation, installation et reboot du Raspberry Pi, on observe:

# wget  https://www.kernel.org
Connecting to www.kernel.org (136.144.49.103:443)
wget: note: TLS certificate validation not implemented 
wget: can't open 'index.html': Read-only file system
#

«wget» ne peux pas écrire dans le répertoire courant puisque le système est en lecture seulement. Se déplacer alors dans «/tmp» sur lequel est monté un «tmpfs» (une sorte de ramdisk) utilisable en écriture :

# cd  /tmp
# wget  https://www.kernel.org
Connecting to www.kernel.org (136.144.49.103:443)
wget: note: TLS certificate validation not implemented
saving to 'index.html'
index.html           100% |********************************| 16749  0:00:00 ETA 
'index.html' saved
#

Tout s'est bien passé, «wget» a pu télécharger une page en utilisant le protocole HTTPS.

Ajout de code métier

Il est rare qu'un système embarqué soit composé uniquement d'applicatifs standards et de fichiers de configuration. Cela m'est arrivé lorsqu'il s'agissait de faire de petits serveurs pour des tâches simples (référence NTP, serveur DHCP, DNS local, etc.) mais ce n'est pas le cas le plus courant d'utilisation de Buildroot.

Si on doit ajouter du code métier, il y a plusieurs approches possibles. Tout d'abord, pendant une phase de développement, on va devoir coder et tester le projet indépendamment du BSP. Pour cela on doit disposer de la toolchain produite par Buildroot.

Extraction de la toolchain

Il est possible d'appeler directement le cross-compiler fourni par Buildroot en appelant les exécutables «arm-linux-gcc» ou «arm-linux-g++» qui se trouvent dans le sous-répertoire «host/bin/» du répertoire de travail.

host/bin/arm-linux-gcc  --version
arm-linux-gcc.br_real (Buildroot 2020.02) 8.3.0
Copyright © 2018 Free Software Foundation, Inc.
Ce logiciel est un logiciel libre; voir les sources pour les conditions de copie.  Il n'y a
AUCUNE GARANTIE, pas même pour la COMMERCIALISATION ni L'ADÉQUATION À UNE TÂCHE PARTICULIÈRE. 

Toutefois ceci peut s'avérer gênant si le développeur applicatif est dans une équipe différente de celle du concepteur du BSP. Pour cela, Buildroot permet d'extraire la toolchain et de l'installer sur une autre machine.

On appelle :

make  sdk

qui fournit une archive :

ls  images/
arm-buildroot-linux-uclibcgnueabihf_sdk-buildroot.tar.gz  rootfs.ext4 
bcm2711-rpi-4-b.dtb                                       rpi-firmware 
boot.vfat                                                 sdcard.img
rootfs.ext2                                               zImage

on peut copier cette archive sur une autre machine ou à un endroit plus approprié de la même machine, et l'extraire. Par exemple on va la placer dans «/opt/» sur mon poste.

sudo  tar  xf  images/arm-buildroot-linux-uclibcgnueabihf_sdk-buildroot.tar.gz  -C  /opt/ 
ls  /opt/
arm-buildroot-linux-uclibcgnueabihf_sdk-buildroot
picomono
picoscope
poky
sublime_text
[build-pi4]$

On peut raccourcir le nom du répertoire :

sudo  mv  /opt/arm-buildroot-linux-uclibcgnueabihf_sdk-buildroot/  /opt/cross-br-2020.02 
ls  /opt/cross-br-2020.02/
arm-buildroot-linux-uclibcgnueabihf  include  libexec          share
bin                                  lib      relocate-sdk.sh  usr
etc                                  lib64    sbin

Puis il faut appeler le script «relocate-sdk» se trouvant dans ce répertoire pour qu'il enregistre le nouvel emplacement. Il faut l'invoquer avec les droits suffisants pour pouvoir écrire dans les fichiers concernés.

sudo  /opt/cross-br-2020.02/relocate-sdk.sh 
Relocating the buildroot SDK from /home/cpb/br-lab/build-pi4/host to /opt/cross-br-2020.02 ... 
[build-pi4]$

Compilation par appel direct du cross-compiler

N'importe quel utilisateur peut alors appeler le cross-compiler.

/opt/cross-br-2020.02/bin/arm-linux-g++  --version
arm-linux-g++.br_real (Buildroot 2020.02) 8.3.0
Copyright © 2018 Free Software Foundation, Inc.
Ce logiciel est un logiciel libre; voir les sources pour les conditions de copie.  Il n'y a
AUCUNE GARANTIE, pas même pour la COMMERCIALISATION ni L'ADÉQUATION À UNE TÂCHE PARTICULIÈRE. 

Supposons par exemple qu'on souhaite compiler un exécutable comme celui-ci :

// hello-direct.cpp

#include <iostream>

int main()
{
        std::cout << "Hello World! (direct call to the cross-compiler)"; 
        std::cout << std::endl;

        return 0;
}

On peut appeler le cross-compiler «arm-linux-g++» directement en ajoutant son emplacement dans la variable «PATH» de la session shell.

PATH=$PATH:/opt/cross-br-2020.02/bin/
arm-linux-g++  hello-direct.cpp  -o  hello-direct  -Wall 

Ce fichier peut être transféré avec «scp» sur la cible ou placé dans l'arborescence de external tree. On choisi cette seconde solution. Elle nécessite bien sûr un rebuild de l'image.

cp  hello-direct  ../pi4-config/custom-rootfs/usr/bin/ 
make
Welcome on board!
R-Pi login: root
Password: (root)
# hello-direct
Hello World! (direct call to the cross-compiler) 

Naturellement, il est possible d'invoquer le compilateur au travers d'un «Makefile» en remplissant les variables d'environnement adéquante (souvent «CROSS_COMPILE»).

Écriture de recettes pour des projets métiers

La meilleure manière d'intégrer un projet disposant d'une structure de compilation comme «automake», «Cmake» ou même un simple «Makefile» est d'écrire une recette que on ajoutera dans external tree et que Buildroot se chargera de produire avec le reste de l'image.

Il y a de très nombreuses recettes fournies avec Buildroot dans le répertoire «package/» de ses sources, elles peuvent servir d'exemples pour compiler des applications supplémentaires.

Par exemple, on souhaite installer un package qui se compile avec les Autools.

On crée dans external tree un répertoire qui accueillera les différentes recettes que nous souhaitons ajouter. Pour être cohérent avec l'organisation de Buildroot, on le nomme «package».

cd  ../pi4-config/ 
mkdir  package
[pi4-config]$

Dans ce répertoire on crée un sous-répertoire dédié au projet concerné. On va prendre le package «hello-autotools» qu'on a écrit en exemple de mon cours en ligne sur Yocto Project.

mkdir  package/hello-autotools 

Dans ce répertoire on doit créer trois fichiers :

Voici le contenu du premier fichier, «package/hello-autotools/Config.in» :

config BR2_PACKAGE_HELLO_AUTOTOOLS
    bool "hello-tools"
    help
        This is an example of a package built with the autotools. 

A présent, voici la recette de compilation «package/hello-autotools/hello-autotools.mk». Du fait qu'on emploie le mécanisme des autotools elle est très simple et se borne à indiquer la provenance du package.

################################################################################ 
#
# hello-autotools
#
################################################################################

HELLO_AUTOTOOLS_VERSION = 1.0
HELLO_AUTOTOOLS_SOURCE = hello-autotools-$(HELLO_AUTOTOOLS_VERSION).tar.bz2
HELLO_AUTOTOOLS_SITE = https://www.blaess.fr/christophe/yocto-lab/files
HELLO_AUTOTOOLS_AUTORECONF = YES
HELLO_AUTOTOOLS_LICENSE = GPL-2.0

$(eval $(autotools-package))

Dès qu'il y a un téléchargement de package, il est conseillé de vérifier une somme de contrôle. Pour cela, on emploie l'utilitaire «sha256sum» et l'on inscrit la checksum dans le fichier «package/hello-autotools/hello-autotools.hash».

sha256  ad06e44345fc85e061932da4bdda964c65f1dc56fbc07ea03ea8b93c68065cfe  hello-autotools-1.0.tar.bz2 

La recette de compilation pour le package est prête. Mais elle ne sera intégrée par Buildroot que si on lui indique où elle se trouve. Pour cela éditer le fichier «external.mk» à la racine de external tree (fichier précédemment vide) et ajoutons la ligne suivante :

include $(sort $(wildcard $(BR2_EXTERNAL_PI4_CONFIG_PATH)/package/*/*.mk)) 

Cette ligne demande à Buildroot d'explorer toutes les recettes se trouvant dans le sous-répertoire «package» de external tree.

Enfin on ajoute dans le fichier «Config.in» de external tree (précédemment vide également) les lignes suivantes qui y définissent un menu incluant le «Config.in» de le package.

menu "Custom packages"

source "$BR2_EXTERNAL_PI4_CONFIG_PATH/package/hello-autotools/Config.in" 

endmenu

Lorsqu'on relance la commande «make menuconfig» de Buildroot, le menu «External options» s'est rempli.

cd  ../build-pi4/ 
make  menuconfig 

Dans ce nouveau menu, on voie l'option «hello-tools». Bien sûr, on active cette option avant de relancer le build.

Pendant la construction du système, on voit passer brièvement les messages indiquant le téléchargement, l'extraction et la compilation du package.

Vérifier sa présence sur la cible :

R-Pi login: root
Password: (root)
# hello-autotools 
Hello from R-Pi (built with autotools) 
# 

De la même façon, on a ajouté des recettes pour compiler un package utilisant CMake ou avec un simple «Makefile» classique. On ne les détaille pas ici, ils se trouvent dans l'archive que l'on trouvera plus loin.

Lancement automatique au démarrage

Buildroot utilise de préférence un mécanisme de lancement des services au démarrage basé sur le processus «init» à la mode System V fourni par «Busybox».

Il est toujours possible, dans le sous-menu «Init system» du menu «System configuration», de choisir un démarrage avec «systemd» mais on l'évite généralement car la maîtrise de la séquence d'initialisation des services est plus simple avec les scripts System V.

Si on veut lancer une application au démarrage, il suffit d'ajouter un script dans le répertoire «etc/init.d» de la cible dont le nom commence par un «S» suivi d'un numéro d'ordre de démarrage. Ce script sera appelé au boot du système avec l'argument «start» et à l'arrêt du système avec l'argument «stop».

Voici ce que contient le répertoire «etc/init.d» initial :

# ls  /etc/init.d/
S01syslogd   S02sysctl    S40network   rcK
S02klogd     S20urandom   S50dropbear  rcS
# 

On ajoute un script de lacement dans l'arborescence de l'external tree :

mkdir  ../pi4-config/custom-rootfs/etc/init.d 
nano  ../pi4-config/custom-rootfs/etc/init.d/S60hello 

Le contenu de ce fichier est le suivant :

#!/bin/sh

if [ "$1" = "start" ]
then
        /usr/bin/hello-autotools &
fi

Ce script est très simple car il ne fait que lancer une commande. Il y a des situations où il est plus complexe car il faut arrêter le service lors du shutdown du système. On a placé un «&» à la fin de la commande, cela n'a aucune utilité ici puisqu'elle se termine immédiatement, mais pourrait servir dans le cas d'une application qui reste en fonction de manière prolongée car le script doit rendre la main une fois le programme démarré.

Il ne faut pas oublier de le rendre exécutable :

chmod  +x  ../pi4-config/custom-rootfs/etc/init.d/S60hello 

Une fois le système recompilé et installé, on vérifie les traces de boot du Raspberry Pi :

random: dropbear: uninitialized urandom read (32 bytes read)
OK
Hello from R-Pi (built with autotools)
Welcome on board!
R-Pi login:

Le processus a donc bien été lancé automatiquement au boot.

Ajout d'une partition utilisateur

Le système fonctionne de manière plutôt satisfaisante :

Welcome on board!
R-Pi login: rpi
Password: (rpi)
$ hello-autotools
Hello from R-Pi (built with autotools)
$ hello-cmake
Hello from R-Pi (built with CMake)
$ hello-makefile
Hello from R-Pi (build with standard Makefile) 

Un petit problème survient si on souhaite écrire dans un fichier :

$ hello-makefile > my-file.txt
-sh: can't create my-file.txt: Read-only file system 

C'est normal, la partition système est en lecture seulement, on a écrit un script pour y remédier :

$ rw
mount: you must be root 
$

Voilà un véritable problème. on a protégé la partition système en la plaçant en lecture seule. Pour pouvoir y faire quand même des modifications, on a prévu des scripts «rw» et «ro» (en étant conscient que pendant la période où le système est en lecture-écriture, une coupure d'alimentation peut lui ètre fatale). Mais ces scripts ne peuvent être appelés que par «root», pas par un utilisateur (ou une application métier) s'exécutant sous une identité quelconque.

D'autre part, on sent bien que le fait de mettre temporairement la partition système en lecture-écriture est un risque. Si une coupure d'alimentation survient juste à ce moment le système de fichiers peut être corrompu. Et la probabilité que l'utilisateur débranche et rebranche le système juste après avoir fait une modification de configuration pour voir si elle a été prise en compte est assez élevée.

Si on souhaite pouvoir sauvegarder des données d'application ou des éléments de paramétrage, il va falloir envisager l’ajout d’une partition supplémentaire montée en lecture-écriture à côté de la partition système.

Cette partition peut être formatée en «vfat», système de fichiers simple et robuste qui résiste plutôt bien à des coupures d’alimentations pendant une écriture.

Il faut indiquer la présence de cette partition dans le fichier /etc/fstab. on va récupérer le fichier original produit par Buildroot (qui se trouve dans le sous-répertoire «target/etc/» de le dossier de compilation), le copier dans external tree et lui ajouter une ligne pour cette nouvelle partition.

cp  target/etc/fstab  ../pi4-config/custom-rootfs/etc/ 
nano  ../pi4-config/custom-rootfs/etc/fstab

Le fichier est modifié ainsi (dernière ligne ajoutée) :

# <file system> <mount pt>      <type>  <options>       <dump>  <pass>
/dev/root       /               ext2    ro,noauto       0       1
proc            /proc           proc    defaults        0       0
devpts          /dev/pts        devpts  defaults,gid=5,mode=620,ptmxmode=0666   0       0
tmpfs           /dev/shm        tmpfs   mode=0777       0       0
tmpfs           /tmp            tmpfs   mode=1777       0       0
tmpfs           /run            tmpfs   mode=0755,nosuid,nodev  0       0
sysfs           /sys            sysfs   defaults        0       0
/dev/mmcblk0p3  /home/rpi       vfat    defaults,uid=1000,gid=1000   0   0

Cette nouvelle partition sera montée directement sur «/home/rpi», elle appartiendra toute entière à l'utilisateur «rpi» créé précédemment, et sera accessible en lecture et écriture.

Pour créer les partitions et l’image «sdcard.img» finale, Buildroot appelle le script «board/raspberrypi4/post-image.sh» qui est indiqué dans l’option «Custom scripts to run after creating filesystem images» du menu «System configuration». Ce script fait appel à un utilitaire nommé «genimage» en lui passant le fichier de configuration «board/raspberrypi4/genimage-raspberrypi4.cfg».

Pour personnaliser cette étape, on va copier ces deux fichiers dans l'external path en respectant la même structure de sous-dossiers, et les personnaliser ensuite.

cd  ../pi4-config/
mkdir  -p  board/raspberrypi4/
cp  ../buildroot-2020.02/board/raspberrypi4/post-image.sh  board/raspberrypi4/
cp  ../buildroot-2020.02/board/raspberrypi4/genimage-raspberrypi4.cfg  board/raspberrypi4/ 

Le script «post-image.sh» fait référence à son propre emplacement dans l'arborescence pour chercher le fichier de description des partitions. Il n'est donc pas nécessaire de le modifier, juste de le placer dans l'external tree.

Editer le fichier de configuration «genimage-raspberrypi4.cfg» et ajouter les lignes en surbrillance :

nano  board/raspberrypi4/genimage-raspberrypi4.cfg 

image boot.vfat {
  vfat {
    files = {
      "bcm2711-rpi-4-b.dtb",
      "rpi-firmware/cmdline.txt",
      "rpi-firmware/config.txt",
      "rpi-firmware/fixup4.dat",
      "rpi-firmware/start4.elf",
      "rpi-firmware/overlays",
      "zImage"
    }
  }
  size = 32M
}

image pi-home.vfat {
  name = "pi-home.vfat"
  vfat {
    files = { }
  }
  empty = true
  size = 128M
}

image sdcard.img {

  hdimage {
  }

  partition boot {
    partition-type = 0xC
    bootable = "true"
    image = "boot.vfat"
  }

  partition rootfs {
    partition-type = 0x83
    image = "rootfs.ext4"
  }

  partition pi-home.vfat {
    partition-type = 0xC
    image  = "pi-home.vfat"
  }
}

Avant de relancer la génération d'une image, il faut indiquer à Buildroot de venir chercher le script dans external tree plutôt que dans le répertoire initial. Ceci se configure dans l'option «Custom scripts to run after creating filesystem image» (avant-dernière ligne) du menu «System configuration».

cd  ../build-pi4/ 
make menuconfig 

Après installation, on vérifie si l'utilisateur «rpi» a bien accès à son répertoire personnel en lecture et écriture :

Welcome on board!
R-Pi login: rpi
Password: (rpi)
$ pwd
/home/rpi
$ echo  hello  >  my-file 
$ cat  my-file
hello
$exit

Vérifier que la partition système est seulement accessible en lecture, même pour l'utilisateur «root» :

Welcome on board!
R-Pi login: root
Password: (root)
# pwd
/root
# echo  hello  >  my-file 
-sh: can't create my-file: Read-only file system
# 

L'image est ainsi robuste et insensible aux coupures d'alimentation en ce qui concerne son arborescence système. La partition utilisateur permet d'enregistrer des données, et en cas d'arrêt brutal seules les données en cours d'écriture seront perdues, le système «vfat» étant assez résilient.

Sauvegarde des paramétrages réalisées

Sauvegarde de la configuration de Busybox

Afin de pouvoir aisément réutiliser la configuration dans un build pour une autre cible par exemple — ou pour la prochaine version de Buildroot — on va sauvegarder cette configuration dans external tree.

La configuration de Busybox est sauvegardée dans un fichier «.config» ressemblant à celui de Buildroot ou du kernel. Ce fichier se trouve dans le sous-répertoire dans lequel Buildroot a fait la compilation de Busybox :

ls  -a  build/busybox-1.31.1/
.                        editors                 networking
..                       examples                NOFORK_NOEXEC.lst
applets                  .files-list.txt         NOFORK_NOEXEC.sh
applets_sh               findutils               printutils
.applied_patches_list    include                 procps
arch                     .indent.pro             qemu_multiarch_testing
archival                 init                    README
AUTHORS                  INSTALL                 runit
busybox                  .kconfig.d              scripts
busybox.links            .kernelrelease          selinux
busybox_unstripped       klibc-utils             shell
.busybox_unstripped.cmd  libbb                   size_single_applets.sh
busybox_unstripped.map   libpwdgrp               .stamp_dotconfig
busybox_unstripped.out   LICENSE                 .stamp_downloaded
.config                  loginutils              .stamp_extracted
Config.in                mailutils               .stamp_kconfig_fixup_done 
.config.old              Makefile                .stamp_patched
configs                  Makefile.custom         sysklogd
console-tools            Makefile.flags          testsuite
coreutils                Makefile.help           .tmp_versions
debianutils              make_single_applets.sh  TODO
docs                     miscutils               TODO_unicode
e2fsprogs                modutils                util-linux

On copie ce fichier dans external tree en lui donnant un nom un peu plus explicite :

cp  build/busybox-1.31.1/.config  ../pi4-config/configs/busybox.config 

Et on indique à Buildroot où trouver ce fichier de configuration. Pour cela, on se rendons en haut de son menu «Target packages» et remplissons l'option «Busybox configuration file to use?» (la deuxième ligne) avec la chaîne «$(BR2_EXTERNAL_PI4_CONFIG_PATH)/configs/busybox.config».

La même méthode pourrait s'appliquer à la configuration du kernel, à laquelle on peut accéder avec la commande «make linux-menuconfig» et dont la configuration est enregistrée dans le fichier «build/linux-custom/.config».

Sauvegarde de la configuration de Buildroot

Il est possible de sauver la configuration de Buildroot lui-même dans external tree afin de pouvoir la réutiliser ultérieurement sous forme de defconfig et regénérer le système sur une autre machine par exemple.

Tout d'abord on va indiquer à Buildroot l'emplacement de la sauvegarde en utilisant l'option «Location to save buildroot config» du menu «Build options» (deuxième ligne). On donne le chemin vers le répertoire «configs» de external tree. Le nom du fichier se termine par «_defconfig» pour pouvoir l'utiliser comme configuration par défaut, par exemple «custom_pi4_defconfig

Une fois cette opération réalisée, il suffit de demander à Buildroot de sauver les élements essentiels de sa configuration ainsi :

make  savedefconfig
  GEN     /home/cpb/br-lab/build-pi4/Makefile
ls  ../pi4-config/configs/
busybox.config  custom_pi4_defconfig  users.tbl

Restauration d'une configuration de Buildroot

Maintenant que la configuration par défaut est enregistrée dans external tree, celui-ci contient :

Il s'agit uniquement de données textuelles (configuration, recettes, etc) que l'on peut versionner avec Git par exemple.

On peut surtout transmettre cet external tree à un autre développeur — c'est ce qu'on fait pour les clients — pour qu'il puisse regénérer le système à l'identique. Il lui faudra juste lancer une ligne un petit peu complexe détaillé plus bas.

On va tester que tout se passe bien. Commençons par effacer toute la compilation faite sur cette machine et télécharger external tree que l'on peut trouver ici :

https://www.blaess.fr/christophe/buildroot-lab/pi4-config.tar.bz2

br-lab]$ ls
build-pi4  buildroot-2020.02  buildroot-2020.02.tar.bz2  dl  pi4-config 
chmod  -R  +w  buildroot-2020.02
rm  -rf  build-pi4/ pi4-config/ buildroot-2020.02/ 
ls
buildroot-2020.02.tar.bz2  dl

Puis recommencer à zéro le build en utilisant la configuration par défaut personnalisée :

tar  xf  buildroot-2020.02.tar.bz2
chmod  -R  -w  buildroot-2020.02 
ls
buildroot-2020.02  buildroot-2020.02.tar.bz2  dl
wget  https://www.blaess.fr/christophe/buildroot-lab/pi4-config.tar.bz2 
  [...]
tar  xf  pi4-config.tar.bz2 
ls
buildroot-2020.02  buildroot-2020.02.tar.bz2  dl  pi4-config  pi4-config.tar.bz2
cd  buildroot-2020.02/

Voici la commande un peu complexe, qui joue trois rôles : demander à Buildroot d'utiliser un external tree, de compiler le code dans un répertoire de travail indépendant de ses sources, et d'utiliser la configuration que on a sauvegardée précédemment :

make  O=../build-pi4  BR2_EXTERNAL=../pi4-config/  custom_pi4_defconfig 
  [...]
#
# configuration written to /home/cpb/br-lab/build-pi4/.config
#

Il ne reste plus qu'à relancer la compilation, et attendre quelques dizaines de minutes :

 cd  ../build-pi4/
[buildroot-2020.02]$ cd  ../build-pi4/
make