fli4l auf dem Raspberry Pi (emuliert)

Einleitung

Als Entwickler hat man es immer schwer, wenn man Code testen soll, der für Hardware geschrieben ist, die man nicht hat (sad) Für solche Fälle bieten sich Hardware-Emulatoren sehr gut an. Im vorliegenden Falle geht es um die Emulation einer Raspberry Pi-Maschine. Leider gibt es zur Zeit keinen Emulator, der diese Hardware komplett emulieren kann, aber man kommt immerhin schon nahe. Diese Anleitung soll dabei helfen, die ersten Schritte mit dem fli4l auf einer ARM-Plattform wie dem Raspberry Pi zu machen.

Voraussetzungen

Es wird ein Linux-Host benötigt, auf dem QEMU mit ARM-Unterstützung installiert ist. Will man die fli4l-VM später netzwerktechnisch auch ans Internet anbinden, ist es von Vorteil, wenn die WAN-Karte Teil einer Bridge ist, in die sich die VM "einklinken" kann. Wie man eine Netzwerkkonfiguration unter Linux mit Bridges hinbekommt, variiert von Distribution zu Distribution und ist nicht Teil dieser Anleitung. (Ich werde aber ganz unten meine eigene spezielle Konfiguration für Gentoo vorstellen.)

Des Weiteren werden die "dosfstools" zum Erstellen von VFAT-Dateisystemen benötigt sowie die Programme "fdisk" und  "losetup" aus dem Paket "util-linux".

