Files
QualcommWireless/docs/ISSUES_AND_POSTMORTEMS.md
T
Erwan 08ead1e88a fix(wifi): étendre la NVRAM à 2048 octets, supprimer la latence SPI et synchroniser l'étape 6
- Breakthroughs & Rétro-ingénierie :
  * Élucidation de la pause ressentie lors du retour au Stade 5 : qcom_substep=1 était resté en NVRAM et les 37 appels successifs à PEWriteNVRAMProperty() provoquaient une latence synchrone cumulée de 2,5 à 3,5 secondes sur le bus SPI Flash de la carte mère B550.
  * Découverte de la saturation mathématique du buffer gNvramTrail : fixé à 512 octets, il atteignait très exactement 511 octets au tag 'register_bypa', rejetant silencieusement toutes les balises de l'Étape 6.
  * Résolution de la perte de télémétrie post-crash : ajout d'un délai matériel IOSleep(200) après 's6_1_calling_start' pour forcer l'achèvement du cycle d'écriture physique dans la puce SPI avant le saut dans fSkywalkInterface->start(this).

- Modifications apportées :
  * QualcommWifi/QualcommWifi.cpp :
    - Quadruplement de gNvramTrail et prevTrail (de 512 à 2048 octets).
    - Filtrage des callbacks répétitifs (getWorkQueue limité à 3 logs NVRAM, getLogger et getFaultReporter limités à 2) éliminant plus de 80% des écritures SPI au boot.
    - Ajout de IOSleep(200) de synchronisation matérielle après 's6_1_calling_start'.
  * docs/ISSUES_AND_POSTMORTEMS.md : rédaction du Postmortem 19.

- Validation :
  * Compilation sans erreur via ./build_wifi.sh.
  * kmutil libraries -p build/Release/QualcommWifi.kext validé à 100% contre BootKernelExtensions.kc.
2026-10-19 10:39:48 +02:00

70 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
INC-10 Triple Fault Processeur ("Coupure de Courant") Logger / CoreCapture Déréférencement de +0x90 -> +0x58 dans CCLogStream::shouldLog Résolu
INC-11 Freeze Apple Logo (non-verbose) / Triple Fault (verbose) NVRAM & Matching IOKit Matching prématuré de registerService() & heap dynamique précoce Résolu
INC-12 Triple Fault / Freeze Apple Logo persistant FakeLogStream / FakeFaultReporter Vtables tronquées à 38 slots (au lieu de 283/269) & 0x88->0x48 Page 0 #PF dans logv Résolu
INC-13 Page Fault CR2: 0x658 dans ml_thread_policy au boot IOWorkLoop vs IO80211WorkQueue IO80211WorkQueue::getThread() retourne 0, deref thread->mutex (+0x658) Résolu
INC-14 Page Fault CR2: 0x1 dans printDataPath IO80211SkywalkInterface.h Méthodes init et setInterfaceRole déclarées virtual par erreur Résolu
INC-15 Kernel Trap / Triple Fault dans initIvars & attachInterface Skywalk Heap & Attachment ABI Tas non initialisé (bzero manquant), header incorrect, ordre d'attachement et registerService prématuré Résolu
INC-16 Bootloop par démarrage complet Skywalk & Piège de libération free() Skywalk Lifecycle & Ivars Guard Exécution aveugle du stade 6 (start), #PF CR2:0xb8 si free() sans initIvars, et stop non-gardé Résolu
INC-17 Page Fault sur table de propriétés NULL par court-circuit de super::init(0) Skywalk Init Architecture initIvars prématuré faisait croire à init() que super::init(0) avait déjà eu lieu Résolu
INC-18 Page Fault CR2: 0xb8 dans free() par initIvars manquant et absence de boot-arg EFI Skywalk Lifecycle & EFI Configuration free() déréférence [0x120]+0xb8 sans test NULL si init() échoue ; boot-arg qcom_step=1 absent d'OpenCore 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).

