Lyx OS – Kernel-Interna

Diese Seite richtet sich an Beitragende, die den Kernel weiterentwickeln möchten: Build-Ablauf, Modul-Interaktion, Initialisierungsreihenfolge und praktische Debugging-Techniken.

Architektur · Syscall-ABI · Übersicht


1. Verzeichnisstruktur

lyx-os/
├── bootloader/
│   ├── boot.asm          ← UEFI-Bootloader (~2900 Zeilen NASM)
│   ├── build.sh          ← Build-Skript (Assemble + Compile + Image)
│   └── run.sh            ← QEMU-Startskript
├── kernel/
│   ├── kernel.lyx        ← Kernel-Einstiegspunkt; importiert alle Module
│   ├── exceptions.lyx    ← IDT (256 Gates), CPU-Fault-Routing
│   ├── pmm.lyx           ← Physical Memory Manager (Bitmap)
│   ├── vmm.lyx           ← Virtual Memory Manager (PML4, Huge Pages)
│   ├── process.lyx       ← Prozessmodell, Scheduler-Bootstrap
│   ├── sync.lyx          ← Mutex, Semaphor, Spinlock
│   ├── smp.lyx           ← SMP, LAPIC, AP-Trampoline
│   ├── ata.lyx           ← ATA-Disk-I/O (sektorweise)
│   ├── fat32.lyx         ← FAT32-Implementierung (~795 Zeilen)
│   ├── vfs.lyx           ← Virtual-Filesystem-Layer
│   ├── keyboard.lyx      ← PS/2-Tastatureingabe
│   └── ring3.lyx         ← Ring-3-Sprungmodul (ELF-Loader + Syscall-Gate)
├── shell/
│   └── shell.lyx         ← Minimale Ring-3-Shell
└── doku/
    ├── syscalls.md       ← Syscall-ABI-Spezifikation
    └── fahrplan.md       ← Meilenstein-Plan


2. Build-Ablauf

bash bootloader/build.sh

Das Skript führt vier Phasen aus:

  1. Bootloader assemblierennasm -f bin boot.asm -o BOOTX64.EFI
  2. Kernel kompilierenlyxc –target=lyxos für jeden .lyx-Kernel-File, dann Linken zu kernel.elf
  3. Disk-Image aufbauen — 128-MB-GPT-Image mit FAT32-EFI-Partition
  4. ESP befüllenBOOTX64.EFI nach /EFI/BOOT/, kernel.elf in die Wurzel

Einzelne Module neu kompilieren (ohne Full-Build):

lyxc --target=lyxos kernel/fat32.lyx -o kernel/fat32.lyu

Die .lyu-Dateien sind vorkompilierte Units, die beim Link-Schritt zusammengefasst werden.


3. Kernel-Einstiegspunkt

Der Bootloader springt in pub fn main(boot_info_ptr: int64) in kernel.lyx. Das Argument ist ein Zeiger auf eine BootInfo-Struktur, die der Bootloader vor dem ExitBootServices-Aufruf aufgebaut hat:

// BootInfo-Layout (boot_info_ptr ist der Zeiger)
// Offset  0: mmap_ptr      — Zeiger auf UEFI-Memory-Map
// Offset  8: mmap_size     — Größe der Memory-Map in Bytes
// Offset 16: desc_size     — Größe eines Memory-Deskriptors
// Offset 24: kernel_end    — Physische Endadresse des Kernels
// Offset 32: bump_ptr_addr — Adresse des Bump-Allocator-Zeigers

pub fn main(boot_info_ptr: int64): int64 {
    var mmap_ptr:      int64 := peek64(boot_info_ptr);
    var mmap_size:     int64 := peek64(boot_info_ptr + 8);
    var desc_size:     int64 := peek64(boot_info_ptr + 16);
    var kernel_end:    int64 := peek64(boot_info_ptr + 24);
    var bump_ptr_addr: int64 := peek64(boot_info_ptr + 32);
    // ...
}


4. Initialisierungsreihenfolge

Die Reihenfolge ist durch Abhängigkeiten erzwungen — sie darf nicht geändert werden ohne die Konsequenzen zu prüfen:

1. PMM          — Bitmap-Allocator für physische Pages (braucht UEFI-Memory-Map)
2. VMM          — PML4-Page-Tables, CR3 laden (braucht PMM für Page-Allokationen)
3. RNG-Seed     — rdtsc() → RandomSeed() (braucht VMM für mmap)
4. Exceptions   — IDT mit 256 Gates laden (braucht funktionierende Page-Tables)
5. SMP          — LAPIC init, AP-Trampoline, INIT+SIPI senden
6. ATA          — ATA-Controller initialisieren (PIO-Modus)
7. FAT32        — BPB lesen, Root-Cluster bestimmen
8. VFS          — Virtuellen Filesystem-Layer aufbauen, FAT32 als Root mounten
9. Keyboard     — PS/2-Controller initialisieren
10. Scheduler   — ProcInit(), ProcActivate() → ab hier preemptiv (100 Hz)
11. Ring-3      — ring3.SysSpawn("shell.elf") → Userspace gestartet

 
Wichtig für Beitragende: Module dürfen mmap, peek64, poke64 und PrintLn erst nach VMM-Init aufrufen. Vor VfsInit() darf kein Code auf Dateien zugreifen.

