Debian 9 mit EFISTUB booten
Marvin Gülker · 15.05.2018
Der Debian-Installer installiert auch auf einem UEFI-System noch GRUB als Bootloader, obwohl das nicht nötig ist. Der Artikel zeigt, wie man auch unter Debian 9 mit EFISTUB bootet.
Kategorien: Software
Zwar folgt Debian nicht dem KISS-Prinzip, sondern achtet ganz im Gegenteil darauf, eine möglichst gut aufeinander abgestimmte Software-Auswahl auszuliefern. Dennoch ist es legitim, die auf seinem PC installierte Software auf das notwendige Minimum beschränken zu wollen. Über EFISTUB gab es auf diesem Blog zuletzt vor Jahren einen Artikel, allerdings sind die dort aufgeführten Probleme mit der Implementation von UEFI seitens der PC-Hersteller mittlerweile behoben worden, und wo nicht, hat man in neueren Kernel-Versionen entsprechende Workarounds in den Code aufgenommen. Einer Nutzung von EFISTUB sollte aus heutiger Sicht deshalb nichts mehr im Wege stehen.
Was ist EFISTUB?
UEFI verfügt im Gegensatz zum alten BIOS über moderne Boot-Fähigkeiten, die einen Bootloader überflüssig machen. Dazu legt man auf der sog. EFI-System-Partition, einer kleinen, mit FAT32 formatierten Partition typischerweise am Anfang der Festplatte, sog. UEFI-Executables ab. Anschließend gibt man deren Speicherort nebst etwaigen Boot-Parametern dem UEFI bekannt. Gute UEFI-Implementationen bieten dafür in der Konfigurations-Oberfläche entsprechende Einstellungen an, für schlechtere muß man auf die EFI-Shell, eine Kommandozeile für UEFI, zurückgreifen. Ein dritter Weg, der hier beschritten werden soll, führt über das Programm efibootmgr(8), welches man aber nur aus einem per UEFI gestarteten System aus erfolgreich ausführen kann (also nicht, wenn man den BIOS-Kompatibilitätsmodus benutzt). Unabhängig davon, für welche Variante man sich entscheidet, werden diese Informationen dann im eigenen Speicher des UEFI (NVRAM) abgelegt. Dieser Speicher befindet sich direkt auf der Hauptplatine und nicht etwa auf der Festplatte.
EFISTUB nun bezeichnet die Fähigkeit des Linux-Kernels, ohne Zuhilfenahme von Dritt-Software direkt aus dem UEFI heraus gestartet zu werden. Das bedeutet, daß man den gewünschten Linux-Kernel direkt auf der EFI-System-Partition ablegt und ihn UEFI als Boot-Eintrag bekannt gibt. Anschließend kann man ihn beim Start des Rechners automatisch starten lassen oder im Boot-Menü auswählen. Eine Notwendigkeit für einen dazwischengeschalteten Bootloader besteht in aller Regel nicht. Ausnahmen gibt es, wenn man seinen Kernel nicht auf der EFI-System-Partition ablegen will, da UEFI nur auf diese eine Partition zugreifen kann. Hier muß ein Bootloader zwischengeschaltet werden, der vom UEFI als UEFI-Executable geladen wird. Dieser Bootloader kann dann mit anderen Partitionen umgehen und auch anderswo platzierte Kernels laden. Dafür muß man aber mitnichten den schwergewichtigen GRUB installieren; vielmehr gibt es spezielle Bootloader wie rEFInd oder den mittlerweile — wie könnte es anders sein — von Systemd geschluckten Gummiboot, heute systemd-boot, die eigens für den Einsatz als UEFI-(Nach-)Bootloader konzipiert wurden. Für ein Setup, wie es dieser Artikel beschreibt, sind sie aber aus dem erwähnten Grunde unnötig.
EFISTUB unter Debian einrichten
Vorbereitungen
Sofern noch nicht geschehen, ist zunächst der efibootmgr(8) zu installieren, damit man aus dem laufenden System heraus Zugriff auf den NVRAM nehmen kann.
# apt-get install efibootmgr
Anschließend notiert man sich seine aktuellen Kernel-Start-Parameter.
Diese findet man in der virtuellen Datei /proc/cmdline
:
# cat /proc/cmdline BOOT_IMAGE=../vmlinuz ro initrd=../initrd.img root=/dev/sda2 ro
Die Option BOOT_IMAGE=...
ist wegzulassen, die Option initrd=...
wird weiter unten noch angepaßt.
Kernel-Installations-Skript
Debian legt den Linux-Kernel stets unter /boot/vmlinuz-*
ab. Die
EFI-System-Partition wird dagegen unter /boot/efi
eingebunden. Da wie
erwähnt UEFI nur auf die EFI-System-Partition zugreifen kann, folgt
daraus, daß ein nur unter /boot
gespeicherter Kernel von UEFI nicht
erreicht werden kann. Zur Lösung dieses Problems gibt es zwei
Möglichkeiten.
- Man hängt die EFI-System-Partition direkt unter
/boot
ein. - Man kopiert den Kernel nach
/boot/efi
.
Beide Ansätze führen zu demselben Ergebnis und sind gleichwertig.
Variante 1 macht unter einem typischen Debian-System aber wohl mehr
Arbeit, denn Debian setzt standardmäßig auf eine eigene Partition für
/boot
, die man extra löschen müßte. Vorgezogen wird hier deshalb die
zweite Möglichkeit. Wichtig zu beachten ist noch, daß es nicht genügt,
allein den Kernel selbst zu kopieren. Auch das Initramfs, also das
kleine „Vor-System“, dessen Aufgabe es ist, das Einhängen der
Root-Partition vorzubereiten (was insbesondere bei per LUKS
vollverschlüsselten Systemen wichtig ist) muß kopiert werden.
Es ist sehr fehleranfällig, Kernel und Initramfs jedes Mal, wenn der
Kernel eine Aktualisierung erhält, manuell in ein Verzeichnis unterhalb
von /boot/efi
zu kopieren. Richtigerweise muß dieser Vorgang
automatisiert stattfinden, damit man ihn nicht vergessen kann. Dazu
dient unter Debian das Verzeichnis /etc/kernel/postinst.d
. Dort
abgelegte, ausführbare Skripte werden in alphabetischer Reihenfolge
ausgeführt, nachdem ein neuer Kernel installiert wurde. Jedes Skript
erhält als Kommandozeilenparameter die Version des gerade installierten
Kernels.
Man lege also das folgende Skript
/etc/kernel/postinst.d/zz-copy-to-efi
an. Durch den hohen Buchstaben
z
am Anfang läuft es sehr spät im Installationsprozeß; wichtig ist für
die Zwecke dieses Artikels vor allem, daß es nach dem Skript
initramfs-tools
läuft, denn jenes Skript erzeugt das für den gerade
installierten Kernel passende Initramfs, welches ja wie dargelegt
mitkopiert werden muß.
#!/bin/sh set -e version="$1" echo >&2 "Copying $version kernel and initramfs to EFI partition" cp /boot/vmlinuz-$version /boot/efi/EFI/debefi/VMLINUZ cp /boot/initrd.img-$version /boot/efi/EFI/debefi/INITRD exit 0
APT zeigt während des Installationsprozesses nur die Ausgabe auf dem
Standard-Error-Stream an, nicht diejenige auf der Standardausgabe. Für
informative Meldungen wie oben mit echo
muß deshalb mit >&2
auf den
Standard-Error-Stream umgeleitet werden.
Die übrigen Zeilen des Skripts kopieren zunächst den Linux-Kernel,
danach das Initramfs auf die EFI-System-Partition in das Verzeichnis
EFI/debefi/
. Der Name des Verzeichnisses kann frei gewählt werden.
Beim Namen der Dateien für Kernel und Initramfs selbst ist man zwar
ebenfalls frei, sie sollten aber keinesfalls die Versionsnummer
enthalten. Man müßte sonst nach jeder Aktualisierung des Kernels den
Eintrag im UEFI ändern und auf die neue Versionsnummer anpassen.
Das Skript muß noch ausführbar gemacht werden:
# chmod a+x /etc/kernel/postinst.d/zz-copy-to-efi
Anschließend führt man das Skript aus, um den aktuellen Kernel und sein Initramfs erstmalig auf die EFI-System-Partition zu kopieren.
# mkdir -p /boot/efi/EFI/debefi # /etc/kernel/postinst.d/zz-copy-to-efi `uname -r`
mkdir -p
legt zunächst die nötigen Verzeichnisse an, uname -r
gibt
die Version des laufenden Kernels zurück.
Änderung der UEFI-Boot-Einträge
Mithilfe des Programms efibootmgr(8) kann man nun die Boot-Einträge im NVRAM ändern. Ohne Parameter aufgerufen erhält man eine Liste aller Boot-Einträge, die so oder ähnlich aussehen kann:
# efibootmgr BootCurrent: 0001 Timeout: 0 seconds BootOrder: 0001,0000,000A,0007,0008,0009,000B Boot0000* EFI Shell Boot0001* debian Boot0004 Diagnostic Splash Screen ...
BootCurrent
gibt an, welcher Boot-Eintrag standardmäßig aktiviert
wird. Führt man das Programm mit --verbose
als zusätzlichem Parameter
aus, erfährt man auch, welche UEFI-Executable ausgeführt wird. So kommt
dann heraus, daß der Boot-Eintrag debian
, den der Debian-Installer
anlegt, GRUB lädt, der als grubx64.efi
auf der EFI-System-Partition
abgelegt wurde. Die Zahlen vor dem Namen geben die Nummer des
Boot-Eintrags (als Hex-Zahl) an; diese benötigt man für die Änderung
eines Eintrags.
Zunächst löscht man den bisherigen Eintrag debian
.
# efibootmgr -b 0001 -B
Danach legt man einen neuen Eintrag für EFISTUB an.
# efibootmgr --disk /dev/sda \ --part 1 \ --create \ --label "Debian" \ --loader /EFI/debefi/VMLINUZ \ --verbose \ --unicode 'root=/dev/sda2 ro initrd=/EFI/debefi/INITRD'
efibootmgr(8) ist schlau genug, für den neuen Eintrag die erste freie
Boot-Nummer zu vergeben (im Beispiel ist dies 0001
, die vorher durch
das zwischenzeitlich gelöschte debian
besetzt war).
Die Optionen im Einzelnen:
--disk
gibt an, auf welcher Festplatte sich die EFI-System-Partition befindet. Ist die EFI-System-Partition/dev/sda1
, dann ist dies/dev/sda
.--part
gibt die (1-basierte) Nummer der EFI-System-Partition an. Ist die EFI-System-Partition/dev/sda1
, dann ist dies1
.--label "Debian"
gibt den Namen des Boot-Eintrages an, so wie er im Boot-Menü dargestellt werden soll. Leerzeichen sind zulässig, wenn sie vor der Shell escaped werden.--loader
gibt die UEFI-Executable an. Im Falle von EFISTUB muß dies der Pfad zum kopierten Linux-Kernel auf der EFI-System-Partition sein. Der Pfad ist absolut anzugeben, wobei man als Wurzel aber die EFI-System-Partition selbst annehmen muß. Es handelt sich hierbei um den vom oben erstellten Postinstall-Skript kopierten Kernel.--verbose
gibt einige zusätzliche Diagnosen aus.--unicode
teilt efibootmgr(8) mit, daß jetzt die Boot-Parameter für die UEFI-Executable folgen und daß diese in UTF-16 zu kodieren sind.
Am Schluß folgen die Boot-Parameter. Im Falle von EFISTUB werden diese
an den Linux-Kernel übergeben, d.h. es handelt sich um die
Kernel-Parameter. Diese wurden oben bereits aus /proc/cmdline
entnommen und müssen hier wieder angegeben werden. Hinzu kommt ein
geänderter Parameter initrd
, der dem Kernel mitteilt, von wo er das
(ebenfalls durch obiges Skript kopierte) Initramfs zu laden hat. Auch
dieser Pfad ist absolut anzugeben, wobei ebenso wie beim Kernel als
Wurzel die EFI-System-Partition anzunehmen ist.
Nun kann man noch einmal kontrollieren, ob alles so aussieht, wie man es erwartet:
# efibootmgr --verbose BootCurrent: 0001 Timeout: 0 seconds BootOrder: 0001,0000,000A,0007,0008,0009,000B Boot0000* EFI Shell HD(1,GPT,...uuid...,0x800,0x40000)/File(\EFI\shellx64_v2.efi) Boot0001* Debian HD(1,GPT,...uuid...,0x800,0x40000)/File(\EFI\debefi\VMLINUZ)r.o.o.t.=./.d.e.v./.s.d.a.2. .r.o. .i.n.i.t.r.d.=./.E.F.I./.d.e.b.e.f.i./.I.N.I.T.R.D. Boot0004 Diagnostic Splash Screen FvFile(...uuid...) ...
GRUB entfernen
Zum Schluß kann der nicht mehr benötigte GRUB einschließlich seiner erzeugten Dateien entfernt werden.
# apt-get remove grub-common # rm -r /boot/grub # find /boot/efi -iname '*grub*' -print -delete
Wer sichergehen will, daß jetzt auch wirklich alles auf demselben Stand ist, reinstalliert den Kernel und stößt damit alle Postinstall-Skripte noch einmal an.
# apt-get install --reinstall linux-image-`uname -r`
Schluß
Selbst mit einer minimalen Installation von Debian erhält man mit GRUB Software, die man jedenfalls auf einem UEFI-System nicht benötigt. Durch die Umstellung auf EFISTUB erhält man eine saubere, moderne Lösung, die insgesamt weniger fehleranfällig sein dürfte als der hochkomplexe GRUB.