Archives par étiquette : Buster

Boot direct EFI sur Debian 10 (EFI stubs)

Contexte

Unified Extensible Firmware Interface (UEFI) est une spécification qui décrit une interface entre le firmware d’une carte mère et un système d’exploitation (OS). Il fait suite à l’Extensible Firmware Interface (EFI), originalement créée par Intel. Elle remplace progressivement le Basic Input Output System (BIOS) fourni à l’origine par les ordinateurs compatibles IBM PC.

Depuis quelques années, le fabricants de cartes mères fournissent des firmware compatibles à la fois avec UEFI et BIOS. Le noyau Linux, quant à lui, fourni les interfaces nécessaires pour être lancé directement par un firmware UEFI. Ce qui fait disparaître le besoin de boot loaders intermédiaires tel que GRUB.

Si vous n’avez pas besoin de ce fameux loader intermédiaire, parce que vous n’avez qu’un seul système d’exploitation par exemple, vous pouvez mettre en place le boot direct par UEFI. Dans ce qui va suivre, je vais décrire comment mettre en place cette configuration.

Configuration

Partition de boot EFI

La première étape à réaliser est de s’assurer que votre système est installé avec une partition de boot EFI lisible par votre firmware. Habituellement c’est une petite partition du disque située au début et formatée avec un système de fichier simple tel que FAT ou EXT2. Cette partition serra utilisée pour stocker le binaires compatibles avec EFI : soit votre GRUB ou le noyau Linux. Durant la phase de boot, l’UEFI va parcourir la partition à la recherche d’un binaire compatible. Voici un exemple du partitionnement sur ma machine :

# lsblk
NAME        MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda           8:0    0 119.2G  0 disk
├─sda1        8:1    0   953M  0 part /boot/efi
└─sda2        8:2    0 118.3G  0 part /

Si vous n’avez pas ce genre de partition, vous devez en créer une, peut être en réinstallant votre système. Mais ceci est en dehors du cadre de cet article, vous pouvez trouver plus d’information à ce sujet ici.

Synchronisation des images

Pour pouvoir démarrer, l’UEFI doit pouvoir accéder à l’exécutable du noyau Linux et à l’image du ramdisk. Ce n’est possible que si ces deux éléments sont présents dans la partition que nous avons mentionné précédemment. Sur une distribution Debian, ces fichiers sont disponibles à la racine du système de fichiers ; nous aurons donc besoin d’un système pour les copier sur la partition EFI à chaque fois qu’ils sont mis à jour. Ainsi, après chaque mise à jour du système, le noyau et son ramdisk seront également mis à jour sur cette partition spéciale.

Sur le wiki de Debian EFI Stubs, la méthode proposée repose sur des scripts post installation pour le noyau et le ramdisk. Malheureusement, cela ne fonctionne plus sur la version Buster car le script post install pour initramfs (ramdisk) n’est plus disponible. Nous ferons donc cette mise à jour en utilisant les services systemd et ses déclencheurs basés sur la surveillance de fichiers. L’idée est de créer un service à exécution unique qui copiera le fichier. Néanmoins, au lieu de lancer le service au démarrage (ou autre évènement régulier), il sera déclenché par un changement sur le fichier que l’on surveille. En l’occurence le noyau et son ramdisk. Nous avons juste à le déclarer, et systemd fera le travail.

Le noyau

Allons-y : on commence par créer le service avec le fichier /etc/systemd/system/uefi-kernel-update.service :

# cat /etc/systemd/system/uefi-kernel-update.service
[Unit]
Description=UEFI Kernel update
After=network.target

[Service]
Type=oneshot
ExecStart=/bin/cp /vmlinuz /boot/efi/EFI/debian/

Ensuite on créé le déclencheur avec /etc/systemd/system/uefi-kernel-update.path :

# cat /etc/systemd/system/uefi-kernel-update.path
[Path]
PathChanged=/vmlinuz

[Install]
WantedBy=multi-user.target

Enfin on active le déclencheur :

# systemctl enable uefi-kernel-update.path
Created symlink /etc/systemd/system/multi-user.target.wants/uefi-kernel-update.path → /etc/systemd/system/uefi-kernel-update.path.
# systemctl start uefi-kernel-update.path

On peut tester que cela fonctionne correctement en faisant un  touch /vmlinuz et en vérifiant dans les logs de systemd que le service a été lancé :

