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