Files
QualcommWireless/docs/ISSUES_AND_POSTMORTEMS.md
T
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

11 KiB

Historique & Post-Mortems des Problèmes Rencontrés

Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocages, panics et bootloops rencontrés lors du développement du pilote natif Wi-Fi Qualcomm WCN7850 (QualcommWifi), avec leurs analyses techniques de cause racine et leurs résolutions vérifiées.


Sommaire des Incidents

ID Incident / Symptôme Composant Touché Cause Racine Statut
INC-01 Erreur d'édition de liens : symboles de réservation IONetworkController non résolus Linker / KPI macOS Méthodes virtuelles de réserve non exportées par le noyau Tahoe Résolu
INC-02 Crash précoce OpenCore : padslot mismatch ABI / Vtable Vtable IO80211Controller allongée à 466 slots sous Darwin 25 Résolu
INC-03 Échec silencieux de démarrage : IO80211Controller::start fails WorkQueue / IOKit IONetworkController::start requiert une IO80211WorkQueue Résolu
INC-04 Bootloop de 5 secondes : réinitialisation matérielle brutale du PC Kernel Panic Apple panic("No ivars->_faultReporter") dans findAndAttachToFaultReporter Résolu
INC-05 Échec d'allocation interne : Failed to allocate RNG Agent RNG / Logger IO80211RNGAgent requiert un logger non-nul lors de l'initialisation Résolu
INC-06 Désynchronisation du cycle d'initialisation au boot Cycle Kexts (Info.plist) OSBundleRequired positionné à Root au lieu de Network-Root Résolu
INC-07 Blocage du chargement firmware AMSS lors du téléversement BHIe PCIe ASPM Désynchronisation BAR0 causée par les transitions d'énergie L0s/L1 Résolu
INC-08 Corruptions mémoires & gel sur plateforme AMD avec > 4 Go RAM Moteur DMA 36-bit Pointeur DMA physique tronqué au-delà de 64 Go Résolu
INC-09 Impossibilité de lire les rapports de panique (redémarrage instantané) OpenCore / NVRAM Paramètres de débogage du noyau par défaut provoquant un reboot Résolu

Détail des Post-Mortems

INC-01 : Erreur d'édition de liens sur les réservations IONetworkController

  • Symptôme : Lors de la compilation et de l'édition de liens du binaire Mach-O Kext, le compilateur Clang échouait avec des erreurs Undefined symbols for architecture x86_64 : _ZN19IONetworkController29_RESERVEDIONetworkController2Ev jusqu'à 7Ev.
  • Cause Racine : Dans le Kernel Collection BootKernelExtensions.kc de macOS Tahoe, Apple conserve les slots virtuels de réservation pour préserver l'ABI C++, mais n'exporte pas leurs symboles dans les tables d'exportation globales (libkern/KPI).
  • Résolution : Définition de stubs d'export locaux C dans QualcommWifi.cpp :
    extern "C" {
        void _ZN19IONetworkController29_RESERVEDIONetworkController2Ev(void) {}
        void _ZN19IONetworkController29_RESERVEDIONetworkController3Ev(void) {}
        void _ZN19IONetworkController29_RESERVEDIONetworkController4Ev(void) {}
        void _ZN19IONetworkController29_RESERVEDIONetworkController5Ev(void) {}
        void _ZN19IONetworkController29_RESERVEDIONetworkController6Ev(void) {}
        void _ZN19IONetworkController29_RESERVEDIONetworkController7Ev(void) {}
    }
    

INC-02 : Crash OpenCore padslot mismatch sur la Vtable

  • Symptôme : Le Kext provoquait un plantage immédiat au chargement par OpenCore ou un KP dans Vtables.c.
  • Cause Racine : OpenCore valide strictement la taille de la table de fonctions virtuelles de chaque classe dérivée. Sous macOS Sonoma/Sequoia (Darwin 24), la vtable d'IO80211Controller comptait 448 entrées. Sous macOS Tahoe 26.x (Darwin 25), elle a été portée à 466 entrées (taille binaire de 0xe90 = 3728 octets). Tout écart dimensionnel invalide la correspondance des slots mémoires.
  • Résolution : Vérification systématique par script de la vtable __ZTV12QualcommWifi contre BootKernelExtensions.kc. Tous les slots jusqu'à l'offset 0xe90 sont rigoureusement alignés sur l'ABI Tahoe.

INC-03 : Échec de IO80211Controller::start sans WorkQueue

  • Symptôme : Le pilote démarrait dans init() et probe(), mais IO80211Controller::start affichait IO80211Controller::start fails et retournait false.
  • Cause Racine : L'analyse désassemblée de IO80211Controller::start (0xffffff8002291f30) a révélé qu'à l'offset +0x41, le code teste le retour de IONetworkController::start(provider). Cette dernière exige impérativement un gestionnaire de queue de travail valide pour instancier ses boucles d'événements.
  • Résolution : Implémentation des surcharges de création de la file de travail dans QualcommWifi.cpp :
    bool QualcommWifi::createWorkQueue(void) {
        if (!fWorkQueue) {
            fWorkQueue = IO80211WorkQueue::workQueue();
        }
        return (fWorkQueue != nullptr);
    }
    IO80211WorkQueue* QualcommWifi::getWorkQueue(void) const { return fWorkQueue; }
    bool QualcommWifi::createWorkLoop(void) { return createWorkQueue(); }
    IOWorkLoop* QualcommWifi::getWorkLoop(void) const { return getWorkQueue(); }
    

