Files
QualcommWireless/docs/RESEARCH_AND_REVERSE_ENGINEERING.md
Erwan d152d81d79 docs: dump comprehensive research, kernel reverse-engineering and post-mortems
- Breakthroughs & Rétro-ingénierie :
  * Création de docs/RESEARCH_AND_REVERSE_ENGINEERING.md : synthèse exhaustive
    de toutes les recherches théoriques, des contraintes matérielles du WCN7850
    (MMIO BAR0 windowing 0x310c, DMA 36-bit page mask, désactivation ASPM L0s/L1),
    de la machine d'état du protocole MHI/BHIe (bootloader 2-étapes FBC, anneaux
    CR/ER/TR, environnement d'exécution AMSS/WFW), ainsi que du reverse-engineering
    complet de l'ABI et de la vtable IO80211Controller (3728 octets, 466 entrées,
    graphe d'appels de start(), slots purs et ivars dans BootKernelExtensions.kc).
  * Création de docs/ISSUES_AND_POSTMORTEMS.md : historique détaillé et
    chronologique de l'ensemble des 9 incidents, bugs et panics rencontrés
    depuis le début du projet (erreurs de link KPI, padslot mismatch OpenCore,
    panique explicite Apple _panic("No ivars->_faultReporter"), échec RNGAgent,
    cycle Kext OSBundleRequired Network-Root, et reboots intempestifs), avec
    symptômes, analyses de cause racine, et résolutions techniques validées.
  * Mise à jour de docs/ARCHITECTURE.md : référencement croisé des nouveaux
    documents d'ingénierie et de post-mortems.

- Validation :
  * Arborescence documentaire consolidée et intègre.
  * Validation syntaxique et liens Markdown vérifiés.
2026-10-17 20:00:22 +02:00

145 lines
9.9 KiB
Markdown

# 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`
* **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).
### 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 cache `fCurrWindow` pour é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 :
```cpp
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)
1. **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.
2. **Chargement de l'étape 1 (SBL)** :
* Configuration de `ATH12K_BHI_IMGADDR_LOW` et `_HIGH` avec l'adresse physique du chunk initial de 512 Ko.
* Écriture de la taille dans `ATH12K_BHI_IMGSIZE`.
* Déclenchement par sonnette BHI : écriture de `1` dans `ATH12K_BHI_IMGTXDB`.
* Attente du statut dans `ATH12K_BHI_STATUS` (vérification du masque `0xC0000000 == 0x80000000`).
3. **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.
4. **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 de `1` dans `ATH12K_BHIE_TXVECDB`.
5. **Transition vers l'état Mission M0** :
* Écriture de l'état `ATH12K_MHI_STATE_M0` (2) dans `ATH12K_MHI_MHICTRL`.
* Surveillance des interruptions MSI et des événements d'anneau MHI (`PKT_TYPE_STATE_CHANGE_EVENT` vers M0 et `PKT_TYPE_EE_EVENT` vers `AMSS`).
---
## 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** : `0xe90` octets (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'`IO80211Controller` sous 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 :
1. `Slot 410 (*0xcc0)` : `IONetworkController::getPacketFilters`
2. `Slot 411 (*0xcc8)` : `IONetworkController::enable(IONetworkInterface*)`
3. `Slot 412 (*0xcd0)` : `IONetworkController::disable(IONetworkInterface*)`
4. `Slot 413 (*0xcd8)` : `IONetworkController::outputPacket(mbuf_t, void*)`
5. `Slot 414 (*0xce0)` : `IONetworkController::getHardwareAddress(IOEthernetAddress*)`
6. **Slot 426 (`*0xd40(%rax)`)** : `virtual void* getLogger(void) const = 0;`
7. **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)` :
1. `+0x03b` : Invoque `IONetworkController::start(provider)` (`0xffffff80026f34dc`).
* Initialise le WorkLoop réseau et publie les interfaces.
* **Exigence** : Nécessite que `createWorkQueue()` soit surchargé et valide, sinon `start` retourne `false` immédiatement.
2. `+0x17e` : Appelle `this->getLogger()` (`callq *0xd40(%rax)`, Slot 426).
* Stocke le résultat à l'offset `0x70` des ivars internes (`ivars->_logger`).
3. `+0x191` : Appelle `createIOReporters(provider)`.
4. `+0x25a` : Appelle `setupControlPathLogging()`.
* Vérifie les flags d'événements et de métriques LQML.
5. `+0x272` : Appelle `findAndAttachToFaultReporter()` (`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 dans `ivars->_faultReporter` (`0x58(%ivars)`).
* **Comportement si NULL** : À `0xffffff8002292737`, exécute l'instruction de panic explicite :
`panic("\"No ivars->_faultReporter\" @IO80211Controller.cpp:2877")` !
6. `+0x2d0` : Appelle `IO80211RangingManager::initWithController(this)`.
7. `+0x2e3` : Appelle `this->getWorkQueue()` (`callq *0xc68(%rax)`).
8. `+0x2f0` : Instancie `IO80211CommandGate::allocWithParams(this, workQueue)`.
9. `+0x471` : Appelle `this->CreatePostOffice()`.
* Crée la boîte aux lettres d'événements Wi-Fi associée à la workQueue.
10. `+0x4ac` : Instancie `IO80211TimerSource::allocWithParams(2, this, workQueue)`.
11. `+0x4e3` : Instancie `IO80211TimerFactory::allocWithParams(workQueue)`.
12. `+0x502` : Appelle `IO80211RNGAgent::withOptions(this)` (`0xffffff80022dd55c`).
* Invoque à son tour `this->getLogger()`. Si nul, abandonne et renvoie `NULL`.
* Si non-nul : appelle `logger->retain()`.
13. `+0x521` : Appelle `this->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()`.
14. `+0x555` : Retourne `true` (`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 `airportd` ignore les interfaces Wi-Fi déclarées sous forme d'`IOEthernetInterface`. L'association et la gestion du lien s'effectuent via `IO80211SkywalkInterface`.
* **Méthodes clés de gestion d'état** :
* `handleCardSpecific` : réceptionne l'ensemble des requêtes `APPLE80211_IOC_*` provenant d'`airportd` et 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).