### Breakthroughs & Rétro-ingénierie :
- Diagnostic de la cause racine du bootloop / crash kernel immédiat survenu lors de l'activation de qcom_skywalk=1 (commit e4c7d59).
- Rétro-ingénierie binaire approfondie de BootKernelExtensions.kc (macOS Tahoe 26.x / Darwin 25) sur la classe IO80211SkywalkInterface :
* Découverte critique : IO80211SkywalkInterface::init(IOService*, ether_addr*) (0xffffff80022f56e2) et IO80211SkywalkInterface::setInterfaceRole(UInt32) (0xffffff80022f60f0) sont des méthodes NON-VIRTUELLES.
* Déclarées virtual dans IO80211SkywalkInterface.h, Clang leur a alloué deux slots virtuels à la fin de la vtable IOSkywalkEthernetInterface aux offsets +0xc50 et +0xc58.
* Dans la table virtuelle native Apple à 461 slots (0xe68 octets), le slot +0xc58 correspond à IO80211SkywalkInterface::printDataPath(userPrintCtx*).
* L'instruction fSkywalkInterface->setInterfaceRole(1) a émis callq *0xc58(%rax), sautant dans printDataPath avec %rsi = 0x1. Le déréférencement de ce pointeur a déclenché un Page Fault kernel sur CR2: 0x0000000000000001 et provoqué un CPU triple fault instantané.
### Modifications apportées :
- QualcommWireless/include/IOKit/80211/IO80211SkywalkInterface.h :
* Suppression du mot-clé virtual devant bool init(IOService *controller, ether_addr *mac) et void setInterfaceRole(UInt32 role).
* Clang génère désormais des appels directs callq __ZN23IO80211SkywalkInterface4init... et callq __ZN23IO80211SkywalkInterface16setInterfaceRoleEj.
- docs/ISSUES_AND_POSTMORTEMS.md :
* Ajout du post-mortem technique détaillé INC-14 (analyse de désassemblage, collision de vtable, résolution).
- docs/RESEARCH_AND_REVERSE_ENGINEERING.md :
* Ajout de la section 4.1 décrivant la topologie de la vtable (461 slots) de IO80211SkywalkInterface, les méthodes non-virtuelles, et les slots d'attachement 441 (attachInterface) et 442 (detachInterface).
### Validation :
- Compilation C++ / ASM / Linker réussie sans avertissement via build_wifi.sh.
- Validation binaire kmutil libraries -p build/Release/QualcommWifi.kext validée à 100% contre BootKernelExtensions.kc.
- Désassemblage de build/Release/QualcommWifi.kext confirmant l'éradication des appels indirects vers 0xc50/0xc58 au profit des appels directs aux symboles globaux du noyau.
12 KiB
Recherche Approfondie & Rétro-Ingénierie : Qualcomm WCN7850 & macOS Tahoe (Darwin 25)
Ce document consigne l'ensemble des recherches théoriques, des analyses matérielles, du reverse-engineering binaire du noyau macOS et des découvertes d'ABI effectuées pour le développement du pilote Wi-Fi natif QualcommWifi.
1. Matériel & Architecture PCIe du Qualcomm WCN7850
1.1. Identification Matérielle
- Puce : Qualcomm FastConnect 7800 (WCN7850)
- Identifiants PCI :
- Vendor ID :
0x17cb(Qualcomm Inc.) - Device ID :
0x1107(WCN7850 Wi-Fi 7 PCIe Adapter) - Subsystem Vendor :
0x0000, Subsystem ID :0x0000
- Vendor ID :
- Architecture Dual-Radio :
- La puce intègre sur le même die le contrôleur Wi-Fi 7 (connecté via bus PCIe Gen 3/4) et le contrôleur Bluetooth 5.4 (connecté via bus USB interne
0x0cf3:0xe005/0xe007/0xe300). - Le Bluetooth fonctionne de manière autonome via
QualcommWireless.kext(transport USB HCI + patches BlueToolFixup).
- La puce intègre sur le même die le contrôleur Wi-Fi 7 (connecté via bus PCIe Gen 3/4) et le contrôleur Bluetooth 5.4 (connecté via bus USB interne
1.2. Espace Mémoire MMIO BAR0 & Mécanisme de Fenêtrage PCIe
Le contrôleur PCIe n'expose qu'une région BAR0 de 512 Ko (0x80000 octets). L'architecture interne du WCN7850 (SoC, processeurs AMSS, mémoires partagées, registres MHI) nécessite un adressage sur plus de 8 Mo.
- Registre de sélection de fenêtre BAR0 :
0x310c(ATH12K_PCI_WINDOW_REG_ADDRESS). - Algorithme de fenêtrage dynamique :
Pour tout offset matériel
A \ge \text{0x80000}:\text{window} = (A \gg 19) \ \& \ 0x3F\text{window\_reg\_val} = 0x80000000 \ | \ \text{window}\text{bar0\_target\_offset} = 0x80000 \ | \ (A \ \& \ 0x7FFFF) - Observation : Le bit 31 (
0x80000000) valide la sélection de la fenêtre active. Le pilote maintient un cachefCurrWindowpour éviter des écritures redondantes sur le bus PCIe.
1.3. Contraintes DMA 36-bit sur Architecture AMD x86_64
- Le moteur DMA interne du WCN7850 utilise des pointeurs physiques limités à 36 bits d'adresse (
ATH12K_DMA_MASK_36BIT_PAGE=0xFFFFFF000ULL, soit 64 Go d'espace physique adressable). - Tout tampon alloué sans masque physique au-delà de 64 Go entraîne des écritures DMA tronquées, provoquant des corruptions de mémoire ou des redémarrages instantanés.
- Règle absolue : Les buffers DMA d'anneaux MHI et de transfert firmware doivent impérativement être instanciés via :
IOBufferMemoryDescriptor::inTaskWithPhysicalMask( kernel_task, kIODirectionInOut | kIOMemoryPhysicallyContiguous, taille, 0xFFFFFF000ULL );
1.4. ASPM (Active State Power Management)
- Pendant le transfert du Secondary Boot Loader (SBL) ou de l'image AMSS, le passage automatique du lien PCIe en mode basse consommation (L0s ou L1) entraîne la perte de synchronisation des horloges MMIO et le blocage de la machine d'état BHIe.
- Solution : Désactivation explicite d'ASPM dans le registre Link Control de la capacité PCIe (
pciCapOffset + 0x10) en masquant les bits 0 et 1 (linkCtrl &= ~0x3).
2. Protocole MHI (Modem Host Interface) & Bootloader BHI/BHIe
Le WCN7850 s'amorce à travers le protocole matériel Qualcomm MHI (Modem Host Interface) :
2.1. Environnements d'Exécution (Execution Environments - EE)
0x00- PBL : Primary Boot Loader (ROM interne au SoC).0x01- SBL : Secondary Boot Loader (chargé par l'hôte en RAM interne).0x02- AMSS : Advanced Mobile Subscriber Software (Firmware principal Mission Mode Wi-Fi 7).0x03- RDDM : RAM Dump Collect (mode crash/debug).0x04- WFW : WLAN Firmware Active.
2.2. Séquence de Démarrage 2-Étapes (FBC - Full Boot Flow)
- Découverte des offsets BHI/BHIe :
Lecture des registres de configuration MHI :
ATH12K_MHI_BHIOFF(0x24) : contient l'offset relatif de base de la BHI.ATH12K_MHI_BHIEOFF(0x28) : contient l'offset relatif de base de la BHIe.
- Chargement de l'étape 1 (SBL) :
- Configuration de
ATH12K_BHI_IMGADDR_LOWet_HIGHavec l'adresse physique du chunk initial de 512 Ko. - Écriture de la taille dans
ATH12K_BHI_IMGSIZE. - Déclenchement par sonnette BHI : écriture de
1dansATH12K_BHI_IMGTXDB. - Attente du statut dans
ATH12K_BHI_STATUS(vérification du masque0xC0000000 == 0x80000000).
- Configuration de
- Configuration des Anneaux MHI (MHI Data Structures) :
- Channel Context Array (CCABAP) : tableau des 64 contextes de canaux (direction, type de transfert, buffers).
- Event Ring Context Array (ECABAP) : anneaux d'événements matériels (ER0 pour les événements généraux, ER1 pour les paquets de données).
- Command Ring Context Array (CRCBAP) : anneau de commandes WMI/MHI.
- Doorbells MHI : programmation des adresses matérielles de sonnette pour notifier le matériel de nouveaux descripteurs.
- Chargement de l'étape 2 (AMSS FBC via BHIe) :
- Allocation d'une table vectorielle Scatter-Gather (
struct bhi_vec_entry). - Découpage de l'image AMSS complète (~6 Mo) en segments de 512 Ko.
- Programmation des registres
ATH12K_BHIE_TXVECADDR_LOW/HIGH,ATH12K_BHIE_TXVECSIZE, et écriture de1dansATH12K_BHIE_TXVECDB.
- Allocation d'une table vectorielle Scatter-Gather (
- Transition vers l'état Mission M0 :
- Écriture de l'état
ATH12K_MHI_STATE_M0(2) dansATH12K_MHI_MHICTRL. - Surveillance des interruptions MSI et des événements d'anneau MHI (
PKT_TYPE_STATE_CHANGE_EVENTvers M0 etPKT_TYPE_EE_EVENTversAMSS).
- Écriture de l'état
3. Reverse-Engineering d'Apple IO80211Controller (macOS Tahoe Darwin 25)
L'analyse a été menée par rétro-ingénierie binaire directe de la Kernel Collection de production : /System/Library/KernelCollections/BootKernelExtensions.kc.
3.1. Structure de la Vtable IO80211Controller
- Emplacement Vtable :
0xffffff8002461148à0xffffff8002461fd8. - Taille exacte :
0xe90octets (3728 octets) = 466 entrées (2 mots d'en-tête RTTI + 464 slots de méthodes virtuelles). - Alignement OpenCore :
OpenCore valide la taille exacte de la vtable lors du chargement des Kexts (
Vtables.c). Tout Kext dérivant d'IO80211Controllersous macOS Tahoe doit impérativement avoir une table virtuelle de 3728 octets sous peine d'échec immédiat (padslot mismatch).
3.2. Méthodes Virtuelles Pures d'Apple (___cxa_pure_virtual)
Le noyau macOS définit 6 slots virtuels purs dans IO80211Controller qui doivent obligatoirement être implémentés par le sous-classeur :
Slot 410 (*0xcc0):IONetworkController::getPacketFiltersSlot 411 (*0xcc8):IONetworkController::enable(IONetworkInterface*)Slot 412 (*0xcd0):IONetworkController::disable(IONetworkInterface*)Slot 413 (*0xcd8):IONetworkController::outputPacket(mbuf_t, void*)Slot 414 (*0xce0):IONetworkController::getHardwareAddress(IOEthernetAddress*)- Slot 426 (
*0xd40(%rax)) :virtual void* getLogger(void) const = 0; - Slot 434 (
*0xd80(%rax)) :virtual void* getFaultReporterFromDriver(void) = 0;
3.3. Déroulement Interne de IO80211Controller::start(provider) (0xffffff8002291f30)
L'analyse désassemblée révèle le graphe d'appels complet exécuté lors de super::start(provider) :
+0x03b: InvoqueIONetworkController::start(provider)(0xffffff80026f34dc).- Initialise le WorkLoop réseau et publie les interfaces.
- Exigence : Nécessite que
createWorkQueue()soit surchargé et valide, sinonstartretournefalseimmédiatement.
+0x17e: Appellethis->getLogger()(callq *0xd40(%rax), Slot 426).- Stocke le résultat à l'offset
0x70des ivars internes (ivars->_logger).
- Stocke le résultat à l'offset
+0x191: AppellecreateIOReporters(provider).+0x25a: AppellesetupControlPathLogging().- Vérifie les flags d'événements et de métriques LQML.
+0x272: AppellefindAndAttachToFaultReporter()(0xffffff8002292628).- Invoque
this->getFaultReporterFromDriver()(callq *0xd80(%rax), Slot 434). - Si le pointeur renvoyé est non-nul : appelle
obj->retain()(callq *0x20(%rax)), et le stocke dansivars->_faultReporter(0x58(%ivars)). - Comportement si NULL : À
0xffffff8002292737, exécute l'instruction de panic explicite :panic("\"No ivars->_faultReporter\" @IO80211Controller.cpp:2877")!
- Invoque
+0x2d0: AppelleIO80211RangingManager::initWithController(this).+0x2e3: Appellethis->getWorkQueue()(callq *0xc68(%rax)).+0x2f0: InstancieIO80211CommandGate::allocWithParams(this, workQueue).+0x471: Appellethis->CreatePostOffice().- Crée la boîte aux lettres d'événements Wi-Fi associée à la workQueue.
+0x4ac: InstancieIO80211TimerSource::allocWithParams(2, this, workQueue).+0x4e3: InstancieIO80211TimerFactory::allocWithParams(workQueue).+0x502: AppelleIO80211RNGAgent::withOptions(this)(0xffffff80022dd55c).- Invoque à son tour
this->getLogger(). Si nul, abandonne et renvoieNULL. - Si non-nul : appelle
logger->retain().
- Invoque à son tour
+0x521: Appellethis->allocFramePool().- Récupère la taille de trame via le slot 440 (retourne
0x100= 256). - Alloue le pool de buffers réseau via
IO80211MallocBufferPool::withOptions().
- Récupère la taille de trame via le slot 440 (retourne
+0x555: Retournetrue(kIOReturnSuccess).
4. Intégration Skywalk & airportd sous macOS Tahoe
- Suppression des interfaces réseau Ethernet pour le Wi-Fi :
Sous macOS 26.x, le démon
airportdignore les interfaces Wi-Fi déclarées sous forme d'IOEthernetInterface. L'association et la gestion du lien s'effectuent viaIO80211SkywalkInterface. - Méthodes clés de gestion d'état :
handleCardSpecific: réceptionne l'ensemble des requêtesAPPLE80211_IOC_*provenant d'airportdet du Centre de Contrôle.dataLinkLayerAttachComplete: notifie le pilote dès que la couche Skywalk de transport est connectée.setPOWER/getPOWER: gestion logicielle de l'alimentation de l'émetteur radio.getCARD_CAPABILITIES: publication des capacités matérielles (chiffrements AES/WPA3, largeurs de bande, 802.11be EHT).
4.1. Anatomie Binaire et ABI de IO80211SkywalkInterface (BootKernelExtensions.kc)
L'analyse par désassemblage Capstone a établi l'exactitude de l'interface Skywalk native :
- Table Virtuelle (
__ZTV23IO80211SkywalkInterface) :- Contient exactement 461 slots virtuels (
0xe68octets). - Slot 184 (
+0x5c0) :virtual bool start(IOService *controller)(0xffffff80022f5792). - Slot 185 (
+0x5c8) :virtual void stop(IOService *controller)(0xffffff80022f885c). - Slot 18 (
+0x90) :virtual void free(void)(0xffffff80022f6184). - Slot 373 (
+0xba8) :virtual IO80211Controller* getController(void)(0xffffff80022f957a).
- Contient exactement 461 slots virtuels (
- Méthodes Non-Virtuelles Essentielles :
init(IOService *controller, ether_addr *mac)(0xffffff80022f56e2) : NON-VIRTUELLE. Initialise les ivars internes (this->ivars + 0x120), alloue la structure de contrôle et copie l'adresse MAC (6 octets) dansivars + 0xe4/+0xe8.setInterfaceRole(UInt32 role)(0xffffff80022f60f0) : NON-VIRTUELLE. Configure le rôle réseau dansthis->ivars + 0x58(role = 1pourkIO80211InterfaceRoleInfra).
- Attachement au Contrôleur (
IO80211Controller) :- Slot 441 (
+0xdc8,0xffffff8002295fce) :bool attachInterface(OSObject *iface, IOService *unused = nullptr). VérifieOSDynamicCast(IO80211SkywalkInterface, iface)et valideiface->ivars->role == 1, puis enregistre l'interface primaire danscontroller->ivars + 0x188. - Slot 442 (
+0xdd0,0xffffff800229604a) :bool detachInterface(OSObject *iface, bool arg2 = false). Détache l'interface et réinitialisecontroller->ivars + 0x188 = NULL.
- Slot 441 (