GRUB2-Anleitung

Betrifft OS:
FreeBSD

Es gibt etliche Anleitungen für die Benutzung von GRUB2 unter FreeBSD, die aber praktisch alle nicht über die Angabe einer Handvoll Befehle für ein Standard-Setup hinausreichen und/oder noch Altlasten von GRUB1 mitbringen.

In dieser kleinen Anleitung will ich GRUB2 ein wenig genauer unter die Lupe nehmen; GRUB1 werde ich jedoch mit keinem weiteren Wort erwähnen. Auch soll dies nicht eine komplette Installationsanleitung sein mit Partitionierung etc. pp.; dazu könnte man vielleicht den Artikel von User „nukama“ unter dem Titel „MultiBoot mit Grub2“ konsultieren.

Das FreeBSD-Paket beinhaltet eine Info-Datei (info grub), die Dokumentation auf GRUBs Homepage ist ein wenig aktueller; die Dokumentationen von Ubuntu oder Arch Linux sind teilweise sogar noch besser (Ubuntu führt auch *BSD auf), aber natürlich auf die betreffende Linux-Distribution fokussiert.

Die GRUB2-Version in den Ports (1.98_1) ist veraltet und kann den Kernel der neuesten Release (9.1) nicht direkt booten. Sobald ein neuerer Port zur Verfügung steht, werde ich diesen Hinweis entfernen. Vorerst ist nur Chainloading möglich.

Erste Schritte

Zuerst will natürlich der Port installiert sein, und zwar sysutils/grub2.
Wenn man diese Anleitung für einen Linux-basierten GRUB verwenden will, kann man eine Überraschung erleben. Manche Distributionen verwenden gepatchte GRUB-Pakete, denen manchmal die Unterstützung für *BSD und zugehörige Dateisysteme wie UFS einfach fehlt. Nur mit dem Original-GRUB aus dem FreeBSD-Port (oder unter Linux von Hand installiert) ist man auf der sicheren Seite.

GRUB2 gehorcht einer Konfigurationsdatei /boot/grub/grub.cfg (so der Standardpfad), aber um deren Syntax dem unbedarften Nutzer zu ersparen und sie vollautomatisch zu erzeugen, werden Tools bereitgestellt, die sich auf eine sehr einfache Meta-Konfigurationsdatei /usr/local/etc/default/grub stützen. In den gängigen Linux-bezogenen Anleitungen lautet ihr Pfad übrigens /etc/default/grub, das geht in FreeBSD nicht.
Für den Anfang, um überhaupt eine Vorstellung einer grub.cfg zu bekommen, empfehle ich die Verwendung dieser Automatik. Weil der FreeBSD-Port aber doch etwas mit heißer Nadel gestrickt ist, sollte man die so erzeugte grub.cfg dann auf jeden Fall manuell nachbearbeiten.

Meine /usr/local/etc/default/grub sieht so aus:

GRUB_DISTRIBUTOR="FreeBSD Release 9.0"
GRUB_DEFAULT=1
GRUB_SAVEDEFAULT=false
GRUB_TERMINAL=console
#GRUB_GFXMODE=1024x768
#GRUB_FONT_PATH=
GRUB_TIMEOUT=9
#GRUB_HIDDEN_TIMEOUT=5
GRUB_DISABLE_OS_PROBER=false
GRUB_DISABLE_RECOVERY=true
GRUB_INIT_TUNE="2000 128 1 192 1 288 1 432 1 648 1 972 1 1458 1 2187 1"

Es werden nur Variablen gesetzt, die den Skripten im Verzeichnis /usr/local/etc/grub.d dann die Informationen liefern, um geeignete Befehle in die grub.cfg zu schreiben. Das Kapitel „Configuration“ in der Dokumentation listet die möglichen Variablen auf, manche Linux-Distributionen fügen denen noch selbstgestrickte hinzu.

Der Aufruf

grub-mkconfig -o /boot/grub/grub.cfg

erstellt die eigentliche Konfigurationsdatei. Bevor wir uns an deren Nachbearbeitung machen, nun ein paar allgemeine Erklärungen.

Bootkonzepte

Der Bootloader besteht, ähnlich wie der von FreeBSD, aus mehreren Stufen. Die erste paßt in einen 512-Byte-Block (ist sogar noch etwas kleiner, da sie ggf. Platz für eine Partitionstabelle lassen muß), und ihr Sinn und Zweck besteht einzig und allein darin, zu wissen, wo die zweite Stufe liegt und wie sie geladen werden kann. Jene ist typisch 20-30 kB groß, hat als Datei den Namen core.img und kann nicht aus einem richtigen Dateisystem herausgelesen werden, weil der Code der ersten Stufe bei weitem nicht die Intelligenz für ein Dateisystem mitbringt.1)

