Utiliser votre devkit STM32MP157 avec Buildroot

Magazine
Marque
Hackable
Numéro
42
Mois de parution
mai 2022
Spécialité(s)


Résumé

Dans le précédent article, nous avons vu comment prendre en main le STM32MP157F-DK2, mettre à jour le système, utiliser le SDK et reconstruire l'ensemble avec OpenEmbedded sur la base d'OpenSTLinux, mais aussi de Poky. Le système de build du projet Yocto n'est cependant pas le seul utilisable et, grâce au travail de Bootlin en partenariat avec ST, il est également possible d'utiliser Buildroot. Voyons cela ensemble...


Body

La philosophie de Buildroot est sensiblement différente de celle d'OpenEmbedded. Il n'y a pas ici de layers (couches) s'ajoutant les uns aux autres sur une base minimale pour composer une distribution qu'on enrichira, ensuite, avec un ou des layers personnalisés. Buildroot est un ensemble complet destiné à produire un système en fonction des éléments que le développeur activera ou non, via une interface similaire à celle utilisée pour configurer le noyau Linux (outil kconfig). L'objectif reste identique, que ce soit avec OpenEmbedded ou Buildroot, nous voulons un noyau, une chaîne de compilation, un bootloader et un système de fichiers racine, mais la façon de construire cet ensemble sera radicalement différente. Autre différence importante, il n'y a pas de notion de paquet avec Buildroot, l'objectif étant de produire un système complet en une fois.

Mais ce qui distingue réellement les deux systèmes de build sera avant tout leur complexité relative en termes de configuration. OpenEmbedded est très modulaire, ce qui implique que la configuration se trouve dispersée dans une collection de fichiers, dont les interactions et les dépendances peuvent être difficiles à appréhender. Buildroot, en revanche, centralise la configuration en un seul fichier et un seul projet, rendant l'ensemble plus simple à embrasser globalement. Comme nous le verrons plus loin dans l'article, ceci n'empêche pas l'intégration d'éléments externes maintenus en parallèle (BR2_EXTERNAL), mais ce n'est pas la base de l'architecture, contrairement à ce que propose le projet Yocto.

La préférence envers l'un ou l'autre système est une affaire de goût personnel, mais aussi, et surtout, de disponibilité de prise en charge de la cible (SoC ou devkit) choisie. Fort heureusement, dans le cas du STM32MP157, les deux environnements de construction sont parfaitement supportés, ce qui en fait une plateforme idéale d'un point de vue pédagogique. En effet, quel que soit votre sentiment sur ces systèmes de build, il est important de ne pas privilégier l'un au détriment de l'autre, car les deux sont activement maintenus et utilisés. Il faut donc savoir utiliser à la fois OpenEmbedded et Buildroot.

Dans le précédent article [1], nous avons fait connaissance avec la distribution OpenSTLinux et ce choix n'était pas totalement innocent. Non seulement il s'agit de la distribution officielle initialement supportée, mais sa relation avec OpenEmbedded implique davantage d'efforts de compréhension. Face à cela, un des objectifs de Buildroot est de rester concis et simple, le rendant facile à comprendre, en particulier pour un utilisateur GNU/Linux connaissant son sujet. Par rapport à Yocto, c'en est presque un soulagement...

stm32MPdk hdmi-s

Le STM32MP157F-DK2 est la déclinaison la plus complète permettant d'évaluer la plateforme STM32MP157 puisqu'en plus de proposer un touch screen, le Wi-Fi et le Bluetooth, c'est également celle vous permettant d'expérimenter les fonctionnalités Trust Zone OP-TEE, contrairement au devkit STM32MP157D-DK1.

1. Construire un système avec Buildroot

Contrairement à OpenEmbedded, nous n'avons ici qu'une seule source pour récupérer Buildroot, ou presque. En effet, à l'heure où est composé cet article, les deux devkits STM32MP157 ne sont encore que partiellement supportés par le projet officiel (mais l'intégration est en cours). Il est donc nécessaire de compléter l'environnement avec une branche externe (BR2_EXTERNAL) ajoutant des éléments de configuration qui ne font pas partie de l'arborescence officielle. Notez que même si ce mécanisme ressemble aux layers d'OpenEmbedded, avec Buildroot il n'est possible de l'utiliser qu'une fois et ceci uniquement pour ajouter des éléments et non écraser ceux qui sont existants. C'est une solution pour ajouter le support d'une plateforme, mais de préférence comme phase préliminaire d'intégration au projet officiel.

Étant donné la nature « temporaire » du support des cartes DK, il sera nécessaire d'utiliser un fork Bootlin de Buildroot [2] en version LTS (2021.02.*) en compagnie de l'arborescence externe, mais tout ceci est susceptible d'évoluer dans le futur. Quoi qu'il en soit, Buildroot étant très facile à « comprendre », adapter ces explications par la suite ne devrait pas être un problème.

Nous commençons donc par récupérer l'arborescence « officielle » depuis GitHub, ainsi que l'arborescence externe :

$ cd quelquepart
$ git clone -b st/2021.02 \
https://github.com/bootlin/buildroot.git
 
$ git clone -b st/2021.02 \
https://github.com/bootlin/buildroot-external-st.git

Notez l'utilisation de branches Git identiques pour les deux dépôts afin de disposer de configurations parfaitement synchronisées. Nous avons maintenant deux répertoires, buildroot pour l'environnement de construction standard et buildroot-external-st constituant l'arborescence externe. La prochaine étape consiste donc à lier les deux :

$ cd buildroot
$ make BR2_EXTERNAL=../buildroot-external-st \
st_stm32mp157c_dk2_demo_defconfig
[...]
# configuration written to /mnt/SSD2T/BR/buildroot/.config
#

Nous utilisons ici l'une des configurations par défaut (defconfigs) fournies par l'arborescence externe, destinée au SoC STM32MP157CACx et incluant un certain nombre de codes de démonstration. La variable d'environnement BR2_EXTERNAL nous permet de spécifier l'emplacement de l'arborescence externe durant cette phase de configuration, mais il ne sera pas nécessaire de l'utiliser par la suite, ceci est inclus dans la configuration locale (BR2_EXTERNAL_ST_PATH dans le .config). Vous pouvez lister toutes les configurations par défaut, de la branche principale et de l'arborescence externe avec un simple make list-defconfigs. Remarquez que nous utilisons une carte STM32MP157F-DK2, équipée d'un SoC STM32MP157FACx, sensiblement différent du STM32MP157CACx équipant le STM32MP157C-DK2 mais majoritairement compatible (ce qui expliquera deux messages d'erreur concernant l'OPP, pour Operating Performance Points, au moment du boot, le devicetree pour le STM32MP157CACxdu ne spécifiant pas de table OPP).

