====== Lyx OS – Das Geräte-Verzeichnis ======
Das **Geräte-Verzeichnis** ist die Ablage für das, was beim Hochfahren über die Hardware bekannt wird: Hersteller, Kennungen, Takte, gefundene Einstellungen. Es ist bewusst formlos gehalten — ein Eintrag hat eine **Art**, einen **Namen** und **vier Zahlen**; was die Zahlen bedeuten, entscheidet die Art.
→ [[lyxos:start|Lyx OS]] · [[lyxos:treiber|Treibermodell]] · [[lyxos:bin:sysinfo|sysinfo]] · [[lyxos:bin:lspci|lspci]] · [[lyxos:bin:lsdrv|lsdrv]] · [[lyxos:syscalls|Syscall-ABI]] · [[lyxos:architektur|Architektur]] · [[lyxos:kernel|Kernel-Interna]]
----
===== 1. Wozu es da ist =====
Beim Hochfahren stellt jedes Subsystem fest, was es vorfindet: der PCI-Scan die Funktionen, ACPI die I²C-Geräte, der Plattencode die Laufwerke, CPUID die Kerne und den Takt. Diese Angaben landeten bisher **nur im Boot-Protokoll** — und das rollt weg, sobald der Desktop startet. Wer später wissen wollte, mit welcher DesignWare-Version der I²C-Bus läuft oder wie viele Blöcke die NVMe hat, hatte keine Quelle mehr; ''dmesg'' zeigt Text, keine Zahlen.
Das Verzeichnis löst genau dieses Problem: **einmal beim Erkennen abgelegt, im Betrieb abrufbar.**
Der Entwurf ist absichtlich anspruchslos. Es gibt keine Geräteklassenhierarchie, keine Treiberbindung, keine Baumstruktur — nur eine flache Liste. Damit kann jedes Subsystem etwas ablegen, **ohne dass das Verzeichnis alle Gerätetypen kennen muss**. Ein neuer Bus braucht eine neue Art-Nummer und sonst nichts.
**Was es nicht ist.** Es ist kein ''/dev'', kein ''sysfs'' und keine Treiberverwaltung. Es kann nichts öffnen, nichts binden, nichts einstellen. Es ist ein **Fundbericht**: was beim Hochfahren gesehen wurde und mit welchen Zahlen. Für „welcher Treiber gehört dazu" ist [[lyxos:bin:lsdrv|lsdrv]] zuständig, für den rohen PCI-Bestand [[lyxos:bin:lspci|lspci]].
----
===== 2. Wo es geführt wird =====
Im Kernel, in der Unit ''kernel/devinfo.lyx''. Die Tabelle ist eine einzelne Speicherseite, die beim ersten Eintrag angelegt und genullt wird:
^ Größe ^ Wert ^ Bedeutung ^
| ''DI_MAX'' | 64 | mehr Einträge nimmt das Verzeichnis nicht auf |
| ''DI_SZ'' | 48 | Byte je Eintrag: ''kind''(8) + Name(8) + ''v0''…''v3''(4 × 8) |
| gesamt | 3072 Byte | eine ''mmap''-Anforderung, beim ersten ''DevInfoAdd'' |
Zwei Modulvariablen halten den Zustand: ''g_di'' (Adresse der Tabelle, ''0'' = noch nicht angelegt) und ''g_di_n'' (Zahl der belegten Einträge).
Daraus folgen drei Eigenschaften, die man kennen muss:
* **Es ist flüchtig.** Die Tabelle liegt im Kernel-Speicher, nicht auf der Platte. Nach einem Neustart wird sie neu gefüllt.
* **Es wächst nur.** Es gibt ''DevInfoAdd'', aber **kein** Entfernen und **kein** Ändern. Ein Eintrag, der einmal drinsteht, bleibt bis zum Neustart unverändert stehen.
* **Es füllt sich beim Hochfahren.** Danach kommt im Normalbetrieb nichts mehr hinzu — mit Ausnahme dessen, was ein Subsystem selbst später anmeldet.
----
===== 3. Der Aufbau eines Eintrags =====
Hier steckt die wichtigste Einzelheit dieser Seite: **die Anordnung im Kernel und die herauskopierte Anordnung sind nicht dieselbe.**
^ Versatz ^ In der Tabelle (''DI_SZ'' = 48) ^ Im Ausgabepuffer von ''sys_devinfo_get'' ^
| +0 | ''kind'' | ''kind'' |
| +8 | Name (8 Byte) | ''v0'' |
| +16 | ''v0'' | ''v1'' |
| +24 | ''v1'' | ''v2'' |
| +32 | ''v2'' | ''v3'' |
| +40 | ''v3'' | Name (8 Byte) |
''DevInfoCopy'' schreibt die Zahlen zusammenhängend nach vorn und hängt den Namen hinten an. Wer aus Ring 3 liest, benutzt also **die rechte Spalte**: ''kind'' bei +0, die vier Zahlen bei +8/+16/+24/+32, der Name ab +40. Ein Puffer von 48 Byte reicht; ''sysinfo'' nimmt 64 und ist damit auf der sicheren Seite.
==== Der Name ====
Acht Byte, nicht null-terminiert im Sinne von „mindestens ein Nullbyte" — sondern **hinter dem Ende mit Nullen aufgefüllt**. Beim Lesen hört man am ersten Nullbyte auf.
> **Warum das ausdrücklich dasteht.** Vorher kopierte ''DevInfoAdd'' stur acht Byte aus dem übergebenen Zeiger. Bei einem kürzeren Namen las das **in das nächste Stringliteral hinein**, und die Anzeige zeigte ''atasata'' statt ''ata''. Aufgefallen ist es erst, als mit den Plattennamen zum ersten Mal Namen unter acht Zeichen vorkamen — vorher war jeder Name zufällig lang genug.
>
> Ein reiner Bereichsfilter (nur Zeichen 32…126 ausgeben) hätte den Fehler **verdeckt**, nicht behoben: die Reste sind gültige Zeichen. Deshalb prüft die Leseseite auf das Nullbyte, nicht auf Druckbarkeit.
----
===== 4. Die Arten und was ihre Zahlen bedeuten =====
^ Nr ^ Konstante ^ Name ^ v0 ^ v1 ^ v2 ^ v3 ^
| 1 | ''DEV_KIND_I2C_CTRL'' | ACPI-Name | MMIO-Basis | Länge | IRQ | DesignWare-Version |
| 2 | ''DEV_KIND_I2C_HID'' | ACPI-Name | Adresse | Hersteller | Produkt | Taktstufe |
| 3 | ''DEV_KIND_USB_CTRL'' | — | BDF | Ports | Slots | MSI-Art |
| 4 | ''DEV_KIND_USB_DEV'' | — | Slot | Klasse | Protokoll | Controller |
| 5 | ''DEV_KIND_CPU'' | — | Kerne | TSC-Takte je µs | Familie | Modell |
| 6 | ''DEV_KIND_DISK'' | Bus-Name | Platten-Nummer | Blöcke | Blockgröße | Anschluss |
| 7 | ''DEV_KIND_GPIO_INT'' | ACPI-Name | Pin | Geräteadresse | — | — |
| 8 | ''DEV_KIND_MEM'' | — | Seiten gesamt | Seiten frei | Seitengröße | — |
| 9 | ''DEV_KIND_HOST'' | CPUID-Signatur | 1 = virtuell | — | — | — |
| 10 | ''DEV_KIND_AUDIO'' | Treibername | BDF | Klasse%%<<%%8 %%|%% Unterklasse | — | — |
Zwei Anmerkungen dazu:
* **''DEV_KIND_USB_CTRL'' (3) ist vergeben, wird aber von niemandem gefüllt.** Eingetragen werden nur die USB-**Geräte** (Art 4). Die Art steht bereit für den Tag, an dem der Controller selbst hineingehört.
* **Der Name trägt bei ''DEV_KIND_HOST'' die CPUID-Signatur**, nicht einen Gerätenamen — bei einer VM steht dort der Hypervisor, auf echter Hardware ist das Feld leer. Die Signatur kommt aus CPUID-Leaf ''0x40000000''.
----
===== 5. Wer es füllt =====
In dieser Reihenfolge beim Hochfahren:
^ Quelle ^ Was hineingeht ^
| ''kernel/kernel.lyx'' nach ''XhciInit()'' | jedes gefundene USB-Gerät (Art 4) |
| ''kernel/disk.lyx'' → ''DiskRegisterInfo()'' | Platten (0–7, Name aus dem Bus: ''ata''/''sata''/''nvme''/''virtio''), RAM-Disks (''ram''), USB-Speicher (''usb'') |
| ''kernel/kernel.lyx'', Tongerät | HD Audio bzw. AC97 — **nur**, was das PCI-Verzeichnis ohnehin weiß |
| ''kernel/kernel.lyx'', CPU/Speicher/Umgebung | Kerne und Takt, Seitenzahlen, echte Hardware oder VM |
| ''kernel/kernel.lyx'', ACPI-Durchlauf | I²C-Controller, I²C-HID-Geräte und deren GPIO-Interrupt |
**Beim Tongerät wird der Treiber ausdrücklich nicht angefasst.** AC97 und HDA greifen beim Hochfahren auf PCI, Anschlüsse und DMA zu — und genau das hat auf echter Hardware Eingabe und Platte gestört. Ins Verzeichnis kommt deshalb nur, **was steckt**; die Auswahl fällt beim ersten Tonaufruf. Darum meldet [[lyxos:bin:sysinfo|sysinfo]] „erkannt, wird beim ersten Ton in Betrieb genommen" und nicht „aktiv".
----
===== 6. Wie man es aus dem Kernel anspricht =====
import devinfo;
// Eintragen — der Rückgabewert ist der Index, -1 wenn die Tabelle voll ist.
DevInfoAdd(DEV_KIND_DISK, "nvme"c as int64, disk_id, bloecke, blockgroesse, port);
var n: int64 := DevInfoCount(); // Zahl der Einträge
var art: int64 := DevInfoKind(3); // Art des vierten Eintrags
DevInfoCopy(3, ziel); // 48 Byte nach `ziel` schreiben
^ Funktion ^ Rückgabe ^
| ''DevInfoAdd(kind, name, v0, v1, v2, v3)'' | Index des neuen Eintrags, ''-1'' wenn ''DI_MAX'' erreicht ist |
| ''DevInfoCount()'' | Zahl der belegten Einträge |
| ''DevInfoCopy(i, out)'' | ''true''/''false'' — schreibt in der **Ausgabe**-Anordnung (siehe oben) |
| ''DevInfoKind(i)'' | Art des Eintrags, ''0'' bei ungültigem Index |
''name'' ist ein Zeiger auf bis zu acht Zeichen und darf ''0'' sein.
----
===== 7. Wie man es aus Ring 3 anspricht =====
Drei Syscalls, ohne Rechteprüfung — die Auskunft ist lesend und verändert nichts:
^ Nr ^ Aufruf ^ Wirkung ^
| 251 | ''sys_devinfo_count()'' | Zahl der Einträge |
| 252 | ''sys_devinfo_get(idx, out_uva)'' | einen Eintrag nach ''out_uva'' schreiben; ''0'' = gut, ''-1'' = Index oder Adresse ungültig |
| 253 | ''sys_cpu_ident(out_uva)'' | 16 Byte Hersteller, dann 64 Byte Modellbezeichnung — direkt aus CPUID, **nicht** aus dem Verzeichnis |
Beide Dispatcher kennen die drei Nummern (''handle_r3_syscall'' und der Scheduler-Pfad), sie sind also aus jedem Ring-3-Programm erreichbar. Die Adresse wird über ''VmmPhysFromUserVirt'' übersetzt; ein Puffer, der nicht abgebildet ist, führt zu ''-1'' statt zu einem Schreibzugriff ins Leere.
==== Ein vollständiges Beispiel ====
import bsys;
con SYS_DEVINFO_COUNT: int64 := 251;
con SYS_DEVINFO_GET: int64 := 252;
con KIND_DISK: int64 := 6;
@capabilities([])
fn main(): int64 {
Init();
var n: int64 := Sys1(SYS_DEVINFO_COUNT, 0);
if (n <= 0) { PutsLn("Verzeichnis leer"); return 1; }
var rec: int64 := mmap(0, 64, 3, 34, -1, 0);
var i: int64 := 0;
while (i < n) {
if (Sys2(SYS_DEVINFO_GET, i, rec) == 0) {
if (peek64(rec) == KIND_DISK) {
Puts("Platte "); PutInt(peek64(rec + 8)); // v0 = Nummer
Puts(" Bloecke "); PutInt(peek64(rec + 16)); // v1
Puts(" a "); PutInt(peek64(rec + 24)); // v2 = Blockgroesse
Puts(" Byte ");
var k: int64 := 0; // Name ab +40
while (k < 8 && peek8(rec + 40 + k) != 0) {
PutCh(peek8(rec + 40 + k)); k := k + 1;
}
PutCh(10);
}
}
i := i + 1;
}
return 0;
}
''Sys1''/''Sys2'' sind die allgemeinen Syscall-Wrapper aus [[lyxos:bin|bsys]] — für Nummern, für die es keinen benannten Wrapper gibt. Sie schreiben Nummer und Argumente in den ''r3_sc_block'' und lösen aus; ein rohes ''SYSCALL'' aus einem Werkzeug würde den Ring-3-Stapel beschädigen.
----
===== 8. Grenzen und Fallstricke =====
* **64 Einträge, dann ist Schluss.** ''DevInfoAdd'' liefert ''-1'' und legt nichts an. Ein Rechner mit vielen USB-Geräten kann die Tabelle füllen — die späteren Funde fehlen dann stillschweigend in der Anzeige.
* **Die Speicherzahlen sind eine Momentaufnahme vom Hochfahren.** ''DEV_KIND_MEM'' trägt ''PmmTotalCount()'' und ''PmmFreeCount()'' **zum Zeitpunkt der Eintragung**. Der Wert „frei" altert also ab der ersten Sekunde. Wer den **aktuellen** Stand braucht, nimmt ''sys_mem_stats'' (203) — das ist die Quelle, aus der [[lyxos:bin:top|top]] liest.
* **Kein Entfernen, kein Aktualisieren.** Ein Gerät, das im Betrieb verschwindet (USB abgezogen), bleibt im Verzeichnis stehen. Das Verzeichnis beschreibt den Fund beim Hochfahren, nicht den Ist-Zustand.
* **Die Zahlen sind roh.** Es gibt keine Einheiten und keine Umrechnung; ''v1'' bei einer Platte sind Blöcke, nicht Byte. Die Übersetzung in etwas Lesbares macht das anzeigende Programm.
* **Kein Rechtebedarf.** ''sysinfo'' trägt ''@capabilities([fs.read])'' — und braucht davon für das Verzeichnis selbst nichts; die Nummern 251–253 sind ungeprüft, weil sie nur lesen.
----
===== 9. Abgrenzung zu den Nachbarn =====
^ Quelle ^ Was sie kennt ^ Werkzeug ^
| **Geräte-Verzeichnis** | was beim Hochfahren gefunden wurde, als Zahlen — quer über alle Busse | [[lyxos:bin:sysinfo|sysinfo]] |
| **PCI-Verzeichnis** (''kernel/pci.lyx'') | jede PCI-Funktion mit Fundstelle, Klasse, Anbieter | [[lyxos:bin:lspci|lspci]] |
| **Treiber-Manifeste** (''/lib/drivers'') | welcher Treiber für welchen Schlüssel zuständig **wäre** | [[lyxos:bin:lsdrv|lsdrv]], [[lyxos:bin|lyxdevd]] |
| **Prozess-/Speicherzähler** | der laufende Betrieb, nicht der Fund | [[lyxos:bin:top|top]], ''ps'' |
Die vier überschneiden sich absichtlich nicht: das Verzeichnis sagt **was da ist**, das PCI-Verzeichnis **wo es steckt**, die Manifeste **wer es bedienen soll**, und die Zähler **was gerade passiert**.
----
Letzte Aktualisierung: 2026-08-27 — Seite neu angelegt; aus ''kernel/devinfo.lyx'', ''kernel/kernel.lyx'', ''kernel/disk.lyx'', ''kernel/ring3.lyx'' und ''bin/sysinfo.lyx'' erhoben.