II. Cross-compilation: Initrd/Initramfs

Il faut préciser que l’installation d’un système linux sur un serveur NAS Buffalo LS-XL pose quelques contraintes, qui ne sont pas des moindre: Il n’y a ni port USB, ni lecteur CD dessus. On ne va compter que sur le port ethernet, sur lequel il va falloir donc tout faire.
De plus, pour compliquer un peu plus l’installation, le chargeur de démarrage (bootloader, U-Boot pour les systèmes embarqués) n’offre aucune possibilité de gérer les séquences de boot. Démarrer son système à partir du disque dur interne est le SEUL choix offert.
Par contre, en l’absence de disque dur, le serveur essaie tout de même de démarrer le système à partir des informations de la mémoire RAM, plus précisément, dans le ramdisk (disque virtuel). Et c’est là où se repose l’astuce. Nous allons utiliser cette “faille” pour y installer temporairement dans la RAM, un système linux qui va prendre en charge l’installation du système d’exploitation (ici, un Debian). C’est exactement le même procédé qui est utilisé par les live Cds.
Le système chargé dans le ramdisk se lance avec le stricte minimum, met en cache les programmes dont il a besoin (ls, cp, mount, etc.), et tout ça, sans avoir besoin d’accéder à un disque dur physique. Par contre, il faut faire attention à mettre tout le système en mémoire sinon, on aurait un kernel panic en l’absence de disque dur.

Pour rappel, un système sous linux a besoin de deux chose pour pouvoir se lancer:
– Un noyau (uImage)
– Un système de fichier root (rootfs, ou /) contenant le programme init.

Grâce à l’image initrd (INITial Ram Disk), le bootloader peut l’utiliser pour charger tous les outils nécessaire au ramdisk afin d’y monter le rootfs. Il est important de noter que l’ajout ou la suppression de pilotes (drivers), compilés en modules (LKM), dans Initrd ne nécessite pas de compilation du noyau.

Pour mieux comprendre l’intérêt de l’initrd lors du boot, je vous ai copié un extrait de la documentation de initrd.txt.

When using initrd, the system typically boots as follows:

1) the boot loader loads the kernel and the initial RAM disk
2) the kernel converts initrd into a « normal » RAM disk and
frees the memory used by initrd
3) if the root device is not /dev/ram0, the old (deprecated)
change_root procedure is followed. see the « Obsolete root change
mechanism » section below.
4) root device is mounted. if it is /dev/ram0, the initrd image is
then mounted as root
5) /sbin/init is executed (this can be any valid executable, including
shell scripts; it is run with uid 0 and can do basically everything
init can do).
6) init mounts the « real » root file system
7) init places the root file system at the root directory using the
pivot_root system call
8) init execs the /sbin/init on the new root filesystem, performing
the usual boot sequence
9) the initrd file system is removed

Note that changing the root directory does not involve unmounting it.
It is therefore possible to leave processes running on initrd during that
procedure. Also note that file systems mounted under initrd continue to
be accessible.

Traduction

Quand on utilise initrd, la procédure de boot type du système est la suivante:

1) le bootloader charge le noyau et l’initrd
2) le noyau convertit l’initrd en disque RAM “normal” et libère la mémoire occupée par initrd
3) si le périphérique root n’est pas dans /dev/ram0, l’ancienne procédure (obsolète) change_root est suivie
4) le périphérique racine (root) est monté. Si c’est /dev/ram0, alors l’image initrd est monté comme root
5) /sbin/init est exécuté
6) init monte (mount) le “vrai” système de fichier racine
7) init place le système de fichier root comme répertoire racine
8) init exécute /sbin/init dans le nouveau système de fichier, en performant la séquence de boot habituelle
9) le système de fichier initrd est supprimé

Noter que le changement de répertoire racine ne nécessite pas de le démonter (unmount). Il est donc possible de laisser les processus dans initrd tourner durant toute la procédure. A noter également que les systèmes de fichiers montés sous initrd continuent à être accessible.

Ce qu’il faut retenir de ce texte est que, l’initrd est le rootfs (système de fichier) temporaire.

1. Téléchargement de l’installeur initramfs pour debian

Télécharger le fichier initrd.buffalo, puis le renommer en orig_uImage.

/opt/nasbuild# mkdir initramfs

/opt/nasbuild# cd initramfs/

/opt/nasbuild/initramfs# wget _http://http.debian.net/debian/dists/unstable/main/installer-armel/current/images/orion5x/network-console/buffalo/ls-chl/initrd.buffalo -O orig_uImage
!—–Tronquée—–!
orig_uImage 100%[=====================>] 6.58M 3.09MB/s in 2.1s

