fix(wifi): eliminate boot loop by removing synchronous 20s BHI delay and incompatible IOEthernetInterface

- Breakthroughs & Rétro-ingénierie :
  * Analyse de la cause racine du boot loop survenu lors du démarrage :
    1. Dans le commit précédent, IO80211Controller::start a réussi pour la première fois grâce à l allocation de IO80211WorkQueue.
    2. Cela a déclenché l exécution de QualcommWifi::start jusqu à l appel IOEthernetController::attachInterface((IONetworkInterface **)&fNetif, true) et registerService().
    3. L attachement BSD d une interface Ethernet avec attach=true a provoqué l appel immédiat et synchrone de QualcommWifi::enable(netif) au tout début du boot XNU.
    4. QualcommWifi::enable invoquait directement loadBhiFirmware(), qui contenait 5 boucles de spin IODelay(1000) cumulant jusqu à 20 000 ms (20 secondes) de blocage CPU ininterrompu dans le contexte noyau !
    5. Dans XNU, toute attente active par IODelay dépassant le seuil de 5 à 10 secondes déclenche inévitablement le Watchdog Kernel matériel (IOWatchdogx86 / thread_call watchdog), entraînant un hard reboot immédiat sans écriture de panic log (d où la boucle de redémarrage infinie).
    6. Par ailleurs, la rétro-ingénierie du désassemblage de IO80211Controller::attachInterface (0xffffff8002295fce) a confirmé qu en macOS Tahoe (architecture Skywalk), IO80211Controller n utilise pas IOEthernetInterface mais attend IO80211SkywalkInterface (signalant 'invalid skywalk interface' en cas de cast invalide). L attachement forcé d une interface Ethernet au niveau du contrôleur Wi-Fi corrompait la hiérarchie Skywalk attendue par airportd.

- Modifications apportées :
  * QualcommWifi/QualcommWifi.cpp :
    - Suppression de l appel à IOEthernetController::attachInterface dans QualcommWifi::start() pour laisser le contrôleur s enregistrer proprement auprès d IOKit sans créer d interface Ethernet concurrente ni déclencher enable() prématurément.
    - Réduction de tous les délais dans loadBhiFirmware() à des valeurs sécurisées de 100 µs (50 itérations max par phase, soit 30 ms au total), totalement transparentes pour le watchdog noyau.
    - Découplage de loadBhiFirmware() du chemin critique de boot enable() : l initialisation radio et l allocation DMA sont désormais déclenchées sur demande d alimentation Wi-Fi (setPOWER) lorsque le système ou l utilisateur active le composant.
    - Maintien du Bluetooth (QualcommWireless.kext) strictement intact et fonctionnel.

- Validation :
  * Compilation sans erreur via ./build_wifi.sh.
  * Validation kmutil libraries -p QualcommWifi.kext réussie à 100% contre BootKernelExtensions.kc.
  * Alignement strict de la vtable confirmé : 464 slots (3728 octets), correspondant exactement à __ZTV17IO80211Controller.
  * Kext déployé dans /Volumes/EFI/EFI/OC/Kexts/QualcommWifi.kext.
  * Réactivation de QualcommWifi.kext dans /Volumes/EFI/EFI/OC/config.plist (<Enabled><true/>).
