====== 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.
→ [[lyxos:architektur|Architektur]] · [[lyxos:syscalls|Syscall-ABI]] · [[lyxos:start|Ü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:
- **Bootloader assemblieren** — ''nasm -f bin boot.asm -o BOOTX64.EFI''
- **Kernel kompilieren** — ''lyxc --target=lyxos'' für jeden ''.lyx''-Kernel-File, dann Linken zu ''kernel.elf''
- **Disk-Image aufbauen** — 128-MB-GPT-Image mit FAT32-EFI-Partition
- **ESP befüllen** — ''BOOTX64.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 =====
- Neue Datei ''kernel/meinmodul.lyx'' anlegen
- ''import meinmodul;'' am Anfang von ''kernel.lyx'' eintragen
- Initialisierungsfunktion (z.B. ''MeinModulInit()'') an der richtigen Stelle in ''kernel.lyx'' aufrufen
- 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