Inhaltsverzeichnis

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.

Lyx OS · Treibermodell · sysinfo · lspci · lsdrv · Syscall-ABI · Architektur · 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 lsdrv zuständig, für den rohen PCI-Bestand 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) + v0v3(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:


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:


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.lyxDiskRegisterInfo() 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 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 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


9. Abgrenzung zu den Nachbarn

Quelle Was sie kennt Werkzeug
Geräte-Verzeichnis was beim Hochfahren gefunden wurde, als Zahlen — quer über alle Busse sysinfo
PCI-Verzeichnis (kernel/pci.lyx) jede PCI-Funktion mit Fundstelle, Klasse, Anbieter lspci
Treiber-Manifeste (/lib/drivers) welcher Treiber für welchen Schlüssel zuständig wäre lsdrv, lyxdevd
Prozess-/Speicherzähler der laufende Betrieb, nicht der Fund 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.