Open Source im professionellen Einsatz

© psdesign1, Fotolia

Kernel- und Treiberprogrammierung mit dem Linux-Kernel – Folge 59

Kern-Technik

,

Linux plattformübergreifend: Die praktische Skriptsammlung Buildroot erzeugt im Handumdrehen einen Kernel für Embedded-Systeme mit ARM-CPU. Als Nebenprodukt fällt dabei ein komplettes, lauffähiges Linux-System ab, das sich im Emulator Qemu testen lässt.

In den Anfangstagen von Linux war es nicht abzusehen, dass der ursprünglich ausschließlich für i386-CPUs entwickelte Betriebssystemkern einmal mehr als 20 verschiedene Prozessoren unterstützen würde. Heute gehen dank Smartphone- und Tablet-Boom täglich mehr Linux-Systeme auf einer ARM-basierten Plattform in Betrieb als auf klassischen, x86-basierten Desktop- oder Serversystemen.

Host und Target

Der herkömmliche PC dient jedoch weiterhin als Entwicklungsrechner (Host), um darauf die Systemsoftware, einen Kernel und ein Root-Dateisystem für das Ziel (Target), etwa ein Android-Smartphone, zu generieren. Für diese so genannte Cross-Entwicklung braucht man Compiler, Linker und Bibliotheken, die Code für den anderen Prozessortyp erzeugen können.

Root-Dateisystem

Für ein funktionstüchtiges Linux ist neben dem Kernel noch ein Root-Dateisystem erforderlich. Es enthält die zum Betrieb notwendigen Programme, Bibliotheken und Konfigurationsdateien. Buildroot erstellt das Root-Filesystem als eine Datei, die per Loop-Device gemountet wird. Dadurch lässt sie sich auch nachträglich beliebig modifizieren.

Die zentrale Komponente des von Buildroot erstellten Dateisystems bildet das Multicall-Binary Busybox. Es vereint in einem einzigen Executable eine Shell mit allen wichtigen Systemkommandos, Editor, Logging und – falls konfiguriert – sogar Web- und FTP-Server. Der Name Multicall-Binary rührt daher, dass dieses einzelne Executable mit Hilfe vieler symbolischer Links unter allen benötigten Programm- und Kommandonamen ansprechbar ist. Busybox erkennt beim Aufruf, unter welchem Namen es gestartet wurde, und nimmt dann wie ein Chamäleon die zugehörige Funktionalität an.

Kein Wunder, dass dieses Schweizer Taschenmesser auch das Init-Programm (System-V-Init), den ersten vom Kernel beim Booten gestarteten Prozess, im generierten Linux stellt. Die Konfiguration von Init befindet sich im Root-Dateisystem unter »/etc/inittab« , die zugehörigen Skripte, die zum Beispiel das Netzwerk konfigurieren, unter »/etc/init.d/« .

Als Gerätedateien finden sich typischerweise »/dev/console« , »/dev/null« und »/dev/zero« im Dateisystem, weitere Einträge lassen sich nach Bedarf anlegen. Außerdem braucht das Root-Filesystem noch ein temporäres Verzeichnis (»/tmp/« ) und ein Logverzeichnis (»/var/log/« ). Sinnvollerweise legt man zudem Mountpoints für die virtuellen Dateisysteme »/proc/« und »/sys/« an. Das Mounten selbst erledigt eines der per Init gestarteten Skripte.

Verantwortlich für das Bauen eines Kernels ist das zum Linux-Quellcode gehörende Kernel-Build-System. Es benötigt die üblichen Entwicklungswerkzeuge (unter Ubuntu die Pakete »ncurses-dev« und »build-essentials« ) sowie Cross-Compiler und Cross-Linker (in der Regel GCC und den GNU-Linker »ld« ) auf dem Entwicklungsrechner.

