Fiasco.OS und CubieBoard 2

2015-02-21

Dieser Artikel ist eine Anleitung zur Installation von Fiasco.OC auf einem Cubieboard.

Motivation

In einem früheren Artikel wurde bereits ein grundlegendes Setup beschrieben, mit welchem man die reine serielle Ausgabe eines BananaPi mittels eines Terminalprogramms ausgeben kann.

In diesem Beitrag soll es vielmehr darum gehen, wie man ein eigenes Betriebssystem (Nicht-Linux) auf ein Cubieboard 2 aufspielt.

Allgemeines

Fiasco.OC und L4Re

Am Lehrstuhl für Betriebssysteme der TU München kommt das Fiasco.OC Echtzeitbetriebssystem für die Forschung zum Einsatz. Das Betriebssystem basiert auf einer Microkernel-Architektur und kann sowohl für große Systeme, als auch eingebettete Systeme verwendet werden. Fiasco.OC wird hauptsächlich von der Betriebssystemgruppe der TU Dresden entwickelt.

Es besteht prinzipiell aus zwei Teilen, welche separat voneinander gebaut werden müssen. Erstens der Kernel, welcher die rudimentären Primitiven wie Speicherverwaltung, Scheduling etc. realisiert. Zweitens die L4Re Laufzeitumgebung, welcher Fiasco.OC zu einem vollständigen Betriebssystem macht. Die folgende Abbildung veranschaulicht nochmals den grundlegenden Aufbau.

Viele weitere Informationen können auf den Seiten rund um den Fiasco.OC Kernel gefunden werden.

Cubieboard 2

Möchte man ein Betriebssystem auf einem eingebetteten System ausführen, sollte man sich zuerst mit den grundlegenden Systemeigenschaften, wie z. B. den genauen Bezeichner des Prozessors, vetraut machen. Diese Bezeichnung benötigt man später um beim Übersetzungsprozess des Betriebssystems die richtige Zielarchitektur/ -hardware auswählen zu können.

Das Cubieboard 2 basiert auf einem Allwinner A20 Prozessor (sun7i) und gehört somit zur Sunxi SoC-Familie. Der A20 ist ein Dual-Core Prozessor, welcher aus zwei ARM-CortexA7 Kernen besteht. Weitere Informationen findet man im Wiki der Linux-Sunxi Community.

U-Boot (Bootloader)

Nachdem kurz das Betriebssystem und die Zielarchitektur vorgestellt wurden, sei an dieser Stelle noch auf den U-Boot Bootloader hingewießen, welcher standardmäßig auf allen Allwinner SoCs zum Einsatz kommt. Dieser wird im späteren Installationsprozess so konfiguriert, dass das neu erstellte Betriebssystem auch richtig geladen werden kann.

Bauen des Kernels und der L4Re

In diesem Abschnitt wird das Bauen des Kernels und der L4Re Laufzeitumgebung anhand eines beispielhaften Ablaufs auf einem Ubuntu beschrieben.

Herunterladen benötigter Pakete und Sourcecode

Bevor man mit dem eigentlichen Bauen des Sourcecodes beginnen kann werden die im Folgenden aufgelisteten Pakete benötigt, welche man über den Paketmanager seines Linux-Systems installieren sollte (hier: Beispiel Ubuntu):

sudo apt-get install subversion
gcc-arm-linux-gnueabi \ libc6-dev libncurses5-dev
g++-arm-linux-gnueabi \ build-essential u-boot-tools

Im Anschluss muss der Sourceode aus den Quellen besorgt werden:

svn co http://svn.tudos.org/repos/oc/tudos/trunk fiasco.oc
Der obige Befehl legt einen Ordner fiasco.oc an, in welchem sich im Verzeichnis kernel der l4-Kernel befindet und respektive im Verzeichnis l4 die L4Re Laufzeitumgebung.

Die eigentliche Übersetzung

Zuerst übersetzt man den Kernel. Hierzu wechselt man in das entsprechende Verzeichnis und erstellen dort einen Ordner, in welchem der fertige Kernel gebaut werden soll (sog. Buildverzeichnis).

cd fiasco.oc/kernel/fiasco/
make BUILDDIR=build-cubieboard2 

Durch den vorangehenden Befehl wird ein Verzeichnis mit dem Namen build-cubieboard2 angelegt. Anschließend wechselt man in dieses Verzeichnis und beginnt mit der Konfiguration des Kernels.

cd build-cubieboard2
make menuconfig

Im folgenden ncurses-Dialog sind folgende Einstellungen vorzunehmen:

Target configuration
|->Architecture (ARM processor family)
|-> Platform (Allwinner (sunxi))
|-> CPU (ARM Cortex-A7 CPU)