INC-10 : Triple Fault Processeur ("Coupure de Courant") lors de l'appel CCLogStream::shouldLog sur fLogger

  • Symptôme : Lors du démarrage d'OpenCore avec QualcommWifi.kext actif, le PC s'éteint et redémarre instantanément comme une coupure de courant brutale, sans panic log ni rapport d'erreur NVRAM.
  • Cause Racine (Breakthrough Fondamental) :
    1. Lors du commit 5357ca6, getLogger() avait été implémenté en renvoyant une instance d'OSString (OSString::withCString("QualcommLogger")).
    2. Lors de l'exécution de IO80211Controller::start() -> createIOReporters() -> addReporterLegend(), le noyau macOS invoque CCLogStream::shouldLog(this->getLogger(), level) (0xffffff80032ff9e6).
    3. Le désassemblage de CCLogStream::shouldLog a révélé :
      movq 0x90(%rdi), %rax   ; Lit l'offset +0x90 de l'objet passé en rdi (fLogger)
      movq 0x58(%rax), %rax   ; Déréférence ce pointeur en Ring 0 !
      cmpl 0x8(%rax), %esi    ; Compare le niveau de log
      setle %al
      retq
      
    4. L'objet OSString ne mesurant que 32 octets (0x20), l'accès à +0x90 pointait sur une adresse mémoire arbitraire ou non mappée dans le heap kernel. Le déréférencement immédiat de cette valeur via movq 0x58(%rax), %rax déclenchait une faute de page (#PF) ou une protection générale (#GP) non interceptée en mode superviseur (Ring 0), provoquant un Triple Fault CPU réinitialisant la carte mère instantanément.
    5. Parallèlement, IO80211RNGAgent::withOptions() (0xffffff80022dd55c) et IO80211ControllerMonitor::initWithControllerAndProvider() (0xffffff80023929d0) requièrent impérativement que getLogger() retourne un pointeur non-nul et appellent logger->retain() (*0x20(%rax)).
  • Résolution Technique :
    1. Conception d'une classe FakeLogStream : public OSObject respectant au millimètre l'ABI de CCLogStream :
      • Taille minimale supérieure à 0x98 octets avec p1 placé exactement à l'offset +0x90 (validé par static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90)).
      • p1 pointe vers une zone mémoire fP1Mem (0x1000 octets) dont l'offset +0x58 pointe vers fP2Mem (0x100 octets initialisés à 0).
      • CCLogStream::shouldLog et CCLogStream::log* lisent ainsi 0x90 -> 0x58 -> 0, constatent un niveau de log nul, et retournent proprement false sans tenter d'émettre sur un pipe inexistant.
      • FakeLogStream étant un OSObject, les appels retain() et release() effectués par RNGAgent s'exécutent avec une fidélité parfaite.
    2. Implémentation locale de QualcommWifi::getActionFramePoolCapacity() (slot 440) renvoyant 0x100 (256 frames).
    3. Suppression de l'écriture directe kIOPCIConfigCommand sur le bus PCIe AMD pour éviter toute déstabilisation du root port.

INC-11 : Divergence Verbose / Non-Verbose, Heap Dynamique Précoce et Matching Prématuré

  • Symptômes :
    • Avec mode verbose (-v) : Réinitialisation instantanée du PC ("reset comme une coupure de courant").
    • Sans mode verbose (non-verbose) : Blocage complet et statique sur le logo Apple OpenCore sans redémarrage.
  • Causes Racines (Breakthroughs Majeurs) :
    1. Comportement XNU & debug=0x100 :
      • En mode non-verbose, debug=0x100 désactive l'écran graphique de panique ("Your computer restarted...") et fige l'exécution sur le framebuffer existant (logo Apple).
      • En mode verbose (-v), la routine display_panic tente d'écrire la trace sur le framebuffer console en cours d'initialisation par le pilote NVIDIA RTX 4070 SUPER (nvfb=1 nvaccel=1 nvfbheads=4). L'accès concurrent au contrôleur vidéo déclenche une double faute / triple faute CPU (reset immédiat).
      • Le phénomène sous-jacent est donc rigoureusement identique dans les deux cas.
    2. Vulnérabilité d'Allocation Précoce dans FakeLogStream :
      • FakeLogStream::init() utilisait des allocations dynamiques IOMallocZero(0x1000) et IOMallocZero(0x100).
      • Durant le boot très précoce (phase IOKit matching des kexts Network-Root), le heap kernel peut refuser les allocations dynamiques, laissant p1 nul. CCLogStream::shouldLog déréférençait alors la page 0 (0x90(%rdi) -> 0x58(%rax)).
    3. Anti-Patron IOKit registerService() sur le Contrôleur :
      • QualcommWifi::start() appelait directement registerService() sur le contrôleur IO80211Controller et publiait des dictionnaires de médiums Ethernet (publishMediumDictionary).
      • Sous macOS Tahoe 26.x, IO80211Controller ne publie aucun médium Ethernet, et ne doit JAMAIS appeler registerService() sur lui-même. C'est l'interface Skywalk fille (attachInterface) qui enregistre son propre service.
      • L'appel de registerService() sur le contrôleur déclenchait un matching prématuré par airportd et IO80211Family avant que toute interface Skywalk ne soit attachée, provoquant une panique immédiate.
  • Résolutions Techniques :
    1. Buffers Statiques BSS pour FakeLogStream :
      • Remplacement de IOMallocZero par des tampons statiques BSS gP1Mem[0x1000] et gP2Mem[0x100] alignés sur 64 octets.
      • Garantis 100% alloués, mappés et initialisés dès le chargement du binaire Mach-O sans solliciter le heap.
    2. Persistance Réelle NVRAM (logNvramStage) :
      • Implémentation directe via IORegistryEntry::fromPath("/options", gIODTPlane)->setProperty("qcom_stage", tag).
      • Permet une traçabilité absolue des étapes atteintes même à travers une réinitialisation brutale (nvram -p | grep qcom_stage).
    3. Suppression de publishMediumDictionary et Contrôle de registerService :
      • Élimination des médiums réseau obsolètes.
      • Contrôle strict de registerService() via le boot-arg qcom_register=1 (désactivé par défaut) pour garantir un démarrage fluide et sécurisé jusqu'au bureau macOS.

INC-12 : Vtables Tronquées à 38 Slots et Déréférencement 0x88 -> 0x48 Page 0 dans CCLogStream::logv

  • Symptômes :
    • Malgré la désactivation du matching automatique (qcom_register=0), le contournement des accès MMIO (qcom_mmio=0), et l'utilisation de buffers statiques BSS, le système continue de redémarrer brutalement en mode verbose ("coupure de courant") ou de figer sur le logo Apple en mode non-verbose.
    • La variable NVRAM qcom_stage n'apparaît pas au redémarrage car l'UEFI OpenCore write-protecte l'écriture des variables à l'exécution du noyau (general error).
  • Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau) :
    1. Asymétrie critique des Vtables (CCLogStream vs OSObject) :
      • Dans le Kernel Collection BootKernelExtensions.kc, CCLogStream hérite de CCStream, qui hérite lui-même de IOService.
      • IOService totalise 266 slots virtuels (jusqu'à l'offset 0x848).
      • CCLogStream y adjoint 14 méthodes virtuelles spécifiques (slots 266 à 279, jusqu'à 0x8b8) : profileLoaded(), profileRemoved(), initWithPipeAndName(), setLevel(int) (0x868), getLevel() (0x870), setFlags(uint64_t) (0x878), getFlags() (0x880), setConsoleLogFlag(uint64_t) (0x8b0), etc. Sa vtable totale __ZTV11CCLogStream mesure exactement 0x8d8 octets (283 slots).
      • Or, notre classe FakeLogStream héritait initialement de OSObject, dont la vtable ne compte que 38 slots (0x130 octets).
      • Dès que le contrôleur stockait fLogger dans la variable globale du noyau _sIO80211ControllerGlobalLogger, tout appel à setLevel (slot 269, offset 0x868) ou à IOService::attach (slot 56, offset 0x1c0 dans IO80211ControllerMonitor::createReportersAndLegend) déréférençait la vtable plus de 1800 octets au-delà de sa limite, exécutant des données corrompues en Ring 0 et déclenchant un Triple Fault immédiat.
    2. Déréférencement de Page 0 dans CCLogStream::logv (movq 0x48(%rax), %rdi) :
      • Le désassemblage de CCLogStream::logv (0xffffff80032fd7d4) a démontré qu'en cas de message filtré/supprimé (quand le niveau requis dépasse le seuil configuré gP2Mem[0]), la routine tente d'incrémenter le compteur statistique via :
        movq 0x88(%rbx), %rax   ; Lit streamData à l'offset +0x88 de FakeLogStream
        movq 0x48(%rax), %rdi   ; Déréférence streamData->fCoreCaptureStreamReporter !
        callq IOSimpleReporter::incrementValue ; Appelle slot 45 (+0x168)
        
      • À l'offset +0x88, notre implémentation contenait une zone mise à zéro. Le déréférencement 0x48(%rax) avec %rax == 0 générait un Page Fault (#PF) Ring 0 à l'adresse virtuelle 0x0000000000000048 (Page 0).
    3. Troncature analogue sur FakeFaultReporter :
      • CCFaultReporter hérite de IOService avec une vtable de 269 slots (0x868 octets) et une taille d'instance de 0x90 octets. FakeFaultReporter héritait d'OSObject (38 slots).
  • Résolutions Techniques Appliquées :
    1. Héritage IOService et Déclaration des 14 Slots CCLogStream :
      • class FakeLogStream : public IOService implémente les 14 méthodes virtuelles (slots 266 à 279) avec des retours sûrs no-op.
      • Sa vtable __ZTV13FakeLogStream atteint exactement 0x8d8 octets (283 slots), en conformité binaire stricte à 100% avec l'ABI Apple de BootKernelExtensions.kc.
    2. Topologie Mémoire 0x98 Octets et Bouchon Reporter Factice :
      • streamData à l'offset +0x88 pointe vers gStreamMem[0x100].
      • L'offset gStreamMem + 0x48 pointe vers un objet gDummyReporter dont la table de pointeurs gDummyReporterVtable[256] intercepte le slot 45 (getFirstElementIndex à +0x168) avec dummyReporterFunc (retour kIOReturnSuccess).
      • p1 à l'offset +0x90 pointe vers gP1Mem[0x1000], dont +0x58 pointe vers gP2Mem[0x100].
      • Taille totale de l'instance fixée à 0x98 octets (static_assert(sizeof(FakeLogStream) == 0x98)).
    3. Mise en Conformité de FakeFaultReporter :
      • class FakeFaultReporter : public IOService avec padding 0x90 - sizeof(IOService). Vtable de 269 slots (0x868 octets) et taille 0x90 octets (static_assert(sizeof(FakeFaultReporter) == 0x90)).

INC-13 : Page Fault Kernel Trap CR2: 0x0000000000000658 dans ml_thread_policy / IONetworkController::start

  • Symptômes :
    • Bootloop instantané ("coupure de courant" / reboot immédiat en mode verbose, ou blocage sur logo Apple en mode normal).
    • Trace matérielle extraite du log de panique (/Library/Logs/DiagnosticReports/.contents.panic) :
      panic(cpu 2 caller 0xffffff80130d528a): Kernel trap at 0xffffff80130cc673, type = 14=page fault, CR2: 0x0000000000000658
      Backtrace:
        _IOLockLock + 0x3 (avec %rdi == 0x658)
        _ml_thread_policy + 0x27 (_thread_policy_set_internal)
        IONetworkController::start(IOService*) + 0x1d4 (0xffffff80026f36b0)
        IO80211Controller::start(IOService*) + 0x41 (0xffffff8002291f71)
        QualcommWifi::start(IOService*) + 0x61
      
  • Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau) :
    1. Désassemblage critique de IONetworkController::start(provider) (0xffffff80026f3650) :
      ffffff80026f365b: callq *0x688(%rax)       ; Appelle this->getWorkLoop() (slot 209 = 0x688)
      ffffff80026f3661: movq %rax, 0x88(%rbx)    ; Stocke dans ivars->workLoop
      ffffff80026f3676: movq 0x88(%rbx), %r15
      ffffff80026f367d: movq (%r14), %rax        ; r14 = provider (IOPCIDevice)
      ffffff80026f3683: callq *0x688(%rax)       ; Appelle provider->getWorkLoop()
      ffffff80026f3689: cmpq %rax, %r15          ; Compare le workloop provider avec notre workloop
      ffffff80026f368c: je 0xffffff80026f36b0    ; SI IDENTIQUE, SAUTE ml_thread_policy !
      ffffff80026f368e: movq 0x88(%rbx), %rdi    ; Si différent, utilise notre workloop
      ffffff80026f3695: movq (%rdi), %rax        ; vtable de notre workloop
      ffffff80026f3698: callq *0x128(%rax)       ; APPELLE workloop->getThread() (slot 37 = 0x128)
      ffffff80026f369e: movq %rax, %rdi          ; TRANSMET LE THREAD RETOURNÉ À ml_thread_policy !
      ffffff80026f36ab: callq ml_thread_policy   ; -> thread_policy_set_internal(thread, ...)
      
    2. Origine du Pointeur Null et Incompatibilité d'ABI IOWorkLoop vs IO80211WorkQueue :
      • Dans QualcommWifi.cpp, l'implémentation initiale de getWorkLoop() redirigeait directement vers getWorkQueue() (un objet IO80211WorkQueue).
      • Le désassemblage de IO80211WorkQueue::getThread() (0xffffff80022ee1ea) dans BootKernelExtensions.kc a révélé son implémentation exacte :
        ffffff80022ee1ea <IO80211WorkQueue::getThread()>:
        pushq %rbp
        movq %rsp, %rbp
        xorl %eax, %eax     ; RETOURNE CONSTAMMENT NULL (0) !
        popq %rbp
        retq
        
      • Contrairement à un véritable IOWorkLoop qui maintient un Mach thread dédié (0x30(%rdi)), IO80211WorkQueue retourne toujours 0.
      • thread_policy_set_internal(thread_t thread, ...) présume que le thread passé en paramètre est non-nul et exécute IOLockLock(&thread->mutex) où le mutex de structure thread se situe à l'offset +0x658.
      • Le déréférencement IOLockLock(0 + 0x658) a déclenché le Page Fault #PF sur CR2: 0x0000000000000658.
  • Résolutions Techniques Appliquées :
    1. Séparation Stricte des Interfaces IOWorkLoop et IO80211WorkQueue :
      • Ajout d'une instance dédiée IOWorkLoop *fWorkLoop; allouée via IOWorkLoop::workLoop() dans QualcommWifi::createWorkLoop().
      • QualcommWifi::getWorkLoop() retourne fWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);.
      • QualcommWifi::createWorkQueue() et getWorkQueue() conservent IO80211WorkQueue *fWorkQueue; allouée via IO80211WorkQueue::workQueue().
    2. Séquence d'Initialisation et Nettoyage :
      • Dans QualcommWifi::start(provider) : instanciation séquentielle de createWorkLoop() puis createWorkQueue() avant l'invocation de super::start(provider).
      • Dans IONetworkController::start, workLoop->getThread() invoque désormais l'implémentation native de IOWorkLoop::getThread (movq 0x30(%rdi), %rax), fournissant un thread_t valide à ml_thread_policy, et permettant l'attachement propre du IOCommandGate via fWorkLoop->addEventSource.
      • Dans QualcommWifi::stop() et QualcommWifi::free() : libération propre des deux objets via OSSafeReleaseNULL(fWorkLoop) et OSSafeReleaseNULL(fWorkQueue).