2015-10-16 19:16:51 (3.09 MB/s) – ‘orig_uImage’ saved [6897551/6897551]

2. Initrd/initramfs

Apparement – Je n’ai pas vérifié cette information en faisant des tests de mon côté, mais je la prends comme argent comptant – le serveur NAS ne peut pas booter correctement avec l’image fournie par Debian (initrd.buffallo), qui est une image initramfs. Il nous faut donc convertir cette image en image initrd.

Etape 1. Extraction du contenu de l’initramfs

L’image initramfs est une image compressée de l’archive cpio au format bzip ou gzip. La commande “file” permet de donner sous quel méthode de compression a été utilisée.

/opt/nasbuild/initramfs$ ls
orig_uImage
/opt/nasbuild/initramfs$ file orig_uImage
orig_uImage: u-boot legacy uImage, debian-installer ramdisk, Linux/ARM, RAMDisk Image (gzip), 6897487 bytes, Fri Sep 11 01:02:57 2015, Load Address: 0x02000000, Entry Point: 0x02000000, Header CRC: 0xFCFF08CF, Data CRC: 0x6B9B8156

#Extraire l’image gzippé de l’initramfs
/opt/nasbuild/initramfs# dd if=orig_uImage ibs=64 skip=1 | zcat > orig_initramfs.cpio

La commande dd est utilisée pour dupliquer le fichier d’origine orig_uImage, mais sans le header. Pour en savoir un peu plus sur la commande dd, je vous renvoie à cet article, puis celle-là.

Les options “ibs=64” et “skip=1” suppriment tout ce qui se trouve sur les 64 premiers bits de ce fichier.

La commande « zcat », tout comme « gunzip -c », décompresse le fichier sans supprimer le fichier d’origne.
Pour résumer, l’image initrd.buffalo (uRamdisk) est une archive cpio compressée au format gzip, et contient:
– Un header (64 premiers octets)
– Une image du ramdisk

Quand on parle de “kernel header” (ou tout simplement, “header”), on fait référence au code source du noyau. C’est ce dont on a besoin pour (re)compiler n’importe quel module.
Petite parenthèse, vous avez certainement entendu parlé de header lors de la virtualisation, avec des problèmes de “header not found” par exemple.
Ce header n’étant pas nécessaire pour démarrer notre système, on peut donc s’en passer en le supprimant de l’image orig_initramfs.cpio.

Etape 2. Création et montage du RAMDISK

Il nous faut maintenant créer un ramdisk (disque virtuelle) initrd, de taille fixe (bloc) égale à 20 Mo dans la mémoire (RAM). Initrd sera donc contenu dans un bloc. On parlera de “block storage”, qu’il faut formater avec un système de fichier (ext2, le système de fichier par défaut pour Initrd) avant de pouvoir le monter. Mkfs est l’outil qu’il nous faut pour le faire.

# Creation du ramdisk de 20 Mo
/opt/nasbuild/initramfs# dd if=/dev/zero of=initrd bs=1M count=20
/opt/nasbuild/initramfs# /sbin/mkfs.ext2 initrd
/opt/nasbuild/initramfs# ls -l
total 24092
-rw-r–r– 1 root root 20971520 Nov 4 23:08 initrd
-rw-r–r– 1 root root 16925184 Nov 4 23:04 orig_initramfs.cpio
-rw-r–r– 1 root root 6897551 Sep 11 01:02 orig_uImage

“A l’origine, l’initrd ou initramfs a essentiellement pour but d’accomplir les opérations préalables au montage du système de fichiers racine, notamment en chargeant les modules nécessaires s’ils ne sont pas compilés en dur dans le noyau. Cela permet de créer un noyau polyvalent mais léger sans mettre tous les pilotes en dur”. (Lu sur le site debian-fr.org démarrer sans image initramfs)

Pour rappel, initrd est une image de disque, compressée puis gzippée, de type bloc. Un des problèmes que ce type d’image peut poser est, l’espace mémoire inutilisé par le ramdisk ne peut être assigné ailleurs. De plus, si le disque virtuel est trop plein, bien qu’il y a encore assez d’espace sur l’ensemble de la RAM, il n’est pas possible d’augmenter la taille du ramdisk sans avoir à le reformater, puis refaire le disque. Il faut donc bien prévoir à l’avance la taille du disque virtuel pour éviter ce genre de problème.
Pour information, l’image du rootfs doit absolument être de taille inferieure à la RAM, à laquelle on soustrait la taille de la mémoire qu’occupe le système en pleine charge. Il faut garder en mémoire cette règle, surtout quand on essaie d’installer un système linux sur un système embarqué ayant très peu de mémoire.
Ce qu’il faut aussi savoir, c’est que, primo, initramfs est le successeur de initrd depuis la version 2.6 du noyau Linux. Initramfs est une archive de type cpio, et non de type bloc comme pour initrd.
Secondo, initrd sert en tant que système de fichier principal temporaire monté dans la RAM, avec le noyau, le couple initrd/noyau permet de démarrer un système sans avoir à monter un disque physique.

