Linux Embedded

Le blog des technologies libres et embarquées

Tutorial : un système Linux embarqué opérationnel avec Buildroot

Dans le domaine de l'embarqué, nous nous retrouvons souvent en situation où nous devons reconstruire un système complet à partir des sources, pour une architecture cible souvent différente de notre architecture hôte. Que l'on soit débutant ou développeur confirmé, la (cross-)compilation et l'organisation d'un système embarqué sont des étapes longues et fastidieuses, surtout lorsque les éléments du système à compiler nécessitent des adaptations. Il existe heureusement des outils libres qui simplifient et accélèrent cette tâche, en proposant généralement des fonctionnalités complémentaires intéressantes.
Cet article est consacré à l'un de ces outils libres pour systèmes Linux embarqué : Buildroot.

Nous avions déjà croisé Buildroot dans un article de Tristan Lelong concernant l'ajout de paquet à Buildroot. Cette fois-ci, nous proposons une approche didactique du logiciel avec un cas d'utilisation simple : nous détaillons en effet les étapes permettant de construire son premier système Linux embarqué grâce à Buildroot. Pour cela, notre cible sera la carte FriendlyARM mini2440 déjà utilisée pour le concours Linuxembedded 2011 et 2012.

Buildroot est un logiciel libre, sous license GPL, composé d'un ensemble de Makefile et de patches. Cette conception fait de Buildroot un outil abordable, et il est relativement simple de participer et de suivre son développement en s'incrivant sur la mailing-list par exemple. On peut obtenir de l'aide également sur IRC (Freenode, #buildroot).

Choix de la version

Pour ce tutorial, privilégions une version stable et figée...En effet, le développement de Buildroot est actif et une nouvelle version est mise en ligne tous les 3 mois, avec son lot d'évolutions.