5. Module: Aufgaben und Schnittstellen

PMM (pmm.lyx)

Bitmap-basierter Physical Memory Manager. 4 GB Adressraum, 4-KB-Pages.

PmmInit(mmap_ptr, mmap_size, desc_size, kernel_end);  // UEFI-Map parsen
var page: int64 := PmmAllocPage();                     // Eine Page allozieren
PmmFreePage(page);                                     // Page zurückgeben
var free: int64 := PmmFreeCount();                     // Anzahl freier Pages

Physische Adressen die von UEFI als EfiConventionalMemory markiert sind, werden als frei eingetragen. Kernel-Code und Bootloader-Bereich werden explizit reserviert.

VMM (vmm.lyx)

Verwaltet die PML4-Page-Table-Hierarchie. Alle 4 GB werden als Identity-Map (1:1 physisch ↔ virtuell) mit 2-MB-Huge-Pages eingetragen.

VmmInit();                       // PML4 aufbauen und CR3 laden
var cr3: int64 := VmmGetCr3();  // Aktuellen CR3 lesen

Zukünftig (M5): 4-KB-Seiten für Ring-3 mit separaten Page-Tables pro Prozess.

Exceptions (exceptions.lyx)

IDT mit 256 Gates. CPU-Exceptions werden auf panic() oder Fault-Handler weitergeleitet.

ExceptionsInit();   // Wird implizit von VMM aufgerufen; kein manueller Aufruf nötig

SMP (smp.lyx)

LAPIC-Init, AP-Trampoline bei physisch 0x8000, INIT+SIPI-Sequenz.

var ncpu: int64 := GetCpuCount();        // CPUID → Anzahl logischer CPUs
var ap_stack: int64 := mmap(0, 8192, 3, 34, -1, 0);
SmpPrepareTrampoline(ap_stack + 8192);   // Trampoline schreiben
SmpStartAp(1);                           // SIPI an LAPIC-ID 1
var ready: int64 := GetApStartedCount(); // Wieviele APs sind hochgefahren?

Sync (sync.lyx)

Kernel-seitige Synchronisationsprimitive für Ring-0-Code.

var mx: int64 := MutexCreate(MUTEX_PLAIN);
MutexLock(mx);
// kritischer Abschnitt
MutexUnlock(mx);

var sem: int64 := SemCreate(1, 0);
SemWait(sem);
SemPost(sem);

var sl: int64 := GetPrintSpinlockAddr();
SpinlockAcquire(sl);
SpinlockRelease(sl);

ATA (ata.lyx)

PIO-Mode-Disk-I/O. Sektorweises Lesen und Schreiben. Kein DMA in M1-M4.

// ATA wird implizit von VfsMountFat32 genutzt
// Direktzugriff (für Kernel-Code):
AtaRead(lba, sector_count, buf_addr);
AtaWrite(lba, sector_count, buf_addr);

FAT32 (fat32.lyx)

Vollständige FAT32-Implementierung. Cluster-Ketten, BPB-Parsing, Verzeichnis-Einträge (inkl. LFN-Unterstützung).

var root_cluster: int64 := Fat32GetRootCluster();
var n: int64 := Fat32ListDir(root_cluster);   // Root-Verzeichnis auflisten

VFS (vfs.lyx)

Abstraktionsschicht über FAT32. Exportiert das Linux-ähnliche open/read/write/close-Interface für Kernel-Code.

VfsInit();
VfsMountFat32(2048);                               // Partition ab LBA 2048 mounten
var fd: int64 := VfsOpen(AT_CWD, "shell.elf", O_READ);
var n:  int64 := VfsRead(fd, buf, 4096);
VfsClose(fd);

// Weitere Operationen:
VfsWrite(fd, buf, n);
VfsStat(AT_CWD, "test.txt", statbuf);
VfsMkdir(AT_CWD, "mydir");
VfsRename(AT_CWD, "old.txt", AT_CWD, "new.txt");
VfsUnlink(AT_CWD, "old.txt", 0);
var dfd: int64 := VfsOpenDir(AT_CWD, ".");
VfsReadDir(dfd, dbuf, 20);

Ring-3 (ring3.lyx)