INC-04 : Le Bootloop de 5 Secondes - Kernel Panic _faultReporter

  • Symptôme : L'utilisateur subissait un redémarrage en boucle constant du Mac : OpenCore se lance, commence le chargement de macOS, puis au bout de 5 secondes environ, le PC s'éteint et redémarre instantanément sans message visible.
  • Cause Racine (Breakthrough Majeur) : Désassemblage complet du flux de IO80211Controller::start à l'adresse 0xffffff8002291f30 dans BootKernelExtensions.kc :
    1. À +0x272, le pilote invoque findAndAttachToFaultReporter() (0xffffff8002292628).
    2. À +0x13e, cette routine appelle la méthode virtuelle this->getFaultReporterFromDriver() (callq *0xd80(%rax), vtable slot 434).
    3. Notre code renvoyait initialement nullptr.
    4. À l'adresse 0xffffff8002292737 (+0x227), findAndAttachToFaultReporter contient un appel direct et inconditionnel à _panic :
      panic("\"No ivars->_faultReporter\" @%s:%d", "IO80211Controller.cpp", 2877)
      
    5. Faute d'argument empêchant le redémarrage, macOS déclenchait immédiatement un hardware reset.
  • Résolution :
    1. Instanciation dans init() d'un objet OSObject valide :
      fFaultReporter = OSString::withCString("QualcommFaultReporter");
      
    2. Surcharge de getFaultReporterFromDriver() retournant fFaultReporter.
    3. Libération propre dans free() via OSSafeReleaseNULL(fFaultReporter).
    4. Dans le noyau, findAndAttachToFaultReporter() constate que le pointeur est non-nul, stocke l'adresse dans ivars->_faultReporter, incrémente la référence via obj->retain() (*0x20(%rax)), et aucun panic n'est émis.

INC-05 : Échec d'allocation IO80211RNGAgent / getLogger()

  • Symptôme : Échec dans IO80211Controller::start à l'étape d'initialisation du générateur aléatoire Wi-Fi avec le log : Failed to allocate RNG Agent.
  • Cause Racine : L'inspection binaire de IO80211RNGAgent::withOptions() (0xffffff80022dd55c) a montré qu'à l'offset +0xe6, la fonction appelle this->getLogger() (vtable slot 426, *0xd40(%rax)). Si ce pointeur est nul, elle saute directement en erreur (+0x12f), détruit le descripteur et renvoie NULL.
  • Résolution : Instanciation dans init() d'un objet fLogger = OSString::withCString("QualcommLogger"), et implémentation de getLogger() retournant (void*)fLogger. RNGAgent effectue son appel logger->retain() et s'initialise avec succès.

INC-06 : Mésalignement du Cycle Kext OSBundleRequired

  • Symptôme : Tentative de matching du pilote PCI trop précoce lors de la séquence de boot, provoquant des échecs d'allocation de services réseau.
  • Cause Racine : Dans Info.plist, OSBundleRequired avait été temporairement passé à Root. Or, IO80211Family.kext d'Apple spécifie impérativement Network-Root. Déclarer Root force le chargement du pilote avant que la pile des protocoles réseau et des sockets du noyau ne soit prête.
  • Résolution : Rétablissement strict de :
    <key>OSBundleRequired</key>
    <string>Network-Root</string>
    

INC-07 : Blocage du Téléversement BHIe par ASPM PCIe

  • Symptôme : Timeout lors du transfert de l'image AMSS complète (~6 Mo) via BHIe, le statut BHI restant figé en boucle.
  • Cause Racine : La gestion d'énergie active du bus PCIe (Active State Power Management - ASPM) tente de basculer la liaison en veille L0s ou L1 pendant les transferts DMA volumineux, corrompant les accès aux registres MMIO de notification de sonnette (ATH12K_BHIE_TXVECDB).
  • Résolution : Désactivation dynamique d'ASPM avant tout transfert firmware :
    UInt8 pciCapOffset = 0;
    if (fPciDevice->findPCICapability(kIOPCIPCIExpressCapability, &pciCapOffset) && pciCapOffset != 0) {
        UInt16 linkCtrl = fPciDevice->configRead16(pciCapOffset + 0x10);
        linkCtrl &= ~0x3; // Désactive L0s et L1
        fPciDevice->configWrite16(pciCapOffset + 0x10, linkCtrl);
    }
    

INC-08 : Échec et Corruption DMA sur Plateformes AMD 64-bit (> 4 Go RAM)

  • Symptôme : Gel ou reboot brutal lors de l'accès aux anneaux de commandes ou lors du démarrage du Primary Boot Loader.
  • Cause Racine : Le SoC WCN7850 possède une unité DMA adressable sur 36 bits maximum (plage physique 0x000000000 à 0xFFFFFF000). Sur les cartes mères équipées de 32 Go ou 64 Go de RAM, les allocations kernel classiques peuvent renvoyer des tampons au-dessus de 64 Go. Les bits de poids fort étaient tronqués par le matériel, écrivant dans des zones mémoires arbitraires du noyau.
  • Résolution : Toutes les allocations DMA contiguës sont contraintes par un masque physique 36 bits :
    #define ATH12K_DMA_MASK_36BIT_PAGE 0xFFFFFF000ULL
    IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
        kernel_task,
        kIODirectionInOut | kIOMemoryPhysicallyContiguous,
        size,
        ATH12K_DMA_MASK_36BIT_PAGE
    );
    

INC-09 : Redémarrages Automatiques Invisibles sans Affichage de KP

  • Symptôme : En cas de panic du noyau, le PC redémarrait instantanément sans donner le temps à l'utilisateur de lire la pile d'appels ou la cause du crash.
  • Cause Racine : Par défaut, le noyau XNU sous OpenCore réinitialise le processeur en cas de panique non interceptée.
  • Résolution : Injection de panic=0 dans les boot-args OpenCore du fichier config.plist. En cas d'erreur critique, le noyau gèle l'affichage, permettant de lire textuellement le fichier source et la ligne fautive (ex: IO80211Controller.cpp:2877).