Ein maßgeschneidertes Embedded Linux für die Ausbildung.
Virtuelle Hardware. Echtes Lernen.
== LabOS Linux 1.0 == Loading kernel from virtio 0:1 ... Booting ... Starting network: OK Starting board agent... LabOS Linux 1.0 board login: root Welcome to LabOS Linux 1.0 Maintained by Andreas Biederbeck # gpioget gpiochip0 0 1 2 3 0 0 0 0 # i2cdetect -y 0 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- # _
LabOS ist ein minimales, maßgeschneidertes ARM64-Linux, das speziell für die Embedded-Ausbildung entwickelt wurde. Es läuft als virtuelle Maschine in QEMU und simuliert echte Hardware-Peripherie — GPIO-Pins, I2C-Busse und UART-Schnittstellen — direkt im Browser.
32 virtuelle GPIO-Pins über das Kernel-Modul gpio-sim. LEDs ansteuern, Buttons lesen, PWM simulieren — alles über libgpiod wie auf echter Hardware.
Virtuelle I2C-Geräte über i2c-stub. Temperatursensoren, EEPROMs und andere Peripherie. Ansprechbar mit i2cget, i2cset und i2cdetect.
Virtuelle serielle Schnittstellen über PTY-Paare mit socat. Serielle Protokolle testen, ohne physische Hardware.
Copy-on-Write SD-Karten-Images im QCOW2-Format. Änderungen überleben Sessions. Snapshots jederzeit möglich.
Voller Shell-Zugriff über xterm.js. GPIO-LEDs und Buttons als interaktive UI-Elemente. Kein Setup, kein SSH — einfach loslegen.
Boot in unter 5 Sekunden. CoW-Overlay in Millisekunden erstellt. Jeder Teilnehmer bekommt seine eigene VM-Instanz.
LabOS besteht aus drei Schichten: dem Host-Service, der QEMU-VM und dem Guest Agent. Die Kommunikation zwischen Host und Guest läuft über einen virtio-serial Kanal mit einem JSON-Lines-Protokoll.
Setzt GPIO-Output via libgpiod: gpioset gpiochip0 0=1
Aktualisiert /sys/.../sim_gpio0/value
Pollt sysfs alle 50ms, erkennt Änderung, sendet JSON über virtio-serial
Empfängt Event, broadcastet an verbundene WebSocket-Clients
LED-Element wechselt auf „leuchtend“
LabOS wird mit Buildroot 2025.02 gebaut — einem Framework zur Erzeugung vollständiger Linux-Systeme für Embedded-Geräte. Der gesamte Build läuft reproduzierbar in einem mehrstufigen Docker-Container.
# Stage 1: Buildroot Kompilierung
FROM ubuntu:24.04 AS builder
# Buildroot 2025.02 herunterladen
RUN wget buildroot-2025.02.tar.xz \
&& tar xf buildroot-2025.02.tar.xz
# External Tree mit eigener Konfiguration
COPY board/ /external/
# Konfiguration anwenden & bauen
RUN make BR2_EXTERNAL=/external \
board_aarch64_defconfig \
&& make -j$(nproc)
# rootfs in QCOW2 konvertieren
RUN qemu-img convert -f raw -O qcow2 \
rootfs.ext2 rootfs.qcow2
# Stage 2: Nur die fertigen Artefakte
FROM alpine:3.21
COPY --from=builder Image rootfs.qcow2 \
u-boot.bin sd.img /output/
Buildroot wird über einen External Tree konfiguriert. Damit bleiben alle Anpassungen getrennt vom Buildroot-Quellcode und sind versionierbar:
board/
├── external.desc # Name & Beschreibung
├── configs/
│ ├── board_aarch64_defconfig # Buildroot-Konfiguration
│ └── kernel.config # Kernel-Fragment
├── overlay/ # Root-FS Overlay
│ ├── etc/init.d/S99agent
│ ├── etc/fstab
│ └── etc/os-release
├── agent/ # Guest Agent (Python)
│ └── board_agent.py
├── genimage.cfg # SD-Image Layout
├── uboot.fragment # U-Boot Anpassungen
└── post-image.sh # Boot-Script & Image-Assembly
gpioget, gpioset, gpioinfo)
i2cdetect, i2cget, i2cset)
| Architektur | aarch64 (ARM 64-Bit) |
| CPU | Cortex-A53 |
| RAM | 256 MB |
| Root-FS | EXT4, 128 MB |
| Kernel | Linux 6.12 |
| Bootloader | U-Boot 2025.01 |
| C-Library | glibc |
| Init-System | BusyBox init |
| Konsole | ttyAMA0 (PL011 UART) |
Image | ARM64 Kernel (unkomprimiert) |
rootfs.qcow2 | Root-FS (QCOW2 für CoW) |
u-boot.bin | Bootloader Binary |
sd.img | Komplettes SD-Karten-Image (MBR) |
Der Linux 6.12 Kernel wird auf Basis der aarch64 defconfig gebaut und mit einem
eigenen Konfigurationsfragment angepasst. Nur die für die Simulation benötigten Treiber werden aktiviert.
Virtio-Treiber ermöglichen effiziente Kommunikation zwischen Host und Guest in QEMU.
CONFIG_VIRTIO=y # Virtio-Kern
CONFIG_VIRTIO_PCI=y # PCIe-Transport
CONFIG_VIRTIO_MMIO=y # Memory-mapped I/O
CONFIG_VIRTIO_BLK=y # Block Device (Disk)
CONFIG_VIRTIO_NET=y # Netzwerk
CONFIG_VIRTIO_CONSOLE=y # Serieller Kanal
Das Modul gpio-sim erzeugt virtuelle GPIO-Chips, die über ConfigFS konfiguriert werden.
CONFIG_GPIOLIB=y # GPIO-Subsystem
CONFIG_GPIO_SIM=m # gpio-sim (Modul!)
CONFIG_GPIO_SYSFS=y # sysfs-Interface
CONFIG_CONFIGFS_FS=y # ConfigFS für Setup
gpio-sim wird als Modul (=m) gebaut und erst nach dem Boot vom Agent geladen.
So kann der Agent die Chip-Konfiguration dynamisch über ConfigFS aufbauen.
Mit i2c-stub werden virtuelle I2C-Devices mit konfigurierbaren Adressen erzeugt.
CONFIG_I2C=y # I2C-Kern
CONFIG_I2C_CHARDEV=y # Character Device
CONFIG_I2C_STUB=m # i2c-stub (Modul!)
Ebenfalls modular — wird vom Agent mit den konkreten Chip-Adressen geladen:
modprobe i2c-stub chip_addr=0x48,0x50
Ermöglicht den Dateiaustausch zwischen Host und Guest über ein gemeinsames Verzeichnis.
CONFIG_NET_9P=y # 9P-Protokoll
CONFIG_NET_9P_VIRTIO=y # Virtio-Transport
CONFIG_9P_FS=y # 9P-Dateisystem
Im Guest gemountet als /mnt/host. Dateien können so zwischen Host und VM
ausgetauscht werden (Upload/Download im Browser).
Unnötige Treiber sind deaktiviert — für minimale Image-Größe und saubere Boot-Logs.
# CONFIG_E1000 is not set # Intel NIC
# CONFIG_MEGARAID_SAS is not set # RAID
# CONFIG_MMC is not set # SD card
# CONFIG_SOUND is not set # Audio
# CONFIG_NFS_FS is not set # NFS
# CONFIG_BLK_DEV_LOOP is not set # Loop
Der Guest Agent (board_agent.py) ist ein Python-Daemon, der innerhalb der VM läuft.
Er ist die Brücke zwischen den Kernel-Modulen und dem Host-Service —
kommuniziert über virtio-serial mit einem JSON-Lines-Protokoll.
Verwaltet den virtuellen GPIO-Chip über ConfigFS und sysfs.
/sys/kernel/config/gpio-sim/sim_gpioN/value alle 50ms auf Output-Änderungensim_gpioN/pull (pull-up/pull-down)Erstellt virtuelle serielle Schnittstellen über PTY-Paare.
socat für PTY-Paare: /dev/ttyVIRT0 ↔ /dev/ttyVIRT0-host/dev/ttyVIRT0 wie eine echte UARTKonfiguriert das Kernel-Modul i2c-stub für virtuelle I2C-Devices.
i2cset// GPIO-Pin setzen (Button-Druck)
{"type": "gpio_set", "line": 5, "value": 1}
// I2C-Devices konfigurieren
{"type": "setup_i2c",
"addresses": ["0x48", "0x50"]}
// Daten an UART senden
{"type": "uart_write",
"port": 0, "data": "AT+OK\r\n"}
// I2C-Register beschreiben
{"type": "i2c_set_register",
"bus": 0, "addr": "0x48",
"reg": "0x00", "data": [25, 128]}
// Agent bereit
{"type": "agent_ready"}
// GPIO-Änderung erkannt
{"type": "gpio_changed",
"chip": 0, "line": 0, "value": 1}
// UART-Daten empfangen
{"type": "uart_data",
"port": 0, "data": "Hello UART"}
#!/bin/sh
mount -t configfs configfs /sys/kernel/config
mount -t debugfs debugfs /sys/kernel/debug
modprobe gpio_sim
dmesg -n 1 # Kernel-Logs unterdrücken
start-stop-daemon -S -b \
-x /usr/bin/python3 -- /data/board_agent.py
Vom QEMU-Start bis zur interaktiven Shell: So bootet LabOS.
Host-Service erstellt ein CoW-Overlay (qcow2) über dem Base-Image und startet QEMU mit virtio-serial, virtio-blk und virtio-9p.
Bootloader lädt boot.scr vom VFAT-Boot-Partition, setzt Kernel-Argumente (root=/dev/vda2 console=ttyAMA0), lädt den Kernel.
ARM64-Kernel initialisiert virtio-Devices, mountet EXT4 Root-FS, startet BusyBox init.
BusyBox init führt /etc/init.d/ aus: Netzwerk, crond, und als letztes S99agent.
Agent startet, erkennt virtio-serial Device, konfiguriert GPIO-Chip über ConfigFS, sendet agent_ready.
Shell bereit. Host-Service empfängt agent_ready, konfiguriert I2C-Devices und UART. Board ist einsatzbereit.
MBR-Partitionstabelle · Generiert mit genimage
Neben der virtuellen LabOS-Variante gibt es ein angepasstes Image für den Raspberry Pi — sowohl als QEMU-Emulation als auch für echte Hardware.
| CPU | Cortex-A72 (statt A53) |
| RAM | 512 MB (statt 256 MB) |
| Hostname | raspberry |
| 9P-Dateisystem | Nicht enthalten |
| Boot-Partition | Enthält config.txt |
Für echte Raspberry Pis läuft ein Board Agent (Go) auf dem Pi,
der sich per WebSocket mit dem Host verbindet. Gleiches Protokoll,
aber echte GPIO-Pins (gpiod), echte I2C-Busse und echte UARTs.
Dozenten können physische Boards über ein Admin-Interface registrieren und Teilnehmern zuweisen — für den nahtlosen Übergang von Simulation zu Hardware.
Andreas Biederbeck
Schulungsplattform / LabOS
E-Mail: info@biederbeck.de
Andreas Biederbeck
Haftung für Inhalte: Die Inhalte dieser Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen.
Haftung für Links: Diese Website enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter verantwortlich.
Diese Website erhebt und speichert keine personenbezogenen Daten. Es werden keine Cookies gesetzt und keine Tracking-Dienste eingesetzt. Die eingebundenen Webfonts (Google Fonts) werden beim Seitenaufruf vom Google-Server geladen.