USB 3 en USB4/Thunderbolt toevoegen aan Windows 98 tot 7
Die versie had echter een nadeel: hij bestuurde alleen de USB 2.0-zijde van de controller, waardoor zelfs een USB 3.0-flashdrive werd beperkt tot ongeveer 34 MB/s. Daarna besloot ik verder te gaan door deze driver ondersteuning te geven voor zowel USB 3.x als USB4/Thunderbolt. Dat laatste is zo modern dat zelfs niet alle huidige machines het ondersteunen.
De broncode van het project is nog steeds beschikbaar via dezelfde link: https://github.com/yeokm1/xhci98. In dit artikel deel ik mijn traject over hoe ik deze ondersteuning heb weten toe te voegen.
Deel 1: USB 3
Waarom xHCI98 1.x stopte bij USB 2.0
Ter herinnering: de eerste release van xHCI98 was een miniport-driver onder Microsoft’s usbport.sys. Dit is dezelfde architectuur als de originele UHCI-, OHCI- en EHCI-drivers van Microsoft.
Het probleem is dat usbport.sys is ontworpen in het USB 2.0-tijdperk; het heeft geen besef van wat "SuperSpeed" is. Destijds schreef ik dat het correct implementeren van USB 3 zou betekenen dat de gehele HCD-stack vervangen moest worden, wat extra inspanning, tokenkosten en testen met zich mee zou brengen. Bovendien zou elk USB 3-apparaat volgens de standaard gracieus moeten terugvallen naar USB 2.0-snelheden van 480 Mbps. Daarom liet ik het daarbij en vroeg ik de xHCI-controller om alle USB 3.0-verbindingen uit te schakelen, zodat alleen de USB 2.0-lijnen in de connector actief bleven.
De kosten van het "liegen" tegen usbport.sys
Wat me deed van gedachten veranderen was niet de snelheid, maar de groeiende lijst met workarounds die nodig waren om de beperkingen van usbport.sys op te vangen.
usbport.sys gaat uit van een EHCI-wereld. Bij EHCI zit een USB 1.1 Full-Speed of Low-Speed apparaat nooit direct op een USB 2.0 root port; het wordt altijd overgedragen aan een aparte companion-controller (UHCI/OHCI) of gaat via de Transaction Translator (TT) van een hub. xHCI maakt geen gebruik van deze splitsing, omdat het alle standaarden zelf afhandelt. Een USB 1.1-apparaat, zoals een muis op een root port, zorgde ervoor dat usbport.sys zocht naar een TT. Wanneer die er niet was, liep het besturingssysteem vast.
De workaround die ik toen toepaste, was om elk root port-apparaat te rapporteren als High-Speed. Dit had enkele gevolgen:
- Full-Speed en Low-Speed apparaten kregen de verkeerde interrupt polling-intervallen.
- Full-Speed USB-audio produceerde geen geluid op XP en hoger.
- Vista en 7 liepen vast als er een USB 1.1-hub in een root port werd geplugd.
In mijn laatste poging om dit op te lossen, had ik zelfs een experimentele "virtuele USB 2.0-hub" per root port toegevoegd. In feite zou elk niet-High-Speed apparaat verbonden met een root port een virtuele USB 2.0-hub als tussenpersoon hebben, puur om usbport.sys de TT te geven die het wilde zien. Dit introduceerde veel edge cases bij het verbinden en ontkoppelen die zorgvuldig getest moesten worden. Daarnaast gebruikte het een van de vijf hub-lagen die USB toestaat.
Ik verwachtte dat dit op de lange termijn alleen maar problematischer zou worden naarmate er nieuwe bugs werden gemeld in code waar ik geen controle over had, of wanneer ik functies wilde toevoegen die usbport.sys niet kon ondersteunen.
Functies van usbport.sys overnemen
Uiteindelijk was het genoeg. De kosten van het vasthouden aan de oude architectuur wogen niet langer op tegen de voordelen. Ik besloot te stoppen met het koppelen aan usbport.sys en de functies ervan volledig over te nemen. Omdat ik dit toch al deed, was de oorspronkelijke beperking om geen USB 3.0-snelheden te gebruiken nu ook verdwenen.
xHCI98 2.x ondersteunt USB 3.0. Alles tussen de class-drivers en de hardware wordt nu door mijn driver afgehandeld. Microsoft’s usbport.sys, usbhub.sys en de composite parent zijn niet langer nodig, en de USB 3.x-poorten worden niet langer stroomloos gelaten.
De nieuwe xhci98.sys is een enkele bus-driver die verschillende rollen op zich neemt die voorheen werden uitgevoerd door:
usbport.sys: Port driverusbhub.sys/usbhub20.sys: Root- en externe hubsusbccgp.sys: Composite deviceusbd.sys: OS-helperfuncties
De class-drivers daarboven zijn ongewijzigd. Ik koos ervoor om compatibel te blijven met de bestaande class-drivers in plaats van ze opnieuw te implementeren, omdat ze volwassen zijn, al decennia worden gebruikt en zo ook third-party vendor-drivers werkend houden.
Er is ook een tweede driver, xhciuas.sys, voor USB Attached SCSI (UAS) opslag. UAS is het snellere opslagprotocol dat de meeste USB 3-schijven en SATA-bridges ondersteunen, in tegenstelling tot het oudere Bulk-Only Transport dat usbstor.sys gebruikt. Hoewel UAS-schijven zouden moeten kunnen terugvallen op Bulk-modus, heb ik UAS-ondersteuning toegevoegd op verzoek van een gebruiker, omdat sommige UAS-schijven zich onjuist gedragen en geen Bulk-modus willen gebruiken.
Leerpunten bij dit ontwerp:
- Eén x86-binary voor alles van Windows 98 SE tot Windows 7. Elke kernel-functie die de driver importeert, moest bewezen aanwezig zijn in een standaardinstallatie van Windows 98 SE. Een ontbrekende export leidt niet tot een foutmelding, maar tot een load-failure. Een aparte x64-binary, gebouwd met WDK 7.1, dient voor XP x64, Vista x64 en 7 x64.
- Geen USB 2.0-stack meer nodig op Windows 98 SE. NUSB is nog steeds nuttig voor zijn opslag-drivers, maar het host-stack gedeelte blijft ongebruikt onder xHCI98.
- Apparaatbeheer meldt nog steeds "High Speed". De
USBDEVICESPEEDinterface die deze besturingssystemen lezen, heeft slechts drie waarden en stamt uit de tijd vóór SuperSpeed. Het apparaat draait in werkelijkheid op SuperSpeed, ook al kan Windows dat niet aangeven.
Uitdagingen onderweg
Intel USB 3-poorten die op de verkeerde controller starten
Op Intel 7-, 8- en 9-serie chipsets (Ivy Bridge tot Broadwell) zijn de blauwe USB 3-poorten verbonden met zowel de EHCI- als de xHCI-controllers. Wanneer de BIOS op "Auto" staat, starten ze op EHCI. Hierdoor werkt een USB 3-flashdrive ofwel op USB 2.0-snelheid, of is hij onzichtbaar voor xHCI98. Dit was gedaan om compatibiliteit te garanderen met OS'en uit die periode die xHCI niet ondersteunden (of niet tijdens de installatie).
De driver kan deze poorten nu bij het opstarten omschakelen naar xHCI via de chipset-registers en ze bij het stoppen weer terugzetten. Dit gebeurt door te schrijven naar twee registers in de PCI-configuratieruimte van de xHCI-controller:
USB3_PSSEN: Schakelt de SuperSpeed-lijnen van elke poort in.XUSB2PR: Verplaatst de USB 2.0-lijnen van elke poort van de EHCI-controller naar xHCI.
Verkeerde IRQ op een Dell Latitude E7250
Op een Dell Latitude E7250 uit 2015 (Broadwell) was de muis traag, werden schijven nooit gekoppeld en liep Windows af en toe vast, zelfs nadat de poorten in de BIOS op USB 3.0 waren gezet.
Het bleek dat de BIOS de chipset zo programmeerde dat de xHCI-interrupt naar IRQ 16 ging, terwijl ACPI Windows vertelde dat deze op IRQ 21 zat. EHCI gebruikte ook IRQ 21. Omdat IRQ 21 alleen door de EHCI-controller werd gebruikt, was er weinig activiteit zodra de poorten waren overgeschakeld naar xHCI. Mijn interrupt-handler draaide alleen wanneer EHCI toevallig op dezelfde lijn interrumpeerde.
Niemand had deze BIOS-bug jarenlang opgemerkt omdat de Windows 7-driver van Intel en de Windows 8+ driver beide gebruikmaken van Message Signaled Interrupts (MSI), die geen gebruik maken van legacy-interrupts. Met hulp van AI en RW-Everything werd de hypothese van de verkeerde IRQ getest door het routing-register van de chipset live naar de correcte IRQ-waarde te herschrijven.
Polling en MSI
De E7250 leerde me dat ik niet altijd op de legacy-interruptlijn kan vertrouwen. Daarom heeft de driver twee alternatieven gekregen:
- Polled event delivery: In plaats van te wachten op een interrupt, controleert de driver de event-ring via een timer. In de automatische modus schakelt hij zelf over op polling als de interrupt niet beschikbaar is.
- MSI op Vista en 7: Voor Vista en 7 gebruikt mijn driver nu MSI, aangezien dit de aanbevolen methode is voor PCIe-apparaten.
Onzichtbare SuperSpeed-apparaten op Intel Sunrise Point machines
Op twee Intel Sunrise Point-machines (een ThinkPad E460 uit 2016 en een HP EliteBook 850 G5 uit 2018) waren USB 3-flashdrives volledig onzichtbaar. Een USB 3-hub werd slechts herkend als een USB 2.0-hub.
Met mijn XHCISNAP-tool zag ik dat de link in werkelijkheid correct trainde op SuperSpeed. De oorzaak was een snelheidstabel die de controller publiceert. Op Sunrise Point bevat deze tabel alleen de interne SSIC chip-to-chip snelheden (1248/2496/4992 Mb/s). Mijn driver las deze tabel als vervanging van de standaard snelheids-ID's in plaats van als aanvulling daarop. Hierdoor werd een normaal SuperSpeed-apparaat gerapporteerd als "onbekende snelheid" en weggegooid voordat er een device slot was toegewezen.
Ik heb de Linux-broncode bestudeerd om te zien hoe zij dit afhandelden. Het bleek dat Linux deze tabel nooit gebruikt om snelheden te mappen, waardoor Linux dit probleem nooit had. De oplossing was om niet-vermelde standaard-ID's terug te laten vallen op hun gebruikelijke betekenis.
Omgaan met controllers die zich onjuist gedragen
Een tester op een AMD AM5-board meldde dat hun muis "willekeurig stopt" en dat een apparaat na een crash alleen hersteld kon worden door het opnieuw in te pluggen.
Mijn driver was strikter dan Linux wanneer een controller zich onjuist gedroeg. Na bestudering van de Linux-aanpak heb ik zeven tolerante gedragingen toegevoegd, zoals het opnieuw proberen van transactiefouten, het herstellen van een gestopte endpoint, het reactiveren van een poort die door de controller was uitgeschakeld en het herstellen van een gestopte controller.
Tevens heb ik quirk-management toegevoegd voor controllers van diverse fabrikanten, waaronder Fresco Logic, ASMedia, AMD, TI, Etron, VIA en sommige Intel-controllers.
VIA VL805-kaarten
Ik kreeg een melding dat op een VIA VL805 PCIe-kaart elk Low- en Full-Speed apparaat stilzwijgend werd genegeerd, zonder foutmelding of uitroepteken in Apparaatbeheer.
De VL805 routeert alles aan de USB 2.0-zijde via een geïntegreerde hub, waardoor de poertsnelheidstabel alleen High-Speed vermeldt. Mijn driver gebruikte die tabel onterecht om ook de apparaten achter de hub te beoordelen. Ik heb dit opgelost op basis van logs van de melder, wat later is bevestigd als werkend.
Controllers ouder dan de xHCI 1.0 specificatie
Elke xHCI-controller rapporteert op welke revisie van de specificatie hij is gebouwd. Mijn driver was geschreven voor xHCI 1.0 en hoger en weigerde alles daaronder als veiligheidsmechanisme (resulterend in Code 10 "Dit apparaat kan niet starten").
Het blijkt dat de allereerste generatie USB 3.0-controllers xHCI 0.96-onderdelen zijn. Dit geldt voor de NEC/Renesas uPD720200 en ASMedia ASM1042 chips, veelvuldig te vinden op vroege USB 3.0 PCIe-kaarten en moederborden uit 2010.
De xHCI-specificatie was destijds nog een concept en Linux schrijft een paar velden anders op deze onderdelen. Zo telt het TD Size-veld in 1 KiB-eenheden in plaats van pakketten, moeten sommige controle- en isochrone velden als 0 worden geschreven, en ontbreekt één capability-register, waardoor een resume uit standby de apparaten opnieuw enumereert. Vanaf versie 2.4.0.0 accepteert de driver standaard 0.96-controllers, met een registry-switch om dit weer uit te schakelen.
F8 indrukken bij elke boot op 64-bit Vista en 7
64-bit Windows Vista en 7 weigeren kernel-drivers te laden die niet zijn ondertekend, en Microsoft tekent geen drivers meer voor deze legacy-OS'en. De enige manier om de x64-driver te laden was door bij elke boot F8 in te drukken en "Disable Driver Signature Enforcement" te kiezen.
De x64-drivers zijn nu test-signed met het eigen certificaat van het project. Gebruikers kunnen hun machines nu in test-signing mode opstarten, waardoor de driver bij elke boot laadt zonder F8. Windows zal tijdens de installatie nog steeds waarschuwen dat de uitgever niet kan worden geverifieerd, wat verwacht is aangezien dit geen Microsoft-handtekening is.
USB 3.0 snelheidstests
Na afronding heb ik benchmarks uitgevoerd en de snelheid ligt rond de 200+ MB/s. Een respectabele waarde voor Windows 98. De snelheid vlakt af vanaf transfers van 64 KB vanwege de limiet van 64 KB op de request-grootte die is opgelegd door de USB-opslagdriver van Windows.
---
Deel 2: USB4/Thunderbolt
Nu USB 3 is voltooid, was de volgende stap de ondersteuning voor de nieuwere USB4- en Thunderbolt-standaarden.
Wat USB4/Thunderbolt nodig heeft van het OS
USB4 en Thunderbolt 3 verschillen aanzienlijk van USB 3. Een USB4-dock is geen USB-apparaat; het lijkt meer op een netwerkswitch, een verzameling routers die andere protocollen in tunnels transporteren. PCIe, USB 3 en DisplayPort krijgen elk hun eigen tunnel.
Er zijn drie manieren om een USB4/Thunderbolt-apparaat werkend te krijgen in een besturingssysteem:
- Pre-boot firmware tunnelling: Geen driver nodig op Alpine Ridge machines waarbij de BIOS-optie is ingeschakeld, maar geen hotplug-ondersteuning.
- Software Connection Manager: Het OS doet al het werk: routers enumereren, ROM's lezen, lanes bonden en elke hop van elk pad programmeren. Dit is wat Windows 11 en Linux doen op moderne USB4-hosts.
- Firmware Connection Manager: Oudere Thunderbolt 3-controllers van Intel doen dit alles in hun eigen firmware (Intel noemt dit de ICM).
Zodra een PCIe-tunnel is opgezet, verschijnt een apparaat in het dock, zoals een NVMe-schijf, simpelweg op de PCIe-bus alsof het in het moederbord is geplugd. Windows kan dan een bestaande PCI-driver hiervoor gebruiken.
xHCI98 v3 bestrijkt optie 2 en 3 met een nieuwe xhcicm.sys driver. Deze draait op alles van Windows 98 SE tot Windows 7 en kiest automatisch de juiste modus:
- Thunderbolt 3 firmware CM client: Ondersteunt alle Intel firmware CM-controllers die Linux ondersteunt.
- USB4 software CM: Voor USB4-hosts (geverifieerd tegen Alder Lake en ASM4242 modellen). Naast de PCIe-tunnel bouwt het een USB3-tunnel, zodat USB 3-apparaten in een dock de xHCI-controller van de host en
xhci98.sysbereiken. - Re-enumeration: Zodat Windows apparaten ziet verschijnen en verdwijnen achter een tunnel zonder handmatig op "Vernieuwen" te klikken in Apparaatbeheer.
- Link-state notification: Voor mijn custom NVMe-driver
Stor98, om wijzigingen in de status van de tunnel-link te melden. - Safe eject: Zodat een getunnelde schijf gestopt kan worden vóór het uitpluggen.
De PCIe-apparaten achter de tunnel gebruiken hun eigen drivers. De xHCI-controller in een dock wordt aangestuurd door xhci98.sys zelf, en een NVMe-schijf door Stor98.
Daarnaast heb ik de tool xHCI98 USB Tree gebouwd, gemodelleerd naar Microsoft’s USB Device Viewer (usbview), om informatie over USB- en Thunderbolt-apparaten weer te geven en ze veilig te verwijderen.
Waarom eerst een emulator bouwen?
Voor xHCI bood QEMU al een qemu-xhci device model. Voor USB4 of Thunderbolt bestaat zo iets niet. Testen op echte hardware zou betekenen dat elke fout leidt tot een crash, met weinig zicht op wat er misgaat.
Bovendien heb ik geen USB4-hardware die Windows 98 kan booten. Dat vereist UEFI-CSM ondersteuning. Mijn Framework Laptop 13 (12e gen) ondersteunt USB4, maar is een UEFI Class 3 device zonder CSM. Mijn P14s Gen 1 kan Windows 98 booten, maar heeft alleen de oudere Thunderbolt 3.
Daarom startte ik een apart project, usb4-emu, een fork van QEMU 11 die USB4/Thunderbolt hosts en apparaten emuleert. De emulator is gebouwd op basis van publieke specificaties, broncode en gecapte communicatie-traces van:
- Intel Alpine Ridge Thunderbolt 3 (ThinkPad P14s Gen 1)
- Intel Alder Lake USB4 (Framework Laptop 13)
- ORICO-AAGM2-U4 USB4 NVMe enclosure
- CalDigit TS3 Plus Thunderbolt 3 dock
- Intel Titan Ridge TB3, ASMedia ASM4242, en generieke Intel/AMD USB4 controllers.
Zodra de emulator klaar was, kon een AI-agent veel sneller itereren op de code.
Het vastleggen van de Thunderbolt-controller van mijn laptop
Op mijn P14s Gen 1 staat een Intel Alpine Ridge LP (JHL6240) Thunderbolt 3-controller. Om de hardware-states vast te leggen, maakte ik capture-tools voor Linux, Windows 11 en DOS.
De BIOS van de P14s heeft twee relevante opties:
- Thunderbolt BIOS Assist Mode: De firmware beheert Thunderbolt hotplug en stroom namens het OS.
- Support in Pre Boot Environment: De firmware verbindt Thunderbolt-apparaten voordat het OS start.
Ik heb alle vier de combinaties getest met een CalDigit TS3 Plus dock en een ORICO USB4 NVMe enclosure. De belangrijkste observaties:
- Met Pre-boot aan is er geen driver nodig; de BIOS stelt de PCIe-link in en Windows 98 ziet gewone PCI-apparaten.
- Met Assist aan kan de controller verdwijnen; hij wordt verborgen als er bij het opstarten niets is aangesloten.
- Een niet-goedgekeurd USB4-apparaat kan terugvallen op USB 3.
- De beste instelling voor Windows 98 is "Assist off en Pre-boot on" met het beveiligingsniveau op "User Authorization".
Deze traces werden samen met de Linux-broncode gebruikt om een QEMU-model te genereren dat deze combinaties reproduceert.
Framework Laptop 13 (12e gen)
Hoewel deze laptop geen Windows 98 kan booten, heeft hij een echte Intel Alder Lake USB4-host. Ik gebruikte Linux-capture scripts om actuele USB4-communicatie te sniffen, inclusief de interactie tussen een USB4-controller en een TB3-apparaat (aangezien TB3-compatibiliteit optioneel is in de USB4-standaard).
De emulator controleren tegen echte besturingssystemen
Om te verifiëren of de emulator correct werkt, testte ik deze met besturingssystemen die al USB4-ondersteuning hebben. Het feit dat Windows 11 en Linux deze controllers en apparaten herkennen met hun eigen native drivers, toont aan dat de emulatiemodellen accuraat zijn. Daarna kon ik Windows 98 in de emulator plaatsen.
Stor98: een hot-pluggable NVMe-driver
Conventionele NVMe-drivers gaan ervan uit dat de schijf permanent verbonden is. Daarom maakte ik Stor98, een fork van nvme9x (op zijn beurt een port van nvme2k). Ik behield de beproefde code en voegde toe wat een hot-pluggable schijf nodig heeft: een link-state notification van xhcicm.sys, zodat de driver netjes kan stoppen wanneer de tunnel wegvalt.
Prestaties:
- Op Windows 7 met een ORICO USB4 NVMe enclosure via Thunderbolt 3: ~1151 MB/s lezen / 1013 MB/s schrijven.
- Op Windows 98 SE met dezelfde drivers: ~210 MB/s lezen / 256 MB/s schrijven. Hier is de opslagstack van Windows 98, en niet de Thunderbolt-link, de bottleneck.
Hot-plugging de ASMedia enclosure op mijn P14s Gen 1
Met de BIOS op "Assist off en Pre-boot on" werkte de ORICO USB4 enclosure op mijn P14s onder Windows 98 SE als hij bij het inschakelen was aangesloten. Hot-plugging was echter een probleem: na het herpluggen toonde Apparaatbeheer Code 29 op de PCIe-bridge en kwam de schijf niet terug.
De logs bevestigden dat de Thunderbolt-zijde gezond was; de firmware keurde het apparaat goed en de PCIe-tunnel was up. De fout zat puur aan de PCI-zijde. Bij het opstarten stelt de BIOS de PCI-resources in (busnummers, memory windows, interrupt routing), maar de PCI-driver van Windows 98 configureert geen nieuw toegevoegde PCI-bridge.
Om dit op te lossen, moet xhcicm.sys het werk van de BIOS overnemen na een hot-plug: het toewijzen van busnummers, memory windows en de interrupt-lijn. Elk apparaat onder de poort krijgt de IRQ van de poort zelf. Hiermee werkt de schijf nu correct, zowel na een hot-plug als na een "Veilig Verwijderen".
Bouwen met AI
Dit project is gebouwd met zware AI-ondersteuning:
- Claude Fable 5.1 voor planning
- Claude Opus 5.5 voor codering
- Codex GPT 6 Astra als adversarial reviewer
De workflow om documentatie als geheugen voor de AI te gebruiken is enorm opgeschaald: de roadmap bevat inmiddels 42 fasen, er zijn 25 design-records voor hoofdfuncties en 15 verslagen van diagnose en oplossingen. AI hielp enorm bij de technische codering, maar fysiek troubleshooten en interactie met testers vereiste mijn persoonlijke aandacht.
Conclusie
Toen ik mijn vorige bericht schreef, dacht ik dat USB 2.0-snelheden op Windows 98 voldoende waren en dat een volledige USB 3-stack de moeite niet waard was. In plaats daarvan heb ik de driver uitgebreid naar USB 3.0, USB4 en Thunderbolt—standaarden die ver voorbijgingen aan wat de Microsoft-engineers voor Windows 98 hadden kunnen voorzien.
Behalve de Thunderbolt 3-communicatie op mijn P14s Gen 1 is veel van dit project nog theoretisch, omdat ik geen USB4-host met CSM bezit. Ik heb Windows 98 tot 7 nog niet geprobeerd te booten op fysieke USB4-hosts in combinatie met fysieke USB4- en Thunderbolt 3-apparaten; het USB4-gedeelte is uitsluitend getest tegen de emulator.
Toch denk ik dat ik het beste heb gedaan wat mogelijk is binnen mijn beperkingen. Als je een machine hebt met een Thunderbolt- of USB4-poort (vooral die high-end AMD AM5 X870-moederborden) en bereid bent Windows 98 of een legacy-OS te draaien, dan hoor ik graag hoe het gaat!
Groetjes,