Kernel options
|-> [*] Enable multi processor support

Ein abschließendes betätigten über Save speichert die Einstellungen. Bevor der Übersetzungsvorgang gestartet werden kann muss noch die Datei Makeconf.arm im Verzeichnis src angepasst werden. Hier muss das SYSTEM_TARGET an den verwendeten Compiler angepasst werden.

# -*- makefile -*-
# vi:se ft=make:
#OPT_SHARED_FLAGS += $(call CHECKCC,-finline-limit=10000,)
SYSTEM_TARGET ?= arm-linux-gnueabi-

Jetzt kann im Builverzeichnis der make Befehl ausgeführt werden. Bei Mehrkernprozessoren kann die Anzahl der zu verwendeten Jobs mit dem Argument -j angegeben werden um den Übersetzungsprozess zu beschleunigen.

# make bei 8 Kernen
make -j 8

Jetzt ist die Laufzeitumgebung L4Re an der Reihe. Hierzu wechselt man in das Verzeichnis l4 und erstellt auch hier wieder ein Buildverzechnis.

cd fiasco.oc/l4
make B=build-cubieboard2

Danach wird die Datei Makeconf im Verzeichnis mk angepasst. Hierbei wird SYSTEM_TARGET_arm wieder auf den zu verwendenden ARM-Compiler gesetzt.

SYSTEM_TARGET_arm = arm-linux-gnueabi-

Anschließend wird die L4Re Laufzeitumgebung mit folgendem Befehl konfiguriert.

make menuconfig O=build-cubieboard2/

Folgende Einstellungen sind im darauf erscheinenden ncurses-Dialog vorzunehmen.

Target Architecture (ARM architecture)
CPU variant (ARMv7A type CPU)
Platform Selection (CubieBoard 2)

Danach wird der Buildprozess durch den folgenden Befehl gestartet:

make O=build-cubieboard2 -j8

Konfiguration des Zielsystems

Nach der (hoffentlich) erfolgreichen Erstellung des Betriebssystems folgt in diesem Abschnitt die Konfiguration des Zielsystems.

Erstellen des UImages

Das bereits vorgestellte UBoot lädt das erstellte Betriebssystem als UImage (vgl. vmlinux), wobei von den Entwicklern der TU-Dresden bereits ein Maketarget zur Erstellung eines solchen Images zur Verfügung gestellt wird. Hierzu wird im Buildverzeichnis l4/build-cubieboard2 folgender Befehl abgesetzt.

make E=hello uimage \
MODULE_SEARCH_PATH=<abs path>/fiasco.oc/kernel/fiasco/build-cubieboard2/

Das Argument MODULE_SEARCH_PATH zeigt auf das Buildverzeichnis des Fiasco.OC Kernels. In dem daraufhin erscheinenden Menü kann nun die zu verwendende Startkonfiguration ausgewählt werden. Für das ”Hello World!“ Programm wird hier der Eintrag ”hello“ ausgewählt. Das erstellte Image befindet sich danach im Unterverzeichnis bin/arm_armv7a/ des Buildverzeichnisses.

Installation des UImage auf der SD-Karte

Wichtig: Alle bisherigen Dateien auf der SD-Karte werden in diesem Schritt gelöscht (Backup machen!)

Zuerst wird eine Partition auf der SD-Karte erstellt, in welcher dann später das Image samt für den Startprozess notwendigen Dateien liegt. Hierzu kann man ein Werkzeug wie cfdisk verwenden (grafisch geht es mit gparted). Nach der Partitionierung erfolgt die Formatierung mittels mkfs. In diesem Artikel wird die Partition mit dem ext2-Dateisystem formatiert. Hierzu wird exemplarisch folgender Befehl eingegeben:

sudo mkfs.ext2 /dev/mmcblk0p1

Anschließend kann man das erstellte Kernel-Image auf die SD-Karte überspielen. Allerdings benötigt man für das korrekte Starten des Kernels noch weitere Dateien, wie das folgende Layout zeigt. Diese werden in den folgenden Abschnitten genauer beschrieben.

/dev/mmcblk0p1
 |-> uEnv.txt
 |-> boot.scr
 |-> bootstrap_hello.uimage

Konfiguration des U-Boot (Bootloader)

U-Boot kann über zwei Dateien an die eigenen Bedürfnisse angepasst werden. Die boot.scr hat dabei die Aufgabe, wie der Name schon verrät, die Bootparameter einzustellen bzw. die Standardwerte zu überschreiben.

