====== Lyx OS – Treibermodell: was unterstützt wird und wie ====== Diese Seite beantwortet zwei Fragen: **welche Controller, Busse und Geräteklassen** Lyx OS heute bedient — und **womit** man einen eigenen Treiber schreibt. → [[lyxos:start|Lyx OS]] · [[lyxos:architektur|Architektur]] · [[lyxos:geraeteverzeichnis|Geräte-Verzeichnis]] · [[lyxos:bin:lsdrv|lsdrv]] · [[lyxos:bin:lspci|lspci]] · [[lyxos:xhci|XHCI-Treiber]] · [[lyxos:syscalls|Syscall-ABI]] ---- ===== 1. Zwei Stufen, ein Modell ===== ^ Stufe ^ Wo der Treiber läuft ^ Wofür ^ | **Tier-0** | in Ring 0, in den Kernel einkompiliert | boot- und latenzkritisch: Platten, Netzkarte, USB-Host, I²C-Bus | | **Tier-1** | in Ring 3, als gewöhnliches Programm | alles Übrige — heute die HID-Treiber ''usbhid'' und ''i2chid'' | Tier-0 heißt **nicht** „fest verdrahtet": Die Treiber tragen sich in **eine** Tabelle ein (''kernel/drv.lyx''), statt sich über if-Ketten in ''disk.lyx'', ''vfs.lyx'' und ''kernel.lyx'' zu verteilen. Ein neuer Blocktreiber ist damit ein Registrierungsaufruf, keine Änderung an drei Stellen. Die Zuordnung läuft über eine **Frage an den Treiber** — „gehört dir diese Gerätenummer?" — statt über fest verdrahtete Nummernbereiche. Welche Geräte eine Maschine hat und in welcher Reihenfolge, darf den Ablauf nicht bestimmen. ---- ===== 2. Was heute bedient wird ===== ==== Massenspeicher ==== ^ Bus ^ Kennung ^ Unit ^ Anmerkung ^ | **ATA (PIO)** | ''DISK_BUS_ATA'' = 0 | ''kernel/ata.lyx'' | der Rückfallweg, funktioniert überall | | **SATA / AHCI** | ''DISK_BUS_AHCI'' = 1 | ''kernel/ata.lyx'' | je Anschluss eigene Strukturen; die Gerätenummer ist **nicht** der AHCI-Port-Index | | **NVMe** | ''DISK_BUS_NVME'' = 2 | ''kernel/nvme.lyx'' | Blockgröße kommt aus dem Namensraum (4Kn möglich), nicht fest 512 | | **virtio-blk** | ''DISK_BUS_VIRTIO'' = 3 | ''kernel/virtio_blk.lyx'' | der vierte Bus, für Virtualisierung | | **RAM-Disk** | Nummern 8–11 | ''kernel/ramdisk.lyx'' | → [[lyxos:ramdisk|RAM-Disk]] | | **USB-Massenspeicher** | Nummer 12 | ''kernel/xhci.lyx'' | Bulk-Only Transport + SCSI über den xHCI-Treiber | Acht Plattenplätze (0–7) für physische Laufwerke, vier RAM-Disks, ein USB-Speicher. Die Registrierung als Blocktreiber erfolgt für ''ata'', ''ramdisk'' und ''usb''; NVMe und virtio-blk hängen im Plattenverwalter am Bus-Feld. ==== Netz ==== ^ Karte ^ Kennung ^ Unit ^ Wo sie typisch steckt ^ | **virtio-net** | ''PCI\VEN_1AF4&DEV_1000'' | ''kernel/virtio_net.lyx'' | KVM/QEMU — wird **zuerst** geprüft, weil dort am schnellsten | | **Intel E1000** | ''PCI\VEN_8086&DEV_100E'' | ''kernel/e1000.lyx'' | QEMU-Vorgabe | | **AMD PCnet** | ''PCI\VEN_1022&DEV_2000'' | ''kernel/pcnet.lyx'' | VirtualBox | | **Realtek RTL8139** | ''PCI\VEN_10EC&DEV_8139'' | ''kernel/rtl8139.lyx'' | verbreitete Emulation | Darüber liegt ''kernel/nic.lyx'' als **eine** Schnittstelle: der Netzstapel (''net.lyx'') kennt keine Karte mehr. Vorher rief er ''E1000Send''/''E1000Recv'' unmittelbar auf — damit hatte Lyx OS in VirtualBox schlicht kein Netz. **Die erste gefundene Karte gewinnt, und welche es war, steht im Protokoll.** Mehrere Karten gleichzeitig wären ein eigenes Arbeitspaket: dafür bräuchte der Stapel einen Schnittstellenbegriff, den er heute nicht hat. Stille Auswahl wäre hier der teuerste Fall — deshalb wird sie ausgegeben. Eine MAC **setzen** kann derzeit nur ''e1000''. ==== USB ==== ^ Was ^ Unit ^ Stand ^ | **xHCI-Host-Controller** | ''kernel/xhci.lyx'' | Enumerierung und Datentransfer; ein Klassentreiber bedient alle (''PCI\CC_0C0330'') | | **USB-Massenspeicher** | ''kernel/xhci.lyx'' | Bulk-Only Transport mit SCSI, zwei Bulk-Endpoints je Gerät | | **USB-HID** | ''bin/usbhid.lyx'' | **Tier-1**, läuft vollständig in Ring 3 | ==== I²C ==== ^ Was ^ Unit ^ Stand ^ | **DesignWare-I²C-Controller** | ''kernel/i2c.lyx'', ''kernel/i2c_dw.lyx'' | über ACPI gefunden; MMIO, IRQ und Version stehen im [[lyxos:geraeteverzeichnis|Geräte-Verzeichnis]] | | **I²C-HID (Trackpad)** | ''bin/i2chid.lyx'' | **Tier-1**: der Kernel stellt nur den Bus (248) und die ACPI-Liste (249/250) | ==== Ton ==== ^ Gerät ^ Kennung ^ Unit ^ Ladeart ^ | **Intel HD Audio** | ''PCI\CC_040300'' | ''kernel/hda.lyx'' | **bei Bedarf** — erst beim ersten Tonaufruf | | **AC97** | ''PCI\CC_040100'' | ''kernel/ac97.lyx'' | **bei Bedarf** | Der Grund für „bei Bedarf" steht im Manifest: AC97 und HDA fassen beim Hochfahren PCI, Anschlüsse und DMA an — und genau das hat auf echter Hardware **Eingabe und Platte gestört**. Ins [[lyxos:geraeteverzeichnis|Geräte-Verzeichnis]] kommt deshalb nur, was steckt; die Auswahl fällt beim ersten Ton. ==== Eingabe ==== ^ Weg ^ Wo ^ | PS/2-Tastatur | ''kernel/keyboard.lyx'' — der Grundweg, der immer da ist | | USB-HID | ''bin/usbhid.lyx'' (Tier-1) meldet Tasten und Maus über 246/247 an den Kernel | | I²C-HID | ''bin/i2chid.lyx'' (Tier-1), gleicher Rückweg | ---- ===== 3. Treiber-Manifeste ===== Jeder Treiber hat eine Datei unter ''/lib/drivers'' (im Baum: ''drivers/*.mf''). Sie sagt, **wofür** er zuständig ist und **wann** er geladen wird: # Manifest: USB-Controller (xHCI) name xhci treiber eingebaut laden sofort abi 1 match PCI\CC_0C0330 ^ Feld ^ Bedeutung ^ | ''name'' | Treibername, wie ihn [[lyxos:bin:lsdrv|lsdrv]] anzeigt | | ''treiber'' | ''eingebaut'' = Tier-0 im Kernel | | ''laden'' | ''sofort'', ''beimStart'' (der Plattenverwalter fragt ihn im Hochfahren) oder ''beiBedarf'' | | ''abi'' | Fassung der Treiberschnittstelle | | ''match'' | Schlüssel, auf den er passt — mehrfach erlaubt (''i2c.mf'' hat zwei) | ==== Die Schlüssel und ihre Reihenfolge ==== Geprüft wird **vom genauesten zum allgemeinsten**; der erste Treffer gewinnt. So kommt ein auf das Modell zugeschnittener Treiber vor einem, der nur die Chipfamilie kennt: ^ Stufe ^ Form ^ Beispiel ^ | 0 | ''PCI\VEN_xxxx&DEV_xxxx&SUBSYS_xxxxxxxx&REV_xx'' | ''PCI\VEN_8086&DEV_1E31&SUBSYS_00000000&REV_04'' | | 1 | ''PCI\VEN_xxxx&DEV_xxxx'' | ''PCI\VEN_1AF4&DEV_1001'' | | 2 | ''PCI\CC_xxxxxx'' (Klasse, Unterklasse, Schnittstelle) | ''PCI\CC_0C0330'' | | 3 | ''PCI\CC_xxxx'' (nur Klasse und Unterklasse) | ''PCI\CC_0C80'' | ''lsdrv'' hält die Manifeste gegen die tatsächlich gefundenen Geräte und zeigt **IST** gegen **SOLL**; ''lyxdevd'' bindet danach wirklich. ---- ===== 4. Was ein Tier-1-Treiber vom Kernel bekommt ===== Ein Ring-3-Treiber braucht genau zwei Dinge, um überhaupt anzufangen: seinen MMIO-Bereich im eigenen Adressraum und Zugriff auf die PCI-Konfiguration. Alles Weitere kommt in Stufen dazu: ^ Nr ^ Aufruf ^ Paket ^ Wofür ^ | 230 | ''sys_map_device(phys, size)'' | DRV-3 | Geräteregister in den eigenen Adressraum abbilden | | 232 | ''sys_pci_cfg_read(bdf, reg)'' | DRV-3 | PCI-Konfiguration lesen | | 233 | ''sys_pci_cfg_write(bdf, reg, val)'' | DRV-3 | PCI-Konfiguration schreiben (Gerät einschalten) | | 234 | ''sys_dma_alloc(size)'' | DRV-4 | DMA-Puffer anfordern, gibt eine **Kennung** | | 235 | ''sys_dma_addr(id)'' | DRV-4 | die abgebildete Adresse des Puffers | | 236 | ''sys_dma_program(id, mmio, offset, width)'' | DRV-4 | der **Kernel** trägt die physische Adresse ins Geräteregister ein | | 237 | ''sys_notify_create()'' | DRV-5 | Benachrichtigungsschlange anlegen | | 238 | ''sys_notify_post(id)'' | DRV-5 | melden | | 239 | ''sys_notify_wait(id)'' | DRV-5 | blockierend warten | | 240 | ''sys_irq_bind(bdf, notify_id)'' | DRV-5 | Interrupt des Geräts auf die Schlange legen | | 241 / 242 | ''sys_hid_claim(slot, notify_id)'' / ''sys_hid_release(slot)'' | DRV-6 | ein HID-Gerät übernehmen und wieder freigeben | | 243 / 244 | ''sys_usb_dev_count()'' / ''sys_usb_dev_info(idx, out)'' | DRV-6 | die enumerierten USB-Geräte durchgehen | | 245 | ''sys_hid_claim_dma(slot, notify_id, dma_id)'' | DRV-6 | dasselbe mit eigenem DMA-Puffer | | 246 / 247 | ''sys_input_key(ascii)'' / ''sys_input_mouse(dx, dy, btn)'' | DRV-6 | Eingaben an den Kernel zurückmelden | | 248 | ''sys_i2c_xfer(bus, wbuf, lens, rbuf)'' | I²C | Übertragung auf dem I²C-Bus | | 249 / 250 | ''sys_i2c_hid_info(idx, out)'' / ''sys_i2c_hid_count()'' | I²C | die von ACPI genannten Geräte | ==== Die Entscheidung hinter DRV-4 ==== **Der Treiber bekommt nie eine rohe physische Adresse.** Er sagt „DMA in meinen Puffer der Größe N", bekommt eine Kennung und den Puffer in seinen Adressraum abgebildet — die Adresse ins Geräteregister trägt der **Kernel** ein, nach Bereichsprüfung. Damit ist die naheliegendste Fußangel („beliebiger physischer Schreibzugriff") schon in Fassung 1 weg. ---- ===== 5. Interrupts statt Abfragen ===== Bis 2026-08 fragten die Treiber ihre Geräte ab: USB-HID mit 100 Hz aus dem vsync-Pfad, der Netzempfang in jeder Warteschleife. Das kostet Rechenzeit, meldet spät — und hängt daran, dass jemand die Abfrage überhaupt ruft. Genau daran war USB-HID im Desktop schon einmal tot, weil ''vsync_wait'' niemand mehr rief. Die Verteilung selbst steht in ''kernel/irq.lyx'' (Vektor → Lyx-Handler, Rahmen ''drv_irq_common'' für 0x50–0x5F mit LAPIC-EOI und ''drv_pic_common'' für 32–47 mit PIC-EOI) — siehe [[lyxos:kernel|Kernel-Interna]]. ==== USB-HID (xhci.lyx) ==== * ''MsiBindVector(bdf, vektor)'' ergänzt ''msi.lyx'' um eine Bindung mit **freier** Vektorwahl. ''MsiBind'' vergibt fest ''0x60+notify_id'' — gedacht für Ring-3-Treiber; ein Treiber **im** Kernel will seinen eigenen Vektor aus der Verteilung. * ''XhciIrqAttach(idx, vektor)'' trägt **erst** den Handler ein, bindet **dann** MSI/MSI-X und schaltet zuletzt den Interrupter scharf. Andersherum könnte der erste Interrupt eintreffen, bevor jemand zuständig ist. QEMU (''--usbhid''): Vektor 0x50 (MSI-X). * **Den Ring einmal leeren, nachdem scharf geschaltet wurde.** Ein Interrupt entsteht beim **Übergang** auf „es liegt etwas an" — lag beim Umschalten schon ein Ereignis im Ring, kommt danach nie einer. Derselbe Aufruf holt zugleich die ausstehenden Transfers nach, denn das Nachladen (Doorbell) passiert in derselben Schleife wie das Abarbeiten. * **Kein fristbasierter Rückfall.** „Kam in N Durchläufen kein Interrupt, dann zurück zur Abfrage" schaltete immer zurück: im Leerlauf ist „kein Interrupt" der Normalfall — die Frist maß nicht den Fehler, sondern die Ruhe. Stattdessen ''XhciTick()'': ohne Interruptbetrieb die alte Abfrage, mit Interrupt nur noch ~1×/s als **Sicherheitsnetz**, damit ein verlorenes Ereignis nicht dauerhaft hängen bleibt. ==== Netzempfang (nic.lyx / e1000) ==== * **Befund zuerst:** der e1000 **maskierte** seine Interrupts nicht, sondern schaltete sie ein. Der Code schrieb ''0xFFFFFFFF'' nach ''0x00D0'' mit dem Kommentar „IMC: alle Interrupts maskieren" — beim 82540EM ist ''0x00D0'' aber **IMS** (Maske //setzen//), IMC liegt auf ''0x00D8''. Folgenlos, solange kein Handler gebunden war, an drei Stellen korrigiert. * ''NicIrqAttach(vektor)'' richtet den Empfangs-Interrupt ein, mit zwei Wegen: **MSI** bevorzugt (kein Routing, keine geteilte Leitung) und **INTx** als Rückfall — QEMUs 82540EM hat gar keine MSI-Capability, meldet sich aber über eine klassische Leitung (Konfigurationsraum ''0x3C''); Vektor = 32 + Leitung, eingehängt über op117. Belegt in QEMU: Leitung 11 → Vektor 43. * **Der Handler verarbeitet nichts** — er quittiert (ICR lesen, read-to-clear) und zählt. Das Auslesen bleibt in den Warteschleifen, die den Inhalt ohnehin deuten müssen; den Protokollstapel in den Interruptkontext zu ziehen wäre viel Risiko für wenig Gewinn. * Freigeschaltet werden nur die **Empfangsursachen** (RXT0, RXO). Sendequittungen und Verbindungswechsel kosten je einen Austritt aus dem Gastmodus, ohne gebraucht zu werden. ==== Platte: gemessen, nicht umgebaut ==== Der geplante Umbau der AHCI/NVMe-Fertigmeldung auf Interrupt **unterblieb** — die Messung davor fiel eindeutig aus: ''disk Wartezeit bis hier: 0 ms in 0 Zugriffen''. Der QEMU-Boot liest überhaupt nicht von der Platte; Kernel, Initrd, Schriften und Hintergrund kommen aus der Initrd im Arbeitsspeicher. Auch bei Zugriffen wäre der Gewinn klein: NVMe wartet in ''cq_wait'' auf ein Phase-Bit in der per DMA gefüllten Completion Queue (Rechenzeit, aber keine teuren Gerätezugriffe), AHCI pollt ''PxCI'' per MMIO aus dem Bootloader-Assembler. Und der ganze Pfad (''DiskRead'' → fat32 → vfs → Syscall) ist **synchron**: der Wartende kann nicht schlafen. Ein Interrupt, der eine Marke setzt, ersetzt die Warteschleife nicht, er ändert nur ihr Ziel. Der Gewinn entstünde erst mit asynchronem Disk-I/O. Die Messung selbst bleibt (zwei TSC-Lesungen je Zugriff) und beantwortet die Frage künftig sofort — auch dort, wo tatsächlich von Platte gelesen wird. Ausgegeben wird sie nur, wenn es Zugriffe gab. > **Offen:** der Nachweis, dass die Zähler unter Last steigen (Netz), und dass die Zeichen aus dem USB-Interruptpfad im Terminal ankommen — im Prüflauf war der Desktop nicht oben. Anbindung und Stabilität sind belegt (20 s ohne Interrupt-Sturm, PIC-Maske zeigt genau Leitung 9 (SCI) und 11 (Netz) offen), die Wirksamkeit noch nicht. ---- ===== 6. Sicherheitsmodus und die Grenzen, die bleiben ===== ^ Sicherheitsmodus ^ Wirkung ^ | **aus** (Vorgabe) | eine Rechtemaske von ''0'' heißt unbeschränkt. Normale ELF-Programme tragen keine, können also auf Geräte zugreifen — der Entwicklungsstand | | **an** | **ohne Manifest kein Gerätezugriff**. Nur Programme mit ausdrücklicher Rechtemaske — also LBF-Dateien, deren Manifest sie trägt — dürfen MMIO abbilden oder PCI anfassen | Der Kernel meldet den Stand beim Hochfahren: ''drv Sicherheitsmodus: AN — Geraetezugriff nur mit Manifest (LBF)''. > **Was das Modell ausdrücklich //nicht// behauptet.** Die Quelltexte benennen die Grenzen selbst, und die Doku übernimmt sie unverändert: > > * **Das Manifest ist eine Selbstauskunft.** Es sagt, **was** ein Treiber anfassen will, und der Kernel hält ihn daran fest — es beweist **nicht**, dass der Treiber vertrauenswürdig **ist**. Dafür braucht es Signaturen (Fassung 2). > * **Tier-1 ist heute ausschließlich eigener Code.** Eine Isolationsgarantie nach außen wird nicht behauptet. > * **Der Schutz bei ''sys_map_device'' verhindert nur das Grobe:** Bereiche, die sicher kein Gerätespeicher sind (Kernel-Abbild und Bump-Arena unterhalb 160 MB), werden abgelehnt; höchstens 16 MB je Aufruf. Eine echte Grenze zieht erst die **IOMMU** (Fassung 2). > * **Deskriptorketten bleiben offen.** Geräte, die vom Treiber geschriebene Ketten im Speicher ablaufen (NVMe-PRP-Listen, Scatter-Gather-Ringe), folgen Zeigern, die der Treiber selbst gesetzt hat. Auch diese Lücke schließt erst die IOMMU. ---- ===== 7. Womit man nachsieht ===== ^ Frage ^ Werkzeug ^ | Was steckt überhaupt im Rechner? | [[lyxos:bin:lspci|lspci]] (jede PCI-Funktion), [[lyxos:bin:sysinfo|sysinfo]] (was beim Hochfahren gefunden wurde) | | Welcher Treiber ist zuständig — und hat er sich gemeldet? | [[lyxos:bin:lsdrv|lsdrv]] (IST gegen SOLL) | | Geräte tatsächlich binden | ''lyxdevd'' | | Welche Platten gibt es? | [[lyxos:bin:diskinfo|diskinfo]] | | Netzschnittstelle | [[lyxos:bin:netz|ifconfig]] | | Was hat die Hardware beim Lauf gemeldet? | ''hwreport'' — schreibt Prozessor, Speicher, Grafik, Geräte-Verzeichnis und das vollständige Boot-Protokoll in eine Datei (Vorgabe ''/hwreport.txt'') | ---- Letzte Aktualisierung: 2026-08-29 — Abschnitt 5 „Interrupts statt Abfragen" ergänzt (WP-DRV-3, USB-HID über MSI-X, Netz-RX über INTx, e1000-Registerfehler, Disk-Messung); erhoben aus ''kernel/irq.lyx'', ''kernel/acpi_pm.lyx'', ''kernel/xhci.lyx'' und ''kernel/nic.lyx''. Vorherige Aktualisierung: 2026-08-27 — Seite neu angelegt; aus ''drivers/*.mf'', ''kernel/drv.lyx'', ''kernel/disk.lyx'', ''kernel/nic.lyx'', ''kernel/ring3.lyx'' (DRV-3…DRV-6) und ''kernel/xhci.lyx'' erhoben.