INC-14 : Page Fault Kernel Trap CR2: 0x0000000000000001 par Déroutement Vtable Virtuel dans IO80211SkywalkInterface::printDataPath

  • Symptômes :
    • Bootloop immédiat (triple fault / redémarrage instantané du kernel) dès l'activation de qcom_skywalk=1.
    • Machine bloquée en boucle de redémarrage sans affichage de panic log si NVRAM inaccessible.
  • Causes Racines (Breakthroughs Majeurs & Rétro-ingénierie Noyau) :
    1. Déclarations virtual indues dans les headers C++ (IO80211SkywalkInterface.h) :
      • Dans le header reconstruit QualcommWireless/include/IOKit/80211/IO80211SkywalkInterface.h, les méthodes init(IOService*, ether_addr*) et setInterfaceRole(UInt32) étaient déclarées avec le mot-clé virtual.
      • Or, l'analyse binaire approfondie de BootKernelExtensions.kc démontre sans équivoque :
        • IO80211SkywalkInterface::init(IOService*, ether_addr*) (0xffffff80022f56e2) est une fonction membre NON-VIRTUELLE (seule init(void) à l'offset 0x88 hérite d'OSObject::init).
        • IO80211SkywalkInterface::setInterfaceRole(UInt32) (0xffffff80022f60f0) est une fonction membre NON-VIRTUELLE qui écrit directement role dans this->ivars + 0x58.
    2. Collision catastrophique de table virtuelle (Vtable Desynchronization) :
      • En déclarant ces deux méthodes comme virtual, Clang leur a assigné deux nouveaux slots virtuels à la fin de la vtable IOSkywalkEthernetInterface, aux offsets +0xc50 (slot 394) et +0xc58 (slot 395).
      • Dans l'objet réel IO80211SkywalkInterface instancié par le noyau (BootKernelExtensions.kc), la vtable native possède 461 slots (0xe68 octets) :
        • Le slot +0xc50 correspond en réalité à IO80211SkywalkInterface::wowEnabled().
        • Le slot +0xc58 correspond à IO80211SkywalkInterface::printDataPath(userPrintCtx*).
      • L'appel fSkywalkInterface->setInterfaceRole(1) a émis l'instruction assembleur callq *0xc58(%rax).
      • Le noyau a donc exécuté printDataPath avec %rdi = fSkywalkInterface et %rsi = 0x1 (le rôle 1 transmis comme pointeur userPrintCtx*).
      • Lors du premier déréférencement mémoire de %rsi à l'adresse 0x1, le processeur a déclenché un Page Fault #PF sur CR2: 0x0000000000000001, entraînant un crash critique et un redémarrage instantané (triple fault).
  • Résolutions Techniques Appliquées :
    1. Suppression du Qualificatif virtual :
      • Retrait strict de virtual devant bool init(IOService *controller, ether_addr *mac); et void setInterfaceRole(UInt32 role); dans QualcommWireless/include/IOKit/80211/IO80211SkywalkInterface.h.
    2. Vérification Binaire de l'Assembleur Généré :
      • Le désassemblage de build/Release/QualcommWifi.kext atteste de la conversion en appels directs non-virtuels :
        0x2d9e: callq __ZN23IO80211SkywalkInterface4initEP9IOServiceP10ether_addr
        0x2dc3: callq __ZN23IO80211SkywalkInterface16setInterfaceRoleEj
        0x2de1: callq *0x670(%rax)   ; fSkywalkInterface->attach(this)
        0x2e08: callq *0x5c0(%rax)   ; fSkywalkInterface->start(this)
        0x2e31: callq *0xdc8(%rax)   ; this->attachInterface(fSkywalkInterface, nullptr)
        0x2e4f: callq *0x5b0(%rax)   ; fSkywalkInterface->registerService()
        
    3. Conformité des Dépendances :
      • kmutil libraries -p build/Release/QualcommWifi.kext valide la résolution intégrale des symboles auprès de BootKernelExtensions.kc.

