From 18a155e845011ec30dd25e6c0d99cd940af4b076 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 18:08:26 -0800 Subject: [PATCH 01/32] FCA: Add the submodule that provides access to global framebuffer controllers. --- WhateverGreen/kern_igfx.cpp | 12 ++++++++++++ WhateverGreen/kern_igfx.hpp | 26 ++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index bd90421e..836461fa 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -607,6 +607,18 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a return false; } +void IGFX::FramebufferControllerAccessSupport::init() { + // We only need to patch the framebuffer driver + requiresPatchingGraphics = false; + requiresPatchingFramebuffer = true; +} + +void IGFX::FramebufferControllerAccessSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::SolveRequest request("_gController", controllers); + if (!patcher.solveMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "FCA: Failed to resolve the symbol of global controllers."); +} + IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { //DBGLOG("igfx, "pavpCallback: cmd = %d, flag = %d, app = %u, a4 = %s", sessionCommand, flag, sessionAppId, a4 == nullptr ? "null" : "not null"); diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 7d953c1d..311555dd 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -414,6 +414,7 @@ class IGFX { // Populated at AppleIntelFramebufferController::start // Useful for getting access to Read/WriteRegister, rather than having // to compute the offsets + // TODO: DEPRECATED AppleIntelFramebufferController** gFramebufferController {}; // Available on ICL+ @@ -1064,6 +1065,31 @@ class IGFX { */ friend class LSPCON; + /** + * A submodule to provide shared access to global framebuffer controllers + */ + class FramebufferControllerAccessSupport: public PatchSubmodule { + /** + * An array of framebuffer controllers populated by `AppleIntelFramebufferController::start()` + */ + AppleIntelFramebufferController **controllers {}; + + public: + /** + * Get the framebuffer controller at the given index + */ + AppleIntelFramebufferController *getController(size_t index) { return controllers[index]; } + + // MARK: Patch Submodule IMP + void init() override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modFramebufferControllerAccessSupport; + + /** + * [Convenient] Get the default framebuffer controller + */ + AppleIntelFramebufferController *defaultController() { return modFramebufferControllerAccessSupport.getController(0); } + /** * A collection of submodules */ From 5910fe008eddf77e3dc4a5b8eddb3190811e2bcc Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 18:14:40 -0800 Subject: [PATCH 02/32] PatchSubmodule: A submodule can now declare whether it needs access to global framebuffer controllers or not. --- WhateverGreen/kern_igfx.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 311555dd..af8040c1 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -455,20 +455,25 @@ class IGFX { virtual ~PatchSubmodule() = default; /** - * True if this submodule should be enabled + * Set to `true` if this submodule should be enabled */ bool enabled {false}; /** - * True if this submodule requires patching the framebuffer driver + * Set to `true` if this submodule requires patching the framebuffer driver */ bool requiresPatchingFramebuffer {false}; /** - * True if this submodule requires patching the graphics acceleration driver + * Set to `true` if this submodule requires patching the graphics acceleration driver */ bool requiresPatchingGraphics {false}; + /** + * Set to `true` if this submodule requires accessing global framebuffer controllers + */ + bool requiresAccessingToGlobalFramebufferControllers {false}; + /** * Initialize any data structure required by this submodule if necessary */ From e91cbac7c8bfb4cf8b1b1dab9f0cc974e4e00aef Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 18:36:45 -0800 Subject: [PATCH 03/32] MLR: Use the new framebuffer controller access module. --- WhateverGreen/kern_igfx_clock.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WhateverGreen/kern_igfx_clock.cpp b/WhateverGreen/kern_igfx_clock.cpp index 6e1b2add..0a08740d 100644 --- a/WhateverGreen/kern_igfx_clock.cpp +++ b/WhateverGreen/kern_igfx_clock.cpp @@ -98,6 +98,7 @@ void IGFX::DPCDMaxLinkRateFix::init() { // We only need to patch the framebuffer driver requiresPatchingGraphics = false; requiresPatchingFramebuffer = true; + requiresAccessingToGlobalFramebufferControllers = true; } void IGFX::DPCDMaxLinkRateFix::processKernel(KernelPatcher &patcher, DeviceInfo *info) { @@ -268,7 +269,7 @@ IOReturn IGFX::DPCDMaxLinkRateFix::orgReadAUX(uint32_t address, void *buffer, ui } bool IGFX::DPCDMaxLinkRateFix::getFramebufferIndex(uint32_t &index) { - auto fb = port != nullptr ? orgICLGetFBFromPort(*callbackIGFX->gFramebufferController, port) : this->framebuffer; + auto fb = port != nullptr ? orgICLGetFBFromPort(callbackIGFX->defaultController(), port) : this->framebuffer; DBGLOG("igfx", "MLR: [COMM] GetFBIndex() Port at 0x%llx; Framebuffer at 0x%llx.", port, fb); return AppleIntelFramebufferExplorer::getIndex(fb, index); } From a601abe939f2bab0f6407a616e60673c9d73699f Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 21:14:31 -0800 Subject: [PATCH 04/32] PatchSubmodule: A submodule can now declare whether it needs R/W access to MMIO registers. --- WhateverGreen/kern_igfx.hpp | 12 +++++++++++- WhateverGreen/kern_igfx_clock.cpp | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index af8040c1..688cfb82 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -472,7 +472,17 @@ class IGFX { /** * Set to `true` if this submodule requires accessing global framebuffer controllers */ - bool requiresAccessingToGlobalFramebufferControllers {false}; + bool requiresGlobalFramebufferControllersAccess {false}; + + /** + * Set to `true` if this submodules requires read access to MMIO registers + */ + bool requiresMMIORegistersReadAccess {false}; + + /** + * Set to `true` if this submodules requires write access to MMIO registers + */ + bool requiresMMIORegistersWriteAccess {false}; /** * Initialize any data structure required by this submodule if necessary diff --git a/WhateverGreen/kern_igfx_clock.cpp b/WhateverGreen/kern_igfx_clock.cpp index 0a08740d..84e571e0 100644 --- a/WhateverGreen/kern_igfx_clock.cpp +++ b/WhateverGreen/kern_igfx_clock.cpp @@ -98,7 +98,7 @@ void IGFX::DPCDMaxLinkRateFix::init() { // We only need to patch the framebuffer driver requiresPatchingGraphics = false; requiresPatchingFramebuffer = true; - requiresAccessingToGlobalFramebufferControllers = true; + requiresGlobalFramebufferControllersAccess = true; } void IGFX::DPCDMaxLinkRateFix::processKernel(KernelPatcher &patcher, DeviceInfo *info) { From d5ac9ee9807d450822d699284fd4452e6be56512 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 21:20:11 -0800 Subject: [PATCH 05/32] Coordinated Injections: Add the definition of the injection descriptor. --- WhateverGreen/kern_igfx.hpp | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 688cfb82..de69cfc7 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -444,6 +444,50 @@ class IGFX { static void forceWake(void*, uint8_t set, uint32_t dom, uint32_t); } ForceWakeWorkaround; + /** + * Describes how to inject code into a shared submodule + * + * @tparam T Specify the type of the trigger + * @tparam I Specify the type of the function to inject code + * @tparam D Specify the concrete type of the descriptor + * @example The trigger type can be an integer type to inject code based on a register address. + */ + template + struct InjectionDescriptor { + /** + * The trigger value to be monitored by the coordinator + */ + T trigger; + + /** + * A function to invoke when the trigger value is observed + * + * @example One may monitor a specific register address and modify its value in the injector function. + */ + I injector; + + /** + * A pointer to the next descriptor in a linked list + */ + D *next; + + /** + * Create an injection descriptor conveniently + */ + InjectionDescriptor(T t, I i) : + trigger(t), injector(i), next(nullptr) { } + + /** + * Member type `Trigger` is required by the coordinator + */ + using Trigger = T; + + /** + * Member type `Injector` is required by the coordinator + */ + using Injector = I; + }; + /** * Interface of a submodule to fix Intel graphics drivers */ From 2a80e1ffb9d48d04deab19e469c2a165964163a3 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 21:26:14 -0800 Subject: [PATCH 06/32] Coordinated Injections: Add the definition of the descriptor list. --- WhateverGreen/kern_igfx.hpp | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index de69cfc7..61aec468 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -488,6 +488,65 @@ class IGFX { using Injector = I; }; + /** + * Represents a list of injection descriptors + * + * @tparam D Specify the concrete type of the descriptor + */ + template + struct InjectionDescriptorList { + private: + /** + * Head of the list + */ + D *head {nullptr}; + + /** + * Tail of the list + */ + D *tail {nullptr}; + + public: + /** + * Get the injector function associated with the given trigger + * + * @param trigger The trigger value + * @return The injector function on success, `nullptr` if the given trigger is not in the list. + */ + typename D::Injector getInjector(typename D::Trigger trigger) { + for (auto current = head; current != nullptr; current = current->next) + if (current->trigger == trigger) + return current->injector; + + return nullptr; + } + + /** + * Add an injection descriptor to the list + * + * @param descriptor A non-null descriptor that specifies the trigger and the injector function + * @warning Patch developers must ensure that triggers are unique. + * The coordinator registers injections on a first come, first served basis. + * i.e. The latter descriptor will NOT overwrite the injection function of the existing one. + * @note This function assumes that the trigger value of the given descriptor has not been registered yet. + */ + void add(D *descriptor NONNULL) { + // Sanitize garbage value + descriptor->next = nullptr; + + // Append the new descriptor + if (head == nullptr) + // Empty list + head = descriptor; + else + // Non-empty list + tail->next = descriptor; + + // Update the tail + tail = descriptor; + } + }; + /** * Interface of a submodule to fix Intel graphics drivers */ From 012d619ffd2566c37d97860bccb360d1ff957b58 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 21:31:39 -0800 Subject: [PATCH 07/32] Coordinated Injections: Add the definition of the injection coordinator. --- WhateverGreen/kern_igfx.hpp | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 61aec468..86e43c1a 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -547,6 +547,39 @@ class IGFX { } }; + /** + * An injection coordinator is capable of coordinating multiple requests of injection to a shared function + * + * @tparam P Specify the type of the prologue injection descriptor that defines how the coordinator injects code before it calls the original function + * @tparam R Specify the type of the replacer injection descriptor that defines how the coordinator replaces the original function implementation + * @tparam E Specify the type of the epilogue injection descriptor that defines how the coordinator injects code after it calls the original function + * @note Patch submodules inherited from this class get coordination support automatically. + * @note Patch submodules invoke the `add()` method of a list to register injections. + */ + template + class InjectionCoordinator { + public: + /** + * Virtual destructor + */ + virtual ~InjectionCoordinator() = default; + + /** + * A list of prologue injection descriptors + */ + InjectionDescriptorList

prologueList; + + /** + * A list of replacer injection descriptors + */ + InjectionDescriptorList replacerList; + + /** + * A list of epilogue injection descriptors + */ + InjectionDescriptorList epilogueList; + }; + /** * Interface of a submodule to fix Intel graphics drivers */ From b95bac15a0d74be06e884514c4ff36e02ca9cff8 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 22:03:03 -0800 Subject: [PATCH 08/32] PatchSubmodule: Add a new virtual function to disable submodules that depend on this one. --- WhateverGreen/kern_igfx.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 86e43c1a..e92e8f30 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -660,6 +660,11 @@ class IGFX { * @note This function is called when the main IGFX module processes the kext. */ virtual void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) {} + + /** + * Disable submodules that depend on this submodules + */ + virtual void disableDependentSubmodules() {} }; /** From 06964362b63619fd06a4563013c96aafd8266455 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 22:13:10 -0800 Subject: [PATCH 09/32] MREG: Separate shared MMIO registers accesses into submodules. --- WhateverGreen/kern_igfx.cpp | 136 +++++++++++++++++++++++++++++++++++- WhateverGreen/kern_igfx.hpp | 105 ++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 3 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 836461fa..809ece4b 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -607,16 +607,146 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a return false; } +// MARK: - Global Framebuffer Controller Access Support + void IGFX::FramebufferControllerAccessSupport::init() { // We only need to patch the framebuffer driver - requiresPatchingGraphics = false; requiresPatchingFramebuffer = true; } void IGFX::FramebufferControllerAccessSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { KernelPatcher::SolveRequest request("_gController", controllers); - if (!patcher.solveMultiple(index, &request, 1, address, size)) - SYSLOG("igfx", "FCA: Failed to resolve the symbol of global controllers."); + if (!patcher.solveMultiple(index, &request, 1, address, size)) { + SYSLOG("igfx", "FCA: Failed to resolve the symbol of global controllers. Will disable all submodules that rely on this one."); + disableDependentSubmodules(); + } +} + +void IGFX::FramebufferControllerAccessSupport::disableDependentSubmodules() { + for (auto submodule : callbackIGFX->submodules) + if (submodule->requiresGlobalFramebufferControllersAccess) + submodule->enabled = false; +} + +// MARK: - MMIO Registers Read Support + +void IGFX::MMIORegistersReadSupport::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::MMIORegistersReadSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + // Enable the verbose output if the boot argument is found + verbose = checkKernelArgument("-igfxvamregs"); +} + +void IGFX::MMIORegistersReadSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZN31AppleIntelFramebufferController14ReadRegister32Em", + wrapReadRegister32, + orgReadRegister32 + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) { + SYSLOG("igfx", "RRS: Failed to resolve the symbol of ReadRegister32. Will disable all submodules that rely on this one."); + disableDependentSubmodules(); + } +} + +void IGFX::MMIORegistersReadSupport::disableDependentSubmodules() { + for (auto submodule : callbackIGFX->submodules) + if (submodule->requiresMMIORegistersReadAccess) + submodule->enabled = false; +} + +uint32_t IGFX::MMIORegistersReadSupport::wrapReadRegister32(void *controller, uint32_t address) { + // Guard: Perform prologue injections + auto prologueInjector = callbackIGFX->modMMIORegistersReadSupport.prologueList.getInjector(address); + if (prologueInjector) { + DBGLOG("igfx", "RRS: Found a prologue injector triggered by the register address 0x%x.", address); + prologueInjector(controller, address); + } + + // Guard: Perform replacer injections + auto replacerInjector = callbackIGFX->modMMIORegistersReadSupport.replacerList.getInjector(address); + if (replacerInjector) { + DBGLOG("igfx", "RRS: Found a replacer injector triggered by the register value 0x%x.", address); + return replacerInjector(controller, address); + } + + // Invoke the original function + uint32_t retVal = callbackIGFX->modMMIORegistersReadSupport.orgReadRegister32(controller, address); + if (callbackIGFX->modMMIORegistersReadSupport.verbose) + DBGLOG("igfx", "RRS: Read MMIO Register = 0x%x; Value = 0x%x.", address, retVal); + + // Guard: Perform epilogue injections + auto epilogueInjector = callbackIGFX->modMMIORegistersReadSupport.epilogueList.getInjector(address); + if (epilogueInjector) { + DBGLOG("igfx", "RRS: Found a epilogue injector triggered by the register value 0x%x.", address); + return epilogueInjector(controller, address, retVal); + } + + // Return the register value retrieved from the original function + return retVal; +} + +// MARK: - MMIO Registers Write Support + +void IGFX::MMIORegistersWriteSupport::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::MMIORegistersWriteSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + // Enable the verbose output if the boot argument is found + verbose = checkKernelArgument("-igfxvamregs"); +} + +void IGFX::MMIORegistersWriteSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZN31AppleIntelFramebufferController15WriteRegister32Emj", + wrapWriteRegister32, + orgWriteRegister32 + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) { + SYSLOG("igfx", "RWS: Failed to resolve the symbol of WriteRegister32. Will disable all submodules that rely on this one."); + disableDependentSubmodules(); + } +} + +void IGFX::MMIORegistersWriteSupport::disableDependentSubmodules() { + for (auto submodule : callbackIGFX->submodules) + if (submodule->requiresMMIORegistersWriteAccess) + submodule->enabled = false; +} + +void IGFX::MMIORegistersWriteSupport::wrapWriteRegister32(void *controller, uint32_t address, uint32_t value) { + // Guard: Perform prologue injections + auto prologueInjector = callbackIGFX->modMMIORegistersWriteSupport.prologueList.getInjector(address); + if (prologueInjector) { + DBGLOG("igfx", "RRS: Found a prologue injector triggered by the register address 0x%x.", address); + prologueInjector(controller, address, value); + } + + // Guard: Perform replacer injections + auto replacerInjector = callbackIGFX->modMMIORegistersWriteSupport.replacerList.getInjector(address); + if (replacerInjector) { + DBGLOG("igfx", "RRS: Found a replacer injector triggered by the register value 0x%x.", address); + return replacerInjector(controller, address, value); + } + + // Invoke the original function + callbackIGFX->modMMIORegistersWriteSupport.orgWriteRegister32(controller, address, value); + if (callbackIGFX->modMMIORegistersWriteSupport.verbose) + DBGLOG("igfx", "RRS: Write MMIO Register = 0x%x; Value = 0x%x.", address, value); + + // Guard: Perform epilogue injections + auto epilogueInjector = callbackIGFX->modMMIORegistersWriteSupport.epilogueList.getInjector(address); + if (epilogueInjector) { + DBGLOG("igfx", "RRS: Found a epilogue injector triggered by the register value 0x%x.", address); + return epilogueInjector(controller, address, value); + } } IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index e92e8f30..8e3eb953 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -1239,6 +1239,7 @@ class IGFX { // MARK: Patch Submodule IMP void init() override; void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; } modFramebufferControllerAccessSupport; /** @@ -1246,6 +1247,110 @@ class IGFX { */ AppleIntelFramebufferController *defaultController() { return modFramebufferControllerAccessSupport.getController(0); } + /** + * Defines the prologue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and returns void. + * @note The injection is performed before the original function is invoked. + */ + struct MMIOReadPrologue: InjectionDescriptor {}; + + /** + * Defines the replacer injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and returns the register value. + * @note The injection replaced the original function call. + */ + struct MMIOReadReplacer: InjectionDescriptor {}; + + /** + * Defines the epilogue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and its value, and returns the new value. + * @note The injection is performed after the original function is invoked. + */ + struct MMIOReadEpilogue: InjectionDescriptor {}; + + /** + * A submodule that provides read access to MMIO registers and coordinates injections to the read function + */ + class MMIORegistersReadSupport: public PatchSubmodule, public InjectionCoordinator { + /** + * Set to `true` to print detailed register access information + */ + bool verbose; + + public: + /** + * Original AppleIntelFramebufferController::ReadRegister32 function + * + * @note Other submodules may use this function pointer to skip injected code. + */ + uint32_t (*orgReadRegister32)(void *, uint32_t) {nullptr}; + + /** + * Wrapper for the AppleIntelFramebufferController::ReadRegister32 function + * + * @param controller The implicit controller instance + * @param address The register address + * @return The register value. + * @note This wrapper function monitors the register address and invokes registered injectors. + */ + static uint32_t wrapReadRegister32(void *controller, uint32_t address); + + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; + } modMMIORegistersReadSupport; + + /** + * Defines the injection descriptor for `AppleIntelFramebufferController::WriteRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and its new value, and returns void. + * @note This type is shared by all three kinds of injection descriptors. + */ + struct MMIOWriteInjectionDescriptor: InjectionDescriptor {}; + + /** + * A submodule that provides write access to MMIO registers and coordinates injections to the write function + */ + class MMIORegistersWriteSupport: public PatchSubmodule, public InjectionCoordinator { + /** + * Set to `true` to print detailed register access information + */ + bool verbose; + + public: + /** + * Original AppleIntelFramebufferController::WriteRegister32 function + * + * @note Other submodules may use this function pointer to skip injected code. + */ + void (*orgWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; + + /** + * Wrapper for the AppleIntelFramebufferController::WriteRegister32 function + * + * @param controller The implicit controller instance + * @param address The register address + * @param value The new register value + * @note This wrapper function monitors the register address and invokes registered injectors. + */ + static void wrapWriteRegister32(void *controller, uint32_t address, uint32_t value); + + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; + } modMMIORegistersWriteSupport; + /** * A collection of submodules */ From e81cddc6ea12d2b73fdbdd1731dc117975b62f64 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 22:59:30 -0800 Subject: [PATCH 10/32] Shared Submodules: Add convenient shared helpers. --- WhateverGreen/kern_igfx.hpp | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 8e3eb953..e523e133 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -1221,6 +1221,10 @@ class IGFX { */ friend class LSPCON; + // + // MARK: Shared Submodules + // + /** * A submodule to provide shared access to global framebuffer controllers */ @@ -1242,11 +1246,6 @@ class IGFX { void disableDependentSubmodules() override; } modFramebufferControllerAccessSupport; - /** - * [Convenient] Get the default framebuffer controller - */ - AppleIntelFramebufferController *defaultController() { return modFramebufferControllerAccessSupport.getController(0); } - /** * Defines the prologue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` * @@ -1351,6 +1350,33 @@ class IGFX { void disableDependentSubmodules() override; } modMMIORegistersWriteSupport; + /** + * [Convenient] Get the default framebuffer controller + */ + AppleIntelFramebufferController *defaultController() { return modFramebufferControllerAccessSupport.getController(0); } + + /** + * [Convenient] Invoke the original AppleIntelFramebufferController::ReadRegister32 function + * + * @param controller The framebuffer controller instance + * @param address The register address + * @return The register value. + */ + uint32_t readRegister32(void *controller, uint32_t address) { + return modMMIORegistersReadSupport.orgReadRegister32(controller, address); + } + + /** + * [Convenient] Invoke the original AppleIntelFramebufferController::WriteRegister32 function + * + * @param controller The framebuffer controller instance + * @param address The register address + * @param value The new register value + */ + void writeRegister32(void *controller, uint32_t address, uint32_t value) { + modMMIORegistersWriteSupport.orgWriteRegister32(controller, address, value); + } + /** * A collection of submodules */ From 94b77fa6e03d49b76f306099362215647fb210e2 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 23:33:28 -0800 Subject: [PATCH 11/32] RPS: Convert RPS control patch into a submodule. --- WhateverGreen/kern_igfx.cpp | 29 ++--- WhateverGreen/kern_igfx.hpp | 38 ++++--- WhateverGreen/kern_igfx_pm.cpp | 188 ++++++++++++++++++--------------- 3 files changed, 134 insertions(+), 121 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 809ece4b..5ea6a06e 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -109,7 +109,7 @@ void IGFX::init() { currentGraphics = &kextIntelKBL; currentFramebuffer = &kextIntelKBLFb; forceCompleteModeset.supported = forceCompleteModeset.enable = true; - RPSControl.available = true; + modRPSControlPatch.available = true; ForceWakeWorkaround.enabled = true; disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; break; @@ -123,7 +123,7 @@ void IGFX::init() { // configuration, supposedly due to Apple not supporting new MOCS table and forcing Skylake-based format. // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 forceCompleteModeset.supported = forceCompleteModeset.enable = true; - RPSControl.available = true; + modRPSControlPatch.available = true; ForceWakeWorkaround.enabled = true; disableTypeCCheck = true; break; @@ -152,7 +152,7 @@ void IGFX::init() { // configuration, supposedly due to Apple not supporting new MOCS table and forcing Skylake-based format. // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 forceCompleteModeset.supported = forceCompleteModeset.enable = true; - RPSControl.available = true; + modRPSControlPatch.available = true; disableTypeCCheck = true; break; default: @@ -191,13 +191,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { dumpPlatformTable = checkKernelArgument("-igfxfbdump"); debugFramebuffer = checkKernelArgument("-igfxfbdbg"); #endif - - uint32_t rpsc = 0; - if (PE_parse_boot_argn("igfxrpsc", &rpsc, sizeof(rpsc)) || - WIOKit::getOSDataValue(info->videoBuiltin, "rps-control", rpsc)) { - RPSControl.enabled = rpsc > 0 && RPSControl.available; - DBGLOG("weg", "RPS control patch overriden (%u) availabile %d", rpsc, RPSControl.available); - } uint32_t forceCompleteModeSet = 0; if (PE_parse_boot_argn("igfxfcms", &forceCompleteModeSet, sizeof(forceCompleteModeSet))) { @@ -334,8 +327,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (disableAGDC) return true; - if (RPSControl.enabled) - return true; if (ForceWakeWorkaround.enabled) return true; if (disableTypeCCheck) @@ -356,8 +347,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (readDescriptorPatch) return true; - if (RPSControl.enabled) - return true; if (disableAccel) return true; return false; @@ -413,9 +402,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a KernelPatcher::RouteRequest request("__ZNK25IGHardwareGlobalPageTable4readEyRyS0_", globalPageTableRead); patcher.routeMultiple(index, &request, 1, address, size); } - - if (RPSControl.enabled) - RPSControl.initGraphics(patcher, index, address, size); if (ForceWakeWorkaround.enabled) ForceWakeWorkaround.initGraphics(*this, patcher, index, address, size); @@ -442,21 +428,21 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a // Also we need to consider the case where multiple submodules want to inject code into these functions. // At this moment, the backlight fix is the only one that wraps these functions. if (bklCoffeeFb || bklKabyFb || - RPSControl.enabled || ForceWakeWorkaround.enabled || modCoreDisplayClockFix.enabled) { + /*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled || modCoreDisplayClockFix.enabled) { AppleIntelFramebufferController__ReadRegister32 = patcher.solveSymbol (index, "__ZN31AppleIntelFramebufferController14ReadRegister32Em", address, size); if (!AppleIntelFramebufferController__ReadRegister32) SYSLOG("igfx", "Failed to find ReadRegister32"); } if (bklCoffeeFb || bklKabyFb || - RPSControl.enabled || ForceWakeWorkaround.enabled) { + /*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled) { AppleIntelFramebufferController__WriteRegister32 = patcher.solveSymbol (index, "__ZN31AppleIntelFramebufferController15WriteRegister32Emj", address, size); if (!AppleIntelFramebufferController__WriteRegister32) SYSLOG("igfx", "Failed to find WriteRegister32"); } // FIXME: Same issue here. - if (RPSControl.enabled || ForceWakeWorkaround.enabled || modDPCDMaxLinkRateFix.enabled) + if (/*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled || modDPCDMaxLinkRateFix.enabled) gFramebufferController = patcher.solveSymbol(index, "_gController", address, size); if (bklCoffeeFb || bklKabyFb) { // Intel backlight is modeled via pulse-width modulation (PWM). See page 144 of: @@ -534,9 +520,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (!patcher.routeMultiple(index, &req, 1, address, size)) SYSLOG("igfx", "failed to route IsTypeCOnlySystem"); } - - if (RPSControl.enabled) - RPSControl.initFB(*this, patcher, index, address, size); if (disableAGDC) { KernelPatcher::RouteRequest request {"__ZN20IntelFBClientControl11doAttributeEjPmmS0_S0_P25IOExternalMethodArguments", wrapFBClientDoAttribute, orgFBClientDoAttribute}; diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index e523e133..2af7c7ff 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -421,18 +421,6 @@ class IGFX { // Apple has refactored quite a large amount of code into a new class `AppleIntelPort` in the ICL graphics driver, // and the framebuffer controller now maintains an array of `ports`. class AppleIntelPort; - - struct RPSControl { - bool available {false}; - bool enabled {false}; - uint32_t freq_max {0}; - - void initFB(IGFX&,KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size); - void initGraphics(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size); - - static int pmNotifyWrapper(unsigned int,unsigned int,unsigned long long *,unsigned int *); - mach_vm_address_t orgPmNotifyWrapper; - } RPSControl; struct ForceWakeWorkaround { bool enabled {false}; @@ -1221,6 +1209,28 @@ class IGFX { */ friend class LSPCON; + /** + * A submodule that patches RPS control for all command streamers + */ + class RPSControlPatch: public PatchSubmodule { + uint32_t freq_max {0}; + bool patchRCSCheck(mach_vm_address_t& start); + int (*orgPmNotifyWrapper)(unsigned int, unsigned int, unsigned long long *, unsigned int *) {nullptr}; + static int wrapPmNotifyWrapper(unsigned int, unsigned int, unsigned long long *, unsigned int *); + + public: + /** + * True if this fix is available for the current Intel platform + */ + bool available {false}; + + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modRPSControlPatch; + // // MARK: Shared Submodules // @@ -1353,7 +1363,9 @@ class IGFX { /** * [Convenient] Get the default framebuffer controller */ - AppleIntelFramebufferController *defaultController() { return modFramebufferControllerAccessSupport.getController(0); } + AppleIntelFramebufferController *defaultController() { + return modFramebufferControllerAccessSupport.getController(0); + } /** * [Convenient] Invoke the original AppleIntelFramebufferController::ReadRegister32 function diff --git a/WhateverGreen/kern_igfx_pm.cpp b/WhateverGreen/kern_igfx_pm.cpp index eca57586..a7f912c5 100644 --- a/WhateverGreen/kern_igfx_pm.cpp +++ b/WhateverGreen/kern_igfx_pm.cpp @@ -52,52 +52,6 @@ struct [[gnu::packed]] IGHwCsDesc { char unk1[12]; }; -static bool patchRCSCheck(mach_vm_address_t& start) { - constexpr unsigned ninsts_max {256}; - - hde64s dis; - - bool found_cmp = false; - bool found_jmp = false; - - for (size_t i = 0; i < ninsts_max; i++) { - auto sz = Disassembler::hdeDisasm(start, &dis); - - if (dis.flags & F_ERROR) { - SYSLOG(log, "Error disassembling submitExecList"); - break; - } - - /* cmp byte ptr [rcx], 0 */ - if (!found_cmp && dis.opcode == 0x80 && dis.modrm_reg == 7 && dis.modrm_rm == 1) - found_cmp = true; - /* jnz rel32 */ - if (found_cmp && dis.opcode == 0x0f && dis.opcode2 == 0x85) { - found_jmp = true; - break; - } - - start += sz; - } - - if (found_jmp) { - auto status = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); - if (status == KERN_SUCCESS) { - constexpr uint8_t nop6[] {0x90, 0x90, 0x90, 0x90, 0x90, 0x90}; - lilu_os_memcpy(reinterpret_cast(start), nop6, arrsize(nop6)); - MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); - DBGLOG(log, "Patched submitExecList"); - return true; - } else { - DBGLOG(log, "Failed to set kernel writing"); - return false; - } - } else { - SYSLOG(log, "jnz in submitExecList not found"); - return false; - } -} - constexpr uint32_t MCHBAR_MIRROR_BASE_SNB = 0x140000; constexpr uint32_t GEN6_RP_STATE_CAP = MCHBAR_MIRROR_BASE_SNB + 0x5998; @@ -169,59 +123,123 @@ constexpr uint32_t fw_clear(uint32_t v) { } } -void IGFX::RPSControl::initGraphics(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { - mach_vm_address_t orgIGHardwareCommandStreamer2__submitExecList {}; - const char* sym = getKernelVersion() >= KernelVersion::Catalina ? - "__ZN26IGHardwareCommandStreamer514submitExecListEj" : "__ZN26IGHardwareCommandStreamer214submitExecListEj"; - orgIGHardwareCommandStreamer2__submitExecList = patcher.solveSymbol(index, sym, address, size); - - /** - * IGHardwareCommandStreamer2::submitExecList only controls RPS for RCS type streamers. - * Patch it to enable control for any kind of streamer. - */ - if (orgIGHardwareCommandStreamer2__submitExecList) { - mach_vm_address_t start = orgIGHardwareCommandStreamer2__submitExecList; - patchRCSCheck(start); - // The second patch is to get to patchFrequencyRequest (unused for now) -// patchRCSCheck(start); - } else { - SYSLOG(log, "Failed to solve submitExecList (%d)", patcher.getError()); - patcher.clearError(); +// MARK: - RPS Control Patch + +void IGFX::RPSControlPatch::init() { + // We need to patch both drivers + requiresPatchingGraphics = true; + requiresPatchingFramebuffer = true; + + // Requires access to global framebuffer controllers + requiresGlobalFramebufferControllersAccess = true; + + // Requires read access to MMIO registers + requiresMMIORegistersReadAccess = true; +} + +void IGFX::RPSControlPatch::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + uint32_t rpsc = 0; + if (PE_parse_boot_argn("igfxrpsc", &rpsc, sizeof(rpsc)) || + WIOKit::getOSDataValue(info->videoBuiltin, "rps-control", rpsc)) { + enabled = rpsc > 0 && available; + DBGLOG("weg", "RPS control patch overriden (%u) availabile %d", rpsc, available); } } -/** - * Request maximum RPS at exec list submission. - * While this sounds dangerous, we are still getting proper power management due to - * force wake clears. - */ -int IGFX::RPSControl::pmNotifyWrapper(unsigned int a0,unsigned int a1,unsigned long long * a2,unsigned int * freq) { - uint32_t cfreq = 0; +void IGFX::RPSControlPatch::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest routeRequest = { + "__ZL15pmNotifyWrapperjjPyPj", + wrapPmNotifyWrapper, + orgPmNotifyWrapper + }; + + if (!patcher.routeMultiple(index, &routeRequest, 1, address, size)) + SYSLOG(log, "Failed to route pmNotifyWrapper."); +} - FunctionCast(IGFX::RPSControl::pmNotifyWrapper, callbackIGFX->RPSControl.orgPmNotifyWrapper)(a0, a1, a2, &cfreq); +void IGFX::RPSControlPatch::processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + // Address of `IGHardwareCommandStreamer2::submitExecList` + mach_vm_address_t orgSubmitExecList; + + KernelPatcher::SolveRequest request = { + getKernelVersion() >= KernelVersion::Catalina ? "__ZN26IGHardwareCommandStreamer514submitExecListEj" : "__ZN26IGHardwareCommandStreamer214submitExecListEj", + orgSubmitExecList + }; - if (!callbackIGFX->RPSControl.freq_max) { - callbackIGFX->RPSControl.freq_max = callbackIGFX->AppleIntelFramebufferController__ReadRegister32(*callbackIGFX->gFramebufferController, GEN6_RP_STATE_CAP) & 0xff; - DBGLOG(log, "Read RP0 %d", callbackIGFX->RPSControl.freq_max); + if (!patcher.solveMultiple(index, &request, 1, address, size)) { + SYSLOG(log, "Failed to solve the symbol for submitExecList."); + return; } -// DBGLOG(log, "pmNotifyWrapper sets freq 0x%x", cfreq); - *freq = (GEN9_FREQ_SCALER << GEN9_FREQUENCY_SHIFT) * callbackIGFX->RPSControl.freq_max; + // `submitExecList()` only controls RPS for RCS type streamers + // Patch it to enable control for any kind of streamer + if (!patchRCSCheck(orgSubmitExecList)) + SYSLOG(log, "Failed to patch RCS check."); +} +int IGFX::RPSControlPatch::wrapPmNotifyWrapper(unsigned int a0, unsigned int a1, unsigned long long *a2, unsigned int *freq) { + // Request the maximum RPS at exec list submission + // While this sounds dangerous, we are still getting proper power management due to force wake clears. + uint32_t cfreq = 0; + callbackIGFX->modRPSControlPatch.orgPmNotifyWrapper(a0, a1, a2, &cfreq); + + if (!callbackIGFX->modRPSControlPatch.freq_max) { + callbackIGFX->modRPSControlPatch.freq_max = callbackIGFX->readRegister32(callbackIGFX->defaultController(), GEN6_RP_STATE_CAP) & 0xFF; + DBGLOG("log", "Read RP0 %d", callbackIGFX->modRPSControlPatch.freq_max); + } + + *freq = (GEN9_FREQ_SCALER << GEN9_FREQUENCY_SHIFT) * callbackIGFX->modRPSControlPatch.freq_max; return 0; } -void IGFX::RPSControl::initFB(IGFX& ig,KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { - KernelPatcher::RouteRequest req { - "__ZL15pmNotifyWrapperjjPyPj", - &IGFX::RPSControl::pmNotifyWrapper, - orgPmNotifyWrapper - }; +bool IGFX::RPSControlPatch::patchRCSCheck(mach_vm_address_t& start) { + constexpr unsigned ninsts_max {256}; + + hde64s dis; + + bool found_cmp = false; + bool found_jmp = false; + + for (size_t i = 0; i < ninsts_max; i++) { + auto sz = Disassembler::hdeDisasm(start, &dis); + + if (dis.flags & F_ERROR) { + SYSLOG(log, "Error disassembling submitExecList"); + break; + } + + /* cmp byte ptr [rcx], 0 */ + if (!found_cmp && dis.opcode == 0x80 && dis.modrm_reg == 7 && dis.modrm_rm == 1) + found_cmp = true; + /* jnz rel32 */ + if (found_cmp && dis.opcode == 0x0f && dis.opcode2 == 0x85) { + found_jmp = true; + break; + } - if (!(ig.AppleIntelFramebufferController__ReadRegister32 && ig.gFramebufferController && patcher.routeMultiple(index, &req, 1, address, size, true, true))) - SYSLOG(log, "failed to route igfx FB PM functions"); + start += sz; + } + + if (found_jmp) { + auto status = MachInfo::setKernelWriting(true, KernelPatcher::kernelWriteLock); + if (status == KERN_SUCCESS) { + constexpr uint8_t nop6[] {0x90, 0x90, 0x90, 0x90, 0x90, 0x90}; + lilu_os_memcpy(reinterpret_cast(start), nop6, arrsize(nop6)); + MachInfo::setKernelWriting(false, KernelPatcher::kernelWriteLock); + DBGLOG(log, "Patched submitExecList"); + return true; + } else { + DBGLOG(log, "Failed to set kernel writing"); + return false; + } + } else { + SYSLOG(log, "jnz in submitExecList not found"); + return false; + } } +// MARK: - Force Wake Workaround + bool IGFX::ForceWakeWorkaround::pollRegister(uint32_t reg, uint32_t val, uint32_t mask, uint32_t timeout) { AbsoluteTime now, deadline; From 3d95f36ef1ec2d5ec55779e248c257f4af663122 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 23:51:34 -0800 Subject: [PATCH 12/32] FWW: Convert the force wake workaround into a submodule. --- WhateverGreen/kern_igfx.cpp | 21 +++++++--------- WhateverGreen/kern_igfx.hpp | 24 +++++++++++-------- WhateverGreen/kern_igfx_pm.cpp | 44 ++++++++++++++++++---------------- 3 files changed, 46 insertions(+), 43 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 5ea6a06e..02b8ada5 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -110,7 +110,7 @@ void IGFX::init() { currentFramebuffer = &kextIntelKBLFb; forceCompleteModeset.supported = forceCompleteModeset.enable = true; modRPSControlPatch.available = true; - ForceWakeWorkaround.enabled = true; + modForceWakeWorkaround.enabled = true; disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; break; case CPUInfo::CpuGeneration::CoffeeLake: @@ -124,7 +124,7 @@ void IGFX::init() { // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 forceCompleteModeset.supported = forceCompleteModeset.enable = true; modRPSControlPatch.available = true; - ForceWakeWorkaround.enabled = true; + modForceWakeWorkaround.enabled = true; disableTypeCCheck = true; break; case CPUInfo::CpuGeneration::CannonLake: @@ -327,8 +327,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (disableAGDC) return true; - if (ForceWakeWorkaround.enabled) - return true; if (disableTypeCCheck) return true; return false; @@ -403,9 +401,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a patcher.routeMultiple(index, &request, 1, address, size); } - if (ForceWakeWorkaround.enabled) - ForceWakeWorkaround.initGraphics(*this, patcher, index, address, size); - // Iterate through each submodule and redirect the request if and only if the submodule is enabled for (auto submodule : submodules) if (submodule->enabled) @@ -428,21 +423,21 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a // Also we need to consider the case where multiple submodules want to inject code into these functions. // At this moment, the backlight fix is the only one that wraps these functions. if (bklCoffeeFb || bklKabyFb || - /*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled || modCoreDisplayClockFix.enabled) { + /*RPSControl.enabled || ForceWakeWorkaround.enabled || */modCoreDisplayClockFix.enabled) { AppleIntelFramebufferController__ReadRegister32 = patcher.solveSymbol (index, "__ZN31AppleIntelFramebufferController14ReadRegister32Em", address, size); if (!AppleIntelFramebufferController__ReadRegister32) SYSLOG("igfx", "Failed to find ReadRegister32"); } - if (bklCoffeeFb || bklKabyFb || - /*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled) { + if (bklCoffeeFb || bklKabyFb //|| + /*RPSControl.enabled || ForceWakeWorkaround.enabled*/) { AppleIntelFramebufferController__WriteRegister32 = patcher.solveSymbol (index, "__ZN31AppleIntelFramebufferController15WriteRegister32Emj", address, size); if (!AppleIntelFramebufferController__WriteRegister32) SYSLOG("igfx", "Failed to find WriteRegister32"); } // FIXME: Same issue here. - if (/*RPSControl.enabled ||*/ ForceWakeWorkaround.enabled || modDPCDMaxLinkRateFix.enabled) + if (/*RPSControl.enabled || ForceWakeWorkaround.enabled || */modDPCDMaxLinkRateFix.enabled) gFramebufferController = patcher.solveSymbol(index, "_gController", address, size); if (bklCoffeeFb || bklKabyFb) { // Intel backlight is modeled via pulse-width modulation (PWM). See page 144 of: @@ -858,7 +853,7 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { OSDictionary* developmentDictCpy {}; - if (callbackIGFX->fwLoadMode != FW_APPLE || callbackIGFX->ForceWakeWorkaround.enabled) { + if (callbackIGFX->fwLoadMode != FW_APPLE || callbackIGFX->modForceWakeWorkaround.enabled) { auto developmentDict = OSDynamicCast(OSDictionary, that->getProperty("Development")); if (developmentDict) { auto c = developmentDict->copyCollection(); @@ -899,7 +894,7 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { // 0: Framebuffer's SafeForceWake // 1: IntelAccelerator::SafeForceWakeMultithreaded (or ForceWakeWorkaround when enabled) // The default is 1. Forcing 0 will result in hangs (due to misbalanced number of calls?) - if (callbackIGFX->ForceWakeWorkaround.enabled && developmentDictCpy) { + if (callbackIGFX->modForceWakeWorkaround.enabled && developmentDictCpy) { auto num = OSNumber::withNumber(1ull, 32); if (num) { developmentDictCpy->setObject("MultiForceWakeSelect", num); diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 2af7c7ff..bdd645dc 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -422,16 +422,6 @@ class IGFX { // and the framebuffer controller now maintains an array of `ports`. class AppleIntelPort; - struct ForceWakeWorkaround { - bool enabled {false}; - - void initGraphics(IGFX&,KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size); - - static bool pollRegister(uint32_t, uint32_t, uint32_t, uint32_t); - static bool forceWakeWaitAckFallback(uint32_t, uint32_t, uint32_t); - static void forceWake(void*, uint8_t set, uint32_t dom, uint32_t); - } ForceWakeWorkaround; - /** * Describes how to inject code into a shared submodule * @@ -1231,6 +1221,20 @@ class IGFX { void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modRPSControlPatch; + /** + * A submodule that fixes the kernel panic due to a rare force wake timeout on KBL and CFL platforms. + */ + class ForceWakeWorkaround: public PatchSubmodule { + static bool pollRegister(uint32_t, uint32_t, uint32_t, uint32_t); + static bool forceWakeWaitAckFallback(uint32_t, uint32_t, uint32_t); + static void forceWake(void *, uint8_t set, uint32_t dom, uint32_t); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modForceWakeWorkaround; + // // MARK: Shared Submodules // diff --git a/WhateverGreen/kern_igfx_pm.cpp b/WhateverGreen/kern_igfx_pm.cpp index a7f912c5..ad8d4837 100644 --- a/WhateverGreen/kern_igfx_pm.cpp +++ b/WhateverGreen/kern_igfx_pm.cpp @@ -246,7 +246,7 @@ bool IGFX::ForceWakeWorkaround::pollRegister(uint32_t reg, uint32_t val, uint32_ clock_interval_to_deadline(timeout, kMillisecondScale, &deadline); for (clock_get_uptime(&now); now < deadline; clock_get_uptime(&now)) { - auto rd = callbackIGFX->AppleIntelFramebufferController__ReadRegister32(*callbackIGFX->gFramebufferController, reg); + auto rd = callbackIGFX->readRegister32(callbackIGFX->defaultController(), reg); // DBGLOG(log, "Rd 0x%x = 0x%x, expected 0x%x", reg, rd, val); @@ -260,21 +260,18 @@ bool IGFX::ForceWakeWorkaround::pollRegister(uint32_t reg, uint32_t val, uint32_ bool IGFX::ForceWakeWorkaround::forceWakeWaitAckFallback(uint32_t d, uint32_t val, uint32_t mask) { unsigned pass = 1; bool ack = false; + auto controller = callbackIGFX->defaultController(); do { pollRegister(ackForDom(d), 0, FORCEWAKE_KERNEL_FALLBACK, FORCEWAKE_ACK_TIMEOUT_MS); - - callbackIGFX->AppleIntelFramebufferController__WriteRegister32(*callbackIGFX->gFramebufferController, - regForDom(d), fw_set(FORCEWAKE_KERNEL_FALLBACK)); + callbackIGFX->writeRegister32(controller, regForDom(d), fw_set(FORCEWAKE_KERNEL_FALLBACK)); IODelay(10 * pass); pollRegister(ackForDom(d), FORCEWAKE_KERNEL_FALLBACK, FORCEWAKE_KERNEL_FALLBACK, FORCEWAKE_ACK_TIMEOUT_MS); - ack = (callbackIGFX->AppleIntelFramebufferController__ReadRegister32(*callbackIGFX->gFramebufferController, - ackForDom(d)) & mask) == val; + ack = (callbackIGFX->readRegister32(controller, ackForDom(d)) & mask) == val; - callbackIGFX->AppleIntelFramebufferController__WriteRegister32(*callbackIGFX->gFramebufferController, - regForDom(d), fw_clear(FORCEWAKE_KERNEL_FALLBACK)); + callbackIGFX->writeRegister32(controller, regForDom(d), fw_clear(FORCEWAKE_KERNEL_FALLBACK)); } while (!ack && pass++ < 10); // DBGLOG(log, "Force wake fallback used to %s %s in %u passes", set ? "set" : "clear", strForDom(d), pass); @@ -291,9 +288,6 @@ bool IGFX::ForceWakeWorkaround::forceWakeWaitAckFallback(uint32_t d, uint32_t va // NOTE: We are either in IRQ context, or in a spinlock critical section void IGFX::ForceWakeWorkaround::forceWake(void*, uint8_t set, uint32_t dom, uint32_t ctx) { - assert(callbackIGFX->gFramebufferController && *callbackIGFX->gFramebufferController); -// DBGLOG(log, "ForceWake %u %u", set, dom); - // ctx 2: IRQ, 1: normal uint32_t ack_exp = set << ctx; @@ -302,8 +296,7 @@ void IGFX::ForceWakeWorkaround::forceWake(void*, uint8_t set, uint32_t dom, uint for (unsigned d = DOM_FIRST; d <= DOM_LAST; d <<= 1) if (dom & d) { - callbackIGFX->AppleIntelFramebufferController__WriteRegister32(*callbackIGFX->gFramebufferController, - regForDom(d), wr); + callbackIGFX->writeRegister32(callbackIGFX->defaultController(), regForDom(d), wr); IOPause(100); if (!pollRegister(ackForDom(d), ack_exp, mask, FORCEWAKE_ACK_TIMEOUT_MS) && !forceWakeWaitAckFallback(d, ack_exp, mask) && @@ -312,13 +305,24 @@ void IGFX::ForceWakeWorkaround::forceWake(void*, uint8_t set, uint32_t dom, uint } } -void IGFX::ForceWakeWorkaround::initGraphics(IGFX& ig, KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +void IGFX::ForceWakeWorkaround::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; + + // Requires access to global framebuffer controllers + requiresGlobalFramebufferControllersAccess = true; + + // Requires read and write access to MMIO registers + requiresMMIORegistersReadAccess = true; + requiresMMIORegistersWriteAccess = true; +} - KernelPatcher::RouteRequest req { - "__ZN16IntelAccelerator26SafeForceWakeMultithreadedEbjj", - &IGFX::ForceWakeWorkaround::forceWake +void IGFX::ForceWakeWorkaround::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZN16IntelAccelerator26SafeForceWakeMultithreadedEbjj", + forceWake }; - if (!(ig.AppleIntelFramebufferController__ReadRegister32 && ig.gFramebufferController && - patcher.routeMultiple(index, &req, 1, address, size, true, true))) - SYSLOG(log, "Failed to route SafeForceWake"); + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "Failed to route SafeForceWake."); } From e34f60742b44c93113edd6298bbf255caf57c959 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 23:55:22 -0800 Subject: [PATCH 13/32] CDC: Use the new MMIO registers submodule. --- WhateverGreen/kern_igfx.hpp | 9 --------- WhateverGreen/kern_igfx_clock.cpp | 14 +++++++------- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index bdd645dc..dd111a11 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -838,15 +838,6 @@ class IGFX { * A submodule to support all valid Core Display Clock frequencies on ICL+ platforms */ class CoreDisplayClockFix: public PatchSubmodule { - /** - * [ICL+] Original AppleIntelFramebufferController::ReadRegister32 function - * - * @param that The implicit hidden framebuffer controller instance - * @param address Address of the MMIO register - * @return The 32-bit integer read from the register. - */ - uint32_t (*orgIclReadRegister32)(AppleIntelFramebufferController *, uint32_t) {nullptr}; - /** * [ICL+] Original AppleIntelFramebufferController::probeCDClockFrequency function * diff --git a/WhateverGreen/kern_igfx_clock.cpp b/WhateverGreen/kern_igfx_clock.cpp index 84e571e0..299a48a2 100644 --- a/WhateverGreen/kern_igfx_clock.cpp +++ b/WhateverGreen/kern_igfx_clock.cpp @@ -474,6 +474,9 @@ void IGFX::CoreDisplayClockFix::init() { // We only need to patch the framebuffer driver requiresPatchingGraphics = false; requiresPatchingFramebuffer = true; + + // Requires read access to MMIO registers + requiresMMIORegistersReadAccess = true; } void IGFX::CoreDisplayClockFix::processKernel(KernelPatcher &patcher, DeviceInfo *info) { @@ -496,11 +499,8 @@ void IGFX::CoreDisplayClockFix::processFramebufferKext(KernelPatcher &patcher, s {"__ZN31AppleIntelFramebufferController19setCDClockFrequencyEy", orgSetCDClockFrequency} }; - orgIclReadRegister32 = reinterpret_cast(callbackIGFX->AppleIntelFramebufferController__ReadRegister32); - if (patcher.routeMultiple(index, &routeRequest, 1, address, size) && - patcher.solveMultiple(index, solveRequests, address, size) && - orgIclReadRegister32) + patcher.solveMultiple(index, solveRequests, address, size)) DBGLOG("igfx", "CDC: Functions have been routed successfully."); else SYSLOG("igfx", "CDC: Failed to route functions."); @@ -509,7 +509,7 @@ void IGFX::CoreDisplayClockFix::processFramebufferKext(KernelPatcher &patcher, s void IGFX::CoreDisplayClockFix::sanitizeCDClockFrequency(AppleIntelFramebufferController *that) { // Read the hardware reference frequency from the DSSM register // Bits 29-31 store the reference frequency value - auto referenceFrequency = callbackIGFX->modCoreDisplayClockFix.orgIclReadRegister32(that, ICL_REG_DSSM) >> 29; + auto referenceFrequency = callbackIGFX->readRegister32(that, ICL_REG_DSSM) >> 29; // Frequency of Core Display Clock PLL is determined by the reference frequency uint32_t newCdclkFrequency = 0; @@ -552,7 +552,7 @@ void IGFX::CoreDisplayClockFix::sanitizeCDClockFrequency(AppleIntelFramebufferCo DBGLOG("igfx", "CDC: sanitizeCDClockFrequency() DInfo: Core Display Clock has been reprogrammed and PLL has been re-enabled."); // "Verify" that the new frequency is effective - auto cdclk = callbackIGFX->modCoreDisplayClockFix.orgIclReadRegister32(that, ICL_REG_CDCLK_CTL) & 0x7FF; + auto cdclk = callbackIGFX->readRegister32(that, ICL_REG_CDCLK_CTL) & 0x7FF; SYSLOG("igfx", "CDC: sanitizeCDClockFrequency() DInfo: Core Display Clock frequency is %s MHz now.", coreDisplayClockDecimalFrequency2String(cdclk)); } @@ -586,7 +586,7 @@ uint32_t IGFX::CoreDisplayClockFix::wrapProbeCDClockFrequency(AppleIntelFramebuf // Read the Core Display Clock frequency from the CDCLK_CTL register // Bit 0 - 11 stores the decimal frequency - auto cdclk = callbackIGFX->modCoreDisplayClockFix.orgIclReadRegister32(that, ICL_REG_CDCLK_CTL) & 0x7FF; + auto cdclk = callbackIGFX->readRegister32(that, ICL_REG_CDCLK_CTL) & 0x7FF; SYSLOG("igfx", "CDC: ProbeCDClockFrequency() DInfo: The current core display clock frequency is %s MHz.", coreDisplayClockDecimalFrequency2String(cdclk)); From 547be21bde1d9c0268481fc0b069dfc5c0e28b6e Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sun, 29 Nov 2020 23:57:37 -0800 Subject: [PATCH 14/32] Controller: Remove deprecated global framebuffer controllers. --- WhateverGreen/kern_igfx.cpp | 7 ++----- WhateverGreen/kern_igfx.hpp | 6 +----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 02b8ada5..5b4031e3 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -422,8 +422,8 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a // Submodules that request access to these functions must set `PatchSubmodule::requiresGenericRegisterAccess` to `true` // Also we need to consider the case where multiple submodules want to inject code into these functions. // At this moment, the backlight fix is the only one that wraps these functions. - if (bklCoffeeFb || bklKabyFb || - /*RPSControl.enabled || ForceWakeWorkaround.enabled || */modCoreDisplayClockFix.enabled) { + if (bklCoffeeFb || bklKabyFb //|| + /*RPSControl.enabled || ForceWakeWorkaround.enabled || modCoreDisplayClockFix.enabled*/) { AppleIntelFramebufferController__ReadRegister32 = patcher.solveSymbol (index, "__ZN31AppleIntelFramebufferController14ReadRegister32Em", address, size); if (!AppleIntelFramebufferController__ReadRegister32) @@ -436,9 +436,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (!AppleIntelFramebufferController__WriteRegister32) SYSLOG("igfx", "Failed to find WriteRegister32"); } - // FIXME: Same issue here. - if (/*RPSControl.enabled || ForceWakeWorkaround.enabled || */modDPCDMaxLinkRateFix.enabled) - gFramebufferController = patcher.solveSymbol(index, "_gController", address, size); if (bklCoffeeFb || bklKabyFb) { // Intel backlight is modeled via pulse-width modulation (PWM). See page 144 of: // https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-kbl-vol12-display.pdf diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index dd111a11..14b9012c 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -410,12 +410,8 @@ class IGFX { uint32_t (*AppleIntelFramebufferController__ReadRegister32)(void*,uint32_t) {}; void (*AppleIntelFramebufferController__WriteRegister32)(void*,uint32_t,uint32_t) {}; + // The opaque framebuffer controller type on BDW+ class AppleIntelFramebufferController; - // Populated at AppleIntelFramebufferController::start - // Useful for getting access to Read/WriteRegister, rather than having - // to compute the offsets - // TODO: DEPRECATED - AppleIntelFramebufferController** gFramebufferController {}; // Available on ICL+ // Apple has refactored quite a large amount of code into a new class `AppleIntelPort` in the ICL graphics driver, From 57e254cfcc0b791731fbd2ecb035307a08d08ec3 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Sat, 5 Dec 2020 01:01:25 -0800 Subject: [PATCH 15/32] FCD, FOD: Convert force complete modest and force online display to submodules. --- WhateverGreen/kern_igfx.cpp | 261 +++++++++++++++++++----------------- WhateverGreen/kern_igfx.hpp | 131 +++++++++++------- 2 files changed, 222 insertions(+), 170 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 5b4031e3..81a0500c 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -101,14 +101,14 @@ void IGFX::init() { supportsGuCFirmware = true; currentGraphics = &kextIntelSKL; currentFramebuffer = &kextIntelSKLFb; - forceCompleteModeset.supported = forceCompleteModeset.legacy = true; // not enabled, as on legacy operating systems it casues crashes. + modForceCompleteModeset.supported = modForceCompleteModeset.legacy = true; // not enabled, as on legacy operating systems it casues crashes. disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; break; case CPUInfo::CpuGeneration::KabyLake: supportsGuCFirmware = true; currentGraphics = &kextIntelKBL; currentFramebuffer = &kextIntelKBLFb; - forceCompleteModeset.supported = forceCompleteModeset.enable = true; + modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; modForceWakeWorkaround.enabled = true; disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; @@ -122,7 +122,7 @@ void IGFX::init() { // Note, several CFL GPUs are completely broken. They freeze in IGMemoryManager::initCache due to incompatible // configuration, supposedly due to Apple not supporting new MOCS table and forcing Skylake-based format. // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 - forceCompleteModeset.supported = forceCompleteModeset.enable = true; + modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; modForceWakeWorkaround.enabled = true; disableTypeCCheck = true; @@ -131,7 +131,7 @@ void IGFX::init() { supportsGuCFirmware = true; currentGraphics = &kextIntelCNL; currentFramebuffer = &kextIntelCNLFb; - forceCompleteModeset.supported = forceCompleteModeset.enable = true; + modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; disableTypeCCheck = true; break; case CPUInfo::CpuGeneration::IceLake: @@ -139,7 +139,7 @@ void IGFX::init() { currentGraphics = &kextIntelICL; currentFramebuffer = &kextIntelICLLPFb; currentFramebufferOpt = &kextIntelICLHPFb; - forceCompleteModeset.supported = forceCompleteModeset.enable = true; + modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modDVMTCalcFix.available = true; break; case CPUInfo::CpuGeneration::CometLake: @@ -151,7 +151,7 @@ void IGFX::init() { // Note, several CFL GPUs are completely broken. They freeze in IGMemoryManager::initCache due to incompatible // configuration, supposedly due to Apple not supporting new MOCS table and forcing Skylake-based format. // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 - forceCompleteModeset.supported = forceCompleteModeset.enable = true; + modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; disableTypeCCheck = true; break; @@ -192,47 +192,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { debugFramebuffer = checkKernelArgument("-igfxfbdbg"); #endif - uint32_t forceCompleteModeSet = 0; - if (PE_parse_boot_argn("igfxfcms", &forceCompleteModeSet, sizeof(forceCompleteModeSet))) { - forceCompleteModeset.enable = forceCompleteModeset.supported && forceCompleteModeSet != 0; - DBGLOG("weg", "force complete-modeset overriden by boot-argument %u -> %d", forceCompleteModeSet, forceCompleteModeset.enable); - } else if (WIOKit::getOSDataValue(info->videoBuiltin, "complete-modeset", forceCompleteModeSet)) { - forceCompleteModeset.enable = forceCompleteModeset.supported && forceCompleteModeSet != 0; - DBGLOG("weg", "force complete-modeset overriden by device property %u -> %d", forceCompleteModeSet, forceCompleteModeset.enable); - } else if (info->firmwareVendor == DeviceInfo::FirmwareVendor::Apple) { - forceCompleteModeset.enable = false; // may interfere with FV2 - DBGLOG("weg", "force complete-modeset overriden by Apple firmware -> %d", forceCompleteModeset.enable); - } - - if (forceCompleteModeset.enable) { - uint64_t fbs; - if (PE_parse_boot_argn("igfxfcmsfbs", &fbs, sizeof(fbs)) || - WIOKit::getOSDataValue(info->videoBuiltin, "complete-modeset-framebuffers", fbs)) { - for (size_t i = 0; i < arrsize(forceCompleteModeset.fbs); i++) - forceCompleteModeset.fbs[i] = (fbs >> (8 * i)) & 0xffU; - forceCompleteModeset.customised = true; - } - } - - uint32_t forceOnline = 0; - if (PE_parse_boot_argn("igfxonln", &forceOnline, sizeof(forceOnline))) { - forceOnlineDisplay.enable = forceOnline != 0; - DBGLOG("weg", "force online overriden by boot-argument %u", forceOnline); - } else if (WIOKit::getOSDataValue(info->videoBuiltin, "force-online", forceOnline)) { - forceOnlineDisplay.enable = forceOnline != 0; - DBGLOG("weg", "force online overriden by device property %u", forceOnline); - } - - if (forceOnlineDisplay.enable) { - uint64_t fbs; - if (PE_parse_boot_argn("igfxonlnfbs", &fbs, sizeof(fbs)) || - WIOKit::getOSDataValue(info->videoBuiltin, "force-online-framebuffers", fbs)) { - for (size_t i = 0; i < arrsize(forceOnlineDisplay.fbs); i++) - forceOnlineDisplay.fbs[i] = (fbs >> (8 * i)) & 0xffU; - forceOnlineDisplay.customised = true; - } - } - if (supportsGuCFirmware && getKernelVersion() >= KernelVersion::HighSierra) { if (!PE_parse_boot_argn("igfxfw", &fwLoadMode, sizeof(fwLoadMode))) WIOKit::getOSDataValue(info->videoBuiltin, "igfxfw", fwLoadMode); @@ -321,10 +280,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (cflBacklightPatch != CoffeeBacklightPatch::Off) return true; - if (forceCompleteModeset.enable) - return true; - if (forceOnlineDisplay.enable) - return true; if (disableAGDC) return true; if (disableTypeCCheck) @@ -492,21 +447,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (submodule->enabled) submodule->processFramebufferKext(patcher, index, address, size); - if (forceCompleteModeset.enable) { - const char *sym = "__ZN31AppleIntelFramebufferController16hwRegsNeedUpdateEP21AppleIntelFramebufferP21AppleIntelDisplayPathPNS_10CRTCParamsEPK29IODetailedTimingInformationV2"; - if (forceCompleteModeset.legacy) - sym = "__ZN31AppleIntelFramebufferController16hwRegsNeedUpdateEP21AppleIntelFramebufferP21AppleIntelDisplayPathPNS_10CRTCParamsE"; - KernelPatcher::RouteRequest request(sym, wrapHwRegsNeedUpdate, orgHwRegsNeedUpdate); - if (!patcher.routeMultiple(index, &request, 1, address, size)) - SYSLOG("igfx", "failed to route hwRegsNeedUpdate"); - } - - if (forceOnlineDisplay.enable) { - KernelPatcher::RouteRequest request("__ZN21AppleIntelFramebuffer16getDisplayStatusEP21AppleIntelDisplayPath", wrapGetDisplayStatus, orgGetDisplayStatus); - if (!patcher.routeMultiple(index, &request, 1, address, size)) - SYSLOG("igfx", "failed to route getDisplayStatus"); - } - if (disableTypeCCheck && (realFramebuffer == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur)) { KernelPatcher::RouteRequest req("__ZN31AppleIntelFramebufferController17IsTypeCOnlySystemEv", wrapIsTypeCOnlySystem); if (!patcher.routeMultiple(index, &req, 1, address, size)) @@ -724,6 +664,141 @@ void IGFX::MMIORegistersWriteSupport::wrapWriteRegister32(void *controller, uint } } +// MARK: - Force Complete Modeset + +void IGFX::ForceCompleteModeset::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::ForceCompleteModeset::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + uint32_t forceCompleteModeSet = 0; + if (PE_parse_boot_argn("igfxfcms", &forceCompleteModeSet, sizeof(forceCompleteModeSet))) { + enabled = supported && forceCompleteModeSet != 0; + DBGLOG("weg", "force complete-modeset overriden by boot-argument %u -> %d", forceCompleteModeSet, enabled); + } else if (WIOKit::getOSDataValue(info->videoBuiltin, "complete-modeset", forceCompleteModeSet)) { + enabled = supported && forceCompleteModeSet != 0; + DBGLOG("weg", "force complete-modeset overriden by device property %u -> %d", forceCompleteModeSet, enabled); + } else if (info->firmwareVendor == DeviceInfo::FirmwareVendor::Apple) { + enabled = false; // may interfere with FV2 + DBGLOG("weg", "force complete-modeset overriden by Apple firmware -> %d", enabled); + } + + if (enabled) { + uint64_t fbs; + if (PE_parse_boot_argn("igfxfcmsfbs", &fbs, sizeof(fbs)) || + WIOKit::getOSDataValue(info->videoBuiltin, "complete-modeset-framebuffers", fbs)) { + for (size_t i = 0; i < arrsize(this->fbs); i++) + this->fbs[i] = (fbs >> (8 * i)) & 0xffU; + customised = true; + } + } +} + +void IGFX::ForceCompleteModeset::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + legacy ? + "__ZN31AppleIntelFramebufferController16hwRegsNeedUpdateEP21AppleIntelFramebufferP21AppleIntelDisplayPathPNS_10CRTCParamsE" : + "__ZN31AppleIntelFramebufferController16hwRegsNeedUpdateEP21AppleIntelFramebufferP21AppleIntelDisplayPathPNS_10CRTCParamsEPK29IODetailedTimingInformationV2", + wrapHwRegsNeedUpdate, + orgHwRegsNeedUpdate + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "FCM: Failed to route the function hwRegsNeedUpdate."); +} + +bool IGFX::ForceCompleteModeset::wrapHwRegsNeedUpdate(void *controller, IORegistryEntry *framebuffer, void *displayPath, void *crtParams, void *detailedInfo) { + // The framebuffer controller can perform panel fitter, partial, or a + // complete modeset (see AppleIntelFramebufferController::hwSetMode). + // In a dual-monitor CFL DVI+HDMI setup, only HDMI output was working after + // boot: it was observed that for HDMI framebuffer a complete modeset + // eventually occured, but for DVI it never did until after sleep and wake + // sequence. + // + // Function AppleIntelFramebufferController::hwRegsNeedUpdate checks + // whether a complete modeset needs to be issued. It does so by comparing + // sets of pipes and transcoder parameters. For some reason, the result was + // never true in the above scenario, so a complete modeset never occured. + // Consequently, AppleIntelFramebufferController::LightUpTMDS was never + // called for that framebuffer. + // + // Patching hwRegsNeedUpdate to always return true seems to be a rather + // safe solution to that. Note that the root cause of the problem is + // somewhere deeper. + + // On older Skylake versions this function has no detailedInfo and does not use framebuffer argument. + // As a result the compiler does not pass framebuffer to the target function. Since the fix is disabled + // by default for Skylake, just force complete modeset on all framebuffers when actually requested. + if (callbackIGFX->modForceCompleteModeset.legacy) + return true; + + // Either this framebuffer is in override list + if (callbackIGFX->modForceCompleteModeset.customised) { + return callbackIGFX->modForceCompleteModeset.inList(framebuffer) || callbackIGFX->modForceCompleteModeset.orgHwRegsNeedUpdate(controller, framebuffer, displayPath, crtParams, detailedInfo); + } + + // Or it is not built-in, as indicated by AppleBacklightDisplay setting property "built-in" for + // this framebuffer. + // Note we need to check this at every invocation, as this property may reappear + return !framebuffer->getProperty("built-in") || callbackIGFX->modForceCompleteModeset.orgHwRegsNeedUpdate(controller, framebuffer, displayPath, crtParams, detailedInfo); +} + +// MARK: - Force Online Display + +void IGFX::ForceOnlineDisplay::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::ForceOnlineDisplay::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + uint32_t forceOnline = 0; + if (PE_parse_boot_argn("igfxonln", &forceOnline, sizeof(forceOnline))) { + enabled = forceOnline != 0; + DBGLOG("weg", "force online overriden by boot-argument %u", forceOnline); + } else if (WIOKit::getOSDataValue(info->videoBuiltin, "force-online", forceOnline)) { + enabled = forceOnline != 0; + DBGLOG("weg", "force online overriden by device property %u", forceOnline); + } + + if (enabled) { + uint64_t fbs; + if (PE_parse_boot_argn("igfxonlnfbs", &fbs, sizeof(fbs)) || + WIOKit::getOSDataValue(info->videoBuiltin, "force-online-framebuffers", fbs)) { + for (size_t i = 0; i < arrsize(this->fbs); i++) + this->fbs[i] = (fbs >> (8 * i)) & 0xffU; + customised = true; + } + } +} + +void IGFX::ForceOnlineDisplay::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZN21AppleIntelFramebuffer16getDisplayStatusEP21AppleIntelDisplayPath", + wrapGetDisplayStatus, + orgGetDisplayStatus + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "FOD: Failed to route the function getDisplayStatus."); +} + +uint32_t IGFX::ForceOnlineDisplay::wrapGetDisplayStatus(IORegistryEntry *framebuffer, void *displayPath) { + // 0 - offline, 1 - online, 2 - empty dongle. + uint32_t ret = callbackIGFX->modForceOnlineDisplay.orgGetDisplayStatus(framebuffer, displayPath); + if (ret != 1) { + if (callbackIGFX->modForceOnlineDisplay.customised) + ret = callbackIGFX->modForceOnlineDisplay.inList(framebuffer) ? 1 : ret; + else + ret = 1; + } + + DBGLOG("igfx", "getDisplayStatus forces %u", ret); + return ret; +} + +// MARK: TODO + IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { //DBGLOG("igfx, "pavpCallback: cmd = %d, flag = %d, app = %u, a4 = %s", sessionCommand, flag, sessionAppId, a4 == nullptr ? "null" : "not null"); @@ -932,46 +1007,6 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { return ret; } -bool IGFX::wrapHwRegsNeedUpdate(void *controller, IOService *framebuffer, void *displayPath, void *crtParams, void *detailedInfo) { - // The framebuffer controller can perform panel fitter, partial, or a - // complete modeset (see AppleIntelFramebufferController::hwSetMode). - // In a dual-monitor CFL DVI+HDMI setup, only HDMI output was working after - // boot: it was observed that for HDMI framebuffer a complete modeset - // eventually occured, but for DVI it never did until after sleep and wake - // sequence. - // - // Function AppleIntelFramebufferController::hwRegsNeedUpdate checks - // whether a complete modeset needs to be issued. It does so by comparing - // sets of pipes and transcoder parameters. For some reason, the result was - // never true in the above scenario, so a complete modeset never occured. - // Consequently, AppleIntelFramebufferController::LightUpTMDS was never - // called for that framebuffer. - // - // Patching hwRegsNeedUpdate to always return true seems to be a rather - // safe solution to that. Note that the root cause of the problem is - // somewhere deeper. - - // On older Skylake versions this function has no detailedInfo and does not use framebuffer argument. - // As a result the compiler does not pass framebuffer to the target function. Since the fix is disabled - // by default for Skylake, just force complete modeset on all framebuffers when actually requested. - if (callbackIGFX->forceCompleteModeset.legacy) - return true; - - // Either this framebuffer is in override list - if (callbackIGFX->forceCompleteModeset.customised) { - return callbackIGFX->forceCompleteModeset.inList(framebuffer) - || FunctionCast(callbackIGFX->wrapHwRegsNeedUpdate, callbackIGFX->orgHwRegsNeedUpdate)( - controller, framebuffer, displayPath, crtParams, detailedInfo); - } - - // Or it is not built-in, as indicated by AppleBacklightDisplay setting property "built-in" for - // this framebuffer. - // Note we need to check this at every invocation, as this property may reappear - return !framebuffer->getProperty("built-in") - || FunctionCast(callbackIGFX->wrapHwRegsNeedUpdate, callbackIGFX->orgHwRegsNeedUpdate)( - controller, framebuffer, displayPath, crtParams, detailedInfo); -} - IOReturn IGFX::wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments) { if (attribute == kAGDCRegisterCallback) { DBGLOG("igfx", "ignoring AGDC registration in FBClientControl::doAttribute"); @@ -992,20 +1027,6 @@ uint64_t IGFX::wrapIsTypeCOnlySystem(void*) { return 0; } -uint32_t IGFX::wrapGetDisplayStatus(IOService *framebuffer, void *displayPath) { - // 0 - offline, 1 - online, 2 - empty dongle. - uint32_t ret = FunctionCast(wrapGetDisplayStatus, callbackIGFX->orgGetDisplayStatus)(framebuffer, displayPath); - if (ret != 1) { - if (callbackIGFX->forceOnlineDisplay.customised) - ret = callbackIGFX->forceOnlineDisplay.inList(framebuffer) ? 1 : ret; - else - ret = 1; - } - - DBGLOG("igfx", "getDisplayStatus forces %u", ret); - return ret; -} - void IGFX::wrapCflWriteRegister32(void *that, uint32_t reg, uint32_t value) { if (reg == BXT_BLC_PWM_FREQ1) { if (value && value != callbackIGFX->driverBacklightFrequency) { diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 14b9012c..4c6dac14 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -277,21 +277,11 @@ class IGFX { */ mach_vm_address_t orgIgBufferGetGpuVirtualAddress {}; - /** - * Original AppleIntelFramebufferController::hwRegsNeedUpdate function - */ - mach_vm_address_t orgHwRegsNeedUpdate {}; - /** * Original IntelFBClientControl::doAttribute function */ mach_vm_address_t orgFBClientDoAttribute {}; - /** - * Original AppleIntelFramebuffer::getDisplayStatus function - */ - mach_vm_address_t orgGetDisplayStatus {}; - /** * Original AppleIntelFramebufferController::ReadRegister32 function */ @@ -385,26 +375,6 @@ class IGFX { * Trace framebuffer logic */ bool debugFramebuffer {false}; - - /** - * Per-framebuffer helper script. - */ - struct FramebufferModifer { - bool supported {false}; // compatible CPU - bool legacy {false}; // legacy CPU (Skylake) - bool enable {false}; // enable the patch - bool customised {false}; // override default patch behaviour - uint8_t fbs[sizeof(uint64_t)] {}; // framebuffers to force modeset for on override - - bool inList(IORegistryEntry* fb) { - uint32_t idx; - if (AppleIntelFramebufferExplorer::getIndex(fb, idx)) - for (auto i : fbs) - if (i == idx) - return true; - return false; - } - }; // NOTE: the MMIO space is also available at RC6_RegBase uint32_t (*AppleIntelFramebufferController__ReadRegister32)(void*,uint32_t) {}; @@ -1222,6 +1192,87 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modForceWakeWorkaround; + /** + * A submodule that applies patches based on the framebuffer index + */ + class FramebufferModiferV2: public PatchSubmodule { + protected: + /** + * Indices of framebuffers to be patched + */ + uint8_t fbs[sizeof(uint64_t)] {}; + + /** + * Check whether the given framebuffer is in the list + */ + bool inList(IORegistryEntry* fb) { + uint32_t idx; + if (AppleIntelFramebufferExplorer::getIndex(fb, idx)) + for (auto i : fbs) + if (i == idx) + return true; + return false; + } + + public: + /** + * `True` if this patch is supported on the current platform + */ + bool supported {false}; + + /** + * `True` if the current platform is Skylake + */ + bool legacy {false}; + + /** + * `True` if patch behavior should be overridden + */ + bool customised {false}; + }; + + /** + * A submodule to ensure that each modeset operation is a complete one + */ + class ForceCompleteModeset: public FramebufferModiferV2 { + /** + * Original AppleIntelFramebufferController::hwRegsNeedUpdate function + */ + bool (*orgHwRegsNeedUpdate)(void *, IORegistryEntry *, void *, void *, void *) {nullptr}; + + /** + * Wrapper to force a complete modeset + */ + static bool wrapHwRegsNeedUpdate(void *controller, IORegistryEntry *framebuffer, void *displayPath, void *crtParams, void *detailedInfo); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modForceCompleteModeset; + + /** + * A submodule to ensure that each display is online + */ + class ForceOnlineDisplay: public FramebufferModiferV2 { + /** + * Original AppleIntelFramebuffer::getDisplayStatus function + */ + uint32_t (*orgGetDisplayStatus)(IORegistryEntry *, void *) {nullptr}; + + /** + * Wrapper to report that a display is online + */ + static uint32_t wrapGetDisplayStatus(IORegistryEntry *framebuffer, void *displayPath); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modForceOnlineDisplay; + // // MARK: Shared Submodules // @@ -1385,16 +1436,6 @@ class IGFX { */ PatchSubmodule *submodules[6] = { &modDVMTCalcFix, &modDPCDMaxLinkRateFix, &modCoreDisplayClockFix, &modHDMIDividersCalcFix, &modLSPCONDriverSupport, &modAdvancedI2COverAUXSupport }; - /** - * Ensure each modeset is a complete modeset. - */ - FramebufferModifer forceCompleteModeset; - - /** - * Ensure each display is online. - */ - FramebufferModifer forceOnlineDisplay; - /** * Prevent IntelAccelerator from starting. */ @@ -1491,11 +1532,6 @@ class IGFX { */ uint32_t driverBacklightFrequency {}; - /** - * See function definition for explanation - */ - static bool wrapHwRegsNeedUpdate(void *controller, IOService *framebuffer, void *displayPath, void *crtParams, void *detailedInfo); - /** * Explore the framebuffer structure in Apple's Intel graphics driver */ @@ -1608,11 +1644,6 @@ class IGFX { * IntelFBClientControl::doAttribute wrapper to filter attributes like AGDC. */ static IOReturn wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments); - - /** - * AppleIntelFramebuffer::getDisplayStatus to force display status on configured screens. - */ - static uint32_t wrapGetDisplayStatus(IOService *framebuffer, void *displayPath); static uint64_t wrapIsTypeCOnlySystem(void*); From 8e6a3cbdf0ff838ae80e47f072b51b159f9f2d7d Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 14:33:32 -0800 Subject: [PATCH 16/32] AGDCD: Convert the AGDC disabler to a submodule. --- WhateverGreen/kern_igfx.cpp | 58 +++++++++++++++++++------------ WhateverGreen/kern_igfx.hpp | 33 ++++++++++++------ WhateverGreen/kern_igfx_debug.cpp | 2 +- 3 files changed, 59 insertions(+), 34 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 81a0500c..3b177a21 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -244,10 +244,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { PE_parse_boot_argn("igfxmetal", &metal, sizeof(metal)); forceMetal = metal == 1; - int agdc = info->videoBuiltin->getProperty("disable-agdc") != nullptr ? 0 : 1; - PE_parse_boot_argn("igfxagdc", &agdc, sizeof(agdc)); - disableAGDC = agdc == 0; - // Starting from 10.14.4b1 Skylake+ graphics randomly kernel panics on GPU usage readDescriptorPatch = cpuGeneration >= CPUInfo::CpuGeneration::Skylake && getKernelVersion() >= KernelVersion::Mojave; @@ -280,8 +276,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (cflBacklightPatch != CoffeeBacklightPatch::Off) return true; - if (disableAGDC) - return true; if (disableTypeCCheck) return true; return false; @@ -447,21 +441,17 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (submodule->enabled) submodule->processFramebufferKext(patcher, index, address, size); + // TODO: PORT if (disableTypeCCheck && (realFramebuffer == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur)) { KernelPatcher::RouteRequest req("__ZN31AppleIntelFramebufferController17IsTypeCOnlySystemEv", wrapIsTypeCOnlySystem); if (!patcher.routeMultiple(index, &req, 1, address, size)) SYSLOG("igfx", "failed to route IsTypeCOnlySystem"); } - if (disableAGDC) { - KernelPatcher::RouteRequest request {"__ZN20IntelFBClientControl11doAttributeEjPmmS0_S0_P25IOExternalMethodArguments", wrapFBClientDoAttribute, orgFBClientDoAttribute}; - if (!patcher.routeMultiple(index, &request, 1, address, size)) - SYSLOG("igfx", "failed to route FBClientControl::doAttribute"); - } - if (debugFramebuffer) loadFramebufferDebug(patcher, index, address, size); + // TODO: PORT if (blackScreenPatch) { bool foundSymbol = false; @@ -797,7 +787,40 @@ uint32_t IGFX::ForceOnlineDisplay::wrapGetDisplayStatus(IORegistryEntry *framebu return ret; } -// MARK: TODO +// MARK: - AGDC Disabler + +void IGFX::AGDCDisabler::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::AGDCDisabler::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + int agdc = info->videoBuiltin->getProperty("disable-agdc") != nullptr ? 0 : 1; + PE_parse_boot_argn("igfxagdc", &agdc, sizeof(agdc)); + enabled = agdc == 0; +} + +void IGFX::AGDCDisabler::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZN20IntelFBClientControl11doAttributeEjPmmS0_S0_P25IOExternalMethodArguments", + wrapFBClientDoAttribute, + orgFBClientDoAttribute + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "AGDCD: Failed to route the function FBClientControl::doAttribute."); +} + +IOReturn IGFX::AGDCDisabler::wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments) { + if (attribute == kAGDCRegisterCallback) { + DBGLOG("igfx", "AGDCD: Ignoring AGDC registration in FBClientControl::doAttribute."); + return kIOReturnUnsupported; + } + + return callbackIGFX->modAGDCDisabler.orgFBClientDoAttribute(fbclient, attribute, unk1, unk2, unk3, unk4, externalMethodArguments); +} + +// MARK: - TODO IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { //DBGLOG("igfx, "pavpCallback: cmd = %d, flag = %d, app = %u, a4 = %s", sessionCommand, flag, sessionAppId, a4 == nullptr ? "null" : "not null"); @@ -1007,15 +1030,6 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { return ret; } -IOReturn IGFX::wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments) { - if (attribute == kAGDCRegisterCallback) { - DBGLOG("igfx", "ignoring AGDC registration in FBClientControl::doAttribute"); - return kIOReturnUnsupported; - } - - return FunctionCast(wrapFBClientDoAttribute, callbackIGFX->orgFBClientDoAttribute)(fbclient, attribute, unk1, unk2, unk3, unk4, externalMethodArguments); -} - /** * Apparently, platforms with (ig-platform-id & 0xf != 0) have only Type C connectivity. * Framebuffer kext uses this fact to sanitise connector type, forcing it to DP. diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 4c6dac14..f69b0b03 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -277,11 +277,6 @@ class IGFX { */ mach_vm_address_t orgIgBufferGetGpuVirtualAddress {}; - /** - * Original IntelFBClientControl::doAttribute function - */ - mach_vm_address_t orgFBClientDoAttribute {}; - /** * Original AppleIntelFramebufferController::ReadRegister32 function */ @@ -341,11 +336,6 @@ class IGFX { */ bool moderniseAccelerator {false}; - /** - * Disable AGDC configuration - */ - bool disableAGDC {false}; - /** * GuC firmware loading scheme */ @@ -1271,7 +1261,28 @@ class IGFX { void init() override; void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; - } modForceOnlineDisplay; + } modForceOnlineDisplay; + + /** + * A submodule that disables Apple's Graphics Device Control (AGDC) + */ + class AGDCDisabler: public PatchSubmodule { + /** + * Original IntelFBClientControl::doAttribute function + */ + IOReturn (*orgFBClientDoAttribute)(void *, uint32_t, unsigned long *, unsigned long, unsigned long *, unsigned long *, void *) {nullptr}; + + /** + * A wrapper to ignore AGDC registration request + */ + static IOReturn wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modAGDCDisabler; // // MARK: Shared Submodules diff --git a/WhateverGreen/kern_igfx_debug.cpp b/WhateverGreen/kern_igfx_debug.cpp index 6ed0f1a2..f9d29d23 100644 --- a/WhateverGreen/kern_igfx_debug.cpp +++ b/WhateverGreen/kern_igfx_debug.cpp @@ -330,7 +330,7 @@ static IOReturn fbdebugWrapFBClientDoAttribute(void *fbclient, uint32_t attribut void IGFX::loadFramebufferDebug(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { SYSLOG("igfx", "using framebuffer debug r15"); - if (disableAGDC) + if (modAGDCDisabler.enabled) PANIC("igfx", "igfxagdc=0 is not compatible with framebuffer debugging"); KernelPatcher::RouteRequest requests[] = { From d28d7ac78168ec3c308752a7b13234796678f1c8 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 14:50:35 -0800 Subject: [PATCH 17/32] TCCD: Convert the Type-C Check Disabler to a submodule. --- WhateverGreen/kern_igfx.cpp | 57 ++++++++++++++++++++++++++----------- WhateverGreen/kern_igfx.hpp | 35 +++++++++++++++-------- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 3b177a21..893b7ef2 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -102,7 +102,7 @@ void IGFX::init() { currentGraphics = &kextIntelSKL; currentFramebuffer = &kextIntelSKLFb; modForceCompleteModeset.supported = modForceCompleteModeset.legacy = true; // not enabled, as on legacy operating systems it casues crashes. - disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; + modTypeCCheckDisabler.enabled = getKernelVersion() >= KernelVersion::BigSur; break; case CPUInfo::CpuGeneration::KabyLake: supportsGuCFirmware = true; @@ -111,7 +111,7 @@ void IGFX::init() { modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; modForceWakeWorkaround.enabled = true; - disableTypeCCheck = getKernelVersion() >= KernelVersion::BigSur; + modTypeCCheckDisabler.enabled = getKernelVersion() >= KernelVersion::BigSur; break; case CPUInfo::CpuGeneration::CoffeeLake: supportsGuCFirmware = true; @@ -125,14 +125,14 @@ void IGFX::init() { modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; modForceWakeWorkaround.enabled = true; - disableTypeCCheck = true; + modTypeCCheckDisabler.enabled = true; break; case CPUInfo::CpuGeneration::CannonLake: supportsGuCFirmware = true; currentGraphics = &kextIntelCNL; currentFramebuffer = &kextIntelCNLFb; modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; - disableTypeCCheck = true; + modTypeCCheckDisabler.enabled = true; break; case CPUInfo::CpuGeneration::IceLake: supportsGuCFirmware = true; @@ -153,7 +153,7 @@ void IGFX::init() { // See: https://github.com/torvalds/linux/blob/135c5504a600ff9b06e321694fbcac78a9530cd4/drivers/gpu/drm/i915/intel_mocs.c#L181 modForceCompleteModeset.supported = modForceCompleteModeset.enabled = true; modRPSControlPatch.available = true; - disableTypeCCheck = true; + modTypeCCheckDisabler.enabled = true; break; default: SYSLOG("igfx", "found an unsupported processor 0x%X:0x%X, please report this!", family, model); @@ -206,8 +206,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { submodule->processKernel(patcher, info); disableAccel = checkKernelArgument("-igfxvesa"); - - disableTypeCCheck &= !checkKernelArgument("-igfxtypec"); // Enable CFL backlight patch on mobile CFL or if IGPU propery enable-cfl-backlight-fix is set int bkl = 0; @@ -223,6 +221,7 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { bool connectorLessFrame = info->reportedFramebufferIsConnectorLess; + // TODO:DEPRECATED // Black screen (ComputeLaneCount) happened from 10.12.4 // It only affects SKL, KBL, and CFL drivers with a frame with connectors. if (!connectorLessFrame && cpuGeneration >= CPUInfo::CpuGeneration::Skylake && @@ -276,8 +275,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (cflBacklightPatch != CoffeeBacklightPatch::Off) return true; - if (disableTypeCCheck) - return true; return false; }; @@ -440,18 +437,11 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a for (auto submodule : submodules) if (submodule->enabled) submodule->processFramebufferKext(patcher, index, address, size); - - // TODO: PORT - if (disableTypeCCheck && (realFramebuffer == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur)) { - KernelPatcher::RouteRequest req("__ZN31AppleIntelFramebufferController17IsTypeCOnlySystemEv", wrapIsTypeCOnlySystem); - if (!patcher.routeMultiple(index, &req, 1, address, size)) - SYSLOG("igfx", "failed to route IsTypeCOnlySystem"); - } if (debugFramebuffer) loadFramebufferDebug(patcher, index, address, size); - // TODO: PORT + // TODO: DEPRECATED if (blackScreenPatch) { bool foundSymbol = false; @@ -820,6 +810,36 @@ IOReturn IGFX::AGDCDisabler::wrapFBClientDoAttribute(void *fbclient, uint32_t at return callbackIGFX->modAGDCDisabler.orgFBClientDoAttribute(fbclient, attribute, unk1, unk2, unk3, unk4, externalMethodArguments); } +// MARK: - Type-C Check Disabler + +void IGFX::TypeCCheckDisabler::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::TypeCCheckDisabler::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + enabled &= !checkKernelArgument("-igfxtypec"); +} + +void IGFX::TypeCCheckDisabler::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + // TODO: Helper Function `getRealFramebuffer()`? + auto realFramebuffer = (callbackIGFX->currentFramebuffer && callbackIGFX->currentFramebuffer->loadIndex == index) ? callbackIGFX->currentFramebuffer : callbackIGFX->currentFramebufferOpt; + + if (realFramebuffer == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur) { + KernelPatcher::RouteRequest request = { + "__ZN31AppleIntelFramebufferController17IsTypeCOnlySystemEv", + wrapIsTypeCOnlySystem + }; + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "TCCD: Failed to route the function IsTypeCOnlySystem."); + } +} + +bool IGFX::TypeCCheckDisabler::wrapIsTypeCOnlySystem(void *controller) { + DBGLOG("igfx", "TCCD: Forcing IsTypeCOnlySystem false."); + return false; +} + // MARK: - TODO IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { @@ -887,6 +907,7 @@ bool IGFX::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, return (flags & 3U) != 0; } +// TODO: DEPRECATED bool IGFX::wrapComputeLaneCount(void *that, void *timing, uint32_t bpp, int32_t availableLanes, int32_t *laneCount) { DBGLOG("igfx", "computeLaneCount: bpp = %u, available = %d", bpp, availableLanes); @@ -913,6 +934,7 @@ bool IGFX::wrapComputeLaneCount(void *that, void *timing, uint32_t bpp, int32_t return r; } +// TODO: DEPRECATED bool IGFX::wrapComputeLaneCountNouveau(void *that, void *timing, int32_t availableLanes, int32_t *laneCount) { bool r = FunctionCast(wrapComputeLaneCountNouveau, callbackIGFX->orgComputeLaneCount)(that, timing, availableLanes, laneCount); if (!r && *laneCount == 0) { @@ -1036,6 +1058,7 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { * This breaks many systems, so we undo this check. * Affected drivers: KBL and newer? */ +// TODO: DEPRECATED uint64_t IGFX::wrapIsTypeCOnlySystem(void*) { DBGLOG("igfx", "Forcing IsTypeCOnlySystem 0"); return 0; diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index f69b0b03..982a2189 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -235,6 +235,7 @@ class IGFX { /** * Original AppleIntelFramebufferController::ComputeLaneCount function used for DP lane count calculation */ + // TODO: DEPRECATED mach_vm_address_t orgComputeLaneCount {}; /** @@ -292,6 +293,7 @@ class IGFX { /** * Set to true if a black screen ComputeLaneCount patch is required */ + // TODO: DEPRECATED bool blackScreenPatch {false}; /** @@ -1284,6 +1286,27 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modAGDCDisabler; + /** + * A submodule that disables the check of Type-C platforms + */ + class TypeCCheckDisabler: public PatchSubmodule { + /** + * A wrapper to always report that this is not a Type-C platform + * + * @note Apparently, platforms with (ig-platform-id & 0xf != 0) have only Type C connectivity. + * Framebuffer kext uses this fact to sanitise connector type, forcing it to DP. + * This breaks many systems, so we undo this check. + * Affected drivers: KBL and newer? + */ + static bool wrapIsTypeCOnlySystem(void *controller); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modTypeCCheckDisabler; + // // MARK: Shared Submodules // @@ -1451,11 +1474,6 @@ class IGFX { * Prevent IntelAccelerator from starting. */ bool disableAccel {false}; - - /** - * Disable Type C framebuffer check. - */ - bool disableTypeCCheck {false}; /** * Perform platform table dump to ioreg @@ -1651,13 +1669,6 @@ class IGFX { */ static uint64_t wrapIgBufferGetGpuVirtualAddress(void *that); - /** - * IntelFBClientControl::doAttribute wrapper to filter attributes like AGDC. - */ - static IOReturn wrapFBClientDoAttribute(void *fbclient, uint32_t attribute, unsigned long *unk1, unsigned long unk2, unsigned long *unk3, unsigned long *unk4, void *externalMethodArguments); - - static uint64_t wrapIsTypeCOnlySystem(void*); - /** * Load GuC-specific patches and hooks * From bec0b239c3b464dac33faeb15a01008b434e470e Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 16:38:44 -0800 Subject: [PATCH 18/32] BSF: Convert the black screen fix to a submodule. --- WhateverGreen/kern_igfx.cpp | 157 +++++++++++++++++++----------------- WhateverGreen/kern_igfx.hpp | 60 +++++++++----- 2 files changed, 119 insertions(+), 98 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 893b7ef2..12bf8c99 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -221,14 +221,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { bool connectorLessFrame = info->reportedFramebufferIsConnectorLess; - // TODO:DEPRECATED - // Black screen (ComputeLaneCount) happened from 10.12.4 - // It only affects SKL, KBL, and CFL drivers with a frame with connectors. - if (!connectorLessFrame && cpuGeneration >= CPUInfo::CpuGeneration::Skylake && - ((getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() >= 5) || getKernelVersion() >= KernelVersion::HighSierra)) { - blackScreenPatch = info->firmwareVendor != DeviceInfo::FirmwareVendor::Apple; - } - // PAVP patch is only necessary when we have no discrete GPU. int pavpMode = connectorLessFrame || info->firmwareVendor == DeviceInfo::FirmwareVendor::Apple; if (!PE_parse_boot_argn("igfxpavp", &pavpMode, sizeof(pavpMode))) @@ -267,8 +259,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { // Ideally, we could get rid of these two lambda expressions auto requiresFramebufferPatches = [this]() { - if (blackScreenPatch) - return true; if (applyFramebufferPatch || hdmiAutopatch) return true; if (dumpFramebufferToDisk || dumpPlatformTable || debugFramebuffer) @@ -441,22 +431,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (debugFramebuffer) loadFramebufferDebug(patcher, index, address, size); - // TODO: DEPRECATED - if (blackScreenPatch) { - bool foundSymbol = false; - - // Currently it is 10.14.1 and Kaby+... - if (getKernelVersion() >= KernelVersion::Mojave && cpuGeneration >= CPUInfo::CpuGeneration::KabyLake) { - KernelPatcher::RouteRequest request("__ZN31AppleIntelFramebufferController16ComputeLaneCountEPK29IODetailedTimingInformationV2jPj", wrapComputeLaneCountNouveau, orgComputeLaneCount); - foundSymbol = patcher.routeMultiple(index, &request, 1, address, size); - } - - if (!foundSymbol) { - KernelPatcher::RouteRequest request("__ZN31AppleIntelFramebufferController16ComputeLaneCountEPK29IODetailedTimingInformationV2jjPj", wrapComputeLaneCount, orgComputeLaneCount); - patcher.routeMultiple(index, &request, 1, address, size); - } - } - if (applyFramebufferPatch || dumpFramebufferToDisk || dumpPlatformTable || hdmiAutopatch) { framebufferStart = reinterpret_cast(address); framebufferSize = size; @@ -840,6 +814,87 @@ bool IGFX::TypeCCheckDisabler::wrapIsTypeCOnlySystem(void *controller) { return false; } +// MARK: - Black Screen Fix (HDMI/DVI Displays) + +void IGFX::BlackScreenFix::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::BlackScreenFix::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + // Black screen (ComputeLaneCount) happened from 10.12.4 + // It only affects SKL, KBL, and CFL drivers with a frame with connectors. + if (!info->reportedFramebufferIsConnectorLess && + BaseDeviceInfo::get().cpuGeneration >= CPUInfo::CpuGeneration::Skylake && + ((getKernelVersion() == KernelVersion::Sierra && getKernelMinorVersion() >= 5) || + getKernelVersion() >= KernelVersion::HighSierra)) { + enabled = info->firmwareVendor != DeviceInfo::FirmwareVendor::Apple; + } +} + +void IGFX::BlackScreenFix::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + bool foundSymbol = false; + + // Currently it is 10.14.1 and Kaby+... + if (getKernelVersion() >= KernelVersion::Mojave && + BaseDeviceInfo::get().cpuGeneration >= CPUInfo::CpuGeneration::KabyLake) { + KernelPatcher::RouteRequest request = { + "__ZN31AppleIntelFramebufferController16ComputeLaneCountEPK29IODetailedTimingInformationV2jPj", + wrapComputeLaneCountNouveau, + orgComputeLaneCountNouveau + }; + + foundSymbol = patcher.routeMultiple(index, &request, 1, address, size); + } + + if (!foundSymbol) { + KernelPatcher::RouteRequest request = { + "__ZN31AppleIntelFramebufferController16ComputeLaneCountEPK29IODetailedTimingInformationV2jjPj", + wrapComputeLaneCount, + orgComputeLaneCount + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "BSF: Failed to route the function ComputeLaneCount."); + } +} + +bool IGFX::BlackScreenFix::wrapComputeLaneCount(void *controller, void *detailedTiming, uint32_t bpp, int availableLanes, int *laneCount) { + DBGLOG("igfx", "BSF: ComputeLaneCount: bpp = %u, available lanes = %d", bpp, availableLanes); + + // It seems that AGDP fails to properly detect external boot monitors. As a result computeLaneCount + // is mistakengly called for any boot monitor (e.g. HDMI/DVI), while it is only meant to be used for + // DP (eDP) displays. More details could be found at: + // https://github.com/vit9696/Lilu/issues/27#issuecomment-372103559 + // Since the only problematic function is AppleIntelFramebuffer::validateDetailedTiming, there are + // multiple ways to workaround it. + // 1. In 10.13.4 Apple added an additional extended timing validation call, which happened to be + // guardded by a HDMI 2.0 enable boot-arg, which resulted in one bug fixing the other, and 10.13.5 + // broke it again. + // 2. Another good way is to intercept AppleIntelFramebufferController::RegisterAGDCCallback and + // make sure AppleGraphicsDevicePolicy::_VendorEventHandler returns mode 2 (not 0) for event 10. + // 3. Disabling AGDC by nopping AppleIntelFramebufferController::RegisterAGDCCallback is also fine. + // Simply returning true from computeLaneCount and letting 0 to be compared against zero so far was + // least destructive and most reliable. Let's stick with it until we could solve more problems. + bool r = callbackIGFX->modBlackScreenFix.orgComputeLaneCount(controller, detailedTiming, bpp, availableLanes, laneCount); + if (!r && *laneCount == 0) { + DBGLOG("igfx", "BSF: Reporting worked lane count (legacy)"); + r = true; + } + + return r; +} + +bool IGFX::BlackScreenFix::wrapComputeLaneCountNouveau(void *controller, void *detailedTiming, int availableLanes, int *laneCount) { + bool r = callbackIGFX->modBlackScreenFix.orgComputeLaneCountNouveau(controller, detailedTiming, availableLanes, laneCount); + if (!r && *laneCount == 0) { + DBGLOG("igfx", "reporting worked lane count (nouveau)"); + r = true; + } + + return r; +} + // MARK: - TODO IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { @@ -907,44 +962,6 @@ bool IGFX::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, return (flags & 3U) != 0; } -// TODO: DEPRECATED -bool IGFX::wrapComputeLaneCount(void *that, void *timing, uint32_t bpp, int32_t availableLanes, int32_t *laneCount) { - DBGLOG("igfx", "computeLaneCount: bpp = %u, available = %d", bpp, availableLanes); - - // It seems that AGDP fails to properly detect external boot monitors. As a result computeLaneCount - // is mistakengly called for any boot monitor (e.g. HDMI/DVI), while it is only meant to be used for - // DP (eDP) displays. More details could be found at: - // https://github.com/vit9696/Lilu/issues/27#issuecomment-372103559 - // Since the only problematic function is AppleIntelFramebuffer::validateDetailedTiming, there are - // multiple ways to workaround it. - // 1. In 10.13.4 Apple added an additional extended timing validation call, which happened to be - // guardded by a HDMI 2.0 enable boot-arg, which resulted in one bug fixing the other, and 10.13.5 - // broke it again. - // 2. Another good way is to intercept AppleIntelFramebufferController::RegisterAGDCCallback and - // make sure AppleGraphicsDevicePolicy::_VendorEventHandler returns mode 2 (not 0) for event 10. - // 3. Disabling AGDC by nopping AppleIntelFramebufferController::RegisterAGDCCallback is also fine. - // Simply returning true from computeLaneCount and letting 0 to be compared against zero so far was - // least destructive and most reliable. Let's stick with it until we could solve more problems. - bool r = FunctionCast(wrapComputeLaneCount, callbackIGFX->orgComputeLaneCount)(that, timing, bpp, availableLanes, laneCount); - if (!r && *laneCount == 0) { - DBGLOG("igfx", "reporting worked lane count (legacy)"); - r = true; - } - - return r; -} - -// TODO: DEPRECATED -bool IGFX::wrapComputeLaneCountNouveau(void *that, void *timing, int32_t availableLanes, int32_t *laneCount) { - bool r = FunctionCast(wrapComputeLaneCountNouveau, callbackIGFX->orgComputeLaneCount)(that, timing, availableLanes, laneCount); - if (!r && *laneCount == 0) { - DBGLOG("igfx", "reporting worked lane count (nouveau)"); - r = true; - } - - return r; -} - OSObject *IGFX::wrapCopyExistingServices(OSDictionary *matching, IOOptionBits inState, IOOptionBits options) { if (matching && inState == kIOServiceMatchedState && options == 0) { auto name = OSDynamicCast(OSString, matching->getObject(gIONameMatchKey)); @@ -1052,18 +1069,6 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { return ret; } -/** - * Apparently, platforms with (ig-platform-id & 0xf != 0) have only Type C connectivity. - * Framebuffer kext uses this fact to sanitise connector type, forcing it to DP. - * This breaks many systems, so we undo this check. - * Affected drivers: KBL and newer? - */ -// TODO: DEPRECATED -uint64_t IGFX::wrapIsTypeCOnlySystem(void*) { - DBGLOG("igfx", "Forcing IsTypeCOnlySystem 0"); - return 0; -} - void IGFX::wrapCflWriteRegister32(void *that, uint32_t reg, uint32_t value) { if (reg == BXT_BLC_PWM_FREQ1) { if (value && value != callbackIGFX->driverBacklightFrequency) { diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 982a2189..00b5c303 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -232,12 +232,6 @@ class IGFX { */ mach_vm_address_t orgPavpSessionCallback {}; - /** - * Original AppleIntelFramebufferController::ComputeLaneCount function used for DP lane count calculation - */ - // TODO: DEPRECATED - mach_vm_address_t orgComputeLaneCount {}; - /** * Original IOService::copyExistingServices function from the kernel */ @@ -290,12 +284,6 @@ class IGFX { void (*orgCflWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; void (*orgKblWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; - /** - * Set to true if a black screen ComputeLaneCount patch is required - */ - // TODO: DEPRECATED - bool blackScreenPatch {false}; - /** * Coffee Lake backlight patch configuration options */ @@ -1307,6 +1295,44 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modTypeCCheckDisabler; + /** + * A submodule to fix the black screen on external HDMI/DVI displays + */ + class BlackScreenFix: public PatchSubmodule { + /** + * [Legacy] Original AppleIntelFramebufferController::ComputeLaneCount function + * + * @note This function is used for DP lane count calculation. + */ + bool (*orgComputeLaneCount)(void *, void *, uint32_t, int, int *) {nullptr}; + + /** + * [Nouveau] Original AppleIntelFramebufferController::ComputeLaneCount function + * + * @note This function is used for DP lane count calculation. + * @note Available on KBL+ and as of macOS 10.14.1. + */ + bool (*orgComputeLaneCountNouveau)(void *, void *, int, int *) {nullptr}; + + /** + * [Legacy] A wrapper to report a working lane count for HDMI/DVI connections + */ + static bool wrapComputeLaneCount(void *controller, void *detailedTiming, uint32_t bpp, int availableLanes, int *laneCount); + + /** + * [Nouveau] A wrapper to report a working lane count for HDMI/DVI connections + * + * @note Available on KBL+ and as of macOS 10.14.1. + */ + static bool wrapComputeLaneCountNouveau(void *controller, void *detailedTiming, int availableLanes, int *laneCount); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modBlackScreenFix; + // // MARK: Shared Submodules // @@ -1595,16 +1621,6 @@ class IGFX { */ static bool globalPageTableRead(void *hardwareGlobalPageTable, uint64_t a1, uint64_t &a2, uint64_t &a3); - /** - * DP ComputeLaneCount wrapper to report success on non-DP screens to avoid black screen - */ - static bool wrapComputeLaneCount(void *that, void *timing, uint32_t bpp, int32_t availableLanes, int32_t *laneCount); - - /** - * DP ComputeLaneCount wrapper to report success on non-DP screens to avoid black screen (10.14.1+ KBL/CFL version) - */ - static bool wrapComputeLaneCountNouveau(void *that, void *timing, int32_t availableLanes, int32_t *laneCount); - /** * copyExistingServices wrapper used to rename Gen6Accelerator from userspace calls */ From b79b6c900e5e35b22a6c59c095fe67c8e010b3fb Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 17:02:28 -0800 Subject: [PATCH 19/32] PAVP: Convert the PAVP disabler to a submodule. --- WhateverGreen/kern_igfx.cpp | 67 ++++++++++++++++++++++++------------- WhateverGreen/kern_igfx.hpp | 36 +++++++++++--------- 2 files changed, 65 insertions(+), 38 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 12bf8c99..3694e97a 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -221,12 +221,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { bool connectorLessFrame = info->reportedFramebufferIsConnectorLess; - // PAVP patch is only necessary when we have no discrete GPU. - int pavpMode = connectorLessFrame || info->firmwareVendor == DeviceInfo::FirmwareVendor::Apple; - if (!PE_parse_boot_argn("igfxpavp", &pavpMode, sizeof(pavpMode))) - WIOKit::getOSDataValue(info->videoBuiltin, "igfxpavp", pavpMode); - pavpDisablePatch = pavpMode == 0 && cpuGeneration >= CPUInfo::CpuGeneration::SandyBridge; - int gl = info->videoBuiltin->getProperty("disable-metal") != nullptr; PE_parse_boot_argn("igfxgl", &gl, sizeof(gl)); forceOpenGL = gl == 1; @@ -269,8 +263,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { }; auto requiresGraphicsPatches = [this]() { - if (pavpDisablePatch) - return true; if (forceOpenGL) return true; if (forceMetal) @@ -313,17 +305,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a auto cpuGeneration = BaseDeviceInfo::get().cpuGeneration; if (currentGraphics && currentGraphics->loadIndex == index) { - if (pavpDisablePatch) { - auto callbackSym = "__ZN16IntelAccelerator19PAVPCommandCallbackE22PAVPSessionCommandID_tjPjb"; - if (cpuGeneration == CPUInfo::CpuGeneration::SandyBridge) - callbackSym = "__ZN15Gen6Accelerator19PAVPCommandCallbackE22PAVPSessionCommandID_t18PAVPSessionAppID_tPjb"; - else if (cpuGeneration == CPUInfo::CpuGeneration::IvyBridge) - callbackSym = "__ZN16IntelAccelerator19PAVPCommandCallbackE22PAVPSessionCommandID_t18PAVPSessionAppID_tPjb"; - - KernelPatcher::RouteRequest request(callbackSym, wrapPavpSessionCallback, orgPavpSessionCallback); - patcher.routeMultiple(index, &request, 1, address, size); - } - if (forceOpenGL || forceMetal || moderniseAccelerator || fwLoadMode != FW_APPLE || disableAccel) { KernelPatcher::RouteRequest request("__ZN16IntelAccelerator5startEP9IOService", wrapAcceleratorStart, orgAcceleratorStart); patcher.routeMultiple(index, &request, 1, address, size); @@ -895,19 +876,59 @@ bool IGFX::BlackScreenFix::wrapComputeLaneCountNouveau(void *controller, void *d return r; } -// MARK: - TODO +// MARK: - PAVP Disabler + +void IGFX::PAVPDisabler::init() { + // We only need to patch the accelerator driver + requiresPatchingGraphics = true; +} + +void IGFX::PAVPDisabler::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + int pavpMode = info->reportedFramebufferIsConnectorLess || info->firmwareVendor == DeviceInfo::FirmwareVendor::Apple; + if (!PE_parse_boot_argn("igfxpavp", &pavpMode, sizeof(pavpMode))) + WIOKit::getOSDataValue(info->videoBuiltin, "igfxpavp", pavpMode); + enabled = pavpMode == 0 && BaseDeviceInfo::get().cpuGeneration >= CPUInfo::CpuGeneration::SandyBridge; +} -IOReturn IGFX::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { +void IGFX::PAVPDisabler::processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + const char* symbol; + switch (BaseDeviceInfo::get().cpuGeneration) { + case CPUInfo::CpuGeneration::SandyBridge: + symbol = "__ZN15Gen6Accelerator19PAVPCommandCallbackE22PAVPSessionCommandID_t18PAVPSessionAppID_tPjb"; + break; + + case CPUInfo::CpuGeneration::IvyBridge: + symbol = "__ZN16IntelAccelerator19PAVPCommandCallbackE22PAVPSessionCommandID_t18PAVPSessionAppID_tPjb"; + break; + + default: + symbol = "__ZN16IntelAccelerator19PAVPCommandCallbackE22PAVPSessionCommandID_tjPjb"; + break; + } + + KernelPatcher::RouteRequest request = { + symbol, + wrapPavpSessionCallback, + orgPavpSessionCallback + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "PAVP: Failed to route the function PAVPCommandCallback."); +} + +IOReturn IGFX::PAVPDisabler::wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag) { //DBGLOG("igfx, "pavpCallback: cmd = %d, flag = %d, app = %u, a4 = %s", sessionCommand, flag, sessionAppId, a4 == nullptr ? "null" : "not null"); if (sessionCommand == 4) { - DBGLOG("igfx", "pavpSessionCallback: enforcing error on cmd 4 (send to ring?)!"); + DBGLOG("igfx", "PAVP: PavpSessionCallback: Enforcing error on cmd 4 (send to ring?)!"); return kIOReturnTimeout; // or kIOReturnSuccess } - return FunctionCast(wrapPavpSessionCallback, callbackIGFX->orgPavpSessionCallback)(intelAccelerator, sessionCommand, sessionAppId, a4, flag); + return callbackIGFX->modPAVPDisabler.orgPavpSessionCallback(intelAccelerator, sessionCommand, sessionAppId, a4, flag); } +// MARK: - TODO + bool IGFX::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, uint64_t &physAddress, uint64_t &flags) { uint64_t pageNumber = address >> PAGE_SHIFT; uint64_t pageEntry = getMember(hardwareGlobalPageTable, 0x28)[pageNumber]; diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 00b5c303..53ed172b 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -227,11 +227,6 @@ class IGFX { */ KernelPatcher::KextInfo *currentFramebufferOpt {nullptr}; - /** - * Original PAVP session callback function used for PAVP command handling - */ - mach_vm_address_t orgPavpSessionCallback {}; - /** * Original IOService::copyExistingServices function from the kernel */ @@ -301,11 +296,6 @@ class IGFX { */ CoffeeBacklightPatch cflBacklightPatch {CoffeeBacklightPatch::Off}; - /** - * Set to true if PAVP code should be disabled - */ - bool pavpDisablePatch {false}; - /** * Set to true if read descriptor patch should be enabled */ @@ -1333,6 +1323,27 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modBlackScreenFix; + /** + * A submodule to disable PAVP code and thus prevent freezes + */ + class PAVPDisabler: public PatchSubmodule { + /** + * Original PAVP session callback function used for PAVP command handling + */ + IOReturn (*orgPavpSessionCallback)(void *, int32_t, uint32_t, uint32_t *, bool) {nullptr}; + + /** + * PAVP session callback wrapper used to prevent freezes on incompatible PAVP certificates + */ + static IOReturn wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modPAVPDisabler; + // // MARK: Shared Submodules // @@ -1610,11 +1621,6 @@ class IGFX { return false; } }; - - /** - * PAVP session callback wrapper used to prevent freezes on incompatible PAVP certificates - */ - static IOReturn wrapPavpSessionCallback(void *intelAccelerator, int32_t sessionCommand, uint32_t sessionAppId, uint32_t *a4, bool flag); /** * Global page table read wrapper for Kaby Lake. From 54a6cd06e4e85334f9e7576d483b096e80ca30b3 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 17:14:18 -0800 Subject: [PATCH 20/32] RDP: Convert the read descriptor patch to a submodule. --- WhateverGreen/kern_igfx.cpp | 36 ++++++++++++++++++++++++------------ WhateverGreen/kern_igfx.hpp | 26 ++++++++++++++++---------- 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 3694e97a..1ff97516 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -229,9 +229,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { PE_parse_boot_argn("igfxmetal", &metal, sizeof(metal)); forceMetal = metal == 1; - // Starting from 10.14.4b1 Skylake+ graphics randomly kernel panics on GPU usage - readDescriptorPatch = cpuGeneration >= CPUInfo::CpuGeneration::Skylake && getKernelVersion() >= KernelVersion::Mojave; - // Automatically enable HDMI -> DP patches bool nohdmi = info->videoBuiltin->getProperty("disable-hdmi-patches") != nullptr; hdmiAutopatch = !applyFramebufferPatch && !connectorLessFrame && getKernelVersion() >= Yosemite && @@ -271,8 +268,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (fwLoadMode != FW_APPLE) return true; - if (readDescriptorPatch) - return true; if (disableAccel) return true; return false; @@ -312,11 +307,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (fwLoadMode == FW_GENERIC && getKernelVersion() <= KernelVersion::Mojave) loadIGScheduler4Patches(patcher, index, address, size); } - - if (readDescriptorPatch) { - KernelPatcher::RouteRequest request("__ZNK25IGHardwareGlobalPageTable4readEyRyS0_", globalPageTableRead); - patcher.routeMultiple(index, &request, 1, address, size); - } // Iterate through each submodule and redirect the request if and only if the submodule is enabled for (auto submodule : submodules) @@ -927,9 +917,29 @@ IOReturn IGFX::PAVPDisabler::wrapPavpSessionCallback(void *intelAccelerator, int return callbackIGFX->modPAVPDisabler.orgPavpSessionCallback(intelAccelerator, sessionCommand, sessionAppId, a4, flag); } -// MARK: - TODO +// MARK: - Read Descriptors Patch + +void IGFX::ReadDescriptorPatch::init() { + // We only need to patch the accelerator driver + requiresPatchingGraphics = true; +} -bool IGFX::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, uint64_t &physAddress, uint64_t &flags) { +void IGFX::ReadDescriptorPatch::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + // Starting from 10.14.4b1 Skylake+ graphics randomly kernel panics on GPU usage + enabled = BaseDeviceInfo::get().cpuGeneration >= CPUInfo::CpuGeneration::Skylake && getKernelVersion() >= KernelVersion::Mojave; +} + +void IGFX::ReadDescriptorPatch::processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + KernelPatcher::RouteRequest request = { + "__ZNK25IGHardwareGlobalPageTable4readEyRyS0_", + globalPageTableRead + }; + + if (!patcher.routeMultiple(index, &request, 1, address, size)) + SYSLOG("igfx", "RDP: Failed to route the function IGHardwareGlobalPageTable::read."); +} + +bool IGFX::ReadDescriptorPatch::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, uint64_t &physAddress, uint64_t &flags) { uint64_t pageNumber = address >> PAGE_SHIFT; uint64_t pageEntry = getMember(hardwareGlobalPageTable, 0x28)[pageNumber]; // PTE: Page Table Entry for 4KB Page, page 82: @@ -983,6 +993,8 @@ bool IGFX::globalPageTableRead(void *hardwareGlobalPageTable, uint64_t address, return (flags & 3U) != 0; } +// MARK: - TODO + OSObject *IGFX::wrapCopyExistingServices(OSDictionary *matching, IOOptionBits inState, IOOptionBits options) { if (matching && inState == kIOServiceMatchedState && options == 0) { auto name = OSDynamicCast(OSString, matching->getObject(gIONameMatchKey)); diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 53ed172b..d4f7dd45 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -296,11 +296,6 @@ class IGFX { */ CoffeeBacklightPatch cflBacklightPatch {CoffeeBacklightPatch::Off}; - /** - * Set to true if read descriptor patch should be enabled - */ - bool readDescriptorPatch {false}; - /** * Set to true to disable Metal support */ @@ -1344,6 +1339,22 @@ class IGFX { void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modPAVPDisabler; + /** + * A submodule to patch read descriptors and thus avoid random kernel panics on SKL+ + */ + class ReadDescriptorPatch: public PatchSubmodule { + /** + * Global page table read wrapper for Kaby Lake. + */ + static bool globalPageTableRead(void *hardwareGlobalPageTable, uint64_t a1, uint64_t &a2, uint64_t &a3); + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modReadDescriptorPatch; + // // MARK: Shared Submodules // @@ -1622,11 +1633,6 @@ class IGFX { } }; - /** - * Global page table read wrapper for Kaby Lake. - */ - static bool globalPageTableRead(void *hardwareGlobalPageTable, uint64_t a1, uint64_t &a2, uint64_t &a3); - /** * copyExistingServices wrapper used to rename Gen6Accelerator from userspace calls */ From 66517f8661abdda7d6c0882b8acabb170da1a841 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 17:58:24 -0800 Subject: [PATCH 21/32] PatchSubmodule: Move the definition of shared modules. --- WhateverGreen/kern_igfx.hpp | 357 ++++++++++++++++++++---------------- 1 file changed, 198 insertions(+), 159 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index d4f7dd45..c671aee5 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -145,6 +145,7 @@ class IGFX { /** * Backlight registers */ + // TODO: DEPRECATED static constexpr uint32_t BXT_BLC_PWM_CTL1 = 0xC8250; static constexpr uint32_t BXT_BLC_PWM_FREQ1 = 0xC8254; static constexpr uint32_t BXT_BLC_PWM_DUTY1 = 0xC8258; @@ -270,18 +271,21 @@ class IGFX { /** * Original AppleIntelFramebufferController::ReadRegister32 function */ + // TODO: DEPRECATED uint32_t (*orgCflReadRegister32)(void *, uint32_t) {nullptr}; uint32_t (*orgKblReadRegister32)(void *, uint32_t) {nullptr}; /** * Original AppleIntelFramebufferController::WriteRegister32 function */ + // TODO: DEPRECATED void (*orgCflWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; void (*orgKblWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; /** * Coffee Lake backlight patch configuration options */ + // TODO: DEPRECATED REMOVED enum class CoffeeBacklightPatch { Auto = -1, On = 1, @@ -294,6 +298,7 @@ class IGFX { * - IGPU property enable-cfl-backlight-fix turns patch on * - laptop with CFL CPU and CFL IGPU drivers turns patch on */ + // TODO: DEPRECATED REMOVED CoffeeBacklightPatch cflBacklightPatch {CoffeeBacklightPatch::Off}; /** @@ -341,6 +346,7 @@ class IGFX { */ bool debugFramebuffer {false}; + // TODO: DEPRECATED // NOTE: the MMIO space is also available at RC6_RegBase uint32_t (*AppleIntelFramebufferController__ReadRegister32)(void*,uint32_t) {}; void (*AppleIntelFramebufferController__WriteRegister32)(void*,uint32_t,uint32_t) {}; @@ -353,6 +359,10 @@ class IGFX { // and the framebuffer controller now maintains an array of `ports`. class AppleIntelPort; + // + // MARK: - Patch Submodule & Injection Kits + // + /** * Describes how to inject code into a shared submodule * @@ -576,6 +586,188 @@ class IGFX { virtual void disableDependentSubmodules() {} }; + // + // MARK: - Shared Submodules + // + + /** + * A submodule to provide shared access to global framebuffer controllers + */ + class FramebufferControllerAccessSupport: public PatchSubmodule { + /** + * An array of framebuffer controllers populated by `AppleIntelFramebufferController::start()` + */ + AppleIntelFramebufferController **controllers {}; + + public: + /** + * Get the framebuffer controller at the given index + */ + AppleIntelFramebufferController *getController(size_t index) { return controllers[index]; } + + // MARK: Patch Submodule IMP + void init() override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; + } modFramebufferControllerAccessSupport; + + /** + * Defines the prologue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and returns void. + * @note The injection is performed before the original function is invoked. + */ + struct MMIOReadPrologue: InjectionDescriptor { + /** + * Inherits the constructor from the super class + */ + using InjectionDescriptor::InjectionDescriptor; + }; + + /** + * Defines the replacer injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and returns the register value. + * @note The injection replaced the original function call. + */ + struct MMIOReadReplacer: InjectionDescriptor { + /** + * Inherits the constructor from the super class + */ + using InjectionDescriptor::InjectionDescriptor; + }; + + /** + * Defines the epilogue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and its value, and returns the new value. + * @note The injection is performed after the original function is invoked. + */ + struct MMIOReadEpilogue: InjectionDescriptor { + /** + * Inherits the constructor from the super class + */ + using InjectionDescriptor::InjectionDescriptor; + }; + + /** + * A submodule that provides read access to MMIO registers and coordinates injections to the read function + */ + class MMIORegistersReadSupport: public PatchSubmodule, public InjectionCoordinator { + /** + * Set to `true` to print detailed register access information + */ + bool verbose; + + public: + /** + * Original AppleIntelFramebufferController::ReadRegister32 function + * + * @note Other submodules may use this function pointer to skip injected code. + */ + uint32_t (*orgReadRegister32)(void *, uint32_t) {nullptr}; + + /** + * Wrapper for the AppleIntelFramebufferController::ReadRegister32 function + * + * @param controller The implicit controller instance + * @param address The register address + * @return The register value. + * @note This wrapper function monitors the register address and invokes registered injectors. + */ + static uint32_t wrapReadRegister32(void *controller, uint32_t address); + + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; + } modMMIORegistersReadSupport; + + /** + * Defines the injection descriptor for `AppleIntelFramebufferController::WriteRegister32()` + * + * @note The trigger is the register address. + * @note The injector function takes the controller along with the register address and its new value, and returns void. + * @note This type is shared by all three kinds of injection descriptors. + */ + struct MMIOWriteInjectionDescriptor: InjectionDescriptor { + /** + * Inherits the constructor from the super class + */ + using InjectionDescriptor::InjectionDescriptor; + }; + + /** + * A submodule that provides write access to MMIO registers and coordinates injections to the write function + */ + class MMIORegistersWriteSupport: public PatchSubmodule, public InjectionCoordinator { + /** + * Set to `true` to print detailed register access information + */ + bool verbose; + + public: + /** + * Original AppleIntelFramebufferController::WriteRegister32 function + * + * @note Other submodules may use this function pointer to skip injected code. + */ + void (*orgWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; + + /** + * Wrapper for the AppleIntelFramebufferController::WriteRegister32 function + * + * @param controller The implicit controller instance + * @param address The register address + * @param value The new register value + * @note This wrapper function monitors the register address and invokes registered injectors. + */ + static void wrapWriteRegister32(void *controller, uint32_t address, uint32_t value); + + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void disableDependentSubmodules() override; + } modMMIORegistersWriteSupport; + + /** + * [Convenient] Get the default framebuffer controller + */ + AppleIntelFramebufferController *defaultController() { + return modFramebufferControllerAccessSupport.getController(0); + } + + /** + * [Convenient] Invoke the original AppleIntelFramebufferController::ReadRegister32 function + * + * @param controller The framebuffer controller instance + * @param address The register address + * @return The register value. + */ + uint32_t readRegister32(void *controller, uint32_t address) { + return modMMIORegistersReadSupport.orgReadRegister32(controller, address); + } + + /** + * [Convenient] Invoke the original AppleIntelFramebufferController::WriteRegister32 function + * + * @param controller The framebuffer controller instance + * @param address The register address + * @param value The new register value + */ + void writeRegister32(void *controller, uint32_t address, uint32_t value) { + modMMIORegistersWriteSupport.orgWriteRegister32(controller, address, value); + } + + // + // MARK: - Individual Fixes + // + /** * A submodule to fix the calculation of DVMT preallocated memory on ICL+ platforms */ @@ -1355,164 +1547,6 @@ class IGFX { void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modReadDescriptorPatch; - // - // MARK: Shared Submodules - // - - /** - * A submodule to provide shared access to global framebuffer controllers - */ - class FramebufferControllerAccessSupport: public PatchSubmodule { - /** - * An array of framebuffer controllers populated by `AppleIntelFramebufferController::start()` - */ - AppleIntelFramebufferController **controllers {}; - - public: - /** - * Get the framebuffer controller at the given index - */ - AppleIntelFramebufferController *getController(size_t index) { return controllers[index]; } - - // MARK: Patch Submodule IMP - void init() override; - void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; - void disableDependentSubmodules() override; - } modFramebufferControllerAccessSupport; - - /** - * Defines the prologue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` - * - * @note The trigger is the register address. - * @note The injector function takes the controller along with the register address and returns void. - * @note The injection is performed before the original function is invoked. - */ - struct MMIOReadPrologue: InjectionDescriptor {}; - - /** - * Defines the replacer injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` - * - * @note The trigger is the register address. - * @note The injector function takes the controller along with the register address and returns the register value. - * @note The injection replaced the original function call. - */ - struct MMIOReadReplacer: InjectionDescriptor {}; - - /** - * Defines the epilogue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` - * - * @note The trigger is the register address. - * @note The injector function takes the controller along with the register address and its value, and returns the new value. - * @note The injection is performed after the original function is invoked. - */ - struct MMIOReadEpilogue: InjectionDescriptor {}; - - /** - * A submodule that provides read access to MMIO registers and coordinates injections to the read function - */ - class MMIORegistersReadSupport: public PatchSubmodule, public InjectionCoordinator { - /** - * Set to `true` to print detailed register access information - */ - bool verbose; - - public: - /** - * Original AppleIntelFramebufferController::ReadRegister32 function - * - * @note Other submodules may use this function pointer to skip injected code. - */ - uint32_t (*orgReadRegister32)(void *, uint32_t) {nullptr}; - - /** - * Wrapper for the AppleIntelFramebufferController::ReadRegister32 function - * - * @param controller The implicit controller instance - * @param address The register address - * @return The register value. - * @note This wrapper function monitors the register address and invokes registered injectors. - */ - static uint32_t wrapReadRegister32(void *controller, uint32_t address); - - // MARK: Patch Submodule IMP - void init() override; - void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; - void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; - void disableDependentSubmodules() override; - } modMMIORegistersReadSupport; - - /** - * Defines the injection descriptor for `AppleIntelFramebufferController::WriteRegister32()` - * - * @note The trigger is the register address. - * @note The injector function takes the controller along with the register address and its new value, and returns void. - * @note This type is shared by all three kinds of injection descriptors. - */ - struct MMIOWriteInjectionDescriptor: InjectionDescriptor {}; - - /** - * A submodule that provides write access to MMIO registers and coordinates injections to the write function - */ - class MMIORegistersWriteSupport: public PatchSubmodule, public InjectionCoordinator { - /** - * Set to `true` to print detailed register access information - */ - bool verbose; - - public: - /** - * Original AppleIntelFramebufferController::WriteRegister32 function - * - * @note Other submodules may use this function pointer to skip injected code. - */ - void (*orgWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; - - /** - * Wrapper for the AppleIntelFramebufferController::WriteRegister32 function - * - * @param controller The implicit controller instance - * @param address The register address - * @param value The new register value - * @note This wrapper function monitors the register address and invokes registered injectors. - */ - static void wrapWriteRegister32(void *controller, uint32_t address, uint32_t value); - - // MARK: Patch Submodule IMP - void init() override; - void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; - void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; - void disableDependentSubmodules() override; - } modMMIORegistersWriteSupport; - - /** - * [Convenient] Get the default framebuffer controller - */ - AppleIntelFramebufferController *defaultController() { - return modFramebufferControllerAccessSupport.getController(0); - } - - /** - * [Convenient] Invoke the original AppleIntelFramebufferController::ReadRegister32 function - * - * @param controller The framebuffer controller instance - * @param address The register address - * @return The register value. - */ - uint32_t readRegister32(void *controller, uint32_t address) { - return modMMIORegistersReadSupport.orgReadRegister32(controller, address); - } - - /** - * [Convenient] Invoke the original AppleIntelFramebufferController::WriteRegister32 function - * - * @param controller The framebuffer controller instance - * @param address The register address - * @param value The new register value - */ - void writeRegister32(void *controller, uint32_t address, uint32_t value) { - modMMIORegistersWriteSupport.orgWriteRegister32(controller, address, value); - } - /** * A collection of submodules */ @@ -1586,27 +1620,32 @@ class IGFX { /** * Store backlight level */ - uint32_t backlightLevel {}; + // TODO: UNUSED + uint32_t backlightLevel {}; // Unused /** * Fallback user-requested backlight frequency in case 0 was initially written to the register. */ + // TODO: DEPRECATED static constexpr uint32_t FallbackTargetBacklightFrequency {120000}; /** * User-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 at system start. * Can be specified via max-backlight-freq property. */ + // TODO: DEPRECATED uint32_t targetBacklightFrequency {}; /** * User-requested pwm control value obtained from BXT_BLC_PWM_CTL1. */ + // TODO: DEPRECATED uint32_t targetPwmControl {}; /** * Driver-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 write attempt at system start. */ + // TODO: DEPRECATED uint32_t driverBacklightFrequency {}; /** From 02085689b83839ee55eaa520d505d2e9d467384c Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 18:13:55 -0800 Subject: [PATCH 22/32] Coordinated Injections: Revise the list definition. --- WhateverGreen/kern_igfx.hpp | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index c671aee5..55279ab8 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -368,10 +368,9 @@ class IGFX { * * @tparam T Specify the type of the trigger * @tparam I Specify the type of the function to inject code - * @tparam D Specify the concrete type of the descriptor * @example The trigger type can be an integer type to inject code based on a register address. */ - template + template struct InjectionDescriptor { /** * The trigger value to be monitored by the coordinator @@ -388,7 +387,7 @@ class IGFX { /** * A pointer to the next descriptor in a linked list */ - D *next; + InjectionDescriptor *next; /** * Create an injection descriptor conveniently @@ -618,12 +617,7 @@ class IGFX { * @note The injector function takes the controller along with the register address and returns void. * @note The injection is performed before the original function is invoked. */ - struct MMIOReadPrologue: InjectionDescriptor { - /** - * Inherits the constructor from the super class - */ - using InjectionDescriptor::InjectionDescriptor; - }; + using MMIOReadPrologue = InjectionDescriptor; /** * Defines the replacer injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` @@ -632,12 +626,7 @@ class IGFX { * @note The injector function takes the controller along with the register address and returns the register value. * @note The injection replaced the original function call. */ - struct MMIOReadReplacer: InjectionDescriptor { - /** - * Inherits the constructor from the super class - */ - using InjectionDescriptor::InjectionDescriptor; - }; + using MMIOReadReplacer = InjectionDescriptor; /** * Defines the epilogue injection descriptor for `AppleIntelFramebufferController::ReadRegister32()` @@ -646,12 +635,7 @@ class IGFX { * @note The injector function takes the controller along with the register address and its value, and returns the new value. * @note The injection is performed after the original function is invoked. */ - struct MMIOReadEpilogue: InjectionDescriptor { - /** - * Inherits the constructor from the super class - */ - using InjectionDescriptor::InjectionDescriptor; - }; + using MMIOReadEpilogue = InjectionDescriptor; /** * A submodule that provides read access to MMIO registers and coordinates injections to the read function @@ -694,12 +678,7 @@ class IGFX { * @note The injector function takes the controller along with the register address and its new value, and returns void. * @note This type is shared by all three kinds of injection descriptors. */ - struct MMIOWriteInjectionDescriptor: InjectionDescriptor { - /** - * Inherits the constructor from the super class - */ - using InjectionDescriptor::InjectionDescriptor; - }; + using MMIOWriteInjectionDescriptor = InjectionDescriptor; /** * A submodule that provides write access to MMIO registers and coordinates injections to the write function From 269291a8063d288aaf3299ad8eca0b0fea48e1a4 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 18:38:31 -0800 Subject: [PATCH 23/32] IGFX: Add a helper function to locate the real framebuffer from the bundle index. --- WhateverGreen/kern_igfx.cpp | 6 ++---- WhateverGreen/kern_igfx.hpp | 7 +++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 1ff97516..770749e4 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -207,6 +207,7 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { disableAccel = checkKernelArgument("-igfxvesa"); + // TODO: DEPRECATED // Enable CFL backlight patch on mobile CFL or if IGPU propery enable-cfl-backlight-fix is set int bkl = 0; if (PE_parse_boot_argn("igfxcflbklt", &bkl, sizeof(bkl))) @@ -767,10 +768,7 @@ void IGFX::TypeCCheckDisabler::processKernel(KernelPatcher &patcher, DeviceInfo } void IGFX::TypeCCheckDisabler::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { - // TODO: Helper Function `getRealFramebuffer()`? - auto realFramebuffer = (callbackIGFX->currentFramebuffer && callbackIGFX->currentFramebuffer->loadIndex == index) ? callbackIGFX->currentFramebuffer : callbackIGFX->currentFramebufferOpt; - - if (realFramebuffer == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur) { + if (callbackIGFX->getRealFramebuffer(index) == &kextIntelCFLFb || getKernelVersion() >= KernelVersion::BigSur) { KernelPatcher::RouteRequest request = { "__ZN31AppleIntelFramebufferController17IsTypeCOnlySystemEv", wrapIsTypeCOnlySystem diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 55279ab8..635d9c87 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -359,6 +359,13 @@ class IGFX { // and the framebuffer controller now maintains an array of `ports`. class AppleIntelPort; + /** + * Get the real framebuffer in use from the given bundle index + */ + KernelPatcher::KextInfo *getRealFramebuffer(size_t index) { + return (currentFramebuffer && currentFramebuffer->loadIndex == index) ? currentFramebuffer : currentFramebufferOpt; + } + // // MARK: - Patch Submodule & Injection Kits // From efbbcf5df37221014107821fa5636518e0a49e2e Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 19:13:43 -0800 Subject: [PATCH 24/32] BLR: Convert the backlight fix to a submodule. --- WhateverGreen/kern_igfx.cpp | 365 ++++++++++++++++++------------------ WhateverGreen/kern_igfx.hpp | 170 +++++++++-------- 2 files changed, 265 insertions(+), 270 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 770749e4..5e66ce66 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -207,19 +207,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { disableAccel = checkKernelArgument("-igfxvesa"); - // TODO: DEPRECATED - // Enable CFL backlight patch on mobile CFL or if IGPU propery enable-cfl-backlight-fix is set - int bkl = 0; - if (PE_parse_boot_argn("igfxcflbklt", &bkl, sizeof(bkl))) - cflBacklightPatch = bkl == 1 ? CoffeeBacklightPatch::On : CoffeeBacklightPatch::Off; - else if (info->videoBuiltin->getProperty("enable-cfl-backlight-fix")) - cflBacklightPatch = CoffeeBacklightPatch::On; - else if (currentFramebuffer == &kextIntelCFLFb && BaseDeviceInfo::get().modelType == WIOKit::ComputerModel::ComputerLaptop) - cflBacklightPatch = CoffeeBacklightPatch::Auto; - - if (WIOKit::getOSDataValue(info->videoBuiltin, "max-backlight-freq", targetBacklightFrequency)) - DBGLOG("igfx", "read custom backlight frequency %u", targetBacklightFrequency); - bool connectorLessFrame = info->reportedFramebufferIsConnectorLess; int gl = info->videoBuiltin->getProperty("disable-metal") != nullptr; @@ -255,8 +242,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { return true; if (dumpFramebufferToDisk || dumpPlatformTable || debugFramebuffer) return true; - if (cflBacklightPatch != CoffeeBacklightPatch::Off) - return true; return false; }; @@ -319,82 +304,7 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if ((currentFramebuffer && currentFramebuffer->loadIndex == index) || (currentFramebufferOpt && currentFramebufferOpt->loadIndex == index)) { - // Find actual framebuffer used (kaby or coffee) - auto realFramebuffer = (currentFramebuffer && currentFramebuffer->loadIndex == index) ? currentFramebuffer : currentFramebufferOpt; - // Accept Coffee FB and enable backlight patches unless Off (Auto turns them on by default). - bool bklCoffeeFb = realFramebuffer == &kextIntelCFLFb && cflBacklightPatch != CoffeeBacklightPatch::Off; - // Accept Kaby FB and enable backlight patches if On (Auto is irrelevant here). - bool bklKabyFb = realFramebuffer == &kextIntelKBLFb && cflBacklightPatch == CoffeeBacklightPatch::On; - // Solve ReadRegister32 just once as it is shared - // FIXME: Refactor generic register access as a separate submodule. - // Submodules that request access to these functions must set `PatchSubmodule::requiresGenericRegisterAccess` to `true` - // Also we need to consider the case where multiple submodules want to inject code into these functions. - // At this moment, the backlight fix is the only one that wraps these functions. - if (bklCoffeeFb || bklKabyFb //|| - /*RPSControl.enabled || ForceWakeWorkaround.enabled || modCoreDisplayClockFix.enabled*/) { - AppleIntelFramebufferController__ReadRegister32 = patcher.solveSymbol - (index, "__ZN31AppleIntelFramebufferController14ReadRegister32Em", address, size); - if (!AppleIntelFramebufferController__ReadRegister32) - SYSLOG("igfx", "Failed to find ReadRegister32"); - } - if (bklCoffeeFb || bklKabyFb //|| - /*RPSControl.enabled || ForceWakeWorkaround.enabled*/) { - AppleIntelFramebufferController__WriteRegister32 = patcher.solveSymbol - (index, "__ZN31AppleIntelFramebufferController15WriteRegister32Emj", address, size); - if (!AppleIntelFramebufferController__WriteRegister32) - SYSLOG("igfx", "Failed to find WriteRegister32"); - } - if (bklCoffeeFb || bklKabyFb) { - // Intel backlight is modeled via pulse-width modulation (PWM). See page 144 of: - // https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-kbl-vol12-display.pdf - // Singal-wise it looks as a cycle of signal levels on the timeline: - // 22111100221111002211110022111100 (4 cycles) - // 0 - no signal, 1 - no value (no pulse), 2 - pulse (light on) - // - Physical Cycle (0+1+2) defines maximum backlight frequency, limited by HW precision. - // - Base Cycle (1+2) defines [1/PWM Base Frequency], limited by physical cycle, see BXT_BLC_PWM_FREQ1. - // - Duty Cycle (2) defines [1/PWM Increment] - backlight level, - // [PWM Frequency Divider] - backlight max, see BXT_BLC_PWM_DUTY1. - // - Duty Cycle position (first vs last) is [PWM Polarity] - // - // Duty cycle = PWM Base Frequeny * (1 / PWM Increment) / PWM Frequency Divider - // - // On macOS there are extra limitations: - // - All values and operations are u32 (32-bit unsigned) - // - [1/PWM Increment] has 0 to 0xFFFF range - // - [PWM Frequency Divider] is fixed to be 0xFFFF - // - [PWM Base Frequency] is capped by 0xFFFF (to avoid u32 wraparound), and is hardcoded - // either in Framebuffer data (pre-CFL) or in the code (CFL: 7777 or 22222). - // - // On CFL the following patches have to be applied: - // - Hardcoded [PWM Base Frequency] should be patched or set after the hardcoded value is written by patching - // hardcoded frequencies. 65535 is used by default. - // - If [PWM Base Frequency] is > 65535, to avoid a wraparound code calculating BXT_BLC_PWM_DUTY1 - // should be replaced to use 64-bit arithmetics. - // [PWM Base Frequency] can be specified via igfxbklt=1 boot-arg or backlight-base-frequency property. - - // This patch will overwrite WriteRegister32 function to rescale all the register writes of backlight controller. - // Slightly different methods are used for CFL hardware running on KBL and CFL drivers. - - if (AppleIntelFramebufferController__ReadRegister32 && - AppleIntelFramebufferController__WriteRegister32) { - (bklCoffeeFb ? orgCflReadRegister32 : orgKblReadRegister32) = AppleIntelFramebufferController__ReadRegister32; - - patcher.eraseCoverageInstPrefix(reinterpret_cast(AppleIntelFramebufferController__WriteRegister32)); - auto orgRegWrite = reinterpret_cast - (patcher.routeFunction(reinterpret_cast(AppleIntelFramebufferController__WriteRegister32), reinterpret_cast(bklCoffeeFb ? wrapCflWriteRegister32 : wrapKblWriteRegister32), true)); - - if (orgRegWrite) { - (bklCoffeeFb ? orgCflWriteRegister32 : orgKblWriteRegister32) = orgRegWrite; - } else { - SYSLOG("igfx", "failed to route WriteRegister32 for cfl %d", bklCoffeeFb); - patcher.clearError(); - } - } else { - SYSLOG("igfx", "failed to find ReadRegister32 for cfl %d", bklCoffeeFb); - patcher.clearError(); - } - } - + // Iterate through each submodule and redirect the request if and only if the submodule is enabled for (auto submodule : submodules) if (submodule->enabled) @@ -991,6 +901,183 @@ bool IGFX::ReadDescriptorPatch::globalPageTableRead(void *hardwareGlobalPageTabl return (flags & 3U) != 0; } +// MARK: - Backlight Registers Fix + +void IGFX::BacklightRegistersFix::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; + + // We need R/W access to MMIO registers + requiresMMIORegistersReadAccess = true; + requiresMMIORegistersWriteAccess = true; +} + +void IGFX::BacklightRegistersFix::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + enabled = checkKernelArgument("-igfxblr"); + if (!enabled) + enabled = info->videoBuiltin->getProperty("enable-backlight-registers-fix") != nullptr; + if (!enabled) + return; + + if (WIOKit::getOSDataValue(info->videoBuiltin, "max-backlight-freq", targetBacklightFrequency)) + DBGLOG("igfx", "BLR: Will use the custom backlight frequency %u.", targetBacklightFrequency); +} + +void IGFX::BacklightRegistersFix::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + // Intel backlight is modeled via pulse-width modulation (PWM). See page 144 of: + // https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-kbl-vol12-display.pdf + // Singal-wise it looks as a cycle of signal levels on the timeline: + // 22111100221111002211110022111100 (4 cycles) + // 0 - no signal, 1 - no value (no pulse), 2 - pulse (light on) + // - Physical Cycle (0+1+2) defines maximum backlight frequency, limited by HW precision. + // - Base Cycle (1+2) defines [1/PWM Base Frequency], limited by physical cycle, see BXT_BLC_PWM_FREQ1. + // - Duty Cycle (2) defines [1/PWM Increment] - backlight level, + // [PWM Frequency Divider] - backlight max, see BXT_BLC_PWM_DUTY1. + // - Duty Cycle position (first vs last) is [PWM Polarity] + // + // Duty cycle = PWM Base Frequeny * (1 / PWM Increment) / PWM Frequency Divider + // + // On macOS there are extra limitations: + // - All values and operations are u32 (32-bit unsigned) + // - [1/PWM Increment] has 0 to 0xFFFF range + // - [PWM Frequency Divider] is fixed to be 0xFFFF + // - [PWM Base Frequency] is capped by 0xFFFF (to avoid u32 wraparound), and is hardcoded + // either in Framebuffer data (pre-CFL) or in the code (CFL: 7777 or 22222). + // + // On CFL the following patches have to be applied: + // - Hardcoded [PWM Base Frequency] should be patched or set after the hardcoded value is written by patching + // hardcoded frequencies. 65535 is used by default. + // - If [PWM Base Frequency] is > 65535, to avoid a wraparound code calculating BXT_BLC_PWM_DUTY1 + // should be replaced to use 64-bit arithmetics. + // [PWM Base Frequency] can be specified via igfxbklt=1 boot-arg or backlight-base-frequency property. + + // This patch will overwrite WriteRegister32 function to rescale all the register writes of backlight controller. + // Slightly different methods are used for CFL hardware running on KBL and CFL drivers. + // Guard: Register injections based on the current framebuffer in use + if (callbackIGFX->getRealFramebuffer(index) == &kextIntelKBLFb) { + DBGLOG("igfx", "BLR: [KBL ] Will setup the fix for KBL platform."); + callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dKBLPWMFreq1); + callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dKBLPWMCtrl1); + } else { + DBGLOG("igfx", "BLR: [CFL+] Will setup the fix for CFL/ICL platform."); + callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dCFLPWMFreq1); + callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dCFLPWMDuty1); + } +} + +void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value) { + DBGLOG("igfx", "BLR: [KBL ] Called with register 0x%x and value 0x%x.", reg, value); + assertf(reg == BXT_BLC_PWM_FREQ1, "Fatal Error: Register should be BXT_BLC_PWM_FREQ1."); + + if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { + // Populate the hardware PWM frequency as initially set up by the system firmware. + callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = callbackIGFX->readRegister32(controller, BXT_BLC_PWM_FREQ1); + DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = 0x%x.", + callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_CTL1 = 0x%x.", + callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1)); + + if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { + // This should not happen with correctly written bootloader code, but in case it does, let's use a failsafe default value. + callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = FallbackTargetBacklightFrequency; + SYSLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = ZERO."); + } + } + + // For the KBL driver, 0xc8254 (BLC_PWM_PCH_CTL2) controls the backlight intensity. + // High 16 of this write are the denominator (frequency), low 16 are the numerator (duty cycle). + // Translate this into a write to c8258 (BXT_BLC_PWM_DUTY1) for the CFL hardware, scaled by the system-provided value in c8254 (BXT_BLC_PWM_FREQ1). + uint16_t frequency = (value & 0xffff0000U) >> 16U; + uint16_t dutyCycle = value & 0xffffU; + + uint32_t rescaledValue = frequency == 0 ? 0 : static_cast((dutyCycle * static_cast(callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency)) / static_cast(frequency)); + DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", + dutyCycle, frequency, rescaledValue, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + + // Reset the hardware PWM frequency. Write the original system value if the driver-requested value is nonzero. If the driver requests + // zero, we allow that, since it's trying to turn off the backlight PWM for sleep. + callbackIGFX->writeRegister32(controller, BXT_BLC_PWM_FREQ1, frequency ? callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency : 0); + + // Finish by writing the duty cycle. + callbackIGFX->writeRegister32(controller, BXT_BLC_PWM_DUTY1, rescaledValue); +} + +void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMCtrl1(void *controller, uint32_t reg, uint32_t value) { + DBGLOG("igfx", "BLR: [KBL ] Called with register 0x%x and value 0x%x.", reg, value); + assertf(reg == BXT_BLC_PWM_CTL1, "Fatal Error: Register should be BXT_BLC_PWM_CTL1."); + + if (callbackIGFX->modBacklightRegistersFix.targetPwmControl == 0) { + // Save the original hardware PWM control value + callbackIGFX->modBacklightRegistersFix.targetPwmControl = callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1); + } + + DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: Write BXT_BLC_PWM_CTL1 0x%x, previous was 0x%x.", + value, callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1)); + + if (value) { + // Set the PWM frequency before turning it on to avoid the 3 minute blackout bug + callbackIGFX->writeRegister32(controller, BXT_BLC_PWM_FREQ1, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + + // Use the original hardware PWM control value. + value = callbackIGFX->modBacklightRegistersFix.targetPwmControl; + } + + // Finish by writing the new value + callbackIGFX->writeRegister32(controller, reg, value); +} + +void IGFX::BacklightRegistersFix::wrapCFLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value) { + DBGLOG("igfx", "BLR: [CFL+] Called with register 0x%x and value 0x%x.", reg, value); + assertf(reg == BXT_BLC_PWM_FREQ1, "Fatal Error: Register should be BXT_BLC_PWM_FREQ1."); + + if (value && value != callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency) { + DBGLOG("igfx", "BRL: [CFL+] WriteRegister32: Driver requested BXT_BLC_PWM_FREQ1 = 0x%x.", value); + callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency = value; + } + + if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { + // Save the hardware PWM frequency as initially set up by the system firmware. + // We'll need this to restore later after system sleep. + callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = callbackIGFX->readRegister32(controller, BXT_BLC_PWM_FREQ1); + DBGLOG("igfx", "BRL: [CFL+] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = 0x%x.", callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + + if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { + // This should not happen with correctly written bootloader code, but in case it does, let's use a failsafe default value. + callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = FallbackTargetBacklightFrequency; + SYSLOG("igfx", "BRL: [CFL+] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = ZERO."); + } + } + + if (value) { + // Nonzero writes to this register need to use the original system value. + // Yet the driver can safely write zero to this register as part of system sleep. + value = callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency; + } + + // Finish by writing the new value + callbackIGFX->writeRegister32(controller, reg, value); +} + +void IGFX::BacklightRegistersFix::wrapCFLWriteRegisterPWMDuty1(void *controller, uint32_t reg, uint32_t value) { + DBGLOG("igfx", "BLR: [CFL+] Called with register 0x%x and value 0x%x.", reg, value); + assertf(reg == BXT_BLC_PWM_DUTY1, "Fatal Error: Register should be BXT_BLC_PWM_DUTY1."); + + if (callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency && callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency) { + // Translate the PWM duty cycle between the driver scale value and the HW scale value + uint32_t rescaledValue = static_cast((value * static_cast(callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency)) / static_cast(callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency)); + DBGLOG("igfx", "BRL: WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", value, + callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency, rescaledValue, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + value = rescaledValue; + } else { + // This should never happen, but in case it does we should log it at the very least. + SYSLOG("igfx", "BRL: WriteRegister32: Write PWM_DUTY1 has zero frequency driver (%d) target (%d).", + callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); + } + + // Finish by writing the new value + callbackIGFX->writeRegister32(controller, reg, value); +} + // MARK: - TODO OSObject *IGFX::wrapCopyExistingServices(OSDictionary *matching, IOOptionBits inState, IOOptionBits options) { @@ -1100,102 +1187,6 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) { return ret; } -void IGFX::wrapCflWriteRegister32(void *that, uint32_t reg, uint32_t value) { - if (reg == BXT_BLC_PWM_FREQ1) { - if (value && value != callbackIGFX->driverBacklightFrequency) { - DBGLOG("igfx", "wrapCflWriteRegister32: driver requested BXT_BLC_PWM_FREQ1 = 0x%x", value); - callbackIGFX->driverBacklightFrequency = value; - } - - if (callbackIGFX->targetBacklightFrequency == 0) { - // Save the hardware PWM frequency as initially set up by the system firmware. - // We'll need this to restore later after system sleep. - callbackIGFX->targetBacklightFrequency = callbackIGFX->orgCflReadRegister32(that, BXT_BLC_PWM_FREQ1); - DBGLOG("igfx", "wrapCflWriteRegister32: system initialized BXT_BLC_PWM_FREQ1 = 0x%x", callbackIGFX->targetBacklightFrequency); - - if (callbackIGFX->targetBacklightFrequency == 0) { - // This should not happen with correctly written bootloader code, but in case it does, let's use a failsafe default value. - callbackIGFX->targetBacklightFrequency = FallbackTargetBacklightFrequency; - SYSLOG("igfx", "wrapCflWriteRegister32: system initialized BXT_BLC_PWM_FREQ1 is ZERO"); - } - } - - if (value) { - // Nonzero writes to this register need to use the original system value. - // Yet the driver can safely write zero to this register as part of system sleep. - value = callbackIGFX->targetBacklightFrequency; - } - } else if (reg == BXT_BLC_PWM_DUTY1) { - if (callbackIGFX->driverBacklightFrequency && callbackIGFX->targetBacklightFrequency) { - // Translate the PWM duty cycle between the driver scale value and the HW scale value - uint32_t rescaledValue = static_cast((value * static_cast(callbackIGFX->targetBacklightFrequency)) / - static_cast(callbackIGFX->driverBacklightFrequency)); - DBGLOG("igfx", "wrapCflWriteRegister32: write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x", value, - callbackIGFX->driverBacklightFrequency, rescaledValue, callbackIGFX->targetBacklightFrequency); - value = rescaledValue; - } else { - // This should never happen, but in case it does we should log it at the very least. - SYSLOG("igfx", "wrapCflWriteRegister32: write PWM_DUTY1 has zero frequency driver (%d) target (%d)", - callbackIGFX->driverBacklightFrequency, callbackIGFX->targetBacklightFrequency); - } - } - - callbackIGFX->orgCflWriteRegister32(that, reg, value); -} - -void IGFX::wrapKblWriteRegister32(void *that, uint32_t reg, uint32_t value) { - if (reg == BXT_BLC_PWM_FREQ1) { // aka BLC_PWM_PCH_CTL2 - if (callbackIGFX->targetBacklightFrequency == 0) { - // Populate the hardware PWM frequency as initially set up by the system firmware. - callbackIGFX->targetBacklightFrequency = callbackIGFX->orgKblReadRegister32(that, BXT_BLC_PWM_FREQ1); - DBGLOG("igfx", "wrapKblWriteRegister32: system initialized BXT_BLC_PWM_FREQ1 = 0x%x", callbackIGFX->targetBacklightFrequency); - DBGLOG("igfx", "wrapKblWriteRegister32: system initialized BXT_BLC_PWM_CTL1 = 0x%x", callbackIGFX->orgKblReadRegister32(that, BXT_BLC_PWM_CTL1)); - - if (callbackIGFX->targetBacklightFrequency == 0) { - // This should not happen with correctly written bootloader code, but in case it does, let's use a failsafe default value. - callbackIGFX->targetBacklightFrequency = FallbackTargetBacklightFrequency; - SYSLOG("igfx", "wrapKblWriteRegister32: system initialized BXT_BLC_PWM_FREQ1 is ZERO"); - } - } - - // For the KBL driver, 0xc8254 (BLC_PWM_PCH_CTL2) controls the backlight intensity. - // High 16 of this write are the denominator (frequency), low 16 are the numerator (duty cycle). - // Translate this into a write to c8258 (BXT_BLC_PWM_DUTY1) for the CFL hardware, scaled by the system-provided value in c8254 (BXT_BLC_PWM_FREQ1). - uint16_t frequency = (value & 0xffff0000U) >> 16U; - uint16_t dutyCycle = value & 0xffffU; - - uint32_t rescaledValue = frequency == 0 ? 0 : static_cast((dutyCycle * static_cast(callbackIGFX->targetBacklightFrequency)) / - static_cast(frequency)); - DBGLOG("igfx", "wrapKblWriteRegister32: write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x", - dutyCycle, frequency, rescaledValue, callbackIGFX->targetBacklightFrequency); - - // Reset the hardware PWM frequency. Write the original system value if the driver-requested value is nonzero. If the driver requests - // zero, we allow that, since it's trying to turn off the backlight PWM for sleep. - callbackIGFX->orgKblWriteRegister32(that, BXT_BLC_PWM_FREQ1, frequency ? callbackIGFX->targetBacklightFrequency : 0); - - // Finish by writing the duty cycle. - reg = BXT_BLC_PWM_DUTY1; - value = rescaledValue; - } else if (reg == BXT_BLC_PWM_CTL1) { - if (callbackIGFX->targetPwmControl == 0) { - // Save the original hardware PWM control value - callbackIGFX->targetPwmControl = callbackIGFX->orgKblReadRegister32(that, BXT_BLC_PWM_CTL1); - } - - DBGLOG("igfx", "wrapKblWriteRegister32: write BXT_BLC_PWM_CTL1 0x%x, previous was 0x%x", value, callbackIGFX->orgKblReadRegister32(that, BXT_BLC_PWM_CTL1)); - - if (value) { - // Set the PWM frequency before turning it on to avoid the 3 minute blackout bug - callbackIGFX->orgKblWriteRegister32(that, BXT_BLC_PWM_FREQ1, callbackIGFX->targetBacklightFrequency); - - // Use the original hardware PWM control value. - value = callbackIGFX->targetPwmControl; - } - } - - callbackIGFX->orgKblWriteRegister32(that, reg, value); -} - bool IGFX::wrapGetOSInformation(IOService *that) { auto cpuGeneration = BaseDeviceInfo::get().cpuGeneration; diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 635d9c87..c5139860 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -142,14 +142,6 @@ class IGFX { */ static constexpr size_t MaxFramebufferPatchCount = 10; - /** - * Backlight registers - */ - // TODO: DEPRECATED - static constexpr uint32_t BXT_BLC_PWM_CTL1 = 0xC8250; - static constexpr uint32_t BXT_BLC_PWM_FREQ1 = 0xC8254; - static constexpr uint32_t BXT_BLC_PWM_DUTY1 = 0xC8258; - /** * Number of SNB frames in a framebuffer kext */ @@ -268,39 +260,6 @@ class IGFX { */ mach_vm_address_t orgIgBufferGetGpuVirtualAddress {}; - /** - * Original AppleIntelFramebufferController::ReadRegister32 function - */ - // TODO: DEPRECATED - uint32_t (*orgCflReadRegister32)(void *, uint32_t) {nullptr}; - uint32_t (*orgKblReadRegister32)(void *, uint32_t) {nullptr}; - - /** - * Original AppleIntelFramebufferController::WriteRegister32 function - */ - // TODO: DEPRECATED - void (*orgCflWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; - void (*orgKblWriteRegister32)(void *, uint32_t, uint32_t) {nullptr}; - - /** - * Coffee Lake backlight patch configuration options - */ - // TODO: DEPRECATED REMOVED - enum class CoffeeBacklightPatch { - Auto = -1, - On = 1, - Off = 0 - }; - - /** - * Set to On if Coffee Lake backlight patch type required - * - boot-arg igfxcflbklt=0/1 forcibly turns patch on or off (override) - * - IGPU property enable-cfl-backlight-fix turns patch on - * - laptop with CFL CPU and CFL IGPU drivers turns patch on - */ - // TODO: DEPRECATED REMOVED - CoffeeBacklightPatch cflBacklightPatch {CoffeeBacklightPatch::Off}; - /** * Set to true to disable Metal support */ @@ -345,11 +304,6 @@ class IGFX { * Trace framebuffer logic */ bool debugFramebuffer {false}; - - // TODO: DEPRECATED - // NOTE: the MMIO space is also available at RC6_RegBase - uint32_t (*AppleIntelFramebufferController__ReadRegister32)(void*,uint32_t) {}; - void (*AppleIntelFramebufferController__WriteRegister32)(void*,uint32_t,uint32_t) {}; // The opaque framebuffer controller type on BDW+ class AppleIntelFramebufferController; @@ -1533,6 +1487,93 @@ class IGFX { void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modReadDescriptorPatch; + /** + * A submodule to patch backlight register values and thus avoid 3-minute black screen on KBL+ + */ + class BacklightRegistersFix: public PatchSubmodule { + /** + * Backlight registers + */ + static constexpr uint32_t BXT_BLC_PWM_CTL1 = 0xC8250; + static constexpr uint32_t BXT_BLC_PWM_FREQ1 = 0xC8254; + static constexpr uint32_t BXT_BLC_PWM_DUTY1 = 0xC8258; + + /** + * Fallback user-requested backlight frequency in case 0 was initially written to the register. + */ + static constexpr uint32_t FallbackTargetBacklightFrequency = 120000; + + /** + * [Common] User-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 at system start. + * Can be specified via max-backlight-freq property. + */ + uint32_t targetBacklightFrequency {}; + + /** + * [KBL] User-requested pwm control value obtained from BXT_BLC_PWM_CTL1. + */ + uint32_t targetPwmControl {}; + + /** + * [CFL, ICL] Driver-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 write attempt at system start. + */ + uint32_t driverBacklightFrequency {}; + + /** + * [KBL] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 + * + * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_FREQ1`. + */ + static void wrapKBLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value); + + /** + * [KBL] Wrapper to fix the value of BXT_BLC_PWM_CTL1 + * + * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_CTL1`. + */ + static void wrapKBLWriteRegisterPWMCtrl1(void *controller, uint32_t reg, uint32_t value); + + /** + * [CFL, ICL] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 + * + * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_FREQ1`. + */ + static void wrapCFLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value); + + /** + * [CFL, ICL] Wrapper to fix the value of BXT_BLC_PWM_DUTY1 + * + * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_DUTY1`. + */ + static void wrapCFLWriteRegisterPWMDuty1(void *controller, uint32_t reg, uint32_t value); + + /** + * [KBL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 + */ + MMIOWriteInjectionDescriptor dKBLPWMFreq1 {BXT_BLC_PWM_FREQ1, wrapKBLWriteRegisterPWMFreq1}; + + /** + * [KBL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_CTL1 + */ + MMIOWriteInjectionDescriptor dKBLPWMCtrl1 {BXT_BLC_PWM_CTL1 , wrapKBLWriteRegisterPWMCtrl1}; + + /** + * [CFL, ICL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 + */ + MMIOWriteInjectionDescriptor dCFLPWMFreq1 {BXT_BLC_PWM_FREQ1, wrapCFLWriteRegisterPWMFreq1}; + + /** + * [CFL, ICL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_DUTY1 + */ + MMIOWriteInjectionDescriptor dCFLPWMDuty1 {BXT_BLC_PWM_DUTY1, wrapCFLWriteRegisterPWMDuty1}; + + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modBacklightRegistersFix; + /** * A collection of submodules */ @@ -1603,37 +1644,6 @@ class IGFX { */ uint32_t realBinarySize {}; - /** - * Store backlight level - */ - // TODO: UNUSED - uint32_t backlightLevel {}; // Unused - - /** - * Fallback user-requested backlight frequency in case 0 was initially written to the register. - */ - // TODO: DEPRECATED - static constexpr uint32_t FallbackTargetBacklightFrequency {120000}; - - /** - * User-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 at system start. - * Can be specified via max-backlight-freq property. - */ - // TODO: DEPRECATED - uint32_t targetBacklightFrequency {}; - - /** - * User-requested pwm control value obtained from BXT_BLC_PWM_CTL1. - */ - // TODO: DEPRECATED - uint32_t targetPwmControl {}; - - /** - * Driver-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 write attempt at system start. - */ - // TODO: DEPRECATED - uint32_t driverBacklightFrequency {}; - /** * Explore the framebuffer structure in Apple's Intel graphics driver */ @@ -1668,12 +1678,6 @@ class IGFX { */ static bool wrapAcceleratorStart(IOService *that, IOService *provider); - /** - * Wrapped AppleIntelFramebufferController::WriteRegister32 function - */ - static void wrapCflWriteRegister32(void *that, uint32_t reg, uint32_t value); - static void wrapKblWriteRegister32(void *that, uint32_t reg, uint32_t value); - /** * AppleIntelFramebufferController::getOSInformation wrapper to patch framebuffer data */ From 432bb89c360790dc689e763dcbd00a4f41546384 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 19:46:55 -0800 Subject: [PATCH 25/32] DBG: Convert the framebuffer logic tracer to a submodule. --- WhateverGreen/kern_igfx.cpp | 6 +----- WhateverGreen/kern_igfx.hpp | 16 +++++++++++----- WhateverGreen/kern_igfx_debug.cpp | 24 +++++++++++++++--------- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 5e66ce66..2acb0ca7 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -189,7 +189,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { #ifdef DEBUG dumpFramebufferToDisk = checkKernelArgument("-igfxdump"); dumpPlatformTable = checkKernelArgument("-igfxfbdump"); - debugFramebuffer = checkKernelArgument("-igfxfbdbg"); #endif if (supportsGuCFirmware && getKernelVersion() >= KernelVersion::HighSierra) { @@ -240,7 +239,7 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { auto requiresFramebufferPatches = [this]() { if (applyFramebufferPatch || hdmiAutopatch) return true; - if (dumpFramebufferToDisk || dumpPlatformTable || debugFramebuffer) + if (dumpFramebufferToDisk || dumpPlatformTable) return true; return false; }; @@ -310,9 +309,6 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if (submodule->enabled) submodule->processFramebufferKext(patcher, index, address, size); - if (debugFramebuffer) - loadFramebufferDebug(patcher, index, address, size); - if (applyFramebufferPatch || dumpFramebufferToDisk || dumpPlatformTable || hdmiAutopatch) { framebufferStart = reinterpret_cast(address); framebufferSize = size; diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index c5139860..f425ac96 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -300,11 +300,6 @@ class IGFX { */ bool dumpFramebufferToDisk {false}; - /** - * Trace framebuffer logic - */ - bool debugFramebuffer {false}; - // The opaque framebuffer controller type on BDW+ class AppleIntelFramebufferController; @@ -1574,6 +1569,17 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modBacklightRegistersFix; + /** + * A submodule to provide support for debugging the framebuffer driver + */ + class FramebufferDebugSupport: public PatchSubmodule { + public: + // MARK: Patch Submodule IMP + void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; + void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + } modFramebufferDebugSupport; + /** * A collection of submodules */ diff --git a/WhateverGreen/kern_igfx_debug.cpp b/WhateverGreen/kern_igfx_debug.cpp index f9d29d23..35c6e496 100644 --- a/WhateverGreen/kern_igfx_debug.cpp +++ b/WhateverGreen/kern_igfx_debug.cpp @@ -326,11 +326,23 @@ static IOReturn fbdebugWrapFBClientDoAttribute(void *fbclient, uint32_t attribut return ret; } +#endif + +void IGFX::FramebufferDebugSupport::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::FramebufferDebugSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { +#ifdef DEBUG + enabled = checkKernelArgument("-igfxfbdbg"); +#endif +} -void IGFX::loadFramebufferDebug(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +void IGFX::FramebufferDebugSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { SYSLOG("igfx", "using framebuffer debug r15"); - if (modAGDCDisabler.enabled) + if (callbackIGFX->modAGDCDisabler.enabled) PANIC("igfx", "igfxagdc=0 is not compatible with framebuffer debugging"); KernelPatcher::RouteRequest requests[] = { @@ -352,11 +364,5 @@ void IGFX::loadFramebufferDebug(KernelPatcher &patcher, size_t index, mach_vm_ad }; if (!patcher.routeMultiple(index, requests, address, size, true, true)) - SYSLOG("igfx", "failed to route igfx tracing"); + SYSLOG("igfx", "DBG: Failed to route igfx tracing."); } - -#else -void IGFX::loadFramebufferDebug(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { - PANIC("igfx", "fb debug is a debug-only feature"); -} -#endif From e31fcf94c6a1dab7e59d70541974d79f2172771d Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 20:18:08 -0800 Subject: [PATCH 26/32] PatchSubmodule: Enable a shared module if it is required by an active submodule. --- WhateverGreen/kern_igfx.cpp | 25 +++++++++++++++++++++++++ WhateverGreen/kern_igfx.hpp | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 2acb0ca7..e4d55ded 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -361,6 +361,15 @@ void IGFX::FramebufferControllerAccessSupport::init() { requiresPatchingFramebuffer = true; } +void IGFX::FramebufferControllerAccessSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { + // Enable if at least one active submodule relies on this shared module + for (auto submodule : callbackIGFX->submodules) + if (submodule->enabled) + enabled |= submodule->requiresGlobalFramebufferControllersAccess; + + DBGLOG("igfx", "FCA: Enabled = %d.", enabled); +} + void IGFX::FramebufferControllerAccessSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { KernelPatcher::SolveRequest request("_gController", controllers); if (!patcher.solveMultiple(index, &request, 1, address, size)) { @@ -385,6 +394,14 @@ void IGFX::MMIORegistersReadSupport::init() { void IGFX::MMIORegistersReadSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { // Enable the verbose output if the boot argument is found verbose = checkKernelArgument("-igfxvamregs"); + enabled |= verbose; + + // Enable if at least one active submodule relies on this shared module + for (auto submodule : callbackIGFX->submodules) + if (submodule->enabled) + enabled |= submodule->requiresMMIORegistersReadAccess; + + DBGLOG("igfx", "RRS: Enabled = %d.", enabled); } void IGFX::MMIORegistersReadSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { @@ -447,6 +464,14 @@ void IGFX::MMIORegistersWriteSupport::init() { void IGFX::MMIORegistersWriteSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { // Enable the verbose output if the boot argument is found verbose = checkKernelArgument("-igfxvamregs"); + enabled |= verbose; + + // Enable if at least one active submodule relies on this shared module + for (auto submodule : callbackIGFX->submodules) + if (submodule->enabled) + enabled |= submodule->requiresMMIORegistersWriteAccess; + + DBGLOG("igfx", "RWS: Enabled = %d.", enabled); } void IGFX::MMIORegistersWriteSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index f425ac96..eb4c7841 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -562,6 +562,7 @@ class IGFX { // MARK: Patch Submodule IMP void init() override; + void processKernel(KernelPatcher &patcher, DeviceInfo *info) override; void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; void disableDependentSubmodules() override; } modFramebufferControllerAccessSupport; @@ -1287,7 +1288,7 @@ class IGFX { /** * A submodule that applies patches based on the framebuffer index */ - class FramebufferModiferV2: public PatchSubmodule { + class FramebufferModifer: public PatchSubmodule { protected: /** * Indices of framebuffers to be patched @@ -1326,7 +1327,7 @@ class IGFX { /** * A submodule to ensure that each modeset operation is a complete one */ - class ForceCompleteModeset: public FramebufferModiferV2 { + class ForceCompleteModeset: public FramebufferModifer { /** * Original AppleIntelFramebufferController::hwRegsNeedUpdate function */ @@ -1347,7 +1348,7 @@ class IGFX { /** * A submodule to ensure that each display is online */ - class ForceOnlineDisplay: public FramebufferModiferV2 { + class ForceOnlineDisplay: public FramebufferModifer { /** * Original AppleIntelFramebuffer::getDisplayStatus function */ @@ -1580,10 +1581,37 @@ class IGFX { void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modFramebufferDebugSupport; + /** + * A collection of shared submodules + */ + PatchSubmodule *sharedSubmodules[3] = { + &modFramebufferControllerAccessSupport, + &modMMIORegistersReadSupport, + &modMMIORegistersWriteSupport + }; + /** * A collection of submodules */ - PatchSubmodule *submodules[6] = { &modDVMTCalcFix, &modDPCDMaxLinkRateFix, &modCoreDisplayClockFix, &modHDMIDividersCalcFix, &modLSPCONDriverSupport, &modAdvancedI2COverAUXSupport }; + PatchSubmodule *submodules[17] = { + &modDVMTCalcFix, + &modDPCDMaxLinkRateFix, + &modCoreDisplayClockFix, + &modHDMIDividersCalcFix, + &modLSPCONDriverSupport, + &modAdvancedI2COverAUXSupport, + &modRPSControlPatch, + &modForceWakeWorkaround, + &modForceCompleteModeset, + &modForceOnlineDisplay, + &modAGDCDisabler, + &modTypeCCheckDisabler, + &modBlackScreenFix, + &modPAVPDisabler, + &modReadDescriptorPatch, + &modBacklightRegistersFix, + &modFramebufferDebugSupport + }; /** * Prevent IntelAccelerator from starting. From 4d1d965e0366d5de99167631408aea16bcd144f2 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 20:59:33 -0800 Subject: [PATCH 27/32] FWW: Fix an issue that the patch processes the wrong kext. --- WhateverGreen/kern_igfx.hpp | 2 +- WhateverGreen/kern_igfx_pm.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index eb4c7841..3105111c 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -1282,7 +1282,7 @@ class IGFX { public: // MARK: Patch Submodule IMP void init() override; - void processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; + void processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) override; } modForceWakeWorkaround; /** diff --git a/WhateverGreen/kern_igfx_pm.cpp b/WhateverGreen/kern_igfx_pm.cpp index ad8d4837..66ff8b65 100644 --- a/WhateverGreen/kern_igfx_pm.cpp +++ b/WhateverGreen/kern_igfx_pm.cpp @@ -306,8 +306,8 @@ void IGFX::ForceWakeWorkaround::forceWake(void*, uint8_t set, uint32_t dom, uint } void IGFX::ForceWakeWorkaround::init() { - // We only need to patch the framebuffer driver - requiresPatchingFramebuffer = true; + // We only need to patch the acceleration driver + requiresPatchingGraphics = true; // Requires access to global framebuffer controllers requiresGlobalFramebufferControllersAccess = true; @@ -317,7 +317,7 @@ void IGFX::ForceWakeWorkaround::init() { requiresMMIORegistersWriteAccess = true; } -void IGFX::ForceWakeWorkaround::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { +void IGFX::ForceWakeWorkaround::processGraphicsKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { KernelPatcher::RouteRequest request = { "__ZN16IntelAccelerator26SafeForceWakeMultithreadedEbjj", forceWake From 16d2de07d4cdc920849d66562e5a462cd6e05a76 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 21:00:06 -0800 Subject: [PATCH 28/32] PatchSubmodule: Setup shared submodules. --- WhateverGreen/kern_igfx.cpp | 38 +++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index e4d55ded..742a9174 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -64,6 +64,8 @@ void IGFX::init() { // Initialize each submodule for (auto submodule : submodules) submodule->init(); + for (auto submodule : sharedSubmodules) + submodule->init(); auto &bdi = BaseDeviceInfo::get(); auto generation = bdi.cpuGeneration; auto family = bdi.cpuFamily; @@ -174,6 +176,8 @@ void IGFX::deinit() { // Deinitialize each submodule for (auto submodule : submodules) submodule->deinit(); + for (auto submodule : sharedSubmodules) + submodule->deinit(); } void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { @@ -200,10 +204,6 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { fwLoadMode = FW_APPLE; /* Do nothing, GuC is either unsupported due to low OS or Apple */ } - // Iterate through each submodule and redirect the request - for (auto submodule : submodules) - submodule->processKernel(patcher, info); - disableAccel = checkKernelArgument("-igfxvesa"); bool connectorLessFrame = info->reportedFramebufferIsConnectorLess; @@ -227,6 +227,16 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) { info->videoBuiltin->setProperty("AAPL00,DualLink", dualLinkBytes, sizeof(dualLinkBytes)); } + // Note that the order does matter + // We first iterate through each submodule and redirect the request + for (auto submodule : submodules) + submodule->processKernel(patcher, info); + + // Then process shared submodules + // A shared submodule will disable itself if no active submodule depends on it + for (auto submodule : sharedSubmodules) + submodule->processKernel(patcher, info); + // Iterate through each submodule and see if we need to patch the graphics and the framebuffer kext auto submodulesRequiresFramebufferPatch = false; auto submodulesRequiresGraphicsPatch = false; @@ -293,7 +303,15 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a loadIGScheduler4Patches(patcher, index, address, size); } - // Iterate through each submodule and redirect the request if and only if the submodule is enabled + // Note that the order is reversed at this stage + // We first process shared submodules + // If a shared submodule fails to process the acceleration driver, + // all subdmoules that depend on it will be disabled automatically. + for (auto submodule : sharedSubmodules) + if (submodule->enabled) + submodule->processGraphicsKext(patcher, index, address, size); + + // Then iterate through each submodule and redirect the request if and only if it is enabled for (auto submodule : submodules) if (submodule->enabled) submodule->processGraphicsKext(patcher, index, address, size); @@ -304,7 +322,15 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a if ((currentFramebuffer && currentFramebuffer->loadIndex == index) || (currentFramebufferOpt && currentFramebufferOpt->loadIndex == index)) { - // Iterate through each submodule and redirect the request if and only if the submodule is enabled + // Note that the order is reversed at this stage + // We first process shared submodules + // If a shared submodule fails to process the framebuffer driver, + // all subdmoules that depend on it will be disabled automatically. + for (auto submodule : sharedSubmodules) + if (submodule->enabled) + submodule->processFramebufferKext(patcher, index, address, size); + + // Then iterate through each submodule and redirect the request if and only if it is enabled for (auto submodule : submodules) if (submodule->enabled) submodule->processFramebufferKext(patcher, index, address, size); From 5934cc48c5af492f270190867f23036135d583d1 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Tue, 8 Dec 2020 21:09:44 -0800 Subject: [PATCH 29/32] BLR: Revise the debug output. --- WhateverGreen/kern_igfx.cpp | 24 ++++++++++++------------ WhateverGreen/kern_igfx.hpp | 18 ++++++++++-------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/WhateverGreen/kern_igfx.cpp b/WhateverGreen/kern_igfx.cpp index 742a9174..7ef56f8c 100644 --- a/WhateverGreen/kern_igfx.cpp +++ b/WhateverGreen/kern_igfx.cpp @@ -1002,7 +1002,7 @@ void IGFX::BacklightRegistersFix::processFramebufferKext(KernelPatcher &patcher, // Slightly different methods are used for CFL hardware running on KBL and CFL drivers. // Guard: Register injections based on the current framebuffer in use if (callbackIGFX->getRealFramebuffer(index) == &kextIntelKBLFb) { - DBGLOG("igfx", "BLR: [KBL ] Will setup the fix for KBL platform."); + DBGLOG("igfx", "BLR: [KBL*] Will setup the fix for KBL platform."); callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dKBLPWMFreq1); callbackIGFX->modMMIORegistersWriteSupport.replacerList.add(&dKBLPWMCtrl1); } else { @@ -1013,21 +1013,21 @@ void IGFX::BacklightRegistersFix::processFramebufferKext(KernelPatcher &patcher, } void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value) { - DBGLOG("igfx", "BLR: [KBL ] Called with register 0x%x and value 0x%x.", reg, value); + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: Called with register 0x%x and value 0x%x.", reg, value); assertf(reg == BXT_BLC_PWM_FREQ1, "Fatal Error: Register should be BXT_BLC_PWM_FREQ1."); if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { // Populate the hardware PWM frequency as initially set up by the system firmware. callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = callbackIGFX->readRegister32(controller, BXT_BLC_PWM_FREQ1); - DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = 0x%x.", + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = 0x%x.", callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); - DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_CTL1 = 0x%x.", + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: System initialized with BXT_BLC_PWM_CTL1 = 0x%x.", callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1)); if (callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency == 0) { // This should not happen with correctly written bootloader code, but in case it does, let's use a failsafe default value. callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency = FallbackTargetBacklightFrequency; - SYSLOG("igfx", "BLR: [KBL ] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = ZERO."); + SYSLOG("igfx", "BLR: [KBL*] WriteRegister32: System initialized with BXT_BLC_PWM_FREQ1 = ZERO."); } } @@ -1038,7 +1038,7 @@ void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMFreq1(void *controller, uint16_t dutyCycle = value & 0xffffU; uint32_t rescaledValue = frequency == 0 ? 0 : static_cast((dutyCycle * static_cast(callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency)) / static_cast(frequency)); - DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", dutyCycle, frequency, rescaledValue, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); // Reset the hardware PWM frequency. Write the original system value if the driver-requested value is nonzero. If the driver requests @@ -1050,7 +1050,7 @@ void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMFreq1(void *controller, } void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMCtrl1(void *controller, uint32_t reg, uint32_t value) { - DBGLOG("igfx", "BLR: [KBL ] Called with register 0x%x and value 0x%x.", reg, value); + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: Called with register 0x%x and value 0x%x.", reg, value); assertf(reg == BXT_BLC_PWM_CTL1, "Fatal Error: Register should be BXT_BLC_PWM_CTL1."); if (callbackIGFX->modBacklightRegistersFix.targetPwmControl == 0) { @@ -1058,7 +1058,7 @@ void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMCtrl1(void *controller, callbackIGFX->modBacklightRegistersFix.targetPwmControl = callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1); } - DBGLOG("igfx", "BLR: [KBL ] WriteRegister32: Write BXT_BLC_PWM_CTL1 0x%x, previous was 0x%x.", + DBGLOG("igfx", "BLR: [KBL*] WriteRegister32: Write BXT_BLC_PWM_CTL1 0x%x, previous was 0x%x.", value, callbackIGFX->readRegister32(controller, BXT_BLC_PWM_CTL1)); if (value) { @@ -1074,7 +1074,7 @@ void IGFX::BacklightRegistersFix::wrapKBLWriteRegisterPWMCtrl1(void *controller, } void IGFX::BacklightRegistersFix::wrapCFLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value) { - DBGLOG("igfx", "BLR: [CFL+] Called with register 0x%x and value 0x%x.", reg, value); + DBGLOG("igfx", "BLR: [CFL+] WriteRegister32: Called with register 0x%x and value 0x%x.", reg, value); assertf(reg == BXT_BLC_PWM_FREQ1, "Fatal Error: Register should be BXT_BLC_PWM_FREQ1."); if (value && value != callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency) { @@ -1106,18 +1106,18 @@ void IGFX::BacklightRegistersFix::wrapCFLWriteRegisterPWMFreq1(void *controller, } void IGFX::BacklightRegistersFix::wrapCFLWriteRegisterPWMDuty1(void *controller, uint32_t reg, uint32_t value) { - DBGLOG("igfx", "BLR: [CFL+] Called with register 0x%x and value 0x%x.", reg, value); + DBGLOG("igfx", "BLR: [CFL+] WriteRegister32: Called with register 0x%x and value 0x%x.", reg, value); assertf(reg == BXT_BLC_PWM_DUTY1, "Fatal Error: Register should be BXT_BLC_PWM_DUTY1."); if (callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency && callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency) { // Translate the PWM duty cycle between the driver scale value and the HW scale value uint32_t rescaledValue = static_cast((value * static_cast(callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency)) / static_cast(callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency)); - DBGLOG("igfx", "BRL: WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", value, + DBGLOG("igfx", "BRL: [CFL+] WriteRegister32: Write PWM_DUTY1 0x%x/0x%x, rescaled to 0x%x/0x%x.", value, callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency, rescaledValue, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); value = rescaledValue; } else { // This should never happen, but in case it does we should log it at the very least. - SYSLOG("igfx", "BRL: WriteRegister32: Write PWM_DUTY1 has zero frequency driver (%d) target (%d).", + SYSLOG("igfx", "BRL: [CFL+] WriteRegister32: Write PWM_DUTY1 has zero frequency driver (%d) target (%d).", callbackIGFX->modBacklightRegistersFix.driverBacklightFrequency, callbackIGFX->modBacklightRegistersFix.targetBacklightFrequency); } diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index 3105111c..e8940ed0 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -1485,6 +1485,8 @@ class IGFX { /** * A submodule to patch backlight register values and thus avoid 3-minute black screen on KBL+ + * + * @note Supported Platforms: KBL, CFL, ICL. */ class BacklightRegistersFix: public PatchSubmodule { /** @@ -1516,50 +1518,50 @@ class IGFX { uint32_t driverBacklightFrequency {}; /** - * [KBL] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 + * [KBL*] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 * * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_FREQ1`. */ static void wrapKBLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value); /** - * [KBL] Wrapper to fix the value of BXT_BLC_PWM_CTL1 + * [KBL*] Wrapper to fix the value of BXT_BLC_PWM_CTL1 * * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_CTL1`. */ static void wrapKBLWriteRegisterPWMCtrl1(void *controller, uint32_t reg, uint32_t value); /** - * [CFL, ICL] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 + * [CFL+] Wrapper to fix the value of BXT_BLC_PWM_FREQ1 * * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_FREQ1`. */ static void wrapCFLWriteRegisterPWMFreq1(void *controller, uint32_t reg, uint32_t value); /** - * [CFL, ICL] Wrapper to fix the value of BXT_BLC_PWM_DUTY1 + * [CFL+] Wrapper to fix the value of BXT_BLC_PWM_DUTY1 * * @note When this function is called, `reg` is guaranteed to be `BXT_BLC_PWM_DUTY1`. */ static void wrapCFLWriteRegisterPWMDuty1(void *controller, uint32_t reg, uint32_t value); /** - * [KBL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 + * [KBL*] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 */ MMIOWriteInjectionDescriptor dKBLPWMFreq1 {BXT_BLC_PWM_FREQ1, wrapKBLWriteRegisterPWMFreq1}; /** - * [KBL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_CTL1 + * [KBL*] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_CTL1 */ MMIOWriteInjectionDescriptor dKBLPWMCtrl1 {BXT_BLC_PWM_CTL1 , wrapKBLWriteRegisterPWMCtrl1}; /** - * [CFL, ICL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 + * [CFL+] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_FREQ1 */ MMIOWriteInjectionDescriptor dCFLPWMFreq1 {BXT_BLC_PWM_FREQ1, wrapCFLWriteRegisterPWMFreq1}; /** - * [CFL, ICL] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_DUTY1 + * [CFL+] A replacer descriptor that injects code when the register of interest is BXT_BLC_PWM_DUTY1 */ MMIOWriteInjectionDescriptor dCFLPWMDuty1 {BXT_BLC_PWM_DUTY1, wrapCFLWriteRegisterPWMDuty1}; From c8556577ed0253373a83780f46470f0fa3e8b563 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Wed, 9 Dec 2020 16:33:11 -0800 Subject: [PATCH 30/32] Update the readme and changelog. --- Changelog.md | 4 ++++ README.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 2d3f0545..c8db9468 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ WhateverGreen Changelog ======================= +#### v1.4.6 +- Backlight registers fix replaces the previous Coffee Lake backlight fix and is now available on Intel Ice Lake platforms. +- Boot argument `igfxcflbklt=1` as well as device property `enable-cfl-backlight-fix` are deprecated and replaced by `-igfxblr` and `enable-backlight-registers-fix`. + #### v1.4.5 - Enabled loading in safe mode (mainly for AGDP fixes) - Resolved an issue that the maximum link rate fix is not working properly on Intel Comet Lake platforms. (Thanks @CoronaHack) diff --git a/README.md b/README.md index 7d28487b..513d4ee9 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,6 @@ Read [FAQs](https://github.com/acidanthera/WhateverGreen/blob/master/Manual/) an - `-cdfon` (and `enable-hdmi20` property) to enable HDMI 2.0 patches. - `-igfxdump` to dump IGPU framebuffer kext to `/var/log/AppleIntelFramebuffer_X_Y` (available in DEBUG binaries). - `-igfxfbdump` to dump native and patched framebuffer table to ioreg at IOService:/IOResources/WhateverGreen -- `igfxcflbklt=1` boot argument (and `enable-cfl-backlight-fix` property) to enable CFL backlight patch - `applbkl=0` boot argument (and `applbkl` property) to disable AppleBacklight.kext patches for IGPU. In case of custom AppleBacklight profile- [read here.](https://github.com/acidanthera/WhateverGreen/blob/master/Manual/FAQ.OldPlugins.en.md) - `-igfxmlr` boot argument (and `enable-dpcd-max-link-rate-fix` property) to apply the maximum link rate fix. - `-igfxhdmidivs` boot argument (and `enable-hdmi-dividers-fix` property) to fix the infinite loop on establishing Intel HDMI connections with a higher pixel clock rate on SKL, KBL and CFL platforms. @@ -88,6 +87,7 @@ indices of connectors for which online status is enforced. Format is similar to - `igfxrpsc=1` boot argument (`rps-control` property) to enable RPS control patch (improves IGPU performance). - `-igfxcdc` boot argument (`enable-cdclk-frequency-fix` property) to support all valid Core Display Clock (CDCLK) frequencies on ICL platforms. [Read the manual](https://github.com/acidanthera/WhateverGreen/blob/master/Manual/FAQ.IntelHD.en.md) - `-igfxdvmt` boot argument (`enable-dvmt-calc-fix` property) to fix the kernel panic caused by an incorrectly calculated amount of DVMT pre-allocated memory on Intel ICL platforms. +- `-igfxblr` boot argument (and `enable-backlight-registers-fix` property) to fix backlight registers on KBL, CFL and ICL platforms. #### Credits From 48b6a177a13eb8411f95049b7f12059157f01231 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Wed, 9 Dec 2020 17:38:07 -0800 Subject: [PATCH 31/32] DBG: Fix an issue that the debug module does not compile under RELEASE scheme. --- WhateverGreen/kern_igfx_debug.cpp | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/WhateverGreen/kern_igfx_debug.cpp b/WhateverGreen/kern_igfx_debug.cpp index 35c6e496..5db46b2b 100644 --- a/WhateverGreen/kern_igfx_debug.cpp +++ b/WhateverGreen/kern_igfx_debug.cpp @@ -326,18 +326,6 @@ static IOReturn fbdebugWrapFBClientDoAttribute(void *fbclient, uint32_t attribut return ret; } -#endif - -void IGFX::FramebufferDebugSupport::init() { - // We only need to patch the framebuffer driver - requiresPatchingFramebuffer = true; -} - -void IGFX::FramebufferDebugSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { -#ifdef DEBUG - enabled = checkKernelArgument("-igfxfbdbg"); -#endif -} void IGFX::FramebufferDebugSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { SYSLOG("igfx", "using framebuffer debug r15"); @@ -366,3 +354,20 @@ void IGFX::FramebufferDebugSupport::processFramebufferKext(KernelPatcher &patche if (!patcher.routeMultiple(index, requests, address, size, true, true)) SYSLOG("igfx", "DBG: Failed to route igfx tracing."); } + +#else +void IGFX::FramebufferDebugSupport::processFramebufferKext(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) { + PANIC("igfx", "DBG: Framebuffer debug support is only available in debug build."); +} +#endif + +void IGFX::FramebufferDebugSupport::init() { + // We only need to patch the framebuffer driver + requiresPatchingFramebuffer = true; +} + +void IGFX::FramebufferDebugSupport::processKernel(KernelPatcher &patcher, DeviceInfo *info) { +#ifdef DEBUG + enabled = checkKernelArgument("-igfxfbdbg"); +#endif +} From 660eafd23d4ab08d21709b0f59aeb61118486113 Mon Sep 17 00:00:00 2001 From: FireWolf Date: Wed, 9 Dec 2020 19:41:50 -0800 Subject: [PATCH 32/32] IGFX: Fix the indentation. --- WhateverGreen/kern_igfx.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WhateverGreen/kern_igfx.hpp b/WhateverGreen/kern_igfx.hpp index e8940ed0..d24597b4 100644 --- a/WhateverGreen/kern_igfx.hpp +++ b/WhateverGreen/kern_igfx.hpp @@ -1593,7 +1593,7 @@ class IGFX { }; /** - * A collection of submodules + * A collection of submodules */ PatchSubmodule *submodules[17] = { &modDVMTCalcFix,