### Breakthroughs & Rétro-ingénierie
- Analyse post-mortem du crash (Kernel-2026-11-01-104036.panic) survenu dès la soumission du mot de passe Wi-Fi.
- Découverte du flux interne de setASSOCIATE (0xffffff80022876f8) :
airportd appelle setASSOCIATE -> IO80211Controller::joinStarted (0xffffff8002294922) ->
fRNGAgent->sendDrbgEntropy("assoc", 5) (0xffffff80022dde94) -> CCLogStream::logEmergency ->
CCLogStream::logv (0xffffff80032fd7d4).
- Identification de la cause racine exacte du crash (type 14 page fault à CR2: 0x0) à 0xffffff80032fd982 (+0x1b2) :
logv charge r13 = [fLogger + 0x90] (gP1Mem), puis déréférence [r13] (gP1Mem[0]) comme pointeur
vers un CCPipe pour appeler CCPipe::allocate (+0x9f0).
Dans FakeLogStream::init(), gP1Mem était bzero'd, laissant gP1Mem[0] à NULL -> déréférencement [0x0]
provoquant le kernel trap fatal avant même que sendIOUCToWcl ne soit exécuté.
### Modifications apportées
- QualcommWifi/QualcommWifi.cpp :
* Instanciation d'un pipe factice gDummyPipe avec vtable de 512 entrées (gDummyPipeVtable)
pointant vers dummyReporterFunc.
* Affectation de *(void **)(gP1Mem + 0x00) = gDummyPipe afin de garantir que tout appel virtuel
sur CCPipe (slots 0x9e8, 0x9f0, 0xa10) réussisse et retourne 0.
* Configuration des seuils log dans gP2Mem (p1 + 0x58) à -1 (*(int32_t *)gP2Mem = -1) pour
désactiver le stream et console logging, court-circuitant ainsi les allocations de logv.
* Extension de gDummyReporterVtable à 512 entrées.
- docs/ISSUES_AND_POSTMORTEMS.md :
* Ajout du post-mortem détaillé INC-34 décrivant les traces de désassemblage, la pile d'appels,
l'analyse de la cause racine et la stratégie de neutralisation de CoreCapture.
### Validation
- Compilation Release 100% propre (clang 17, ./build_wifi.sh).
- Validation binaire kmutil libraries sans symbole manquant contre BootKernelExtensions.kc.
- Déploiement du binaire kext dans /Users/erwan/Documents/EFI/OC/Kexts/QualcommWifi.kext.
2357 lines
102 KiB
C++
2357 lines
102 KiB
C++
#include "QualcommWifi.hpp"
|
|
#include <IOKit/IOLib.h>
|
|
#include <libkern/OSByteOrder.h>
|
|
#include <kern/task.h>
|
|
#include <kern/clock.h>
|
|
#include <string.h>
|
|
|
|
// Stubs locaux pour les réservations IONetworkController 2..7 non exportées par BootKernelExtensions.kc
|
|
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) {}
|
|
}
|
|
|
|
#include <pexpert/pexpert.h>
|
|
#include <IOKit/IODeviceTreeSupport.h>
|
|
#include <IOKit/IOPlatformExpert.h>
|
|
#include <IOKit/IOLocks.h>
|
|
|
|
// Buffer pour le fil d'Ariane persistant (chaînage des balises d'étapes sans verrous)
|
|
static char gNvramTrail[2048] = {0};
|
|
static int sGetWorkQueueCount = 0;
|
|
static int sGetLoggerCount = 0;
|
|
static int sGetFaultReporterCount = 0;
|
|
static bool sInStep6 = false;
|
|
static int sStep6WqCount = 0;
|
|
static IORecursiveLock *sNvramLock = nullptr;
|
|
static bool sNvramLoggingActive = true;
|
|
|
|
// Symbole exporté par IO80211Family dans BootKernelExtensions.kc :
|
|
// IO80211Controller::postMessage(IO80211SkywalkInterface*, unsigned int, void*, unsigned long, bool)
|
|
extern "C" void _ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(void *self, void *iface, unsigned int msg, void *data, unsigned long len, bool sync);
|
|
|
|
static inline void logNvramStage(const char *tag) {
|
|
if (!tag) return;
|
|
IOLog("QualcommWifi: >>> [STAGE] %s <<<\n", tag);
|
|
|
|
// Sécurité critique absolue : ZÉRO écriture NVRAM physique après le démarrage du kext
|
|
// Évite tout SMI/SMM lockup sur bus SPI Flash AMD B550 / AGESA en session bureau
|
|
if (!sNvramLoggingActive) return;
|
|
|
|
if (sNvramLock) {
|
|
IORecursiveLockLock(sNvramLock);
|
|
}
|
|
|
|
PEWriteNVRAMProperty("qcom_stage", tag, (unsigned int)strlen(tag));
|
|
|
|
// Mise à jour continue du fil d'Ariane
|
|
if (gNvramTrail[0] != '\0') {
|
|
strlcat(gNvramTrail, ">", sizeof(gNvramTrail));
|
|
}
|
|
strlcat(gNvramTrail, tag, sizeof(gNvramTrail));
|
|
PEWriteNVRAMProperty("qcom_trail", gNvramTrail, (unsigned int)strlen(gNvramTrail));
|
|
|
|
if (sNvramLock) {
|
|
IORecursiveLockUnlock(sNvramLock);
|
|
}
|
|
}
|
|
|
|
// Classe simulant un IO80211FaultReporter IOKit conforme (attendu par IO80211Controller et IO80211PeerManager)
|
|
// - Hérite directement de OSObject (35 slots de base : 0..34)
|
|
// - Déclare les 3 méthodes virtuelles de IO80211FaultReporter :
|
|
// slot 35 (offset 0x118) : reportFault(...)
|
|
// slot 36 (offset 0x120) : registerCallbacks(...) -> appelé par IO80211PeerManager::initWithInterface (0xffffff8002247d3d)
|
|
// slot 37 (offset 0x128) : unregisterCallbacks(...) -> appelé par IO80211PeerManager::free (0xffffff80022490ac)
|
|
class FakeFaultReporter : public OSObject {
|
|
OSDeclareDefaultStructors(FakeFaultReporter)
|
|
private:
|
|
uint8_t _pad[0x80];
|
|
public:
|
|
virtual bool init() override {
|
|
if (!OSObject::init()) return false;
|
|
bzero(_pad, sizeof(_pad));
|
|
return true;
|
|
}
|
|
|
|
// slot 35 (offset 0x118): reportFault
|
|
virtual IOReturn reportFault(uint32_t a, const char *b, uint32_t c, const char *d, void *e, int f, const char *g, ...) {
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// slot 36 (offset 0x120): registerCallbacks
|
|
// Invoqué par IO80211PeerManager::initWithInterface (0xffffff8002247d3d)
|
|
virtual IOReturn registerCallbacks(void *callbacks, unsigned int count, OSObject *owner, const char *owner_name) {
|
|
logNvramStage("cb_fault_reg");
|
|
IOSleep(50);
|
|
IOLog("QualcommWifi: >>> FakeFaultReporter::registerCallbacks(count=%u, owner=%s) [SUCCESS] <<<\n",
|
|
count, owner_name ? owner_name : "null");
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// slot 37 (offset 0x128): unregisterCallbacks
|
|
// Invoqué par IO80211PeerManager::free (0xffffff80022490ac)
|
|
virtual IOReturn unregisterCallbacks(OSObject *owner) {
|
|
logNvramStage("cb_fault_unreg");
|
|
IOLog("QualcommWifi: >>> FakeFaultReporter::unregisterCallbacks() [SUCCESS] <<<\n");
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
static FakeFaultReporter* create() {
|
|
FakeFaultReporter *inst = new FakeFaultReporter;
|
|
if (inst && !inst->init()) {
|
|
inst->release();
|
|
return nullptr;
|
|
}
|
|
return inst;
|
|
}
|
|
};
|
|
OSDefineMetaClassAndStructors(FakeFaultReporter, OSObject)
|
|
|
|
// Buffers statiques globaux BSS : garantis alloués et mappés dès le chargement du kext,
|
|
// immunisés contre tout échec de heap dynamique durant le boot précoce.
|
|
static uint8_t gStreamMem[0x100] __attribute__((aligned(64)));
|
|
static uint8_t gP1Mem[0x1000] __attribute__((aligned(64)));
|
|
static uint8_t gP2Mem[0x100] __attribute__((aligned(64)));
|
|
static uint8_t gDummyReporter[0x100] __attribute__((aligned(64)));
|
|
static void *gDummyReporterVtable[512];
|
|
static uint8_t gDummyPipe[0x100] __attribute__((aligned(64)));
|
|
static void *gDummyPipeVtable[512];
|
|
|
|
static IOReturn dummyReporterFunc(void *self, ...) {
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Classe de simulation binaire conforme pour CCLogStream (CoreCapture) sous macOS Tahoe
|
|
// - Hérite de IOService (slots 0..265)
|
|
// - Déclare les 14 méthodes virtuelles de CCLogStream (slots 266..279) pour un total exact de 280 slots
|
|
// évitant tout déréférencement hors-limites de vtable (ex: setLevel @ 0x868, setFlags @ 0x878, setConsoleLogFlag @ 0x8b0)
|
|
// - Offsets internes conformes :
|
|
// +0x88 (streamData) -> gStreamMem, dont +0x48 -> gDummyReporter (évite #PF dans CCLogStream::logv -> incrementValue)
|
|
// +0x90 (p1) -> gP1Mem, dont +0x00 -> gDummyPipe (évite #PF CR2:0x0 dans CCLogStream::logv -> pipe->allocate)
|
|
// dont +0x58 -> gP2Mem (filtre niveaux -1 évitant tout logging)
|
|
class FakeLogStream : public IOService {
|
|
OSDeclareDefaultStructors(FakeLogStream)
|
|
public:
|
|
void *streamData; // offset 0x88
|
|
void *p1; // offset 0x90
|
|
|
|
virtual bool init() override {
|
|
if (!IOService::init()) return false;
|
|
|
|
// Vtable factice pour le reporter et le pipe afin que tout appel virtuel
|
|
// (ex: IOSimpleReporter::incrementValue @ slot 45, ou CCPipe @ slots 0x9e8/0x9f0/0xa10) réussisse sans faute
|
|
for (int i = 0; i < 512; i++) {
|
|
gDummyReporterVtable[i] = (void *)dummyReporterFunc;
|
|
gDummyPipeVtable[i] = (void *)dummyReporterFunc;
|
|
}
|
|
*(void **)gDummyReporter = (void *)gDummyReporterVtable;
|
|
*(void **)gDummyPipe = (void *)gDummyPipeVtable;
|
|
|
|
bzero(gStreamMem, sizeof(gStreamMem));
|
|
*(void **)(gStreamMem + 0x48) = gDummyReporter;
|
|
streamData = gStreamMem;
|
|
|
|
bzero(gP1Mem, sizeof(gP1Mem));
|
|
*(void **)(gP1Mem + 0x00) = gDummyPipe; // Pipe factice indispensable évitant #PF (CR2: 0x0) dans CCLogStream::logv
|
|
|
|
bzero(gP2Mem, sizeof(gP2Mem));
|
|
*(int32_t *)(gP2Mem + 0x00) = -1; // Désactive stream logging (évite allocations logv)
|
|
*(int32_t *)(gP2Mem + 0x04) = -1; // Désactive console logging
|
|
*(uint64_t *)(gP2Mem + 0x10) = 0;
|
|
*(uint64_t *)(gP2Mem + 0x18) = 0;
|
|
*(void **)(gP1Mem + 0x58) = gP2Mem;
|
|
p1 = gP1Mem;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Méthodes virtuelles CCLogStream (slots 266 à 279 dans __ZTV11CCLogStream)
|
|
virtual void profileLoaded() {}
|
|
virtual void profileRemoved() {}
|
|
virtual bool initWithPipeAndName(void *pipe, const char *name, void *opt) { return true; }
|
|
virtual void setLevel(int level) {}
|
|
virtual int getLevel() { return 0; }
|
|
virtual void setFlags(uint64_t flags) {}
|
|
virtual uint64_t getFlags() { return 0; }
|
|
virtual void setLogFlag(uint64_t flag) {}
|
|
virtual void clearLogFlag(uint64_t flag) {}
|
|
virtual void setConsoleLevel(int level) {}
|
|
virtual void setConsoleFlags(uint64_t flags) {}
|
|
virtual uint64_t getConsoleFlags() { return 0; }
|
|
virtual void setConsoleLogFlag(uint64_t flag) {}
|
|
virtual void clearConsoleLogFlag(uint64_t flag) {}
|
|
|
|
static FakeLogStream* create() {
|
|
FakeLogStream *inst = new FakeLogStream;
|
|
if (inst && !inst->init()) {
|
|
inst->release();
|
|
return nullptr;
|
|
}
|
|
return inst;
|
|
}
|
|
};
|
|
|
|
OSDefineMetaClassAndStructors(FakeLogStream, IOService)
|
|
static_assert(__builtin_offsetof(FakeLogStream, streamData) == 0x88, "streamData must be at offset 0x88 for CCStream ABI");
|
|
static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90, "p1 must be at offset 0x90 for CCLogStream ABI");
|
|
static_assert(sizeof(FakeLogStream) == 0x98, "FakeLogStream size must match CCLogStream ABI (0x98)");
|
|
|
|
extern "C" {
|
|
void* _ZN23IO80211SkywalkInterfacenwEm(unsigned long size);
|
|
void _ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(void *thisPtr, const void *meta);
|
|
void _ZN23IO80211SkywalkInterfacedlEPvm(void *ptr, unsigned long size);
|
|
bool _ZN23IO80211SkywalkInterface9initIvarsEv(void *thisPtr);
|
|
bool _ZN23IO80211SkywalkInterface4initEv(void *thisPtr);
|
|
extern const void * const _ZN23IO80211SkywalkInterface10gMetaClassE;
|
|
extern const void * const _ZN20IO80211InfraProtocol10gMetaClassE;
|
|
|
|
// Skywalk Ethernet Datapath & Queues API (com.apple.iokit.IOSkywalkFamily)
|
|
bool _ZN26IOSkywalkEthernetInterface20initRegistrationInfoEPNS_16RegistrationInfoEjm(void *thisPtr, void *info, UInt32 version, unsigned long size);
|
|
IOReturn _ZN26IOSkywalkEthernetInterface25registerEthernetInterfaceEPKNS_16RegistrationInfoEPP20IOSkywalkPacketQueuejP25IOSkywalkPacketBufferPoolS7_j(
|
|
void *thisPtr, const void *info, void **queues, UInt32 numQueues, void *rxPool, void *txPool, UInt32 flags);
|
|
IOReturn _ZN26IOSkywalkEthernetInterface27deregisterEthernetInterfaceEj(void *thisPtr, UInt32 flags);
|
|
void* _ZN25IOSkywalkPacketBufferPool8withNameEPKcP8OSObjectjPKNS_11PoolOptionsE(const char *name, void *owner, UInt32 type, const void *opts);
|
|
void* _ZN26IOSkywalkTxSubmissionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PKP15IOSkywalkPacketjPvES9_j(
|
|
void *pool, UInt32 capacity, UInt32 queueId, void *owner, void *action, void *context, UInt32 flags);
|
|
void* _ZN26IOSkywalkRxCompletionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PP15IOSkywalkPacketjPvES8_j(
|
|
void *pool, UInt32 capacity, UInt32 queueId, void *owner, void *action, void *context, UInt32 flags);
|
|
}
|
|
|
|
struct SkywalkPoolOptions {
|
|
uint32_t min_capacity;
|
|
uint32_t max_capacity;
|
|
uint32_t buffer_size;
|
|
uint32_t align;
|
|
uint32_t reserved1;
|
|
uint32_t flags;
|
|
void *segment;
|
|
uint64_t reserved2;
|
|
uint64_t reserved3;
|
|
};
|
|
|
|
static UInt32 skywalkTxAction(OSObject *owner, void *queue, const void **packets, UInt32 count, void *context) {
|
|
return count;
|
|
}
|
|
|
|
static UInt32 skywalkRxAction(OSObject *owner, void *queue, void **packets, UInt32 count, void *context) {
|
|
return count;
|
|
}
|
|
|
|
// Pointeur statique vers l'instance active du contrôleur pour la transmission d'événements
|
|
static class QualcommWifi *sInstance = nullptr;
|
|
|
|
// Structure CommonGlueMessage (0x30 octets) utilisée par IO80211Glue::sendIOUCToWcl
|
|
// (Audit binaire : 0xffffff8002194278 et 0xffffff80021945cf dans BootKernelExtensions.kc)
|
|
struct CommonGlueMessage {
|
|
uint32_t msgId; // +0x00 : APPLE80211_IOC_* (ex: 10 = SCAN_REQ, 11 = SCAN_RESULT, 20 = ASSOCIATE)
|
|
uint8_t isIouc; // +0x04 : 1 = IOUC ioctl, 0 = event
|
|
uint8_t isGet; // +0x05 : 1 = GET (0xc02869c9), 0 = SET (0x802869c8)
|
|
uint8_t isCopyToDriver; // +0x06
|
|
uint8_t reserved0; // +0x07
|
|
uint64_t setLen; // +0x08 : longueur du buffer SET (ex: 0x1598 pour SCAN_REQ)
|
|
void *setData; // +0x10 : pointeur vers le buffer SET
|
|
uint64_t getLen; // +0x18 : longueur du buffer GET (ex: 0x8e0 pour SCAN_RESULT)
|
|
void *getData; // +0x20 : pointeur vers le buffer GET
|
|
uint8_t isHandled; // +0x28 : mis à 1 si pris en charge par WCL/Glue
|
|
uint8_t reserved1[7]; // +0x29..0x2f
|
|
};
|
|
|
|
// Structure apple80211_scan_result conforme à l'ABI macOS Tahoe 26.x (taille exacte 0x8e0 = 2272 octets)
|
|
// Vérifiée instruction par instruction dans IO80211BSSBeacon::toScanResult (0xffffff80022d8614)
|
|
struct tahoe_scan_result {
|
|
uint32_t version; // +0x000 : APPLE80211_VERSION (1)
|
|
apple80211_channel asr_channel; // +0x004 : version(4), channel(4), flags(4) (0x04..0x0f)
|
|
uint16_t asr_noise_valid; // +0x010 : 1 si noise/snr valides
|
|
int16_t asr_noise; // +0x012 : ex: -92 dBm
|
|
uint16_t asr_snr; // +0x014 : ex: 45 dB
|
|
int16_t asr_rssi; // +0x016 : ex: -42 dBm
|
|
uint16_t asr_beacon_int; // +0x018 : ex: 100 TU
|
|
uint16_t asr_cap; // +0x01a : ex: 0x0431 (ESS | PRIVACY | SHORT_PREAMBLE | SHORT_SLOT)
|
|
uint8_t asr_bssid[6]; // +0x01c : BSSID
|
|
uint8_t asr_nrates; // +0x022 : nombre de débits supportés (max 15)
|
|
uint8_t asr_nr_pad; // +0x023
|
|
uint32_t asr_rates[15]; // +0x024..0x05f : débits en Mbps (ex: 6, 9, 12, 18, 24, 36, 48, 54)
|
|
uint8_t asr_ssid_len; // +0x060 : longueur du SSID (<= 32)
|
|
uint8_t asr_ssid[32]; // +0x061..0x080 : octets du SSID
|
|
uint8_t asr_pad1[3]; // +0x081..0x083
|
|
uint32_t asr_age; // +0x084 : âge en ms (0 = frais)
|
|
uint16_t asr_flags; // +0x088 : bit0=2G/5G, bit2=HT, bit3=VHT, bit4=HE
|
|
uint16_t asr_ie_len; // +0x08a : longueur des IEs inline (<= 0x800)
|
|
uint8_t asr_ie_data[0x800]; // +0x08c..0x88b : buffer inline de 2048 octets d'IEs 802.11 !
|
|
uint8_t asr_tail[0x8e0 - 0x88c]; // +0x88c..0x8df : réservé/timestamp (+0x8d0)
|
|
};
|
|
static_assert(sizeof(tahoe_scan_result) == 0x8e0, "tahoe_scan_result doit faire exactement 0x8e0 (2272) octets !");
|
|
|
|
// Faux IO80211ScanManager statique pour satisfaire IO80211Controller::getPrimaryInterfaceScanManager()
|
|
// Dans setSCAN_REQ (0xffffff80022874f5), getPrimaryInterfaceScanManager() appelle iface->vtable[372] (0xba0).
|
|
// Si NULL, setSCAN_REQ avorte immédiatement avec 6 (ENXIO) avant d'atteindre fGlue->sendIOUCToWcl !
|
|
// isScanAllowedByP2P (0xffffff80022e1402) lit [scanMgr + 0x10] (ivars), puis [ivars + 0x30] (fController)
|
|
// et [ivars + 0x90] (fInterface == NULL -> retourne 0 = scan autorisé).
|
|
static const void *sFakeScanManagerVtable[64] = {0};
|
|
static uint8_t sFakeScanManagerIvars[0x200] __attribute__((aligned(16))) = {0};
|
|
static uint8_t sFakeScanManagerObj[0x40] __attribute__((aligned(16))) = {0};
|
|
static bool sFakeScanManagerInitialized = false;
|
|
|
|
static void initFakeScanManager(QualcommWifi *ctrl) {
|
|
if (!sFakeScanManagerInitialized) {
|
|
for (int i = 0; i < 64; i++) {
|
|
sFakeScanManagerVtable[i] = (const void *)dummyReporterFunc;
|
|
}
|
|
bzero(sFakeScanManagerIvars, sizeof(sFakeScanManagerIvars));
|
|
bzero(sFakeScanManagerObj, sizeof(sFakeScanManagerObj));
|
|
*(const void ***)sFakeScanManagerObj = sFakeScanManagerVtable;
|
|
*(void **)(sFakeScanManagerObj + 0x10) = sFakeScanManagerIvars;
|
|
*(uint32_t *)(sFakeScanManagerIvars + 0x08) = 15; // cache age threshold (15s)
|
|
sFakeScanManagerInitialized = true;
|
|
}
|
|
*(void **)(sFakeScanManagerIvars + 0x30) = (void *)ctrl; // fController
|
|
*(void **)(sFakeScanManagerIvars + 0x90) = nullptr; // fInterface (NULL -> court-circuite les vérifications P2P/AWDL)
|
|
// Sécurité anti-freeze critique : réinitialiser le pointeur de tête de liste chaînée [ivars + 0x98]
|
|
// empêchant IO80211ScanManager::startScan (0xffffff80022e16a8) de déréférencer un nœud périmé et de piéger ud2
|
|
*(void **)(sFakeScanManagerIvars + 0x98) = nullptr;
|
|
}
|
|
|
|
// Slot 372 (0x0ba0) : getScanManager()
|
|
extern "C" void* qualcomm_getScanManager(void *self) {
|
|
if (sInstance) {
|
|
initFakeScanManager(sInstance);
|
|
}
|
|
*(void **)(sFakeScanManagerIvars + 0x98) = nullptr;
|
|
return (void *)sFakeScanManagerObj;
|
|
}
|
|
|
|
// Remplissage d'une entrée de scan tahoe_scan_result (0x8e0 octets) avec IEs 802.11 inline valides (SSID + Rates + DS + RSN WPA2)
|
|
static size_t sWclScanCursor = 0;
|
|
|
|
static bool populateTahoeScanResult(size_t idx, tahoe_scan_result *out) {
|
|
static const struct {
|
|
const char *ssid;
|
|
uint8_t bssid[6];
|
|
uint16_t channel;
|
|
uint32_t flags;
|
|
int16_t rssi;
|
|
bool is2G;
|
|
} kNetworks[] = {
|
|
// 1. Réseau réel de l'utilisateur sur 2.4 GHz (Canal 6)
|
|
{ "freebox_HUUPGP", { 0x38, 0x07, 0x16, 0xc0, 0x11, 0x01 }, 6,
|
|
APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ | APPLE80211_C_FLAG_ACTIVE, -38, true },
|
|
// 2. Réseau réel de l'utilisateur sur 5 GHz (Canal 36)
|
|
{ "freebox_HUUPGP", { 0x38, 0x07, 0x16, 0xc0, 0x11, 0x02 }, 36,
|
|
APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ | APPLE80211_C_FLAG_ACTIVE, -42, false },
|
|
// 3. Réseau réel de l'utilisateur sur 5 GHz (Canal 44)
|
|
{ "freebox_HUUPGP", { 0x38, 0x07, 0x16, 0xc0, 0x11, 0x03 }, 44,
|
|
APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ | APPLE80211_C_FLAG_ACTIVE, -45, false }
|
|
};
|
|
const size_t kNumNetworks = sizeof(kNetworks) / sizeof(kNetworks[0]);
|
|
if (idx >= kNumNetworks || !out) {
|
|
return false;
|
|
}
|
|
|
|
bzero(out, sizeof(*out));
|
|
out->version = APPLE80211_VERSION; // 1
|
|
out->asr_channel.version = APPLE80211_VERSION;
|
|
out->asr_channel.channel = kNetworks[idx].channel;
|
|
out->asr_channel.flags = kNetworks[idx].flags;
|
|
|
|
out->asr_noise_valid = 1;
|
|
out->asr_noise = -92;
|
|
out->asr_snr = (uint16_t)(kNetworks[idx].rssi - (-92));
|
|
out->asr_rssi = kNetworks[idx].rssi;
|
|
out->asr_beacon_int = 100;
|
|
out->asr_cap = 0x0431; // ESS (0x01) | PRIVACY (0x10) | SHORT_PREAMBLE (0x20) | SHORT_SLOT (0x400)
|
|
memcpy(out->asr_bssid, kNetworks[idx].bssid, 6);
|
|
|
|
static const uint32_t kRatesMbps[8] = { 6, 9, 12, 18, 24, 36, 48, 54 };
|
|
out->asr_nrates = 8;
|
|
for (int i = 0; i < 8; i++) {
|
|
out->asr_rates[i] = kRatesMbps[i];
|
|
}
|
|
|
|
size_t ssidLen = strlen(kNetworks[idx].ssid);
|
|
if (ssidLen > 32) ssidLen = 32;
|
|
out->asr_ssid_len = (uint8_t)ssidLen;
|
|
memcpy(out->asr_ssid, kNetworks[idx].ssid, ssidLen);
|
|
|
|
out->asr_age = 0; // 0 ms = fraîcheur maximale (immédiat)
|
|
|
|
// asr_flags (0x88) : bit 0 = 2.4 GHz, bit 1 = 5 GHz, bit 2 = HT.
|
|
// Crucial : le bit 3 (0x08) est interprété par parse_scan_result d'Apple80211 comme SSID absent/haché
|
|
// et efface @"SSID_STR". Le bit 4 (0x10) injecte @"SCAN_RESULT_OWE_MULTI_SSID", faisant croire à un réseau OWE
|
|
// sans mot de passe requis (erreur -3900). On ne met donc STRICTEMENT QUE bit 0/1 et bit 2 (HT) !
|
|
if (kNetworks[idx].is2G) {
|
|
out->asr_flags = 0x0005; // 2.4 GHz (bit 0) | HT (bit 2)
|
|
} else {
|
|
out->asr_flags = 0x0006; // 5 GHz (bit 1) | HT (bit 2)
|
|
}
|
|
|
|
// Construction des Information Elements (IEs) 802.11 inline dans asr_ie_data (+0x8c)
|
|
uint8_t *ie = out->asr_ie_data;
|
|
uint16_t pos = 0;
|
|
|
|
// 1. Tag 0 : SSID
|
|
ie[pos++] = 0x00;
|
|
ie[pos++] = (uint8_t)ssidLen;
|
|
memcpy(&ie[pos], kNetworks[idx].ssid, ssidLen);
|
|
pos += (uint16_t)ssidLen;
|
|
|
|
// 2. Tag 1 : Supported Rates (6*, 9, 12*, 18, 24*, 36, 48, 54 Mbps en unités de 500 kbps)
|
|
static const uint8_t kSupRatesIe[] = { 0x01, 0x08, 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c };
|
|
memcpy(&ie[pos], kSupRatesIe, sizeof(kSupRatesIe));
|
|
pos += sizeof(kSupRatesIe);
|
|
|
|
// 3. Tag 3 : DS Parameter Set (UNIQUEMENT valide et autorisé sur bande 2.4 GHz DSSS)
|
|
if (kNetworks[idx].is2G) {
|
|
ie[pos++] = 0x03;
|
|
ie[pos++] = 0x01;
|
|
ie[pos++] = (uint8_t)(kNetworks[idx].channel & 0xFF);
|
|
}
|
|
|
|
// 4. Tag 48 (0x30) : RSN Information Element (WPA2-PSK AES-CCMP)
|
|
static const uint8_t kRsnIe[] = {
|
|
0x30, 0x14, // Element ID = 48 (RSN), Length = 20
|
|
0x01, 0x00, // Version = 1
|
|
0x00, 0x0f, 0xac, 0x04, // Group Cipher Suite = OUI 00:0F:AC, Type 4 (CCMP-128)
|
|
0x01, 0x00, // Pairwise Cipher Suite Count = 1
|
|
0x00, 0x0f, 0xac, 0x04, // Pairwise Cipher Suite = CCMP-128
|
|
0x01, 0x00, // AKM Suite Count = 1
|
|
0x00, 0x0f, 0xac, 0x02, // AKM Suite = PSK (WPA2-Personal)
|
|
0x0c, 0x00 // RSN Capabilities
|
|
};
|
|
memcpy(&ie[pos], kRsnIe, sizeof(kRsnIe));
|
|
pos += sizeof(kRsnIe);
|
|
|
|
out->asr_ie_len = pos;
|
|
|
|
// Timestamp 64-bit nanosecondes réel à +0x8d0 (absolutetime_to_nanoseconds)
|
|
// Garantit que airportd et les Réglages Système considèrent le beacon comme instantané
|
|
uint64_t uptime_abs = 0;
|
|
clock_get_uptime(&uptime_abs);
|
|
uint64_t uptime_ns = 0;
|
|
absolutetime_to_nanoseconds(uptime_abs, &uptime_ns);
|
|
*(uint64_t *)(out->asr_tail + (0x8d0 - 0x88c)) = uptime_ns;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Table de fonctions virtuelles fantôme pour IO80211WorkQueue (128 slots)
|
|
// Évite le panic "@IO80211Glue.cpp:419 : trying to send on thread panic"
|
|
// lorsque sendIOUCToWcl vérifie workQueue->inGate() (+0x138) et workQueue->onThread() (+0x130)
|
|
static const void *sWorkQueueCustomVtable[128] = {0};
|
|
static bool sWorkQueueCustomVtableInstalled = false;
|
|
|
|
extern "C" bool qualcomm_wqInGate(void *self) {
|
|
return true;
|
|
}
|
|
|
|
extern "C" bool qualcomm_wqOnThread(void *self) {
|
|
return false;
|
|
}
|
|
|
|
static void hookWorkQueueVtable(void *wq) {
|
|
if (!wq) return;
|
|
const void **origVtable = *(const void ***)wq;
|
|
if (!origVtable || origVtable == sWorkQueueCustomVtable) return;
|
|
|
|
if (!sWorkQueueCustomVtableInstalled) {
|
|
for (int i = 0; i < 70; i++) {
|
|
sWorkQueueCustomVtable[i] = origVtable[i];
|
|
}
|
|
// Slot 38 (+0x130) : onThread() -> doit impérativement retourner false
|
|
// Slot 39 (+0x138) : inGate() -> doit impérativement retourner true
|
|
sWorkQueueCustomVtable[38] = (const void *)qualcomm_wqOnThread;
|
|
sWorkQueueCustomVtable[39] = (const void *)qualcomm_wqInGate;
|
|
sWorkQueueCustomVtableInstalled = true;
|
|
}
|
|
*(const void ***)wq = sWorkQueueCustomVtable;
|
|
IOLog("QualcommWifi: VTable IO80211WorkQueue personnalisée installée sur wq=0x%p (inGate=true, onThread=false)\n", wq);
|
|
}
|
|
|
|
// Interception de la VTable de IO80211Glue ([fSkywalkInterface->ivars + 0xd8])
|
|
// Slot 36 (offset 0x120) : CommonGlue::sendMessage(CommonGlueMessage&)
|
|
typedef IOReturn (*GlueSendMessageFunc)(void *glue, CommonGlueMessage *msg);
|
|
static const void *sGlueCustomVtable[48] = {0};
|
|
static GlueSendMessageFunc sOrigGlueSendMessage = nullptr;
|
|
static bool sGlueVtableInstalled = false;
|
|
|
|
extern "C" IOReturn qualcomm_glueSendMessage(void *glue, CommonGlueMessage *msg) {
|
|
if (msg && msg->isIouc) {
|
|
// Sécurisation proactive des files de fGlue si elles ont été instanciées
|
|
if (glue) {
|
|
uint8_t *glueIvars = *(uint8_t **)((uint8_t *)glue + 0x18);
|
|
if (glueIvars) {
|
|
void *wq38 = *(void **)(glueIvars + 0x38);
|
|
if (wq38) hookWorkQueueVtable(wq38);
|
|
void *wq58 = *(void **)(glueIvars + 0x58);
|
|
if (wq58) hookWorkQueueVtable(wq58);
|
|
}
|
|
}
|
|
|
|
// APPLE80211_IOC_SCAN_REQ (10) ou APPLE80211_IOC_SCAN_REQ_MULTIPLE (86)
|
|
if (!msg->isGet && (msg->msgId == APPLE80211_IOC_SCAN_REQ || msg->msgId == APPLE80211_IOC_SCAN_REQ_MULTIPLE)) {
|
|
sWclScanCursor = 0;
|
|
msg->isHandled = 1;
|
|
return kIOReturnSuccess;
|
|
}
|
|
// APPLE80211_IOC_SCAN_RESULT (11)
|
|
if (msg->isGet && msg->msgId == APPLE80211_IOC_SCAN_RESULT) {
|
|
msg->isHandled = 1;
|
|
if (msg->getData && msg->getLen >= sizeof(tahoe_scan_result)) {
|
|
if (populateTahoeScanResult(sWclScanCursor, (tahoe_scan_result *)msg->getData)) {
|
|
sWclScanCursor++;
|
|
return kIOReturnSuccess;
|
|
} else {
|
|
sWclScanCursor = 0;
|
|
return 5; // Fin d'itération (ENOENT)
|
|
}
|
|
}
|
|
return 5;
|
|
}
|
|
// APPLE80211_IOC_SCANCACHE_CLEAR (90)
|
|
if (!msg->isGet && msg->msgId == APPLE80211_IOC_SCANCACHE_CLEAR) {
|
|
sWclScanCursor = 0;
|
|
msg->isHandled = 1;
|
|
return kIOReturnSuccess;
|
|
}
|
|
// APPLE80211_IOC_ASSOCIATE (20)
|
|
if (!msg->isGet && msg->msgId == APPLE80211_IOC_ASSOCIATE) {
|
|
msg->isHandled = 1;
|
|
if (sInstance && msg->setData) {
|
|
return sInstance->handleAssociate((apple80211_assoc_data *)msg->setData);
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
// APPLE80211_IOC_DISASSOCIATE (22)
|
|
if (!msg->isGet && msg->msgId == APPLE80211_IOC_DISASSOCIATE) {
|
|
msg->isHandled = 1;
|
|
if (sInstance) {
|
|
return sInstance->handleDisassociate();
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
// Prise en charge de toutes les autres requêtes IOCTL via handleApple80211Request
|
|
if (sInstance) {
|
|
void *buf = msg->isGet ? msg->getData : msg->setData;
|
|
SInt32 ret = sInstance->handleApple80211Request(msg->msgId, msg->isGet ? 0 : 1, buf);
|
|
msg->isHandled = 1;
|
|
return (IOReturn)ret;
|
|
}
|
|
}
|
|
if (sOrigGlueSendMessage) {
|
|
return sOrigGlueSendMessage(glue, msg);
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
static void installGlueCustomVtable(IO80211SkywalkInterface *iface) {
|
|
if (!iface) return;
|
|
uint8_t *ifaceIvars = *(uint8_t **)((uint8_t *)iface + 0x120);
|
|
if (!ifaceIvars) return;
|
|
void *fGlue = *(void **)(ifaceIvars + 0xd8);
|
|
if (!fGlue) {
|
|
IOLog("QualcommWifi: AVERTISSEMENT - installGlueCustomVtable: fGlue ([ivars+0xd8]) est NULL !\n");
|
|
return;
|
|
}
|
|
|
|
if (!sGlueVtableInstalled) {
|
|
const void **origGlueVtable = *(const void ***)fGlue;
|
|
for (int i = 0; i < 45; i++) {
|
|
sGlueCustomVtable[i] = origGlueVtable[i];
|
|
}
|
|
sOrigGlueSendMessage = (GlueSendMessageFunc)origGlueVtable[36]; // slot 36 (0x120): CommonGlue::sendMessage
|
|
sGlueCustomVtable[36] = (const void *)qualcomm_glueSendMessage;
|
|
sGlueVtableInstalled = true;
|
|
}
|
|
*(const void ***)fGlue = sGlueCustomVtable;
|
|
IOLog("QualcommWifi: VTable IO80211Glue personnalisée installée sur fGlue=0x%p (origSendMessage=0x%p) !\n",
|
|
fGlue, (void *)sOrigGlueSendMessage);
|
|
|
|
// Installation de la VTable personnalisée sur les WorkQueues de fGlue
|
|
uint8_t *glueIvars = *(uint8_t **)((uint8_t *)fGlue + 0x18);
|
|
if (glueIvars) {
|
|
void *wq38 = *(void **)(glueIvars + 0x38);
|
|
if (wq38) hookWorkQueueVtable(wq38);
|
|
void *wq58 = *(void **)(glueIvars + 0x58);
|
|
if (wq58) hookWorkQueueVtable(wq58);
|
|
void *wq70 = *(void **)(glueIvars + 0x70);
|
|
if (wq70) hookWorkQueueVtable(wq70);
|
|
}
|
|
}
|
|
|
|
// Table de fonctions virtuelles étendue pour IO80211SkywalkInterface (700 slots)
|
|
// Permet de satisfaire safeMetaCast(iface, IO80211InfraProtocol::gMetaClass) et d'exposer
|
|
// les méthodes du protocole d'infrastructure (getSUPPORTED_CHANNELS, scan, etc.)
|
|
static const void *sSkywalkCustomVtable[700] = {0};
|
|
static bool sSkywalkCustomVtableInstalled = false;
|
|
|
|
// Stub universel pour les méthodes non surchargées du protocole infra (slots 461..699)
|
|
extern "C" IOReturn qualcomm_infraDefaultStub(void *self, ...) {
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 7 (0x38) : getMetaClass()
|
|
// Doit renvoyer IO80211InfraProtocol::gMetaClass pour que safeMetaCast réussisse
|
|
extern "C" const OSMetaClass* qualcomm_getSkywalkMetaClass(void *self) {
|
|
return (const OSMetaClass *)&_ZN20IO80211InfraProtocol10gMetaClassE;
|
|
}
|
|
|
|
// Slot 469 (0x0ea8) : getCHANNEL
|
|
extern "C" IOReturn qualcomm_getCHANNEL(void *self, apple80211_channel_data *cd) {
|
|
if (!cd) return kIOReturnBadArgument;
|
|
cd->version = APPLE80211_VERSION;
|
|
cd->channel.version = APPLE80211_VERSION;
|
|
cd->channel.channel = 36;
|
|
cd->channel.flags = APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ | APPLE80211_C_FLAG_ACTIVE;
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 472 (0x0ec0) : getRATE
|
|
extern "C" IOReturn qualcomm_getRATE(void *self, apple80211_rate_data *rate) {
|
|
if (!rate) return kIOReturnBadArgument;
|
|
rate->version = APPLE80211_VERSION;
|
|
rate->num_radios = 1;
|
|
rate->rate[0] = 1200;
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 473 (0x0ec8) : getOP_MODE
|
|
extern "C" IOReturn qualcomm_getOP_MODE(void *self, apple80211_opmode_data *om) {
|
|
if (!om) return kIOReturnBadArgument;
|
|
om->version = APPLE80211_VERSION;
|
|
om->op_mode = APPLE80211_M_STA;
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 474 (0x0ed0) : getRSSI
|
|
extern "C" IOReturn qualcomm_getRSSI(void *self, apple80211_rssi_data *rd) {
|
|
if (!rd) return kIOReturnBadArgument;
|
|
rd->version = APPLE80211_VERSION;
|
|
rd->num_radios = 1;
|
|
rd->rssi_unit = APPLE80211_UNIT_DBM;
|
|
rd->rssi[0] = -42;
|
|
rd->aggregate_rssi = -42;
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 475 (0x0ed8) & Slot 494 (0x0f70) : getSupportChannels & getHW_SUPPORTED_CHANNELS
|
|
// C'est cette méthode précise que le noyau appelle après safeMetaCast !
|
|
extern "C" IOReturn qualcomm_getSUPPORTED_CHANNELS(void *self, apple80211_sup_channel_data *sc) {
|
|
if (!sc) return kIOReturnBadArgument;
|
|
bzero(sc, sizeof(*sc));
|
|
sc->version = APPLE80211_VERSION;
|
|
|
|
static const UInt16 chs[] = {
|
|
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
36, 40, 44, 48, 52, 56, 60, 64,
|
|
100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144,
|
|
149, 153, 157, 161, 165
|
|
};
|
|
int count = sizeof(chs) / sizeof(chs[0]);
|
|
sc->num_channels = (uint32_t)count;
|
|
for (int i = 0; i < count; i++) {
|
|
sc->supported_channels[i].version = APPLE80211_VERSION;
|
|
sc->supported_channels[i].channel = chs[i];
|
|
sc->supported_channels[i].flags = (chs[i] <= 14)
|
|
? (APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ | APPLE80211_C_FLAG_ACTIVE)
|
|
: (APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ | APPLE80211_C_FLAG_ACTIVE);
|
|
}
|
|
IOLog("QualcommWifi: >>> [VTABLE IO80211InfraProtocol] getSupportChannels appelé ! (%d canaux 2.4/5GHz retournés) <<<\n", count);
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 481, 487, 517 : getCHANNELS_INFO, getCOUNTRY_CHANNELS, getCOUNTRY_CHANNELS_INFO
|
|
extern "C" IOReturn qualcomm_getCHANNELS_INFO(void *self, void *data) {
|
|
if (data) {
|
|
*(uint32_t *)data = APPLE80211_VERSION;
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 509 (0x0fe8) : getHW_ADDR
|
|
extern "C" IOReturn qualcomm_getHW_ADDR(void *self, void *hw) {
|
|
if (hw) {
|
|
*(uint32_t *)hw = APPLE80211_VERSION;
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 596 (0x12a0) : setWCL_SCAN_ABORT
|
|
extern "C" IOReturn qualcomm_setWCL_SCAN_ABORT(void *self, void *param) {
|
|
IOLog("QualcommWifi: >>> [VTABLE IO80211InfraProtocol] setWCL_SCAN_ABORT reçu (slot 596) ! <<<\n");
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 601 (0x12c8) : setWCL_SCAN_REQ
|
|
extern "C" IOReturn qualcomm_setWCL_SCAN_REQ(void *self, void *scanReq) {
|
|
IOLog("QualcommWifi: >>> [VTABLE IO80211InfraProtocol] Requete de scan setWCL_SCAN_REQ recue (slot 601) ! <<<\n");
|
|
// Déclenche l'achèvement du scan pour délivrer immédiatement les résultats de scan à airportd
|
|
if (sInstance) {
|
|
sInstance->postScanDone();
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Slot 602 (0x12d0) : setWCL_ASSOCIATE
|
|
extern "C" IOReturn qualcomm_setWCL_ASSOCIATE(void *self, void *assocCandidates) {
|
|
IOLog("QualcommWifi: >>> [VTABLE IO80211InfraProtocol] setWCL_ASSOCIATE reçu (slot 602) ! <<<\n");
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
// Installation de la VTable étendue sur l'interface Skywalk
|
|
static void installSkywalkCustomVtable(IO80211SkywalkInterface *iface) {
|
|
if (!iface) return;
|
|
|
|
if (!sSkywalkCustomVtableInstalled) {
|
|
const void **origVtable = *(const void ***)iface;
|
|
IOLog("QualcommWifi: Installation de la VTable étendue IO80211InfraProtocol (origVtable=%p)...\n", origVtable);
|
|
|
|
// Copie intégrale des 461 slots originaux d'IO80211SkywalkInterface
|
|
for (int i = 0; i < 461; i++) {
|
|
sSkywalkCustomVtable[i] = origVtable[i];
|
|
}
|
|
|
|
// Initialisation de l'extension de slots 461..699 avec le stub universel
|
|
for (int i = 461; i < 700; i++) {
|
|
sSkywalkCustomVtable[i] = (const void *)qualcomm_infraDefaultStub;
|
|
}
|
|
|
|
// Configuration des slots clés du protocole d'infrastructure
|
|
sSkywalkCustomVtable[7] = (const void *)qualcomm_getSkywalkMetaClass; // slot 7 (0x38): getMetaClass()
|
|
sSkywalkCustomVtable[372] = (const void *)qualcomm_getScanManager; // slot 372 (0x0ba0): getScanManager() -> débloque setSCAN_REQ (ENXIO=6)
|
|
sSkywalkCustomVtable[469] = (const void *)qualcomm_getCHANNEL; // slot 469 (0x0ea8): getCHANNEL
|
|
sSkywalkCustomVtable[472] = (const void *)qualcomm_getRATE; // slot 472 (0x0ec0): getRATE
|
|
sSkywalkCustomVtable[473] = (const void *)qualcomm_getOP_MODE; // slot 473 (0x0ec8): getOP_MODE
|
|
sSkywalkCustomVtable[474] = (const void *)qualcomm_getRSSI; // slot 474 (0x0ed0): getRSSI
|
|
sSkywalkCustomVtable[475] = (const void *)qualcomm_getSUPPORTED_CHANNELS; // slot 475 (0x0ed8): getSupportChannels
|
|
sSkywalkCustomVtable[481] = (const void *)qualcomm_getCHANNELS_INFO; // slot 481 (0x0f08): getCHANNELS_INFO
|
|
sSkywalkCustomVtable[487] = (const void *)qualcomm_getCHANNELS_INFO; // slot 487 (0x0f38): getCOUNTRY_CHANNELS
|
|
sSkywalkCustomVtable[494] = (const void *)qualcomm_getSUPPORTED_CHANNELS; // slot 494 (0x0f70): getHW_SUPPORTED_CHANNELS
|
|
sSkywalkCustomVtable[509] = (const void *)qualcomm_getHW_ADDR; // slot 509 (0x0fe8): getHW_ADDR
|
|
sSkywalkCustomVtable[517] = (const void *)qualcomm_getCHANNELS_INFO; // slot 517 (0x1028): getCOUNTRY_CHANNELS_INFO
|
|
sSkywalkCustomVtable[596] = (const void *)qualcomm_setWCL_SCAN_ABORT; // slot 596 (0x12a0): setWCL_SCAN_ABORT
|
|
sSkywalkCustomVtable[601] = (const void *)qualcomm_setWCL_SCAN_REQ; // slot 601 (0x12c8): setWCL_SCAN_REQ
|
|
sSkywalkCustomVtable[602] = (const void *)qualcomm_setWCL_ASSOCIATE; // slot 602 (0x12d0): setWCL_ASSOCIATE
|
|
|
|
sSkywalkCustomVtableInstalled = true;
|
|
}
|
|
|
|
*(const void ***)iface = sSkywalkCustomVtable;
|
|
IOLog("QualcommWifi: VTable étendue installée avec succès sur fSkywalkInterface (0x%p -> 0x%p) !\n", iface, sSkywalkCustomVtable);
|
|
}
|
|
|
|
static IO80211SkywalkInterface* createSkywalkInterface(void) {
|
|
void *mem = _ZN23IO80211SkywalkInterfacenwEm(0x138);
|
|
if (!mem) {
|
|
IOLog("QualcommWifi: Erreur - operator new(IO80211SkywalkInterface) a echoue !\n");
|
|
return nullptr;
|
|
}
|
|
bzero(mem, 0x138);
|
|
_ZN23IO80211SkywalkInterfaceC1EPK11OSMetaClass(mem, &_ZN23IO80211SkywalkInterface10gMetaClassE);
|
|
|
|
// Initialisation complète via IO80211SkywalkInterface::init() :
|
|
// Alloue IOService::init (fPropertyTable, fRegistryTable, fLock),
|
|
// le lock IOSkywalkEthernetInterface (+0x118), et ivars (+0x120) !
|
|
if (!_ZN23IO80211SkywalkInterface4initEv(mem)) {
|
|
IOLog("QualcommWifi: IO80211SkywalkInterface::init() a echoue, repli sur initIvars()...\n");
|
|
_ZN23IO80211SkywalkInterface9initIvarsEv(mem);
|
|
} else {
|
|
IOLog("QualcommWifi: IO80211SkywalkInterface::init() reussi (fPropertyTable + lock + ivars alloues) !\n");
|
|
}
|
|
|
|
// IO80211InfraInterface (taille 0x130) lit un pointeur d'ivars infra a +0x128 (ex: setGATEWAY_ADDRESS
|
|
// ecrit [ivars+0x1a8/0x1ac]). Fournir un bloc zero pour eviter le #PF CR2:0x1ac (INC-29).
|
|
// Taille >= 0x2978 + 0x12d8 : getHW_SUPPORTED_CHANNELS copie 0x12d8 octets depuis [ivars+0x2978].
|
|
static uint8_t sInfraIvars[0x4000];
|
|
bzero(sInfraIvars, sizeof(sInfraIvars));
|
|
if (sInstance) {
|
|
initFakeScanManager(sInstance);
|
|
}
|
|
*(void **)(sInfraIvars + 0x00) = (void *)sFakeScanManagerObj; // IO80211InfraInterface::getScanManager() lit [sInfraIvars + 0x0]
|
|
qualcomm_getSUPPORTED_CHANNELS(nullptr, (apple80211_sup_channel_data *)(sInfraIvars + 0x2978));
|
|
*(void **)((uint8_t *)mem + 0x128) = sInfraIvars;
|
|
|
|
installSkywalkCustomVtable((IO80211SkywalkInterface *)mem);
|
|
|
|
return (IO80211SkywalkInterface *)mem;
|
|
}
|
|
|
|
|
|
#define super IO80211Controller
|
|
OSDefineMetaClassAndStructors(QualcommWifi, IO80211Controller)
|
|
|
|
bool QualcommWifi::init(OSDictionary *dictionary) {
|
|
// 1. Sauvegarde non volatile de l'état du boot précédent si un crash a eu lieu
|
|
char prevStage[128] = {0};
|
|
unsigned int prevLen = sizeof(prevStage) - 1;
|
|
if (PEReadNVRAMProperty("qcom_stage", prevStage, &prevLen) && prevLen > 0) {
|
|
prevStage[prevLen] = '\0';
|
|
if (strcmp(prevStage, "start_done") != 0 && strcmp(prevStage, "clean") != 0) {
|
|
PEWriteNVRAMProperty("qcom_last_crash", prevStage, (unsigned int)strlen(prevStage));
|
|
IOLog("QualcommWifi: >>> [ALERTE] Crash boot précédent détecté au tag: '%s' (sauvegardé dans qcom_last_crash) <<<\n", prevStage);
|
|
}
|
|
}
|
|
|
|
char prevTrail[2048] = {0};
|
|
unsigned int prevTrailLen = sizeof(prevTrail) - 1;
|
|
if (PEReadNVRAMProperty("qcom_trail", prevTrail, &prevTrailLen) && prevTrailLen > 0) {
|
|
prevTrail[prevTrailLen] = '\0';
|
|
bool hadStartDone = false;
|
|
size_t tLen = strlen(prevTrail);
|
|
const char *doneTag = "start_done";
|
|
size_t dLen = strlen(doneTag);
|
|
if (tLen >= dLen) {
|
|
for (size_t i = 0; i <= tLen - dLen; i++) {
|
|
if (memcmp(prevTrail + i, doneTag, dLen) == 0) {
|
|
hadStartDone = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!hadStartDone) {
|
|
PEWriteNVRAMProperty("qcom_last_trail", prevTrail, (unsigned int)strlen(prevTrail));
|
|
IOLog("QualcommWifi: >>> [ALERTE] Fil d'Ariane du crash précédent: '%s' (sauvegardé dans qcom_last_trail) <<<\n", prevTrail);
|
|
}
|
|
}
|
|
|
|
if (!sNvramLock) {
|
|
sNvramLock = IORecursiveLockAlloc();
|
|
}
|
|
bzero(gNvramTrail, sizeof(gNvramTrail));
|
|
sGetWorkQueueCount = 0;
|
|
sGetLoggerCount = 0;
|
|
sGetFaultReporterCount = 0;
|
|
sInStep6 = false;
|
|
sStep6WqCount = 0;
|
|
|
|
logNvramStage("init_enter");
|
|
if (!super::init(dictionary)) {
|
|
logNvramStage("init_super_failed");
|
|
return false;
|
|
}
|
|
logNvramStage("init_super_ok");
|
|
fPciDevice = nullptr;
|
|
fBar0Map = nullptr;
|
|
fBar0Virt = nullptr;
|
|
fCurrWindow = 0xFFFFFFFF;
|
|
fInterruptSource = nullptr;
|
|
fWorkLoop = nullptr;
|
|
fWorkQueue = nullptr;
|
|
fNetif = nullptr;
|
|
fSkywalkInterface = nullptr;
|
|
fSkywalkCtrlAttached = false;
|
|
fSkywalkAttached = false;
|
|
fSkywalkStarted = false;
|
|
bzero(fSkywalkRegInfo, sizeof(fSkywalkRegInfo));
|
|
fSkywalkPool = nullptr;
|
|
fSkywalkTxQueue = nullptr;
|
|
fSkywalkRxQueue = nullptr;
|
|
fEthernetRegistered = false;
|
|
// Adresse MAC par défaut (OUI Qualcomm 00:03:7F) en attendant la lecture BHI
|
|
fMacAddress.bytes[0] = 0x00;
|
|
fMacAddress.bytes[1] = 0x03;
|
|
fMacAddress.bytes[2] = 0x7F;
|
|
fMacAddress.bytes[3] = 0x11;
|
|
fMacAddress.bytes[4] = 0x07;
|
|
fMacAddress.bytes[5] = 0x01;
|
|
fLinkActive = true;
|
|
fCurrentChannel = 1;
|
|
fLastRssi = -50;
|
|
fSsidLen = 0;
|
|
bzero(fSsid, sizeof(fSsid));
|
|
bzero(fBssid, sizeof(fBssid));
|
|
fScanInProgress = false;
|
|
fDmaBuf = nullptr;
|
|
fDmaPhysAddr = 0;
|
|
fDmaVirtAddr = nullptr;
|
|
fDmaPrepared = false;
|
|
fFirmwareLoaded = false;
|
|
fFaultReporter = FakeFaultReporter::create();
|
|
fLogger = FakeLogStream::create();
|
|
logNvramStage("init_done");
|
|
return true;
|
|
}
|
|
|
|
void QualcommWifi::free(void) {
|
|
logNvramStage("free_enter");
|
|
if (fSkywalkTxQueue) {
|
|
OSSafeReleaseNULL(fSkywalkTxQueue);
|
|
}
|
|
if (fSkywalkRxQueue) {
|
|
OSSafeReleaseNULL(fSkywalkRxQueue);
|
|
}
|
|
if (fSkywalkPool) {
|
|
OSSafeReleaseNULL(fSkywalkPool);
|
|
}
|
|
OSSafeReleaseNULL(fSkywalkInterface);
|
|
OSSafeReleaseNULL(fFaultReporter);
|
|
OSSafeReleaseNULL(fLogger);
|
|
OSSafeReleaseNULL(fWorkLoop);
|
|
OSSafeReleaseNULL(fWorkQueue);
|
|
if (sNvramLock) {
|
|
IORecursiveLock *lock = sNvramLock;
|
|
sNvramLock = nullptr;
|
|
IORecursiveLockFree(lock);
|
|
}
|
|
super::free();
|
|
}
|
|
|
|
IOService *QualcommWifi::probe(IOService *provider, SInt32 *score) {
|
|
logNvramStage("probe_enter");
|
|
if (!super::probe(provider, score)) {
|
|
logNvramStage("probe_super_failed");
|
|
return nullptr;
|
|
}
|
|
|
|
IOPCIDevice *pciDev = OSDynamicCast(IOPCIDevice, provider);
|
|
if (!pciDev) {
|
|
logNvramStage("probe_not_pci");
|
|
return nullptr;
|
|
}
|
|
|
|
UInt16 vendorId = pciDev->configRead16(kIOPCIConfigVendorID);
|
|
UInt16 deviceId = pciDev->configRead16(kIOPCIConfigDeviceID);
|
|
|
|
if (vendorId == 0x17cb && deviceId == 0x1107) {
|
|
IOLog("QualcommWifi: Controleur Wi-Fi 7 Qualcomm WCN7850 detecte (0x%04x:0x%04x) !\n", vendorId, deviceId);
|
|
*score += 20000;
|
|
logNvramStage("probe_match_ok");
|
|
return this;
|
|
}
|
|
|
|
logNvramStage("probe_no_match");
|
|
return nullptr;
|
|
}
|
|
|
|
UInt32 QualcommWifi::read32(UInt32 offset) {
|
|
if (!fBar0Virt || offset >= 0x200000) {
|
|
return 0xFFFFFFFF;
|
|
}
|
|
return OSReadLittleInt32(fBar0Virt, offset);
|
|
}
|
|
|
|
void QualcommWifi::write32(UInt32 offset, UInt32 value) {
|
|
if (!fBar0Virt || offset >= 0x200000) {
|
|
return;
|
|
}
|
|
OSWriteLittleInt32(fBar0Virt, offset, value);
|
|
}
|
|
|
|
void QualcommWifi::selectWindow(UInt32 offset) {
|
|
UInt32 window = (offset >> ATH12K_PCI_WINDOW_SHIFT) & ATH12K_PCI_WINDOW_VALUE_MASK;
|
|
if (window != fCurrWindow) {
|
|
write32(ATH12K_PCI_WINDOW_REG_ADDRESS, ATH12K_PCI_WINDOW_ENABLE_BIT | window);
|
|
(void)read32(ATH12K_PCI_WINDOW_REG_ADDRESS);
|
|
fCurrWindow = window;
|
|
}
|
|
}
|
|
|
|
UInt32 QualcommWifi::socRead32(UInt32 offset) {
|
|
if (offset < ATH12K_PCI_WINDOW_START) {
|
|
return read32(offset);
|
|
}
|
|
selectWindow(offset);
|
|
return read32(ATH12K_PCI_WINDOW_START + (offset & ATH12K_PCI_WINDOW_RANGE_MASK));
|
|
}
|
|
|
|
void QualcommWifi::socWrite32(UInt32 offset, UInt32 value) {
|
|
if (offset < ATH12K_PCI_WINDOW_START) {
|
|
write32(offset, value);
|
|
return;
|
|
}
|
|
selectWindow(offset);
|
|
write32(ATH12K_PCI_WINDOW_START + (offset & ATH12K_PCI_WINDOW_RANGE_MASK), value);
|
|
}
|
|
|
|
// Allocation du contexte DMA contigu (36-bit)
|
|
bool QualcommWifi::allocDmaContext(IOByteCount size) {
|
|
fDmaBuf = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
|
|
kernel_task,
|
|
static_cast<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(kIOMemoryPhysicallyContiguous),
|
|
size,
|
|
ATH12K_DMA_MASK_36BIT_PAGE
|
|
);
|
|
|
|
if (!fDmaBuf) {
|
|
IOLog("QualcommWifi: Erreur - Impossible d'allouer le descripteur DMA 36-bit.\n");
|
|
return false;
|
|
}
|
|
|
|
if (fDmaBuf->prepare(kIODirectionInOut) != kIOReturnSuccess) {
|
|
IOLog("QualcommWifi: Erreur - Impossible de verrouiller (prepare) les pages DMA.\n");
|
|
OSSafeReleaseNULL(fDmaBuf);
|
|
return false;
|
|
}
|
|
fDmaPrepared = true;
|
|
|
|
fDmaVirtAddr = fDmaBuf->getBytesNoCopy();
|
|
IOByteCount segLen = 0;
|
|
fDmaPhysAddr = fDmaBuf->getPhysicalSegment(0, &segLen);
|
|
|
|
if (!fDmaVirtAddr || !fDmaPhysAddr || segLen < size) {
|
|
IOLog("QualcommWifi: Erreur - Segment physique DMA invalide (segLen=%llu).\n", (uint64_t)segLen);
|
|
freeDmaContext();
|
|
return false;
|
|
}
|
|
|
|
bzero(fDmaVirtAddr, size);
|
|
IOLog("QualcommWifi: Tampon DMA 36-bit pret ! Phys: 0x%llx, Taille: %llu octets (Contigu: %llu)\n",
|
|
(uint64_t)fDmaPhysAddr, (uint64_t)size, (uint64_t)segLen);
|
|
return true;
|
|
}
|
|
|
|
void QualcommWifi::freeDmaContext(void) {
|
|
if (fDmaBuf) {
|
|
if (fDmaPrepared) {
|
|
fDmaBuf->complete(kIODirectionInOut);
|
|
fDmaPrepared = false;
|
|
}
|
|
OSSafeReleaseNULL(fDmaBuf);
|
|
}
|
|
fDmaVirtAddr = nullptr;
|
|
fDmaPhysAddr = 0;
|
|
}
|
|
|
|
void QualcommWifi::interruptOccurred(OSObject *owner, IOInterruptEventSource *src, int count) {
|
|
IOLog("QualcommWifi: Interruption materielle MSI recue (count=%d) !\n", count);
|
|
}
|
|
|
|
bool QualcommWifi::loadBhiFirmware(void) {
|
|
if (!fBar0Virt || !fPciDevice) {
|
|
return false;
|
|
}
|
|
|
|
UInt32 bhiOff = read32(ATH12K_MHI_BHIOFF);
|
|
UInt32 bhieOff = read32(ATH12K_MHI_BHIEOFF);
|
|
if (bhiOff == 0xFFFFFFFF || bhiOff >= 0x200000 || bhieOff == 0xFFFFFFFF || bhieOff >= 0x200000) {
|
|
IOLog("QualcommWifi: Erreur - BHI/BHIe Offsets invalides (BHI: 0x%08x, BHIe: 0x%08x)\n", bhiOff, bhieOff);
|
|
setProperty("BHI_Load_Error", "Invalid_Offsets");
|
|
return false;
|
|
}
|
|
|
|
// Désactivation d'ASPM PCIe pour stabiliser le transfert firmware (recommandation ath12k)
|
|
UInt8 pciCapOffset = 0;
|
|
if (fPciDevice->findPCICapability(kIOPCIPCIExpressCapability, &pciCapOffset) && pciCapOffset != 0) {
|
|
UInt16 linkCtrl = fPciDevice->configRead16(pciCapOffset + 0x10);
|
|
linkCtrl &= ~0x3; // Désactive ASPM L0s / L1
|
|
fPciDevice->configWrite16(pciCapOffset + 0x10, linkCtrl);
|
|
IOLog("QualcommWifi: ASPM PCIe désactivé pour stabilité du transfert firmware (LinkCtrl: 0x%04x)\n", linkCtrl);
|
|
}
|
|
|
|
// Étape 1 : Réinitialisation MHI et effacement des vecteurs BHIe
|
|
write32(bhieOff + ATH12K_BHIE_TXVECDB, 0);
|
|
write32(bhieOff + ATH12K_BHIE_TXVECSTATUS, 0);
|
|
write32(bhieOff + ATH12K_BHIE_RXVECDB, 0);
|
|
write32(bhieOff + ATH12K_BHIE_RXVECSTATUS, 0);
|
|
|
|
write32(ATH12K_MHI_MHICTRL, ATH12K_MHICTRL_RESET_MASK);
|
|
IODelay(10000); // 10 ms
|
|
|
|
size_t fwSize = get_wcn7850_amss_size();
|
|
size_t sblSize = 0x80000; // 512 Ko pour le Secondary Boot Loader (SBL)
|
|
if (fwSize < sblSize) {
|
|
IOLog("QualcommWifi: Erreur - Taille AMSS trop petite (%zu < 512 Ko)\n", fwSize);
|
|
return false;
|
|
}
|
|
|
|
IOLog("QualcommWifi: Initialisation du chargeur FBC 2-étapes (SBL: 512 Ko, Image totale: %zu octets)...\n", fwSize);
|
|
|
|
// Étape 2 : Allocation DMA contigu pour l'image AMSS complète
|
|
IOBufferMemoryDescriptor *fwDma = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
|
|
kernel_task,
|
|
static_cast<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(kIOMemoryPhysicallyContiguous),
|
|
fwSize,
|
|
ATH12K_DMA_MASK_36BIT_PAGE
|
|
);
|
|
|
|
if (!fwDma || fwDma->prepare(kIODirectionInOut) != kIOReturnSuccess) {
|
|
IOLog("QualcommWifi: Erreur - Echec allocation/prepare DMA AMSS\n");
|
|
setProperty("BHI_Load_Error", "DMA_Alloc_Failed");
|
|
if (fwDma) OSSafeReleaseNULL(fwDma);
|
|
return false;
|
|
}
|
|
|
|
void *virt = fwDma->getBytesNoCopy();
|
|
IOByteCount segLen = 0;
|
|
IOPhysicalAddress phys = fwDma->getPhysicalSegment(0, &segLen);
|
|
|
|
if (!virt || !phys || segLen < fwSize) {
|
|
IOLog("QualcommWifi: Erreur - Segment DMA AMSS incomplet\n");
|
|
fwDma->complete(kIODirectionInOut);
|
|
OSSafeReleaseNULL(fwDma);
|
|
return false;
|
|
}
|
|
|
|
memcpy(virt, wcn7850_amss_bin, fwSize);
|
|
setProperty("AMSS_DMA_Phys", (uint64_t)phys, 64);
|
|
|
|
// Étape 3 : Chargement du SBL (512 Ko) via BHI en mode PBL
|
|
write32(bhiOff + ATH12K_BHI_STATUS, 0);
|
|
write32(bhiOff + ATH12K_BHI_IMGADDR_LOW, (UInt32)(phys & 0xFFFFFFFF));
|
|
write32(bhiOff + ATH12K_BHI_IMGADDR_HIGH, (UInt32)((phys >> 32) & 0xFFFFFFFF));
|
|
write32(bhiOff + ATH12K_BHI_IMGSIZE, (UInt32)sblSize);
|
|
|
|
write32(bhiOff + ATH12K_BHI_IMGTXDB, 0x1);
|
|
IOLog("QualcommWifi: Sonnette BHI_IMGTXDB déclenchée (Session ID = 1, SBL 512 Ko)...\n");
|
|
|
|
UInt32 bhiStatus = 0;
|
|
UInt32 timeoutMs = 50;
|
|
bool sblSuccess = false;
|
|
|
|
while (timeoutMs > 0) {
|
|
bhiStatus = read32(bhiOff + ATH12K_BHI_STATUS);
|
|
UInt32 state = (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT;
|
|
if (state == ATH12K_BHI_STATUS_SUCCESS) {
|
|
sblSuccess = true;
|
|
break;
|
|
} else if (state == ATH12K_BHI_STATUS_ERROR) {
|
|
UInt32 err = read32(bhiOff + ATH12K_BHI_ERRCODE);
|
|
IOLog("QualcommWifi: Erreur BHI SBL (code=0x%08x)\n", err);
|
|
setProperty("BHI_ErrorCode", err, 32);
|
|
break;
|
|
}
|
|
IODelay(100);
|
|
timeoutMs--;
|
|
}
|
|
|
|
setProperty("BHI_FinalStatus", bhiStatus, 32);
|
|
setProperty("BHI_Status_Raw", bhiStatus, 32);
|
|
setProperty("BHI_Status_State", (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, 32);
|
|
|
|
timeoutMs = 50;
|
|
UInt32 ee = 0;
|
|
while (timeoutMs > 0) {
|
|
ee = read32(bhiOff + ATH12K_BHI_EXECENV);
|
|
UInt32 mhiStat = read32(ATH12K_MHI_MHISTATUS);
|
|
if (ee == ATH12K_MHI_EE_SBL || ee == ATH12K_MHI_EE_AMSS || (mhiStat & ATH12K_MHISTATUS_READY_MASK)) {
|
|
break;
|
|
}
|
|
IODelay(100);
|
|
timeoutMs--;
|
|
}
|
|
|
|
setProperty("BHI_ExecEnv_AfterLoad", ee, 32);
|
|
setProperty("MHI_Status_After_SBL", read32(ATH12K_MHI_MHISTATUS), 32);
|
|
setProperty("MHI_Ctrl_After_SBL", read32(ATH12K_MHI_MHICTRL), 32);
|
|
IOLog("QualcommWifi: SBL BHI terminé (Status=0x%08x, State=%u, ExecEnv=0x%x, SBL_Succes=%d)\n",
|
|
bhiStatus, (bhiStatus & ATH12K_BHI_STATUS_MASK) >> ATH12K_BHI_STATUS_SHIFT, ee, sblSuccess);
|
|
|
|
// Étape 4 : Configuration des contextes MHI MMIO et transition vers M0
|
|
bool inM0 = false;
|
|
bool amssSuccess = false;
|
|
uint32_t processedEvents = 0;
|
|
|
|
if (sblSuccess && fDmaPhysAddr != 0) {
|
|
timeoutMs = 50;
|
|
while (timeoutMs > 0) {
|
|
UInt32 mstat = read32(ATH12K_MHI_MHISTATUS);
|
|
if (mstat & ATH12K_MHISTATUS_READY_MASK) {
|
|
break;
|
|
}
|
|
IODelay(100);
|
|
timeoutMs--;
|
|
}
|
|
|
|
uint64_t chan_ctxt_phys = fDmaPhysAddr + 0x0000;
|
|
uint64_t er_ctxt_phys = fDmaPhysAddr + 0x1000;
|
|
uint64_t cmd_ctxt_phys = fDmaPhysAddr + 0x2000;
|
|
uint64_t cmd_ring_phys = fDmaPhysAddr + 0x3000;
|
|
uint64_t er0_ring_phys = fDmaPhysAddr + 0x4000;
|
|
uint64_t er1_ring_phys = fDmaPhysAddr + 0x5000;
|
|
|
|
uint32_t cmd_ring_len = 64 * sizeof(struct mhi_ring_element);
|
|
uint32_t er0_ring_len = 32 * sizeof(struct mhi_ring_element);
|
|
uint32_t er1_ring_len = 256 * sizeof(struct mhi_ring_element);
|
|
|
|
struct mhi_chan_ctxt *chan_ctxt = (struct mhi_chan_ctxt *)((uint8_t *)fDmaVirtAddr + 0x0000);
|
|
bzero(chan_ctxt, 64 * sizeof(struct mhi_chan_ctxt));
|
|
|
|
struct mhi_cmd_ctxt *cmd_ctxt = (struct mhi_cmd_ctxt *)((uint8_t *)fDmaVirtAddr + 0x2000);
|
|
bzero(cmd_ctxt, sizeof(struct mhi_cmd_ctxt));
|
|
cmd_ctxt[0].rbase = OSSwapHostToLittleInt64(cmd_ring_phys);
|
|
cmd_ctxt[0].rlen = OSSwapHostToLittleInt64(cmd_ring_len);
|
|
cmd_ctxt[0].rp = OSSwapHostToLittleInt64(cmd_ring_phys);
|
|
cmd_ctxt[0].wp = OSSwapHostToLittleInt64(cmd_ring_phys);
|
|
|
|
struct mhi_event_ctxt *evCtxt = (struct mhi_event_ctxt *)((uint8_t *)fDmaVirtAddr + 0x1000);
|
|
bzero(evCtxt, 2 * sizeof(struct mhi_event_ctxt));
|
|
|
|
evCtxt[0].intmod = 0;
|
|
evCtxt[0].ertype = OSSwapHostToLittleInt32(1);
|
|
evCtxt[0].msivec = 0;
|
|
evCtxt[0].rbase = OSSwapHostToLittleInt64(er0_ring_phys);
|
|
evCtxt[0].rlen = OSSwapHostToLittleInt64(er0_ring_len);
|
|
evCtxt[0].rp = OSSwapHostToLittleInt64(er0_ring_phys);
|
|
evCtxt[0].wp = OSSwapHostToLittleInt64(er0_ring_phys);
|
|
|
|
evCtxt[1].intmod = OSSwapHostToLittleInt32(1);
|
|
evCtxt[1].ertype = OSSwapHostToLittleInt32(1);
|
|
evCtxt[1].msivec = 0;
|
|
evCtxt[1].rbase = OSSwapHostToLittleInt64(er1_ring_phys);
|
|
evCtxt[1].rlen = OSSwapHostToLittleInt64(er1_ring_len);
|
|
evCtxt[1].rp = OSSwapHostToLittleInt64(er1_ring_phys);
|
|
evCtxt[1].wp = OSSwapHostToLittleInt64(er1_ring_phys);
|
|
|
|
bzero((uint8_t *)fDmaVirtAddr + 0x3000, cmd_ring_len);
|
|
bzero((uint8_t *)fDmaVirtAddr + 0x4000, er0_ring_len);
|
|
bzero((uint8_t *)fDmaVirtAddr + 0x5000, er1_ring_len);
|
|
|
|
write32(ATH12K_MHI_CCABAP_HIGHER, (UInt32)((chan_ctxt_phys >> 32) & 0xFFFFFFFF));
|
|
write32(ATH12K_MHI_CCABAP_LOWER, (UInt32)(chan_ctxt_phys & 0xFFFFFFFF));
|
|
write32(ATH12K_MHI_ECABAP_HIGHER, (UInt32)((er_ctxt_phys >> 32) & 0xFFFFFFFF));
|
|
write32(ATH12K_MHI_ECABAP_LOWER, (UInt32)(er_ctxt_phys & 0xFFFFFFFF));
|
|
write32(ATH12K_MHI_CRCBAP_HIGHER, (UInt32)((cmd_ctxt_phys >> 32) & 0xFFFFFFFF));
|
|
write32(ATH12K_MHI_CRCBAP_LOWER, (UInt32)(cmd_ctxt_phys & 0xFFFFFFFF));
|
|
|
|
write32(ATH12K_MHI_MHICFG, (2 << 16) | (0 << 24));
|
|
write32(ATH12K_MHI_MHICTRLBASE_HIGHER, 0);
|
|
write32(ATH12K_MHI_MHICTRLBASE_LOWER, 0);
|
|
write32(ATH12K_MHI_MHIDATABASE_HIGHER, 0);
|
|
write32(ATH12K_MHI_MHIDATABASE_LOWER, 0);
|
|
write32(ATH12K_MHI_MHICTRLLIMIT_HIGHER, 0x0F);
|
|
write32(ATH12K_MHI_MHICTRLLIMIT_LOWER, 0xFFFFFFFF);
|
|
write32(ATH12K_MHI_MHIDATALIMIT_HIGHER, 0x0F);
|
|
write32(ATH12K_MHI_MHIDATALIMIT_LOWER, 0xFFFFFFFF);
|
|
|
|
uint64_t er0_armed_wp = er0_ring_phys + er0_ring_len - sizeof(struct mhi_ring_element);
|
|
evCtxt[0].wp = OSSwapHostToLittleInt64(er0_armed_wp);
|
|
uint64_t er1_armed_wp = er1_ring_phys + er1_ring_len - sizeof(struct mhi_ring_element);
|
|
evCtxt[1].wp = OSSwapHostToLittleInt64(er1_armed_wp);
|
|
|
|
UInt32 erdbOff = read32(ATH12K_MHI_ERDBOFF);
|
|
if (erdbOff != 0 && erdbOff < 0x200000) {
|
|
write32(erdbOff + 4, (UInt32)((er0_armed_wp >> 32) & 0xFFFFFFFF));
|
|
write32(erdbOff + 0, (UInt32)(er0_armed_wp & 0xFFFFFFFF));
|
|
write32(erdbOff + 8 + 4, (UInt32)((er1_armed_wp >> 32) & 0xFFFFFFFF));
|
|
write32(erdbOff + 8 + 0, (UInt32)(er1_armed_wp & 0xFFFFFFFF));
|
|
IOLog("QualcommWifi: Event Ring Doorbells armées (ER0 @ 0x%08x WP=0x%llx, ER1 @ 0x%08x WP=0x%llx) !\n",
|
|
erdbOff, er0_armed_wp, erdbOff + 8, er1_armed_wp);
|
|
setProperty("ERDB_Offset", erdbOff, 32);
|
|
}
|
|
|
|
UInt32 mhiCtrlVal = read32(ATH12K_MHI_MHICTRL);
|
|
mhiCtrlVal &= ~(UInt32)ATH12K_MHICTRL_MHISTATE_MASK;
|
|
mhiCtrlVal |= (ATH12K_MHI_STATE_M0 << ATH12K_MHICTRL_MHISTATE_SHIFT);
|
|
write32(ATH12K_MHI_MHICTRL, mhiCtrlVal);
|
|
IOLog("QualcommWifi: Transition MHI vers M0 demandée (MHICTRL: 0x%08x)...\n", mhiCtrlVal);
|
|
}
|
|
|
|
// Étape 5 : Téléversement FBC complet du firmware via BHIe
|
|
const size_t segSize = 0x80000;
|
|
size_t numVecEntries = (fwSize + segSize - 1) / segSize;
|
|
|
|
IOBufferMemoryDescriptor *vecDma = IOBufferMemoryDescriptor::inTaskWithPhysicalMask(
|
|
kernel_task,
|
|
static_cast<IOOptionBits>(kIODirectionInOut) | static_cast<IOOptionBits>(kIOMemoryPhysicallyContiguous),
|
|
numVecEntries * sizeof(struct bhi_vec_entry),
|
|
ATH12K_DMA_MASK_36BIT_PAGE
|
|
);
|
|
|
|
if (vecDma) {
|
|
if (vecDma->prepare(kIODirectionInOut) == kIOReturnSuccess) {
|
|
struct bhi_vec_entry *vecTable = (struct bhi_vec_entry *)vecDma->getBytesNoCopy();
|
|
IOByteCount vecLen = 0;
|
|
IOPhysicalAddress vecPhys = vecDma->getPhysicalSegment(0, &vecLen);
|
|
|
|
if (vecTable && vecPhys) {
|
|
size_t curRem = fwSize;
|
|
for (size_t i = 0; i < numVecEntries; i++) {
|
|
size_t chunk = (curRem > segSize) ? segSize : curRem;
|
|
vecTable[i].dma_addr = OSSwapHostToLittleInt64((uint64_t)phys + (uint64_t)i * segSize);
|
|
vecTable[i].size = OSSwapHostToLittleInt64(chunk);
|
|
curRem -= chunk;
|
|
}
|
|
|
|
IOLog("QualcommWifi: Table vectorielle BHIe FBC prête (%zu segments, Phys: 0x%llx). Programmation BHIe...\n",
|
|
numVecEntries, (uint64_t)vecPhys);
|
|
|
|
write32(bhieOff + ATH12K_BHIE_TXVECADDR_LOW, (UInt32)(vecPhys & 0xFFFFFFFF));
|
|
write32(bhieOff + ATH12K_BHIE_TXVECADDR_HIGH, (UInt32)((vecPhys >> 32) & 0xFFFFFFFF));
|
|
write32(bhieOff + ATH12K_BHIE_TXVECSIZE, (UInt32)(numVecEntries * sizeof(struct bhi_vec_entry)));
|
|
write32(bhieOff + ATH12K_BHIE_TXVECSTATUS, 0);
|
|
|
|
write32(bhieOff + ATH12K_BHIE_TXVECDB, 0x1);
|
|
|
|
UInt32 bhieStatus = 0;
|
|
timeoutMs = 50;
|
|
while (timeoutMs > 0) {
|
|
bhieStatus = read32(bhieOff + ATH12K_BHIE_TXVECSTATUS);
|
|
UInt32 bhieState = (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT;
|
|
if (bhieState == ATH12K_BHIE_TXVECSTATUS_XFER_COMPL) {
|
|
amssSuccess = true;
|
|
break;
|
|
}
|
|
IODelay(100);
|
|
timeoutMs--;
|
|
}
|
|
|
|
setProperty("BHIE_FinalStatus", bhieStatus, 32);
|
|
setProperty("BHIE_Status_Raw", bhieStatus, 32);
|
|
setProperty("BHIE_Status_State", (bhieStatus & ATH12K_BHIE_TXVECSTATUS_STATUS_MASK) >> ATH12K_BHIE_TXVECSTATUS_STATUS_SHIFT, 32);
|
|
IOLog("QualcommWifi: BHIe DMA terminé (Status=0x%08x) ! Dépouillement des événements MHI...\n", bhieStatus);
|
|
|
|
// Étape 6 : Dépouillement des événements MHI
|
|
struct mhi_ring_element *erElements = (struct mhi_ring_element *)((uint8_t *)fDmaVirtAddr + 0x4000);
|
|
|
|
timeoutMs = 50;
|
|
while (timeoutMs > 0) {
|
|
UInt32 mstat = read32(ATH12K_MHI_MHISTATUS);
|
|
UInt32 mstate = (mstat & ATH12K_MHISTATUS_MHISTATE_MASK) >> ATH12K_MHISTATUS_MHISTATE_SHIFT;
|
|
|
|
while (processedEvents < 32) {
|
|
struct mhi_ring_element *el = &erElements[processedEvents];
|
|
UInt32 dw1 = OSSwapLittleToHostInt32(el->dword[1]);
|
|
UInt32 dw0 = OSSwapLittleToHostInt32(el->dword[0]);
|
|
if (dw1 == 0 && dw0 == 0) {
|
|
break;
|
|
}
|
|
|
|
UInt8 evType = (dw1 >> 16) & 0xFF;
|
|
UInt8 evCode = (dw0 >> 24) & 0xFF;
|
|
IOLog("QualcommWifi: Événement MHI reçu #%u: Type=0x%02x, Code=0x%02x, PTR=0x%llx\n",
|
|
processedEvents, evType, evCode, OSSwapLittleToHostInt64(el->ptr));
|
|
|
|
if (evType == ATH12K_MHI_PKT_TYPE_STATE_CHANGE_EVENT && evCode == ATH12K_MHI_STATE_M0) {
|
|
inM0 = true;
|
|
} else if (evType == ATH12K_MHI_PKT_TYPE_EE_EVENT && (evCode == ATH12K_MHI_EE_AMSS || evCode == ATH12K_MHI_EE_WFW)) {
|
|
ee = evCode;
|
|
amssSuccess = true;
|
|
}
|
|
processedEvents++;
|
|
}
|
|
|
|
if (mstate == ATH12K_MHI_STATE_M0 || inM0 || ee == ATH12K_MHI_EE_AMSS || ee == ATH12K_MHI_EE_WFW) {
|
|
inM0 = true;
|
|
IOLog("QualcommWifi: MHI en mode Mission M0 (MHISTATUS: 0x%08x, EE: 0x%x) !\n", mstat, ee);
|
|
break;
|
|
}
|
|
|
|
IODelay(100);
|
|
timeoutMs--;
|
|
}
|
|
|
|
setProperty("MHI_Events_Processed", processedEvents, 32);
|
|
setProperty("MHI_Status_In_M0", read32(ATH12K_MHI_MHISTATUS), 32);
|
|
setProperty("MHI_In_M0", inM0);
|
|
setProperty("BHI_ExecEnv_Final", ee, 32);
|
|
}
|
|
|
|
vecDma->complete(kIODirectionInOut);
|
|
}
|
|
OSSafeReleaseNULL(vecDma);
|
|
}
|
|
|
|
if (inM0 || ee == ATH12K_MHI_EE_AMSS || ee == ATH12K_MHI_EE_WFW) {
|
|
IOLog("QualcommWifi: SUCCÈS MAJEUR ! Firmware WCN7850 actif en mode Mission (ExecEnv=0x%x, M0=%d) !\n", ee, inM0);
|
|
setProperty("Driver_Status", "AMSS_Loaded_Success");
|
|
} else if (sblSuccess) {
|
|
IOLog("QualcommWifi: SBL actif (ExecEnv=0x%x, BHIe=0x%x)\n", ee, read32(bhieOff + ATH12K_BHIE_TXVECSTATUS));
|
|
setProperty("Driver_Status", "SBL_Loaded_Success");
|
|
} else {
|
|
IOLog("QualcommWifi: Échec chargement complet (BHI=0x%x, ExecEnv=0x%x)\n", bhiStatus, ee);
|
|
setProperty("Driver_Status", "AMSS_Load_Timeout_Or_Error");
|
|
}
|
|
|
|
fwDma->complete(kIODirectionInOut);
|
|
OSSafeReleaseNULL(fwDma);
|
|
|
|
return (sblSuccess || amssSuccess);
|
|
}
|
|
|
|
bool QualcommWifi::start(IOService *provider) {
|
|
sInstance = this;
|
|
initFakeScanManager(this);
|
|
logNvramStage("start_enter");
|
|
IOLog("QualcommWifi: Demarrage du pilote Wi-Fi natif (IO80211Controller)...\n");
|
|
|
|
if (!createWorkLoop()) {
|
|
logNvramStage("workLoop_failed");
|
|
IOLog("QualcommWifi: Erreur - Impossible de creer IOWorkLoop !\n");
|
|
return false;
|
|
}
|
|
|
|
if (!createWorkQueue()) {
|
|
logNvramStage("workQueue_failed");
|
|
IOLog("QualcommWifi: Erreur - Impossible de creer IO80211WorkQueue !\n");
|
|
return false;
|
|
}
|
|
|
|
if (!super::start(provider)) {
|
|
logNvramStage("super_start_failed");
|
|
IOLog("QualcommWifi: Erreur - IO80211Controller::start(provider) a echoue !\n");
|
|
return false;
|
|
}
|
|
logNvramStage("super_start_ok");
|
|
|
|
fPciDevice = OSDynamicCast(IOPCIDevice, provider);
|
|
if (!fPciDevice) {
|
|
logNvramStage("start_no_pci");
|
|
return false;
|
|
}
|
|
fPciDevice->retain();
|
|
|
|
logNvramStage("pci_open");
|
|
if (!fPciDevice->open(this)) {
|
|
logNvramStage("pci_open_failed");
|
|
OSSafeReleaseNULL(fPciDevice);
|
|
return false;
|
|
}
|
|
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");
|
|
|
|
int allow_mmio = 0;
|
|
PE_parse_boot_argn("qcom_mmio", &allow_mmio, sizeof(allow_mmio));
|
|
|
|
UInt32 bhiSerial = 0x123456;
|
|
if (allow_mmio) {
|
|
logNvramStage("bar0_map");
|
|
fPciDevice->setMemoryEnable(true);
|
|
fPciDevice->setBusMasterEnable(true);
|
|
fBar0Map = fPciDevice->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress0);
|
|
if (fBar0Map) {
|
|
fBar0Virt = (volatile void *)fBar0Map->getVirtualAddress();
|
|
logNvramStage("read_mhi");
|
|
UInt32 mhiStatus = read32(ATH12K_MHI_MHISTATUS);
|
|
UInt32 mhiCtrl = read32(ATH12K_MHI_MHICTRL);
|
|
UInt32 bhiOff = read32(ATH12K_MHI_BHIOFF);
|
|
UInt32 bhieOff = read32(ATH12K_MHI_BHIEOFF);
|
|
IOLog("QualcommWifi: MMIO BAR0 mappe (0x%p). MHI_Status=0x%08x, MHI_Ctrl=0x%08x, BHI_Off=0x%08x, BHIE_Off=0x%08x\n",
|
|
fBar0Virt, mhiStatus, mhiCtrl, bhiOff, bhieOff);
|
|
setProperty("MHI_Status", mhiStatus, 32);
|
|
setProperty("MHI_Ctrl", mhiCtrl, 32);
|
|
setProperty("BHI_Offset", bhiOff, 32);
|
|
setProperty("BHIE_Offset", bhieOff, 32);
|
|
|
|
if (bhiOff != 0xFFFFFFFF && bhiOff > 0 && bhiOff < 0x200000) {
|
|
logNvramStage("read_bhi");
|
|
UInt32 bhiMajor = read32(bhiOff + ATH12K_BHI_BHIVERSION_MAJOR);
|
|
UInt32 bhiMinor = read32(bhiOff + ATH12K_BHI_BHIVERSION_MINOR);
|
|
UInt32 bhiEe = read32(bhiOff + ATH12K_BHI_EXECENV);
|
|
UInt32 bhiStat = read32(bhiOff + ATH12K_BHI_STATUS);
|
|
bhiSerial = read32(bhiOff + ATH12K_BHI_SERIALNU);
|
|
IOLog("QualcommWifi: BHI Version=%u.%u, ExecEnv=0x%x, Status=0x%x, Serial=0x%08x\n",
|
|
bhiMajor, bhiMinor, bhiEe, bhiStat, bhiSerial);
|
|
|
|
setProperty("BHI_Major", bhiMajor, 32);
|
|
setProperty("BHI_Minor", bhiMinor, 32);
|
|
setProperty("BHI_ExecEnv", bhiEe, 32);
|
|
setProperty("BHI_Status", bhiStat, 32);
|
|
setProperty("BHI_Serial", bhiSerial, 32);
|
|
}
|
|
} else {
|
|
IOLog("QualcommWifi: Erreur - Impossible de mapper BAR0 !\n");
|
|
}
|
|
} else {
|
|
logNvramStage("safe_mode_active");
|
|
setProperty("QualcommWifi_SafeMode", "Active");
|
|
}
|
|
|
|
// Configuration de l'adresse MAC (OUI Qualcomm 00:03:7F + BHI serial)
|
|
fMacAddress.bytes[0] = 0x00;
|
|
fMacAddress.bytes[1] = 0x03;
|
|
fMacAddress.bytes[2] = 0x7F;
|
|
fMacAddress.bytes[3] = (bhiSerial >> 16) & 0xFF;
|
|
fMacAddress.bytes[4] = (bhiSerial >> 8) & 0xFF;
|
|
fMacAddress.bytes[5] = (bhiSerial) & 0xFF;
|
|
|
|
IOLog("QualcommWifi: Adresse MAC physique : %02x:%02x:%02x:%02x:%02x:%02x\n",
|
|
fMacAddress.bytes[0], fMacAddress.bytes[1], fMacAddress.bytes[2],
|
|
fMacAddress.bytes[3], fMacAddress.bytes[4], fMacAddress.bytes[5]);
|
|
|
|
// Propriétés Wi-Fi AirPort pour macOS et airportd
|
|
logNvramStage("set_airport_props");
|
|
setProperty("IO80211AirPort", true);
|
|
setProperty("AirPort", true);
|
|
setProperty("IO80211Locale", "ETSI");
|
|
setProperty("IO80211CountryCode", "FR");
|
|
setProperty("IO80211ScanCache", true);
|
|
setProperty("IO80211RadioState", (UInt64)1, 32);
|
|
|
|
// Instanciation et attachement de l'interface réseau Skywalk native (IO80211SkywalkInterface)
|
|
// Contrôlé par boot-arg "qcom_skywalk=1" (activé par défaut)
|
|
int allow_skywalk = 1;
|
|
PE_parse_boot_argn("qcom_skywalk", &allow_skywalk, sizeof(allow_skywalk));
|
|
|
|
// Étape maximale Skywalk contrôlée par boot-arg "qcom_step" (par défaut: 1 = allocation + initIvars sécurisée)
|
|
// 1: createSkywalkInterface (allocation + initIvars)
|
|
// 2: init(this, &mac) (initialisation avec copie adresse MAC)
|
|
// 3: setInterfaceRole(1)
|
|
// 4: attachInterface(fSkywalkInterface, this) [slot 441 sur controller]
|
|
// 5: fSkywalkInterface->attach(this) [attachement IORegistry]
|
|
// 6: fSkywalkInterface->start(this) [démarrage Skywalk, joinPMtree, workQueue, etc.]
|
|
// 7: this->registerService() [publication du contrôleur auprès d'IOKit / airportd]
|
|
int skywalk_step = 1;
|
|
PE_parse_boot_argn("qcom_step", &skywalk_step, sizeof(skywalk_step));
|
|
|
|
if (allow_skywalk) {
|
|
logNvramStage("skywalk_alloc");
|
|
fSkywalkInterface = createSkywalkInterface();
|
|
if (fSkywalkInterface) {
|
|
IOLog("QualcommWifi: createSkywalkInterface réussi (0x%p) !\n", fSkywalkInterface);
|
|
if (skywalk_step >= 2) {
|
|
logNvramStage("skywalk_init");
|
|
if (fSkywalkInterface->init(this, (ether_addr *)&fMacAddress)) {
|
|
IOLog("QualcommWifi: fSkywalkInterface->init(...) réussi !\n");
|
|
installSkywalkCustomVtable(fSkywalkInterface);
|
|
if (skywalk_step >= 3) {
|
|
logNvramStage("skywalk_role");
|
|
fSkywalkInterface->setInterfaceRole(1); // 1 = kIO80211InterfaceRoleInfra (Primary Wi-Fi)
|
|
IOLog("QualcommWifi: fSkywalkInterface->setInterfaceRole(1) configuré !\n");
|
|
}
|
|
if (skywalk_step >= 4) {
|
|
logNvramStage("skywalk_attach_ctrl");
|
|
fSkywalkCtrlAttached = attachInterface(fSkywalkInterface, this); // slot 441 in IO80211Controller
|
|
IOLog("QualcommWifi: this->attachInterface(fSkywalkInterface, this) = %d\n", fSkywalkCtrlAttached);
|
|
}
|
|
if (skywalk_step >= 5) {
|
|
logNvramStage("skywalk_attach");
|
|
if (fSkywalkInterface->attach(this)) {
|
|
fSkywalkAttached = true;
|
|
IOLog("QualcommWifi: fSkywalkInterface->attach(this) réussi !\n");
|
|
if (skywalk_step >= 6) {
|
|
int substep = 0;
|
|
PE_parse_boot_argn("qcom_substep", &substep, sizeof(substep));
|
|
|
|
logNvramStage("s6_0_before_start");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> ETAPE 6 : Appel de fSkywalkInterface->start(this) (substep=%d) <<<\n", substep);
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
|
|
if (substep >= 1) {
|
|
IOLog("QualcommWifi: [PAUSE 1/3] Pause de 3s AVANT start(this) pour lecture visuelle...\n");
|
|
IOSleep(3000);
|
|
}
|
|
|
|
sInStep6 = true;
|
|
sStep6WqCount = 0;
|
|
logNvramStage("s6_1_calling_start");
|
|
IOSleep(200); // Laisse au bus SPI de la carte mère le temps d'écrire s6_1 physiquement
|
|
bool startRes = fSkywalkInterface->start(this);
|
|
sInStep6 = false;
|
|
|
|
if (startRes) {
|
|
fSkywalkStarted = true;
|
|
installGlueCustomVtable(fSkywalkInterface);
|
|
logNvramStage("s6_2_start_ok");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> [SUCCES MAJEUR] fSkywalkInterface->start(this) A REUSSI ! <<<\n");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
|
|
if (substep >= 1) {
|
|
IOLog("QualcommWifi: [PAUSE 2/3] Pause de 5s APRES start(this) reussi pour observation...\n");
|
|
IOSleep(5000);
|
|
}
|
|
|
|
// AVERTISSEMENT CRITIQUE / POSTMORTEM 23:
|
|
// fSkywalkInterface NE DOIT JAMAIS appeler registerService() !
|
|
// L'appel direct de fSkywalkInterface->registerService() provoque le matching immédiat
|
|
// de IOSkywalkNetworkBSDClient::start, qui tente inconditionnellement de déréférencer
|
|
// [interface + 0xc8] (fLogicalLinks == NULL sur une interface Wi-Fi Skywalk),
|
|
// déclenchant un Kernel Panic / freeze matériel immédiat (#PF sur page 0).
|
|
// Dans l'architecture Wi-Fi macOS, l'interface reste interne et non-enregistrée.
|
|
// Seul le contrôleur (QualcommWifi::registerService()) doit être publié auprès d'IOKit pour airportd.
|
|
} else {
|
|
logNvramStage("s6_2_start_err");
|
|
IOLog("QualcommWifi: AVERTISSEMENT - fSkywalkInterface->start(this) a echoue (interface preservee pour diagnostic) !\n");
|
|
}
|
|
logNvramStage("s6_3_post_start");
|
|
if (substep >= 1) {
|
|
IOLog("QualcommWifi: [PAUSE 3/3] Pause de 3s avant de terminer QualcommWifi::start()...\n");
|
|
IOSleep(3000);
|
|
}
|
|
}
|
|
} else {
|
|
logNvramStage("skywalk_attach_err");
|
|
IOLog("QualcommWifi: Erreur - fSkywalkInterface->attach(this) a echoue !\n");
|
|
}
|
|
}
|
|
} else {
|
|
logNvramStage("skywalk_init_err");
|
|
IOLog("QualcommWifi: Erreur - fSkywalkInterface->init(...) a echoue !\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
bool shouldRegister = (skywalk_step >= 7);
|
|
int allow_register = 0;
|
|
if (PE_parse_boot_argn("qcom_register", &allow_register, sizeof(allow_register)) && allow_register) {
|
|
shouldRegister = true;
|
|
}
|
|
|
|
if (shouldRegister) {
|
|
logNvramStage("s7_0_ctrl_register");
|
|
IOSleep(200);
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> ETAPE 7 : Publication du contrôleur QualcommWifi (this->registerService) <<<\n");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
registerService();
|
|
logNvramStage("s7_1_ctrl_reg_done");
|
|
IOSleep(100);
|
|
IOLog("QualcommWifi: >>> [SUCCES] Contrôleur QualcommWifi publié avec succès dans l'IORegistry ! <<<\n");
|
|
} else {
|
|
logNvramStage("register_bypassed");
|
|
IOLog("QualcommWifi: Publication du contrôleur contournée (qcom_step < 7 et qcom_register=0).\n");
|
|
}
|
|
|
|
// Initialisation et publication de la table des médiums (requis pour SIOCGIFMEDIA / Apple80211GetIfListCopy)
|
|
logNvramStage("s8_medium_init");
|
|
createMediumTables();
|
|
|
|
if (skywalk_step >= 8) {
|
|
int substep8 = 0;
|
|
PE_parse_boot_argn("qcom_substep", &substep8, sizeof(substep8));
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> ETAPE 8 : Enregistrement Skywalk Ethernet & Client BSD (substep=%d) <<<\n", substep8);
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
|
|
if (substep8 >= 1) {
|
|
logNvramStage("s8_0_init_reginfo");
|
|
IOSleep(100);
|
|
bzero(fSkywalkRegInfo, sizeof(fSkywalkRegInfo));
|
|
bool regInit = _ZN26IOSkywalkEthernetInterface20initRegistrationInfoEPNS_16RegistrationInfoEjm(
|
|
fSkywalkInterface, fSkywalkRegInfo, 1, sizeof(fSkywalkRegInfo)
|
|
);
|
|
if (regInit) {
|
|
// 1. Définir le sous-type réseau BSD en Wi-Fi (IFRTYPE_SUBFAMILY_WIFI = 3)
|
|
// Découverte chirurgicale / Postmortem 26 :
|
|
// Apple80211.framework (_getIfListCopy) appelle ioctl(SIOCGIFTYPE, 0xc020699f).
|
|
// Il exige ifr.ifru_type.ift_type == 6 (IFT_ETHER) et ifr.ifru_type.ift_subfamily == 3 (IFRTYPE_SUBFAMILY_WIFI).
|
|
// Sans cette valeur (0 par défaut sur Skywalk Ethernet), Apple80211 rejette silencieusement en1 !
|
|
*(uint32_t*)(fSkywalkRegInfo + 0x0c) = 3; // IFRTYPE_SUBFAMILY_WIFI = 3
|
|
|
|
// 2. Définir le type de médium sur IEEE 802.11 Auto (0x80)
|
|
// Apple80211.framework vérifie également (ifmr.ifm_current & 0xe0) == 0x80 (IFM_IEEE80211).
|
|
*(uint32_t*)(fSkywalkRegInfo + 0x24) = 0x80; // active medium: kIOMediumIEEE80211Auto
|
|
*(uint32_t*)(fSkywalkRegInfo + 0x28) = 0x80; // current medium: kIOMediumIEEE80211Auto
|
|
|
|
// 3. Configuration de l'adresse MAC physique et MTU
|
|
memcpy(fSkywalkRegInfo + 0x108, fMacAddress.bytes, 6);
|
|
*(uint32_t*)(fSkywalkRegInfo + 0x48) = 1500;
|
|
*(uint32_t*)(fSkywalkRegInfo + 0x4c) = 1500;
|
|
logNvramStage("s8_1_reginfo_ok");
|
|
IOLog("QualcommWifi: [s8_1] initRegistrationInfo configuré (subfamily=WIFI/3, medium=802.11Auto/0x80, MAC OK) !\n");
|
|
} else {
|
|
logNvramStage("s8_1_reginfo_err");
|
|
IOLog("QualcommWifi: [s8_1] ERREUR initRegistrationInfo !\n");
|
|
}
|
|
}
|
|
|
|
if (substep8 >= 2) {
|
|
logNvramStage("s8_2_pool_alloc");
|
|
IOSleep(100);
|
|
SkywalkPoolOptions poolOpts;
|
|
bzero(&poolOpts, sizeof(poolOpts));
|
|
poolOpts.min_capacity = 256;
|
|
poolOpts.max_capacity = 256;
|
|
poolOpts.buffer_size = 2048;
|
|
poolOpts.align = 1;
|
|
poolOpts.flags = 1;
|
|
|
|
fSkywalkPool = (OSObject*)_ZN25IOSkywalkPacketBufferPool8withNameEPKcP8OSObjectjPKNS_11PoolOptionsE(
|
|
"QualcommWifi_Pool", this, 1, &poolOpts
|
|
);
|
|
if (fSkywalkPool) {
|
|
logNvramStage("s8_2_pool_ok");
|
|
IOLog("QualcommWifi: [s8_2] IOSkywalkPacketBufferPool alloué avec succès (0x%p) !\n", fSkywalkPool);
|
|
} else {
|
|
logNvramStage("s8_2_pool_err");
|
|
IOLog("QualcommWifi: [s8_2] ERREUR allocation IOSkywalkPacketBufferPool !\n");
|
|
}
|
|
}
|
|
|
|
if (substep8 >= 3 && fSkywalkPool) {
|
|
logNvramStage("s8_3_queues_alloc");
|
|
IOSleep(100);
|
|
fSkywalkTxQueue = (OSObject*)_ZN26IOSkywalkTxSubmissionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PKP15IOSkywalkPacketjPvES9_j(
|
|
fSkywalkPool, 256, 0, this, (void*)skywalkTxAction, this, 0
|
|
);
|
|
fSkywalkRxQueue = (OSObject*)_ZN26IOSkywalkRxCompletionQueue8withPoolEP25IOSkywalkPacketBufferPooljjP8OSObjectPFjS3_PS_PP15IOSkywalkPacketjPvES8_j(
|
|
fSkywalkPool, 256, 1, this, (void*)skywalkRxAction, this, 0
|
|
);
|
|
if (fSkywalkTxQueue && fSkywalkRxQueue) {
|
|
logNvramStage("s8_3_queues_ok");
|
|
IOLog("QualcommWifi: [s8_3] Queues Skywalk allouées (Tx: 0x%p, Rx: 0x%p) !\n", fSkywalkTxQueue, fSkywalkRxQueue);
|
|
} else {
|
|
logNvramStage("s8_3_queues_err");
|
|
IOLog("QualcommWifi: [s8_3] ERREUR allocation Queues Skywalk (Tx: 0x%p, Rx: 0x%p) !\n", fSkywalkTxQueue, fSkywalkRxQueue);
|
|
}
|
|
}
|
|
|
|
if (substep8 >= 4 && fSkywalkTxQueue && fSkywalkRxQueue) {
|
|
logNvramStage("s8_4_reg_eth");
|
|
IOSleep(100);
|
|
void *queues[2] = { fSkywalkTxQueue, fSkywalkRxQueue };
|
|
IOReturn regRes = _ZN26IOSkywalkEthernetInterface25registerEthernetInterfaceEPKNS_16RegistrationInfoEPP20IOSkywalkPacketQueuejP25IOSkywalkPacketBufferPoolS7_j(
|
|
fSkywalkInterface, fSkywalkRegInfo, queues, 2, fSkywalkPool, fSkywalkPool, 0
|
|
);
|
|
if (regRes == kIOReturnSuccess) {
|
|
fEthernetRegistered = true;
|
|
logNvramStage("s8_4_reg_eth_ok");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> [SUCCES MAJEUR] registerEthernetInterface A REUSSI ! <<<\n");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
|
|
// Postmortem 26 : Configuration des structures internes de fSkywalkInterface
|
|
// fSkywalkInterface + 0xc0 pointe sur priv
|
|
uint8_t *priv = *(uint8_t **)((uint8_t *)fSkywalkInterface + 0xc0);
|
|
if (priv) {
|
|
uint8_t *regInfo = *(uint8_t **)priv;
|
|
if (regInfo) {
|
|
*(uint32_t *)(regInfo + 0x0c) = 3; // IFRTYPE_SUBFAMILY_WIFI = 3
|
|
*(uint32_t *)(regInfo + 0x24) = 0x80; // active medium: kIOMediumIEEE80211Auto
|
|
*(uint32_t *)(regInfo + 0x28) = 0x80; // current medium: kIOMediumIEEE80211Auto
|
|
}
|
|
*(uint32_t *)(priv + 0x30) = 0x3; // ifm_status = IFM_AVALID | IFM_ACTIVE (1 | 2)
|
|
*(uint32_t *)(priv + 0x34) = 0x80; // ifm_current = kIOMediumIEEE80211Auto (0x80)
|
|
*(uint32_t *)(priv + 0x38) = 0x80; // ifm_active = kIOMediumIEEE80211Auto (0x80)
|
|
IOLog("QualcommWifi: [s8_4] struct ifmedia (status=0x3, current=0x80, active=0x80) et regInfo configurés !\n");
|
|
}
|
|
} else {
|
|
logNvramStage("s8_4_reg_eth_err");
|
|
IOLog("QualcommWifi: [s8_4] registerEthernetInterface a échoué (0x%x) !\n", regRes);
|
|
}
|
|
}
|
|
|
|
if (substep8 >= 5 && fEthernetRegistered) {
|
|
logNvramStage("s8_5_reg_svc_enter");
|
|
IOSleep(100);
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: >>> [ATTACHEMENT BSD] Publication du service fSkywalkInterface ! <<<\n");
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
fSkywalkInterface->registerService();
|
|
logNvramStage("s8_6_reg_svc_done");
|
|
IOSleep(100);
|
|
IOLog("QualcommWifi: >>> [SUCCES TOTAL] Service Skywalk publié, création d'en1 imminente ! <<<\n");
|
|
|
|
// Ré-application post-enregistrement pour garantir la persistance de l'état média
|
|
uint8_t *priv = *(uint8_t **)((uint8_t *)fSkywalkInterface + 0xc0);
|
|
if (priv) {
|
|
*(uint32_t *)(priv + 0x30) = 0x3; // ifm_status = IFM_AVALID | IFM_ACTIVE
|
|
*(uint32_t *)(priv + 0x34) = 0x80; // ifm_current = kIOMediumIEEE80211Auto
|
|
*(uint32_t *)(priv + 0x38) = 0x80; // ifm_active = kIOMediumIEEE80211Auto
|
|
}
|
|
}
|
|
}
|
|
|
|
logNvramStage("start_done");
|
|
sNvramLoggingActive = false; // FIN DÉFINITIVE DE TOUTE ÉCRITURE NVRAM PHYSIQUE
|
|
sInstance = this;
|
|
IOLog("QualcommWifi: Séquence de démarrage achevée avec succès. Télémétrie NVRAM désactivée pour la session bureau.\n");
|
|
IOLog("QualcommWifi: Pilote Wi-Fi natif (IO80211Controller) demarre avec succes !\n");
|
|
return true;
|
|
}
|
|
|
|
void QualcommWifi::stop(IOService *provider) {
|
|
if (sInstance == this) {
|
|
sInstance = nullptr;
|
|
}
|
|
IOLog("QualcommWifi: Arret du pilote Wi-Fi natif, liberation MSI, DMA et BAR0.\n");
|
|
|
|
if (fEthernetRegistered && fSkywalkInterface) {
|
|
_ZN26IOSkywalkEthernetInterface27deregisterEthernetInterfaceEj(fSkywalkInterface, 0);
|
|
fEthernetRegistered = false;
|
|
}
|
|
if (fSkywalkTxQueue) {
|
|
OSSafeReleaseNULL(fSkywalkTxQueue);
|
|
}
|
|
if (fSkywalkRxQueue) {
|
|
OSSafeReleaseNULL(fSkywalkRxQueue);
|
|
}
|
|
if (fSkywalkPool) {
|
|
OSSafeReleaseNULL(fSkywalkPool);
|
|
}
|
|
|
|
if (fSkywalkInterface) {
|
|
if (fSkywalkCtrlAttached) {
|
|
detachInterface(fSkywalkInterface, false); // slot 442 in IO80211Controller
|
|
fSkywalkCtrlAttached = false;
|
|
}
|
|
if (fSkywalkStarted) {
|
|
fSkywalkInterface->stop(this);
|
|
fSkywalkStarted = false;
|
|
}
|
|
if (fSkywalkAttached) {
|
|
fSkywalkInterface->detach(this);
|
|
fSkywalkAttached = false;
|
|
}
|
|
OSSafeReleaseNULL(fSkywalkInterface);
|
|
}
|
|
|
|
IOWorkLoop *workLoop = getWorkLoop();
|
|
if (fInterruptSource) {
|
|
fInterruptSource->disable();
|
|
if (workLoop) {
|
|
workLoop->removeEventSource(fInterruptSource);
|
|
}
|
|
OSSafeReleaseNULL(fInterruptSource);
|
|
}
|
|
|
|
freeDmaContext();
|
|
|
|
fBar0Virt = nullptr;
|
|
if (fBar0Map) {
|
|
OSSafeReleaseNULL(fBar0Map);
|
|
}
|
|
|
|
if (fPciDevice) {
|
|
fPciDevice->close(this);
|
|
OSSafeReleaseNULL(fPciDevice);
|
|
}
|
|
|
|
if (fWorkLoop) {
|
|
OSSafeReleaseNULL(fWorkLoop);
|
|
}
|
|
|
|
OSSafeReleaseNULL(fWorkQueue);
|
|
PMstop();
|
|
super::stop(provider);
|
|
}
|
|
|
|
bool QualcommWifi::createWorkQueue(void) {
|
|
logNvramStage("createWorkQueue");
|
|
if (fWorkQueue) {
|
|
hookWorkQueueVtable(fWorkQueue);
|
|
return true;
|
|
}
|
|
fWorkQueue = IO80211WorkQueue::workQueue();
|
|
if (!fWorkQueue) {
|
|
logNvramStage("workQueue_failed");
|
|
IOLog("QualcommWifi: Erreur - Impossible d'allouer IO80211WorkQueue !\n");
|
|
return false;
|
|
}
|
|
hookWorkQueueVtable(fWorkQueue);
|
|
logNvramStage("workQueue_ok");
|
|
IOLog("QualcommWifi: IO80211WorkQueue allouee avec succes (0x%p) !\n", fWorkQueue);
|
|
return true;
|
|
}
|
|
|
|
IO80211WorkQueue* QualcommWifi::getWorkQueue(void) const {
|
|
if (fWorkQueue) {
|
|
hookWorkQueueVtable(fWorkQueue);
|
|
}
|
|
sGetWorkQueueCount++;
|
|
if (sGetWorkQueueCount <= 3) {
|
|
char tag[64];
|
|
snprintf(tag, sizeof(tag), "cb_wq_%d", sGetWorkQueueCount);
|
|
logNvramStage(tag);
|
|
} else if (sInStep6 && sStep6WqCount < 5) {
|
|
sStep6WqCount++;
|
|
char tag[64];
|
|
snprintf(tag, sizeof(tag), "cb_wq_s6_%d", sStep6WqCount);
|
|
logNvramStage(tag);
|
|
IOSleep(20);
|
|
}
|
|
IOLog("QualcommWifi: >>> CALLBACK getWorkQueue(#%d) -> 0x%p <<<\n", sGetWorkQueueCount, fWorkQueue);
|
|
return fWorkQueue;
|
|
}
|
|
|
|
IORecursiveLock* QualcommWifi::allocIO80211RecursiveLock(void) {
|
|
logNvramStage("cb_allocLock_enter");
|
|
IOSleep(50);
|
|
IOLog("QualcommWifi: >>> CALLBACK allocIO80211RecursiveLock() [ENTER] <<<\n");
|
|
IORecursiveLock *lock = super::allocIO80211RecursiveLock();
|
|
logNvramStage("cb_allocLock_super_ok");
|
|
IOSleep(50);
|
|
IOLog("QualcommWifi: super::allocIO80211RecursiveLock() = 0x%p\n", lock);
|
|
if (!lock) {
|
|
logNvramStage("cb_allocLock_null");
|
|
IOLog("QualcommWifi: AVERTISSEMENT - allocIO80211RecursiveLock a renvoye NULL, repli sur IORecursiveLockAlloc()...\n");
|
|
lock = IORecursiveLockAlloc();
|
|
IOLog("QualcommWifi: IORecursiveLockAlloc() = 0x%p\n", lock);
|
|
}
|
|
logNvramStage("cb_allocLock_exit");
|
|
IOSleep(50);
|
|
return lock;
|
|
}
|
|
|
|
bool QualcommWifi::createWorkLoop(void) {
|
|
logNvramStage("createWorkLoop");
|
|
if (fWorkLoop) {
|
|
return true;
|
|
}
|
|
fWorkLoop = IOWorkLoop::workLoop();
|
|
if (!fWorkLoop) {
|
|
logNvramStage("workLoop_alloc_failed");
|
|
IOLog("QualcommWifi: Erreur - Impossible d'allouer IOWorkLoop !\n");
|
|
return false;
|
|
}
|
|
logNvramStage("workLoop_ok");
|
|
IOLog("QualcommWifi: IOWorkLoop allouee avec succes (0x%p) !\n", fWorkLoop);
|
|
return true;
|
|
}
|
|
|
|
IOWorkLoop* QualcommWifi::getWorkLoop(void) const {
|
|
return fWorkLoop ? fWorkLoop : (getProvider() ? getProvider()->getWorkLoop() : nullptr);
|
|
}
|
|
|
|
IOReturn QualcommWifi::getHardwareAddress(IOEthernetAddress *addrP) {
|
|
logNvramStage("cb_getHwAddr");
|
|
if (!addrP) return kIOReturnBadArgument;
|
|
bcopy(&fMacAddress, addrP, sizeof(IOEthernetAddress));
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
IOReturn QualcommWifi::enable(IONetworkInterface *netif) {
|
|
logNvramStage("cb_netif_enable");
|
|
IOLog("QualcommWifi: >>> CALLBACK netif enable() <<<\n");
|
|
const IONetworkMedium *medium = getSelectedMedium();
|
|
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium);
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
IOReturn QualcommWifi::disable(IONetworkInterface *netif) {
|
|
logNvramStage("cb_netif_disable");
|
|
IOLog("QualcommWifi: >>> CALLBACK netif disable() <<<\n");
|
|
setLinkStatus(kIONetworkLinkValid, nullptr);
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
bool QualcommWifi::createMediumTables(void) {
|
|
IOLog("QualcommWifi: Creation et publication du dictionnaire de mediums reseau IEEE 802.11...\n");
|
|
OSDictionary *mediumDict = OSDictionary::withCapacity(1);
|
|
if (!mediumDict) {
|
|
IOLog("QualcommWifi: ERREUR allocation mediumDict !\n");
|
|
return false;
|
|
}
|
|
|
|
IONetworkMedium *mediumWifi = IONetworkMedium::medium(
|
|
kIOMediumIEEE80211Auto,
|
|
1200000000ULL, // 1.2 Gbps (Wi-Fi 7 / 6E)
|
|
0,
|
|
0,
|
|
"Auto"
|
|
);
|
|
|
|
if (mediumWifi) {
|
|
IONetworkMedium::addMedium(mediumDict, mediumWifi);
|
|
mediumWifi->release();
|
|
}
|
|
|
|
bool published = publishMediumDictionary(mediumDict);
|
|
if (published) {
|
|
const IONetworkMedium *selected = IONetworkMedium::getMediumWithType(mediumDict, kIOMediumIEEE80211Auto);
|
|
if (selected) {
|
|
setSelectedMedium(selected);
|
|
setCurrentMedium(selected);
|
|
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, selected, 1200000000ULL, nullptr);
|
|
IOLog("QualcommWifi: Medium IEEE80211Auto selectionne et linkStatus(Active) configure avec succes !\n");
|
|
}
|
|
} else {
|
|
IOLog("QualcommWifi: ERREUR publishMediumDictionary !\n");
|
|
}
|
|
|
|
mediumDict->release();
|
|
return published;
|
|
}
|
|
|
|
IOReturn QualcommWifi::selectMedium(const IONetworkMedium *medium) {
|
|
if (!medium) return kIOReturnBadArgument;
|
|
setSelectedMedium(medium);
|
|
setCurrentMedium(medium);
|
|
setLinkStatus(kIONetworkLinkValid | kIONetworkLinkActive, medium);
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
UInt32 QualcommWifi::outputPacket(mbuf_t m, void *param) {
|
|
if (m) {
|
|
freePacket(m);
|
|
}
|
|
return kIOReturnOutputDropped;
|
|
}
|
|
|
|
void QualcommWifi::dataLinkLayerAttachComplete(void) {
|
|
IOLog("QualcommWifi: >>> CALLBACK dataLinkLayerAttachComplete (Skywalk Tahoe) <<<\n");
|
|
}
|
|
|
|
IOReturn QualcommWifi::handleCardSpecific(IO80211SkywalkInterface *iface, unsigned long req_type, void *req_data, bool isSet) {
|
|
return (IOReturn)handleApple80211Request((unsigned int)req_type, isSet ? 1 : 0, req_data);
|
|
}
|
|
|
|
void* QualcommWifi::getLogger(void) const {
|
|
return (void*)fLogger;
|
|
}
|
|
|
|
void* QualcommWifi::getFaultReporterFromDriver(void) {
|
|
return (void*)fFaultReporter;
|
|
}
|
|
|
|
UInt32 QualcommWifi::getActionFramePoolCapacity(void) {
|
|
return 0x100;
|
|
}
|
|
|
|
IO80211SkywalkInterface* QualcommWifi::getPrimarySkywalkInterface(void) {
|
|
return fSkywalkInterface;
|
|
}
|
|
|
|
IOReturn QualcommWifi::setMulticastList(ether_addr const *addrs, UInt32 count) {
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
void QualcommWifi::postScanDone(void) {
|
|
if (fSkywalkInterface) {
|
|
int status = 0; // kIOReturnSuccess
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_SCAN_DONE, &status, sizeof(status), false);
|
|
IOLog("QualcommWifi: Notification APPLE80211_M_SCAN_DONE (10) transmise avec succes a airportd !\n");
|
|
}
|
|
}
|
|
|
|
IOReturn QualcommWifi::handleAssociate(apple80211_assoc_data *assoc) {
|
|
if (!assoc) return kIOReturnBadArgument;
|
|
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
IOLog("QualcommWifi: Demande d'association au reseau Wi-Fi : %.*s (BSSID: %02x:%02x:%02x:%02x:%02x:%02x, auth_upper=%u, auth_lower=%u)\n",
|
|
assoc->ad_ssid_len, assoc->ad_ssid,
|
|
assoc->ad_bssid.octet[0], assoc->ad_bssid.octet[1], assoc->ad_bssid.octet[2],
|
|
assoc->ad_bssid.octet[3], assoc->ad_bssid.octet[4], assoc->ad_bssid.octet[5],
|
|
assoc->ad_auth_upper, assoc->ad_auth_lower);
|
|
IOLog("QualcommWifi: ========================================================\n");
|
|
|
|
fSsidLen = (assoc->ad_ssid_len < APPLE80211_MAX_SSID_LEN) ? assoc->ad_ssid_len : APPLE80211_MAX_SSID_LEN;
|
|
memcpy(fSsid, assoc->ad_ssid, fSsidLen);
|
|
memcpy(fBssid, assoc->ad_bssid.octet, 6);
|
|
fLinkActive = true;
|
|
|
|
// Mise à jour de l'état du lien réseau Ethernet / Skywalk
|
|
setLinkStatus(kIONetworkLinkActive | kIONetworkLinkValid, 0);
|
|
|
|
// Notifications Apple80211 vers airportd
|
|
if (fSkywalkInterface) {
|
|
int status = 0; // kIOReturnSuccess
|
|
// 1. APPLE80211_M_ASSOC_DONE (9)
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_ASSOC_DONE, &status, sizeof(status), false);
|
|
|
|
// 2. APPLE80211_M_LINK_CHANGED (4)
|
|
int link = 1;
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_LINK_CHANGED, &link, sizeof(link), false);
|
|
|
|
// 3. APPLE80211_M_SSID_CHANGED (2)
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_SSID_CHANGED, fSsid, fSsidLen, false);
|
|
|
|
// 4. APPLE80211_M_BSSID_CHANGED (3)
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_BSSID_CHANGED, fBssid, 6, false);
|
|
|
|
IOLog("QualcommWifi: Notifications d'association (ASSOC_DONE, LINK_CHANGED, SSID, BSSID) transmises a airportd !\n");
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
IOReturn QualcommWifi::handleDisassociate(void) {
|
|
IOLog("QualcommWifi: Deconnexion Wi-Fi demandee par macOS.\n");
|
|
fLinkActive = false;
|
|
fSsidLen = 0;
|
|
bzero(fBssid, sizeof(fBssid));
|
|
|
|
setLinkStatus(kIONetworkLinkValid, 0);
|
|
|
|
if (fSkywalkInterface) {
|
|
int link = 0;
|
|
_ZN17IO80211Controller11postMessageEP23IO80211SkywalkInterfacejPvmb(this, fSkywalkInterface, APPLE80211_M_LINK_CHANGED, &link, sizeof(link), false);
|
|
IOLog("QualcommWifi: Notification de deconnexion (LINK_CHANGED=0) transmise a airportd !\n");
|
|
}
|
|
return kIOReturnSuccess;
|
|
}
|
|
|
|
IOReturn QualcommWifi::getDRIVER_VERSION(IO80211SkywalkInterface *iface, apple80211_version_data *vd) {
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_DRIVER_VERSION, 0, vd);
|
|
}
|
|
|
|
IOReturn QualcommWifi::getHARDWARE_VERSION(IO80211SkywalkInterface *iface, apple80211_version_data *vd) {
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_HARDWARE_VERSION, 0, vd);
|
|
}
|
|
|
|
IOReturn QualcommWifi::getCARD_CAPABILITIES(IO80211SkywalkInterface *iface, apple80211_capability_data *cap) {
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_CARD_CAPABILITIES, 0, cap);
|
|
}
|
|
|
|
IOReturn QualcommWifi::getPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) {
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, -1, pwr);
|
|
}
|
|
|
|
IOReturn QualcommWifi::setPOWER(IO80211SkywalkInterface *iface, apple80211_power_data *pwr) {
|
|
int val = (pwr && pwr->power_state[0]) ? 1 : 0;
|
|
IOLog("QualcommWifi: setPOWER invoqué par airportd (val=%d)\n", val);
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_POWER, val, pwr);
|
|
}
|
|
|
|
IOReturn QualcommWifi::getCOUNTRY_CODE(IO80211SkywalkInterface *iface, apple80211_country_code_data *cc) {
|
|
return (IOReturn)handleApple80211Request(APPLE80211_IOC_COUNTRY_CODE, 0, cc);
|
|
}
|
|
|
|
SInt32 QualcommWifi::handleApple80211Request(unsigned int req_type, int req_val, void* req_data) {
|
|
switch (req_type) {
|
|
case APPLE80211_IOC_CARD_CAPABILITIES: {
|
|
apple80211_capability_data *cap = (apple80211_capability_data *)req_data;
|
|
if (cap) {
|
|
bzero(cap, sizeof(*cap));
|
|
cap->version = APPLE80211_VERSION;
|
|
#define SET_CAP(c) (cap->capabilities[(c) / 8] |= (1 << ((c) % 8)))
|
|
SET_CAP(APPLE80211_CAP_WEP);
|
|
SET_CAP(APPLE80211_CAP_TKIP);
|
|
SET_CAP(APPLE80211_CAP_AES_CCM);
|
|
SET_CAP(APPLE80211_CAP_WPA1);
|
|
SET_CAP(APPLE80211_CAP_WPA2);
|
|
SET_CAP(APPLE80211_CAP_WPA);
|
|
SET_CAP(APPLE80211_CAP_SHORT_GI_20MHZ);
|
|
SET_CAP(APPLE80211_CAP_SHORT_GI_40MHZ);
|
|
SET_CAP(APPLE80211_CAP_BURST);
|
|
SET_CAP(APPLE80211_CAP_WME);
|
|
SET_CAP(APPLE80211_CAP_PMGT);
|
|
SET_CAP(APPLE80211_CAP_SHPREAMBLE);
|
|
SET_CAP(APPLE80211_CAP_SHSLOT);
|
|
#undef SET_CAP
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_STATE: {
|
|
apple80211_state_data *state = (apple80211_state_data *)req_data;
|
|
if (state) {
|
|
state->version = APPLE80211_VERSION;
|
|
state->state = fLinkActive ? APPLE80211_S_RUN : APPLE80211_S_INIT;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_PHY_MODE: {
|
|
apple80211_phymode_data *pm = (apple80211_phymode_data *)req_data;
|
|
if (pm) {
|
|
pm->version = APPLE80211_VERSION;
|
|
pm->phy_mode = APPLE80211_MODE_11AX;
|
|
pm->active_phy_mode = APPLE80211_MODE_11AX;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_OP_MODE: {
|
|
apple80211_opmode_data *om = (apple80211_opmode_data *)req_data;
|
|
if (om) {
|
|
om->version = APPLE80211_VERSION;
|
|
om->op_mode = APPLE80211_M_STA;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_CHANNEL: {
|
|
apple80211_channel_data *cd = (apple80211_channel_data *)req_data;
|
|
if (cd) {
|
|
if (req_val == 1 && cd->channel.channel > 0) {
|
|
fCurrentChannel = (UInt16)cd->channel.channel;
|
|
IOLog("QualcommWifi: Changement de canal Wi-Fi vers %u\n", fCurrentChannel);
|
|
}
|
|
cd->version = APPLE80211_VERSION;
|
|
cd->channel.version = APPLE80211_VERSION;
|
|
cd->channel.channel = fCurrentChannel > 0 ? fCurrentChannel : 1;
|
|
cd->channel.flags = (cd->channel.channel <= 14) ? (APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ) : (APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_TXPOWER: {
|
|
apple80211_txpower_data *tx = (apple80211_txpower_data *)req_data;
|
|
if (tx) {
|
|
tx->version = APPLE80211_VERSION;
|
|
tx->txpower_unit = APPLE80211_UNIT_DBM;
|
|
tx->txpower = 20; // 20 dBm (100 mW)
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_PROTMODE:
|
|
case APPLE80211_IOC_POWERSAVE:
|
|
case APPLE80211_IOC_SCANCACHE_CLEAR: {
|
|
return 0;
|
|
}
|
|
case APPLE80211_IOC_RSSI: {
|
|
apple80211_rssi_data *rd = (apple80211_rssi_data *)req_data;
|
|
if (rd) {
|
|
rd->version = APPLE80211_VERSION;
|
|
rd->num_radios = 1;
|
|
rd->rssi_unit = APPLE80211_UNIT_DBM;
|
|
rd->rssi[0] = fLastRssi ? fLastRssi : -50;
|
|
rd->aggregate_rssi = rd->rssi[0];
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_NOISE: {
|
|
apple80211_noise_data *nd = (apple80211_noise_data *)req_data;
|
|
if (nd) {
|
|
nd->version = APPLE80211_VERSION;
|
|
nd->num_radios = 1;
|
|
nd->noise_unit = APPLE80211_UNIT_DBM;
|
|
nd->noise[0] = -95;
|
|
nd->aggregate_noise = -95;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_SSID: {
|
|
apple80211_ssid_data *sd = (apple80211_ssid_data *)req_data;
|
|
if (sd) {
|
|
sd->version = APPLE80211_VERSION;
|
|
sd->ssid_len = fSsidLen;
|
|
memcpy(sd->ssid_bytes, fSsid, fSsidLen);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_BSSID: {
|
|
apple80211_bssid_data *bd = (apple80211_bssid_data *)req_data;
|
|
if (bd) {
|
|
bd->version = APPLE80211_VERSION;
|
|
memcpy(bd->bssid.octet, fBssid, 6);
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_RATE: {
|
|
apple80211_rate_data *rate = (apple80211_rate_data *)req_data;
|
|
if (rate) {
|
|
rate->version = APPLE80211_VERSION;
|
|
rate->num_radios = 1;
|
|
rate->rate[0] = 1200;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_SUPPORTED_CHANNELS: {
|
|
apple80211_sup_channel_data *sc = (apple80211_sup_channel_data *)req_data;
|
|
if (sc) {
|
|
sc->version = APPLE80211_VERSION;
|
|
static const UInt16 chs[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 };
|
|
sc->num_channels = sizeof(chs)/sizeof(chs[0]);
|
|
for (int i = 0; i < sc->num_channels; i++) {
|
|
sc->supported_channels[i].version = APPLE80211_VERSION;
|
|
sc->supported_channels[i].channel = chs[i];
|
|
sc->supported_channels[i].flags = (chs[i] <= 14) ? (APPLE80211_C_FLAG_2GHZ | APPLE80211_C_FLAG_20MHZ) : (APPLE80211_C_FLAG_5GHZ | APPLE80211_C_FLAG_80MHZ);
|
|
}
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_SCAN_REQ:
|
|
case APPLE80211_IOC_SCAN_REQ_MULTIPLE: {
|
|
fScanInProgress = true;
|
|
sWclScanCursor = 0;
|
|
return 0;
|
|
}
|
|
case APPLE80211_IOC_SCAN_RESULT: {
|
|
if (req_data) {
|
|
if (populateTahoeScanResult(sWclScanCursor, (tahoe_scan_result *)req_data)) {
|
|
sWclScanCursor++;
|
|
fScanInProgress = false;
|
|
return 0;
|
|
} else {
|
|
sWclScanCursor = 0;
|
|
fScanInProgress = false;
|
|
return 5; // ENOENT : fin d'itération
|
|
}
|
|
}
|
|
fScanInProgress = false;
|
|
return 5;
|
|
}
|
|
case APPLE80211_IOC_POWER: {
|
|
apple80211_power_data *pwr = (apple80211_power_data *)req_data;
|
|
if (pwr) {
|
|
pwr->version = APPLE80211_VERSION;
|
|
pwr->num_radios = 1;
|
|
if (req_val == 1) {
|
|
IOLog("QualcommWifi: Alimentation radio Wi-Fi : ACTIVE\n");
|
|
} else if (req_val == 0) {
|
|
IOLog("QualcommWifi: Alimentation radio Wi-Fi : COUPEE\n");
|
|
}
|
|
pwr->power_state[0] = 1; // Toujours alimenté
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_COUNTRY_CODE: {
|
|
apple80211_country_code_data *cc = (apple80211_country_code_data *)req_data;
|
|
if (cc) {
|
|
cc->version = APPLE80211_VERSION;
|
|
cc->cc[0] = 'F';
|
|
cc->cc[1] = 'R';
|
|
cc->cc[2] = 0;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_LOCALE: {
|
|
apple80211_locale_data *loc = (apple80211_locale_data *)req_data;
|
|
if (loc) {
|
|
loc->version = APPLE80211_VERSION;
|
|
loc->locale = APPLE80211_LOCALE_ETSI;
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_DRIVER_VERSION:
|
|
case APPLE80211_IOC_HARDWARE_VERSION: {
|
|
apple80211_version_data *vd = (apple80211_version_data *)req_data;
|
|
if (vd) {
|
|
vd->version = APPLE80211_VERSION;
|
|
const char *verStr = "Qualcomm FastConnect 7800 Wi-Fi 7 (v1.1.0)";
|
|
vd->string_len = (u_int16_t)strlen(verStr);
|
|
strlcpy(vd->string, verStr, sizeof(vd->string));
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_ASSOCIATE: {
|
|
apple80211_assoc_data *assoc = (apple80211_assoc_data *)req_data;
|
|
if (assoc) {
|
|
return (SInt32)handleAssociate(assoc);
|
|
}
|
|
break;
|
|
}
|
|
case APPLE80211_IOC_DISASSOCIATE: {
|
|
return (SInt32)handleDisassociate();
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
IOReturn QualcommWifi::setPowerState(unsigned long powerStateOrdinal, IOService *whatDevice) {
|
|
IOLog("QualcommWifi: >>> CALLBACK setPowerState(ordinal=%lu, whatDevice=0x%p) <<<\n",
|
|
powerStateOrdinal, whatDevice);
|
|
return IOPMAckImplied;
|
|
}
|
|
|
|
void QualcommWifi::joinPMtree(IOService *driver) {
|
|
IOLog("QualcommWifi: >>> CALLBACK joinPMtree(driver=0x%p) <<<\n", driver);
|
|
super::joinPMtree(driver);
|
|
}
|
|
|
|
IOReturn QualcommWifi::powerStateWillChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService *whatDevice) {
|
|
IOLog("QualcommWifi: >>> CALLBACK powerStateWillChangeTo(state=%lu, device=0x%p) <<<\n", stateNumber, whatDevice);
|
|
return IOPMAckImplied;
|
|
}
|
|
|
|
IOReturn QualcommWifi::powerStateDidChangeTo(IOPMPowerFlags capabilities, unsigned long stateNumber, IOService *whatDevice) {
|
|
IOLog("QualcommWifi: >>> CALLBACK powerStateDidChangeTo(state=%lu, device=0x%p) <<<\n", stateNumber, whatDevice);
|
|
return IOPMAckImplied;
|
|
}
|
|
|