Si vous voulez savoir un peu plus la différence entre initramfs et initrd, je vous propose de lire cette article puis celle là.

# montage du ramdisk
/opt/nasbuild/initramfs# mkdir rootfs
/opt/nasbuild/initramfs# mount initrd rootfs

Pour avoir la liste des fichiers montés, je vous propose les commandes “lsblk” (LiSt BLock device: disque dur, CDRom, etc.) puis “fdisk -l”.
~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 465.8G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 10M 0 part
├─sda3 8:3 0 18.7G 0 part /
├─sda4 8:4 0 7.7G 0 part [SWAP]
└─sda5 8:5 0 439.3G 0 part /home
sr0 11:0 1 1024M 0 rom
loop0 7:0 0 20M 0 loop /opt/nasbuild/initramfs/rootfs

# fdisk -l
Disk /dev/sda: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 21D716B8-4FA6-41C3-8441-0B1B39B326FC

Device Start End Sectors Size Type
/dev/sda1 2048 4095 2048 1M BIOS boot
/dev/sda2 4096 24575 20480 10M Linux filesystem
/dev/sda3 24576 39282687 39258112 18.7G Linux filesystem
/dev/sda4 39282688 55492607 16209920 7.7G Linux swap
/dev/sda5 55492608 976771071 921278464 439.3G Linux filesystem

Disk /dev/loop0: 20 MiB, 20971520 bytes, 40960 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Etape 3. Extraction du contenu de initramfs

Il faut ensuite extraire le contenu du fichier orig_initramfs.cpio, qui pour rappel, est une image initramfs. Puis placer le fichier extrait dans le ramdisk de taille 20 Mo, formaté en EXT2.

#extraire le fichier initramfs gzippé et le placer dans le ramdisk
/opt/nasbuild/initramfs# cd rootfs
/opt/nasbuild/initramfs/rootfs# cpio -idv < ../orig_initramfs.cpio
!—–Tronquée—–!
33057 blocks

Il y a trois modes (option) pour cpio. Celui qui nous intéresse ici est l’option -i. Cette option offre la possibilité de créer des archives à partir de nos fichiers, que l’on placera ensuite dans le rootfs qui est notre système de fichier.
L’option -v (verbose, ou bavard en français), offre la possibilité au cpio d’afficher à l’écran ce qui se passe. On devrait voir les informations sur l’extraction, l’archivage, et la copie du fichier orig_initramfs.cpio.
Il faut rajouter l’option -d au cpio, puisque par défaut, cpio ne crée pas de dossier. Il faut donc le lui dire d’en créer un, si c’est nécessaire.

Voyons maintenant le contenu du dossier rootfs.

/opt/nasbuild/initramfs/rootfs# ls
bin etc initrd lost+found mnt run sys usr
dev init lib media proc sbin tmp var

Etape 4. Mise à jour des modules

Durant cette étape, nous allons remplacer tous les modules du rootfs par ceux créés avec le noyau dans la première partie du chapitre.

/opt/nasbuild/initramfs/rootfs# rm -rfv lib/modules/*
/opt/nasbuild/initramfs/rootfs# cd ../../linux-3.3.4/
/opt/nasbuild/linux-3.3.4# make INSTALL_MOD_PATH=../initramfs/rootfs/ modules_install
/opt/nasbuild/linux-3.3.4# cd ../initramfs/rootfs/

Après avoir supprimé les modules de l’initramfs, la commande MAKE nous permet de compiler de nouveaux modules, basés sur le noyau construit dans le chapitre précédent.

Etape 5. Configuration automatique

Il nous faut maintenant créer un fichier préconfiguré (preseed.cfg) dans le dossier rootfs, afin d’automatiser une partie de l’installation.

/opt/nasbuild/initramfs/rootfs# cat << EOF > preseed.cfg
d-i lowmem/low note

d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_domain string unassigned-domain

d-i network-console/password password rootme
d-i network-console/password-again password rootme
EOF

/opt/nasbuild/initramfs/rootfs# ls
bin etc initrd lost+found mnt proc sbin tmp var
dev init lib media preseed.cfg run sys usr

Il nous fallait ce script pour activer l’installation à distance via le service SSH. Vous pouvez modifier le mot de passe rootme dans ce fichier, par un autre de votre choix. Ce mot de passe vous sera demandé lors de la connection à l’installeur de Debian.

