diff --git a/.gitignore b/.gitignore index 6bc2b1e..3503187 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ xcuserdata/ # Compiled Kexts *.kext/ + +# Knowledge graph / Agent artifacts +graphify-out/ diff --git a/QualcommWifi/QualcommWifi.cpp b/QualcommWifi/QualcommWifi.cpp index 0c92aa8..393053f 100644 --- a/QualcommWifi/QualcommWifi.cpp +++ b/QualcommWifi/QualcommWifi.cpp @@ -17,17 +17,79 @@ extern "C" { #include #include -static char gLogBuffer[2048] = "QCOM_BOOT:"; - -static void logNvramStage(const char *tag) { - size_t curLen = strlen(gLogBuffer); - if (curLen + strlen(tag) + 2 < sizeof(gLogBuffer)) { - strcat(gLogBuffer, ">"); - strcat(gLogBuffer, tag); - } +static inline void logNvramStage(const char *tag) { IOLog("QualcommWifi: >>> [STAGE] %s <<<\n", tag); } +// Classe simulant un FaultReporter IOKit valide (attendu par IO80211Controller::findAndAttachToFaultReporter) +class FakeFaultReporter : public OSObject { + OSDeclareDefaultStructors(FakeFaultReporter) +public: + virtual bool init() override { + return OSObject::init(); + } + static FakeFaultReporter* create() { + FakeFaultReporter *inst = new FakeFaultReporter; + if (inst && !inst->init()) { + inst->release(); + return nullptr; + } + return inst; + } +}; +OSDefineMetaClassAndStructors(FakeFaultReporter, OSObject) + +// Classe de simulation binaire conforme pour CCLogStream (CoreCapture) sous macOS Tahoe +// Évite tout déréférencement invalide dans CCLogStream::shouldLog (offset +0x90 -> +0x58 -> level/flags) +class FakeLogStream : public OSObject { + OSDeclareDefaultStructors(FakeLogStream) +private: + char _pad[0x90 - sizeof(OSObject)]; +public: + void *p1; + void *fP1Mem; + void *fP2Mem; + + virtual bool init() override { + if (!OSObject::init()) return false; + bzero(_pad, sizeof(_pad)); + fP1Mem = IOMallocZero(0x1000); + fP2Mem = IOMallocZero(0x100); + if (fP1Mem && fP2Mem) { + *(void **)((uint8_t *)fP1Mem + 0x58) = fP2Mem; + p1 = fP1Mem; + } else { + p1 = nullptr; + } + return true; + } + + virtual void free() override { + if (fP2Mem) { + IOFree(fP2Mem, 0x100); + fP2Mem = nullptr; + } + if (fP1Mem) { + IOFree(fP1Mem, 0x1000); + fP1Mem = nullptr; + } + p1 = nullptr; + OSObject::free(); + } + + static FakeLogStream* create() { + FakeLogStream *inst = new FakeLogStream; + if (inst && !inst->init()) { + inst->release(); + return nullptr; + } + return inst; + } +}; + +OSDefineMetaClassAndStructors(FakeLogStream, OSObject) +static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90, "p1 must be at offset 0x90 for CCLogStream ABI"); + #define super IO80211Controller OSDefineMetaClassAndStructors(QualcommWifi, IO80211Controller) @@ -64,8 +126,8 @@ bool QualcommWifi::init(OSDictionary *dictionary) { fDmaVirtAddr = nullptr; fDmaPrepared = false; fFirmwareLoaded = false; - fFaultReporter = OSString::withCString("QualcommFaultReporter"); - fLogger = OSString::withCString("QualcommLogger"); + fFaultReporter = FakeFaultReporter::create(); + fLogger = FakeLogStream::create(); return true; } @@ -570,11 +632,9 @@ bool QualcommWifi::start(IOService *provider) { } logNvramStage("pci_open_ok"); - UInt16 pciCommand = fPciDevice->configRead16(kIOPCIConfigCommand); - pciCommand |= kIOPCICommandMemorySpace; - pciCommand &= ~kIOPCICommandBusMaster; // Protection absolue contre tout accès DMA non configuré sur AMD-Vi - logNvramStage("pci_cmd_write"); - fPciDevice->configWrite16(kIOPCIConfigCommand, pciCommand); + // 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)); @@ -760,6 +820,10 @@ void* QualcommWifi::getFaultReporterFromDriver(void) { return (void*)fFaultReporter; } +UInt32 QualcommWifi::getActionFramePoolCapacity(void) { + return 0x100; +} + IOReturn QualcommWifi::getDRIVER_VERSION(IO80211SkywalkInterface *iface, apple80211_version_data *vd) { return (IOReturn)handleApple80211Request(APPLE80211_IOC_DRIVER_VERSION, 0, vd); } diff --git a/QualcommWifi/QualcommWifi.hpp b/QualcommWifi/QualcommWifi.hpp index a5b8194..a6c88d7 100644 --- a/QualcommWifi/QualcommWifi.hpp +++ b/QualcommWifi/QualcommWifi.hpp @@ -257,6 +257,7 @@ public: virtual IOReturn setGET_DEBUG_INFO(IO80211SkywalkInterface *iface, apple80211_debug_command *dbg) override { return kIOReturnSuccess; } virtual void* getLogger(void) const override; virtual void* getFaultReporterFromDriver(void) override; + virtual UInt32 getActionFramePoolCapacity(void) override; // Dispatcher de requêtes Apple80211 / airportd SInt32 handleApple80211Request(unsigned int req_type, int req_val, void* req_data); diff --git a/build_wifi.sh b/build_wifi.sh index bc70787..d96d262 100755 --- a/build_wifi.sh +++ b/build_wifi.sh @@ -24,7 +24,7 @@ clang++ -target x86_64-apple-macos26.5 \ -I"${SCRIPT_DIR}/QualcommWireless/include" \ -I"${SCRIPT_DIR}/QualcommWifi" \ -std=c++17 -fno-builtin -fno-common -mkernel -fno-rtti -fno-exceptions \ - -fno-stack-protector -fno-strict-aliasing -Os \ + -fno-stack-protector -fno-strict-aliasing -Wno-invalid-offsetof -Os \ -DKERNEL -D__IO80211_TARGET=260000 -DAPPLE_KEXT_OVERRIDE=override \ -c "${SCRIPT_DIR}/QualcommWifi/QualcommWifi.cpp" \ -o "${BUILD_DIR}/QualcommWifi.o" diff --git a/docs/ISSUES_AND_POSTMORTEMS.md b/docs/ISSUES_AND_POSTMORTEMS.md index a8db845..a7f2c4d 100644 --- a/docs/ISSUES_AND_POSTMORTEMS.md +++ b/docs/ISSUES_AND_POSTMORTEMS.md @@ -166,3 +166,31 @@ Ce document répertorie de manière exhaustive l'ensemble des anomalies, blocage Par défaut, le noyau XNU sous OpenCore réinitialise le processeur en cas de panique non interceptée. * **Résolution** : Injection de `panic=0` dans les `boot-args` OpenCore du fichier `config.plist`. En cas d'erreur critique, le noyau gèle l'affichage, permettant de lire textuellement le fichier source et la ligne fautive (ex: `IO80211Controller.cpp:2877`). + +--- + +### INC-10 : Triple Fault Processeur ("Coupure de Courant") lors de l'appel `CCLogStream::shouldLog` sur `fLogger` +* **Symptôme** : + Lors du démarrage d'OpenCore avec `QualcommWifi.kext` actif, le PC s'éteint et redémarre instantanément comme une coupure de courant brutale, sans panic log ni rapport d'erreur NVRAM. +* **Cause Racine (Breakthrough Fondamental)** : + 1. Lors du commit `5357ca6`, `getLogger()` avait été implémenté en renvoyant une instance d'`OSString` (`OSString::withCString("QualcommLogger")`). + 2. Lors de l'exécution de `IO80211Controller::start()` -> `createIOReporters()` -> `addReporterLegend()`, le noyau macOS invoque `CCLogStream::shouldLog(this->getLogger(), level)` (`0xffffff80032ff9e6`). + 3. Le désassemblage de `CCLogStream::shouldLog` a révélé : + ```assembly + movq 0x90(%rdi), %rax ; Lit l'offset +0x90 de l'objet passé en rdi (fLogger) + movq 0x58(%rax), %rax ; Déréférence ce pointeur en Ring 0 ! + cmpl 0x8(%rax), %esi ; Compare le niveau de log + setle %al + retq + ``` + 4. L'objet `OSString` ne mesurant que 32 octets (`0x20`), l'accès à `+0x90` pointait sur une adresse mémoire arbitraire ou non mappée dans le heap kernel. Le déréférencement immédiat de cette valeur via `movq 0x58(%rax), %rax` déclenchait une faute de page (`#PF`) ou une protection générale (`#GP`) non interceptée en mode superviseur (Ring 0), provoquant un Triple Fault CPU réinitialisant la carte mère instantanément. + 5. Parallèlement, `IO80211RNGAgent::withOptions()` (`0xffffff80022dd55c`) et `IO80211ControllerMonitor::initWithControllerAndProvider()` (`0xffffff80023929d0`) requièrent impérativement que `getLogger()` retourne un pointeur non-nul et appellent `logger->retain()` (`*0x20(%rax)`). +* **Résolution Technique** : + 1. Conception d'une classe `FakeLogStream : public OSObject` respectant au millimètre l'ABI de `CCLogStream` : + - Taille minimale supérieure à `0x98` octets avec `p1` placé exactement à l'offset `+0x90` (validé par `static_assert(__builtin_offsetof(FakeLogStream, p1) == 0x90)`). + - `p1` pointe vers une zone mémoire `fP1Mem` (`0x1000` octets) dont l'offset `+0x58` pointe vers `fP2Mem` (`0x100` octets initialisés à 0). + - `CCLogStream::shouldLog` et `CCLogStream::log*` lisent ainsi `0x90 -> 0x58 -> 0`, constatent un niveau de log nul, et retournent proprement `false` sans tenter d'émettre sur un pipe inexistant. + - `FakeLogStream` étant un `OSObject`, les appels `retain()` et `release()` effectués par `RNGAgent` s'exécutent avec une fidélité parfaite. + 2. Implémentation locale de `QualcommWifi::getActionFramePoolCapacity()` (slot 440) renvoyant `0x100` (256 frames). + 3. Suppression de l'écriture directe `kIOPCIConfigCommand` sur le bus PCIe AMD pour éviter toute déstabilisation du root port. +