ELF64-Loader und Userspace-Sprungmodul. Lädt ein ELF-Binary aus dem VFS, setzt Ring-3-Page-Tables auf und springt in main().

var exit_code: int64 := ring3.SysSpawn("shell.elf");


6. Neues Kernel-Modul hinzufügen

  1. Neue Datei kernel/meinmodul.lyx anlegen
  2. import meinmodul; am Anfang von kernel.lyx eintragen
  3. Initialisierungsfunktion (z.B. MeinModulInit()) an der richtigen Stelle in kernel.lyx aufrufen
  4. Im Build-Skript build.sh den Compile-Schritt für das neue Modul ergänzen:

lyxc --target=lyxos kernel/meinmodul.lyx -o kernel/meinmodul.lyu

Konventionen für Kernel-Module:

  • Alle exportierten Funktionen beginnen mit dem Modul-Präfix (z.B. PmmAllocPage, VfsOpen)
  • Kein Ring-3-Syscall-Interface direkt im Modul — Syscall-Handler werden in ring3.lyx registriert
  • PrintLn / PrintStr für Debug-Ausgaben; nicht im Release-Build verwenden wenn möglich
  • Kein globaler State außerhalb des Moduls; alle Zustandsvariablen als modul-lokale var deklarieren

7. Debugging-Techniken

Serielle Ausgabe

PrintLn, PrintStr und PrintInt schreiben sowohl auf COM1 (serielle Konsole) als auch auf den QEMU-Debug-Port (0xE9 / debugcon). Beides erscheint im Terminal wenn run.sh mit –headless gestartet wird.

bash bootloader/run.sh --headless 2>&1 | tee kernel_log.txt

Debug-Konsole

tail -f /tmp/lyx_debugcon.txt

QEMU-Monitor

Im laufenden QEMU Ctrl-Alt-2 drücken:

info registers       # CPU-Registerstand aller VCPUs
info mem             # Page-Table-Dump
x/10i $rip           # Disassembly ab aktuellem Instruction Pointer
xp /10gx 0x200000    # Physischer Speicher-Dump ab Kernel-Basis

Kernel-Panic und assert

// In Kernel-Code:
assert(condition, "Fehlermeldung");   // Hält die Maschine an wenn condition false
panic("Unbekannter Zustand");         // Sofortiger Halt mit Debug-Ausgabe

Beim Panic wird der Registerstand auf COM1 ausgegeben, bevor die CPU in eine Endlosschleife geht (hlt-Loop). QEMU-Monitor zeigt dann den letzten Stand.

GDB über QEMU

# run.sh mit GDB-Server starten
bash bootloader/run.sh --gdb     # wartet auf Port 1234

# In zweitem Terminal:
gdb kernel/kernel.elf
(gdb) target remote :1234
(gdb) break PmmAllocPage
(gdb) continue


8. Prozessmodell (M1–M4)

Im aktuellen Stand (M1-M4) ist das Prozessmodell minimal:

  • ProcInit() initialisiert den 100-Hz-Preemptiv-Scheduler
  • ProcCreate(fn_idx) erzeugt einen Kernel-Thread (Ring-0, nicht Ring-3)
  • ProcActivate() startet den Scheduler; ab hier ist der Kernel preemptiv
  • TaskSpawn(fn_idx, arg) erzeugt einen leichtgewichtigen Task im Kernel-Kontext
  • TaskAwait(task_id) wartet auf Task-Abschluss und gibt den Rückgabewert zurück
  • TaskRunPending() führt auf Single-Core alle ausstehenden Tasks synchron aus (BSP-Fallback)

Ab M5: vollständige Ring-3-Prozesse mit separaten Adressräumen, sys_spawn-Syscall und ELF-Loader über VFS.


9. Meilenstein-Übersicht

Meilenstein Inhalt Status
M1 — Boot & Bare-Metal UEFI-Bootloader, ELF-Loader
M2 — Kernel-Kern PMM, VMM, IDT, SMP
M3 — Runtime & Scheduler Laufzeit, Mutex, Semaphor, Prozessmodell
M4 — I/O ATA, FAT32, VFS, Keyboard, Ring-3-Shell
M5 — Ring-3 & Shell Vollständige Userspace-Shell, sys_spawn, ELF-Loader Offen
M6 — Netzwerk TCP/IP-Stack, Syscall-Gruppe 0x0600 Offen
M7 — Lyra-Agent Kernel-KI-Subsystem (kernel/ai.lyx), Lyra-Basisschicht Offen
M8–M10 Semantic OS Layer, Aerospace Safety, Distribution Offen

Der vollständige Fahrplan mit Work-Package-Details liegt unter doku/fahrplan.md im Repository.

Letzte Aktualisierung: 2026-06-09