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.