Nous avons ici un mécanisme assez similaire à une configuration de noyau Linux, puisque nous appliquons une configuration par défaut qui sera copiée dans un .config local. Nous pourrons ensuite modifier ce fichier à l'aide d'un make menuconfig pour éventuellement ajuster les options. Ceci est généralement une bonne idée, en particulier sur une machine où vous travaillez en parallèle. Dans le menu présenté avec un make menuconfig, visitez « Build options » et vous trouverez l'entrée « Number of jobs to run simultaneously » ajustant la valeur de BR2_JLEVEL, correspondant à l'option -j de make. La configuration par défaut règle cette valeur automatique sur le nombre de CPU (ou cœurs/threads) plus 1. En réduisant celle-ci, à la moitié par exemple, vous permettez à votre système de rester réactif pendant la construction. Vous pouvez également activer « Enable compiler cache » (BR2_CCACHE) permettant d'utiliser ccache afin d'accélérer grandement les compilations consécutives (le cache sera créé dans ~/.buildroot-ccache/ par défaut, mais peut être ajusté également). Enfin, l'entrée « gcc optimization level » correspond à l'option -O de GCC et donc au niveau d'optimisation du compilateur. Le réglage par défaut sur -Os (BR2_OPTIMIZE_S) optimise pour la taille, mais vous pouvez vouloir optimiser pour les performances (-O3 / BR2_OPTIMIZE_3) ou ne pas optimiser du tout (-O0 / BR2_OPTIMIZE_0).

Dans la suite de cet article, je préciserai systématiquement, entre parenthèses, le nom de l'élément de configuration tel qu'il apparaît dans le fichier de configuration (.config). L'interface accessible via make menuconfig, comme celle du noyau Linux permet de procéder à une recherche sur ces éléments via le raccourci /, vous présentant alors le ou les résultats, accompagnés d'un descriptif, des dépendances liées et aussi de l'emplacement de l'option dans l'arborescence de menus pour y accéder facilement.

Une fois satisfait de vos réglages, quittez l'interface de configuration et lancez la construction d'un simple make :

$ make
[...]
INFO: hdimage(sdcard.img): adding partition
'fsbl1' (in MBR) from 'tf-a-stm32mp157c-dk2-mx.stm32' ...
INFO: hdimage(sdcard.img): adding partition
'fsbl2' (in MBR) from 'tf-a-stm32mp157c-dk2-mx.stm32' ...
INFO: hdimage(sdcard.img): adding partition
'fip' (in MBR) from 'fip.bin' ...
INFO: hdimage(sdcard.img): adding partition
'rootfs' (in MBR) from 'rootfs.ext4' ...
INFO: hdimage(sdcard.img): writing GPT
INFO: hdimage(sdcard.img): writing protective MBR
INFO: hdimage(sdcard.img): writing MBR

Comme avec d'autres systèmes de construction, Buildroot va tout d'abord produire une chaîne de compilation adaptée à la cible (deux dans le cas présent, une pour le Cortex-A7 et la seconde pour le M4 afin de compiler les codes de démonstration), puis télécharger les sources des composants du système pour les extraire et les compiler. Enfin, une fois cet ensemble d'étapes accomplies, le ou les systèmes de fichiers seront assemblés pour produire une image qu'il vous sera possible de flasher sur la plateforme. L'ensemble de la procédure, sur mon bi-Xeon E5520, avec la configuration par défaut (avec les codes de démonstration et donc Qt) et 10 jobs prend initialement une bonne heure. Moins que le build OpenEmbedded, mais ceci n'est pas réellement comparable étant donné que le système produit n'est que vaguement similaire.

Au final, vous obtenez dans output/images/ le résultat de la construction sous la forme de plusieurs fichiers :

  • fip.bin : le FIP ou Firmware Image Package regroupant de façon structurée et binaire les bootloaders, un devicetree et un certificat pour le boot TF-A (Trusted Firmware-A [3]).
  • rootfs.ext2 : une image du système de fichiers racine au format EXT4 (l'extension ext2 étant présente pour des raisons de compatibilité).
  • rootfs.ext4 : un lien symbolique vers rootfs.ext2 avec une extension plus adaptée.
  • sdcard.img : une image du support amovible contenant plusieurs partitions GPT (FSBL1, FSBL2, FIP et système de fichiers racine).
  • stm32mp157c-dk2.dtb : un devicetree binaire utilisé par le noyau Linux pour la prise en charge des périphériques non détectables.
  • tf-a-stm32mp157c-dk2-mx.stm32 : un bootloader de premier niveau (ou FSBL pour First Stage BootLoader) utilisé comme FSBL1 et FSBL2.
  • tee.bin, tee-header_v2.bin, tee-pageable_v2.bin et tee-pager_v2.bin : les éléments composant l’environnement d'exécution OP-TEE, la partie sécurisée du système Trust Zone.
  • u-boot.dtb : le devicetree binaire utilisé par le bootloader U-Boot.
  • u-boot-nodtb.bin : le binaire U-Boot lui-même.
  • zImage : l'image du noyau Linux.

Comme avec OpenSTLinux, ces éléments peuvent être utilisés pour écrire la microSD ou être utilisés avec STM32CubeProgammer. Pour ce faire, la branche externe développée par Bootlin intègre un fichier TSV que vous trouverez dans buildroot-external-st/board/stmicroelectronics/stm32mp157 sous le nom flash.tsv. Pour mettre à jour votre devkit, vous pouvez placer les micro-interrupteurs BOOT0 et BOOT2 sur OFF, connecter la carte en USB-C, l'alimenter puis utiliser :

$ cd output/images
$ STM32CubeProgrammer/bin/STM32_Programmer_CLI \
-c port=usb1 -w ../../../buildroot-external-st/\
board/stmicroelectronics/stm32mp157/flash.tsv
[...]
Memory Programming ...
Opening and parsing file: sdcard.img
  File          : sdcard.img
  Size          : 212290048 Bytes
  Partition ID  : 0x10
 
Download in Progress:
[=====================================] 100%
File download complete
Time elapsed during download operation: 00:01:05.783
 
RUNNING Program ...
  PartID:      :0x10
Start operation done successfully at partition 0x10
Flashing service completed successfully

Une fois l'opération terminée, repositionnez les micro-interrupteurs sur ON et procédez à un reset via le bouton dédié. En utilisant la console série mise à votre disposition via le connecteur ST-LINK/V2-1 (ttyACM et 115200 8N1), vous devrez alors constater le démarrage du système et arriver à l'invite de connexion. Vous pouvez alors vous identifier comme root et obtenir une invite de shell sans saisir de mot de passe. Bravo, vous avez construit, flashé et démarré votre premier système GNU/Linux sur STM32MP157F-DK2 avec Buildroot.

stm32MPdk 40broches-s

Les devkits STM32MP157 proposent un connecteur 40 broches similaire à celui d'une Raspberry Pi avec une disposition majoritairement compatible (3v3, GND, port série, i2c, etc., aux mêmes emplacements).

2. Procédons à quelques ajustements

Si vous faites le tour du propriétaire, vous vous rendrez rapidement compte que tout ceci est assez spartiate. Vous n'avez, par exemple, ni console sur l'écran LCD ni services intéressants (SSH, Avahi, etc.) ou même de connectivité réseau. Vous trouverez cependant quelques éléments intéressants découlant de la configuration utilisée par défaut, comme le contenu de /usr/lib/qt/examples réunissant quelques exemples amusants de ce qu'il est possible de faire avec Qt, ou encore dans /usr/lib/Cube-M4-examples, un jeu de firmwares à destination du coprocesseur Cortex-M4 intégré au STM32MP157.

Même si la plateforme n'est en rien destinée à être une sorte de mini-ordinateur générique, un minimum de confort est nécessaire, ne serait-ce que pour sereinement développer sa ou ses applications pour ensuite les faire s'exécuter sur la cible. Notre objectif est de changer la configuration de Buildroot pour disposer d'un système toujours aussi light, mais capable de supporter un minimum de manipulations (oui, c'est une excuse pour découvrir les spécificités de Buildroot).

