Processus de démarrage d'Alpine sur le Raspberry Pi

Aujourd'hui, nous allons examiner en détail le processus de démarrage de Alpine Linux sur un Raspberry Pi.

clip_image001

L'image montre le contenu d'une carte SD "vierge" avec l'image Alpine, qui n'a pas encore été démarrée.

clip_image003

Cette image montre le contenu du dossier de démarrage.

Étapes initiales de démarrage sur le Raspberry Pi

Consultez ce lien pour un guide détaillé des différents supports de démarrage :

https://www.raspberrypi.org/documentation/hardware/raspberrypi/bootmodes/bootflow.md

Le Raspberry Pi n'a pas de "BIOS" classique comme dans les ordinateurs compatibles IBM PC basés sur x86. Son processeur doit être considéré comme une extension du VideoCore IV pour le démarrage, plutôt que l'inverse.

Ainsi, initialement, le VideoCore IV, le GPU, a le contrôle. Il démarre à partir de sa propre ROM (premier étage), et vérifie les sources de démarrage pour un fichier appelé bootcode.bin. Pour nos besoins, nous supposerons que ce fichier est chargé depuis la carte SD.

bootcode.bin (51 KB) est chargé dans le cache L2 local de 128K du GPU. Il active la RAM et charge la troisième étape : start.elf (2759 KB)

start.elf va lire le fichier config.txt, et configurer les paramètres appropriés pour le VideoCore IV. config.txt est un fichier de paramètres pour le Raspberry Pi où vous pouvez définir par exemple le mode vidéo, l'overscan, les options audio, les codecs MPEG, les paramètres d'overclocking, etc. - il est "comme" la partie des paramètres BIOS de l'ordinateur traditionnel. start.elf lui-même est le firmware pour le GPU, qui est son propre système d'exploitation appelé "VideoCore OS".

NB : Il existe différentes versions de start.elf, dont start_x.elf qui inclut les pilotes de caméra, et start_cd.elf - qui est une version réduite de start.elf, à utiliser lorsque la mémoire du GPU est réduite au maximum : gpu_mem=16. Cette dernière version pourrait être intéressante pour les systèmes embarqués. Référez-vous à ce site pour plus d'informations : https://www.raspberrypi.org/documentation/configuration/boot_folder.md

config.txt est facultatif. Si vous utilisez la même carte SD (ou image) pour plusieurs Pi, vous pouvez définir des filtres conditionnels à appliquer à certains scénarios ; vous pouvez même appliquer une condition basée sur le numéro de série du Raspberry Pi, ou sur l'état d'un GPIO. Il est également possible d'inclure des fichiers supplémentaires dans le fichier config.txt. Ensemble, vous obtenez un système assez puissant pour la configuration de base du Raspberry Pi.

Bien sûr, il existe également des options de démarrage dans config.txt, qui contrôlent la façon dont le démarrage se déroule.

start.elf charge ensuite le noyau (comme spécifié par config.txt / defaults), et le lance avec cmdline.txt comme ligne de commande du noyau .

config.txt options de démarrage

Jetez un coup d'œil :

https://www.raspberrypi.org/documentation/configuration/config-txt/boot.md

pour en savoir plus sur les options de démarrage disponibles.

Le fichier config.txt par défaut d'Alpine