Etapte 6. Démonter (unmount) le système de fichier et empaquetter l’initrd

/opt/nasbuild/initramfs/rootfs# cd ..

# finalisation
/opt/nasbuild/initramfs/# umount rootfs

~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 465.8G 0 disk
├─sda1 8:1 0 1M 0 part
├─sda2 8:2 0 10M 0 part
├─sda3 8:3 0 18.7G 0 part /
├─sda4 8:4 0 7.7G 0 part [SWAP]
└─sda5 8:5 0 439.3G 0 part /home
sr0 11:0 1 1024M 0 rom

/opt/nasbuild/initramfs/# gzip initrd

Il ne nous reste plus qu’à générer la nouvelle image ramdisk, avec la commande mkimage.

#Générer une image Ramdisk initrd.buffalo
/opt/nasbuild/initramfs/# mkimage -A arm -O linux -T ramdisk -C gzip -a 0x0 -e 0x0 -n initrd -d initrd.gz initrd.buffalo

Et voilà, notre nouvel image initrd, en remplacement de initramfs, est enfin prêt.

Résumé des différentes étapes de la création de la création d’initrd

#Telechager l’image initrd.buffalo
/opt/nasbuild# mkdir initramfs
/opt/nasbuild# cd initramfs/
/opt/nasbuild/initramfs# wget _http://http.debian.net/debian/dists/unstable/main/installer-armel/current/images/orion5x/network-console/buffalo/ls-chl/initrd.buffalo -O orig_uImage

#Copier l’image sans le header
/opt/nasbuild/initramfs# dd if=orig_uImage ibs=64 skip=1 | zcat > orig_initramfs.cpio

#Creer un ramdisk de 20 Mo
/opt/nasbuild/initramfs# dd if=/dev/zero of=initrd bs=1M count=20
/opt/nasbuild/initramfs# /sbin/mkfs.ext2 initrd

#Monter le ramdisk
/opt/nasbuild/initramfs# mkdir rootfs
/opt/nasbuild/initramfs# mount initrd rootfs

#Copier l’archive dans le systeme de fichier rootfs
/opt/nasbuild/initramfs# cd rootfs
/opt/nasbuild/initramfs/rootfs# cpio -idv < ../orig_initramfs.cpio

#Supprimer les modules du rootfs
/opt/nasbuild/initramfs/rootfs# sudo rm -fr lib/modules/*

# Copier les modules construit avec le noyau
/opt/nasbuild/initramfs/rootfs# cd ../../linux-3.3.4/
/opt/nasbuild/linux-3.3.4# sudo make INSTALL_MOD_PATH=../initramfs/rootfs/ modules_install

/opt/nasbuild/linux-3.3.4# cd ../initramfs/rootfs/

#Créer le fichier preseed.cfg avec les informations ci-dessous
/opt/nasbuild/initramfs/rootfs# cat << EOF > preseed.cfg
d-i lowmem/low note

d-i netcfg/get_hostname string unassigned-hostname
d-i netcfg/get_domain string unassigned-domain

d-i network-console/password password rootme
d-i network-console/password-again password rootme
EOF

#Démonter le système de fichier en empaqueter le nouveau initrd
/opt/nasbuild/initramfs/rootfs# cd ..

#Finalisation
/opt/nasbuild/initramfs/# sudo umount rootfs
/opt/nasbuild/initramfs/# gzip initrd

#Générer la nouvelle image Ramdisk initrd.buffalo
/opt/nasbuild/initramfs/# mkimage -A arm -O linux -T ramdisk -C gzip -a 0x0 -e 0x0 -n initrd -d initrd.gz initrd.buffalo

Sources:

Ce que je trouve génial sur la documentation de linux, c’est qu’il n’y a pas de date de péremption. Certe, certains documents datent un peu, mais on peut toujours les accomoder à nos besoins.

[Debian] Programme en cache
[Debian] Création d’un initramfs
What is the difference between initrd and initramfs?
https://wiki.debian.org/fr/Initrd
Hacking RAM disks
What is ramfs?
[Debian] démarrer sans image initramfs
How to View, Modify and Recreate initrd.img
How to create your own initial ram disk (initrd)
Chapter 1. Linux Filesystem Hierarchy – 1.8. /initrd
What is Initrd Image ? How To Create Initrd Image In Linux
initrd, modules, and tools
GNU Make
HowTo Repack root filesystem (ramdisk)
Tuning dd block size
What Are Kernel Headers?
Forum Linux.noyau comprendre le lien entre les headers du noyau et les modules