Breakthroughs & Reverse Engineering (macOS Tahoe 26.7.1 / Darwin 25.6.0): - Reconstructed the complete 463-slot Skywalk vtable and class layout (0x128 / 296 bytes) of IO80211Controller (__ZTV17IO80211Controller at 0xffffff8002461148) and AppleBCMWLANCore from BootKernelExtensions.kc (IO80211Family 1200.13.1, source 1566.5). - Verified IOUSBHostDevice::deviceRequest vtable slot in Tahoe's BootKernelExtensions.kc (__ZTV15IOUSBHostDevice at 0xffffff8002e72720): confirmed exact match at slot 374 (0x176, byte offset 0xbb0). - Disassembled Tahoe's /usr/sbin/bluetoothd (FindTransportType at 0x10014c700, Found_USB_Dongle at 0x1000e9559, GetProductAndVendorID at 0x1000edf2a) confirming USB transport matching on 'Bluetooth USB Host Controller' and CSR 0x0a12 descriptor spoofing. Code & Build Modifications (v1.1.0): - IO80211Controller.h: Add full 463-slot Tahoe Skywalk vtable declaration (slots 394..462, offsets 0xc50..0xe70) and exact 0x80 filler size when __IO80211_TARGET >= 260000 while preserving legacy compatibility. - QualcommWifi.hpp/.cpp: Implement Tahoe IO80211Controller pure virtual methods (isCommandProhibited, handleCardSpecific, getDRIVER_VERSION, getHARDWARE_VERSION, getCARD_CAPABILITIES, getPOWER, setPOWER, getCOUNTRY_CODE, setCOUNTRY_CODE, setGET_DEBUG_INFO, getLogger, getFaultReporterFromDriver) and guard un-reserved IONetworkController slots 6..7 on SDK 26.5. - QualcommWireless.cpp: Add non-null vtable slot sanity check prior to installing the shadow vtable hook on IOUSBHostDevice::deviceRequest. - project.pbxproj & Info.plist: Upgrade to Xcode 26.6 (MacOSX26.5.sdk), MACOSX_DEPLOYMENT_TARGET = 26.0, __IO80211_TARGET = 260000, and bump version to 1.1.0. - docs & README: Document macOS Tahoe (Darwin 25) architecture, bluetoothd offsets, and Skywalk IO80211Controller ABI.
9.0 KiB
Diagnostic & Architecture Bluetooth (Qualcomm WCN7850)
Ce document décrit en profondeur le fonctionnement du sous-système Bluetooth de la puce Qualcomm FastConnect 7800 (WCN7850), l'analyse détaillée du crash au démarrage de session macOS, et la solution définitive.
1. Description du Contrôleur Bluetooth
- Identifiants matériels :
- Vendeur :
0x0489(Foxconn / Hon Hai) - Produit :
0xe10d - Nom du chipset : Qualcomm WCN7850 Bluetooth 5.4 HCI
- Bus : USB 2.0 (Interface 0 : Contrôle/Bulk HCI, Interface 1 : SCO Audio)
- Vendeur :
- Rôle dans macOS :
- Fournit la couche transport HCI (Host Controller Interface) au daemon système
bluetoothd. - Gère les périphériques HID (claviers, souris, Magic Trackpad), les casques audio A2DP/HFP, et les fonctionnalités Continuity / AirDrop si la carte Wi-Fi est associée.
- Fournit la couche transport HCI (Host Controller Interface) au daemon système
2. Analyse Post-Mortem du Kernel Panic au Login
2.1. Symptômes observés
- Au démarrage, l'écran de verrouillage macOS s'affiche correctement.
- Dès que le mot de passe utilisateur est validé et que la session commence à charger, l'écran se fige complètement puis la machine redémarre (Kernel Panic).
2.2. Traces du Kernel Panic (Kernel-2026-10-15-174229.panic)
panic(cpu 0 caller 0xffffff800ec4b86d): A kext releasing a(n) IOBluetoothL2CAPChannel has corrupted the registry. @OSObject.cpp:224
Panicked task 0xffffff8b71de3980: 235 threads: pid 0: kernel_task
Backtrace (CPU 0), panicked thread: 0xffffff903e00ab30, Frame : Return Address
0xfffffff2a9823bd0 : 0xffffff800edd07bd mach_kernel : _panic + 0x81
0xfffffff2a9823cc0 : 0xffffff800ec4b86d mach_kernel : __ZNK8OSObject13taggedReleaseEPKvi + 0x8d
0xfffffff2a9823cd0 : 0xffffff8010694464 com.apple.iokit.IOBluetoothFamily : __ZN17IOBluetoothDevice13removeChannelEP23IOBluetoothL2CAPChannel + 0xae
0xfffffff2a9823d70 : 0xffffff8010697629 com.apple.iokit.IOBluetoothFamily : __ZN23IOBluetoothL2CAPChannel6stopWLEv + 0x65
0xfffffff2a9823df0 : 0xffffff8010697583 com.apple.iokit.IOBluetoothFamily : __ZN23IOBluetoothL2CAPChannel4stopEP9IOService + 0x33
0xfffffff2a9823e90 : 0xffffff800ecc7dd6 mach_kernel : __ZN9IOService15terminateWorkerEj + 0x8b6
Kernel Extensions in backtrace:
com.apple.iokit.IOBluetoothFamily(9.0)
2.3. Cause Racine (Root Cause)
- L'Intrusion de
IntelBTPatcher.kext:- Le kext tiers
IntelBTPatcher.kext(conçu spécifiquement pour les puces Intel AX200/AX210) était resté activé (Enabled: true) dans le fichierconfig.plistd'OpenCore. IntelBTPatcherintercepte et patche globalement les méthodes virtuelles deIOBluetoothFamilydans le noyau pour contourner des anomalies de paquets HCI propres à Intel.
- Le kext tiers
- Incompatibilité avec Darwin 24 (macOS 15 Sequoia) :
- Sous macOS 15 Sequoia, Apple a profondément remanié la gestion mémoire d'IOKit, notamment la validation des
taggedReleasedansOSObject.cpp:224. IntelBTPatcher v2.5.0(compilé pour Monterey/Ventura/Sonoma) n'est pas compatible avec cette sémantique et effectue une libération désynchronisée (double release ou tag invalide) lors de la fermeture d'un canal L2CAP.
- Sous macOS 15 Sequoia, Apple a profondément remanié la gestion mémoire d'IOKit, notamment la validation des
- Le déclencheur au Login :
- Dès la validation du mot de passe de session,
loginwindowdémarrebluetoothd. - Les périphériques Apple couplés (notamment la Magic Mouse 2
78:CA:39:F4:15:5Aet le Magic Keyboard78:CA:39:4C:A3:9A) établissent ou réinitialisent leurs canaux L2CAP (HID ACL). - Lors de la libération ou du renouvellement du canal L2CAP (
IOBluetoothDevice::removeChannel), le hook corrompu d'IntelBTPatcher déclenche immédiatement l'assertion de corruption de registre du noyau (OSObject::taggedRelease), provoquant le kernel panic instantané.
- Dès la validation du mot de passe de session,
3. Solution et Bonnes Pratiques
3.1. Désactivation définitive d'IntelBTPatcher
Pour les cartes Qualcomm FastConnect 7800, IntelBTPatcher ne doit JAMAIS être utilisé :
- Dans
/Volumes/EFI/EFI/OC/config.plist:<dict> <key>BundlePath</key> <string>IntelBTPatcher.kext</string> <key>Enabled</key> <false/> </dict> - Suppression physique du bundle
IntelBTPatcher.kextdu dossierEFI/OC/Kexts/.
3.2. Rôle de BlueToolFixup.kext
BlueToolFixup.kext(de la suite BrcmPatchRAM / Acidanthera) est, quant à lui, requis et compatible.- Il patche dynamiquement
bluetoothden espace utilisateur pour accepter les dongles tiers non-Apple et débloquer les commandes de bas niveau (HCI Vendor Commands). - Arguments de démarrage recommandés dans
boot-args:-btlfxallowanyaddr -btlfxnvramcheck
4. Séquence de Chargement du Firmware USB
Le firmware Bluetooth Qualcomm nécessite deux étages binaires téléversés au démarrage :
sequenceDiagram
autonumber
participant K as QualcommWireless (Kext)
participant USB as Contrôleur USB WCN7850
participant D as bluetoothd (macOS)
K->>USB: QCA_GET_TARGET_VERSION (Requête de contrôle vendeur)
USB-->>K: Versions ROM / Patch / RAM
K->>USB: QCA_CHECK_STATUS (Vérification statut)
USB-->>K: Status 0x00 (RAMPATCH et Syscfg requis)
K->>USB: EP0: Header DFU (40 octets)
K->>USB: EP2 (Bulk OUT): RAMPATCH WCN7850 (paquets de 4096 octets)
K->>USB: EP0: Header DFU (4 octets)
K->>USB: EP2 (Bulk OUT): NVM WCN7850 (paquets de 4096 octets)
K->>USB: HCI Reset (0x03, 0x0C, 0x00)
USB-->>K: HCI Command Complete Event
K->>K: Injection des propriétés IORegistry (spoofing CSR 0x0a12)
K->>K: Libération de l'interface USB (close)
Note over D,USB: bluetoothd prend la main en exclusivité
D->>USB: Établissement transport HCI USB
D->>USB: Connexion Magic Mouse 2 / Clavier (L2CAP)
Grâce à cette séquence, le contrôleur Bluetooth WCN7850 opère de manière parfaitement stable sous macOS Sequoia (Darwin 24) et macOS Tahoe (Darwin 25) sans aucun crash au démarrage de session.
5. Rétro-ingénierie de bluetoothd (macOS Sequoia & macOS Tahoe) & Détection du Transport
5.1. Algorithme de sélection du transport dans FindTransportType
Le désassemblage complet du binaire /usr/sbin/bluetoothd sous macOS Sequoia (0x100121555) et sous macOS Tahoe 26.7.1 (0x10014c700, Found_USB_Dongle à 0x1000e9559, GetProductAndVendorID à 0x1000edf2a) a révélé l'algorithme exact de détection du bus HCI :
-
Test 1 :
transport-encodingdans l'arbre matériel (IODeviceTree)bluetoothdinterroge la propriététransport-encodingsousIODeviceTree:/chosenou les nœuds bluetooth.- S'il trouve
0x00000000, il force immédiatement le transport USB ("DeviceTree transport = 0x00000000").
-
Test 2 : Matching IORegistry du service hôte USB (
0x100121620/0x10014c7ca)bluetoothdappelle :IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceNameMatching("Bluetooth USB Host Controller"));- Découverte majeure : Si un service quelconque dans l'IORegistry porte le nom
"Bluetooth USB Host Controller",bluetoothdassigne immédiatementTransport = 0 (USB)et retourne sans jamais évaluer leboard-idde la machine !
-
Test 3 : Fallback par table statique des
board-id(0x1001216c6/0x10014c86f)- Si aucun service
"Bluetooth USB Host Controller"n'a été trouvé,bluetoothdlit leboard-iddans l'arbre IODeviceTree ("sysBoardID %s"). - Il compare l'ID à deux tables internes :
- Table 1 : Modèles avec Bluetooth USB natif (iMac, Mac mini, MacBook Pro pré-T2).
- Table 2 : Modèles avec Bluetooth UART natif (
MacPro7,1/Mac-27AD2F918AE68F61).
- Sur SMBIOS
MacPro7,1, le démon sélectionne par défaut le transport UART (H4BC) et ignore complètement tout périphérique USB, affichantChipset: BCM_4350C2, Transport: UART, State: Off.
- Si aucun service
5.2. Résolution native et Hook Shadow VTable dans QualcommWireless
En appliquant les enseignements de la rétro-ingénierie :
QualcommWirelesss'enregistre explicitement sous le nom"Bluetooth USB Host Controller".fDevice->setName("Bluetooth USB Host Controller").- Injection de
transport-encoding = <00 00 00 00>sur le périphérique et surIODeviceTree:/chosen. - Spoofing de
idVendor = 0x0a12(CSR) validé nativement parFound_USB_Dongle. - Shadow VTable (
IOUSBHostDevice::deviceRequest) :- L'index vtable de
IOUSBHostDevice::deviceRequest(IOService*, StandardUSB::DeviceRequest&, void*, IOMemoryDescriptor*, uint32_t&, IOUSBHostCompletion*, uint32_t)est résolu dynamiquement à la compilation viaDeviceRequestAccessor::getVtableIndex(): slot374(0x176, offset0xbb0), identique sous macOS Sequoia (Darwin 24) et macOS Tahoe (Darwin 25.6.0,__ZTV15IOUSBHostDeviceà0xffffff8002e72720). - L'interception du descripteur de périphérique USB (
GET_DESCRIPTOR DEVICE, 18 octets) remplace à la volée le Vendor ID0x0489par0x0a12(CSR) lors de la lecture directe parbluetoothd(GetProductAndVendorID).
- L'index vtable de
- Résultat :
bluetoothdconfigure le contrôleur en USB de manière native et immédiate.