Jetons un coup d'oeil au fichier config.txt fourni avec la version 3.8.1 ARMHF d'Alpine :

  • disable_splash=1
    • cela désactivera l'écran arc-en-ciel au démarrage (purement cosmétique)
  • boot_delay=0
    • n'attendez pas dans start.elf avant de charger le noyau. (un délai de démarrage peut être nécessaire pour que certaines cartes SD soient "prêtes").
  • gpu_mem=256
    • définit la répartition de la mémoire entre le GPU et le CPU. La mémoire du Pi est partagée. Ici, nous avons défini 256 Mo pour le GPU, vous pourriez vouloir réduire cette valeur sur les systèmes embarqués qui ne pilotent pas d'écrans.
  • gpu_mem_256=64
    • définit la répartition de la mémoire pour les Pi ne disposant que de 256 Mo de mémoire totale (uniquement certains Pi 1B d'ancienne génération).
  • [pi0]
    • déclaration conditionnelle pour Pi Zero ; cela couvre Pi Zero et Pi Zero W
    • toutes les déclarations jusqu'au prochain conditionnel ne seront appliquées que si nous fonctionnons sur un Pi Zero / Zero W
    • Les Pi Zero / Zero W utilisent toujours un ancien CPU ISA ARMv6 (ARM11 @ 1 GHz).
    • nous utiliserons un noyau compilé pour ARMv6
  • kernel=boot/vmlinuz-rpi
    • ceci spécifie le noyau à charger
    • il doit s'agir d'un fichier image du noyau non compressé
    • Les noyaux 32 bits sont chargés à l'adresse 0x8000 par défaut.
  • initramfs boot/initramfs-rpi
    • "la commande initramfs spécifie à la fois le nom du fichier ramfs et l'adresse mémoire à laquelle le charger"
    • initramfs = système de fichiers RAM initial
    • ce fichier peut être extrait avec 7Zip, par exemple (extraire deux fois)
    • NOTE : ce paramètre de configuration est spécifié sans le "=".
  • [pi1]
    • ceci appliquera les lignes de configuration suivantes à tous les Raspberry Pi 1 - ne correspondra pas sur les Pi 2, etc.
  • kernel=boot/vmlinuz-rpi
  • initramfs boot/initramfs-rpi
  •  [pi2]
  • kernel=boot/vmlinuz-rpi2
    • remarquez comment nous utilisons un noyau différent, puisque nous avons ici un CPU qui peut exécuter du code ARMv7
  • initramfs boot/initramfs-rpi2
    • et un initramfs différent ...
  • [pi3]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [pi3+]
    • kernel=boot/vmlinuz-rpi2
    • initramfs boot/initramfs-rpi2
  • [tous]
    • la ou les lignes suivantes s'appliqueront à nouveau à tous les Pi
  • inclure usercfg.txt
    • inclura un fichier usercfg.txt dans la configuration, qui n'existe pas encore dans l'image Alpine par défaut.
    • Vous pouvez y ajouter la configuration personnalisée que vous souhaitez, par exemple la résolution HDMI, etc.
    • Alternativement, vous pouvez simplement coller le texte ci-dessous dans le fichier config.txt.

Note : la configuration utilisée dans Alpine ne spécifie pas un second paramètre pour l'initramfs (adresse à laquelle monter l'initramfs) ; il n'est donc pas conseillé de copier ceci 1:1. Ceci étant dit, cela semble fonctionner, probablement par défaut en toute sécurité !

En savoir plus sur config.txt :

Le noyau et l'Initramfs

Le noyau

En fonction des paramètres du fichier config.txt, le bon noyau sera sélectionné et chargé dans la RAM. Une fois qu'il est chargé, le(s) cœur(s) du processeur ARM est/sont libéré(s) de la réinitialisation afin de pouvoir démarrer le noyau.

On lui passera le contenu de cmdline.txt comme ligne de commande du noyau. start.elf passera aussi ses propres paramètres supplémentaires, par exemple le réglage des canaux DMA, l'adresse MAC de la puce SMSC LAN, etc.

Obtenez le cmdline.txt:

cat /proc/cmdline

dmesg | grep "Ligne de commande"

cmdline.txt

regardons le cmdline.txt par défaut d'Alpine :

modules=loop,squashfs,sd-mod,usb-storage quiet dwc_otg.lpm_enable=0 console=tty1

Cela charge les modules

  • boucle
    • le périphérique de boucle est utilisé pour monter un fichier comme système de fichiers
  • squashfs
    • Système de fichiers compressés en lecture-onymes pour Linux
    • particulièrement adapté aux systèmes embarqués
    • https://en.wikipedia.org/wiki/SquashFS
    • ceci est utilisé pour le modloop plus tard
  • sd-mod
    • Pilote de disque SCSI / pilote de bloc
    • apparemment copié de la configuration par défaut d'Alpine, par exemple ici : https://wiki.alpinelinux.org/wiki/Create_a_Bootable_Compact_Flash
  • stockage usb
    • Pilote de stockage de masse USB pour Linux

NB : les noms de modules peuvent contenir à la fois - et _, ces symboles peuvent apparemment être intervertis.

calme

  • définit le niveau de journalisation par défaut du noyau à KERN_WARNING, qui supprime tous les messages de journalisation, sauf ceux qui sont très sérieux, pendant le démarrage (source : documentation Raspberry Pi)

dwc_otg.lpm_enable=0

console=tty1

  • définit la console série

Pour en savoir plus :

initramfs

" La commande initramfs spécifie à la fois le nom du fichier ramfs et l'adresse mémoire à laquelle le charger. Elle réalise les actions de ramfsfile et ramfsaddr en un seul paramètre. L'adresse peut également être followkernel (ou 0) pour le placer en mémoire après l'image du noyau. Les exemples de valeurs sont : initramfs initramf.gz 0x00800000 ou initramfs init.gz followkernel. NOTE : Cette option utilise une syntaxe différente de toutes les autres options, et vous ne devriez pas utiliser le caractère = ici." - Documentation sur le Raspberry Pi