Une partie des modifications que nous souhaitons apporter impliquent l'utilisation de fichiers qui vont venir s'ajouter en complément de ceux déjà utilisés ou remplacer ceux existants. Il nous faut donc un emplacement pour stocker ces fichiers. Un parfait exemple concerne l'ajout d'un utilisateur standard, qui passe généralement par la création d'un fichier (la users table) stockant les informations utilisées par Buildroot. L'emplacement recommandé pour un tel fichier, selon la documentation officielle, est board/<company>/<boardname>/ et donc, dans le board/stmicroelectronics/stm32mp157 de buildroot-external-st. Nous ne voulons cependant pas modifier notre copie du dépôt de Bootlin.

Nous pourrions forker ce dépôt sur GitHub, mais dans ce cas, il ne nous sera pas possible d'en réduire la visibilité pour le rendre privé. La solution consiste donc à créer un dépôt vide quelque part (GitHub ou ailleurs) avec git init --bare et de tout simplement ajouter le dépôt distant avec git remote add, puis de faire un git push -u suivi du nouveau nom. Nous pouvons même basculer sur la nouvelle branche avec git checkout -b et donc suivre nos modifications avec Git, sans pour autant risquer de nous mélanger les pinceaux.

2.1 Réseau, SSH, etc.

La première chose qu'on peut souhaiter activer est le support réseau ou plus exactement sa configuration. En effet, les interfaces sont bien présentes, comme en témoigne la sortie d'un simple ifconfig -a ou ip addr. Nous retrouvons là l'interface loopback (lo), Ethernet (eth0), Wi-Fi (wlan0) et l'interface virtuelle IPv6/IPv4 (sit0). Ce qui nous manque en revanche, c'est la configuration de l'interface eth0, comme en témoigne le contenu du fichier /etc/network/interfaces :

# interface file auto-generated by buildroot
 
auto lo
iface lo inet loopback

Le client DHCP est également déjà présent puisqu'il s'agit de celui fourni par BusyBox [4], /sbin/udhcpc. Nous pouvons d'ailleurs nous assurer du bon fonctionnement de l'ensemble en l'invoquant manuellement :

# udhcpc -i eth0
udhcpc: started, v1.33.0
udhcpc: sending discover
udhcpc: sending discover
udhcpc: sending discover
udhcpc: sending select for 192.168.0.92
udhcpc: lease of 192.168.0.92 obtained, lease time 600
deleting routers
adding dns 81.253.149.13
adding dns 80.10.246.5
 
# ping connect.ed-diamond.com
PING connect.ed-diamond.com (185.169.94.231): 56 data bytes
64 bytes from 185.169.94.231: seq=0 ttl=45 time=22.206 ms
64 bytes from 185.169.94.231: seq=1 ttl=45 time=22.132 ms
64 bytes from 185.169.94.231: seq=2 ttl=45 time=22.487 ms
^C
--- connect.ed-diamond.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 22.132/22.275/22.487 ms

Pour modifier la configuration en place, nous pouvons utiliser la notion d'overlay de Buildroot. Cette approche nous permet de modifier le système de fichiers racine avant la création de l'image et un certain nombre de choses sont d'ores et déjà ajustées de cette façon. Un coup d’œil au répertoire buildroot-external-st/board/stmicroelectronics/stm32mp157/dk2-overlay nous montre, entre autres choses, une configuration ALSA via etc/asound.conf. Pour ajouter le support DHCP, il nous suffit donc de créer un fichier interfaces dans un sous-répertoire network/ de etc/ contenant :

auto lo eth0
 
iface lo inet loopback
 
iface eth0 inet dhcp

Nous pouvons également choisir de ne pas changer l'arborescence existante et dupliquer dk2-overlay sous un autre nom avant d'y faire notre ajout. Il suffira alors de faire la modification, via make menuconfig, « System configuration » puis « Root filesystem overlay directories » (BR2_ROOTFS_OVERLAY) pour adapter le chemin correspondant (relatif à BR2_EXTERNAL_ST_PATH). Ceci d'autant que, puisque nous sommes dans l'interface, nous pouvons également en profiter pour :

  • changer le nom d'hôte de « buildroot » en quelque chose de plus reconnaissable (« stm32mp1br » par exemple) : « System configuration », « System hostname » (BR2_TARGET_GENERIC_HOSTNAME) ;
  • activer le serveur SSH : « Target packages », « Networking applications », « dropbear » (BR2_PACKAGE_DROPBEAR) ;
  • activer Avahi pour la résolution mDNS : « Target packages », « Networking applications », « avahi » (BR2_PACKAGE_AVAHI et BR2_PACKAGE_AVAHI_DAEMON).

