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.
→ Prozesse & Ring-3 · Architektur · Kernel-Interna
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.
| 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 <verz> | Verzeichnis wechseln |
ls | ls / ls <pfad> | Verzeichnis-Inhalt auflisten |
cat | cat <datei> | 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 <disk> <lba-start> <lba-end> | MBR-Partition anlegen |
mkfat32 | mkfat32 <disk> <lba-start> <anzahl-sektoren> | FAT32 formatieren |
mount | mount <vol-id> <disk> <lba-start> | Disk als Volume mounten |
vol | vol <vol-id> | 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 ...
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ückmmap(0, -6, …) → Kernel führt handle_r3_syscall aus, gibt result zurückboot.asm) fängt rsi < 0 in vmm_op ab; −5 und −6 sind spezielle Operationscodes
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 |
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:
VmmPhysFromUserVirt(proc_cr3, vaddr) in physische Adressenmmap alloziert werden (damit sie in der Prozess-Page-Table stehen) — kein direktes Pointer-Casting auf Stack-Adressenpchar-Literal-Initialisierer werden nicht emittiert. g_argbuf und g_chbuf müssen in main() manuell initialisiert werden:
g_argbuf := "________________________________a";
g_chbuf := "x";
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, …).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