Il y aura un message du noyau comme celui-ci "[ 0.143709] Trying to unpack rootfs image as initramfs..." dans dmesg, montrant que l'image est en cours de décompression.

L'initramfs permet au noyau Linux d'ajouter des modules et d'effectuer d'autres tâches pour préparer le montage du système de fichiers réel. Il permet ainsi d'obtenir un système de démarrage très polyvalent, sans avoir à recompiler le noyau / ajouter des frais généraux au noyau. Par exemple, pour les utilisateurs ayant un système de fichiers crypté, initramfs demandera la phrase de passe avant de pouvoir décrypter et monter les systèmes de fichiers.

Selon le CPU de votre Raspberry Pi, différents fichiers initramfs seront utilisés, comme défini dans config.txt:

  • boot/initramfs-rpi pour Pi 1 / 1B+ et Pi Zero / Zero W, et Compute Module 1
  • boot/initramfs-rpi2 pour tous les autres Pi (2, 3B, 3B+, ...)

Le fichier initramfs peut être extrait en utilisant, par exemple, sur Windows 7Zip. (Vous devez l'extraire deux fois : le premier fichier contient un second fichier gzippé).

Les images initramfs peuvent avoir différents formats, selon les algorithmes compilés statiquement dans le noyau (par exemple gzip / bzip2 / LZMA / XZ / LZO / LZ4).

clip_image005

Ce deuxième fichier regroupe une structure de dossiers et de fichiers, que vous reconnaîtrez si vous avez déjà utilisé Linux. Il est dans un format spécial, "cpio", qui est similaire au goudron.

clip_image007

Si vous êtes curieux, vous pouvez en savoir plus sur cpio ici :

https://en.wikipedia.org/wiki/Cpio

Voici ce que contient le fichier :

clip_image008

dans le bac, il contient :

  • busybox
    • exécutable compact contenant plusieurs utilitaires UNIX en une seule application
    • notamment pour les systèmes embarqués
    • https://busybox.net/about.html
  • sh

dans sbin, il contient

le site init est lancé par le noyau Linux en tant que premier processuset met en place Alpine.

Voici l'outil utilisé pour générer l'initramfs pour Alpine :

https://github.com/alpinelinux/mkinitfs

init

il s'agit d'une séquence extrait de ce que fait le script init lancé depuis l'initramfs :

  • crée les répertoires /usr/bin, /usr/sbin, /proc, /sys, /dev, /sysroot , /media/cdrom, /media/usb, /tmp, /run récursivement
    • certains de ces répertoires semblent être déjà présents dans l'initramfs
  • installe busybox (met en place des liens symboliques vers les commandes intégrées de busybox)
  • établit le PATH à /usr/bin:/bin:/usr/sbin:/sbin
  • crée /dev/null en utilisant mknod
  • monte proc dans /proc
    • proc est un système de pseudo-fichiers d'informations sur les processus
    • vous pouvez obtenir des informations sur le noyau en lisant et configurer certaines choses en écrivant dans certains fichiers de ce répertoire
    • les entrées numérotées sont des répertoires contenant des informations sur le processus avec cet id de processus particulier.
    • https://www.tldp.org/LDP/Linux-Filesystem-Hierarchy/html/proc.html
  • ... et sysfs vers /sys
  • analyse les options passées via cmdline.txt, les suivantes sont reconnues :
    • alpine_dev autodetect autoraid chart cryptroot cryptdm cryptheader cryptoffset
    • cryptdiscards cryptkey debug_init dma init_args keep_apk_new modules ovl_dev
    • pkgs quiet root_size root usbdelay ip alpine_repo apkovl alpine_start splash
    • blacklist overlaytmpfs rootfstype rootflags nbd resume s390x_net dasd ssh_key

Conseil : essayez de changer "calme"dans cmdline.txt en "noquiet"pour obtenir un démarrage plus verbeux, et voir de nombreux messages de init

  • monte devtmpfs sur /dev
  • charge les pilotes pour pouvoir monter modloop plus tard
  • si le paramètre nbd est présent et configuré (périphérique de bloc réseau), essaie de faire apparaître une adresse IP et de configurer le périphérique de bloc réseau à l'aide de nbd-client.
  • si le paramètre racine est défini
    • if [ -n "$KOPT_root" ] ; then
    • n évalue si la longueur de "$KOPT_root" est non nulle.
    • alors nlplug-findfs est exécuté
    • overlaytmpfs est géré de manière optionnelle
    • les points de montage actuels sont migrés vers /sysroot
    • switch_root est lancé et le contrôle est passé à /sbin/init
    • (c'est un point d'entrée pour installer Alpine Linux en mode normal, pas en mode lecture seule)

autrement :

  • nlplug-findfs est exécuté pour trouver le support d'amorçage,
    • les crypto-options sont possibles et traitées ici
    • Les chemins des apkovls trouvés sont ajoutés à /tmp/apkovl (en utilisant le commutateur -a, voir https://pi3g.com/2019/01/09/nlplug-findfs-documentation/)
      • apparemment le fichier n'est créé que si des apkovl sont trouvés.
    • https://github.com/alpinelinux/mkinitfs/blob/master/nlplug-findfs.c
    • apkovls sont assortis à la recherche "*.apkovl.tar.gz*", c'est pourquoi les fichiers de sauvegarde que lbu commit crée sont pas trouvés / appliqués automatiquement - ils ont une structure de nom d'hôte.horodatage.tar.gz, par exemple pidoctor.20190110180745.tar.gz
  • si le paramètre apkovl est réglé,
    • et il est vide - et /tmp/apkovls existe (c'est-à-dire que les fichiers ont été trouvés par nlplug-findfs) - l'ovl est défini de la première ligne de /tmp/apkovls
    • et il commence par http:// / https:// / ftp:// - une connexion réseau est essayée pour tirer l'apkovl
    • sinon, l'ovl est défini par la chaîne de l'option apkovl.
  • si l'apkovl existe,
    • et est un fichier .gz, l'apkovl est déballé. tar -C "$dest" -zxvf "$ovl" > $ovlfiles et /tmp/ovlfiles est rempli avec la sortie de tar
  • il y a du code pour un écran d'accueil, fbsplash est une des fonctions qui est incluse dans busybox - elle affiche des fichiers fbsplash*.ppm
  • if [ -z "$ALPINE_REPO" ] -> signifie, si la chaîne $ALPINE_REPO est vide
    • dans certaines circonstances, des services de démarrage par défaut sont ajoutés, pour les niveaux d'exécution sysinit, boot, shutdown et default (par exemple le service firstboot).
  • les dépôts apk sont ajoutés et étendus
    • le paquet alpine-base dépend de alpine-baselayout, alpine-conf et de quelques autres paquets
      • il inclut également le fichier /etc/os-release -> qui indique le nom, la version, et le joli nom d'Alpine Linu
    • alpine-baselayout est la disposition du répertoire de base
    • alpine-conf comprend, entre autres, le script lbu et les scripts de configuration.
    • busybox comprend le binaire busybox
    • busybox-initscripts comprend quelques initscripts
  • enfin, le répertoire racine est changé, et /sbin/init est lancé
    • ceci va démarrer OpenRC et exécuter les niveaux d'exécution.
    • /sbin/init est un lien symbolique vers /bin/busybox

Remarque : L'apkovl est monté avant modloop et avant l'installation des paquets. Cela permet à l'apkovl de configurer les paquets qui vont être installés, entre autres choses.

Niveaux de fonctionnement de l'OpenRC

sysinit -> boot -> default -> shutdown

https://hoverbear.org/2014/09/29/init-runlevels-and-targets/

Niveau d'exécution sysinit :

clip_image010

modloop sera démarré après dev-mount, et avant checkfs, fsck, hwdriver, modules, hwclock, dev, sysfs

hwdrivers sera lancé après modloop. hwdrivers chargera les pilotes en utilisant modprobe.

Démarrage au niveau de l'exécution :

clip_image012

Niveau d'exécution par défaut :

clip_image014

Modloop

Il existe également le fichier modloop (modloop-rpi ou modloop-rpi2).

comment il est monté

Le bon fichier pour votre système est déterminé dans le script /etc/init.d/modloop dans la fonction find_modloop()

Le résultat de uname -r est vérifié par rapport au répertoire modules/ - le sous-répertoire nommé de manière appropriée doit s'y trouver.

Par exemple : 4.14.69-0-rpi2

De cette façon, les bons modules pour votre noyau sont utilisés (uname -r imprime la version du noyau).

modloop est monté dans le répertoire /.modloop

clip_image015

Les liens symboliques sont créés à partir de /lib/firmware à /lib/modules/firmwareet /lib/modules est lié par un lien symbolique à /.modloop/modules

IndiceLe problème est le suivant : si vous voulez patcher le firmware WiFi du Raspberry Pi 3B+, vous devez le mettre dans modloop.

Contenu de Modloop

Pour explorer le contenu de modloop, vous pouvez utiliser (testé sur un système mint) :

apt-get install squashfs-tools

unsquashfs modloop-rpi2

Il contient le micrologiciel et les pilotes.

clip_image016

clip_image017

Références :

Pour en savoir plus :