Installationsmöglichkeiten

Wo findet sich nun Platz für GRUBs core.img? Ein FreeBSD-User hat im wesentlichen drei Möglichkeiten:

  1. in einer eigenen, dedizierten Partition (bzw. Slice) ohne Dateisystem oder sonstigen Inhalt. Dies wird normalerweise bei GPT-partitionierten Festplatten angewandt und ist wohl die sauberste, eleganteste Lösung. Bei Festplatten nach dem MBR-Schema könnte man theoretisch einen eigenen Slice oder sogar eine Unterpartition innerhalb eines BSD-Labels dafür verwenden, aber so etwas habe ich noch nie gesehen.
  2. in dem für Bootcode vorgesehenen Leerraum eines BSD-Labels. Dieser ist mit 64 kiB leicht groß genug; man hat großzügig in die Zukunft geplant, obwohl der FreeBSD-eigene Bootcode ja nur 8 kiB umfaßt.
    Es macht auch keinen Unterschied, ob die ganze Festplatte BSD-partitioniert ist (aka dangerously dedicated) oder ob das BSDsche Partitionierungsschema nur in einen MBR-Slice hineingeschachtelt wurde, was ja bis vor kurzem der Standardweg für eine BSD-Installation war.
  3. im MBR und den folgenden Sektoren einer MBR-partitionierten Festplatte. Dies ist der Standardweg für die Verwendung von GRUB auf MBR-Festplatten. Mit Ausnahme des MBR bleibt nämlich die erste Spur ungenutzt, d. h. wird keinem Slice zugeteilt. Eine Spur umfaßt meistens 63 Sektoren, d. i. ca. 31 kiB. GRUB ist natürlich so gestrickt, daß er die fest verplanten Teile des MBR, Partitionstabelle und Magic number, unangetastet läßt.

Eigentlich kann das Core image in jedem staubigen Winkel einer Datenträgerpartitionierung stehen, sofern es sich dort von vorgeschalteten Bootmanagern nur irgendwie hervorholen läßt.

Bootmöglichkeiten

Und wie genau wird FreeBSD nun von GRUB gebootet? Auch dafür finden sich drei Möglichkeiten:

  1. Chainloading: Wenn der FreeBSD-eigene Bootloader vollständig da ist – also nicht im obigen Fall 2 – ruft GRUB einfach den Slice bzw. die GPT-Partition mit diesem Bootloader auf und legt alles weitere vertrauensvoll in dessen Hände. Das ist der bequemste Weg, und GRUB bedarf keiner FreeBSD-spezifischen Konfiguration.
  2. Übergabe an /boot/loader: Man könnte dies als halbes Chainloading bezeichnen. Stufe 1 und 2 des FreeBSD-Bootloaders werden nicht gebraucht, deshalb verträgt sich diese Methode auch mit dem obigen Fall 2; die eigentliche Arbeit erledigt aber immer noch der „loader“ von FreeBSD.
  3. direktes Laden der Systemdateien: GRUB muß nun die ganzen Aufgaben des FreeBSD-Loaders selbst übernehmen, insbesondere das Verarbeiten der Konfigurationsdateien device.hints und loader.conf. Im einzelnen:
    • den Kernel laden, ggf. mit Kernelparametern z. B. für serielle Konsole;
    • die „früh“ zu ladenden Kernelmodule laden, die sonst in der loader.conf aufgeführt sind, z. B. den NVidia-Grafiktreiber;
    • die device.hints dem Kernel verabreichen;
    • desgleichen für die in der loader.conf aufgeführten Kernel-Umgebungsvariablen und Sysctls.

Methode 3 ist natürlich die komplizierteste; aber weil sie die größte Flexibilität bietet, ist sie diejenige, welche bei Ausführung von grub-mkconfig durch die Skripte des FreeBSD-Ports automatisch eingerichtet wird.

Konfiguration

Wie oben angegeben, sollte man eine Prä-Konfigurationsdatei /usr/local/etc/default/grub anlegen und dann das Shellskript aufrufen:

grub-mkconfig -o /boot/grub/grub.cfg

Anschließend editiert man die erzeugte Datei:

vi /boot/grub/grub.cfg

