====== Lyx OS – Interaktive Shell ====== Diese Seite dokumentiert die Lyx OS Shell (''shell/shell.lyx'', WP13): Built-in-Befehle, das r3_sc_block-Kommunikationsprotokoll und die Implementierungsdetails für Entwickler, die die Shell erweitern oder eigene Ring-3-Anwendungen schreiben möchten. → [[lyxos:prozesse|Prozesse & Ring-3]] · [[lyxos:architektur|Architektur]] · [[lyxos:kernel|Kernel-Interna]] ---- ===== 1. Überblick ===== Die Shell ist der erste Ring-3-Prozess in Lyx OS. Sie wird von ''SysSpawn("shell.elf", r3sc_addr)'' gestartet und läuft in einem eigenen Adressraum (separates PML4, ''SHELL_VMA_BASE = 0x400000''). **Start-Ausgabe:** Lyx Shell v0.1 - Ring-3 active Type 'help' for available commands. lyx> Der Prompt ist ''lyx> ''. Die Shell liest Zeichen einzeln über ''sys_read(0, buf, 1)'' (stdin = Tastatur-fd) und puffert eine Zeile (max. 63 Zeichen). Backspace/Delete (Byte 8 oder 127) löscht das letzte Zeichen inklusive Terminal-Echo. ---- ===== 2. Built-in-Befehle ===== ^ Befehl ^ Syntax ^ Beschreibung ^ | ''help'' | ''help'' | Befehlsübersicht ausgeben | | ''exit'' | ''exit'' | Shell beenden (gibt 0 zurück) | | ''clear'' | ''clear'' | Terminal-Screen leeren (ANSI ''ESC[2J ESC[H'') | | ''pwd'' | ''pwd'' | Aktuelles Arbeitsverzeichnis ausgeben | | ''cd'' | ''cd '' | Verzeichnis wechseln | | ''ls'' | ''ls'' / ''ls '' | Verzeichnis-Inhalt auflisten | | ''cat'' | ''cat '' | Datei-Inhalt ausgeben (63-Byte-Chunks) | | ''echo'' | ''echo [text]'' | Text ausgeben | | ''locale'' | ''locale'' | Aktuelle Locale-Einstellungen anzeigen | | ''diskinfo'' | ''diskinfo'' | Erkannte ATA-Laufwerke auflisten | | ''part'' | ''part '' | MBR-Partition anlegen | | ''mkfat32'' | ''mkfat32 '' | FAT32 formatieren | | ''mount'' | ''mount '' | Disk als Volume mounten | | ''vol'' | ''vol '' | Aktives Volume wechseln (CWD → ''/'')| **Beispiele:** lyx> ls [DIR] . [DIR] testdir [FILE] kernel.elf [FILE] shell.elf lyx> cat kernel.elf (Binärdaten...) lyx> cd testdir lyx> pwd /testdir lyx> diskinfo [0] Primary Master 128 MB (262144 sectors) [1] Primary Slave 256 MB (524288 sectors) lyx> part 1 2048 524287 Partitioning disk 1 LBA 2048..524287 (522240 sectors)... OK lyx> mkfat32 1 2048 522240 Format disk 1 LBA 2048 sz=522240... OK lyx> mount 1 1 2048 Mount disk 1 LBA 2048 as vol 1... OK lyx> vol 1 Switch to vol 1... OK lyx> ls (Dateien von Disk 1, Partition 1) lyx> locale locale=de_DE keyboard=de ... ---- ===== 3. Syscall-Protokoll (r3_sc_block) ===== Die Shell kommuniziert mit dem Kernel nicht über klassische ''SYSCALL''-Register-Konventionen, sondern über einen gemeinsamen Speicherblock: den ''r3_sc_block''. Dieser Block ist physisch adressiert und in der Prozess-Page-Table als user-zugänglich gemappt. **Ablauf für jeden Syscall aus Ring-3:** 1. Shell schreibt Syscall-Nr + Argumente via poke64 in den r3_sc_block 2. Shell ruft lyx_trigger() auf → mmap(0, -6, ...) 3. Kernel (handle_r3_syscall) erhält Kontrolle: - liest r3sc[NR], r3sc[A0], r3sc[A1], r3sc[A2] - führt Aktion aus (VfsRead, VfsOpen, ...) - schreibt Ergebnis nach r3sc[RESULT] 4. Kernel gibt Kontrolle via IRETQ zurück (vmm_op33) 5. Shell liest Ergebnis via lyx_trigger()-Rückgabewert **Implementierung in ''shell.lyx'':** // Einmalig beim Start: Blockadresse holen g_r3sc := lyx_get_r3sc(); // mmap(0, -5, ...) → &r3_sc_block // Syscall-Wrapper-Muster: fn lyx_read(fd: int64, buf: int64, count: int64): int64 { poke64(g_r3sc + R3_OFF_NR, 0); // nr = 0 (sys_read) poke64(g_r3sc + R3_OFF_A0, fd); poke64(g_r3sc + R3_OFF_A1, buf); poke64(g_r3sc + R3_OFF_A2, count); return lyx_trigger(); // Ergebnis = r3sc[RESULT] } **Sentinel-Werte:** * ''mmap(0, -5, ...)'' → Kernel gibt physische Adresse des r3_sc_block zurück * ''mmap(0, -6, ...)'' → Kernel führt ''handle_r3_syscall'' aus, gibt result zurück * Der Bootloader (''boot.asm'') fängt ''rsi < 0'' in ''vmm_op'' ab; −5 und −6 sind spezielle Operationscodes ---- ===== 4. Puffer-Layout ===== Die Shell alloziert ihre globalen Puffer beim Start via ''mmap'' (''user_mmap'', Syscall 9): ^ Variable ^ Größe ^ Zweck ^ | ''g_kbuf'' | 8 B | Einzelzeichen-Lese-Puffer (sys_read 1 Byte) | | ''g_line'' | 64 B | Eingabezeile (max. 63 Zeichen + Null) | | ''g_lsbuf'' | 1024 B | Verzeichnis-Eintrags-Puffer für ''ls'' (21 Einträge × 48 B) | | ''g_catbuf'' | 64 B | Chunk-Puffer für ''cat'' (63 Byte pro Leseaufruf) | | ''g_argbuf'' | 33 B | Argument-Extraktions-Puffer (max. 32 Zeichen) | | ''g_chbuf'' | 1 B | Einzelzeichen-Ausgabe-Puffer für ''lyx_putchar'' | **Verzeichnis-Eintrag-Format** (''g_lsbuf'', 48 Bytes pro Eintrag): ^ Offset ^ Feld ^ Beschreibung ^ | 0–31 | name[32] | Dateiname (null-terminiert, max. 31 Zeichen) | | 32–39 | size | Dateigröße in Bytes (int64) | | 40–47 | type | Typ: 0 = Datei, 1 = Verzeichnis | ---- ===== 5. Eigene Syscall-Wrapper schreiben ===== Neue Ring-3-Operationen folgen immer dem gleichen Muster. Beispiel: ein Wrapper für ''sys_locale_info'' (nr=81): fn lyx_locale(buf: int64): int64 { poke64(g_r3sc + R3_OFF_NR, 81); // Syscall-Nummer poke64(g_r3sc + R3_OFF_A0, buf); // Argument: Puffer-Adresse (user-virtuell) return lyx_trigger(); // Ergebnis } **Wichtig beim Umgang mit Puffer-Adressen:** * Adressen in Ring-3 sind user-virtuelle Adressen * Der Kernel übersetzt sie intern via ''VmmPhysFromUserVirt(proc_cr3, vaddr)'' in physische Adressen * Deshalb müssen Puffer via ''mmap'' alloziert werden (damit sie in der Prozess-Page-Table stehen) — kein direktes Pointer-Casting auf Stack-Adressen ---- ===== 6. Bekannte Compiler-Einschränkungen (lyxc 0.9.7A) ===== * **Globale ''pchar''-Literal-Initialisierer** werden nicht emittiert. ''g_argbuf'' und ''g_chbuf'' müssen in ''main()'' manuell initialisiert werden: g_argbuf := "________________________________a"; g_chbuf := "x"; * **Negative Literale in ''mmap''**: ''mmap(0, -6, ...)'' muss über eine lokale Variable übergeben werden, da der Compiler negative Literale als drittes Argument falsch übergibt. Die Shell nutzt ''var s: int64 := -6; mmap(0, s, ...)''. * **Direkte ''SYSCALL rax=1'' (write)**: Schreibt auf user-Stack und korrumpiert Register. Die Shell umgeht dies, indem alle Ausgaben über ''lyx_putchar'' (Syscall nr=1 via r3_sc_block + ''lyx_trigger'') laufen. Letzte Aktualisierung: 2026-06-13