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, per-Prozess CR3)
│ ├── process.lyx ← Prozessmodell, Scheduler-Bootstrap
│ ├── sync.lyx ← Mutex, Semaphor, Spinlock
│ ├── smp.lyx ← SMP, LAPIC, AP-Trampoline, Task-API
│ ├── ata.lyx ← ATA-Disk-I/O (Legacy + Multi-Disk)
│ ├── disk.lyx ← Disk-Abstraktion (Probing, Modell-String, Sektorzahl)
│ ├── mbr.lyx ← MBR-Partition-Tabelle (Lesen/Schreiben)
│ ├── fat32.lyx ← FAT32-Implementierung mit LFN-Schreiben
│ ├── vfs.lyx ← Virtual-Filesystem-Layer
│ ├── keyboard.lyx ← PS/2-Tastatureingabe, Layout-Umschaltung
│ ├── initrd.lyx ← In-Memory-Ramdisk (LIXR-Format)
│ ├── locale.lyx ← Internationalisierung (5 Locales, /config/locale.cfg)
│ ├── xhci.lyx ← XHCI-Treiber (USB 3.x, WP25)
│ ├── iofs_alloc.lyx ← IOFS Block-Allocator, mkfs, Page-I/O (WP01+WP09, Syscall 155)
│ ├── iofs_rw.lyx ← IOFS Mount/Read/Write/Checkpoint (WP02, Syscalls 156–164)
│ ├── iofs_wal.lyx ← IOFS Write-Ahead-Log (WP04, kernel-intern, kein Syscall)
│ ├── iofs_graph.lyx ← IOFS Graph/Edges (WP05, Syscalls 165–169)
│ ├── iofs_traverse.lyx ← IOFS Graph-Traversal BFS/DFS/Reachable/ShortestPath (WP06, Syscalls 170–173)
│ ├── iofs_names.lyx ← IOFS Named Nodes / Name-Store (WP07, Syscalls 174–178)
│ ├── ramdisk.lyx ← RAM-Disk-Treiber (IDs 4–7, Syscalls 153–154, WP30)
│ ├── net.lyx ← E1000-Treiber, DHCP, ARP, TCP-Stack (WP26, WP16-TCP, Syscalls 180–184)
│ └── ring3.lyx ← ELF64-Loader, SysSpawn, Syscall-Handler, Capabilities
├── shell/
│ └── shell.lyx ← Interaktive 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=lyxosfür jeden.lyx-Kernel-File, dann Linken zukernel.elf - Disk-Image aufbauen — 128-MB-GPT-Image mit FAT32-EFI-Partition
- ESP befüllen —
BOOTX64.EFInach/EFI/BOOT/,kernel.elfin 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
// Offset 40: r3sc_addr — Physische Adresse des r3_sc_block (Ring-3 ↔ Kernel-Protokoll)
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);
var r3sc_addr: int64 := peek64(boot_info_ptr + 40);
// ...
}
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. SMP — LAPIC init, AP-Trampoline, INIT+SIPI senden
5. VFS — fd-Tabelle anlegen; ruft intern Fat32VolumeInit + MbrInit auf
6. DiskInit — alle 4 ATA-Kanäle proben, Sektorzahl + Modell-String cachen
7. FAT32-Mount — VfsMountFat32(2048): BPB lesen, Root-Cluster bestimmen
8. LocaleInit — /config/locale.cfg lesen, Locale + Tastaturbelegung setzen
9. Initrd-Mount — InitrdBuildEmpty + VfsMountInitrd → /initrd/ mounten
10. Ring-3 — SysSpawn("shell.elf"): ELF laden, per-Prozess CR3, shell ausführen
11. Keyboard — KbdReadChar-Poll nach Shell-Exit (mit Timeout)
12. Sync-Tests — MutexCreate/Lock/Unlock, SemCreate/Wait/Post
13. Scheduler — ProcInit(), ProcActivate() → preemptiv (100 Hz), Endlos-Loop P0
Wichtig für Beitragende: Module dürfenmmap,peek64,poke64undPrintLnerst nach VMM-Init aufrufen. VorVfsInit()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. Ab WP14 kommen per-Prozess 4-KB-Page-Tables für Ring-3 hinzu.
VmmInit(); // PML4 aufbauen und CR3 laden
var cr3: int64 := VmmGetCr3(); // Aktuellen CR3 lesen
// WP14: Per-Prozess Address-Space
var proc_pml4: int64 := VmmAllocProcessPML4(
SHELL_PHYS_BASE, elf_sz_pages, // ELF-Bereich
stack_phys, USER_STACK_SIZE); // User-Stack
VmmMapUserPage(proc_pml4, virt_addr, phys_addr); // Einzelne 4-KB-Seite mappen
VmmSetUserHeapVirt(USER_HEAP_BASE); // Heap-Pointer für user_mmap setzen
var virt: int64 := VmmGetUserHeapVirt(); // Heap-Pointer lesen
var phys: int64 := VmmPhysFromUserVirt(proc_cr3, vaddr); // Adress-Translation
var proc_cr3: int64 := VmmGetCurrentProcCr3(); // Aktiven Prozess-CR3 lesen
var page: int64 := VmmPmmAllocPage(); // Physische Page allozieren
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 (512 Bytes/Sektor). Kein DMA.
// Legacy (Primary Master, disk_id implizit 0):
AtaReadSector(lba, dst_buf); // → vmm_op26
AtaWriteSector(lba, src_buf); // → vmm_op27
// Multi-Disk (disk_id: 0=pri-master 1=pri-slave 2=sec-master 3=sec-slave):
var sects: int64 := AtaProbe(disk_id, identify_buf); // IDENTIFY; -1 = nicht vorhanden
AtaReadDisk(disk_id, lba, dst_buf); // → vmm_op43; 0 = OK, -1 = Fehler
AtaWriteDisk(disk_id, lba, src_buf); // → vmm_op44; inkl. FLUSH CACHE
Disk (disk.lyx)
Abstraktionsschicht über ATA. Probt beim Start alle 4 ATA-Kanäle und cacht Sektorzahl + Modell-String (byteswapped aus IDENTIFY-Daten extrahiert).
DiskInit(); // alle 4 Kanäle proben + cachen
var ok: bool := DiskPresent(disk_id); // Disk vorhanden?
var n: int64 := DiskSectorCount(disk_id); // Sektorzahl (= Kapazität / 512 B)
DiskPrintInfo(); // Auflistung auf COM1/FB
MBR (mbr.lyx)
Liest und schreibt MBR-Partitions-Einträge (klassische 4-Einträge-Tabelle, Typ 0x0C = FAT32 LBA). Wird von VfsMkPart und VfsMountDisk genutzt.
MbrInit(); // 512-B-Buffer anlegen (von VfsInit aufgerufen)
MbrWritePart(disk_id, part_idx, lba_start, sectors); // Eintrag schreiben (0–3)
var out: int64 := mmap(0, 16, 3, 34, -1, 0);
MbrReadPart(disk_id, part_idx, out); // lba_start@out+0, sectors@out+8
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 und Initrd. Exportiert ein posix-artiges open/read/write/close-Interface für Kernel-Code und Ring-3-Syscall-Handler. MAX_FD = 32 gleichzeitig offene Deskriptoren.
VfsInit(); // von MbrInit + Fat32VolumeInit aufgerufen
VfsMountFat32(2048); // Partition ab LBA 2048 mounten
VfsMountInitrd(base, size); // Ramdisk unter /initrd/ mounten
VfsMountDisk(vol_id, disk_id, lba_start); // Volume mounten (Multi-Volume)
VfsSwitchVolume(vol_id); // Aktives Volume wechseln, CWD → "/"
var fd: int64 := VfsOpen(AT_CWD, "datei.txt", O_READ);
var n: int64 := VfsRead(fd, buf, 4096);
VfsWrite(fd, buf, n);
VfsSeek(fd, offset, SEEK_SET); // SEEK_SET=0, SEEK_CUR=1, SEEK_END=2
var fd2: int64 := VfsDup(fd, 0); // fd duplizieren
VfsClose(fd);
VfsStat(AT_CWD, "test.txt", statbuf); // size@statbuf+0, type@statbuf+8
VfsFstat(fd, statbuf);
var t: int64 := VfsFdType(fd); // FD_FILE=1, FD_KBD=2, FD_DIR=3, FD_INITRD=4
VfsSync(fd); // Stub (FAT32 schreibt per Commit)
VfsMkdir(AT_CWD, "mydir");
var dfd: int64 := VfsOpenDir(AT_CWD, ".");
VfsReadDir(dfd, dbuf, 20); // 48-B-DirEntry-Structs (name[32]+size[8]+type[8])
VfsRename(AT_CWD, "old.txt", AT_CWD, "new.txt");
VfsUnlink(AT_CWD, "old.txt", 0); // UNLINK_DIR=1 für Verzeichnisse
VfsGetCwd(buf, size);
VfsChdir(AT_CWD, "subdir");
var kbdfd: int64 := VfsOpenKbd(); // Keyboard-fd für IRQ-Binding
VfsIrqBind(1, kbdfd); // IRQ 1 (PS/2 Kbd) an fd binden
VfsDiskPrintInfo(); // Disk-Übersicht auf COM1/FB
VfsMkPart(disk_id, lba_start, sectors); // MBR-Partition anlegen
VfsMkFat32(disk_id, lba_start, sectors); // FAT32 formatieren
VfsMkFat32ByPart(disk_id, part_no); // FAT32 per Partitionsnummer
VfsMountByPart(vol_id, disk_id, part_no); // Mount per Partitionsnummer
VfsGptMount(disk_id, vol_id_start); // GPT scannen + alle FAT-Partitionen mounten
VfsDiskMap(disk_id); // Partition-Layout mit Lücken (vmm_op41)
→ Shell-Befehle, die diese Funktionen nutzen: diskinfo · partinfo · part · mkfat32 · mount · mountp · vol · gpt
FD-Slot-Layout (48 Bytes pro Eintrag in fd_table):
| Offset | Feld | Bedeutung |
|---|---|---|
| 0 | type | FD_EMPTY=0, FD_FILE=1, FD_KBD=2, FD_DIR=3, FD_INITRD=4 |
| 8 | cluster | Aktueller FAT32-Cluster (oder 0 für Initrd) |
| 16 | offset | Byte-Offset innerhalb der Datei |
| 24 | size | Dateigröße in Bytes |
| 32 | flags | FD_FLAG_CLOEXEC=1, FD_FLAG_APPEND=2 |
| 40 | dir_cluster | Start-Cluster des enthaltenden Verzeichnisses |
Initrd (initrd.lyx)
In-Memory-Ramdisk. Format: LIXR-Magic (4 Bytes) gefolgt von Einträgen (Name + Daten) und einem 4-Byte-Null-Terminator.
var base: int64 := InitrdBuildEmpty(); // Minimales 4-KB-Initrd mit Magic + Terminator
VfsMountInitrd(base, size); // Mountet unter /initrd/
InitrdFind("dateiname", size_out); // → physische Datenadresse oder -1
InitrdRead("dateiname", buf, offset, count); // Bytes lesen
InitrdEntryCount(); // Anzahl Einträge
Initrd-Eintrag-Layout:
[4B name_len uint32 LE, inkl. Null-Terminator]
[name_len Bytes Name (inkl. '\0')]
[8B data_len uint64]
[data_len Bytes Daten]
Locale (locale.lyx)
Internationalisierung: 5 Locales, Zahlenformat (Tausender-Trennzeichen), Zeitformat (24h oder 12h AM/PM) und Datumsformat (MDY, DMY-Dot, DMY-Slash). Liest /config/locale.cfg beim Start.
LocaleInit(); // /config/locale.cfg lesen + KbdSetLayout aufrufen
LocaleFormatInt(1234567, buf); // "1,234,567" (EN) oder "1.234.567" (DE)
LocaleFormatTime(14, 5, buf); // "14:05" (24h) oder "2:05 PM" (12h)
LocaleFormatDate(12, 6, 2026, buf); // je nach Locale: "06/12/2026" / "12.06.2026" / "12/06/2026"
LocaleSummary(buf); // Zusammenfassung als ASCII-String
LocalePrintBuf(buf); // Ausgabe auf COM1/FB (char-by-char)
| Konstante | Wert | Locale |
|---|---|---|
| LOC_EN_US | 0 | Englisch US (MDY, Komma-Tausender, 12h) |
| LOC_DE_DE | 1 | Deutsch (DMY-Punkt, Punkt-Tausender, 24h) |
| LOC_FR_FR | 2 | Französisch (DMY-Slash, Space-Tausender, 24h) |
| LOC_EN_GB | 3 | Englisch GB (DMY-Slash, Komma-Tausender, 24h) |
| LOC_ES_ES | 4 | Spanisch (DMY-Slash, Punkt-Tausender, 24h) |
Ring-3 (ring3.lyx)
ELF64-Loader, Syscall-Handler und Capability Routing Gate. SysSpawn lädt ein ELF-Binary aus dem VFS, legt per-Prozess-Page-Tables an und startet Ring-3. Der Kernel kommuniziert mit Ring-3 über den r3_sc_block (physisch adressierter Shared-Memory-Block).
// SysSpawn: ELF laden und als Ring-3-Prozess starten
var exit_code: int64 := SysSpawn("shell.elf", r3sc_addr);
// Capabilities einschränken (Aufruf aus Ring-3 via Syscall 87):
SysPledge(PLEDGE_STDIO | PLEDGE_VFS); // irreversibel; kein PLEDGE_EXEC mehr → sys_spawn verboten
WP17b: Fenstersystem, Ereignisse und Mauseingabe
ring3.lyx implementiert ab WP17b drei weitere Kernel-Subsysteme: Fenstertabelle, Ereignis-Ring und Mauseingabe.
Fenstertabelle (win_table)
Max. 32 Fenster × 104 Bytes/Slot. Lazy-Allokiert beim ersten sys_win_create-Aufruf aus dem PMM-Bump-Allocator (3 328 Bytes).
| Offset | Feld | Bedeutung |
|---|---|---|
| 0 | win_id | Monoton steigende ID; 0 = freier Slot |
| 8 | owner_pid | PID des erzeugenden Prozesses |
| 16 | x | Linke Kante (Pixel) |
| 24 | y | Obere Kante (Pixel) |
| 32 | w | Breite (Pixel) |
| 40 | h | Höhe (Pixel) |
| 48 | z_order | Z-Ordnung (höher = weiter vorne) |
| 56 | shm_id | (reserviert) |
| 64 | flags | Fenster-Flags (derzeit immer 0) |
| 72 | state | 0 = frei, 1 = aktiv |
| 80 | shm_phys | Physische Basisadresse des Fenster-Framebuffers |
| 88 | next_z | Scratch für Z-Order-Berechnung |
Framebuffer-Format: 32-bit XRGB, zeilenweise, stride = w × 4 Bytes. sys_win_get_fb (nr=115) mappt die physischen Seiten in den User-Adressraum des aufrufenden Prozesses.
Ereignis-Ring (ev_table)
Pro-PID-Ringpuffer für asynchrone Ereignisse (Maus, Tastatur, Fensterereignisse). Max. 8 PIDs × 16 Slots × 32 Bytes = 4 096 Bytes.
| Offset | Feld | Beschreibung |
|---|---|---|
| 0 | type | Ereignistyp |
| 8 | a | Parameter A |
| 16 | b | Parameter B |
| 24 | c | Parameter C (beim Senden via sys_event_send derzeit immer 0) |
Interne Syscall-Nummern (WP17b)
Diese Nummern beziehen sich auf das r3_sc_block-Protokoll (nr-Feld). Sie sind nicht identisch mit den öffentlichen Hex-Nummern aus Syscall-ABI.
| Nr | Name | Argumente (a0, a1, a2, a3) | Rückgabe |
|---|---|---|---|
| 110 | sys_win_create | x, y, w, h | win_id oder −1 |
| 111 | sys_win_destroy | win_id | 0 / −1 |
| 112 | sys_win_raise | win_id | 0 / −1; setzt z_order auf max+1 |
| 113 | sys_win_move | win_id, x, y | 0 / −1 |
| 114 | sys_win_resize | win_id, new_w, new_h | 0 / −1; nur Metadaten, kein FB-Realloc |
| 115 | sys_win_get_fb | win_id | user_virt des FB oder −1 |
| 121 | sys_event_send | pid, type, a, b | 0 |
| 122 | sys_event_recv | out_buf_uva | 0 (Ereignis vorhanden) / −1 (Ring leer) |
| 126 | sys_get_mouse_x | — | Maus-X-Koordinate |
| 127 | sys_get_mouse_y | — | Maus-Y-Koordinate |
| 128 | sys_get_mouse_btn | — | Maustasten-Bitmask |
| 129 | sys_set_mouse_bounds | max_x, max_y | 0; einmalig vom Compositor nach DisplayInit aufgerufen |
| 130 | sys_win_get_table_phys | — | Physische Adresse der win_table (Compositor-Direktzugriff) |
| 131 | sys_kbd_poll | — | Zeichen oder 0 (non-blocking, kehrt sofort zurück) |
| 148 | sys_win_set_title | win_id, title_ptr | 0 / −1; Fenster-Titel setzen (WP27) |
| 149 | sys_win_find_by_idx | idx | win_id oder −1 (WP27) |
| 150 | sys_win_get_title | win_id, buf | 0 / −1; Titel in buf lesen (WP27) |
| 151 | sys_win_get_geom | win_id, out_buf | 0 / −1; out_buf = {x,y,w,h} je int64 (WP29) |
| 152 | sys_win_get_pid | win_id | owner_pid oder −1 (WP29) |
Hinweise:
sys_win_createsetztz_order = max_z + 1— ein neues Fenster liegt immer zuoberst.sys_win_raisemacht dasselbe explizit. Es gibt keinsys_win_lower.sys_win_resize(nr=114) aktualisiert nur W/H-Metadaten. Der physische Framebuffer bleibt in der ursprünglichen Größe; nach einem Resize musssys_win_get_fberneut aufgerufen werden.sys_win_get_table_phys(nr=130) ist für den Compositor gedacht, der die win_table direkt liest um alle Fenster zu kompositionieren.sys_kbd_poll(nr=131) ist nicht-blockierend — gibt 0 zurück wenn kein Zeichen im PS/2-Ring liegt. Gegensatz:sys_read(kbd)(nr=0) blockiert bis ein Zeichen vorliegt.- Der Ereignis-Ring ist PID-indiziert (max. 8 PIDs); PIDs ≥ 8 werden von
ev_push/ev_popignoriert.
→ Vollständige Beschreibung: Prozesse & Ring-3
6. Neues Kernel-Modul hinzufügen
- Neue Datei
kernel/meinmodul.lyxanlegen import meinmodul;am Anfang vonkernel.lyxeintragen- Initialisierungsfunktion (z.B.
MeinModulInit()) an der richtigen Stelle inkernel.lyxaufrufen - Im Build-Skript
build.shden 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.lyxregistriert PrintLn/PrintStrfür Debug-Ausgaben; nicht im Release-Build verwenden wenn möglich- Kein globaler State außerhalb des Moduls; alle Zustandsvariablen als modul-lokale
vardeklarieren
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
Kernel-Threads (Ring-0):
ProcInit()initialisiert den 100-Hz-Preemptiv-SchedulerProcCreate(fn_idx)erzeugt einen Kernel-ThreadProcActivate()startet den Scheduler; ab hier preemptiv
SMP-Tasks (leichtgewichtig, Ring-0):
TaskSpawn(fn_idx, arg)erzeugt einen Task; max. 16 Slots; 0=square(arg), 1=sum(N)TaskPoll(task_id)→ 0=FREE, 1=PENDING, 2=RUNNING, 3=DONETaskAwait(task_id)spinnt auf DONE und gibtTaskGetResultzurückTaskRunPending()führt auf Single-Core alle Tasks synchron aus (BSP-Fallback)
Ring-3-Prozesse (Userspace):
SysSpawn(path, r3sc)lädt ELF aus VFS, legt per-Prozess CR3 an, startet Ring-3- Kommunikation über
r3_sc_block(physische Adresse aus BootInfo[5]) SysPledge(mask)schränkt Capabilities des laufenden Prozesses ein
→ Details: Prozesse & Ring-3 · SMP & Tasks
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 (inkl. LFN-Write), VFS, Keyboard | ✅ |
| M5 — Ring-3 & Shell | WP12–WP15 ✅; WP17b ✅; IOFS WP01–WP07+GC ✅; RAM-Disk WP30 ✅; XHCI WP25 ✅ | ✅ Abgeschlossen |
| M6 — Netzwerk | TCP-Stack WP16-TCP ✅ (E1000, DHCP, ARP, TCP, Syscalls 180–184) | POSIX-Socket-API offen |
| M7 — Lyra-Agent | Lyra-Domäne (Ring-3 privilegierter LLM-Dienst, kein kernel/ai.lyx) | 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-25