Dabei ist zu beachten, daß grub-mkconfig die Datei schreibgeschützt anlegt. Entweder entfernt man den Schreibschutz (chmod u+w /boot/grub/grub.cfg) oder überzeugt den Editor beim Abspeichern, sie trotzdem zu überschreiben (in vi :w!).
Auf die vielfältigen Konfigurationsmöglichkeiten von GRUB2 will ich hier nicht eingehen; das Internet ist voll von Linux-Dokumentation. Ich habe u. a. die Teile mit „load_env“ und „save_env“ eliminiert, weil ich von GRUB das immergleiche Verhalten erwarte und mir nicht wohl dabei ist, wenn er bei jedem Boot etwas in eine nicht gemountete Partition schreibt. Die insmod-Befehle sind alle überflüssig, weil GRUB immer von selbst weiß, wann er welches Modul hinzufügen muß. Die locale-Datei, z. B. für „de“, hat im FreeBSD-Port den Pfad /usr/local/share/locale/de/LC_MESSAGES/grub.mo. Sie soll ggf. nach /boot/grub/locale/de/LC_MESSAGES/grub.mo kopiert werden, dgl. für andere Sprachen.
Was ich hier beschreibe, sind nur die FreeBSD-spezifischen menuentry-Abschnitte. Statt des einen, der nun schon automatisch in der grub.cfg angelegt wurde, will ich drei Menüeinträge zeigen, die alle ein und dieselbe FreeBSD-Installation booten, und zwar entsprechend den oben genannten Möglichkeiten 1, 2 und 3.

menuentry "FreeBSD 9.0 (1)" --class freebsd --class bsd --class os {
        echo            Chainloading slice hd0,2 ...
        chainloader     (hd0,2)+1
}
 
menuentry "FreeBSD 9.0 (2)" --class freebsd --class bsd --class os {
        set root=hd0,2,a
        search --no-floppy --set --fs-uuid 42869b9f5cddfbea
        echo                    Loading FreeBSD '"loader"' from $root ...
        kfreebsd                /boot/loader
}
 
menuentry "FreeBSD 9.0 (3)" --class freebsd --class bsd --class os {
        set root=hd0,2,a
        search --no-floppy --set --fs-uuid 42869b9f5cddfbea
        echo                    Loading FreeBSD 9.0 from $root ...
        kfreebsd                /boot/kernel/kernel -v
        kfreebsd_module_elf     /boot/modules/nvidia.ko
        kfreebsd_module_elf     /boot/kernel/portalfs.ko
        kfreebsd_loadenv        /boot/device.hints
        set kFreeBSD.vfs.root.mountfrom=ufs:/dev/ada0s3
        #set kFreeBSD.vfs.root.mountfrom.options=rw
        #set kFreeBSD.hint.acpi.0.disabled="1"
        set kFreeBSD.debug.acpi.disabled="timer acad video"
        set kFreeBSD.kern.hz=100
        set kFreeBSD.hw.pci.do_power_nodriver=3
}

zu (1): Beim Chainloading braucht man keine Variable $root, weil die Angabe von Partition bzw. Slice im Kommando chainloader selbst erfolgen kann.

zu (2): Die Variable $root wird zweimal mit einem Wert belegt, und zwar einmal ausdrücklich und einmal über das search-Kommando. search ist vorzuziehen, sofern man die UUID des Dateisystems weiß, weil sich die genaue Partitionsbezeichnung „hd0,2,a“ bei Änderung der Partitionierung oder Hinzufügen (in Hardware) eines weiteren Laufwerks ändern kann. Der ausdrückliche Wert dient nur als Rückfalloption, falls das search-Kommando versagt.
Die UUID erfragt man mit

grub-probe -t fs_uuid -d /dev/ada0s2

Die Binärdatei /boot/loader wird überraschenderweise mit demselben Kommando geladen wie in (3) auch der FreeBSD-Kernel. Man könnte sagen: /boot/loader wird von GRUB als Kernel betrachtet. Wenn der „loader“ dann läuft, erledigt er alle weiteren Aufgaben: Kernel und Kernelmodule laden, device.hints und loader.conf befolgen etc. Auch das Menü zeigt er an.

zu (3): Beim Laden des Kernels mit kfreebsd können dem Kernel Kommandozeilenoptionen übergeben werden. Folgende werden von GRUB 2.00 erkannt, soweit ich dem Quellcode entnehmen konnte. Weil ich einige davon nicht verstehe, zitiere ich sie mit den englischen Textbausteinen:

