Open Source · ARM64 · Buildroot 2025.02

LabOS Linux

Ein maßgeschneidertes Embedded Linux für die Ausbildung.
Virtuelle Hardware. Echtes Lernen.

board — ttyAMA0
== 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: -- -- -- -- -- -- -- --
# _
256 MB RAM
128 MB Root-FS
6.12 Linux Kernel
< 5s Boot-Zeit

Was ist LabOS?

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.

🔌

GPIO-Simulation

32 virtuelle GPIO-Pins über das Kernel-Modul gpio-sim. LEDs ansteuern, Buttons lesen, PWM simulieren — alles über libgpiod wie auf echter Hardware.

📡

I2C-Devices

Virtuelle I2C-Geräte über i2c-stub. Temperatursensoren, EEPROMs und andere Peripherie. Ansprechbar mit i2cget, i2cset und i2cdetect.

🔄

UART-Kommunikation

Virtuelle serielle Schnittstellen über PTY-Paare mit socat. Serielle Protokolle testen, ohne physische Hardware.

💾

Persistente Images

Copy-on-Write SD-Karten-Images im QCOW2-Format. Änderungen überleben Sessions. Snapshots jederzeit möglich.

🌐

Browser-basiert

Voller Shell-Zugriff über xterm.js. GPIO-LEDs und Buttons als interaktive UI-Elemente. Kein Setup, kein SSH — einfach loslegen.

🚀

Sofort startklar

Boot in unter 5 Sekunden. CoW-Overlay in Millisekunden erstellt. Jeder Teilnehmer bekommt seine eigene VM-Instanz.

Architektur

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.

Browser
Board UIReact + xterm.js
↓ REST + WebSocket ↓
Host (K8s Pod)
VM ManagerQEMU Lifecycle
Device ManagerGPIO, I2C, UART State
Console ProxySerial ↔ WebSocket
↓ virtio-serial (JSON-Lines) ↓
Guest VM (QEMU aarch64)
Guest Agentboard_agent.py
gpio-simKernel-Modul
i2c-stubKernel-Modul
socat PTYUART Emulation

Datenfluss: GPIO-Output (LED)

1
Guest-Programm

Setzt GPIO-Output via libgpiod: gpioset gpiochip0 0=1

2
Kernel (gpio-sim)

Aktualisiert /sys/.../sim_gpio0/value

3
Guest Agent

Pollt sysfs alle 50ms, erkennt Änderung, sendet JSON über virtio-serial

4
Device Manager

Empfängt Event, broadcastet an verbundene WebSocket-Clients

5
Browser UI

LED-Element wechselt auf „leuchtend“

Buildroot — Das Build-System

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.

Build-Prozess

Dockerfile.buildroot (vereinfacht)
# 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/

External Tree

Buildroot wird über einen External Tree konfiguriert. Damit bleiben alle Anpassungen getrennt vom Buildroot-Quellcode und sind versionierbar:

Verzeichnisstruktur
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

Installierte Pakete

BusyBox Leichtgewichtige Unix-Tools und Init-System
libgpiod + Tools GPIO Character Device Library (gpioget, gpioset, gpioinfo)
i2c-tools I2C-Bus Diagnose (i2cdetect, i2cget, i2cset)
socat Multiplex I/O für virtuelle UART-PTY-Paare
Python 3 Laufzeitumgebung für den Guest Agent
U-Boot 2025.01 Bootloader mit QEMU ARM64 Konfiguration

Ziel-Plattform

Architekturaarch64 (ARM 64-Bit)
CPUCortex-A53
RAM256 MB
Root-FSEXT4, 128 MB
KernelLinux 6.12
BootloaderU-Boot 2025.01
C-Libraryglibc
Init-SystemBusyBox init
KonsolettyAMA0 (PL011 UART)

Build-Artefakte

ImageARM64 Kernel (unkomprimiert)
rootfs.qcow2Root-FS (QCOW2 für CoW)
u-boot.binBootloader Binary
sd.imgKomplettes SD-Karten-Image (MBR)

Kernel-Konfiguration

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 (QEMU-Plattform)

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

GPIO-Simulation

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.

I2C-Interface

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

9P-Dateisystem

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).

Deaktivierte Treiber

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

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.

Komponenten

GpioSimManager

Verwaltet den virtuellen GPIO-Chip über ConfigFS und sysfs.

  • Erstellt 32-zeiligen GPIO-Chip via /sys/kernel/config/gpio-sim/
  • Pollt sim_gpioN/value alle 50ms auf Output-Änderungen
  • Injiziert Input-Werte über sim_gpioN/pull (pull-up/pull-down)

UartManager

Erstellt virtuelle serielle Schnittstellen über PTY-Paare.

  • Nutzt socat für PTY-Paare: /dev/ttyVIRT0/dev/ttyVIRT0-host
  • Guest-Programme öffnen /dev/ttyVIRT0 wie eine echte UART
  • Agent leitet Daten bidirektional über virtio-serial weiter

I2cStubManager

Konfiguriert das Kernel-Modul i2c-stub für virtuelle I2C-Devices.

  • Lädt Modul dynamisch mit konkreten Chip-Adressen
  • Setzt Register-Werte über i2cset
  • Speichert Register-State, antwortet auf Read-Requests vom Host

Protokoll (virtio-serial)

Host → Guest
// 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]}
Guest → Host
// 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"}

Startup

/etc/init.d/S99agent
#!/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

Boot-Prozess

Vom QEMU-Start bis zur interaktiven Shell: So bootet LabOS.

t=0

QEMU startet

Host-Service erstellt ein CoW-Overlay (qcow2) über dem Base-Image und startet QEMU mit virtio-serial, virtio-blk und virtio-9p.

t+0.5s

U-Boot

Bootloader lädt boot.scr vom VFAT-Boot-Partition, setzt Kernel-Argumente (root=/dev/vda2 console=ttyAMA0), lädt den Kernel.

t+1s

Linux Kernel

ARM64-Kernel initialisiert virtio-Devices, mountet EXT4 Root-FS, startet BusyBox init.

t+3s

Init-Scripts

BusyBox init führt /etc/init.d/ aus: Netzwerk, crond, und als letztes S99agent.

t+4s

Guest Agent

Agent startet, erkennt virtio-serial Device, konfiguriert GPIO-Chip über ConfigFS, sendet agent_ready.

t+5s

Login-Prompt

Shell bereit. Host-Service empfängt agent_ready, konfiguriert I2C-Devices und UART. Board ist einsatzbereit.

SD-Image Aufbau

Partition 1: VFAT (boot) 48 MB
Image, boot.scr
Partition 2: EXT4 (rootfs) 128 MB
BusyBox, libgpiod, Python, Agent, ...

MBR-Partitionstabelle · Generiert mit genimage

Raspberry Pi Variante

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.

Unterschiede zum LabOS

CPUCortex-A72 (statt A53)
RAM512 MB (statt 256 MB)
Hostnameraspberry
9P-DateisystemNicht enthalten
Boot-PartitionEnthält config.txt

Physische Boards

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.

Impressum

Angaben gemäß § 5 TMG

Andreas Biederbeck
Schulungsplattform / LabOS

Kontakt

E-Mail: info@biederbeck.de

Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV

Andreas Biederbeck

Haftungsausschluss

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.

Datenschutz

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.