Folgender Quellcode in boot.cmd zeigt die für das Setup notwendigsten Argumente. Das Kernelimage (bootstrap_hello.uimage) soll an die Adresse 0x40000000 abgelegt werden, dies kann im Prinzip eine beliebige Adresse im Hauptspeicher sein. Für dieses Beispiel reicht aber die erste mögliche Adresse im Hauptspeicher (siehe hierzu Tabelleneintrag DDR-II/DDR-III in Kapitel 1.3 “Memory Mapping” des Allwinner A20 User Manual).

Der Befehl ext2load lädt mittels mmc-Subystem das Kernelimage von der ersten Partition 0 and die frei gewählte Adresse. Ein abschließendes bootm initiiert den Startprozess.

setenv kernel_addr_r 0x40000000
ext2load mmc 0 ${kernel_addr_r} /bootstrap_hello.uimage
bootm ${kernel_addr_r}

Die boot.scr ist eine binäre Datei, damit aus dem Quellcode (boot.cmd) eine von UBoot lesbare boot.scr wird, muss diese noch mit dem folgendem Befehl übersetzt werden.

mkimage -C none -A arm -T script -d boot.cmd boot.scr

Das rechte Bild zeigt den ersten Start mit dem (hoffentlich) erfolgreich installiertem System. Möchte man allerdings dem Kernel noch Parameter übergeben gibt es noch weitere Dateien/Einstellungen, die angepasst werden müssen. Im folgenden wird noch eine beispielhafte erweiterte Konfiguration der boot.scr vorgestellt, welche kurz das Potential der Konfigurationsmöglichkeiten aufzeigen soll.

setenv kernel_addr_r 0x46000000 # 8M
setenv fdt_addr 0x49000000 # 2M
setenv fdt_high 0xffffffff # Load fdt in place instead of relocating

fatload mmc 0 0x43000000 /script.bin

fatload mmc 0 ${kernel_addr_r} /uImage
setenv bootargs "console=ttyS0,115200 hdmi.audio=EDID:0
disp.screen0_output_mode=EDID:1280x1024p60 rw root=/dev/mmcblk0p2 rootwait"

fatload mmc 0 ${fdt_addr} /sun7i-a20-cubieboard2.dtb

bootm ${kernel_addr_r} - ${fdt_addr}

Im obigen Quellcode sind besonders die Zeilen 2, 3 und 11, die den fdt (siehe Device Tree vs. Fex) konfigurieren, von Interesse. Darüber hinaus zeigt die Zeile 8ff. eine Möglichkeit, wie man Bootargumente an den Kernel übergibt (eine weitere Möglichkeit zeigt der folgende Abschnitt).

uEnv.txt

Dieser Abschnitt soll kurz zeigen, welche andere Möglichkeit besteht, beim Booten weitere Kernelargumente zu spezifizieren. Neben der boot.scr gibt es noch die uEnv.txt, welche hierzu im Plain-Text Format vorliegt. In der uEnv.txt können, wie im folgenden Quellcode, auch Kernelargumente spezifiziert werden.

console=ttyS0,115200
consoleblank=0

Vergleicht man dies mit den bootargs des obigem erweiterten Listings der boot.cmd findet man auf jeden Fall den console-Parameter wieder, weshalb es hier eben zwei Möglichkeiten gibt, Kernelparameter zu übergeben.

Device Tree vs. Fex

Der folgende Abschnitt geht nochmal auf die Device Trees und den FEX-Standard ein. Anders als in Desktopsystemen gibt es im Allgemeinen bei eingebetteten Systemen keine Möglichkeit die Hardware “abzufragen”. Vielmehr wird die verfügbare Hardware dem System statisch mitgeteilt. Hierfür dienen die sog. Device Trees (dt) in welchen die Hardware systematisch aufgeführt wird. Hierbei gibt es unterschiedliche Ausprägungen (Quelle).

dtb = device tree blob
dts = device tree source
dtsi = like dts, but I think the 'i' is to indicate that this file is
supposed to be included in another dts(i) file.

FEX auf der anderen Seite ist ein properitäres Format, welches hauptsächlich von der Allwinner-Prozessor-Familie auch zur Beschreibung der Hardware verwendet wird. Daraus folgt, dass man nur eine der beiden Beschreibungssprachen im boot.scr/.cmd benötigt.

Fazit

In diesem Artikel wurde eine Schritt-für-Schritt Anleitung zur Installation des Fiasco.OC-Mikrokerns samt Laufzeitumgebung auf dem Cubieboard 2 beschrieben. Darüber hinaus wurde versucht einige detailiertere Informationen für das bessere Verständnis der beteiligten Komponenten und Begrifflichkeiten zu geben. Die Installationsanleitung wurde so bzw. so ähnlich auch bereits mit anderen Boards (z. B. BananaPi, Beagle Bone Black) erfolgreich getestet. Abschließend hoffe ich, dass diese Anleitung für den ein oder anderen hilfreich sein kann.