====== 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