INC-15 : Kernel Trap / Triple Fault dans initIvars & Incohérence d'Attachement Skywalk

  • Symptômes :
    • Bootloop persistant et redémarrage brutal de la machine lors de la phase d'instanciation de IO80211SkywalkInterface.
    • Machine incapable de franchir le stade d'attachement réseau natif sans désactivation manuelle de QualcommWifi.kext dans OpenCore.
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Piège du tas non initialisé (Uninitialized Heap Trap) dans initIvars (0xffffff80022f520c) :
      • _ZN23IO80211SkywalkInterfacenwEm(0x128) alloue la mémoire de l'objet via kalloc_type. Le noyau ne met pas la mémoire à zéro.
      • Dans le constructeur et dans IO80211SkywalkInterface::initIvars, le noyau évalue :
        0xffffff80022f5215: cmpq $0x0, 0x120(%rdi)
        0xffffff80022f521d: je   0xffffff80022f5265 ; Alloue ivars si NULL
        ...
        0xffffff80022f5244: movq 0x120(%rbx), %rdi  ; Garbage pointer !
        0xffffff80022f524b: movl $0xf0, %esi
        0xffffff80022f5250: movl $0xf0, %ecx
        0xffffff80022f5255: xorl %edx, %edx
        0xffffff80022f5257: callq memset            ; memset(garbage, 0, 0xf0) -> Page Fault Ring 0 !
        
      • Si mem + 0x120 contient des résidus non nuls de la mémoire du tas, le noyau tente un memset de 0xf0 octets sur cette adresse invalide, causant un Crash Kernel Page Fault / Triple Fault instantané.
    2. Header erroné network/IOSkywalkEthernetInterface.h :
      • Le fichier QualcommWireless/include/IOKit/network/IOSkywalkEthernetInterface.h déclarait de manière erronée class IOSkywalkEthernetInterface : public IOEthernetController.
      • Dans l'architecture Apple Skywalk, IOSkywalkEthernetInterface hérite en réalité de IOSkywalkNetworkInterface -> IOSkywalkInterface -> IOService, sans aucun lien d'héritage avec IOEthernetController.
    3. Ordre d'attachement et second argument du contrôleur (Slot 441) :
      • Dans AppleBCMWLANCore::createSkywalkInterface (0xffffff80016aaa8a), Apple invoque this->attachInterface(fSkywalkInterface, this) (slot 441 d'IO80211Controller) avant fSkywalkInterface->attach(this) et fSkywalkInterface->start(this).
      • De plus, le second paramètre transmis doit être this (le contrôleur), ce qui renseigne controller->ivars + 0x188 (fPrimaryInterface). L'ancien code passait nullptr après start().
    4. Publication prématurée via registerService() :
      • Appeler registerService() sur fSkywalkInterface publie le service auprès du système IOKit matching et d'airportd avant que les queues Skywalk et les pools de buffers ne soient négociés, provoquant une panique réseau immédiate.
  • Résolutions Techniques Appliquées :
    1. Remise à zéro explicite du bloc mémoire avant construction :
      void *mem = _ZN23IO80211SkywalkInterfacenwEm(0x128);
      if (!mem) return nullptr;
      bzero(mem, 0x128);
      _ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(mem, &_ZN23IO80211SkywalkInterface10gMetaClassE);
      
    2. Correction des inclusions d'en-têtes Skywalk :
      • Redirection du faux header vers <IOKit/skywalk/IOSkywalkEthernetInterface.h> et ajout du typedef uint32_t kern_packet_svc_class_t; dans IOSkywalkTypes.h.
    3. Ordonnancement conforme du cycle d'attachement Skywalk :
      • Invocation de attachInterface(fSkywalkInterface, this) au slot 441 avant attach(this) et start(this).
    4. Contrôle granulaire d'exécution par palier via le boot-arg qcom_step :
      • Introduction du palier qcom_step (par défaut 6 = exécution complète de start(this) sans appeler prématurément registerService()).
      • Validation binaire par désassemblage et validation kmutil libraries à 100%.

INC-16 : Bootloop par Démarrage Complet Skywalk & Piège de Libération free()

  • Symptômes :
    • Bootloop persistant et redémarrage spontané de la machine lors du chargement de QualcommWifi.kext (commit 506df09).
    • Nécessité de désactiver manuellement QualcommWifi.kext dans OpenCore pour atteindre le bureau macOS.
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Piège de Libération sans initIvars dans IO80211SkywalkInterface::free() (0xffffff80022f6184) :
      • Le désassemblage de IO80211SkywalkInterface::free révèle :
        0xffffff80022f618e: movq 0x120(%rdi), %rax  ; rax = ivars
        0xffffff80022f6195: movq 0xb8(%rax), %rdi   ; Déréférencement direct rax + 0xb8 sans vérification NULL !
        
      • Si l'interface est allouée (operator new + C1) mais que initIvars n'a pas encore été invoqué, 0x120 est NULL. Tout appel subséquent à release() / free() déclenche un Kernel Page Fault instantané sur CR2: 0x00000000000000b8 !
    2. Démarrage complet prématuré par défaut (skywalk_step=6) :
      • Dans le commit 506df09, skywalk_step était fixé à 6 par défaut. Lors du boot standard (sans boot-arg explicite), le code franchissait d'un coup l'allocation, l'initialisation, le rôle, l'attachement au contrôleur (slot 441), l'attachement IORegistry ET l'appel fSkywalkInterface->start(this).
      • Dans start(this), Apple initialise CCPipe, CCStream, IO80211PeerManager, la gestion d'énergie joinPMtree / registerPowerDriver / changePowerStateTo(0), IO80211Glue, WCLGlue, et IO80211MacAddressAgent. Un échec non géré dans ces sous-systèmes sans isolation pas-à-pas provoquait le bootloop.
    3. Nettoyage non gardé dans stop() :
      • QualcommWifi::stop invoquait inconditionnellement detachInterface, fSkywalkInterface->stop(this) et fSkywalkInterface->detach(this). Or, IO80211SkywalkInterface::stop déréférence également ivars + 0x80 (movq 0x80(%rax), %rdi), causant un crash en cascade si l'interface n'était pas entièrement démarrée.
  • Résolutions Techniques Appliquées :
    1. Invocation explicite et systématique de initIvars() dès la création :
      • Déclaration et appel de _ZN23IO80211SkywalkInterface9initIvarsEv(mem) dans createSkywalkInterface dès l'allocation, garantissant que ivars est toujours non-nul et immunisé contre tout #PF dans free().
    2. Réduction sécurisée du palier par défaut à l'étape 1 (skywalk_step=1) :
      • int skywalk_step = 1; par défaut, permettant de valider isolément la stabilité de la création de l'objet avant d'activer les étapes suivantes via qcom_step.
    3. Protection conditionnelle du cycle de vie dans stop() via des drapeaux d'état :
      • Ajout de fSkywalkCtrlAttached, fSkywalkAttached, fSkywalkStarted pour ne détacher ou arrêter que ce qui a été effectivement attaché ou démarré avec succès.
    4. Validation intégrale de la chaîne de compilation :
      • build_wifi.sh validé à 100% avec kmutil libraries auprès de BootKernelExtensions.kc.

INC-17 : Page Fault sur Table de Propriétés NULL par Court-Circuit de super::init(0)

  • Symptômes :
    • Bootloop lors du passage au stade 5 (commit 79d7726), alors que le stade 1 (commit b7e32db) démarrait impeccablement jusqu'au bureau macOS.
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Analyse de la condition d'initialisation dans IO80211SkywalkInterface::init (0xffffff80022f56e2) :
      • Le désassemblage précis montre la logique suivante :
        0xffffff80022f5710: cmpq $0x0, 0x120(%r14)       ; ivars est-il NULL ?
        0xffffff80022f5718: jne  0xffffff80022f5733      ; Si NON-NULL, SAUTE super::init(0) !
        0xffffff80022f571a: movq 0x15aa07(%rip), %rax   ; __ZTV26IOSkywalkEthernetInterface
        0xffffff80022f5724: movq %r14, %rdi
        0xffffff80022f5729: callq *0x258(%rax)           ; super::init(0) -> IOService::init()
        0xffffff80022f5733: movq %r14, %rdi
        0xffffff80022f5736: callq initIvars              ; initIvars()
        
      • Dans le commit b7e32db/79d7726, nous appelions initIvars dès createSkywalkInterface() pour éviter le piège de free().
      • Par conséquent, lors de l'appel à fSkywalkInterface->init(...), mem + 0x120 était déjà non-nul !
      • Le saut conditionnel jne 0x5733 a donc été emprunté, court-circuitant totalement l'appel super::init(0) (IOService::init()) !
    2. Conséquence fatale sur IOService et les tables de propriétés :
      • Sans IOService::init(), l'infrastructure de base de l'objet (dictionnaire de propriétés fPropertyTable, table de registre IORegistry, verrous de synchronisation) n'était pas allouée.
      • Dès l'étape suivante (setInterfaceRole(1)), le noyau exécutait setPropertyHelper(this, "IO80211InterfaceRole", ...) qui tente d'écrire dans la table de propriétés NULL de l'objet, déclenchant un Kernel Page Fault instantané !
  • Résolutions Techniques Appliquées :
    1. Suppression de l'appel prématuré à initIvars dans createSkywalkInterface :
      • createSkywalkInterface ne fait plus que operator new + mise à zéro explicite (bzero(0x128)) + constructeur C1.
      • mem + 0x120 reste strictement NULL.
      • fSkywalkInterface->init(...) constate 0x120 == 0, invoque légitimement super::init(0) pour initialiser IOService, puis invoque initIvars().
      • L'objet est alors 100% cohérent à la fois côté IOService et côté IO80211SkywalkInterface.
    2. Calibrage sécurisé du palier par défaut sur l'étape 2 (skywalk_step=2) :
      • Permet de valider isolément que createSkywalkInterface + init(this, mac) démarre avec succès sans risque d'effets de bord ultérieurs.
    3. Validation binaire et dépendances :
      • Compilation build_wifi.sh et validation kmutil libraries à 100%.

INC-18 : Page Fault CR2: 0xb8 dans free() et Manque de boot-arg OpenCore pour le Stade 1

  • Symptômes :
    • Bootloop sur le stade 2 (commit 0519af4) lorsque initIvars a été retiré de createSkywalkInterface.
    • L'utilisateur n'a pas pu forcer le retour au stade 1 car le boot-arg qcom_step=1 n'était pas présent dans le config.plist d'OpenCore.
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Déréférencement aveugle dans IO80211SkywalkInterface::free() (0xffffff80022f6184) :
      • Le désassemblage précis de free() révèle :
        0xffffff80022f618e: mov rax, qword ptr [rdi + 0x120]  ; rax = ivars
        0xffffff80022f6195: mov rdi, qword ptr [rax + 0xb8]   ; [ivars + 0xb8] (sans test rax!=0 !)
        
      • Si init(...) échoue, retourne false ou si free() est déclenché alors que 0x120 est NULL, le processeur exécute mov rdi, [0 + 0xb8], provoquant un #PF immédiat sur CR2: 0x00000000000000b8.
      • Le stade 1 (commit b7e32db) avait démarré jusqu'au bureau avec un succès total précisément parce que initIvars allouait 0x120 dès createSkywalkInterface, immunisant l'objet contre tout crash de libération.
    2. Absence du boot-arg qcom_step dans OpenCore :
      • Le config.plist ne contenait pas qcom_step, forçant le pilote à utiliser la valeur hardcodée en C++ (skywalk_step = 2).
  • Résolutions Techniques Appliquées :
    1. Restauration de initIvars dans createSkywalkInterface :
      • Garantit que mem + 0x120 est toujours non-nul, éliminant définitivement le piège de free() (CR2: 0xb8).
    2. Rétablissement du palier par défaut sur l'étape 1 (skywalk_step = 1) :
      • Garantit le boot immédiat jusqu'au bureau macOS comme au commit b7e32db.
    3. Injection explicite de qcom_step=1 dans les boot-args OpenCore (config.plist) :
      • Ajouté dans /Volumes/EFI/EFI/OC/config.plist sous NVRAM 7C436110-AB2A-4BBB-A880-FE41995C9F82 -> boot-args.
      • Permet à l'utilisateur de modifier le palier directement depuis OpenCore ou NVRAM.
    4. Déploiement et synchronisation EFI :
      • QualcommWifi.kext compilé et copié sur /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext (MD5: 388636543393e9cf34e5816e4a011cce).
      • Vérifié QualcommWifi.kext activé (Enabled: True) dans config.plist.

INC-19 : Découverte de IO80211SkywalkInterface::init() (Sans Arguments) et Résolution Définitive de la Table de Propriétés pour le Stade 3

  • Symptômes & Constats :
    • Succès total du Stade 2 (qcom_step=2) :
      • Démarrage 100% propre jusqu'au bureau macOS avec QualcommWifi.kext (1.1.0) et QualcommWireless.kext (1.1.0) chargés simultanément.
      • Journal noyau (/usr/bin/log) confirmant l'exécution parfaite :
        IO80211SkywalkInterface::init with controller start
        IO80211SkywalkInterface::initIvars complete
        IO80211SkywalkInterface::init complete
        
      • IORegistry confirmant l'attachement propre de l'objet QualcommWifi avec ses pipes CoreCapture (CCLogPipeUserClient).
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Désassemblage exhaustif de IO80211SkywalkInterface::init() sans arguments (0xffffff80022f568e) :
      0xffffff80022f56b5: mov rax, qword ptr [rip + 0x15aa6c] ; vtable de IOSkywalkEthernetInterface
      0xffffff80022f56bc: mov rdi, rbx
      0xffffff80022f56bf: xor esi, esi                        ; OSDictionary* = NULL
      0xffffff80022f56c1: call qword ptr [rax + 0x258]        ; IOSkywalkEthernetInterface::init(nullptr)
      0xffffff80022f56c7: test al, al
      0xffffff80022f56c9: je 0x22f56d9
      0xffffff80022f56cb: mov rdi, rbx
      0xffffff80022f56d4: jmp 0xffffff80022f520c              ; jmp initIvars() !
      
      Cette méthode effectue exactement la chaîne complète attendue :
      • Appel de IOSkywalkEthernetInterface::init(nullptr) -> IOSkywalkNetworkInterface::init(nullptr) -> IOService::init(nullptr).
      • Allocation de fPropertyTable, fRegistryTable, et fLock sur IOService.
      • Allocation du verrou interne à l'offset +0x118 (IOLockAlloc).
      • Appel terminal à initIvars() pour allouer la structure ivars à l'offset +0x120.
    2. Comportement de IO80211SkywalkInterface::setInterfaceRole(UInt32 role) (0xffffff80022f60f0) :
      • Écrit le rôle dans ivars + 0x58 (1 = kIO80211InterfaceRoleInfra).
      • Appelle IOService::SetProperties() avec la clé "IO80211InterfaceRole" et valeur "Infra".
      • Grâce à l'appel préalable à IO80211SkywalkInterface::init(), fPropertyTable est garantie allouée et valide, éliminant totalement le risque de Page Fault lors de setInterfaceRole.
  • Résolutions Techniques Appliquées :
    1. Intégration du symbole exporté _ZN23IO80211SkywalkInterface4initEv dans createSkywalkInterface :
      • Invoqué immédiatement après le constructeur C1 pour initialiser la chaîne complète d'héritage (IOService + IOSkywalkEthernetInterface + lock +0x118 + ivars +0x120).
    2. Passage au palier 3 (qcom_step=3) dans OpenCore :
      • boot-args mis à jour dans /Volumes/EFI/EFI/OC/config.plist : qcom_step=3 pour exécuter fSkywalkInterface->setInterfaceRole(1).
    3. Compilation et déploiement :
      • build_wifi.sh validé à 100% avec kmutil libraries.
      • Kext déployé sur /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
    4. Validation Live du Stade 3 (qcom_step=3) :
      • Boot 100% propre jusqu'au bureau macOS.
      • QualcommWifi.kext (1.1.0) et QualcommWireless.kext (1.1.0) chargés simultanément.
      • Initialisation complète et configuration du rôle kIO80211InterfaceRoleInfra (IO80211InterfaceRole = "Infra") sans incident.
    5. Validation Live du Stade 4 (qcom_step=4) :
      • Boot 100% propre jusqu'au bureau macOS avec les deux kexts chargés.
      • Exécution nominale de attachInterface(fSkywalkInterface, this) (slot 441 de IO80211Controller).
      • IONetworkController::prepare() et IOEthernetController::publishProperties() exécutés avec succès.
      • Publication dans l'IORegistry des propriétés réseau Ethernet fondamentales (IOMACAddress = <00037f123456>, IOMaxPacketSize = 1518, IOMinPacketSize = 64, IOPacketFilters).
      • Liaison établie dans les structures internes du contrôleur (controller->ivars + 0x188 = fSkywalkInterface).
    6. Validation Live du Stade 5 (qcom_step=5) :
      • Boot 100% propre jusqu'au bureau macOS avec les deux kexts chargés.
      • Confirmation dans l'IORegistry de l'attachement effectif de IO80211SkywalkInterface sous l'arborescence QualcommWifi.
      • Propriété "IO80211InterfaceRole" = "Infrastructure" vérifiée sur le nœud IO80211SkywalkInterface.
      • L'arborescence matérielle IOKit complète est désormais formée (QualcommWifi -> CCPipe, CCLogStream, IO80211SkywalkInterface).
    7. Armement du Stade 6 (qcom_step=6) :
      • boot-args configuré sur qcom_step=6 dans /Volumes/EFI/EFI/OC/config.plist pour démarrer le sous-système Skywalk (fSkywalkInterface->start(this) : initialisation PMtree, PeerManager, IO80211Glue et association au WorkQueue).

INC-20 : Analyse de l'Échec du Stade 6 (qcom_step=6) & Résolution par Implémentation du Power Management IOKit

  • Date : 18 Octobre 2026
  • Composant : QualcommWifi.kext / IO80211SkywalkInterface::start(IOService *controller)
  • Symptômes & Constats :
    • Lors du test initial de qcom_step=6, la machine a rencontré un redémarrage en boucle (bootloop).
    • L'utilisateur est revenu temporairement sur qcom_step=5, qui a redémarré de manière 100% stable jusqu'au bureau avec IO80211SkywalkInterface parfaitement attaché (IO80211InterfaceRole = "Infrastructure").
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Kernel Collection) :
    1. Analyse de la séquence interne de IO80211SkywalkInterface::start(IOService *provider) (0xffffff80022f5792) :
      • Le désassemblage Capstone direct de BootKernelExtensions.kc a révélé les étapes précises :
        1. Vérification ivars + 0x28 == NULL (protection contre appel réentrant : panic "Calling start twice in a row is not allowed").
        2. Appel de super::start(provider) (IOService::start(provider)).
        3. Cast dynamique du contrôleur via OSDynamicCast(IO80211Controller, provider).
        4. Récupération et conservation de controller->getWorkQueue().
        5. Création des canaux CoreCapture CCPipe et flux de journalisation CCStream.
        6. Instanciation du IO80211PeerManager via createPeerManager() (0xffffff80022f7be6) qui alloue le verrou récursif controller->allocIO80211RecursiveLock() (slot 435 / offset +0xd98) et le IO80211CommandGate.
        7. Étape critique de gestion de l'énergie (Power Management) :
          0xffffff80022f5c6c: mov rax, qword ptr [r12]
          0xffffff80022f5c70: mov rdi, r12
          0xffffff80022f5c73: call qword ptr [rax + 0x790] ; fSkywalkInterface->PMinit() (slot 242)
          0xffffff80022f5c79: mov rax, qword ptr [r15]     ; r15 = QualcommWifi (contrôleur)
          0xffffff80022f5c7c: mov rdi, r15
          0xffffff80022f5c7f: mov rsi, r12                 ; rsi = fSkywalkInterface
          0xffffff80022f5c82: call qword ptr [rax + 0x7a0] ; QualcommWifi->joinPMtree(fSkywalkInterface) (slot 244)
          0xffffff80022f5c88: mov rax, qword ptr [r12]
          0xffffff80022f5c8c: lea rdx, [rip + 0x19564d]    ; tableau des 2 états IOPMPowerState
          0xffffff80022f5c93: mov ecx, 2
          0xffffff80022f5c98: mov rdi, r12
          0xffffff80022f5c9b: mov rsi, r12
          0xffffff80022f5c9e: call qword ptr [rax + 0x7a8] ; fSkywalkInterface->registerPowerDriver(...) (slot 245)
          0xffffff80022f5ca4: mov esi, 1
          0xffffff80022f5ca9: mov rdi, r12
          0xffffff80022f5cac: call 0xffffff8000abbb80     ; changePowerStateToPriv(1)
          0xffffff80022f5cb1: mov rdi, r12
          0xffffff80022f5cb4: xor esi, esi
          0xffffff80022f5cb6: call 0xffffff8000abbaf0     ; changePowerStateTo(0)
          
    2. Origine du Panic / Page Fault : Absence de PM sur le Contrôleur Parent :
      • À 0xffffff80022f5c82, IO80211SkywalkInterface invoque QualcommWifi->joinPMtree(fSkywalkInterface) afin d'intégrer l'interface réseau dans l'arbre d'alimentation du pilote hôte.
      • Dans le noyau XNU (0xffffff8000ab9a00), IOService::joinPMtree transmet à gIOPMrootDomain->joinPMtree(QualcommWifi, fSkywalkInterface).
      • Cette méthode manipule obligatoirement le contexte de puissance du parent (QualcommWifi->pm_vars à l'offset +0x60).
      • Puisque QualcommWifi n'avait jamais exécuté PMinit(), son pointeur pm_vars était nul (0x0), provoquant un déréférencement immédiat de pointeur nul et un Kernel Trap Page Fault (CR2: 0x00000000000000d8 / _IOLockLock).
    3. Dangerosité du gestionnaire d'erreur de QualcommWifi.cpp :
      • En cas de retour false de start(), l'ancien code de repli appelait detachInterface(fSkywalkInterface, false) (qui est une méthode d'IOEthernetController attendant un IONetworkInterface*) et libérait fSkywalkInterface alors qu'il était déjà enregistré dans l'IORegistry et dans controller->ivars + 0x188, garantissant un panic même si start() échouait proprement.
  • Résolutions Techniques Appliquées :
    1. Intégration du cycle de vie Power Management complet dans QualcommWifi :
      • Ajout de PMinit() et joinPMtree(provider) dans QualcommWifi::start() dès l'ouverture du périphérique PCIe.
      • Enregistrement du modèle standard d'alimentation à 2 états (State 0: Off, State 1: On / IOPMDeviceUsable | IOPMPowerOn) via registerPowerDriver(this, sPowerStates, 2) et transition initiale vers l'état actif (changePowerStateToPriv(1)).
      • Surcharge de setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) dans QualcommWifi.hpp et QualcommWifi.cpp renvoyant IOPMAckImplied.
      • Appel de PMstop() dans QualcommWifi::stop().
    2. Sécurisation du gestionnaire d'erreur de start() :
      • Suppression de tous les appels destructeurs detachInterface() et OSSafeReleaseNULL() dans les branches d'erreur de skywalk_start, skywalk_attach et skywalk_init : l'interface est préservée intacte pour analyse des journaux système sans déstabiliser le noyau.
    3. Validation et Déploiement :
      • Recompilation avec ./build_wifi.sh : validation à 100% avec kmutil libraries contre BootKernelExtensions.kc.
      • Déploiement du nouveau kext sur /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
      • Configuration du boot-arg dans /Volumes/EFI/EFI/OC/config.plist : qcom_step=6 prêt pour test en direct.

Postmortem 16 : Diagnostic par Persistance NVRAM du Crash Early-Boot (Régression de l'Alias WorkLoop et Isolation Chirurgicale du Stade 6)

  • Date : 18-19 Octobre 2026
  • Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, QualcommWifi.kext (WCN7850 0x17cb:0x1107)
  • Symptômes & Constats :
    • Après avoir testé le commit e0414a9 avec qcom_step=6 puis en revenant sur qcom_step=5, la machine est restée bloquée en bootloop immédiat dès les premières lignes du noyau XNU.
    • La pause visuelle de 10 secondes prévue dans qcom_substep=1 ne s'exécutait même plus.
    • L'utilisateur a dû désactiver temporairement QualcommWifi.kext (Enabled: false) via Linux pour redémarrer sur le bureau macOS.
    • Interrogation en direct de la mémoire NVRAM macOS (nvram -p | grep qcom) :
      qcom_stage    createWorkQueue
      boot-args     ... qcom_step=5 qcom_substep=1
      
  • Causes Racines (Breakthroughs & Rétro-ingénierie du Noyau) :
    1. Origine du Panic Instantané : Régression de l'Alias createWorkLoop() :
      • Dans le commit e0414a9, une tentative d'unification avait été faite pour que createWorkLoop() appelle createWorkQueue() et que getWorkLoop() retourne (IOWorkLoop*)fWorkQueue.
      • Dans la hiérarchie IOKit, IONetworkController::start(provider) invoque createWorkLoop() puis getWorkLoop(), s'attendant impérativement à une instance allouée via IOWorkLoop::workLoop().
      • IO80211WorkQueue n'hérite pas de IOWorkLoop : leurs tables virtuelles (vtables) sont incompatibles.
      • Dès le déréférencement de fWorkLoop dans super::start(provider) (ligne 706 de QualcommWifi.cpp), le noyau a subi un Kernel Panic immédiat avant même d'atteindre le Stade 1 ou le Stade 5, et avant le IOSleep(10000).
      • La variable NVRAM qcom_stage figée sur createWorkQueue a fourni la preuve irréfutable du lieu exact du crash.
    2. Confirmation du Comportement Matériel de la NVRAM UEFI :
      • La NVRAM UEFI réside physiquement dans la puce SPI Flash de la carte mère.
      • Elle est totalement non-volatile : un reboot, une coupure de courant ou un démarrage sous Linux ne supprime aucunement les propriétés NVRAM.
      • L'impossibilité d'écrire dans un fichier disque en early boot : au chargement des kexts de boot, les volumes APFS sont scellés cryptographiquement en lecture seule (snapshot système) et le VFS BSD n'est pas encore monté. PEWriteNVRAMProperty constitue ainsi l'unique canal de diagnostic persistant bas-niveau disponible.
    3. Cartographie Précise du Chemin d'Exécution entre getWorkQueue() et allocIO80211RecursiveLock() dans IO80211SkywalkInterface::start() :
      • Le désassemblage Capstone complet a révélé la séquence exacte exécutée par IO80211SkywalkInterface::start(IOService *provider) (0xffffff80022f5792) :
        1. 0xffffff80022f5867: call [rax + 0xc68] -> 1er appel à QualcommWifi::getWorkQueue().
        2. 0xffffff80022f5888: fWorkQueue->retain().
        3. 0xffffff80022f59f4: call CCPipe::withOwnerNameCapacity (CoreCapture data/log pipe).
        4. 0xffffff80022f5a62: call IO80211Controller::io80211isDebuggable(bool*).
        5. 0xffffff80022f5a8f: call CCStream::withPipeAndName.
        6. 0xffffff80022f5abc: call CCPipe::startPipe.
        7. 0xffffff80022f5bca: call IO80211SkywalkInterface::createPeerManager() (0xffffff80022f7be6).
        8. Dans IO80211PeerManager::initWithInterface() (0xffffff8002246c88) :
          • 0xffffff8002246ed5: call [rax + 0xc68] -> 2ème appel à QualcommWifi::getWorkQueue().
          • 0xffffff8002246ee6: fWorkQueue->retain().
          • 0xffffff8002246f04: call [rax + 0xd88] -> QualcommWifi::allocIO80211RecursiveLock().
  • Résolutions Techniques Appliquées :
    1. Restauration de l'Allocation Dédiée IOWorkLoop :
      • Réintégration de fWorkLoop = IOWorkLoop::workLoop() dans createWorkLoop() et du retour de fWorkLoop dans getWorkLoop().
    2. Suppression de kprintf() :
      • Élimination de kprintf de logNvramStage() pour prévenir tout blocage spinlock/MSR avant l'attachement de la console série.
    3. Traçage Numéroté Granulaire dans la NVRAM :
      • getWorkQueue() incrémente un compteur statique et enregistre cb_getWorkQueue_1, cb_getWorkQueue_2 dans la NVRAM.
      • allocIO80211RecursiveLock() enregistre cb_allocLock dans la NVRAM.
    4. Validation :
      • Compilation réussie sans avertissement (./build_wifi.sh), validation kmutil libraries à 100% contre BootKernelExtensions.kc.

Postmortem 17 : Audit Binaire Exhaustif des Callbacks Skywalk / airportd et Armement Sécurisé du Stade 6

  • Date : 19 Octobre 2026
  • Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, QualcommWifi.kext (WCN7850 0x17cb:0x1107)
  • Contexte & Démarche (/grill-me) :
    • Suite à l'éradication de la régression d'alias IOWorkLoop (Postmortem 16), un audit désassemblé systématique des interactions binaires entre IO80211Family.kext et le contrôleur IO80211Controller / QualcommWifi a été mené afin d'anticiper et de neutraliser tout déréférencement fatal ou point de blocage avant le démarrage matériel.
  • Breakthroughs & Rétro-ingénierie du Kernel Collection (BootKernelExtensions.kc) :
    1. Cartographie Complète des 38 Slots Virtuels Sollicités dans IO80211Family :
      • Le balayage automatique Capstone sur les 2,5 Mo du segment IO80211Family a recensé l'intégralité des 38 slots de méthodes virtuelles invoqués sur le contrôleur et l'interface :
        • Slot 394 (0xc50) : isCommandProhibited(int) (1 appel) -> retourne false.
        • Slot 397 (0xc68) : getWorkQueue() (47 appels) -> retourne fWorkQueue.
        • Slot 399 (0xc78) : getIO80211CommandGate() (10 appels) -> instancié dans super::start (ivars + 0x3c0).
        • Slot 400 (0xc80) : getPrimarySkywalkInterface() (99 appels !) :
          • Révélation critique à l'adresse 0xffffff80021d9836 :
            call [rax + 0xc80]           ; controller->getPrimarySkywalkInterface()
            mov rax, qword ptr [rax + 0x120] ; Déréférencement direct de ivars SANS test de nullité !
            
          • Si getPrimarySkywalkInterface() retourne NULL, le noyau subit un Kernel Page Fault instantané CR2: 0x0000000000000120.
          • L'implémentation par défaut de IO80211Controller interroge ivars + 0x188.
          • Blindage : Surcharge explicite de getPrimarySkywalkInterface() dans QualcommWifi renvoyant directement fSkywalkInterface.
        • Slot 403 (0xc98) : handleCardSpecific() (5 appels) -> redirigé vers handleApple80211Request.
        • Slot 404 (0xca0) : hardwareOutputQueueDepth() (2 appels) -> retourne 0.
        • Slot 405 (0xca8) : performCountryCodeOperation() (2 appels) -> retourne 0.
        • Slot 406 (0xcb0) : dataLinkLayerAttachComplete() -> accusé de réception journalisé NVRAM.
        • Slot 424 (0xd40) : getLogger() (54 appels) -> retourne fLogger (conforme FakeLogStream).
        • Slot 432 (0xd80) : getFaultReporterFromDriver() (1 appel) -> retourne fFaultReporter (conforme FakeFaultReporter).
        • Slot 433 (0xd88) : allocIO80211RecursiveLock() (1 appel) -> allocation concrète du verrou récursif pour IO80211PeerManager.
        • Slot 435 (0xd98) : wasDynSARInFailSafeMode() (1 appel) -> stub sécurisé renvoyant 0.
        • Slot 438 (0xdb0) : getActionFramePoolCapacity() (2 appels) -> retourne 0x100.
        • Slot 462 (0xe70) : setMulticastList(ether_addr const*, UInt32) (5 appels) :
          • Surcharge explicite ajoutée retournant kIOReturnSuccess pour sécuriser les requêtes multicast de la pile réseau BSD.
    2. Stratégie d'Amorçage Validée : Isolation Logicielle (qcom_mmio=0) et Stade 6 (qcom_step=6) :
      • Démarrage en mode logiciel pur : pas d'accès direct MMIO/ASPM aux registres matériels du SoC WCN7850 afin de tester la chaîne Skywalk/airportd dans un environnement parfaitement déterministe.
      • Configuration du boot-arg qcom_substep=1 : pause de 10 secondes précédant l'appel start(this) pour observation visuelle sur console série/écran.
  • Modifications Apportées :
    1. QualcommWifi/QualcommWifi.hpp : déclaration des surcharges getPrimarySkywalkInterface() et setMulticastList().
    2. QualcommWifi/QualcommWifi.cpp : implémentation avec journalisation NVRAM (cb_getPrimarySkywalk, cb_setMulticast).
    3. Déploiement :
      • Recompilation avec ./build_wifi.sh (validation kmutil libraries 100%).
      • Déploiement vers /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
      • Mise à jour du boot-args OpenCore dans /Volumes/EFI/EFI/OC/config.plist : qcom_step=6 qcom_substep=1.

Postmortem 18 : Découpage Chirurgical en 3 Pauses de l'Étape 6 et Blindage du Fil d'Ariane NVRAM sans Lock IORegistry

  • Date : 19 Octobre 2026
  • Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, QualcommWifi.kext (WCN7850 0x17cb:0x1107)
  • Symptômes & Retours du Test en Direct (Stade 6) :
    • Boot-args armé : qcom_step=6 qcom_substep=1.
    • La pause visuelle de 10 secondes précédant l'appel fSkywalkInterface->start(this) s'est exécutée avec un succès absolu : le mode verbose de macOS est resté figé 10s de manière saine, attestant de la stabilité intégrale des étapes 1 à 5 (allocation, initialisation, rôle infrastructure, attachement IOKit et arborescence CCPipe).
    • Dès la fin exacte des 10 secondes, lors de l'engagement de fSkywalkInterface->start(this) ou immédiatement après, le système a crashé et redémarré instantanément sans afficher l'écran de Kernel Panic classique (malgré debug=0x100).
    • Au redémarrage de secours sous qcom_step=5, QualcommWifi::start() a complété son initialisation et réécrit "start_done" dans qcom_stage, masquant le point d'interruption du crash.
  • Causes Racines & Breakthroughs Techniques :
    1. Écrasement Silencieux de la Télémétrie NVRAM :
      • Lorsque l'utilisateur revient sur le bureau avec qcom_step=5 pour diagnostiquer le crash, QualcommWifi::init() et start() s'exécutent avec succès et écrasent qcom_stage.
      • Résolution : Implémentation dans QualcommWifi::init() d'une détection de crash antécédent. Si qcom_stage ne vaut pas start_done ou clean, il est automatiquement copié de manière non volatile dans qcom_last_crash, et le fil d'Ariane est copié dans qcom_last_trail.
    2. Contention et Risque de Deadlock sous IOKit Power Management :
      • logNvramStage() appelait systématiquement IORegistryEntry::fromPath("/options", gIODTPlane) pour y inscrire la propriété IOKit.
      • Dans IO80211SkywalkInterface::start(), macOS initialise la gestion d'alimentation (PMinit(), joinPMtree(this), registerPowerDriver), puis ordonne les transitions changePowerStateToPriv(1) et changePowerStateTo(0), déclenchant le callback QualcommWifi::setPowerState().
      • Tenter de traverser et verrouiller le plan /options du Device Tree (gIODTPlane) sous un verrou PM d'un thread noyau peut provoquer une contention mortelle ou un blocage critique.
      • Résolution : Suppression totale de l'accès à IORegistryEntry::fromPath dans logNvramStage(). Utilisation exclusive et directe de PEWriteNVRAMProperty (Platform Expert) qui écrit dans la NVRAM matérielle sans aucun verrou de plan IORegistry.
    3. Manque de Temporisation Post-start(this) et Fil d'Ariane Continu :
      • Sous qcom_substep=1, aucune pause n'était observée après start(this). Si start(this) réussissait, le kext sortait immédiatement de start(), exposant l'interface aux démons macOS (airportd, Skywalk) sans permettre de vérifier si s6_2_start_ok avait été atteint.
      • Résolution : Implémentation d'un fil d'Ariane continu (qcom_trail) enchaînant chaque balise (s6_0>s6_1>cb_wq_1>cb_lock>...) et découpage de l'Étape 6 en 3 pauses balisées explicites :
        • [PAUSE 1/3] : 3 secondes avant fSkywalkInterface->start(this).
        • [PAUSE 2/3] : 5 secondes après retour réussi de start(this) avec bannière géante [SUCCES MAJEUR].
        • [PAUSE 3/3] : 3 secondes avant la fin de QualcommWifi::start().
  • Modifications Apportées :
    1. QualcommWifi/QualcommWifi.cpp :
      • Élimination des locks IORegistry dans logNvramStage().
      • Ajout de gNvramTrail et écriture en temps réel de qcom_trail.
      • Sauvegarde automatique de qcom_last_crash et qcom_last_trail dans init().
      • Découpage en 3 pauses de l'Étape 6 sous qcom_substep >= 1.
    2. Déploiement :
      • Compilation sans erreur avec ./build_wifi.sh (validation kmutil à 100%).
      • Déploiement dans /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
      • Armement de qcom_step=6 qcom_substep=1 dans /Volumes/EFI/EFI/OC/config.plist.

Postmortem 19 : Élucidation de la Saturation NVRAM (511 Octets), Latence SPI et Synchronisation Matérielle de l'Étape 6

  • Date : 19 Octobre 2026
  • Cible : macOS Tahoe 26.x (Darwin 25.6.0 x86_64), AMD Ryzen 7 5800X3D, QualcommWifi.kext (WCN7850 0x17cb:0x1107)
  • Symptômes & Retours Utilisateur (/grill-me) :
    • L'utilisateur a testé l'Étape 6 (qcom_step=6 qcom_substep=1) et la machine a redémarré (crash / triple fault).
    • En régressant à qcom_step=5 pour relancer macOS, l'utilisateur a constaté qu'un temps d'arrêt / pause d'environ 2 à 3 secondes persistait au démarrage.
    • La console verbose n'affiche aucun nouveau message après MB: B550 AORUS ELITE V2 Gigabyte Technology Co., Ltd. car le framebuffer vidéo se fige à ce stade alors que le noyau poursuit son initialisation en tâche de fond.
    • La variable qcom_last_trail capturée dans la NVRAM matérielle se terminait abruptement par register_bypa sans faire apparaître les balises de l'Étape 6 (s6_0_before_start, s6_1_calling_start).
  • Causes Racines & Breakthroughs Techniques :
    1. Saturation Mathématique du Buffer gNvramTrail à 511 Octets :
      • Le buffer static char gNvramTrail[512] avait une capacité maximale de 511 caractères utiles (+ \0).
      • Le cumul des 37 balises d'initialisation a atteint très exactement 511 octets au tag register_bypa (troncature de register_bypassed).
      • Par conséquent, strlcat rejetait silencieusement toutes les balises ultérieures.
    2. Explication Physique de la Pause Résiduelle en Régressant de Step :
      • La persistance perçue au boot sous qcom_step=5 provient de deux facteurs :
        • qcom_substep=1 était toujours mémorisé dans les boot-args de la NVRAM.
        • PEWriteNVRAMProperty est un appel synchrone bloquant vers les EFI Runtime Services pour écrire dans la puce SPI Flash de la carte mère Gigabyte B550. Avec 37 écritures successives (dont des callbacks répétés comme cb_getWorkQueue et cb_getLogger), le CPU accumulait 50 à 100 ms par écriture, soit 2,5 à 3,5 secondes de freeze matériel, parfaitement confondu avec une pause logicielle.
    3. Perte Volatile des Écritures lors d'un Triple Fault Immédiat :
      • Lors du crash dans fSkywalkInterface->start(this), le hard reset intervenait dans la microseconde suivant s6_1_calling_start, empêchant le contrôleur SPI d'achever son cycle d'écriture.
  • Résolutions Techniques Appliquées :
    1. Quadruplement du Buffer Télémétrique (gNvramTrail) :
      • Extension de gNvramTrail et prevTrail à 2048 octets.
    2. Filtrage des Callbacks Répétitifs pour Supprimer la Latence SPI :
      • getWorkQueue() ne loggue dans la NVRAM que ses 3 premières invocations.
      • getLogger() et getFaultReporterFromDriver() ne logguent dans la NVRAM que leurs 2 premières invocations.
      • Élimination de plus de 80% des écritures SPI inutiles au boot.
    3. Délai de Synchronisation Matérielle (IOSleep(200)) :
      • Insertion d'une temporisation de 200 ms immédiatement après logNvramStage("s6_1_calling_start") pour garantir que l'écriture est physiquement gravée dans la puce flash avant l'engagement de fSkywalkInterface->start(this).
    4. Validation :
      • Compilation réussie sans avertissement (./build_wifi.sh), conformité kmutil libraries à 100% contre BootKernelExtensions.kc.