En quittant l'interface, sauvegardez les changements (dans .config), puis relancer la construction avec un simple make. De nouvelles archives sources seront automatiquement téléchargées, désarchivées, compilées et intégrées au système de fichiers racine pour produire une nouvelle image que vous pourrez immédiatement flasher, comme précédemment. Toutes les dépendances liées aux deux ajouts auront bien entendu été traitées dans le même temps.

En surveillant sur la console série ce premier redémarrage, on pourra constater le lancement du serveur SSH, ainsi que la résolution DHCP et l'exécution du démon avahi-daemon. La résolution fonctionnera d'ailleurs sans problème en utilisant le nom d'hôte défini depuis une autre machine du réseau :

$ ping stm32mp1br.local
PING stm32mp1br.local (192.168.0.92) 56(84) bytes of data.
64 bytes from 192.168.0.92 (192.168.0.92): icmp_seq=1 ttl=64 time=0.870 ms
64 bytes from 192.168.0.92 (192.168.0.92): icmp_seq=2 ttl=64 time=0.464 ms
64 bytes from 192.168.0.92 (192.168.0.92): icmp_seq=3 ttl=64 time=0.445 ms
^C
--- stm32mp1br.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2025ms
rtt min/avg/max/mdev = 0.445/0.593/0.870/0.196 ms

OpenSSH nous pause par contre un problème sensiblement différent :

$ ssh root@stm32mp1br.local
Host key fingerprint is SHA256:
Xwgn9vIHvhB7Fkrwb7YaOoqh0Bt7eezDZNowVxN6lo8
root@stm32mp1br.local's password:

En effet, non seulement notre root n'a pas de mot de passe, mais en plus, il n'est pas question de procéder à une connexion SSH sous cette identité. La configuration par défaut du serveur SSH (que ce soit Dropbear ou OpenSSH) ne le permet pas et c'est très bien ainsi. Pour nous connecter depuis une autre machine, nous devons disposer d'un autre utilisateur.

stm32MPdk 4usb-s

Le connecteur USB-C situé entre le hub USB et l'HDMI est un port USB OTG permettant au bootloader de fournir une interface DFU pour le flashage de la microSD. Ce port peut, bien entendu, être utilisé par Linux via l'API USB Gadget, pour émuler toutes sortes de périphériques USB.

2.2 Le root de tous les maux

N'avoir que l'utilisateur root et/ou l'utiliser pour des tâches courantes, même de développement et de mise au point, est une mauvaise idée. Mieux vaut disposer d'un utilisateur standard qui, le moment venu, obtiendra temporairement les privilèges adéquats pour des actions spécifiques. Nous devons donc intégrer la création de nouveaux utilisateurs dans notre configuration Buildroot. Pour cela, nous avons à notre disposition un mécanisme adapté passant par la création d'un fichier faisant office de users table.

Ce fichier sera référencé via l'interface accessible obtenue avec make menuconfig, sous « System configuration » puis « Path to the users tables » (BR2_ROOTFS_USERS_TABLES). Nous le placerons dans l'arborescence externe, en compagnie des overlays et le nommerons mkusers.table. Il sera donc désigné par $(BR2_EXTERNAL_ST_PATH)/board/stmicroelectronics/stm32mp157/mkusers.table dans la configuration.

Ce fichier respecte une syntaxe relativement simple, voici le nôtre (sur une ligne !) :

denis -1 denis -1 =coucou /home/denis /bin/sh
wheel,plugdev,dialout,sudo Utilisateur de base

Nous avons une liste de champs séparés par des espaces avec, de gauche à droite :

  • le nom d'utilisateur (denis) ;
  • son ID, avec -1 pour laisser Buildroot le déterminer à la construction ;
  • le nom du groupe de l'utilisateur (denis) ;
  • l'ID du groupe (GID) également calculé par Buildroot (-1) ;
  • le mot de passe en clair, précédé de =, ceci sera chiffré (crypt) avant d'être intégré au système. Notez qu'en faisant précéder le tout d'un !, nous pouvons interdire le login (pour un service fonctionnant sous une identité particulière, par exemple) ;
  • le répertoire personnel de l'utilisateur ($HOME) ;
  • le shell par défaut, ici /bin/sh (fourni par BusyBox) ;
  • une liste de groupes supplémentaires séparés par des virgules ;
  • et enfin, un commentaire qui sera intégré dans (/etc/passwd), ce champ étant le dernier, l'utilisation d'espaces est possible.

Nous avons ajouté ce nouvel utilisateur dans le groupe sudo, car la configuration par défaut de cette commande dans Buildroot ajoute automatiquement un /etc/sudoers incluant une ligne %sudo ALL=(ALL) ALL, signifiant que tous les utilisateurs du groupe sudo peuvent passer super-utilisateur pour n'importe quelle commande, en s'authentifiant. Ce réglage est fait par le script package/sudo/sudo.mk, et donc par Buildroot.

Tout ce que nous avons à faire est de nous plier d'un make menuconfig et d’activer « Target packages », « Shell and utilities » et « sudo » (BR2_PACKAGE_SUDO). Nous en profitons au passage pour faire de même, quelques lignes plus haut, avec le multiplexeur de terminal GNU Screen (BR2_PACKAGE_SCREEN) (ou Tmux (BR2_PACKAGE_TMUX) si vous préférez), qui peut également s'avérer très utile, que ce soit en SSH ou via la console série. Il peut être également intéressant d'activer « linux-pam » (BR2_PACKAGE_LINUX_PAM) pour Pluggable Authentication Modules permettant d'ajuster finement les paramètres d'authentification, de validation de compte et de session des utilisateurs.

À ce stade (peut-être après avoir préalablement testé les changements), nous pouvons terminer en faisant un tour dans « System configuration » puis désactiver « Enable root login with password » (BR2_TARGET_ENABLE_ROOT_LOGIN). Nous interdisons alors l'utilisation du compte root, ne laissant plus que sudo être là pour obtenir les privilèges en question :