Dann kann das System mit Hilfe der Environment-Variablen beziehungsweise Aufrufparameter »ARCH« , »CROSS_COMPILE« und »O« den Code für die Zielplattform generieren. »ARCH« spezifiziert die Zielplattform – bei Kernel 3.0 stehen die in Abbildung 1 gelisteten Architekturen zur Auswahl. »CROSS_COMPILE« spezifiziert den Teilstring, den das Kernel-Build-System vor den Namen des Compilers und des Linkers einfügt.

Abbildung 1: Im Verzeichnis arch des Kernel-Quellcodes finden sich die von Linux unterstützten Architekturen.

Es liegt im Namen

Typischerweise haben die Cross-Entwicklungswerkzeuge einen Namen, der unter anderem auf die Zielplattform hinweist: »arm-linux-gcc« beispielsweise steht für einen Compiler (»gcc« ), der Code für einen ARM-Prozessor generiert. Der Namensbestandteil »CROSS_COMPILE=arm-linux-« etwa weist das Kernel-Build-System dazu an, eine Cross-Generierung mit diesem Werkzeug durchzuführen – vorausgesetzt die Pfadvariable schließt das Verzeichnis ein, in dem sich der gewünschte Compiler befindet.

Nicht unbedingt notwendig, aber hilfreich ist schließlich die Variable »O« , die das Ausgabeverzeichnis bestimmt. Damit ist es möglich, Konfigurations- und Generierungs-Informationen vom eigentlichen Quellcode des Kernels zu trennen. Liegen die Kernelquellen beispielsweise im Verzeichnis »/usr/src/linux-3.0/« und gibt es ein Verzeichnis »/usr/src/linux-3.0-arm/« , lässt sich der Kernel mit dem folgenden Befehl konfigurieren, wobei das GUI-Tool Menuconfig zum Einsatz kommt (siehe Abbildung 2):

Abbildung 2: Je nach Plattform zeigt Menuconfig bereits im Hauptmenü unterschiedliche Konfigurationsoptionen (links x86_64, rechts ARM).

cd /usr/src/linux-3.0/
make ARCH=arm CROSS_COMPILE=arm-linux- O=/usr/src/linux-3.0-arm menuconfig

Das nächste Kommando veranlasst anschließend die Übersetzung:

cd /usr/src/linux-3.0/
make ARCH=arm CROSS_COMPILE=arm-linux- O=/usr/src/linux-3.0-arm bzImage modules

Das Kernel-Build-System legt den übersetzten Kernel in »/usr/src/linux-3.0-arm/arch/arm/boot/zImage« ab.

Leider liegen auf dem Weg zum funktionierenden Kernel noch ein paar Stolpersteine aus, zunächst die korrekte Konfiguration: ARM-basierte Plattformen sind deutlich vielgestaltiger als die bekannte x86-Plattform (siehe Kasten "Advanced Risc Machine (ARM)").

Advanced Risc Machine (ARM)

Eine der zurzeit wichtigsten Prozessorarchitekturen firmiert unter dem Namen Advanced Risc Machine (ARM). Von den im Jahr 2010 nach Schätzungen rund 13 Milliarden verkauften (Mikro-)Prozessoren verarbeiteten wohl beinahe 50 Prozent den ARM-Befehlssatz ([2], [3])Fast alle Android-Smartphones und Tablets oder auch Navigationsgeräte sind typischerweise mit mehr als einer ARM-CPU realisiert, ebenso Apples Mobilgeräte. Das liegt daran, dass diese Architektur hohe Leistung bei niedrigem Stromverbrauch bietet.

Die Entwicklung begann bereits 1983, der erste Prozessor stand 1987 mit dem ARM 2 zur Verfügung. Es handelte sich schon damals um einen 32-Bit-Prozessor, der sich mit erstaunlich wenigen Transistoren und damit Silizium bauen lässt. Bei der implementierten Risc-Architektur handelt es sich um eine so genannte 3-Register-Maschine, die das Verknüpfungsergebnis der Inhalte zweier Register in einem dritten Register ablegt.

