Installation de Debian sur des machines ARM
dim. 22 nov. 2020 by MarmotteLes machines basées sur un processeur ARM sont devenues très populaires depuis une dizaine d'années. Comme pour la plupart des machines, j'installe Debian dessus, mais contrairement aux ordinateurs à base de processeur x86, il n'existe pas de procédure d'installation générique.
Dans cet article, je décris la manière dont j'installe Debian sur ces machines, en détaillant les spécificités de chaque installation.
Pourquoi pas Armbian ?¶
En 2011, j'ai acheté un SheevaPlug, afin de disposer d'une machine consommant très peu d'électricité sur laquelle je pourrais installer Debian, afin de gérer mon réseau (DNS, DHCP, PXE, VPN, etc.). Ce genre d'appareils n'était pas encore répandu, et il n'existait pas de projet proposant un système prêt à l'emploi pour ces machines. J'ai donc dû construire moi-même le système que j'ai installé dessus. La procédure ressemblait fortement à celle que je commençais à utiliser sur mes ordinateurs fixes et portables à base de processeur x86.
Pour les machines plus récentes, des projets comme Armbian proposent des images toutes prêtes à télécharger, et la procédure permettant de les construire soi-même. Mais ces systèmes tous faits ne me conviennent pas, puisqu'ils contiennent tout un tas de paquets installés et de configurations qui ne me servent pas, voire me dérangent. Je préfère, comme toujours, installer une Debian minimale, afin d'avoir un système correspondant exactement à mes besoins, et surtout à mes habitudes, plutôt qu'installer un système déjà construit duquel je devrais commencer par supprimer tout ce qui ne me plait pas.
J'utilise cependant le projet Armbian dans deux cas :
- Lorsque j'achète un nouvel appareil, afin d'avoir rapidement un système fonctionnel, qui me permet de prendre connaissance des particularités et des configurations nécessaires pour que la machine fonctionne.
- Lorsque les paquets présents dans les dépôts Debian ne permettent pas de faire fonctionner la machine. Dans ce cas, j'ajoute le dépôt Armbian sur mon système afin d'installer les paquets nécessaires, comme un noyau Linux contenant les bons modules ou patches.
Enfin, en construisant moi-même le système, je suis plus à l'aise lorsqu'il est nécessaire de le dépanner, puisque je connais le rôle de chaque élément nécessaire à son fonctionnement.
Ma procédure complète, en bref¶
Même s'il existe des particularités, la base de mes installations est toujours la même, que ce soit pour un ordinateur fixe, une machine ARM, un conteneur, ou toute autre machine : un système Debian minimal, créé à l'aide de debootstrap
.
J'ai d'ailleurs déjà écrit des articles décrivant mes installations de Debian Wheezy et Debian Jessie par cette méthode.
Afin d'éviter de répéter ces étapes à la main, j'ai maintenant écrit quelques scripts qui me permettent de générer rapidement un système Debian vierge comportant les quelques personnalisations que j'applique sur tous mes systèmes.
Particularités génériques pour les machines ARM¶
Installation initiale¶
Comme je construis les systèmes de mes machines ARM depuis mon ordinateur fixe qui n'a pas la même architecture, je dois exécuter debootstrap
en deux étapes.
La première, avec le paramètre --foreign
, génère le rootfs initial, alors que la seconde, exécutée par dans l'architecture cible, effectue les configurations de base sur le nouveau système.
Pour cela, le binaire statique qemu
de l'architecture cible doit être copié dans le système en cours de création, afin de pouvoir exécuter des programmes compilés pour cette architecture.
$ sudo debootstrap --foreign --arch=${ARCH} --verbose --variant=minbase --include=bash-completion,dbus,dialog,htop,iproute2,less,libpam-systemd,locales,openssh-server,screen,sudo,systemd,systemd-sysv,tzdata,vim --exclude=nano buster ${NEW_ROOTFS} ${MIRROR}
$ sudo cp /usr/bin/qemu-${QEMU_ARCH}-static ${NEW_ROOTFS}/usr/bin/
$ sudo chroot ${NEW_ROOTFS} debootstrap/debootstrap --second-stage
J'effectue ensuite quelques configurations de base :
$ # Définition du nom d'hôte
$ echo ${NEW_HOSTNAME} | sudo tee ${NEW_ROOTFS}/etc/hostname
$ # Écriture du fstab avec quelques options pour allonger la durée de vie des mémoires flash
$ echo /dev/mmcblk0p1 / ext4 defaults,noatime,nodiratime,commit=300,errors=remount-ro 0 0 | sudo tee ${NEW_ROOTFS}/etc/fstab
$ # Ajout du dépôt backports, souvent nécessaire pour une Debian stable
$ echo deb http://deb.debian.org/debian buster-backports main | sudo tee ${NEW_ROOTFS}/etc/apt/sources.list.d/backports.list
$ # Activation du réseau
$ cat << EOF | sudo tee ${NEW_ROOTFS}/etc/systemd/network/10-eth0.network
[Match]
Name=eth0
[Network]
DHCP=yes
[DHCP]
UseDomains=yes
EOF
$ sudo chroot ${NEW_ROOTFS} systemctl enable systemd-networkd.service systemd-resolved.service
La plupart des machines nécessitent le dépôt backports pour fonctionner. Il est donc nécessaire, après avoir ajouté ce dépôt dans les sources, d'effectuer une mise à jour du système :
$ sudo chroot ${NEW_ROOTFS} apt update
$ sudo chroot ${NEW_ROOTFS} apt --assume-yes dist-upgrade
Enfin, comme aucune connexion utilisateur n'est possible à ce moment, il faut aussi créer un utilisateur avec des droits d'administration :
$ sudo chroot ${NEW_ROOTFS} adduser --gecos "" ${ADMIN_USERNAME}
$ sudo chroot ${NEW_ROOTFS} adduser ${ADMIN_USERNAME} adm
$ sudo chroot ${NEW_ROOTFS} adduser ${ADMIN_USERNAME} sudo
Configuration du bootloader¶
Le bootloader u-boot
est rapidement devenu un standard pour permettre de démarrer des machines ARM.
Comme grub
, c'est un programme très simple, qui charge les premiers composants du système en mémoire, et passe ensuite la main au système à démarrer.
Pour la plupart des machines récentes, il lit ses instructions dans un fichier /boot/boot.scr
placé sur le périphérique contenant le système (carte SD, clé USB, etc.).
La génération de ce fichier est une des étapes réalisées par l'utilitaire flash-kernel
, dont le nom vient de la nécessité de flasher le noyau directement sur la mémoire flash de certains appareils.
La suite d'instructions peut varier d'une machine à l'autre, il lui faut donc pouvoir déterminer quel modèle il utilise.
Cette information est trouvée dans le fichier virtuel /proc/device-tree/model
lorsqu'il est exécuté depuis la machine en question.
Lorsque cette information n'est pas disponible, ou qu'on veut la modifier, il est possible de l'écrire dans le fichier /etc/flash-kernel/machine
, ce qui est nécessaire ici, puisque les commandes sont lancées dans un environnement chroot
.
On peut ensuite installer le noyau correspondant à l'appareil pour lequel le système est construit.
Il ne faut pas non plus oublier les paquets u-boot-tools
et flash-kernel
, qui permettent de générer les éléments nécessaires au démarrage du système.
$ echo ${BOARD_MODEL} | sudo tee ${NEW_ROOTFS}/etc/flash-kernel/machine
$ sudo chroot ${NEW_ROOTFS} apt --assume-yes install u-boot-tools flash-kernel linux-image-armmp
Indication des particularités de la machine au système¶
Au début, la liste des périphériques présents dans chaque machine était écrite directement dans le noyau Linux.
Cependant, avec l'explosion du nombre de machines, il a été décidé d'extraire cette liste dans un fichier séparé, permettant d'ajouter beaucoup plus facilement le support de nouvelles machines, sans nécessiter d'attendre des mises à jour du noyau.
Cela se traduit par la présence d'un fichier binaire, le dtb
(pour Device Tree Blob), généré à partir d'un simple fichier texte, le dts
(Device Tree Source).
Les versions récentes de u-boot
permettent d'indiquer le fichier dtb
à charger via des instructions spécifiques.
Avec des versions plus anciennes, il était nécessaire de concaténer le contenu du dtb
au noyau Linux.
Activation de fonctionnalités optionnelles¶
Certaines machines proposent des fonctionnalités optionnelles.
Par exemple, certaines broches d'un connecteur GPIO peuvent constituer un port série.
Dans ce cas, l'utilisateur a alors le choix entre interroger les broches directement, ou configurer le système pour n'avoir qu'un port série standard à manipuler, ce qui nécessite de le décrire dans le fichier dtb
.
Pour activer ces fonctionnalités, il est possible de remplacer le fichier dtb
, mais cette solution nécessite d'en créer un par combinaison de fonctionnalités optionnelles possible, ce qui génèrerait un nombre exponentiel de dtb
.
Ce problème est résolu grâce à l'utilisation des fichiers dtbo
(Device Tree Blob Overlay), qui sont des "extraits" de fichier dtb
, chargés en plus du fichier d'origine, pour y ajouter des définitions de périphériques supplémentaires.
Ils sont chargés de la même manière que le dtb
, soit par des instructions spécifiques de u-boot
, soit concaténés à la fin du noyau.
Globalscale SheevaPlug¶
C'est la plus vieille machine ARM que je possède. Il a malheureusement cessé de fonctionner cet été, suite à une extinction pourtant tout à fait normale. L'alimentation avait visiblement bien vécu, et elle n'a jamais voulu redémarrer.
Cette machine est dotée de 512 Mo de mémoire flash, contenant au moins u-boot
Il est possible d'installer le système sur cette mémoire flash, ou sur un autre périphérique de stockage comme une carte SD ou une clé USB.
Il peut faire tourner un système Debian en architecture armel
(qemu-arm-static
) avec le noyau linux-image-kirkwood
des dépôts Debian, au moins depuis Squeeze.
Configuration de u-boot
¶
u-boot
est installé sur la mémoire interne, mais ne contient pas de configuration particulière par défaut.
Il est nécessaire de se connecter en console série (mini-USB) pour le configurer.
$ sudo screen /dev/ttyUSB0 115200
Voici pour information la configuration qui était présente sur mon SheevaPlug : uboot-env.
Préparation de la carte SD¶
Le contenu de la carte SD n'a rien de particulier, puisque u-boot
vient directement lire les fichiers dessus pour charger le système.
Il suffit d'y créer une partition et d'y copier les fichiers du système Debian généré.
SolidRun Cubox Pro¶
La seconde machine ARM que j'ai achetée est un Cubox Pro de SolidRun. Cet achat est une de mes rares déceptions, la machine n'ayant eu que très peu de support. Moins d'un an après sa sortie, une nouvelle série ayant une architecture matérielle totalement différente est sortie sous le nom Cubox-i, et le constructeur a totalement abandonné le vieux Cubox, supprimant même toutes les informations à son propos sur son site internet et son wiki. Huit ans après, il est toujours compliqué d'obtenir un affichage fluide en 1080p avec une distribution comme GeeXboX (dédiée à Kodi), et même impossible d'obtenir un affichage graphique tout court sous Debian. Il fonctionne heureusement très bien en tant que serveur headless, mais avec un port HDMI/CEC, une sortie S/PDIF et un capteur infra-rouge, ce n'est pas du tout l'usage que je lui destinais.
Tout comme pour le SheevaPlug, u-boot
est installé sur une mémoire flash interne.
Cependant, il est configuré par défaut pour charger les instructions de démarrage depuis un fichier /boot/boot.scr
placé à la racine de la carte microSD.
Il n'est donc pas nécessaire de modifier son paramétrage, qui est de toutes façons partiellement verrouillé, de manière à empêcher la modification des instructions de démarrage.
Il peut faire tourner un système Debian en architecture armhf
(qemu-arm-static
) avec le noyau linux-image-armmp
des dépôts Debian depuis Buster.
Le contenu du fichier /etc/flash-kernel/machine
doit être SolidRun CuBox
.
Préparation de la carte microSD¶
Tout comme pour le SheevaPlug, le contenu de la carte SD n'a rien de particulier, puisque u-boot
vient directement lire les fichiers dessus pour charger le système.
Il suffit là encore d'y créer une partition et d'y copier les fichiers du système Debian généré.
Xunlong OrangePi Zero¶
Plus récemment, j'ai acheté deux OrangePi Zero. Le premier est utilisé comme serveur Minitel, alors que le second est connecté sur mon compteur d'électricité.
Il peut faire tourner un système Debian en architecture armhf
(qemu-arm-static
) avec le noyau linux-image-current-sunxi
des dépôts Armbian.
Le contenu du fichier /etc/flash-kernel/machine
doit être Xunlong Orange Pi Zero
.
Dépôt supplémentaire nécessaire¶
Étant bien plus récent que les deux précédents, il n'est pas encore géré nativement par le noyau Linux disponible dans les dépôts Debian. J'utilise donc le noyau mis à disposition par le projet Armbian, ce qui nécessite d'ajouter le dépôt armbian :
$ curl -L http://apt.armbian.com/armbian.key | sudo chroot ${NEW_ROOTFS} apt-key add -
$ echo deb http://apt.armbian.com buster main | sudo tee ${NEW_ROOTFS}/etc/apt/sources.list.d/armbian.list
$ sudo chroot ${NEW_ROOTFS} apt update
Configuration de la connexion WiFi¶
L'utilisation de l'interface WiFi nécessite d'installer le paquet armbian-firmware
depuis le dépôt Armbian.
Il faut ensuite installer le paquet wpasupplicant
et le configurer en lui fournissant le SSID du réseau WiFi et la passphrase associée :
$ cat << EOF | sudo tee ${NEW_ROOTFS}/etc/systemd/network/10-wlan0.network
[Match]
Name=wlan0
[Network]
DHCP=yes
[DHCP]
UseDomains=yes
EOF
$ wpa_passphrase ${WIFI_SSID} ${WIFI_PASSPHRASE} | sudo tee ${NEW_ROOTFS}/etc/wpa_supplicant/wpa_supplicant-wlan0.conf > /dev/null
$ sudo chmod 0440 ${NEW_ROOTFS}/etc/wpa_supplicant/wpa_supplicant-wlan0.conf
$ sudo chroot ${NEW_ROOTFS} systemctl enable wpa_supplicant@wlan0.service
Configuration de la console série¶
Une console série est disponible sur le port microUSB d'alimentation, en utilisant le pilote gadget USB du noyau Linux. Elle n'est donc accessible qu'à partir du moment où le système commence à démarrer, et ne permet pas de déboguer les problèmes de démarrage.
$ echo g_serial | sudo tee ${NEW_ROOTFS}/etc/modules-load.d/serial.conf
$ sudo chroot ${NEW_ROOTFS} systemctl enable serial-getty@ttyGS0.service
Une fois le système lancé et l'OrangePi Zero connecté en USB sur une autre machine, on pourra s'y connecter en utilisant la commande suivante :
$ sudo screen /dev/ttyUSB0 115200
Configuration de u-boot
¶
Malgré que Debian ne fournisse pas de noyau permettant de démarrer un OrangePi Zero, la configuration de flash-kernel
fournie est correcte.
Cependant, elle ne permet pas de détecter le noyau installé depuis les dépôts Armbian, je dois donc la modifier pour ajouter la référence de ce noyau.
De plus, comme j'ajoute un dtbo
pour pouvoir utiliser un port série du connecteur GPIO, j'ai ajouté les instructions permettant de le charger dans le script de démarrage, il faut donc aussi référencer ce script modifié.
Pour cela, il suffit de créer un fichier /usr/share/flash-kernel/db/00-orangepi-zero.db
contenant ces lignes :
Machine: Xunlong Orange Pi Zero
Kernel-Flavors: armmp armmp-lpae sunxi
U-Boot-Script-Name: bootscr.sunxi-overlays
Le script de démarrage modifié doit être placé dans le répertoire /etc/flash-kernel/bootscript/
et j'ai choisi de placer les dtbo
dans le répertoire /boot/dtbs/overlays/
.
Préparation de la carte microSD¶
Cette machine ne dispose d'aucune mémoire flash.
u-boot
doit donc être placé sur la carte microSD, à un emplacement bien précis indiqué sur la documentation officielle :
$ sudo dd if=/dev/zero of=${SDCARD_DEVICE} bs=1M count=4
$ sudo dd if=u-boot-sunxi-with-spl.bin of=${SDCARD_DEVICE} bs=1k seek=8
On peut ensuite créer une partition sur la carte microSD et y copier simplement le contenu du système Debian généré.
Corriger la lecture de la température pour la version LTS¶
L'un des deux exemplaires que je possède est le modèle LTS. C'est une nouvelle version matérielle ayant subi quelques modifications mineures afin d'améliorer le signal WiFi ainsi que la consommation électrique et la température.
Cependant, son capteur de température souffre d'un problème qui n'existait pas dans la version précédente et indique une valeur erronée.
Selon les informations trouvées sur Internet, il semblerait que ce soit un simple décalage d'environ 27°C.
J'ai donc appliqué une configuration corrective afin que le programme sensors
modifie les valeurs retournées, dans le fichier /etc/sensors.d/orangepi-zero-lts
:
chip "cpu_thermal-virtual-0"
compute temp1 @+27,@-27
FriendlyElec NanoPi R2S¶
La dernière machine ARM que je possède à ce jour est un NanoPi R2S, destiné à prendre la place du SheevaPlug pour gérer le réseau, en ajoutant un rôle de pare-feu/NAT entre la Livebox et le reste du LAN.
Il peut faire tourner un système Debian en architecture arm64
(qemu-aarch64-static
) avec le noyau linux-image-current-rockchip64
des dépôts Armbian.
Le contenu du fichier /etc/flash-kernel/machine
doit être FriendlyElec NanoPi R2S
.
Dépôt supplémentaire nécessaire¶
Tout comme l'OrangePi Zero, il nécessite des paquets du dépôt Armbian. J'ajoute donc ici aussi le dépôt armbian :
$ curl -L http://apt.armbian.com/armbian.key | sudo chroot ${NEW_ROOTFS} apt-key add -
$ echo deb http://apt.armbian.com buster main | sudo tee ${NEW_ROOTFS}/etc/apt/sources.list.d/armbian.list
$ sudo chroot ${NEW_ROOTFS} apt update
Configuration de la console série¶
Toujours comme l'OrangePi Zero, sa console série est disponible sur le port microUSB d'alimentation, en utilisant le pilote gadget USB du noyau Linux. La configuration est donc encore une fois la même :
$ echo g_serial | sudo tee ${NEW_ROOTFS}/etc/modules-load.d/serial.conf
$ sudo chroot ${NEW_ROOTFS} systemctl enable serial-getty@ttyGS0.service
Une fois le système lancé et le NanoPi R2S connecté en USB sur une autre machine, on pourra s'y connecter en utilisant la commande suivante :
$ sudo screen /dev/ttyUSB0 115200
Configuration de u-boot
¶
Debian ne fournit aucune configuration pour le NanoPi R2S dans le paquet flash-kernel
, il est donc nécessaire de la définir totalement.
Heureusement, le format est très simple, et j'ai donc simplement écrit ces lignes dans le fichier /usr/share/flash-kernel/db/nanopi-r2s.db
:
Machine: FriendlyElec NanoPi R2S
Kernel-Flavors: arm64 rockchip64
DTB-Id: rk3328-nanopi-r2-rev00.dtb
Boot-Script-Path: /boot/boot.scr
U-Boot-Script-Name: bootscr.nanopi-r2s
Required-Packages: u-boot-tools
Deux éléments cités dans cette configuration n'existent pas dans les paquets Debian : le dtb
et le script de démarrage.
Pour le dtb
, j'ai simplement pris celui fourni par Armbian, il doit être placé dans le répertoire /etc/flash-kernel/dtbs/
.
Le script de démarrage, placé dans le répertoire /etc/flash-kernel/bootscript/
, est très proche du script bootscr.uboot-generic
fourni par Debian, mais pour une raison que ne ne comprends pas, le chargement du dtb
ne fonctionne pas avec celui d'origine.
J'ai donc appliqué une modification mineure dedans pour corriger ce point :
--- bootscr.uboot-generic 2019-05-25 03:36:25.000000000 +0200
+++ bootscr.nanopi-r2s 2020-11-17 20:41:31.967175267 +0100
@@ -44,7 +44,7 @@
@@UBOOT_PREBOOT_EXTRA@@
load ${devtype} ${devnum}:${partition} ${kernel_addr_r} ${prefix}vmlinuz-${fk_kvers} \
-&& load ${devtype} ${devnum}:${partition} ${fdt_addr_r} ${prefix}${fdtpath} \
+&& load ${devtype} ${devnum}:${partition} ${fdt_addr_r} ${prefix}dtb-${fk_kvers} \
&& load ${devtype} ${devnum}:${partition} ${ramdisk_addr_r} ${prefix}initrd.img-${fk_kvers} \
&& echo "Booting Debian ${fk_kvers} from ${devtype} ${devnum}:${partition}..." \
&& booti ${kernel_addr_r} ${ramdisk_addr_r}:${filesize} ${fdt_addr_r}
Préparation de la carte microSD¶
Le NanoPi R2S ne dispose pas non plus de mémoire flash.
Comme pour l'OrangePi Zero, u-boot
doit donc être aussi placé sur la carte microSD, à un emplacement bien précis indiqué sur la documentation officielle.
Cependant, la disposition est différente, et les éléments à placer occupent 16 Mo au total (preloader, puis bootloader et enfin ATF).
$ sudo dd if=/dev/zero of=${TARGET_DEVICE} bs=1M count=16
$ sudo dd if=${BOARD_CONFIGURATION}/idbloader.bin of=${TARGET_DEVICE} seek=64
$ sudo dd if=${BOARD_CONFIGURATION}/uboot.img of=${TARGET_DEVICE} seek=16384
$ sudo dd if=${BOARD_CONFIGURATION}/trust.bin of=${TARGET_DEVICE} seek=24576
Puisque les données copiées occupent 16 Mo, il est nécessaire de décaler la partition, puisque l'emplacement par défaut de la première partition créée sur un disque est le plus souvent à 1Mo du début du disque.
$ echo 32768, | sudo sfdisk ${TARGET_DEVICE}
On peut enfin formater cette partition partition sur la carte microSD et y copier simplement le contenu du système Debian généré.