Démarrage d'un Linux embarqué : du reset au premier processus, étape par étape
Un Linux embarqué qui ne démarre pas s'arrête toujours à un endroit précis, et cet endroit se déduit du dernier message affiché. Encore faut-il savoir ce qui aurait dû venir après. Voici la séquence complète sur une plateforme ARM 64 bits typique, avec ce qui casse à chaque étage.
Étage 0 : le code ROM
À la sortie du reset, le processeur exécute un code gravé dans le silicium. Il initialise le strict minimum, lit les broches de configuration ou une mémoire OTP pour savoir sur quel support chercher la suite, puis charge un petit binaire dans la SRAM interne, parce que la DRAM n'est pas encore configurée.
Cette contrainte explique l'existence d'un étage intermédiaire : le premier chargeur doit tenir dans quelques dizaines de kilooctets.
Ce qui casse ici : rien ne s'affiche du tout, pas même un caractère. Les causes sont matérielles ou de configuration : mauvaises broches de sélection de support, image non signée alors que le démarrage sécurisé est activé, en-tête au mauvais format. Le diagnostic passe par la fiche technique du SoC, pas par le logiciel.
Étage 1 : le chargeur de premier niveau
Sur les plateformes ARM 64 bits modernes, c'est TF-A, Trusted Firmware-A, dans son rôle BL2. Il configure le contrôleur DRAM, ce qui est la partie la plus délicate, met en place le monde sécurisé avec BL31, puis charge U-Boot en DRAM et lui donne la main.
Sur les plateformes 32 bits plus anciennes, ce rôle est tenu par le SPL de U-Boot.
Ce qui casse ici : un message tronqué, ou un blocage après quelques lignes. La cause la plus fréquente est le paramétrage DRAM, qui dépend du modèle exact de puce mémoire. Une carte qui démarre à froid et pas à chaud, ou une sur dix seulement, pointe presque toujours vers un calibrage mémoire limite.
Étage 2 : U-Boot
U-Boot dispose de la DRAM complète. Il initialise les périphériques nécessaires au chargement, réseau, eMMC, SD, charge le noyau, le devicetree et éventuellement un initramfs, puis construit les arguments de démarrage et saute dans le noyau.
Deux détails comptent. Les variables bootargs transmettent la ligne de commande, dont la console et la racine du système de fichiers. Et U-Boot peut modifier le devicetree à la volée avant de le passer, ce qui explique certains écarts entre le fichier compilé et ce que voit le noyau.
Ce qui casse ici : l'invite U-Boot s'affiche mais le noyau ne démarre pas. Vérifiez les adresses de chargement, un noyau et un devicetree qui se recouvrent en mémoire produisent un plantage silencieux. Le bdinfo et le printenv donnent l'essentiel.
Étage 3 : décompression et entrée du noyau
Le noyau ARM 64 bits est généralement une Image compressée. Le code de décompression s'exécute, puis le noyau démarre avec le pointeur vers le devicetree en registre x0.
C'est le moment du silence le plus déroutant : entre le saut de U-Boot et les premiers messages du noyau, il n'y a aucune sortie tant que la console n'est pas initialisée.
Ce qui casse ici : U-Boot dit « Starting kernel... » et plus rien. Trois causes dominent. La console indiquée dans bootargs ne correspond pas au matériel, auquel cas le noyau démarre mais ne parle pas ; activez earlycon pour obtenir la sortie très tôt. Le devicetree ne correspond pas au SoC, et le noyau panique avant la console. Ou l'adresse de décompression écrase quelque chose.
L'option earlycon est le premier réflexe, elle transforme un silence en diagnostic.
Étage 4 : le noyau, jusqu'au montage de la racine
Le noyau initialise la mémoire virtuelle, démarre les cœurs secondaires, sonde les périphériques déclarés dans le devicetree, monte le système de fichiers racine, puis exécute le premier processus.
Ce qui casse ici : le fameux Kernel panic - not syncing: VFS: Unable to mount root fs. La cause est presque toujours l'une des trois suivantes. Le pilote du support n'est pas compilé en dur dans le noyau, il est en module, donc indisponible avant le montage de la racine. Le paramètre root= désigne une partition inexistante ; sur eMMC, la numérotation change selon l'ordre d'énumération, ce qui rend root=PARTUUID= plus fiable que root=/dev/mmcblk0p2. Ou le système de fichiers n'est pas du type attendu.
Étage 5 : PID 1
Le noyau exécute /sbin/init, ou ce que désigne init=. Selon le système, c'est BusyBox init, systemd, ou un script.
Ce qui casse ici : Kernel panic - not syncing: Attempted to kill init!. Le binaire n'existe pas, n'est pas exécutable, ou il lui manque une bibliothèque dynamique. Sur une racine construite à la main, l'oubli classique est l'éditeur de liens dynamique. Le test décisif est de démarrer avec init=/bin/sh : si vous obtenez un shell, la racine est saine et le problème est dans l'init.
Tableau de diagnostic rapide
| Symptôme | Étage | Première vérification |
|---|---|---|
| Aucun caractère | ROM | Broches de sélection, signature, format d'en-tête |
| Quelques lignes puis blocage | TF-A ou SPL | Calibrage DRAM, comportement à froid |
| Invite U-Boot mais pas de noyau | U-Boot | Adresses de chargement, recouvrement mémoire |
| « Starting kernel » puis silence | Entrée noyau | earlycon, console dans bootargs, devicetree |
Unable to mount root fs | Noyau | Pilote en dur, root=, type de système de fichiers |
Attempted to kill init | PID 1 | init=/bin/sh, bibliothèques manquantes |
Les trois options qui font gagner des heures
earlycon affiche la sortie noyau avant l'initialisation complète de la console. À mettre systématiquement pendant la mise au point.
initcall_debug trace chaque fonction d'initialisation avec sa durée. Indispensable quand le démarrage est lent plutôt que bloqué, et pour repérer un pilote qui attend un délai d'expiration.
ignore_loglevel force l'affichage de tous les messages, y compris ceux que le niveau par défaut masque, et révèle des avertissements qui expliquent le blocage suivant.
Pourquoi le système de construction change le diagnostic
Sur une image Yocto ou Buildroot, chaque étage est reproductible et vous pouvez recompiler un seul composant. Sur une image reçue d'un fournisseur sans les sources, vous n'avez ni les symboles du noyau, ni la configuration exacte, et le diagnostic se limite à l'observation.
C'est un argument souvent négligé au moment de choisir entre reprendre le BSP du fabricant tel quel ou reconstruire la chaîne : la capacité à diagnostiquer un démarrage est une propriété du système de construction, pas du produit.
Références
- Trusted Firmware-A, rôles BL2 et BL31
- Documentation U-Boot, variables
bootargset adresses de chargement - Kernel.org, Kernel parameters,
earlycon,initcall_debug,ignore_loglevel,root= - Kernel.org, Booting AArch64 Linux, état d'entrée du noyau et registre x0
Pour aller plus loin
Cette séquence prend son sens quand on l'a parcourue sur une carte réelle, en cassant volontairement chaque étage pour observer le message obtenu. C'est ce que couvrent nos formations Linux embarqué et notre cours sur les pilotes Linux.