Welcome to Buildroot
stm32mp1br login: root
Password:
Login incorrect
 
stm32mp1br login: denis
Password:
 
$ sudo -s
Password:
 
$ id
uid=0(root) gid=0(root) groups=0(root),10(wheel)

Ajout d'une clé SSH statique.

En phase de développement et de composition du système, il peut rapidement devenir pénible de devoir sans cesse supprimer et valider à nouveau la signature du serveur SSH du système embarqué. En effet, à chaque premier démarrage du système, Dropbear génère une nouvelle clé d'hôte qui sera forcément différente de la précédente.

Pour nous simplifier la vie, nous pouvons parfaitement récupérer cet élément (via scp) depuis /etc/dropbear/dropbear_ecdsa_host_key et l'intégrer dans notre overlay. Ainsi, la clé d'hôte sera déjà présente dans le système et l'empreinte correspondante sera donc toujours la même.

Ce faisant, vous remarquerez peut-être que les permissions sur le fichier seront sensiblement différentes, passant de 600 à 644, rendant le contenu du fichier lisible par tout le monde. Cela ne pose pas un problème particulier à Dropbear, mais n'est cependant pas correct, et nous donne l'occasion de voir comment ajuster ce genre de choses.

De la même manière que nous avons utilisé une table pour créer l'utilisateur supplémentaire, il en existe une pour fixer les permissions, c'est system/device_table.txt depuis la racine de notre Buildroot. Nous pouvons donc copier ce fichier au côté de mkusers.table (en le renommant éventuellement) et y ajouter une entrée pour notre fichier de clé. Là, nous ajoutons : /etc/dropbear/dropbear_ecdsa_host_key f 600 0 0 - - - - -, puis ajustons le chemin depuis l'interface menuconfig, « System configuration », « Path to the permission tables » (BR2_ROOTFS_DEVICE_TABLE), exactement comme nous l'avons fait pour mkusers.table.

2.3 Quelque chose à l'écran

Notez tout d'abord que ce qui va suivre n'est ni nécessaire, ni forcément souhaitable. L'objectif ici sera simplement de prendre en main le système de build en obtenant un résultat visuel amusant, tout en apprenant quelque chose. Raisonnablement, la configuration par défaut, n'affichant strictement rien à l'écran, si ce n'est en appelant les codes de démonstration Qt, est l'approche à adopter dans un projet destiné à présenter une IHM à l'utilisateur.

Cependant, comme nous aimons fouiller dans les coins et faire pleinement le tour du propriétaire, nous nous fixons comme objectif de présenter sur l'écran LCD une console telle que celle dont nous disposons sur la liaison série. Ceci va nécessiter une modification de la configuration du noyau Linux, sa recompilation et naturellement, la génération d'une nouvelle image.

L'ensemble nous est grandement simplifié par Buildroot, puisque les composants majeurs d'un système embarqué utilisent généralement le même système de configuration que Buildroot. Ainsi, nous connaissons déjà make menuconfig, mais avons également à notre disposition :

  • make busybox-menuconfig pour BusyBox ;
  • make uclibc-menuconfig pour la bibliothèque C standard uClibc ;
  • make uboot-menuconfig pour le bootloader U-Boot ;
  • make barebox-menuconfig pour Barebox, un bootloader alternatif à U-Boot ;
  • et make linux-menuconfig pour le noyau Linux.

Ces différentes interfaces de configuration peuvent être utilisées, sous réserve que l'élément concerné soit activé dans la configuration principale, pour procéder à une configuration de ces composants. Il ne s'agit cependant pas d'une intégration complète à Buildroot, mais d'interfaces distinctes. Ainsi, make linux-menuconfig nous renvoie sur l'interface classique de configuration du noyau Linux, où nous pouvons ajuster des éléments sur la base d'un fichier de configuration préalablement chargé et ceci fait, nous enregistrons ces changements dans le même fichier ou sous un autre nom. Le fichier de configuration en question est ensuite spécifié dans la configuration Buildroot, exactement comme nous l'avons fait pour les tables des utilisateurs ou des permissions.

L'arborescence externe proposée par Bootlin intègre une configuration Linux stockée dans board/stmicroelectronics/stm32mp157/linux.config. Nous devons donc charger ce fichier via « < Load > » (au bas de l'écran) pour ensuite modifier cette configuration. Mais avant de faire cela, nous avons besoin de parler de la gestion de l'affichage avec Linux.

stm32MPdk ajout-s

L'espace entre l'écran LCD et la carte étant relativement réduit, l'accès aux GPIO n'est pas aisé en phase de développement. Il peut donc être intéressant de rehausser l'ensemble à l'aide d'entretoises pour gagner en souplesse.

Les plus anciens utilisateurs du système se souviennent très certainement du périphérique framebuffer, alias fbdev, généralement accessible via un pseudofichier /dev/fb0. La notion de framebuffer est relativement simple, il s'agit d'une zone mémoire (buffer) représentant littéralement la valeur de chaque pixel à l'écran. Ceci explique pourquoi un cat /dev/urandom > /dev/fb0 permet de remplir l'écran de pixels aux couleurs aléatoires, et il était donc très facile de dessiner à l'écran de cette manière en considérant le buffer comme un canevas. Facile, certes, mais totalement inefficace, car fbdev était très limité (buffer de taille statique préallouée, pas de pipeline, problème de synchronisation, etc.) et obligeait donc des serveurs d'affichage comme Xorg à accéder directement aux registres matériels pour reconfigurer l'affichage. Un autre système a donc été créé pour corriger ces imperfections et permettre au noyau de prendre en charge totalement l'infrastructure d'affichage incluant la gestion du framebuffer, mais également la configuration du matériel et de l'affichage.

Pour comprendre ce système, il est tout d'abord important de faire la distinction entre un contrôleur d'affichage (display controller) et un GPU (Graphics Processing Unit). Le travail du contrôleur d'affichage se limite à une tâche et une seule : copier le contenu d'un ou plusieurs framebuffers à l'écran, qu'il soit interfacé en HDMI, DisplayPort ou MIPI DSI. Il peut utiliser et combiner plusieurs framebuffers pour composer cet affichage, mais ne fait absolument aucun travail de rendering 3D ou autre opération impliquant des choses comme OpenGL.

Le GPU, en revanche, est chargé du rendering. C'est un processeur graphique programmable, généralement avec OpenGL, dont le travail est de décharger le processeur généraliste (CPU) des opérations graphiques. Mais le GPU, par lui-même, n'affiche rien et, au contraire, s'en remet au contrôleur d'affichage pour cela. Voilà pourquoi, il est parfaitement possible d'utiliser OpenGL et des shaders (microprogramme fonctionnant purement sur le GPU) sans afficher quoi que ce soit à l'écran (voir [5] pour un exemple). Dans le monde de l'embarqué, certains SoC disposent d'un GPU et d'autres non, mais si un écran ou une sortie vidéo est disponible, il y a toujours un contrôleur d'affichage. Le STM32MP157 dispose des deux.

Avec le noyau Linux moderne, ces deux éléments sont à présent contrôlés par un sous-système appelé DRM pour Direct Rendering Manager, parfois également appelé DRM KMS (pour Kernel Mode-Setting), avec KMS étant un sous-élément de l'API DRM, chargé de la configuration du mode (résolution, nombre de couleurs, vitesse de rafraîchissement) d'affichage. KMS règle précisément le problème évoqué plus haut, évitant un accès privilégié au matériel depuis l'espace utilisateur (pilotes X). D'autre part, l'architecture DRM permet un niveau abstraction supplémentaire puisque n'importe quel programme de l'espace utilisateur faisant usage de l'API DRM fonctionnera forcément avec n'importe quel pilote DRM, quel que soit le matériel contrôlé.

stm32MPdk dessous-s

Le dessous du devkit propose des « connecteurs Arduino » permettant d'utiliser des shields compatibles, qu'il s'agisse des « expansion boards Nucleo » du constructeur, ou d'autres modèles.

Aujourd'hui, l'ancien fbdev est considéré comme totalement obsolète et n'est presque plus utilisé, au bénéfice de DRM KMS. Le seul élément du noyau en faisant encore usage est... fbcon, la console permettant un affichage des messages de démarrage et la gestion d'un terminal associé (TTY). Fort heureusement, DRM met à disposition une émulation fbdev et donc une solution pour disposer d'un /dev/fb0, mais aussi, et surtout, d’une console. Précisément ce que nous désirons obtenir.

Nous disposons déjà d'un support DRM pour le contrôleur d'affichage et le GPU (Vivante) intégré au SoC dans la configuration par défaut du noyau, comme le prouve la parfaite exécution des démonstrations QT (alors même que rien ne s'affiche au démarrage du devkit). Vous pouvez d'ailleurs très simplement tester cette fonctionnalité (via la console série ou l'accès SSH) en utilisant la commande modetest. sudo modetest -M stm permettra d'afficher des informations (connecteurs, encodeurs, modes disponibles, etc.) et de procéder à un test avec sudo modetest -M stm -s suivi d'un 33:1280×720 pour la sortie DVI, ou d'un 35:480×800 pour l'écran LCD (à ajuster en fonction de la sortie de sudo modetest -M stm -c).

Ce qui nous manque, en premier lieu, c'est une console pour afficher les messages du noyau et éventuellement nous fournir une interface textuelle pour le login. En d'autres termes, il nous manque fbcon. Tout ce que nous avons donc à faire est d'activer quelques fonctionnalités.

Commençons par l'émulation fbdev en passant par :

  • « Device Drivers » ;
  • « Graphics support » ;
  • « Direct Rendering Manager » ;
  • « Enable legacy fbdev support for your modesetting driver » (CONFIG_DRM_FBDEV_EMULATION).

Comme l'émulation est active, nous pouvons avoir accès à l'activation de fbcon :

  • « Device Drivers » ;
  • « Graphics support » ;
  • « Console display driver support » ;
  • « Framebuffer Console support » (CONFIG_FRAMEBUFFER_CONSOLE).

Et puisqu'un simple défilement de texte sur un écran serait bien triste sans pingouins (et pas assez nostalgique), nous pouvons également activer l'affichage du logo :

  • « Device Drivers » ;
  • « Graphics support » ;
  • « Bootup logo » (CONFIG_LOGO_LINUX_MONO, CONFIG_LOGO_LINUX_VGA16 et CONFIG_LOGO_LINUX_CLUT224).

C'est tout. Nous enregistrons les modifications avant de quitter l'interface de configuration et nous stockons cette configuration, au même emplacement que linux.config, mais en nommant le fichier linux-fbcon.config. Il nous suffit ensuite d'un petit make menuconfig pour ajuster cet élément via « Kernel » et « Configuration file path » (BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE).

Il faudra ensuite nettoyer ce composant du système et reconstruire le noyau avec :

$ make linux-dirclean
$ make linux-rebuild

Mais avant de reconstruire l'image et de la flasher sur la plateforme, nous avons un dernier point à configurer. Dans l'état, le noyau configurera effectivement fbcon et nous verrons les messages de boot s'afficher en compagnie des deux petits pingouins, mais nous n'avons pas pour autant de terminal sur cette sortie. Pour cela, nous devons nous pencher sur la configuration de getty, fourni par BusyBox, ainsi que sur le système d'init, également pris en charge par cet outil.

Fort heureusement, la configuration par défaut proposée par Bootlin n'utilise pas systemd (contrairement au build Yocto/OpenSTLinux) et tout cela reste donc extrêmement simple, efficace et cohérent. Comme l'option « Run a getty (login prompt) after boot » (BR2_TARGET_GENERIC_GETTY) est activée, ceci signifie qu'un squelette de base (package/busybox/inittab) est utilisé, puis modifié par le système de build pour décommenter la ligne concernant la console série. Celle-ci est ajustée en fonction des paramètres choisis dans l'interface (port, vitesse, type de terminal).

Nous ne pouvons donc pas simplement et brutalement remplacer inittab avec l'overlay, mais Buildroot propose d'autres solutions. Dans board/stmicroelectronics/stm32mp157/ vous trouverez, par exemple, le fichier post-image.sh, un script permettant de générer le fichier genimage.cfg utilisé pour produire l'image finale. Ce script est exécuté à un moment précis du build, juste après que Buildroot ait composé le système de fichiers racine. Vous retrouvez cette configuration dans « System configuration » et « Custom scripts to run after creating filesystem images » (BR2_ROOTFS_POST_IMAGE_SCRIPT).

Ce hook n'est pas le seul utilisable, il en existe un autre, désigné par « Custom scripts to run before creating filesystem images » (BR2_ROOTFS_POST_BUILD_SCRIPT) exécutable juste après le build et avant la composition de l'image du système de fichiers. C'est précisément ce qu'il nous faut pour exécuter un script, que nous appelons post-build.sh, pour modifier à la voler le contenu du fichier inittab :

#!/bin/sh
 
set -u
set -e
 
# Add a console on tty1
if [ -e ${TARGET_DIR}/etc/inittab ]; then
    grep -qE '^tty1::' ${TARGET_DIR}/etc/inittab || \
        sed -i '/GENERIC_SERIAL/a\
tty1::respawn:/sbin/getty -L tty1 0 vt100 # LCD console' ${TARGET_DIR}/etc/inittab
fi

Ce script n'est pas de moi, il est utilisé dans plusieurs entrées dans buildroot/board/, raspberrypi/ ou n'importe quelle autre carte proposant une sortie HDMI. Il se contente de trouver la ligne concernant la console série (via la chaîne GENERIC_SERIAL en commentaire) et d'en ajouter une, juste après, pour lancer un nouveau getty.

Nous plaçons ce script à côté de post-image.sh et pouvons alors lancer un nouveau build, puis flasher la carte. Celle-ci va alors présenter les messages de démarrage et se terminer sur :

Welcome to Buildroot
stm32mp1br login:      

Si un clavier USB est branché, nous pouvons alors entrer notre login et son mot de passe pour obtenir un shell. À ce stade, si vous êtes satisfait du résultat, vous pouvez choisir d'enregistrer votre configuration Buildroot courante (présentement dans .config), sous un nom plus convenable et à un emplacement adapté, comme buildroot-external-st/configs/. Vous pouvez également utiliser make savedefconfig qui produira un fichier plus concis en supprimant les redondances et les lignes indiquant les éléments non activés, mais mettra à jour le fichier initialement utilisé (st_stm32mp157c_dk2_demo_defconfig). Vous pouvez cependant forcer l'emplacement et le nom du fichier en ajoutant BR2_DEFCONFIG= à la commande, suivie du chemin complet. Une commande équivalente existe pour la configuration du noyau Linux (make linux-savedefconfig ou make linux-update-defconfig BR2_LINUX_KERNEL_CUSTOM_CONFIG_FILE= suivi du chemin vers le fichier), pour U-Boot (make uboot-savedefconfig ou uboot-update-defconfig) et Barebox (make barebox-savedefconfig).

Attention cependant, si vous comptez partager cette configuration, à ne pas oublier d'ajuster l'ensemble en conséquence en supprimant les éléments qui ne doivent pas être statiques, je pense en particulier à la clé d'hôte de Dropbear.

Enfin, je le précise une nouvelle fois, ajouter la compatibilité fbdev et donc fbcon est amusant d'un point de vue pédagogique, puisque cela nous a permis de voir comment modifier la configuration du noyau et d'autres éléments de Buildroot, mais ce n'est pas une bonne idée pour un projet. Si l'on veut proposer une IHM, mieux vaut ne pas s'en occuper et se concentrer sur la pile graphique standard en utilisant la LibDRM par exemple ou utiliser un toolkit comme GTK+, QT ou les EFL, éventuellement en passant par Weston (l'implémentation de référence de Wayland).

stm32MPdk reset-s

Sur la tranche de la carte, un jeu de LED et de boutons est à la disposition de l'utilisateur. Leurs connexions ainsi que leur configuration par défaut sont décrites dans le premier document que vous devrez lire en recevant le matériel : l’user manual UM2534 [7].

Conclusion

Je ne vous cacherai pas une certaine préférence personnelle pour Buildroot, que je trouve bien plus « cohérent » qu'OpenEmbbeded d'un point de vue de l'architecture. Certes, cette approche monolithique est plus rigide que ce qu'il est possible d'obtenir avec un mécanisme de layers, mais en contre-partie, la composition et la configuration « locale » d'un système s'en trouvent grandement simplifiée. Bien entendu, la comparaison est totalement dépendante du support effectivement offert par les constructeurs, car même si l'approche consistant à simplement produire un layer fournissant un BSP pour une carte est fort séduisante, en pratique, la quantité de layers non maintenus est relativement importante [6]. Inversement, Buildroot incite les constructeurs à proposer un support s'intégrant pleinement au système de build, les options comme BR2_EXTERNAL n'étant souvent que les prémices à une intégration upstream. Au final, Buildroot est plus « maîtrisable » de la part de l'utilisateur final et l'adaptation à un projet spécifique s'en trouve d'autant simplifiée. Bien entendu, ceci est également une question d'expérience, mais la courbe d'apprentissage est clairement plus clémente dans le cas de Buildroot.

En ce qui concerne les manipulations que nous venons de voir, un certain nombre d'améliorations restent à faire. Le fait que ce soit les anciennes versions du devkit (STM32MP157A-DK1 et STM32MP157C-DK2), maintenant considérées comme obsolètes, car remplacées par les STM32MP157D-DK1 et STM32MP157F-DK2, ne pose pas de grands problèmes, même si cela mériterait d'être complété. Vous avez sans doute remarqué un message du noyau au démarrage vous faisant remarquer que « OPP table can't be empty ». Ceci provient d'éléments manquants dans le devicetree, en particulier concernant les horloges du SoC et le support OPP gérant des paires fréquence/tension supportées par le SoC. Il est possible de régler ce problème en générant un nouveau source du devicetree avec STM32CubeMX et en l'adaptant (voir le fichier doc/stm32cubemx.md de buildroot-external-st pour plus d'informations).

Un autre élément à régler est le fait que le devkit, démarré avec le système de démonstration, perturbe l'hôte auquel il est connecté en USB, remplissant par la même occasion les logs de messages comme :

new high-speed USB device number 19 using ehci-pci
device descriptor read/64, error -110
evice descriptor read/64, error -110
attempt power cycle
new high-speed USB device number 20 using ehci-pci
unable to enumerate USB device