Im Laufe der Zeit hat der Hersteller verschiedene Befehlssätze spezifiziert und implementiert, unter anderem mit Jazelle auch einen, der auf die Verarbeitung von Javacode spezialisiert ist. ARM-Prozessoren haben allerdings in Normalausführung keine Floating Point Unit.

Bei der Namensgebung der Prozessoren hat ARM den großen Vorbildern Intel und AMD nachgeeifert, sie ist – mit einem Wort gesagt – unübersichtlich. Es werden zunächst vier Architekturen (Befehlssätze) unterschieden: ARMv4, ARMv5, ARMv6 und ARMv7. Letztere ist aktuell. Darüber hinaus gibt es etwa sechs Familien: Arm7, Arm9, Arm11, Cortex R, Cortex M und Cortex A. Der Namensvorsatz Cortex scheint reines Marketing zu sein, M steht dagegen für Mikrocontroller, also eine CPU samt notwendigster Peripherie, R für Realtime und A für Application.

Außerdem werden noch Implementierungsvariationen an den Architekturnamen gehängt. T etwa steht für einen zusätzlichen, Speicherplatz sparenden Befehlssatz (Thumb), D für Hardware-technische Debugmöglichkeiten, M für Multiply-Enhanced und I für eine eingebettete ICE-Makrozelle.

In den aktuellen Topmodellen der Smartphones befindet sich als High-End-Prozessor ein Cortex A9. Diese Version ist mehrkernfähig. Typischerweise bauen die Hersteller damit Dualcore-Prozessoren auf, beispielsweise den in den aktuellen Tablets vorwiegend eingesetzten Tegra von Nvidia (Abbildung 4).

© © Nvidia CorporationAbbildung 4: ARM-CPUs wie Nvidias Tegra 2 finden sich in Smartphones und Tablets.

Besonders interessant ist, dass ARM selbst keine Prozessoren herstellt, sondern nur das Design erledigt. Die Firma verdient ihr Geld allein auf Basis von Lizenzen. Die Lizenznehmer, Qualcomm, Nvidia, Apple und diverse andere, nehmen das Basisdesign, erweitern oder modifizieren es und bauen dann die hauseigenen Prozessoren daraus. Ob ARM seine Vormachtstellung im Segment der Mobilgeräte halten kann, ist noch fraglich. Denn der Hunger nach Rechenleistung und Speicherausbau wird immer größer und ARM hat noch keine 64-Bit-Versionen angekündigt.

Unübersichtlich wie die Welt der ARM-Prozessoren ist auch der architekturspezifische Quellcode im Linux-Kernel. Linus Torvalds hat daher zu einer Aufräumaktion aufgerufen. Erste Patches sind in Kernelversion 3.0 eingeflossen.

Das Kernel-Build-System bietet zum Ausgleich immerhin eine Reihe von Default-Konfigurationen für verschiedenste ARM-Systeme an. Welche das sind, lässt sich mit Hilfe des Aufrufs

cd /usr/src/linux-3.0/
make help ARCH=arm

anzeigen und durch »make Default-Konfiguration« aktivieren. Die zweite Stolperfalle sind die Cross-Entwicklungswerkzeuge selbst. Auf eventuell vorhandene vorkompilierte Pakete der gerade verwendeten Distribution zurückzugreifen ist frustrierend. Ebenso wenig Erfolg versprechend ist es, Pakete für Compiler und Linker gemäß diverser Anleitungen aus dem Internet selbst zu generieren.

Diesen Artikel als PDF kaufen

Express-Kauf als PDF

Umfang: 4 Heftseiten

Preis € 0,99
(inkl. 19% MwSt.)

Als digitales Abo

Als PDF im Abo bestellen

comments powered by Disqus

Ausgabe 07/2013

Preis € 6,40

Insecurity Bulletin

Insecurity Bulletin

Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...

Linux-Magazin auf Facebook