Créer un système complet avec Buildroot
Le projet Buildroot nous fournit désormais une version de travail trimestrielle et une version annuelle maintenue sur le long terme. Buildroot permet de construire un système embarqué plus traditionnel qu’en utilisant une distribution pré-compilée, et d’ajuster plus finement son contenu. Nous allons l’utiliser pour construire un système personnalisé pour Raspberry Pi.
Cet article a été mis à jour en mars 2019 pour prendre en considération Buildroot 2019.02 (la dernière version « long terme » disponible à ce jour) et testé sur Raspberry Pi 3b et 3b+ (les exemples ci-dessous sont exécutés sur Raspberry Pi 3b).
Nous allons successivement préparer une chaîne de cross-compilation, construire un système minimal pour vérifier le bon fonctionnement de celle-ci puis générer un système configuré de façon plus personnalisée.
Environnement de travail
Commençons par créer une arborescence de travail qui contiendra tous nos fichiers personnalisés, les répertoires de construction, la chaîne de compilation, etc.
[~]$ mkdir br-tree [~]$ cd br-tree [br-tree]$
Au sein de cet environnement, nous allons essayer de respecter l’organisation des fichiers proposée par le projet Buildroot : une arborescence board/
contenant un sous-répertoire pour chaque carte que nous supporterons (ici les variantes du Raspberry Pi 3 uniquement). Tous nos fichiers personnalisés se trouveront dans cette sous-arborescence.
[br-tree]$ mkdir -p board/rpi-3/
Nous téléchargeons les sources de la dernière version de Buildroot, les décompressons et entrons dans ce répertoire :
[br-tree]$ wget http://www.buildroot.org/downloads/buildroot-2019.02.tar.bz2 [br-tree]$ tar xf buildroot-2019.02.tar.bz2 [br-tree]$ cd buildroot-2019.02/
Toolchain
J’ai l’habitude de commencer mes projets embarqués par la construction d’une toolchain de cross-compilation. Il s’agit d’obtenir un compilateur – et tous les outils associés – fonctionnant sur la machine de développement (un PC par exemple) et produisant du code pour la plate-forme cible (ici le Raspberry Pi 3b/3b+).
Il est tout à fait possible de se procurer une toolchain pré-compilée, mais je trouve qu’il est dommage de se priver de cette étape de construction, d’autant que cela nous permet de maîtriser exactement les versions de la bibliothèque C, du noyau, des outils, etc. que nous souhaitons.
Si j’isole cette étape de la production du système complet, c’est qu’elle prend un temps significatif (une vingtaine de minutes sur un petit processeur i7 avec une connexion Internet fibre). La toolchain est donc compilée et installée une fois pour toutes, et ne sera plus modifiée même si nous réitérons autant de génération du système que nous le désirons.
J’ai l’habitude de placer la toolchain dans le répertoire board/<target>/cross
.
Pour produire la toolchain, nous demandons une configuration de Buildroot par défaut pour la cible choisie, et l’élaguons pour ne laisser que la production du compilateur et de ses outils. Les configurations par défaut disponibles sont visibles dans le sous-répertoire configs/
. Nous choisissons celle pour Raspberry Pi 3b/3b+.
[buildroot-2019.02]$ make raspberrypi3_defconfig [buildroot-2019.02]$ make menuconfig
Voici la liste des modifications apportées :
- Menu
Target options
: pas de changement, mais on peut en profiter pour observer et vérifier le support du processeur cible. - Menu
Build options
:Download dir
: nous extrayons de l’arborescence de Buildroot ce répertoire dans lequel il stocke les fichiers téléchargés. Ainsi les compilations successives ne nécessiterons pas de nouveaux téléchargements.
Nouvelle valeur :$(TOPDIR)/../dl
Host dir
: l’emplacement où se trouvera la toolchain compilée. Comme indiqué plus haut, j’ai pour habitude de la placer dans le répertoireboard/<target>/cross
de notre arborescence de travail.
Nouveau chemin :$(TOPDIR)/../board/rpi-3/cross
- Menu
Toolchain
:C library
: c’est un choix 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 (appels-système). Pour être le plus générique possible, nous choisissons la Gnu C library, un peu plus volumineuse que les autres, mais plus riche également.
Nouvelle valeur :glibc
Kernel Headers
: la compilation de la bibliothèque C nécessite de charger les fichiers headers du noyau afin de connaître ses points d’entrée (voir cet article pour plus de détails). Par défaut Buildroot utilise la même version de noyau que celui qui sera construit pour le système (Linux 4.14.x). Toutefois nous n’allons pas compiler tout de suite de noyau aussi se rabat-il sur la version la plus récente qu’il connaît (Linux 4.19.x) pour produire la libC. Si nous laissons en l’état, une fois que nous aurons compilé tout le système et booterons le Raspberry Pi 3, la GlibC compilée pour un 4.19 refusera de s’initialiser sur un 4.14. et affichera le message « Kernel too old » avant qu’un Kernel Panic se produise puisque le processus init ne peut démarrer.
Nouvelle valeur :Linux 4.14.x kernel headers
. Le menuCustom kernel headers series
disparaît alors.Build cross gdb for the host
: en prévision des éventuelles séances de débogage distant du code applicatif, j’ajoute dans la toolchain un débogueur qui fonctionne sur PC mais peut analyser du code au format de la cible.
Nouvelle valeur :[*]
.
- Menu
System configuration
:Init system
: cette option contient au préalableBusyBox
mais nous la désactivons pour pouvoir éliminer ce package.
Nouvelle valeur :None
Custom scripts to run before creating filesystem images
: nous n’allons pas produire de système complet, il ne faut donc pas lancer le script qui finalise l’arborescence avant de créer les images des systèmes de fichiers.
Nouvelle valeur :()
.Custom scripts to run after creating filesystem images
: pour la même raison nous ne lançons pas le script qui assemble les images des systèmes de fichiers pour créer une image complète de la carte microSD pour Raspberry Pi.
Nouvelle valeur :()
.
- Menu
Kernel
:Linux Kernel
: nous ne voulons, dans un premier temps, produire que la toolchain et rien d’autre. Nous désactivons cette option. Nouvelle valeur :[ ]
- Menu
Target packages
:BusyBox
: c’est le seul package initialement présent. Nous le désactivons.
Nouvelle valeur :[ ]
- Menu
Filesystem images
:ext2/3/4 root filesystem
: inutile, nous ne voulons pas de filesystem pour le moment.
Nouvelle valeur :[ ]
- Menu
Legacy config options
: Je jette toujours un œil à ce menu lorsque j’utilise une nouvelle version de Buildroot avec un fichier de configuration pré-existant car il nous indique les fonctionnalités incompatibles (modifiées, disparues, etc.) entre deux versions. Comme nous avons généré un fichier de configuration « from scratch« , ce n’est pas nécessaire ici.
Sauvegardons la configuration et lançons la compilation. Une nouvelle cible de compilation « toolchain
» est disponible depuis quelques versions de Buildroot. Je suppose que certaines de nos modifications dans les menus « System configuration
« , « Kernel
» et « Target Packages
» sont redondantes avec cette nouvelle cible mais je préfère les appliquer quand même.
La configuration peut être téléchargée directement ici.
[buildroot-2019.02]$ cp .config ../board/rpi-3/buildroot-2019.02-toolchain.config [buildroot-2019.02]$ make toolchain
Après quelques minutes, la compilation se termine avec ces lignes :
sed -e 's#@@STAGING_SUBDIR@@#arm-buildroot-linux-gnueabihf/sysroot#' -e 's#@@TARGET_CFLAGS@@#-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os#' -e 's#@@TARGET_CXXFLAGS@@#-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -Os#' -e 's#@@TARGET_FCFLAGS@@#-Os#' -e 's#@@TARGET_LDFLAGS@@##' -e 's#@@TARGET_CC@@#bin/arm-buildroot-linux-gnueabihf-gcc#' -e 's#@@TARGET_CXX@@#bin/arm-buildroot-linux-gnueabihf-g++#' -e 's#@@TARGET_FC@@#bin/arm-buildroot-linux-gnueabihf-gfortran#' -e 's#@@CMAKE_SYSTEM_PROCESSOR@@#armv8l#' -e 's#@@TOOLCHAIN_HAS_FORTRAN@@#0#' -e 's#@@CMAKE_BUILD_TYPE@@#Release#' /home/cpb/br-tree/buildroot-2019.02/support/misc/toolchainfile.cmake.in > /home/cpb/br-tree/buildroot-2019.02/../board/rpi-3/cross/share/buildroot/toolchainfile.cmake >>> toolchain Fixing libtool files >>> toolchain Installing to target
Vérifions la toolchain produite :
[buildroot-2019.02]$ cd .. [br-tree]$ ls board/rpi-3/cross/usr/bin/ arm-buildroot-linux-gnueabihf-addr2line arm-linux-c++ arm-buildroot-linux-gnueabihf-ar arm-linux-c++.br_real arm-buildroot-linux-gnueabihf-as arm-linux-cc arm-buildroot-linux-gnueabihf-c++ arm-linux-cc.br_real arm-buildroot-linux-gnueabihf-c++.br_real arm-linux-c++filt arm-buildroot-linux-gnueabihf-cc arm-linux-cpp arm-buildroot-linux-gnueabihf-cc.br_real arm-linux-cpp.br_real arm-buildroot-linux-gnueabihf-c++filt arm-linux-elfedit arm-buildroot-linux-gnueabihf-cpp arm-linux-g++ arm-buildroot-linux-gnueabihf-cpp.br_real arm-linux-g++.br_real arm-buildroot-linux-gnueabihf-elfedit arm-linux-gcc arm-buildroot-linux-gnueabihf-g++ arm-linux-gcc-7.4.0 arm-buildroot-linux-gnueabihf-g++.br_real arm-linux-gcc-7.4.0.br_real arm-buildroot-linux-gnueabihf-gcc arm-linux-gcc-ar arm-buildroot-linux-gnueabihf-gcc-7.4.0 arm-linux-gcc.br_real arm-buildroot-linux-gnueabihf-gcc-7.4.0.br_real arm-linux-gcc-nm arm-buildroot-linux-gnueabihf-gcc-ar arm-linux-gcc-ranlib arm-buildroot-linux-gnueabihf-gcc.br_real arm-linux-gcov arm-buildroot-linux-gnueabihf-gcc-nm arm-linux-gcov-dump arm-buildroot-linux-gnueabihf-gcc-ranlib arm-linux-gcov-tool arm-buildroot-linux-gnueabihf-gcov arm-linux-gprof arm-buildroot-linux-gnueabihf-gcov-dump arm-linux-ld arm-buildroot-linux-gnueabihf-gcov-tool arm-linux-ld.bfd arm-buildroot-linux-gnueabihf-gprof arm-linux-nm arm-buildroot-linux-gnueabihf-ld arm-linux-objcopy arm-buildroot-linux-gnueabihf-ld.bfd arm-linux-objdump arm-buildroot-linux-gnueabihf-nm arm-linux-ranlib arm-buildroot-linux-gnueabihf-objcopy arm-linux-readelf arm-buildroot-linux-gnueabihf-objdump arm-linux-size arm-buildroot-linux-gnueabihf-ranlib arm-linux-strings arm-buildroot-linux-gnueabihf-readelf arm-linux-strip arm-buildroot-linux-gnueabihf-size bison arm-buildroot-linux-gnueabihf-strings gawk arm-buildroot-linux-gnueabihf-strip lzip arm-linux-addr2line m4 arm-linux-ar toolchain-wrapper arm-linux-as yacc [br-tree]$
La toolchain de cross-compilation regroupe tous les outils dont les noms sont préfixés par l’architecture (arm
), l’outil de production (buildroot
), le système d’exploitation de la cible (linux
) et les conventions d’interfaçage binaire entre applications et système (gnueabi
). Pour simplifier l’appel des outils, des liens symboliques existent raccourcissant le préfixe pour ne garder que l’architecture et le système d’exploitation. On invoquera donc arm-linux-gcc
ou arm-linux-g++
par exemple.
[br-tree]$ board/rpi-3/cross/usr/bin/arm-linux-gcc -v Utilisation des specs internes. COLLECT_GCC=/home/testing/Build/Lab/br-tree/board/rpi-3/cross/bin/arm-linux-gcc.br_real COLLECT_LTO_WRAPPER=/home/testing/Build/Lab/br-tree/board/rpi-3/cross/bin/../libexec/gcc/arm-buildroot-linux-gnueabihf/7.4.0/lto-wrapper Cible : arm-buildroot-linux-gnueabihf Configuré avec: ./configure --prefix=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross --sysconfdir=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross/etc --enable-static --target=arm-buildroot-linux-gnueabihf --with-sysroot=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross/arm-buildroot-linux-gnueabihf/sysroot --enable-__cxa_atexit --with-gnu-ld --disable-libssp --disable-multilib --disable-decimal-float --with-gmp=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross --with-mpc=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross --with-mpfr=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross --with-pkgversion='Buildroot 2019.02' --with-bugurl=http://bugs.buildroot.net/ --disable-libquadmath --enable-tls --disable-libmudflap --enable-threads --without-isl --without-cloog --with-abi=aapcs-linux --with-cpu=cortex-a53 --with-fpu=neon-vfpv4 --with-float=hard --with-mode=arm --enable-languages=c,c++ --with-build-time-tools=/home/testing/Build/Lab/br-tree/buildroot-2019.02/../board/rpi-3/cross/arm-buildroot-linux-gnueabihf/bin --enable-shared --disable-libgomp Modèle de thread: posix gcc version 7.4.0 (Buildroot 2019.02)
Si l’on souhaite pouvoir invoquer directement le cross-compiler depuis la ligne de commande sans préciser tout le chemin (par exemple pendant une phase de développement de code métier hors Buildroot), on peut éditer le fichier ~/.bashrc
afin d’y ajouter à la fin la ligne suivante :
PATH=$PATH:~/br-tree/board/rpi-3/cross/usr/bin/
On peut aussi adopter une autre approche qui consiste à créer un script qui modifie dynamiquement le PATH
, script que l’on sourcera (que l’on invoquera en le précédant d’un point) avant chaque session de travail sur le code applicatif.
Système complet
Nous allons construire à présent une image d’un système complet, y compris le noyau, en utilisant la toolchain obtenue précédemment. Il nous faut effacer les fichiers objets, fichiers temporaires, etc. produits auparavant et l’on serait tenté de faire un make clean
. Abstenons-nous en néanmoins car cela aurait pour effet d’effacer la toolchain compilée. La solution la plus simple pour éviter les erreurs de manipulation est de supprimer le répertoire de compilation de Buildroot et de décompresser à nouveau l’archive téléchargée.
[br-tree]$ rm -rf buildroot-2019.02 [br-tree]$ tar xf buildroot-2019.02.tar.bz2 [br-tree]$ cd buildroot-2019.02/ [buildroot-2019.02]$
Puis nous préparons une nouvelle configuration, toujours, en partant de celle par défaut.
[buildroot-2019.02]$ make raspberrypi3_defconfig [buildroot-2019.02]$ make menuconfig
Passons en revue les menus pour observer ce qu’il faut modifier :
Target options
: rien à changerBuild options
:Download dir
: configurons le répertoire de téléchargement pour retrouver le précédent.
Nouvelle valeur :$(TOPDIR)/../dl
Toolchain
: plusieurs modifications sont nécessaires pour retrouver la toolchain précédente.Toolchain type
: nous souhaitons que Buildroot considère la toolchain comme préexistante, même si c’est lui qui l’a créée auparavant.
Nouvelle valeur :External toolchain
Toolchain
: elle a été compilée spécifiquement.
Nouvelle valeur :Custom toolchain
Toolchain origin
: il n’est pas nécessaire de la télécharger.
Valeur conservée :Pre-installed toolchain
Toolchain path
: le répertoire dans lequel se trouve le sous-répertoirebin
de la chaîne de compilation.
Nouvelle valeur :$(TOPDIR)/../board/rpi-3/cross/usr
External toolchain gcc version
: si vous n’avez pas noté ce numéro de version lors de la configuration de la toolchain, vous pouvez l’obtenir en appelantarm-linux-gcc -v
comme ci-dessus.
Nouvelle valeur :7.x
External toolchain kernel headers series
: on peut retrouver le numéro de version si on ne l’a pas noté, mais c’est plus compliqué. Il faut regarder le contenu du fichier../board/rpi-3/cross/usr/arm-buildroot-linux-gnueabihf/sysroot/usr/include/linux/version.h
. On y trouve une valeurLINUX_VERSION_CODE 265831
. Il faut convertir ce nombre en hexadécimal, par exemple en saisissant sur la ligne de commande du shellprintf '%x\n' 265831
. Ceci nous affiche40e67
qui représente chiffre par chiffre le numéro de noyau 4.14.103. Ce sont les deux premiers chiffres qui comptent, le troisième n’est pas nécessairement le même que celui qui sera effectivement sélectionné pour compiler le système complet.
Nouvelle valeur :4.14.x
External toolchain C library
: en tant que bibliothèque C, nous avons choisi de compiler une GlibC.
Nouvelle valeur :glibc/eglibc
Toolchain has SSP support?
: l’option Stack Smashing Protection (qui limite les dégâts en cas de débordement de buffer dans la pile) est active par défaut dans GCC.
Nouvelle valeur :[*]
.Toolchain has RPC support?
: le support pour les Remote Procedure Call est activé dans la chaîne de compilation produite.Nous pouvons l’ajouter ici.
Nouvelle valeur :[*]
.Toolchain has C++ support
: l’intégration d’un compilateur C++ dans la toolchain était activée par défaut lors de la compilation précédente.
Nouvelle valeur :[*]
System Configuration
: rien à changer dans ce menu, mais nous l’ajusterons un peu plus tard.Kernel
: rien à changer.Target packages
: rien à changer.Filesystem images
: rien à changer.Bootloaders
: rien à changer.Host utilities
: rien à changer.Legacy config options
: rien à changer.
À nouveau, sauvegardons notre configuration pour pouvoir la réutiliser directement si besoin et lançons la compilation.
[buildroot-2019.02]$ cp .config ../board/rpi-3/buildroot-2019.02-system-01.config [buildroot-2019.02]$ make
La compilation se termine au bout de quelques minutes :
INFO: vfat(boot.vfat): adding file 'zImage' as 'zImage' ... INFO: vfat(boot.vfat): cmd: "mcopy -bsp -i '/home/testing/Build/Lab/br-tree/buildroot-2019.02/output/images/boot.vfat' '/home/testing/Build/Lab/br-tree/buildroot-2019.02/output/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 [buildroot-2019.02]
Tous les fichiers produits lors de la compilation se trouvent dans la sous arborescences output/
:
[buildroot-2019.02]$ ls output/ build host images staging target
Dans le sous répertoires output/build
se trouvent tous les fichiers de compilation (fichiers sources, objets, temporaires) dans des sous-dossiers correspondant aux différents packages. Dans output/host
les fichiers binaires destinés à la machine hôte, notamment la toolchain si nous ne l’avions pas placée explicitement ailleurs. Dans output/images
les fichiers de résultat de la compilation :
[buildroot-2019.02]$ ls output/images bcm2710-rpi-3-b.dtb bcm2710-rpi-cm3.dtb rootfs.ext2 rpi-firmware zImage bcm2710-rpi-3-b-plus.dtb boot.vfat rootfs.ext4 sdcard.img
On y voit notamment le noyau (zImage
) et les fichiers du device tree (*.dtb
), l’image de la partition principale (rootfs.ext4
) et de celle du bootloader (boot.vfat
) et dans le sous-dossier rpi-firmware/
les fichiers appartenant aux bootloader propriétaire du Raspberry Pi. Ce qui nous intéressera surtout c’est sdcard.img
, une image de l’ensemble de la carte micro-SD à fournir au Raspberry Pi, regroupant le secteur de boot, une table de partitionnement et les images de deux partitions précédentes.
On peut aussi observer un sous-répertoire output/target
représentant l’arborescence complète que l’on installera sur la cible.
[buildroot-2019.02]$ ls output/target/ bin etc lib32 media opt root sbin THIS_IS_NOT_YOUR_ROOT_FILESYSTEM usr dev lib linuxrc mnt proc run sys tmp var
Mais que signifie donc ce THIS_IS_NOT_YOUR_ROOT_FILESYSTEM
?
Il s’agit en fait d’un nom de fichier, très anodin. Lorsque Buildroot prépare l’arborescence de la cible, il construit une représentation de son système de fichiers, qui se trouve dans output/target
. Ces fichiers sont créés en appartenant à l’utilisateur courant. Or les fichiers systèmes (ceux se trouvant dans les répertoires /bin
, /etc
, /usr
… de la cible) doivent appartenir à root. Pour pouvoir produire une image (une archive tar
par exemple) avec les bonnes appartenances, on fait appel à un utilitaire nommé fakeroot
qui modifie les droits au moment de la création de l’archive. Pas d’inquiétude, il n’y a rien de malicieux là-dessous, aucun problème de sécurité.
Comme le répertoire output/target
contient des fichiers n’ayant pas la bonne appartenance, il ne faut pas l’utiliser aveuglément, ne pas le copier directement sur une cible où l’exporter pour un montage NFS root. C’est ce que Buildroot nous rappelle en créant ce fameux fichier.
Installation et boot
Depuis quelques versions, l’installation de l’image sur un Raspberry Pi est devenue beaucoup plus simple qu’auparavant puisqu’il suffit de copier un simple fichier sur l’ensemble de la carte micro-SD.
Avant de faire quoique ce soit, lancez la commande lsblk
afin de voir la liste des périphériques blocs et partitions présents. En voici un exemple :
[buildroot-2019.02]$ 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] 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 [buildroot-2019.02]$
Insérons une carte micro-SD sur le poste de développement (par exemple avec un adaptateur USB), puis relançons la même commande pour voir ce qui est apparu.
[buildroot-2019.02]$ 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 1 14,4G 0 disk ├─sdb1 8:17 1 32M 0 part /media/cpb/86B1-7E7E └─sdb2 8:18 1 400M 0 part /media/cpb/68346014-8add-4d95-84f1-1dad224 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 [buildroot-2019.02]$
Dans cet exemple, il s’agit du périphérique sdb
, c’est ce que nous utiliserons ci-dessous.
Attention à ne pas vous tromper d’identifiant de périphérique ! Cela pourrait être dangereux pour votre système si vous confondez avec votre disque dur par exemple.
Je démonte les partitions auto-montées de la carte micro-SD (sdb1
et sdb2
dans mon cas). Ceci est indispensable pour s’assurer qu’il n’y aura plus d’accès à ces emplacements de la carte.
[buildroot-2019.02]$ umount /dev/sdb?
Comme nous l’avons vu, tous les éléments produits par Buildroot se trouvent dans son arborescence output/
. Je m’intéresse à output/images/
qui contient les éléments à installer sur le système cible et plus particulièrement à output/images/sdcard.img
qui représente le contenu complet de l’image à inscrire sur la carte SD.
Il est important de comprendre que ce fichier doit venir écraser l’ensemble des données de la carte SD, y compris les premiers secteurs contenant la table des partitions. Nous devons donc écrire sur le périphérique bloc lui-même /dev/sdb
et pas dans le système de fichiers qu’il peut contenir.
[buildroot-2019.02]$ sudo cp output/images/sdcard.img /dev/sdb
La copie dure un petit moment, c’est normal, il faut remplir quelques centaines de méga-octets. Pas d’inquiétude sur la taille de la carte SD, Buildroot n’utilise que le minimum vital.
Une fois la copie terminée, j’insère la carte micro-SD sur un Raspberry Pi 3 auquel je suis relié par une liaison série avec un câble USB-Série de ce genre (lien Amazon : https://amzn.to/2AHKbaH).
J’observe les messages suivants à la mise sous tension.
[ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 4.14.95-v7 (cpb@why-240) (gcc version 7.4.0 (Buildroot 2019.02)) #1 SMP Wed Mar 6 02:14:35 CET 2019 [ 0.000000] CPU: ARMv7 Processor [410fd034] revision 4 (ARMv7), cr=10c5383d [ 0.000000] CPU: div instructions available: patching division code [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache [ 0.000000] OF: fdt: Machine model: Raspberry Pi 3 Model B Rev 1.2 [ 0.000000] Memory policy: Data cache writealloc [ 0.000000] cma: Reserved 8 MiB at 0x39400000 [ 0.000000] percpu: Embedded 17 pages/cpu @b8b7e000 s38668 r8192 d22772 u69632 [ 0.000000] Built 1 zonelists, mobility grouping on. Total pages: 234465 [ 0.000000] Kernel command line: 8250.nr_uarts=1 bcm2708_fb.fbwidth=720 bcm2708_fb.fbheight=480 bcm2708_fb.fbswap=1 vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000 root=/dev/mmcblk0p2 rootwait console=tty1 console=ttyAMA0,115200 [ 0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes) [ 0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes) [ 0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes) [ 0.000000] Memory: 915872K/946176K available (7168K kernel code, 577K rwdata, 2080K rodata, 1024K init, 698K bss, 22112K reserved, 8192K cma-reserved) [ 0.000000] Virtual kernel memory layout: [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.000000] fixmap : 0xffc00000 - 0xfff00000 (3072 kB) [ 0.000000] vmalloc : 0xba000000 - 0xff800000 (1112 MB) [ 0.000000] lowmem : 0x80000000 - 0xb9c00000 ( 924 MB) [ 0.000000] modules : 0x7f000000 - 0x80000000 ( 16 MB) [ 0.000000] .text : 0x80008000 - 0x80800000 (8160 kB) [ 0.000000] .init : 0x80b00000 - 0x80c00000 (1024 kB) [ 0.000000] .data : 0x80c00000 - 0x80c9063c ( 578 kB) [ 0.000000] .bss : 0x80c98048 - 0x80d46860 ( 699 kB) [ 0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1 [ 0.000000] ftrace: allocating 25246 entries in 75 pages [ 0.000000] Hierarchical RCU implementation. [ 0.000000] NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16 [ 0.000000] arch_timer: cp15 timer(s) running at 19.20MHz (phys). [ 0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x46d987e47, max_idle_ns: 440795202767 ns [ 0.000007] sched_clock: 56 bits at 19MHz, resolution 52ns, wraps every 4398046511078ns [ 0.000021] Switching to timer-based delay loop, resolution 52ns [ 0.000295] Console: colour dummy device 80x30 [ 0.000821] console [tty1] enabled [ 0.000859] Calibrating delay loop (skipped), value calculated using timer frequency.. 38.40 BogoMIPS (lpj=192000) [ 0.000899] pid_max: default: 32768 minimum: 301 [ 0.001211] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes) [ 0.001244] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 bytes) [ 0.002192] Disabling memory control group subsystem [ 0.002283] CPU: Testing write buffer coherency: ok [ 0.002700] CPU0: thread -1, cpu 0, socket 0, mpidr 80000000 [ 0.003120] Setting up static identity map for 0x100000 - 0x10003c [ 0.003259] Hierarchical SRCU implementation. [ 0.003949] smp: Bringing up secondary CPUs ... [ 0.004692] CPU1: thread -1, cpu 1, socket 0, mpidr 80000001 [ 0.005475] CPU2: thread -1, cpu 2, socket 0, mpidr 80000002 [ 0.006247] CPU3: thread -1, cpu 3, socket 0, mpidr 80000003 [ 0.006352] smp: Brought up 1 node, 4 CPUs [ 0.006425] SMP: Total of 4 processors activated (153.60 BogoMIPS). [ 0.006446] CPU: All CPU(s) started in HYP mode. [ 0.006464] CPU: Virtualization extensions available. [ 0.007408] devtmpfs: initialized [ 0.017110] random: get_random_u32 called from bucket_table_alloc+0x120/0x24c with crng_init=0 [ 0.017620] VFP support v0.3: implementor 41 architecture 3 part 40 variant 3 rev 4 [ 0.017861] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns [ 0.017905] futex hash table entries: 1024 (order: 4, 65536 bytes) [ 0.018490] pinctrl core: initialized pinctrl subsystem [ 0.019235] NET: Registered protocol family 16 [ 0.021644] DMA: preallocated 1024 KiB pool for atomic coherent allocations [ 0.026208] hw-breakpoint: found 5 (+1 reserved) breakpoint and 4 watchpoint registers. [ 0.026240] hw-breakpoint: maximum watchpoint size is 8 bytes. [ 0.026440] Serial: AMBA PL011 UART driver [ 0.028065] bcm2835-mbox 3f00b880.mailbox: mailbox enabled [ 0.028526] uart-pl011 3f201000.serial: could not find pctldev for node /soc/gpio@7e200000/uart0_pins, deferring probe [ 0.058858] bcm2835-dma 3f007000.dma: DMA legacy API manager at ba013000, dmachans=0x1 [ 0.060340] SCSI subsystem initialized [ 0.060576] usbcore: registered new interface driver usbfs [ 0.060650] usbcore: registered new interface driver hub [ 0.060750] usbcore: registered new device driver usb [ 0.070170] raspberrypi-firmware soc:firmware: Attached to firmware from 2019-01-22 16:50 [ 0.071337] clocksource: Switched to clocksource arch_sys_counter [ 0.154103] VFS: Disk quotas dquot_6.6.0 [ 0.154214] VFS: Dquot-cache hash table entries: 1024 (order 0, 4096 bytes) [ 0.154415] FS-Cache: Loaded [ 0.154625] CacheFiles: Loaded [ 0.163362] NET: Registered protocol family 2 [ 0.164083] TCP established hash table entries: 8192 (order: 3, 32768 bytes) [ 0.164213] TCP bind hash table entries: 8192 (order: 4, 65536 bytes) [ 0.164415] TCP: Hash tables configured (established 8192 bind 8192) [ 0.164561] UDP hash table entries: 512 (order: 2, 16384 bytes) [ 0.164624] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes) [ 0.164860] NET: Registered protocol family 1 [ 0.165332] RPC: Registered named UNIX socket transport module. [ 0.165356] RPC: Registered udp transport module. [ 0.165376] RPC: Registered tcp transport module. [ 0.165395] RPC: Registered tcp NFSv4.1 backchannel transport module. [ 0.166431] hw perfevents: enabled with armv7_cortex_a7 PMU driver, 7 counters available [ 0.169130] workingset: timestamp_bits=14 max_order=18 bucket_order=4 [ 0.177069] FS-Cache: Netfs 'nfs' registered for caching [ 0.177691] NFS: Registering the id_resolver key type [ 0.177738] Key type id_resolver registered [ 0.177759] Key type id_legacy registered [ 0.177788] nfs4filelayout_init: NFSv4 File Layout Driver Registering... [ 0.179689] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251) [ 0.179842] io scheduler noop registered [ 0.179864] io scheduler deadline registered [ 0.180045] io scheduler cfq registered (default) [ 0.180068] io scheduler mq-deadline registered [ 0.180088] io scheduler kyber registered [ 0.182782] BCM2708FB: allocated DMA memory f9510000 [ 0.182828] BCM2708FB: allocated DMA channel 0 @ ba013000 [ 0.192342] Console: switching to colour frame buffer device 90x30 [ 0.200039] Serial: 8250/16550 driver, 1 ports, IRQ sharing enabled [ 0.203752] bcm2835-rng 3f104000.rng: hwrng registered [ 0.206092] vc-mem: phys_addr:0x00000000 mem_base=0x3ec00000 mem_size:0x40000000(1024 MiB) [ 0.211075] vc-sm: Videocore shared memory driver [ 0.213818] gpiomem-bcm2835 3f200000.gpiomem: Initialised: Registers at 0x3f200000 [ 0.225757] brd: module loaded [ 0.236757] loop: module loaded [ 0.239167] Loading iSCSI transport class v2.0-870. [ 0.242198] libphy: Fixed MDIO Bus: probed [ 0.244608] usbcore: registered new interface driver lan78xx [ 0.247002] usbcore: registered new interface driver smsc95xx [ 0.249301] dwc_otg: version 3.00a 10-AUG-2012 (platform bus) [ 0.279542] dwc_otg 3f980000.usb: base=0xf0980000 [ 0.482058] Core Release: 2.80a [ 0.484295] Setting default values for core params [ 0.486509] Finished setting default values for core params [ 0.688970] Using Buffer DMA mode [ 0.691208] Periodic Transfer Interrupt Enhancement - disabled [ 0.693552] Multiprocessor Interrupt Enhancement - disabled [ 0.695926] OTG VER PARAM: 0, OTG VER FLAG: 0 [ 0.698323] Dedicated Tx FIFOs mode [ 0.701052] WARN::dwc_otg_hcd_init:1046: FIQ DMA bounce buffers: virt = 0xb9504000 dma = 0xf9504000 len=9024 [ 0.706100] FIQ FSM acceleration enabled for : [ 0.706100] Non-periodic Split Transactions [ 0.706100] Periodic Split Transactions [ 0.706100] High-Speed Isochronous Endpoints [ 0.706100] Interrupt/Control Split Transaction hack enabled [ 0.718005] WARN::hcd_init_fiq:459: FIQ on core 1 at 0x805f06e8 [ 0.720303] WARN::hcd_init_fiq:460: FIQ ASM at 0x805f0a44 length 36 [ 0.722533] WARN::hcd_init_fiq:486: MPHI regs_base at 0xf0006000 [ 0.724821] dwc_otg 3f980000.usb: DWC OTG Controller [ 0.727092] dwc_otg 3f980000.usb: new USB bus registered, assigned bus number 1 [ 0.729417] dwc_otg 3f980000.usb: irq 62, io mem 0x00000000 [ 0.731737] Init: Port Power? op_state=1 [ 0.734028] Init: Power Port (0) [ 0.736450] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002 [ 0.738793] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1 [ 0.741142] usb usb1: Product: DWC OTG Controller [ 0.743489] usb usb1: Manufacturer: Linux 4.14.95-v7 dwc_otg_hcd [ 0.745839] usb usb1: SerialNumber: 3f980000.usb [ 0.748736] hub 1-0:1.0: USB hub found [ 0.751037] hub 1-0:1.0: 1 port detected [ 0.754006] usbcore: registered new interface driver usb-storage [ 0.756430] mousedev: PS/2 mouse device common for all mice [ 0.758774] IR NEC protocol handler initialized [ 0.760983] IR RC5(x/sz) protocol handler initialized [ 0.763147] IR RC6 protocol handler initialized [ 0.765271] IR JVC protocol handler initialized [ 0.767349] IR Sony protocol handler initialized [ 0.769441] IR SANYO protocol handler initialized [ 0.771560] IR Sharp protocol handler initialized [ 0.773602] IR MCE Keyboard/mouse protocol handler initialized [ 0.775657] IR XMP protocol handler initialized [ 0.778380] bcm2835-wdt 3f100000.watchdog: Broadcom BCM2835 watchdog timer [ 0.780792] bcm2835-cpufreq: min=600000 max=1200000 [ 0.783279] sdhci: Secure Digital Host Controller Interface driver [ 0.785441] sdhci: Copyright(c) Pierre Ossman [ 0.787910] mmc-bcm2835 3f300000.mmc: could not get clk, deferring probe [ 0.790428] sdhost-bcm2835 3f202000.mmc: could not get clk, deferring probe [ 0.792817] sdhci-pltfm: SDHCI platform and OF driver helper [ 0.796467] ledtrig-cpu: registered to indicate activity on CPUs [ 0.798903] hidraw: raw HID events driver (C) Jiri Kosina [ 0.801348] usbcore: registered new interface driver usbhid [ 0.803610] usbhid: USB HID core driver [ 0.806439] vchiq: vchiq_init_state: slot_zero = b9580000, is_master = 0 [ 0.810195] [vc_sm_connected_init]: start [ 0.819016] [vc_sm_connected_init]: end - returning 0 [ 0.821923] Initializing XFRM netlink socket [ 0.824225] NET: Registered protocol family 17 [ 0.826583] Key type dns_resolver registered [ 0.829078] Registering SWP/SWPB emulation handler [ 0.831976] registered taskstats version 1 [ 0.839872] uart-pl011 3f201000.serial: cts_event_workaround enabled [ 0.842403] 3f201000.serial: ttyAMA0 at MMIO 0x3f201000 (irq = 87, base_baud = 0) is a PL011 rev2 [ 1.859490] console [ttyAMA0] enabled [ 1.867474] 3f215040.serial: ttyS0 at MMIO 0x0 (irq = 166, base_baud = 50000000) is a 16550 [ 1.881916] mmc-bcm2835 3f300000.mmc: mmc_debug:0 mmc_debug2:0 [ 1.890386] mmc-bcm2835 3f300000.mmc: DMA channel allocated [ 1.951951] sdhost: log_buf @ b9507000 (f9507000) [ 1.983589] Indeed it is in host mode hprt0 = 00021501 [ 2.060956] mmc1: queuing unknown CIS tuple 0x80 (2 bytes) [ 2.070551] mmc1: queuing unknown CIS tuple 0x80 (3 bytes) [ 2.073931] random: fast init done [ 2.085541] mmc1: queuing unknown CIS tuple 0x80 (3 bytes) [ 2.096266] mmc1: queuing unknown CIS tuple 0x80 (7 bytes) [ 2.111358] mmc0: sdhost-bcm2835 loaded - DMA enabled (>1) [ 2.120238] of_cfs_init [ 2.125059] of_cfs_init: OK [ 2.130459] uart-pl011 3f201000.serial: no DMA platform data [ 2.138694] Waiting for root device /dev/mmcblk0p2... [ 2.201360] usb 1-1: new high-speed USB device number 2 using dwc_otg [ 2.210283] Indeed it is in host mode hprt0 = 00001101 [ 2.312608] mmc0: host does not support reading read-only switch, assuming write-enable [ 2.323343] mmc1: new high speed SDIO card at address 0001 [ 2.332559] mmc0: new high speed SDHC card at address aaaa [ 2.341056] mmcblk0: mmc0:aaaa SL16G 14.8 GiB [ 2.353029] mmcblk0: p1 p2 [ 2.386347] EXT4-fs (mmcblk0p2): INFO: recovery required on readonly filesystem [ 2.396090] EXT4-fs (mmcblk0p2): write access will be enabled during recovery [ 2.435830] EXT4-fs (mmcblk0p2): recovery complete [ 2.448339] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null) [ 2.461062] VFS: Mounted root (ext4 filesystem) readonly on device 179:2. [ 2.470309] usb 1-1: New USB device found, idVendor=0424, idProduct=9514 [ 2.473602] devtmpfs: mounted [ 2.479425] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [ 2.495175] hub 1-1:1.0: USB hub found [ 2.496418] Freeing unused kernel memory: 1024K [ 2.508486] hub 1-1:1.0: 5 ports detected [ 2.625652] EXT4-fs (mmcblk0p2): re-mounted. Opts: data=ordered Starting syslogd: OK Starting klogd: OK Initializing random number generator... [ 2.730764] random: dd: uninitialized urandom read (512 bytes read) done. Starting network: Waiting for interface eth0 to appear[ 2.831428] usb 1-1.1: new high-speed USB device number 3 using dwc_otg [ 2.961615] usb 1-1.1: New USB device found, idVendor=0424, idProduct=ec00 [ 2.971216] usb 1-1.1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [ 2.983921] smsc95xx v1.0.6 [ 3.074533] smsc95xx 1-1.1:1.0 eth0: register 'smsc95xx' at usb-3f980000.usb-1.1, smsc95xx USB 2.0 Ethernet, b8:27:eb:fa:03:50. [ 3.913456] smsc95xx 1-1.1:1.0 eth0: hardware isn't capable of remote wakeup udhcpc: started, v1.29.3 udhcpc: sending discover udhcpc: sending discover udhcpc: sending discover udhcpc: no lease, failing FAIL Welcome to Buildroot buildroot login:
Il ne faut pas s’étonner, lorsqu’aucun câble n’est branché dans la prise Ethernet du Raspberry Pi, la fin du boot est un peu longue. Le service udhcpc
répète ses sending discover
toutes les trois secondes jusqu’à renoncer à joindre un serveur DHCP et affiche alors le message no lease, failing
. Le script d’initialisation du réseau nous indique sèchement son échec d’un FAIL
abrupt.
Nous disposons bien d’une invite de login, et pouvons nous connecter :
buildroot login: root # uname -a Linux buildroot 4.14.95-v7 #1 SMP Wed Mar 6 02:14:35 CET 2019 armv7l GNU/Linux
Le noyau correspondant bien à la version indiquée plus haut. Vérifions le processeur du Raspberry Pi 3.
# cat /proc/cpuinfo processor : 0 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 1 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 2 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 processor : 3 model name : ARMv7 Processor rev 4 (v7l) BogoMIPS : 38.40 Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32 CPU implementer : 0x41 CPU architecture: 7 CPU variant : 0x0 CPU part : 0xd03 CPU revision : 4 Hardware : BCM2835 Revision : a02082 Serial : 000000003efa0350 #
Nous avons bien observé les quatre cœurs de processeur du Raspberry Pi 3. Examinons l’état de la mémoire :
# free total used free shared buffers cached Mem: 925088 17408 907680 44 300 2288 -/+ buffers/cache: 14820 910268 Swap: 0 0 0
Le système compte au total un Go (dont une partie est réservée pour le contrôleur graphique) et dispose de 890 Mo de mémoire libre. En pressant deux fois la touche tabulation, nous pouvons voir la liste des commandes disponibles :
# (tab) (tab) [ fsfreeze microcom sha3sum [[ fstrim mkdir sha512sum addgroup fuser mkdosfs shred adduser getopt mke2fs sleep ar getty mkfifo sort arch grep mknod start-stop-daemon arp gunzip mkpasswd strings arping gzip mkswap stty ash halt mktemp su awk hdparm modprobe sulogin basename head more svc blkid hexdump mount svok bunzip2 hexedit mountpoint swapoff busybox hostid mt swapon bzcat hostname mv switch_root cat hwclock nameif sync chattr i2cdetect netstat sysctl chgrp i2cdump nice syslogd chmod i2cget nl tail chown i2cset nohup tar chroot id nproc tc chrt ifconfig nslookup tee chvt ifdown nuke telnet cksum ifup od test clear inetd openvt tftp cmp init partprobe time cp insmod passwd top cpio install paste touch crond ip patch tr crontab ipaddr pidof traceroute cut ipcrm ping true date ipcs pipe_progress truncate dc iplink pivot_root tty dd ipneigh poweroff ubirename deallocvt iproute printenv udhcpc delgroup iprule printf uevent deluser iptunnel ps umount devmem kill pwd uname df killall rdate uniq diff killall5 readlink unix2dos dirname klogd readprofile unlink dmesg last realpath unlzma dnsd less reboot unlzop dnsdomainname link renice unxz dos2unix linux32 reset unzip du linux64 resize uptime dumpkmap ln resume usleep echo loadfont rm uudecode egrep loadkmap rmdir uuencode eject logger rmmod vconfig env login route vi ether-wake logname run-init vlock expr losetup run-parts w factor ls runlevel watch fallocate lsattr sed watchdog false lsmod seq wc fbset lsof setarch wget fdflush lspci setconsole which fdformat lsscsi setfattr who fdisk lsusb setkeycodes whoami fgrep lzcat setlogcons xargs find lzma setpriv xxd flock lzopcat setserial xz fold makedevs setsid xzcat free md5sum sh yes freeramdisk mdev sha1sum zcat fsck mesg sha256sum #
La commande ps
nous affiche la liste des processus présents :
# ps PID USER COMMAND 1 root init 2 root [kthreadd] 3 root [kworker/0:0] 4 root [kworker/0:0H] 5 root [kworker/u8:0] 6 root [mm_percpu_wq] 7 root [ksoftirqd/0] 8 root [rcu_sched] 9 root [rcu_bh] 10 root [migration/0] 11 root [cpuhp/0] 12 root [cpuhp/1] 13 root [migration/1] 14 root [ksoftirqd/1] 15 root [kworker/1:0] 16 root [kworker/1:0H] 17 root [cpuhp/2] 18 root [migration/2] 19 root [ksoftirqd/2] 20 root [kworker/2:0] 21 root [kworker/2:0H] 22 root [cpuhp/3] 23 root [migration/3] 24 root [ksoftirqd/3] 26 root [kworker/3:0H] 27 root [kdevtmpfs] 28 root [netns] 30 root [kworker/1:1] 31 root [kworker/2:1] 32 root [kworker/3:1] 33 root [khungtaskd] 34 root [oom_reaper] 35 root [writeback] 36 root [kcompactd0] 37 root [crypto] 38 root [kblockd] 39 root [watchdogd] 40 root [rpciod] 41 root [xprtiod] 42 root [kswapd0] 43 root [nfsiod] 53 root [kthrotld] 54 root [iscsi_eh] 55 root [dwc_otg] 56 root [DWC Notificatio] 57 root [vchiq-slot/0] 58 root [vchiq-recy/0] 59 root [vchiq-sync/0] 60 root [vchiq-keep/0] 61 root [SMIO] 63 root [irq/92-mmc1] 64 root [kworker/3:3] 66 root [mmcqd/0] 67 root [kworker/1:1H] 68 root [jbd2/mmcblk0p2-] 69 root [ext4-rsv-conver] 84 root /sbin/syslogd -n 86 root [kworker/3:1H] 89 root /sbin/klogd -n 119 root [kworker/u8:1] 120 root [kworker/0:2] 122 root -sh 123 root /sbin/getty -L tty1 0 vt100 124 root [kworker/2:1H] 129 root [kworker/0:1H] 130 root [kworker/0:1] 131 root ps #
Hormis les threads du noyau (toutes les tâches avec des noms entre crochets), nous observons la présence de seulement six processus :
init
: le premier processus qui est chargé d’abord de l’initialisation du système depuis l’espace utilisateur et par la suite de « l’adoption » des processus dont le parent se termine ;syslogd
etklogd
: démons chargés de l’enregistrement des messages du système ;getty
: l’outil qui attend les connexions sur le terminaltty1
(écran HDMI + clavier USB), je suis connecté sur la console série et legetty
correspondant a laissé sa place au shell ;sh
le shell sur lequel nous sommes connectés et la commandeps
elle-même.
Voilà un système dont le contenu est bien sous contrôle !
Affinement de la configuration
Nous pouvons faire une première 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.
Configuration de Buildroot
[buildroot-2019.02]$ make menuconfig
- Menu
System configuration
:System hostname
: Choisissons un nom plus représentatif pour notre carte. Il apparaîtra par exemple dans l’invite de connexion, et nous l’afficherons également dans le prompt du shell.
Nouvelle valeur :R-Pi
.System banner
: Cette petite phrase s’affichera au démarrage avant la proposition de connexion ; on peut la personnaliser à volonté.
Nouvelle valeur :Welcome on board!
Enable root login with password
: Si le système a la moindre chance de se retrouver connecté à Internet, il est préférable de désactiver cette option. En effet le compte root sera le premier visé par les attaques automatiques par force brute. Si cette option est désactivée, il faudra intégrer la commandesudo
afin de pouvoir réaliser les opérations d’administration.
Sur un système expérimental, nous laissons la valeur originale :[*]
.Root password
: De même, il est conseillé de choisir pour tous les comptes des mots de passe solides (longs, assez faciles à retenir mais difficiles à deviner). Pour cette démonstration prenons un mot de passe ridiculement simple.
Nouvelle valeur :root
.remount root filesystem read-write during boot
: Sur un système embarqué où l’alimentation peut être coupée à tout moment, il est conseillé de conserver le système de fichiers principal en lecture-seule. On le basculera en lecture-écriture temporairement pour des modifications de configuration par exemple.
Nouvelle valeur :[ ]
Network interface to configure through DHCP
: Suivant la situation, on utilisera ou non une configuration réseau par DHCP. Si tel est le cas, on indique ici le nom de l’interface Ethernet.
Valeur conservée :eth0
.Path to the users tables
: On indique ici le chemin d’accès pour un fichier contenant la liste des utilisateurs (voir plus loin).
Nouvelle valeur :$(TOPDIR)/../board/rpi-3/users.tbl
.Root filesystem overlay directories
: Le répertoire indiqué ici est l’origine d’une arborescence qui sera appliquée « par-dessus » le système de fichiers obtenu à l’issue des compilations et installations, avant de préparer l’image de sortie. Autrement dit notre arborescence va venir se superposer (remplaçant éventuellement des fichiers) à celle se trouvant dansoutput/target
avant de la stocker dansoutput/image/rootfs.tar
Nous détaillerons plus loin ce qu’il faut ajouter dans cet overlay.
Nouvelle valeur :$(TOPDIR)/../board/rpi-3/ovl
.
Table des utilisateurs
Nous avons rempli l’option 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 un espace, sont les suivants :
- login : identifiant de connexion du compte (sauf root).
- uid : numéro d’utilisateur. On peut indiquer
-1
pour que l’attribution soit automatique, mais nous aurons besoin un peu plus loin d’une UID connue, je précise donc1000
. - group : groupe principal de l’utilisateur. Généralement le même nom que le login, ou alors un groupe global pour tous les comptes, comme
users
. - gid : numéro du groupe. Comme pour le champ uid, je précise
1000
pour connaître à l’avance le numéro de groupe. - password : le mot de passe, en clair si précédé d’un ‘
=
‘, crypté sinon. Si le mot de passe est ‘!
‘, pas de connexion possible (compte utilisé pour un démon système par exemple). - home : répertoire personnel (aucun si ‘
-
‘). - shell : le shell de connexion. Sur notre système minimal,
/bin/sh
est un shell ash inclus dans Busybox. - groups : ce champ contient la liste des groupes supplémentaires auxquels appartient l’utilisateur (
-1
si aucun). - gecos : des informations sur le compte, comme le nom en clair de l’utilisateur. Ce dernier champ peut contenir éventuellement des espaces.
Voici notre fichier <code>../board/rpi-3/users.tbl :
rpi 1000 rpi 1000 =rpi /home/rpi /bin/sh - Raspberry Pi User
Overlay
Comme indiqué plus haut nous nous créons une arborescence spécifique, contenant des fichiers qui viendront s’ajouter à ceux produits par Buildroot.
[buildroot-2019.02]$ mkdir -p ../board/rpi-3/ovl/usr/bin/
Scripts supplémentaires
Le système de fichiers principal est monté en lecture seule. Mais il est parfois nécessaire de repasser temporairement en lecture-écriture. Pour cela j’ai l’habitude de créer deux petits scripts rw
et ro
qui remontent la racine du système de fichiers respectivement en lecture-écriture ou lecture seule. Pour que Buildroot puisse les intégrer automatiquement dans l’image qu’il produit, nous les plaçons dans l’arborescence overlay :
[buildroot-2019.02]$ nano ../board/rpi-3/ovl/usr/bin/rw
Contenu du script rw
#! /bin/sh mount / -o rw,remount
[buildroot-2019.02]$ cp ../board/rpi-3/ovl/usr/bin/rw ../board/rpi-3/ovl/usr/bin/ro [buildroot-2019.02]$ nano ../board/rpi-3/ovl/usr/local/bin/ro
Contenu du script ro
#! /bin/sh mount / -o ro,remount
[buildroot-2019.02]$ chmod +x ../board/rpi-3/ovl/usr/bin/*
Lançons la nouvelle production d’image (qui ne dure que quelques secondes).
[buildroot-2019.02]$ make
Nous ré-insérons la carte micro-SD dans le PC de développement et re-flashons l’image.
[buildroot-2019.02]$ umount /dev/sdb? [buildroot-2019.02]$ sudo cp output/images/sdcard.img /dev/sdb
Après redémarrage du Raspberry Pi, vérifions la connexion avec l’identité root :
Welcome on board! R-Pi login: root Password: (root) #
Vérifions tout de suite si le système de fichiers est bien monté en lecture seulement.
# ls / bin lib media root tmp dev lib32 mnt run usr etc linuxrc opt sbin var home lost+found proc sys # echo hello > /test-file -sh: can't create /test-file: Read-only file system
Très bien. Vérifions qu’il soit possible de passer temporairement en lecture-écriture.
# rw [ 86.847490] EXT4-fs (mmcblk0p2): re-mounted. Opts: data=ordered # echo hello > /test-file # ls / bin lib media root test-file dev lib32 mnt run tmp etc linuxrc opt sbin usr home lost+found proc sys var # ro [ 116.742951] EXT4-fs (mmcblk0p2): re-mounted. Opts: data=ordered # rm /test-file rm: remove '/test-file'? y rm: can't remove '/test-file': Read-only file system # exit
La protection du système est donc correcte. Vérifions l’accès en tant qu’utilisateur normal.
Welcome on board! R-Pi login: rpi Password: (rpi) $ pwd /home/rpi $ ls $ echo hello > my-file -sh: can't create my-file: Read-only file system $
Ici, un petit problème se pose. La partition « système » de notre Raspberry Pi est bien protégée contre les écritures, mais nous aimerions peut-être disposer de possibilités de stockage de données utilisateur. Pour cela il va falloir envisager l’ajout d’une partition supplémentaire montée en lecture-écriture.
Nouvelles améliorations
Nous allons donc ajouter une nouvelle partition, formatée en vfat
, afin de contenir les données utilisateur. Comme j’en ai parlé dans cet article, le format vfat
est beaucoup plus simple que les traditionnels ext4
et consorts, mais il 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
. Nous allons copier le fichier original produit par Buildroot dans notre overlay et lui ajouter une dernière ligne.
[buildroot-2019.02]$ mkdir -p ../board/rpi-3/ovl/etc [buildroot-2019.02]$ cp output/target/etc/fstab ../board/rpi-3/ovl/etc/ [buildroot-2019.02]$ nano ../board/rpi-3/ovl/etc/fstab
Le fichier est modifié ainsi (dernière ligne ajoutée) :
/dev/root / ext2 rw,noauto 0 1 proc /proc proc defaults 0 0 devpts /dev/pts devpts defaults,gid=5,mode=620 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 vfat defaults,uid=1000,gid=1000 0 0
Pour créer les partitions et l’image sdcard.img
finale, Buildroot appelle le script board/raspberrypi3/post-image.sh
(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/raspberrypi3/genimage-raspberrypi3.cfg
.
Pour éviter de modifier un fichier fourni par Buildroot, nous pourrions copier le script et le fichier de configuration dans notre répertoire, les modifier à cet endroit, et fournir notre version dans l’option « Custom scripts to run after creating filesystem images« . Pour garder cet article relativement simple, je vais modifier directement le fichier de configuration genimage-raspberrypi3.cfg
.
[buildroot-2019.02]$ nano board/raspberrypi3/genimage-raspberrypi3.cfg
Le fichier est modifié (lignes ajoutées en gras) pour devenir :
image boot.vfat { vfat { files = { "bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b-plus.dtb", "bcm2710-rpi-cm3.dtb", "rpi-firmware/bootcode.bin", "rpi-firmware/cmdline.txt", "rpi-firmware/config.txt", "rpi-firmware/fixup.dat", "rpi-firmware/start.elf", "rpi-firmware/overlays", "zImage" } } size = 32M } image home.vfat { name = "home.vfat" vfat { files = { } } size = 128M mountpoint = "/home" } image sdcard.img { hdimage { } partition boot { partition-type = 0xC bootable = "true" image = "boot.vfat" } partition rootfs { partition-type = 0x83 image = "rootfs.ext4" } partition home { partition-type = 0xC image = "home.vfat" } }
Je crée un répertoire dans la nouvelle partition home
:
[buildroot-2019.02]$ mkdir -p ../board/rpi-3/ovl/home/rpi
Recompilons notre système, et réinscrivons-le sur la carte SD.
[buildroot-2019.02]$ cp .config ../board/rpi-3/buildroot-2019.02-system-02.config [buildroot-2019.02]$ make [buildroot-2019.02]$ umount /dev/sdb? [buildroot-2019.02]$ sudo cp output/images/sdcard.img /dev/sdb
Après démarrage du Raspberry Pi, nous pouvons vérifier que l’utilisateur rpi
a bien accès à son répertoire personnel en lecture et écriture.
Welcome on board! R-Pi login: rpi Password: (rpi) $ echo 1 > my-file $ ls -l total 2 -rwxr-xr-x 1 rpi rpi 2 Jan 1 00:00 my-file $ exit
Vérifions aussi que la partition système est bien en lecture seulement, même pour l’utilisateur root
.
Welcome on board! R-Pi login: root Password: (root) # echo 1 > my-file -sh: can't create my-file: Read-only file system #
Bien entendu, root
peut appeler notre commande rw
s’il souhaite vraiment modifier un fichier de l’arborescence système.
Conclusion
Nous disposons ainsi d’un système Linux embarqué minimal assez personnalisé. Bien sûr il faudra ajouter de nombreux outils pour avoir un véritable environnement de travail complet, citons par exemple le serveur SSH Dropbear, un serveur web comme celui de Busybox ou Lighttp, un client NTP, etc. Nous verrons cela dans de prochains articles.