"dual"    'D'   "Display output on all consoles."
"serial"  'h'   "Use serial console."
"askname" 'a'   "Ask for file name to reboot from."
"cdrom"   'C'   "Use CD-ROM as root."
"config"  'c'   "Invoke user configuration routing."
"kdb"     'd'   "Enter in KDB on boot."
"gdb"     'g'   "Use GDB remote debugger instead of DDB."
"mute"    'm'   "Disable all boot output."
"nointr"  'n'
"pause"   'p'   "Wait for keypress after every line of output."
"quiet"   'q'
"dfltroot"'r'   "Use compiled-in root device."
"single"  's'   "Boot into single mode."
"verbose" 'v'   "Boot with verbose messages."

Angegeben werden kann die Langform (--verbose, unbedingt mit doppeltem Bindestrich!) oder Kurzform (-v) wie beim Kernel oben.

Die gewöhnlichen Kernelmodule *.ko lädt man mit kfreebsd_module_elf. GRUB will den vollen Verzeichnispfad, daher muß man feststellen, ob ein Modul in /boot/modules oder /boot/kernel steht.
Es gibt auch einen Befehl kfreebsd_module (ohne _elf); ich bin nicht sicher, wozu er dient FIXME, eventuell für nicht ausführbare Module wie Ramdisk-Images o. ä. Er findet normalerweise keine Verwendung.

Mit kfreebsd_loadenv liest man eine Datei mit Umgebungsvariablen für den Kernel ein. Das ist geeignet für device.hints, nicht aber für loader.conf. Jene enthält nämlich neben Umgebungsvariablen bzw. Sysctls noch einige andere Anweisungen:

  • zu ladende Kernelmodule,
  • Anweisungen für den FreeBSD-Bootloader selbst, z. B. bzgl. Menü oder Beastie-Bild,
  • Anweisungen für Kerneloptionen, die GRUB ja direkt übergibt, siehe oben.

Entweder schreibt man die Umgebungsvariablen und Sysctls also in eine separate Datei, die man dann wie die device.hints einliest:

kfreebsd_loadenv /boot/loader.conf.rest

… oder man setzt sie einzeln mit GRUBs set-Befehl. Dazu muß ihnen ein „kFreeBSD.“ (beachte die Großschreibung) vorangestellt werden.
Im Beispiel oben habe ich zweiteres getan mit einigen Einstellungen, die u. a. dem Energiesparen dienen.

Bei verschiedenen Anleitungen im Internet findet sich für die Pfadangaben in kfreebsd und den anderen Befehlen eine Syntax der Art

kfreebsd /foo/bar@/boot/kernel/kernel

Was man dort meist verschweigt, ist, daß der Teil vor dem Zeichen „@“ nur für das Dateisystem ZFS gilt. Ein ZPool enthält i. a. mehrere Verzeichnisbäume, und mit /foo/bar@ wählt man einen bestimmten aus, also: Dataset bar innerhalb von Dataset foo – und dort bitte den Pfad /boot/kernel/kernel!
Für UFS gibt es diese Syntax nicht.

Wenn etwas schiefläuft und das System so schnell rebootet, daß man die Fehlermeldung nicht erkennt, hilft ein Befehl sleep 7 am Ende des menuentry. Damit gewinnt man 7 s Zeit.

Schluß

Ist alles vollständig, so wird der Bootcode installiert mit

grub-install /dev/ada0

… hier z. B. in den MBR – oder mit /dev/ada0s2 in einen Slice anstelle des FreeBSD-Bootcodes. Für GRUB-Versionen vor 2.00 beta2 sollte man die Option --no-floppy angeben, weil sonst auch nach einer Floppy gesucht wird, was die Installation verlangsamt und u. U. sogar blockiert.

Einen Wunsch noch: Könnte jemand dieses HowTo bzgl. der anderen BSDs ergänzen? Was, außer dem einfachen Umschreiben der Befehle (kfreebsd–>knetbsd), ist dafür notwendig?

Wenn man GRUB erst einmal beherrscht, kann man richtig abgehobene Sachen damit machen, z. B. Boot übers Netzwerk oder aus einem Image. Der Grund, GRUB einzusetzen, sind ja gerade die exotischen Setups, die der FreeBSDsche Bootloader nicht kann.

Viel Spaß beim Experimentieren!

1)
Linux-Dateisysteme lassen, im Gegensatz etwa zu FreeBSDs UFS, den ersten Block einer Partition für die erste Stufe eines Bootloaders frei. Dort, statt im MBR, kann die erste Stufe von GRUB untergebracht werden und so eingerichtet, daß sie die Datei core.img über Blocklists aus der Partition herausliest, ohne das Dateisystem zu verstehen. So funktionierte auch der gute alte LILO. Natürlich darf diese Datei dann in Linux nicht verschoben oder irgendwie manipuliert werden, ohne GRUB jedesmal zu aktualisieren, weshalb diese Methode fehleranfällig ist.