====== Lyx OS – Prozesse & Ring-3 ====== Diese Seite beschreibt, wie Ring-3-Prozesse in Lyx OS gestartet werden, wie sie mit dem Kernel kommunizieren und wie das Capability-System mit ''PLEDGE_*''-Bitmasks die Berechtigungen einschränkt. Implementierung: ''kernel/ring3.lyx'' (WP12 + WP14). → [[lyxos:architektur|Architektur]] · [[lyxos:shell|Shell]] · [[lyxos:syscalls|Syscall-ABI]] · [[lyxos:kernel|Kernel-Interna]] ---- ===== 1. Überblick ===== Lyx OS kennt genau einen Mechanismus, um ein Programm zu starten: ''SysSpawn''. Es gibt kein ''fork()'', kein ''exec()'' und keine Copy-on-Write-Semantik. Jeder Prozess beginnt mit einem sauberen, neu angelegten Adressraum. **Prozess-Lebenszyklus:** Kernel-Start │ └──► SysSpawn("shell.elf", r3sc_addr) │ ├── ELF aus VFS öffnen + PT_LOAD-Segmente nach SHELL_PHYS_BASE laden ├── User-Stack anlegen (16 KB, physisch aus Bump-Allokator) ├── Per-Prozess PML4 anlegen (VmmAllocProcessPML4) ├── r3_sc_block-Seite in Prozess-Page-Table mappen ├── r3_launch(entry, stack_top, proc_pml4) → Ring-3 │ │ Ring-3 läuft (handle_r3_syscall bei jedem Syscall) │ └── Shell ruft sys_exit → SysSpawn gibt Exit-Code zurück ---- ===== 2. Speicherlayout (Ring-3-Prozess) ===== ^ Adressbereich ^ Inhalt ^ Anmerkung ^ | Phys. ''0x1800000'' (24 MB) | ELF-Binary (''SHELL_PHYS_BASE'') | PT_LOAD-Segmente 1:1 geladen | | Virt. ''0x400000'' (4 MB) | ''SHELL_VMA_BASE'' — Linker-Basis des ELF | phys = SHELL_PHYS_BASE + (vaddr − VMA_BASE) | | Stack | 16 KB (''USER_STACK_SIZE = 0x4000'') | Physisch aus Bump-Allokator; nach unten wachsend | | Virt. ''0x10000000'' (256 MB) | ''USER_HEAP_BASE'' — Heap-Basis für ''user_mmap'' | Wächst aufwärts; physisch via PmmAllocPage | Der Prozess bekommt ein eigenes PML4 (''proc_pml4''), das nur den ELF-Bereich, den Stack und den r3_sc_block mappt. Der restliche physische Speicher ist nicht sichtbar. ---- ===== 3. SysSpawn — ELF-Loader ===== pub fn SysSpawn(path: pchar, r3sc: int64): int64 { // ELF öffnen und Header lesen // PT_LOAD-Segmente laden: phys_dest = SHELL_PHYS_BASE + (p_vaddr − SHELL_VMA_BASE) // User-Stack anlegen (mmap, USER_STACK_SIZE Bytes) // VmmSetUserHeapVirt(USER_HEAP_BASE) → Heap-Pointer zurücksetzen // proc_pml4 = VmmAllocProcessPML4(ELF_base, elf_size, stack_phys, stack_size) // r3_sc_block-Seite user-zugänglich mappen // r3_launch(phys_entry, user_stack_top, proc_pml4) → Ring-3 starten // Syscall-Loop: while ret == -2 { ret = handle_r3_syscall(r3sc) } // return r3_exit_code() } **Was passiert bei jedem Ring-3-Syscall:** - Ring-3 schreibt Syscall-Nummer + Argumente in den r3_sc_block - Ring-3 ruft ''lyx_trigger()'' auf (''mmap(0,-6,...)'') - Der Kernel erhält Kontrolle, liest Block, führt Aktion aus, schreibt Ergebnis in ''r3sc[40]'' - Ring-3 liest Ergebnis aus ''r3sc[40]'' **Bekannter lyxc-0.9.7A-Bug:** Globale ''pchar''-Variablen mit Literal-Initialisierer werden nicht korrekt emittiert. ''SysSpawn'' setzt daher ''cwd_path_buf'' und ''open_path_buf'' manuell auf temporäre Strings, bevor es sie nutzt. ---- ===== 4. r3_sc_block-Protokoll ===== Der r3_sc_block ist ein physisch adressierter 48-Byte-Speicherblock, den der Bootloader anlegt und in BootInfo[5] (Offset 40) an den Kernel übergibt. ^ Offset ^ Feld ^ Typ ^ Bedeutung ^ | 0 | nr | int64 | Syscall-Nummer | | 8 | a0 | int64 | Argument 0 | | 16 | a1 | int64 | Argument 1 | | 24 | a2 | int64 | Argument 2 | | 32 | a3 | int64 | Argument 3 | | 40 | result | int64 | Rückgabewert (Kernel schreibt; Ring-3 liest) | **Aus Ring-3-Sicht (shell.lyx):** fn lyx_get_r3sc(): int64 { return mmap(0, -5, 0, 0, 0, 0); } // Blockadresse holen fn lyx_trigger(): void { mmap(0, -6, 0, 0, 0, 0); } // Syscall auslösen // Beispiel: sys_read(fd=0, buf, 1) var sc: int64 := lyx_get_r3sc(); // einmalig poke64(sc, 0); // nr = 0 (sys_read) poke64(sc + 8, 0); // a0 = fd (0 = stdin) poke64(sc + 16, buf); // a1 = Puffer-Adresse poke64(sc + 24, 1); // a2 = count lyx_trigger(); var result: int64 := peek64(sc + 40); **Adress-Übersetzung:** User-virtuelle Puffer-Adressen (aus Ring-3-Sicht) sind nicht identisch mit physischen Adressen. Der Kernel übersetzt sie via ''VmmPhysFromUserVirt(proc_cr3, vaddr)'' bevor er sie an VFS-Funktionen weitergibt. ---- ===== 5. Syscall-Tabelle (Ring-3) ===== Der Syscall-Handler ''handle_r3_syscall'' in ''ring3.lyx'' kennt folgende Nummern: ^ nr ^ Name ^ Argumente ^ Beschreibung ^ | 0 | sys_read | fd, buf, count | Lesen von stdin (fd=0) oder VFS-fd | | 1 | lyx_putchar | char, 1 | Zeichen auf COM1/FB ausgeben | | 2 | sys_open | path, flags, mode | Datei oder Verzeichnis öffnen | | 3 | sys_close | fd | Datei-Deskriptor schließen | | 9 | user_mmap | 0, size, ... | Userspace-Heap allozieren | | 79 | sys_getcwd | buf, size | Aktuelles Arbeitsverzeichnis | | 80 | sys_chdir | path | Verzeichnis wechseln | | 81 | sys_locale_info | buf | Locale-Zusammenfassung lesen | | 82 | sys_diskinfo | — | ATA-Disk-Übersicht ausgeben | | 83 | sys_mkpart | disk_id, lba, sectors | MBR-Partition anlegen | | 84 | sys_mkfat32 | disk_id, lba, sectors | FAT32-Partition formatieren | | 85 | sys_mount | vol_id, disk_id, lba | Disk als Volume mounten | | 86 | sys_vol | vol_id | Aktives Volume wechseln | **FD-Offset:** Ring-3-FDs sind VFS-FDs + 3. FD 0 aus Ring-3-Sicht → Tastatur (stdin). FD 1 aus Ring-3-Sicht → VFS-FD 0 (die erste vom Kernel geöffnete Datei). ---- ===== 6. Capability-System (PLEDGE_*) ===== Capabilities werden als Bitmask pro Prozess verwaltet. ''SysPledge'' kann die Maske nur einschränken, nie erweitern — ein Prozess kann sich irreversibel in einen minimal-privilegierten Zustand versetzen. pub con PLEDGE_STDIO: int64 := 1; // sys_read (stdin), lyx_putchar pub con PLEDGE_VFS: int64 := 2; // sys_open, sys_close, sys_getcwd, sys_chdir pub con PLEDGE_EXEC: int64 := 4; // sys_spawn (weiteren Prozess starten) pub con PLEDGE_BLOCK: int64 := 8; // Raw-Block-Device-Zugriff pub con PLEDGE_NET: int64 := 16; // Netzwerk-Sockets pub con PLEDGE_PROC: int64 := 32; // Prozess-Management pub con PLEDGE_ALL: int64 := 63; // alle Capabilities (Initial-Zustand) // Capabilities eines Prozesses dauerhaft einschränken: SysPledge(PLEDGE_STDIO | PLEDGE_VFS); // Ab jetzt: sys_spawn → ERR_CAPVIOL (WP15) **Capability Routing Gate** (''syscall_capability_class'' in ''ring3.lyx''): Jede Syscall-Nummer ist einer Capability-Klasse zugeordnet. Der Handler prüft bei jedem Syscall, ob die benötigte Klasse in ''pledge_mask'' des Prozesses enthalten ist. Verstoß → ERR_CAPVIOL (Enforcement ab WP15 implementiert). ^ Syscall ^ Benötigte Capability ^ | sys_read (fd=0, stdin) | PLEDGE_STDIO | | sys_open, sys_close, sys_getcwd, sys_chdir | PLEDGE_VFS | | user_mmap, lyx_putchar, sys_locale_info | — (immer erlaubt) | ---- ===== 7. Per-Prozess Adressraum ===== Jeder Ring-3-Prozess bekommt ein eigenes PML4. Die Kernel-Identity-Map ist im Prozess-PML4 **nicht** sichtbar — Ring-3 kann nicht direkt auf Kernel-Speicher zugreifen. // Kernel-Seite (SysSpawn): var proc_pml4: int64 := VmmAllocProcessPML4( SHELL_PHYS_BASE, elf_sz_pages, // ELF-Bereich stack_phys, USER_STACK_SIZE // User-Stack ); // r3_sc_block-Seite user-zugänglich mappen: var r3sc_page: int64 := r3sc & (~0xFFF); VmmMapUserPage(proc_pml4, r3sc_page, r3sc_page); // user_mmap-Syscall (nr=9): neue Pages in Prozess-PML4 eintragen: var phys: int64 := VmmPmmAllocPage(); VmmMapUserPage(proc_cr3, virt_base, phys); VmmSetUserHeapVirt(virt_base + 4096); Letzte Aktualisierung: 2026-06-13