Im Rahmen der Idee, einen Raspberry Pi 5 mit einem PCI-HAT zu betreiben, um so eine externe Grafikkarte zu verwenden, musste ich auch den Linux-Kernel neu kompilieren. Gleich zu Anfang kann ich sagen, dass dieses Experiment nicht neu ist, aber in meinem Fall leider nicht von Erfolg gekrönt war. Zwar wird aktuell die NVIDIA Geforce GT 730 vom Raspberry Pi OS erkannt, aber beim Starten wichtiger Prozesse kommen sogenannte Panik-Meldungen, da Module aus dem Kernel Probleme verursachen.
Was ist der Linux-Kernel und warum ist er so wichtig?
Aber was ist der Linux-Kernel, warum ist er auf dem Pi 5 und anderen Linux-Distributionen so wichtig? Warum brauche ich für meinen Test einen anderen Kernel als den, der von der Raspberry Pi Foundation ausgeliefert wurde? Und wie kann ich mir selbst einen Kernel für den Raspberry Pi kompilieren? All diese Fragen möchte ich heute einfach erklären, sodass man z.B. auf einem Pi 5 einen aktuelleren Kernel verwenden kann.
Hardware-Voraussetzung
Tatsächlich benötigt dieser Blogbeitrag nicht viel an Hardware! Einen Raspberry Pi 5 auf dem Schreibtisch mit einer großen SD-Karte oder besser einem großen USB3.0-Stick reicht an der Stelle vollkommen aus. Das Thema Cross-Compiling, also mit einem anderen System einen Kernel für den Pi 5 bereitstellen, werde ich an der Stelle nicht behandeln. Der Pi 5 ist mittlerweile so leistungsstark, dass die paar Minuten mehr an Zeit für Kompilieren durchaus vertretbar sind.
Solltest du hingegen mit einem älteren Raspberry Pi arbeiten, so musst du an diversen Stellen aufpassen, was ich dann in diesem Blog näher erläutern werde.
Hinweis zu diesem Thema
Jedes gängige OS für den Raspberry Pi wird mit einem entsprechenden Kernel ausgeliefert. In der Regel braucht es also kein manuelles kompilieren dafür, da über die regulären Updates immer mal wieder neue Versionen vom Kernel installiert werden. Wer aber viel experimentiert oder z.B. das GitHub-Repository für die Raspberry Pi Kernel-Entwicklung verfolgt, der findet schnell mal einige interessante Features oder Bug-Fixes. Teilweise werden auch schon neuere Versionen vom Kernel entwickelt, z.B. hat der aktuell ausgelieferte Kernel für den Raspberry Pi die Version 6.6 (Stand Oktober 2024), es gibt aber schon Weiterentwicklungen zu Version 6.12.
Gleichzeitig verweise ich an vielen Stellen auf die offizielle Dokumentation der Raspberry Pi Foundation, da hier viel erklärt wird. Gerade hier sollte die englische Sprache sitzen und man sollte wissen, was man tut.
Solltest du den Raspberry Pi produktiv nutzen, dann empfehle ich dir, vorher ein BackUp von deinem gesamten USB-Stick oder SD-Karte zu machen. Sollte beim Kompilieren oder Kopieren vom Kernel etwas schief gehen, dann kann der Pi 5 eventuell nicht mehr starten. Wichtig an der Stelle ist dann der Inhalt der FAT32-Partition, auf dem alle boot-Dateien abgelegt sind!
Der Kernel
Sucht man im Internet nach Kernel, so findet man zu diesem Begriff viele Definitionen. Interessant ist aber an der Stelle die Definition für das Betriebssystem, da dieser ja auch später von uns neu kompiliert und verwendet wird. Der Kernel selbst ist der zentrale Baustein eines Betriebssystems. Er legt die Prozess- und Datenorganisation fest, auf dem das Betriebssystem später aufbaut. Der Kernel ist dabei das Bindeglied zwischen Software und Hardware. Der Kernel selbst besteht wiederum aus sogenannten Schichten, die später dann für den reibungslosen Betrieb notwendig sind.
Im Kernel sind grundlegende Funktionen verankert, es können aber auch Module für z.B. die Grafikkarte integriert werden.
In der Regel besteht ein Kernel aus den folgenden Schichten:
- Schnittstelle zur Hardware (Geräte, Speicher, Prozessoren)
- Speicherverwaltung (evtl. einschließlich virtuellem Hauptspeicher)
- Prozessverwaltung (auch Scheduler genannt)
- Geräteverwaltung (auch Device Management genannt)
Dateisysteme
Eine weitere Frage wird nun so, wo wird der Kernel den nun eingesetzt? Tatsächlich hat jedes Betriebssystem einen Kernel. Egal ob Linux, Windows, MAC, IOS oder Android, all diese Betriebssysteme haben einen Kernel. So gesehen nutzen Sie täglich einen Kernel, ohne es überhaupt zu merken.
Ein interessanter Fakt ist, dass es auch MicroController gibt, die mit einem Kernel arbeiten könnten. Auch gibt es Projekte, die auf MicroControllern einen Linux-Kernel zum Laufen bekommen haben. Der Einsatz ist höchst fraglich in diesem Fall, zeigt aber auch, wie vielfältig der Linux-Kernel einsetzbar ist.
Tatsächlich, wenn von Linux gesprochen wird, ist im Allgemeinen vom Linux-Kernel die Rede, welcher dann von einem Linux-Betriebssystem verwendet wird.
Wer mehr zu dem Thema Kernel sucht oder Interesse an diesem Thema hat, kann sich hier oder speziell für den Linux-Kernel hier einlesen.
Die Grundvoraussetzung zum Kernelbau schaffen
Um den Kernel kompilieren zu können, sind einige Bibliotheken und Programme nötig. Teilweise sind diese ggf. schon vorhanden, aber zunächst sollte man den Pi 5 auf den aktuellen Stand bringen, Code 1 entsprechend in einer Kommandozeile ausführen.
sudo apt update && sudo apt dist-upgrade -y
Code 1: Den Pi 5 auf den aktuellen Stand bringen
Als nächstes brauchen wir einige Bibliotheken und Programme. Gerade damit wir später den Kernel kompilieren können, wird „make“ benötigt. Damit wir den Quellcode vom Kernel kompilieren können, braucht es das Tool „git“. Code 2 installiert in diesem Fall alles, was gebraucht wird.
sudo apt install bc bison flex libssl-dev make git libncurses5-dev
Code 2: Die nötigen Bibliotheken und Programme runterladen
Zuletzt braucht es noch den Quellcode vom Linux-Kernel, der später dann auf unsere Bedürfnisse kompiliert wird. An der Stelle bevorzuge ich immer einen Unterordner im Home-Verzeichnis anzulegen, damit nicht so viel Chaos auf der Platte entsteht,
mkdir ~/build && cd ~/build
Code 3: Im Home-Verzeichnis vom aktuell angemeldeten User einen neuen Ordner anlegen und dahin wechseln
Als nächstes holen wir von GitHub den aktuellen Quellcode vom Kernel, siehe Code 4.
git clone --depth=1 https://github.com/raspberrypi/linux
Code 4: Kernelquellcode runterladen
An dieser Stelle nun der besondere Part. Mit dem Befehl aus Code 4 wird der aktuelle Kernel-Quellcode für die Version 6.6 runtergeladen. Das liegt daran, dass dieser branche, also der Arbeitszweig, als der aktuelle Standard in GitHub definiert wurde. Willst du hingegen einen anderen branche verwenden, so musst du mittels Code 5 und statt einer legitimen Version, den von dir gewollten branche runterladen.
git clone --depth=1 --branch https://github.com/raspberrypi/linux
Code 5: Einen anderen Branche vom Kernel runterladen
Falls du nicht weißt, welche branches es gibt, so hilft ein kurzer Blick auf GitHub, siehe Abbildung 1.
Abbildung 1: Über das Menü die branches einsehen
Ich kann hier gleich sagen, man kann hier alle branches runterladen, die man möchte, jedoch würde ich erst einmal die aktuelle branch-Version verwenden. Einen anderen branche als der aktuell unter GitHub definierte Standard hat immer den Nachteil, dass es zu Problemen während dem Betrieb kommen kann oder aber der Raspberry Pi nicht bootet.
Auch bei anderen Linux-Distributionen kann es bei der Kompilierung des eigenen Kernels zu solchen Problemen kommen.
Das Kopieren des Kernels von GitHub dauert einen kurzen Moment, was aber auch kein Wunder ist, da das GitHub-Repository in für den Standard schon 1,8GB an Platz in Anspruch nimmt, siehe Abbildung 2.
Abbildung 2: Das einfache Repository hat schon 1,8Gb
Nun muss in den Ordner linux gewechselt werden, siehe Code 6. Du kannst an der Stelle gerne einmal in den Ordnern stöbern, da hier alle Grundfunktionen vom Kernel enthalten sind.
cd ~/build/linux
Code 6: In das korrekte Verzeichnis wechseln
Wichtig an der Stelle, es dürfen keine Dateien modifiziert oder verschoben werden. Andernfalls wird der spätere Kernelbau nicht möglich sein, da die Abhängigkeiten nicht mehr passen oder der Quellcode nicht funktionsfähig ist.
Die Kernelkomponenten konfigurieren und Kernel bauen
Bevor nun das Kompilieren beginnt, muss erst einmal definiert bzw. konfiguriert werden, was genau gebaut werden soll. Dafür wird eine sogenannte .config-Datei angelegt, der den Grundstein fürs Kompilieren bildet. In der Regel schreibt man diese von Hand, aber durch einen kleines Tool ist dies mit einer grafischen Oberfläche möglich.
Zunächst legen wir einen Parameter fest, der später beim Kopieren vom Kernel gebraucht wird, siehe Code 7. Dieser Parameter ist nach einem Neustart vom Raspberry Pi nicht mehr vorhanden, da nicht dauerhaft gespeichert.
KERNEL=kernel_2712
Code 7: Parameter KERNEL festlegen
Je nach Raspberry Pi – Modell wird ein anderer Wert für den Parameter benötigt, daher hilft an der Stelle ein Blick in die Dokumentation unter natively build. Zudem muss die Architektur, also 32bit oder 64bit, von dem Raspberry Pi – Modell beachtet werden. Ein Pi 5 kann sowohl ein 32bit oder 64bit-Kernel verwenden, ich empfehle hier aber ganz klar die 64bit-Architektur. Jedoch kann ein Raspberry Pi 2 und jünger nur die 32bit-Kernel nutzen.
Jetzt muss definiert werden, was genau gewollt ist. Vereinfacht gesagt legen wir fest, welche Module und Grundfunktionen gewollt sind, siehe Code 8. Das kann man sich an der Stelle so vorstellen, wie eine Backmischung für den Kernel.
make bcm2712_defconfig
Code 8: Festlegen der Kernelgrundparameter
Im Grunde schreibt damit make die gewollte .config mit allen Modulen und Grundbausteinen für den Bau des Kernels. Doch damit wird der Kernel noch nicht gebaut, sondern es kann noch weiter definiert werden, was gewollt ist. Über Code 9 können weitere Modifikationen vorgenommen werden, wobei uns eine grafische Oberfläche alles erleichtert, jedoch die bisherigen Einstellungen mit übernommen und angezeigt werden.
make menuconfig
Code 9: Über grafische Oberfläche weitere Einstellungen vornehmen
Sofern du alles richtig gemacht hast, sollte nun die grafische Oberfläche erscheinen, siehe Abbildung 3.
Abbildung 3: Die grafische Oberfläche für .config
Unter dem Menü-Punkt General setup -> Local version sollte zunächst mal ein anderer Name als den Standardnamen für den Kernel, verwendet werden. Das hilft später bei der Identifikation, um herauszufinden, welcher Kernel gerade läuft. Ich persönlich bevorzuge hier einen Namen, bei dem ich weiß, wofür der Kernel genau gebaut wurde.
Stöbern Sie ruhig durch die vielen Optionen, dass anschließende Speichern via Save sollte nicht vergessen werden, da der Kernel sonst nicht mit den ausgewählten Modulen oder Grundbausteinen gebaut werden würde.
Über Code 10 (64bit) oder Code 11 (32bit) wird nun der Bau, also das Kompilieren, des Kernels angestoßen.
make -j6 Image.gz modules dtbs
Code 10: Kernel für 64bit Architektur bauen
make -j6 zImage modules dtbs
Code 11: Kernel für 324bit Architektur bauen
An der Stelle ist nun Geduld gefragt und auch gefordert, weil das Kompilieren einiges an Zeit in Anspruch nimmt, je nach System. Interessant ist, was im Terminal angezeigt wird, siehe Abbildung 4.
Abbildung 4: Terminalausgabe während dem Kompilieren
Nach knapp einer halben Stunde, gemessen am Raspberry Pi 5, sollte das Teminal dir wieder die Eingabeaufforderung anzeigen, siehe Abbildung 5. Solltest du stattdessen Fehlermeldungen oder Warnungen sehen, dann ist beim Kompilieren grundlegend was schief gegangen und ggf. musst du den Quellcode noch einmal runterladen und die Befehle durchgehen, um den Kernel erneut zu kompilieren.
Abbildung 5: Das Terminal ist bereit für weitere Eingaben
An diesem Punkt ist der Kernel fertig kompiliert, aber noch nicht verfügbar für den Raspberry Pi 5. Dazu müssen zunächst einmal die Kernelmodule auf das boot-Medium installiert werden, siehe Code 12.
sudo make -j6 modules_install
Code 12: Installiere Module auf dem Pi 5
Nach ein paar Sekunden sollte der Raspberry Pi 5 damit fertig sein und der Kernel samt Overlays können kopiert werden, siehe Code 13.
sudo cp /boot/firmware/$KERNEL.img /boot/firmware/$KERNEL-backup.img
sudo cp arch/arm64/boot/Image.gz /boot/firmware/$KERNEL.img
sudo cp arch/arm64/boot/dts/broadcom/.dtb /boot/firmware/ sudo cp arch/arm64/boot/dts/overlays/.dtb* /boot/firmware/overlays/
sudo cp arch/arm64/boot/dts/overlays/README /boot/firmware/overlays/
Code 13: Den neuen Kernel und die Overlays kopieren (64bit Architektur)
An der Stelle kommt auch der Parameter aus Code 7 zum Tragen, damit der Schreibaufwand etwas geringer wird. Wichtig ist, der Befehl ist leicht anders für 32bit Architektur, daher unbedingt in der Dokumentation nachlesen wo alles hin kopiert werden muss.
Damit nun der neue Kernel verwendet werden kann, braucht es einen Neustart, siehe Code 14 und der neue Kernel sollte genutzt werden, siehe Abbildung 6.
sudo reboot
Code 14: Raspberry Pi neustarten
Abbildung 6: Der Raspberry Pi nutzt den selbst gebauten Kernel
Damit du auch sehen kannst, welcher Kernel gerade verwendet wird, muss im Terminal Code 15 ausgeführt werden.
uname -a
Code 15: Zeige Systeminformationen an
Hinter der Versionnummer vom Linux-Kernel sollte dann der Tag erscheinen, der im grafischen Menü von euch definiert wurde.
Sollte beim Neustart der Raspberry Pi nicht mehr booten, ist entweder der Kernel beschädigt, falsch kompiliert oder hat den falschen Namen. Alles ist an der Stelle denkbar und muss überprüft werden. Solltest du, wie Eingangs erwähnt, ein BackUp der Boot-Partition haben, kannst du diesen einfach austauschen. Andernfalls besteht noch die Möglichkeit, dass unter /boot/firmware, eine Datei mit dem Namen *-backup.img zu finden ist. Dieses kann ggf. deinem Raspberry Pi 5 wieder Leben einhauchen.
Abschließende Worte
Wer sich in der Welt des Programmierens auskennt und auch etwas Erfahrung mit Linux hat, dem werde ich an der Stelle nichts Neues erklärt haben. Die Tatsache, sich einen eigenen Kernel für seine Bedürfnisse zu bauen, finde ich immer wieder genial und bietet einiges an Vorteile. Ein Risiko hier einen Kernel zu kompilieren, der ggf. später nicht funktioniert ist nicht gerade gering, sofern man nicht genau weiß, was man tut. Jedoch kann man recht schnell und einfach Bugfixes und Features ausprobieren, die erst später durch ein normales Update über den Paketmanager verfügbar sind. Man ist quasi ein Beta-Tester, wobei der Quellcode vom Linux-Kernel und auch dem Kernel für die Raspberry Pis, mit Sorgfalt im GitHub-Repository gepflegt werden.
An der Stelle habe ich das Thema Cross-Compiling bewusst außer Acht gelassen, da es dafür Linux oder ein docker-Container für braucht. Das macht die Erklärung an der Stelle etwas komplizierter und wäre in diesem kurzen Blogbeitrag nicht unterzubringen. Die Möglichkeit hinter dem Cross-Compiling liegt darin, einen leistungsstärkeren PC den gewünschten Kernel bauen zu lassen und später das Resultat auf das Boot-Medium vom eigentlichen Ziel zu kopieren. Aber auch an der Stelle gibt es ein gewisses Risiko, dass der Kernel nicht funktioniert.
Zuletzt noch ein paar Worte zu dem am Anfang erwähnten Experiment mit der externen Grafikkarte und dem Raspberry Pi 5. Aktuell stockt dieses Experiment, da ich mich noch weiter in die Materie einlesen muss. So einfach, wie mal eben an einem Desktop-PC die Grafikkarte zu tauschen, ist das Thema leider nicht. Der Raspberry Pi 5 hat mit dem PCT-Anschluss und den diversen HATs mittlerweile einiges an Features hinzubekommen, aber im Falle der PCI-Erweiterung, gibt es immer wieder Probleme. Der YouTuber und Blogger Jeff Geerling hat dieses Thema angefangen und auch er hat für dieses Projekt viel Zeit und Arbeit investiert. Auch er hatte mit vielen Rückschlägen und Problemen zu kämpfen. Mittlerweile hat sich um dieses Projekt eine kleine Fanbase gebildet, die alle möglichen Karten testen und teilen die Erfahrung in dem vorhandenen GitHub-Repository.
Ich für meinen Teil möchte ich an der Stelle weiter in das Thema Kernel vertiefen, damit ich schlussendlich meine GT730 zum Laufen bekomme.
Fazit
Das Kompilieren eines eigenen Kernels für den Raspberry Pi 5 mag zunächst eine Herausforderung sein, doch es eröffnet spannende Möglichkeiten, das volle Potenzial der Hardware auszuschöpfen. Ob für Experimentierfreudige oder Technik-Enthusiasten – das Eintauchen in die Welt des Linux-Kernels lohnt sich auf jeden Fall. Viel Erfolg und Spaß beim Ausprobieren, und denk dran, immer ein Backup parat zu haben!