# touch /vmlinuz
# journalctl -xn
Dec 14 18:14:18 fixe-damien systemd[1]: Starting UEFI Kernel update…
 -- Subject: A start job for unit uefi-kernel-update.service has begun execution
 -- Defined-By: systemd
 -- Support: https://www.debian.org/support
 -- A start job for unit uefi-kernel-update.service has begun execution.
 -- The job identifier is 2611.
 Dec 14 18:14:18 fixe-damien systemd[1]: uefi-kernel-update.service: Succeeded.
 -- Subject: Unit succeeded
 -- Defined-By: systemd
 -- Support: https://www.debian.org/support
 -- The unit uefi-kernel-update.service has successfully entered the 'dead' state.
 Dec 14 18:14:18 fixe-damien systemd[1]: Started UEFI Kernel update.
 -- Subject: A start job for unit uefi-kernel-update.service has finished successfully
 -- Defined-By: systemd
 -- Support: https://www.debian.org/support

Le ramdisk (initrd)

Répétons l’opération en créant un service et un déclencheur pour copier le fichier /initrd.img. Les fichiers sont fournis ci-dessous.

# cat /etc/systemd/system/uefi-initrd-update.service
[Unit]
Description=UEFI ignited update
After=network.target

[Service]
Type=oneshot
ExecStart=/bin/cp /initrd.img /boot/efi/EFI/debian/
# cat /etc/systemd/system/uefi-initrd-update.path
[Path]
PathChanged=/initrd.img

[Install]
WantedBy=multi-user.target

Enfin, on active le service et son déclencheur.

# systemctl enable uefi-initrd-update.path
Created symlink /etc/systemd/system/multi-user.target.wants/uefi-initrd-update.path → /etc/systemd/system/uefi-initrd-update.path.
# systemctl start uefi-initrd-update.path

N’oubliez pas de tester que cela fonctionne avec touch et journalctl.

Ajouter une entrée dans l’UEFI

Maintenant que nous sommes sur que le noyau et le ramdisk seront correctement mis à jour, il faut ajouter une entrée dans l’UEFI pour qu’il sache quel noyau lancer et comment.

Premièrement, il faut obtenir la ligne de commande du noyau (les paramètres qui lui sont passés) à partir de la configuration de GRUB. Dans le fichier /boot/grub/grub.cfg, cherchez le bloc menuentry ... { ... } qui correspond à l’entrée que GRUB utilise pour démarrer. Ensuite lisez ce bloc et cherchez la ligne linux /boot/vmlinuz..., elle nous indique les arguments passés au noyau au démarrage. Sur ma machine elle ressemble à ceci :

menuentry 'Debian GNU/Linux' ... {
  ...
  echo   'Loading Linux 4.19.0-6-amd64 ...'
  linux  /boot/vmlinuz-4.19.0-6-amd64 root=UUID=3c51b884-79b9-46f5-aff9-a2d8f68cd308 ro quiet
  echo   'Loading initial ramdisk ...'
  initrd /boot/initrd.img-4.19.0-6-amd64
}

Les arguments du noyau sont root=UUID=3c51b884-79b9-46f5-aff9-a2d8f68cd308 ro quiet. On peut maintenant utiliser efibootmgr pour ajouter une entrée dans l’UEFI, n’oubliez pas de mettre à jour la commande avec vos paramètres :

# efibootmgr -c -g -L "Debian (EFI stubs)" -l '\EFI\debian\vmlinuz' -u "root=UUID=3c51b884-79b9-46f5-aff9-a2d8f68cd308 ro quiet rootfstype=ext4 add_efi_memmap initrd=\\EFI\\debian\\initrd.img"

Si la commande échoue, vous aurez peut être besoin d’ajouter une option telle que : --disk /dev/nvme0n1.

Vous pouvez maintenant vérifier qu’elle a été ajoutée correctement :

# efibootmgr
 BootCurrent: 0008
 Timeout: 1 seconds
 BootOrder: 0008,0000,0009,0003,0001,0002,0004,0005
 Boot0000* debian
 Boot0001* Hard Drive
 Boot0002* UEFI:CD/DVD Drive
 Boot0003* CD/DVD Drive
 Boot0004* UEFI:Removable Device
 Boot0005* UEFI:Network Device
 Boot0008* Debian (EFI stubs)
 Boot0009* debian

La prochaine étape est le redémarrage ! Si tout s’est bien passé, votre machine devrait redémarrer et sauter le boot avec GRUB. Dans le cas où la configuration ne serait pas correcte, vous pourriez ne pas être capable de terminer le démarrage. Si cela arrive, vous pouvez toujours choisir manuellement l’entrée UEFI debian entry. Ainsi vous pourrez résoudre le problème.

Conclusion

Évidemment c’est essentiellement une réussite technique, car le temps passé dans GRUB lors du boot n’est pas excessif. Mais il était important pour moi de consigner cette procédure quelque part car j’utilise cette configuration tous les jours. J’espère qu’elle pourra servir à d’autres.

Par ailleurs, il y a des améliorations possibles. Par exemple, il serait pertinent de régénérer l’entrée dans l’UEFI lorsque la ligne de commande du noyau est mise à jour.

Sources