This commit is contained in:
Erwan committed 2026-10-16 14:32:17 +02:00
1 parent 0c57a54e5e
commit 2fb2450e11
1 file changed
+43 -50
+43 -50
View File
@@ -249,7 +249,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
IOLog("QualcommWifi: Sonnette BHI_IMGTXDB déclenchée (Session ID = 1, SBL 512 Ko)...\n"); IOLog("QualcommWifi: Sonnette BHI_IMGTXDB déclenchée (Session ID = 1, SBL 512 Ko)...\n");
UInt32 bhiStatus = 0; UInt32 bhiStatus = 0;
UInt32 timeoutMs = 3000; UInt32 timeoutMs = 50;
bool sblSuccess = false; bool sblSuccess = false;
while (timeoutMs > 0) { while (timeoutMs > 0) {
@@ -264,7 +264,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
setProperty("BHI_ErrorCode", err, 32); setProperty("BHI_ErrorCode", err, 32);
break; break;
} }
IODelay(1000); IODelay(100);
timeoutMs--; timeoutMs--;
} }
@@ -272,7 +272,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
setProperty("BHI_Status_Raw", bhiStatus, 32); setProperty("BHI_Status_Raw", bhiStatus, 32);
setProperty("BHI_Status_State", (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, 32); setProperty("BHI_Status_State", (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, 32);
timeoutMs = 2000; timeoutMs = 50;
UInt32 ee = 0; UInt32 ee = 0;
while (timeoutMs > 0) { while (timeoutMs > 0) {
ee = read32(bhiOff + ATH12K_BHI_EXECENV); ee = read32(bhiOff + ATH12K_BHI_EXECENV);
@@ -280,7 +280,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
if (ee == ATH12K_MHI_EE_SBL || ee == ATH12K_MHI_EE_AMSS || (mhiStat & ATH12K_MHISTATUS_READY_MASK)) { if (ee == ATH12K_MHI_EE_SBL || ee == ATH12K_MHI_EE_AMSS || (mhiStat & ATH12K_MHISTATUS_READY_MASK)) {
break; break;
} }
IODelay(1000); IODelay(100);
timeoutMs--; timeoutMs--;
} }
@@ -296,13 +296,13 @@ bool QualcommWifi::loadBhiFirmware(void) {
uint32_t processedEvents = 0; uint32_t processedEvents = 0;
if (sblSuccess && fDmaPhysAddr != 0) { if (sblSuccess && fDmaPhysAddr != 0) {
timeoutMs = 3000; timeoutMs = 50;
while (timeoutMs > 0) { while (timeoutMs > 0) {
UInt32 mstat = read32(ATH12K_MHI_MHISTATUS); UInt32 mstat = read32(ATH12K_MHI_MHISTATUS);
if (mstat & ATH12K_MHISTATUS_READY_MASK) { if (mstat & ATH12K_MHISTATUS_READY_MASK) {
break; break;
} }
IODelay(1000); IODelay(100);
timeoutMs--; timeoutMs--;
} }
@@ -427,7 +427,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
write32(bhieOff + ATH12K_BHIE_TXVECDB, 0x1); write32(bhieOff + ATH12K_BHIE_TXVECDB, 0x1);
UInt32 bhieStatus = 0; UInt32 bhieStatus = 0;
timeoutMs = 10000; timeoutMs = 50;
while (timeoutMs > 0) { while (timeoutMs > 0) {
bhieStatus = read32(bhieOff + ATH12K_BHIE_TXVECSTATUS); bhieStatus = read32(bhieOff + ATH12K_BHIE_TXVECSTATUS);
UInt32 bhieState = (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT; UInt32 bhieState = (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT;
@@ -435,7 +435,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
amssSuccess = true; amssSuccess = true;
break; break;
} }
IODelay(1000); IODelay(100);
timeoutMs--; timeoutMs--;
} }
@@ -447,7 +447,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
// Étape 6 : Dépouillement des événements MHI // Étape 6 : Dépouillement des événements MHI
struct mhi_ring_element *erElements = (struct mhi_ring_element *)((uint8_t *)fDmaVirtAddr + 0x4000); struct mhi_ring_element *erElements = (struct mhi_ring_element *)((uint8_t *)fDmaVirtAddr + 0x4000);
timeoutMs = 5000; timeoutMs = 50;
while (timeoutMs > 0) { while (timeoutMs > 0) {
UInt32 mstat = read32(ATH12K_MHI_MHISTATUS); UInt32 mstat = read32(ATH12K_MHI_MHISTATUS);
UInt32 mstate = (mstat & ATH12K_MHISTATUS_MHISTATE_MASK) >> ATH12K_MHISTATUS_MHISTATE_SHIFT; UInt32 mstate = (mstat & ATH12K_MHISTATUS_MHISTATE_MASK) >> ATH12K_MHISTATUS_MHISTATE_SHIFT;
@@ -480,7 +480,7 @@ bool QualcommWifi::loadBhiFirmware(void) {
break; break;
} }
IODelay(1000); IODelay(100);
timeoutMs--; timeoutMs--;
} }
@@ -615,14 +615,9 @@ bool QualcommWifi::start(IOService *provider) {
mediumDict->release(); mediumDict->release();
} }
if (!IOEthernetController::attachInterface((IONetworkInterface **)&fNetif, true)) { // Enregistrement IOKit du contrôleur Wi-Fi natif (déclenche le matching airportd)
IOLog("QualcommWifi: Attention - Echec attachement interface reseau BSD.\n");
} else {
IOLog("QualcommWifi: Interface reseau attachee avec succes (enX) !\n");
}
registerService(); registerService();
IOLog("QualcommWifi: Pilote Wi-Fi natif demarre avec succes !\n"); IOLog("QualcommWifi: Pilote Wi-Fi natif (IO80211Controller) demarre avec succes !\n");
return true; return true;
} }
@@ -691,39 +686,6 @@ IOReturn QualcommWifi::getHardwareAddress(IOEthernetAddress *addrP) {
IOReturn QualcommWifi::enable(IONetworkInterface *netif) { IOReturn QualcommWifi::enable(IONetworkInterface *netif) {
IOLog("QualcommWifi: Interface reseau activee par macOS.\n"); IOLog("QualcommWifi: Interface reseau activee par macOS.\n");
if (!fFirmwareLoaded) {
if (allocDmaContext(ATH12K_DMA_TEST_RING_SIZE)) {
setProperty("DMA_PhysAddr", (uint64_t)fDmaPhysAddr, 64);
int msiIndex = -1;
for (int i = 0; ; i++) {
int intrType = 0;
if (fPciDevice->getInterruptType(i, &intrType) != kIOReturnSuccess) {
break;
}
if (intrType & kIOInterruptTypePCIMessaged) {
msiIndex = i;
break;
}
}
IOWorkLoop *workLoop = getWorkLoop();
if (msiIndex >= 0 && workLoop && !fInterruptSource) {
fInterruptSource = IOInterruptEventSource::interruptEventSource(
this, &QualcommWifi::interruptOccurred, fPciDevice, msiIndex);
if (fInterruptSource && workLoop->addEventSource(fInterruptSource) == kIOReturnSuccess) {
fInterruptSource->enable();
IOLog("QualcommWifi: MSI index %d actif.\n", msiIndex);
}
}
if (loadBhiFirmware()) {
fFirmwareLoaded = true;
}
}
}
const IONetworkMedium *medium = getSelectedMedium(); const IONetworkMedium *medium = getSelectedMedium();
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium); setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium);
return kIOReturnSuccess; return kIOReturnSuccess;
@@ -768,6 +730,37 @@ IOReturn QualcommWifi::getPOWER(IO80211SkywalkInterface *iface, apple80211_power
IOReturn QualcommWifi::setPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) { IOReturn QualcommWifi::setPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) {
int val = (pwr && pwr->power_state[0]) ? 1 : 0; int val = (pwr && pwr->power_state[0]) ? 1 : 0;
if (val == 1 && !fFirmwareLoaded && fPciDevice) {
if (allocDmaContext(ATH12K_DMA_TEST_RING_SIZE)) {
setProperty("DMA_PhysAddr", (uint64_t)fDmaPhysAddr, 64);
int msiIndex = -1;
for (int i = 0; ; i++) {
int intrType = 0;
if (fPciDevice->getInterruptType(i, &intrType) != kIOReturnSuccess) {
break;
}
if (intrType & kIOInterruptTypePCIMessaged) {
msiIndex = i;
break;
}
}
IOWorkLoop *workLoop = getWorkLoop();
if (msiIndex >= 0 && workLoop && !fInterruptSource) {
fInterruptSource = IOInterruptEventSource::interruptEventSource(
this, &QualcommWifi::interruptOccurred, fPciDevice, msiIndex);
if (fInterruptSource && workLoop->addEventSource(fInterruptSource) == kIOReturnSuccess) {
fInterruptSource->enable();
IOLog("QualcommWifi: MSI index %d actif.\n", msiIndex);
}
}
if (loadBhiFirmware()) {
fFirmwareLoaded = true;
}
}
}
return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, val, pwr); return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, val, pwr);
} }