Le port USB-C par lequel nous flashons la microSD est, en effet, USB OTG, et donc en mesure de se comporter comme un périphérique. Mais rien n'est configuré par défaut et l'hôte ne cesse de tenter d'énumérer ce qu'il pense être un périphérique. L'une des conséquences est une perturbation complète du sous-système USB de l'hôte qui devient incapable, par exemple, d'utiliser correctement une YubiKey 5 (ce qui est relativement pénible lorsqu'on tente de s'authentifier sur GitHub pour proposer un patch justement). Pour régler ce problème, la solution simple est de débrancher le câble USB, mais il serait bien plus intéressant de configurer l'USB OTG via l'API USB Gadget au démarrage (pour fournir un port série ou une interface Ethernet).

Il est fort probable que nous parlions encore à l'avenir de cette sympathique plateforme, peut-être pour traiter de ces points, peut-être pour nous pencher sur le Cortex-M4 intégré au SoC, ou tout simplement pour faire plus ample connaissance avec Buildroot...

Références

[1] https://connect.ed-diamond.com/hackable/hk-041/stm32mp1-le-soc-qui-etend-l-ecosysteme-stm32-vers-linux-embarque

[2] https://github.com/bootlin/buildroot-external-st/blob/st/2021.02/docs/internals.md

[3] https://wiki.st.com/stm32mpu/wiki/TF-A_overview#FIP

[4] https://busybox.net/

[5] https://github.com/matusnovak/rpi-opengl-without-x

[6] https://layers.openembedded.org/layerindex/branch/master/layers/

[7] https://www.st.com/resource/en/user_manual/dm00591354-discovery-kits-with-stm32mp157-mpus-stmicroelectronics.pdf



Article rédigé par

Par le(s) même(s) auteur(s)

ESP32 : créer ses composants réutilisables avec ESP-IDF

Magazine
Marque
Hackable
Numéro
53
Mois de parution
mars 2024
Spécialité(s)
Résumé

Vous connaissez certainement l'habituelle routine de développement. On part d'une page vierge et l’on expérimente jusqu'à obtenir un résultat fonctionnel, puis on affine, on nettoie, on sépare le code de test des routines « utilitaires » et l’on fait en sorte d'avoir, dans un ou plusieurs fichiers sources, quelque chose qu'on pourra réutiliser avec un autre projet. Mais, avec l'ESP-IDF, l'environnement de développement pour les ESP32, il est possible de pousser cela plus loin, et de façon plus rationnelle...

Édito : Quelque chose m'inquiète (peut-être)...

Magazine
Marque
GNU/Linux Magazine
Numéro
268
Mois de parution
mars 2024
Résumé

Une publication sœur (MISC 131) a dernièrement publié un article fort intéressant concernant NYSM et mettant en lumière le danger que présentent des outils de post-exploitation basés sur eBPF, permettant relativement facilement à l'attaquant de dissimuler sa présence ainsi que celle des outils qu'il aura laissés derrière lui.

Édito

Magazine
Marque
Hackable
Numéro
53
Mois de parution
mars 2024
Résumé

Chers consœurs et confrères journalistes de la presse « technique » et « scientifique », si je vous écris ce mot aujourd'hui, c'est pour vous donner un petit conseil qui pourra peut-être à la fois vous éviter de passer pour des demeurés, mais aussi, accessoirement, de prendre vos lecteurs pour des demeurés d'un calibre similaire, sinon bien supérieur.

Les derniers articles Premiums

Les derniers articles Premium

Les nouvelles menaces liées à l’intelligence artificielle

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Sommes-nous proches de la singularité technologique ? Peu probable. Même si l’intelligence artificielle a fait un bond ces dernières années (elle est étudiée depuis des dizaines d’années), nous sommes loin d’en perdre le contrôle. Et pourtant, une partie de l’utilisation de l’intelligence artificielle échappe aux analystes. Eh oui ! Comme tout système, elle est utilisée par des acteurs malveillants essayant d’en tirer profit pécuniairement. Cet article met en exergue quelques-unes des applications de l’intelligence artificielle par des acteurs malveillants et décrit succinctement comment parer à leurs attaques.

Migration d’une collection Ansible à l’aide de fqcn_migration

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Distribuer du contenu Ansible réutilisable (rôle, playbooks) par l’intermédiaire d’une collection est devenu le standard dans l’écosystème de l’outil d’automatisation. Pour éviter tout conflit de noms, ces collections sont caractérisées par un nom unique, formé d’une espace de nom, qui peut-être employé par plusieurs collections (tel qu'ansible ou community) et d’un nom plus spécifique à la fonction de la collection en elle-même. Cependant, il arrive parfois qu’il faille migrer une collection d’un espace de noms à un autre, par exemple une collection personnelle ou communautaire qui passe à un espace de noms plus connus ou certifiés. De même, le nom même de la collection peut être amené à changer, si elle dépasse son périmètre d’origine ou que le produit qu’elle concerne est lui-même renommé.

Mise en place d'Overleaf Community pour l’écriture collaborative au sein de votre équipe

Magazine
Marque
Contenu Premium
Spécialité(s)
Résumé

Si vous utilisez LaTeX pour vos documents, vous connaissez vraisemblablement Overleaf qui vous permet de rédiger de manière collaborative depuis n’importe quel poste informatique connecté à Internet. Cependant, la version gratuite en ligne souffre de quelques limitations et le stockage de vos projets est externalisé chez l’éditeur du logiciel. Si vous désirez maîtriser vos données et avoir une installation locale de ce bel outil, cet article est fait pour vous.

Les listes de lecture

7 article(s) - ajoutée le 01/07/2020
La SDR permet désormais de toucher du doigt un domaine qui était jusqu'alors inaccessible : la réception et l'interprétation de signaux venus de l'espace. Découvrez ici différentes techniques utilisables, de la plus simple à la plus avancée...
8 article(s) - ajoutée le 01/07/2020
Au-delà de l'aspect nostalgique, le rétrocomputing est l'opportunité unique de renouer avec les concepts de base dans leur plus simple expression. Vous trouverez ici quelques-unes des technologies qui ont fait de l'informatique ce qu'elle est aujourd'hui.
9 article(s) - ajoutée le 01/07/2020
S'initier à la SDR est une activité financièrement très accessible, mais devant l'offre matérielle il est parfois difficile de faire ses premiers pas. Découvrez ici les options à votre disposition et les bases pour aborder cette thématique sereinement.
Voir les 31 listes de lecture

Abonnez-vous maintenant

et profitez de tous les contenus en illimité

Je découvre les offres

Déjà abonné ? Connectez-vous