A la date d'écriture de ces lignes, nous prendrons la version 2012.11.1 qui, comme son nom l'indique, est sortie en Novembre 2012 (suivie d'un bugfix en janvier 2013). La prochaine version sera donc la 2013.02.

L'archive est disponible ici.

Documentation

Après avoir extrait l'archive, la première chose à conseiller est de consulter la documentation. Une version en texte est disponible dans "docs/manual/". Vous pouvez la compiler dans un autre format (html, split-html, pdf, txt et epub) pour plus de lisibilité.

Pour du HTML par exemple :

$ make manual-html

La documentation générée se trouve dans "output/docs/manual/manual.html". Celle-ci explique la structure et les fonctionnements principaux de Buildroot. Elle est pour l'instant disponible en Anglais uniquement. Je ne peux que vous conseiller de vous orienter vers la documentation générée qui est à jour, et répond à bon nombre de questions (notamment concernant le chapitre des licences).

Principe de fonctionnement

Buildroot est techniquement un ensemble de Makefiles définissant, en fonction des options paramétrées par l'utilisateur, la manière de compiler chaque paquet sélectionné avec des options particulières. Il construit au final une distribution complète et cohérente dont chaque composant a été compilé.

Buildroot possède un outil confortable de configuration, basé et très similaire à celui du noyau Linux : Kconfig, que l'on retrouve également avec Busybox, uClibc et qui peut être utilisé dans tout projet, comme présenté par Vincent Bassi dans un article précédent.

La configuration est donc accessible avec :

make menuconfig

Le gestionnaire de configuration est basé sur ncurses. Il existe d'autres versions du gestionnaire utilisant des bibliothèques de rendu différentes :

make xconfig (qt)

make gconfig (gtk)

[caption id="attachment_1275" align="aligncenter" width="610" caption="Buildroot make menuconfig"][/caption]

A la fermeture du menuconfig, la configuration est sauvegardée dans un fichier (.config a la racine de buildroot).

Buildroot intègre un système de "templates" prédéfinis de configuration, ce qui permet de configurer des options par défaut pour une cible particulière. Bonne nouvelle : le support de la mini2440 est disponible !

Ces templates sont sauvegardés dans buildroot dans le dossier configs/*_defconfig et sont listés par :

$ make help

[...]

--> mini2440_defconfig                  - Build for mini2440

[...]

Nous allons donc configurer buildroot pour utiliser ce template de base :

$ make mini2440_defconfig

Nous pouvons maintenant refaire un make menuconfig pour retrouver les options configurées par défaut.

Compilation et configuration des «briques» principales

Buildroot va nous permettre de compiler toutes les briques de base nécessaires pour utiliser notre carte. Cela concerne quatre composants principaux :

  1. La toolchain
  2. Le bootloader
  3. Le noyau Linux
  4. Le rootfs

Petit conseil : avant de poursuivre, nous allons créer un dossier friendly-arm/ dans board/ puis un dossier mini2440. C'est une bonne habitude à prendre pour clarifier la configuration d'une cible avec Buildroot.

mkdir -p board/friendly-arm/mini2440/

A l'avenir, ce dossier va permettre de centraliser des configurations, des patchs ou même la structure du système de fichier final pour notre cible.

La toolchain

La toolchain désigne l'ensemble des outils à compiler qui nous permettra ensuite d'avoir un environnement capable de cross compiler depuis notre architecture host ( dans notre cas de l'x86_64)  vers l'architecture cible (ARM).

La toolchain regroupe un certain nombre de composants obligatoires comme :

  • un compilateur
  • un linker
  • un assembleur

Buildroot propose plusieurs mécanismes pour utiliser une toolchain. Le plus direct est d'utiliser la toolchain interne, et dans ce cas c'est Buildroot qui gèrera la création et l'utilisation de la toolchain.

Il est possible aussi d'utiliser un backend vers crosstool-ng qui est un outil spécifique pour faire des toolchains, disposant en particulier d'un nombre d'options de configuration plus important. En contrepartie certaines options ne sont pas directement prises en compte par buildroot, et il peut être nécessaire d’accéder à la configuration de crosstool-ng via la commande :

make ctng-menuconfig

Notons qu'il est aussi possible d'utiliser une toolchain externe et donc déjà compilée, mais il n'y a du coup pas de possibilité pour buildroot de vérifier que les options de compilation sont cohérentes avec les composants qui seront ensuite sélectionnés.

Nous choisissons ici le backend crosstool-ng par default, avec la libc uClibc et en ajoutant le support des WCHAR (Utile pour la suite).

Toolchain -> Toolchain Type (Crosstool-ng)

Toolchain -> Croostool-ng C library (uClibc)

Toolchain -> [*] Enable WCHAR support

Note: J'ai rencontré un problème de compilation de crosstool-ng concernant les «Companion libraries». Pour résoudre le problème, il faut faire un tour dans la configuration de ct-ng (make ctng-menuconfig) et mettre en version supérieure les «Companion libraries».

Le bootloader

Pour le bootloader, nous choisirons u-boot (Qui supporte la mini2440). Il faut noter que Buildroot propose le support d'autres bootloader tels que Barebox, syslinux, ou grub (sur architecture x86 uniquement).

Les options par défaut du template sont correctes pour u-boot. Le dépôt récupéré n'est pas le dépôt officiel, mais celui d'un fork réalisé par buserror qui a repris un fork de la communauté openmoko (Modèle de Processeurs très proche: s3c2410 et s3c2440). (Accès gitweb)

Note:
Une chose à retenir concernant l'utilisation de Buildroot est qu'il faut toujours être attentif à l'ajout de composants sur notre cible. En effet, Buildroot n'est pas un outil de vérification de compatibilité entre un composant et une plateforme hardware, c'est au développeur de s'assurer du bon fonctionnement d'un composant sur sa carte. Dans le cas présent, si on choisit u-boot comme bootloader et que le support n'est pas prévu pour la mini2440, celui-ci ne fonctionnera pas.

Le noyau

Comme évoqué précédemment, l'interface de configuration de Buildroot est basée sur celle du noyau. L'idéal pour le configurer serait d’accéder à son propre menu de configuration avec l'environnement de notre carte (ARCH=arm). Buildroot permet cela, via la règle :

$ make linux-menuconfig

Note: Il faut savoir que cette opération télécharge, extrait (dans le répertoire output/build/linux-<version>), et pre-configure le kernel avec les options paramétrées dans Buildroot pour la configuration du noyau. La règle make ci-dessus rentre dans le répertoire et lance un `make menuconfig` avec les options d'environnement pour votre cible.

Nous allons ajouter une option indispensable dans le noyau pour la suite de notre article : le support de devtmpfs (C'est le noyau qui va se charger de créer les noeuds dans /dev)

$ make linux-menuconfig

Device Drivers -->

Generic Driver Options -->

[*] Maintain a devtmpfs filesystem to mount at /dev

Une fois le noyau configuré, nous pouvons sauvegarder la configuration. Le mécanisme des "defconfig" du noyau est aussi supporté :

make linux-savedefconfig

La configuration est sauvegardée dans le dossier output/build/linux-<version>/defconfig. Nous allons le récupérer et le copier dans notre dossier de configuration :

cp output/build/linux-<version>/defconfig board/friendly-arm/mini2440/linux.defconfig

Il faut maintenant indiquer à Buildroot le fichier de configuration qui sera utilisé à la compilation du Kernel.

Kernel ->

Kernel Configuration (Select : Using a custom config file)

configuration File Path -> "board/friendly-arm/mini2440/linux.defconfig"

Le rootfs

Ce composant constitue le système complet installé sur la carte qui sera utilisé après le démarrage du bootloader puis de Linux. Sa configuration s'effectue principalement dans le choix des paquets à installer sur la cible, dans le menu :

Package Selection for the target

Certaines configurations de buildroot affectent aussi le rootfs, comme par exemple les options que l'on retrouve dans le menu :

System Configuration

Ce menu nous permet de configurer par exemple l'invite de login qui sera envoyé pour ajouter la console série (Ce qui est fait par le template de la mini2440 de buildroot). Il dispose aussi d'une option très importante, celle de la gestion des nœuds de périphériques dans /dev (Menu "/dev/ management"). Plusieurs options sont disponibles :

  • "static using device table" : Dans ce cas, un fichier spécifie chaque noeud a créer.
  • devtmpfs only : Laisse le kernel créer les noeuds avec devtmpfs
  • dynamic using mdev : Utilise mdev de busybox pour gérer les noeuds.
  • dynamic using udev : Utilise udev

Nous choisirons dans notre cas "devtmpfs only".

Nous pouvons choisir quelques paquets de test à installer sur notre cible pour la suite de cet article :

  • Busybox (Sélectionné par défaut)
  • Enlightenment Foundation Libraries (Menu "Graphic libraries and applications")

Coffee time

Maintenant tout est prêt pour lancer la compilation du système ! Pour cela rien de plus simple, c'est la règle exécutée par défaut du Makefile de Buildroot.

$ make

Vous avez le temps de prendre un café :)

Si tout ce passe sans erreur (et que vous n'avez pas renversé votre café...), les images finales sont dans le dossier output/images/.

  • uboot.bin : L'image d'uboot.
  • uImage : l'image du noyau Linux
  • rootfs.tar : La tarball du système de fichier complet.

Installation sur la friendly-arm

Nous allons utiliser notre carte avec le rootfs en NFS et le noyau à récupérer via tftp sur notre machine de développement.

A partir de ce point, nous considérons que vous avez déjà un serveur TFTP fonctionnel et un serveur NFS avec les bonnes options (Ne pas oublier le "no_root_squash" !). De même, il est nécessaire que votre mini2440 possède encore en NOR l'utilitaire supervivi.

Installation et configuration de uboot

Nous allons commencer par installer l'image u-boot.bin sur la mémoire Nand de la mini2440 à l'aide de l'utilitaire usb-push de friendlyarm disponible ici.

(Attention : vous avez besoin de la `libusb-dev` pour compiler usb-push)

Connectez le port RS232 de la carte sur votre ordinateur, ainsi que l'usb hôte. Ouvrez un minicom (ou autre) sur la liaison série en 115200 bps 8N1.

La carte peut maintenant être démarrée avec le switch sur la position NOR. Sur votre console de port série, l'utilitaire supervivi se lance et affiche un menu. Nous allons choisir le menu "Download vivi" qui signifie en fait de copier l'image que nous allons envoyer par usb vers la première partition de la Nand.

sudo ./usbpush </buildroot/path/>output/images/u-boot.bin

Une fois la copie effectuée, éteindre la carte, changer le switch en position NAND, puis rebooter. Si tout se passe bien, u-boot doit se lancer et nous allons maintenant pouvoir le configurer.

Cette commande permet d'afficher le partitionnement de la mémoire NAND :

# mdtparts

Le résultat doit ressembler à celui-ci :

device nand0 <mini2440-nand>, # parts = 4

#: name                        size            offset          mask_flags

0: u-boot              0x00040000      0x00000000      0

1: env                 0x00020000      0x00040000      0

2: kernel              0x00500000      0x00060000      0

3: root                0x07aa0000      0x00560000      0

active partition: nand0,0 - (u-boot) 0x00040000 @ 0x00000000

Avant de continuer, nous allons préparer la partition NAND de la mini2440. Pour cela, depuis la console de la liaison série, effectuons la commande suivante :

MINI2440 # nand createbbt

Create BBT and erase everything ? <y/N> y

Évidemment, vous pouvez prévoir le problème que l'on aura au prochain démarrage de la carte : Nous avons copié uboot sur la 1ère partition NAND et nous avons tout écrasé pour créer la BBT. Il faut donc refaire l'étape précédente de l'usbpush (sans oublier de remettre le switch sur la position NOR)

Vous avez maintenant une NAND fonctionnelle et nous pouvons reprendre la configuration de uboot.

La commande suivante nous permet de spécifier à u-boot que son espace de sauvegarde de configuration se trouve sur la nand (le "env" est la deuxième partition listée par mtdparts) :

# dynenv set env

Spécifier à u-boot l'adresse ip de notre carte :

# setenv ipaddr <ip_carte>

Spécifier l'adresse ip du serveur :

# setenv serverip <ip_de_hôte>

Spécifier le répertoire du root nfs. C'est dans ce répertoire (de l'hôte) que nous allons extraire la tarball du rootfs et celui-ci sera partagé en NFS.

# setenv root_nfs <path/nfs>

Spécifier le nom de l'image noyau à récupérer.

# setenv bootfile uImage

Spécifier l'adresse en ram de stockage du noyau.

# setenv fileaddr 32000000
# setenv loadaddr 0x32000000

Le système de variable de u-boot est minimaliste mais nous permet de définir des alias pour former des commandes complexes à partir des variables définies ci-dessus. Nous allons configurer la ligne de boot pour utiliser le boot par NFS. Pour cela, la commande `set_bootarg_nfs` est déjà prête :

# run set_bootargs_nfs

# printenv bootargs

# saveenv

Cette dernière commande va sauvegarder l'environnement uboot actuel dans la partition Nand que nous avons préalablement précisée ("env"). Ainsi, au redémarrage de la carte, "printenv" devrait retourner l'environnement que l'on vient de sauvegarder.

Noyau et rootfs

Nous allons récupérer le noyau (uImage dans le répertoire output/ de buildroot) via le protocole TFTP. Une commande Uboot va automatiquement récupérer les informations que nous lui avons passé précédemment (serverip, bootfile et loadaddr) :

# tftp

Pour le rootfs, il suffit de récupérer la tarball (rootfs.tar) générée par Buildroot puis de l'extraire dans le répertoire de votre choix que vous avez partagé en NFS.

Note : n'oubliez pas d'extraire la tarball en root, car il est nécessaire de garder les bonnes permissions ainsi que les noeuds dans /dev

sudo tar xvf rootfs.tar -C <ROOT_NFS>

Enjoy

Vous n'avez plus qu'à lancer la dernière commande depuis la console série, qui va exécuter le kernel que uboot viens de copier en RAM avec la commande tftp.

# bootm

Si tout s'est déroulé correctement, vous avez maintenant une carte qui démarre avec un noyau récupéré par tftp et un rootfs sur NFS.

En plus de cela, vous avez également la satisfaction d'avoir construit (à l'aide de votre système hôte) un système complet sans utiliser d'outil pré-compilé !

    • le 12 août 2013 à 15:13

      Buildroot ne fait que créer le système racine, il ne l'installe pas.

      Le problème est qu'il y a autant de façons d'installer le système que de cartes, voire plus vu que le partitionnement est généralement au choix de l'utilisateur.

      Donc, malheureusement, pas de méthode simple pour installer. Essayez de faire un mount NFS c'est le plus simple pour développer facilement.

    • le 08 août 2013 à 11:10

      Comment faire pour faire

      make option_qui_place_le_système_crée
      avec buildroot.

    • le 08 août 2013 à 11:11

      Comment faire pour faire

      make option_qui_place_le_système_crée_dans _la_cible
      avec buildroot.

      Merci pour vatre tuto.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée.