fix(wifi): initialiser l'arborescence Power Management IOKit pour le Stade 6 (joinPMtree)

- Breakthroughs & Rétro-ingénierie :
  * Analyse désassemblée complète de IO80211SkywalkInterface::start (0xffffff80022f5792) dans BootKernelExtensions.kc.
  * Découverte de l'appel interne à QualcommWifi->joinPMtree(fSkywalkInterface) (slot 244 de IOService, offset +0x7a0).
  * Cause racine identifiée : QualcommWifi n'initialisait pas Power Management (PMinit non appelé), laissant son pointeur pm_vars à NULL. L'accès à pm_vars dans gIOPMrootDomain provoquait un Kernel Trap Page Fault immédiat.
  * Détection d'un gestionnaire d'erreur destructeur dans start() qui appelait detachInterface(IONetworkInterface*) et provoquait des corruptions mémoire en cas d'échec transitoire.

- Modifications apportées :
  * QualcommWifi/QualcommWifi.hpp : déclaration de la méthode virtuelle setPowerState override.
  * QualcommWifi/QualcommWifi.cpp : initialisation complète du cycle Power Management (PMinit, joinPMtree, registerPowerDriver sur 2 états Off/On, changePowerStateToPriv), implémentation de setPowerState et PMstop().
  * QualcommWifi/QualcommWifi.cpp : sécurisation des blocs d'erreur dans start() (suppression des detachInterface/release brutaux d'objets enregistrés dans l'IORegistry).
  * docs/ISSUES_AND_POSTMORTEMS.md : consignation détaillée de l'incident INC-20.

- Validation :
  * Compilation clang++ réussie sans erreur.
  * Validation kmutil libraries à 100% contre BootKernelExtensions.kc.
  * Kext déployé sur /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
  * OpenCore armé avec boot-arg qcom_step=6 dans config.plist.
This commit is contained in:
Erwan committed 2026-10-18 19:26:59 +02:00
1 parent cfdbaaa676
commit 6a1af27d4f
3 files changed
+85 -18

No files matched your search

+22 -18
View File
@@ -717,6 +717,18 @@ bool QualcommWifi::start(IOService *provider) {
}
logNvramStage("pci_open_ok");
// Initialisation complète de l'arborescence Power Management (requis pour fSkywalkInterface->joinPMtree(this))
logNvramStage("pm_init");
PMinit();
joinPMtree(provider);
static IOPMPowerState sPowerStates[2] = {
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 1, IOPMDeviceUsable | IOPMPowerOn, IOPMPowerOn, IOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0 }
};
registerPowerDriver(this, sPowerStates, 2);
changePowerStateToPriv(1);
IOLog("QualcommWifi: Power Management initialise avec succes (PMinit + joinPMtree + registerPowerDriver) !\n");
// La gestion de l'espace mémoire PCIe et du Bus Master est gérée automatiquement par IOKit
// (Éviter les écritures de configuration brutes risquant de déstabiliser le root port AMD)
logNvramStage("pci_ready");
@@ -841,32 +853,17 @@ bool QualcommWifi::start(IOService *provider) {
}
} else {
logNvramStage("skywalk_start_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->start(this) a échoué !\n");
if (fSkywalkAttached) {
fSkywalkInterface->detach(this);
fSkywalkAttached = false;
}
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false);
fSkywalkCtrlAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
IOLog("QualcommWifi: AVERTISSEMENT - fSkywalkInterface->start(this) a echoue (interface preservee pour diagnostic) !\n");
}
}
} else {
logNvramStage("skywalk_attach_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->attach(this) a échoué !\n");
if (fSkywalkCtrlAttached) {
detachInterface(fSkywalkInterface, false);
fSkywalkCtrlAttached = false;
}
OSSafeReleaseNULL(fSkywalkInterface);
IOLog("QualcommWifi: Erreur - fSkywalkInterface->attach(this) a echoue !\n");
}
}
} else {
logNvramStage("skywalk_init_err");
IOLog("QualcommWifi: Erreur - fSkywalkInterface->init(...) a échoué !\n");
OSSafeReleaseNULL(fSkywalkInterface);
IOLog("QualcommWifi: Erreur - fSkywalkInterface->init(...) a echoue !\n");
}
}
}
@@ -936,6 +933,7 @@ void QualcommWifi::stop(IOService *provider) {
OSSafeReleaseNULL(fWorkQueue);
}
PMstop();
super::stop(provider);
}
@@ -1295,3 +1293,9 @@ SInt32 QualcommWifi::handleApple80211Request(unsigned int req_type, int req_val,
}
return 0;
}
IOReturn QualcommWifi::setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) {
IOLog("QualcommWifi: setPowerState(ordinal=%lu, device=%s)\n",
powerStateOrdinal, whatDevice ? whatDevice->getName() : "null");
return IOPMAckImplied;
}
+3
View File
@@ -233,6 +233,9 @@ public:
virtual bool start(IOService *provider) override;
virtual void stop(IOService *provider) override;
// Power Management (requis pour satisfaire joinPMtree de fSkywalkInterface->start)
virtual IOReturn setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) override;
// WorkQueue / WorkLoop interface required by IO80211Controller / IONetworkController
virtual bool createWorkLoop(void) override;
virtual IOWorkLoop* getWorkLoop(void) const override;
+60
View File
@@ -554,6 +554,66 @@ Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocage
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)** :
```assembly
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.