Auch benötigen wir einen speziellen Kernel. Leider können wir nicht den fli4l-Kernel verwenden, weil dieser einen Raspberry Pi voraussetzt, der ja nicht emuliert werden kann. Wir brauchen stattdessen einen Kernel, der eine "versatilepb"-Maschine erwartet, denn diese kann von QEMU emuliert werden. Diesen Kernel kann/muss man von [http://xecdesign.com/downloads/linux-qemu/kernel-qemu] herunterladen.

Schließlich brauchen wir natürlich einen aktuellen fli4l-Checkout, und zwar den SVN-Zweig 4.0/trunk ab Revision r38903. Den kann man sich wie folgt beschaffen:

$ svn checkout -r 38903 https://ssl.nettworks.org/svn/fli4l/branches/4.0/trunk<ENTER>

Konfiguration

Man kann eine beliebige fli4l-Konfiguration wählen, welche aber den folgenden Bedingungen genügt:

  • nur eine Netzwerkkarte (mehr geht leider nicht), d.h. IP_NET_N='1'
  • kein IPv6 (kann der oben erwähnte versatilepb-Kernel leider nicht)
  • will man das Paket "hwsupp" nutzen (momentan hat es beim Raspberry Pi keine Funktion), muss man HWSUPP_TYPE='rpi' setzen
  • OPT_E3 und OPT_VALGRIND müssen beide auf no gesetzt werden, des Weiteren braucht man POWERMANAGEMENT='none' und BEEP='no'
  • keine Festplatten-Installation und Recover-Funktionatlität, weil SYSLINUX fehlt, deshalb OPT_HDINSTALL='no' und OPT_RECOVER='no'
  • auch wenn der fli4l-Kernel nicht benutzt wird, weiß das "mkfli4l" ja nicht; da es weder "-virt"-Kernel noch "3.18.*"-Kernel für den R'Pi gibt, muss man KERNEL_VERSION='3.19.6' oder KERNEL_VERSION='3.19.6-nonfree' verwenden

Erstellen des Installationsmediums

Zuerst konfiguriert man seine mkfli4l.txt so, dass kein Remote-Update vorgenommen wird (REMOTEUPDATE='no'). Dann lässt man wie üblich mkfli4l.sh laufen und hat hinterher in config/build die folgenden Dateien vorliegen:

  • rc.cfg, die Konfigurationsdatei
  • kernel, den fli4l-Kernel
  • rootfs.img, das RootFS-Archiv
  • opt.img, das OPT-Archiv

Diese gilt es nun auf ein geeignetes Boot-Medium zu kopierern. Zuerst erstellen wir dies via dd:

$ dd if=/dev/zero of=rpi-hd.img bs=1048576 count=64<ENTER>
64+0 Datensätze ein
64+0 Datensätze aus
67108864 Bytes (67 MB) kopiert, 0,93694 s, 71,6 MB/s
$

Dann müssen wir eine Partition anlegen. Dies kann man z.B. via fdisk erledigen:

$ /sbin/fdisk rpi-hd.img<ENTER>

Willkommen bei fdisk (util-linux 2.25.2).
Änderungen werden vorerst nur im Speicher vorgenommen, bis Sie sich
entscheiden, sie zu schreiben.
Seien Sie vorsichtig, bevor Sie den Schreibbefehl anwenden.

Gerät enthält keine erkennbare Partitionstabelle.
Created a new DOS disklabel with disk identifier 0xfafe4f9c.

Befehl (m für Hilfe): n<ENTER>
Partitionstyp
p Primär (0 primär, 0 erweitert, 4 frei)
e Erweitert (Container für logische Partitionen)
Wählen (Vorgabe p): <ENTER>
Standardantwort p wird verwendet.
Partitionsnummer (1-4, Vorgabe 1): <ENTER>
Erster Sektor (2048-131071, Vorgabe 2048): <ENTER>
Letzter Sektor, +Sektoren oder +Größe{K,M,G,T,P} (2048-131071, Vorgabe 131071): <ENTER>

Eine neue Partition 1 des Typs »Linux« und der Größe 63 MiB wurde erstellt.

Befehl (m für Hilfe): t<ENTER>
Partition 1 ausgewählt
Hexadezimalcode (geben Sie L ein, um alle Codes aufzulisten): 6<ENTER>
WARNUNG: Wenn Sie eine DOS-6.x-Partition angelegt oder verändert haben, dann schauen Sie bitte in die cfdisk-Handbuchseite nach weiteren Informationen.
Partitionstyp von »Linux« nach »FAT16« geändert.

Befehl (m für Hilfe): a<ENTER>
Partition 1 ausgewählt
Die Bootfähig-Markierung auf Partition 1 ist nun aktiviert.

Befehl (m für Hilfe): w<ENTER>
Die Partitionstabelle wurde verändert.
Festplatten werden synchronisiert.

$

Nun erstellen wir auf unserem Rechner ein Gerät für diese Partition, damit wir sie einhängen können:

$ sudo losetup -f -P --show rpi-hd.img
/dev/loop0

Dank der Option "-P" ist die Partition unter /dev/loop0p1 verfügbar (oder allgemein unter /dev/loopXp1, falls ein anderer Index als "null" von losetup zurückgeliefert wird). Diese können wir nun mit dem VFAT-Dateisystem formatieren, einhängen, die Dateien darauf kopieren und wieder aushängen:

$ sudo mkfs -t vfat /dev/loop0p1<ENTER>
mkfs.fat 3.0.26 (2014-03-07)
unable to get drive geometry, using default 255/63
$ mkdir rpi
<ENTER>
$ sudo mount /dev/loop0p1 rpi<ENTER>
$ sudo cp -a config/build/{rc.cfg,kernel,rootfs.img,opt.img} rpi/<ENTER>
$ sudo umount rpi<ENTER>
$

Jetzt entfernen wir noch das loop-Gerät:

$ sudo losetup -d /dev/loop0<ENTER>
$

Geschafft! Das Boot-Medium ist bereit.

Emulation

Nun müssen wir nur noch QEMU starten:

$ qemu-system-arm -kernel kernel-qemu -cpu arm1176 -m 256 -M versatilepb -no-reboot -serial stdio -append "console=ttyAMA0,115200 console=tty1 panic=1 load_ramdisk=1" \
  -initrd config/build/rootfs.img -hda rpi-hd.img -netdev bridge,id=net0,br=br0 -net nic,netdev=net0
<ENTER>
Uncompressing Linux... done, booting the kernel.
Booting Linux on physical CPU 0x0
Linux version 3.10.26+ (shift@Shift-PC) (gcc version 4.7.3 (Gentoo 4.7.3-r1 p1.4, pie-0.5.5) ) #2 Fri Jan 17 22:13:59 EST 2014
CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d
CPU: VIPT aliasing data cache, unknown instruction cache
Machine: ARM-Versatile PB
[...]

Fertig!

Screenshots

Anhang A: Gentoo-Netzwerk-Konfiguration

Die unter "Emulation" verwendete Netzwerkbrücke br0 wird wie folgt über die /etc/conf.d/net konfiguriert:

config_br0="dhcp"
brctl_br0="setfd 0
sethello 10
stp off"
mac_br0="00:25:22:6c:68:f4"
bridge_br0="enp0s10"

Das Programm brctl gibt Folgendes aus, während die fli4l-VM läuft:

 

$ /sbin/brctl show br0
bridge name     bridge id               STP enabled     interfaces
br0             8000.0025226c68f4       no              enp0s10
                                                        tap0

Anhang B: Die komplette fli4l-Konfiguration

Die folgende Konfiguration kann als Grundlage für ein _fli4l.txt-Overlay herangezogen werden.

#
# package 'base'
#
HOSTNAME='swing'
PASSWORD='...'
KERNEL_VERSION='3.19.6'
POWERMANAGEMENT='none'
BEEP='no'
NET_DRV_N='0'
IP_NET_N='1'
IP_NET_1='{dhcp}'
IP_NET_1_DEV='eth0'
DOMAIN_NAME='lan'
DNS_FORWARDERS='8.8.8.8'
OPT_SYSLOGD='yes'
SYSLOGD_DEST_N='2'
SYSLOGD_DEST_1='*.*;kern.!* /var/log/syslog'
SYSLOGD_DEST_2='kern.* /var/log/kern.log'
OPT_KLOGD='yes'

#
# package 'dns_dhcp'
#
OPT_DHCP_CLIENT='yes'

#
# package 'httpd'
#
OPT_HTTPD='yes'
HTTPD_USER_N='0'

#
# package 'chrony'
#
OPT_CHRONY='yes'
CHRONY_TIMESERVER_N='1'
CHRONY_TIMESERVER_1='fence.schulz.ip-v6.eu'

#
# package 'sshd'
#
OPT_SSHD='yes'

#
# package 'circuits'
#
CIRC_N='1'
CIRC_1_NAME='dhcp'
CIRC_1_ENABLED='yes'
CIRC_1_TYPE='dhcp'
CIRC_1_UP='yes'
CIRC_1_USEPEERDNS='yes'
CIRC_1_DHCP_DEV='IP_NET_1_DEV'
CIRC_1_NETS_IPV4_N='1'
CIRC_1_NETS_IPV4_1='0.0.0.0/0'