Skip to content

Commit

Permalink
igfx: use RPS control for all the command streamers (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
07151129 authored Jun 28, 2020
1 parent fee20a9 commit 6a1990e
Show file tree
Hide file tree
Showing 5 changed files with 186 additions and 9 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ WhateverGreen
- Implements the driver support for onboard LSPCON chips to enable DisplayPort to HDMI 2.0 output on some platforms with Intel IGPU.
- Enforces complete modeset on non-built-in displays on Kaby Lake and newer to fix booting to black screen.
- Allows non-supported cards to use HW video encoder (`-radcodec`)
- Fixes choopy video playback on Intel Kaby Lake and newer.

#### Documentation
Read [FAQs](https://github.com/acidanthera/WhateverGreen/blob/master/Manual/) and avoid asking any questions. No support is provided for the time being.
Expand Down Expand Up @@ -76,6 +77,7 @@ not in the list, the driver's logic is used to determine whether complete modese
- `igfxonlnfbs=MASK` boot argument (`force-online-framebuffers` device property) to specify
indices of connectors for which online tatus is enforced. Format is similar to `igfxfcmsfbs`.
- `wegtree=1` boot argument (`rebuild-device-tree` property) to force device renaming on Apple FW.
- `igfxnorpsc=1` boot argument (`no-rps-control` property) to disable RPS control patch.

#### Credits
- [Apple](https://www.apple.com) for macOS
Expand Down
4 changes: 4 additions & 0 deletions WhateverGreen.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1C748C2D1C21952C0024EED2 /* kern_start.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C748C2C1C21952C0024EED2 /* kern_start.cpp */; };
1C9CB7B01C789FF500231E41 /* kern_rad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1C9CB7AE1C789FF500231E41 /* kern_rad.cpp */; };
1C9CB7B11C789FF500231E41 /* kern_rad.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 1C9CB7AF1C789FF500231E41 /* kern_rad.hpp */; };
2F30012424A00F2800C590C3 /* kern_igfx_pm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2F30012324A00F2800C590C3 /* kern_igfx_pm.cpp */; };
CE1970FF21C380DF00B02AB4 /* kern_nvhda.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1970FD21C380DF00B02AB4 /* kern_nvhda.cpp */; };
CE19710021C380DF00B02AB4 /* kern_nvhda.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CE1970FE21C380DF00B02AB4 /* kern_nvhda.hpp */; };
CE1F61B92432DEE800201DF4 /* kern_igfx_debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CE1F61B82432DEE800201DF4 /* kern_igfx_debug.cpp */; };
Expand Down Expand Up @@ -77,6 +78,7 @@
1CF01C901C8CF97F002DCEA3 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
1CF01C921C8CF997002DCEA3 /* Changelog.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Changelog.md; sourceTree = "<group>"; };
1CF01C931C8DF02E002DCEA3 /* LICENSE.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE.txt; sourceTree = "<group>"; };
2F30012324A00F2800C590C3 /* kern_igfx_pm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = kern_igfx_pm.cpp; sourceTree = "<group>"; };
CE1970FD21C380DF00B02AB4 /* kern_nvhda.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = kern_nvhda.cpp; sourceTree = "<group>"; };
CE1970FE21C380DF00B02AB4 /* kern_nvhda.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = kern_nvhda.hpp; sourceTree = "<group>"; };
CE1F61B82432DEE800201DF4 /* kern_igfx_debug.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = kern_igfx_debug.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -187,6 +189,7 @@
CE7FC0AC20F5622700138088 /* kern_igfx.cpp */,
CE7FC0AD20F5622700138088 /* kern_igfx.hpp */,
CE1F61B82432DEE800201DF4 /* kern_igfx_debug.cpp */,
2F30012324A00F2800C590C3 /* kern_igfx_pm.cpp */,
CE7FC0A820F55E7400138088 /* kern_ngfx.cpp */,
CE7FC0A920F55E7400138088 /* kern_ngfx.hpp */,
CE7FC0B020F563CA00138088 /* kern_ngfx_asm.S */,
Expand Down Expand Up @@ -434,6 +437,7 @@
1C9CB7B01C789FF500231E41 /* kern_rad.cpp in Sources */,
CE766ED6210763B200A84567 /* kern_guc.cpp in Sources */,
CE7FC0B420F6809600138088 /* kern_shiki.cpp in Sources */,
2F30012424A00F2800C590C3 /* kern_igfx_pm.cpp in Sources */,
CE7FC0B120F563CA00138088 /* kern_ngfx_asm.S in Sources */,
CEA03B5E20EE825A00BA842F /* kern_weg.cpp in Sources */,
CE8190A21F1E3ECE00DE95F4 /* kern_model.cpp in Sources */,
Expand Down
31 changes: 25 additions & 6 deletions WhateverGreen/kern_igfx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ void IGFX::init() {
currentGraphics = &kextIntelKBL;
currentFramebuffer = &kextIntelKBLFb;
forceCompleteModeset.supported = forceCompleteModeset.enable = true;
RPSControl.enabled = true;
break;
case CPUInfo::CpuGeneration::CoffeeLake:
supportsGuCFirmware = true;
Expand All @@ -109,6 +110,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.enabled = true;
break;
case CPUInfo::CpuGeneration::CannonLake:
supportsGuCFirmware = true;
Expand Down Expand Up @@ -171,6 +173,13 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) {
dumpPlatformTable = checkKernelArgument("-igfxfbdump");
debugFramebuffer = checkKernelArgument("-igfxfbdbg");
#endif

uint32_t nrpsc = 0;
if (PE_parse_boot_argn("igfxnorpsc", &nrpsc, sizeof(nrpsc)) ||
WIOKit::getOSDataValue(info->videoBuiltin, "no-rps-control", nrpsc)) {
DBGLOG("weg", "RPS control patch overriden (%u)", nrpsc);
RPSControl.enabled &= !nrpsc;
}

uint32_t forceCompleteModeSet = 0;
if (PE_parse_boot_argn("igfxfcms", &forceCompleteModeSet, sizeof(forceCompleteModeSet))) {
Expand Down Expand Up @@ -310,7 +319,7 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) {
int gl = info->videoBuiltin->getProperty("disable-metal") != nullptr;
PE_parse_boot_argn("igfxgl", &gl, sizeof(gl));
forceOpenGL = gl == 1;

int metal = info->videoBuiltin->getProperty("enable-metal") != nullptr;
PE_parse_boot_argn("igfxmetal", &metal, sizeof(metal));
forceMetal = metal == 1;
Expand Down Expand Up @@ -347,6 +356,8 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) {
return true;
if (disableAGDC)
return true;
if (RPSControl.enabled)
return true;
return false;
};

Expand All @@ -363,6 +374,8 @@ void IGFX::processKernel(KernelPatcher &patcher, DeviceInfo *info) {
return true;
if (readDescriptorPatch)
return true;
if (RPSControl.enabled)
return true;
return false;
};

Expand Down Expand Up @@ -421,6 +434,9 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a
patcher.routeMultiple(index, &request, 1, address, size);
}

if (RPSControl.enabled)
RPSControl.initGraphics(patcher, index, address, size);

return true;
}

Expand Down Expand Up @@ -516,6 +532,9 @@ bool IGFX::processKext(KernelPatcher &patcher, size_t index, mach_vm_address_t a
if (!patcher.routeMultiple(index, &request, 1, address, size))
SYSLOG("igfx", "failed to route getDisplayStatus");
}

if (RPSControl.enabled)
RPSControl.initFB(patcher, index, address, size);

if (disableAGDC) {
KernelPatcher::RouteRequest request {"__ZN20IntelFBClientControl11doAttributeEjPmmS0_S0_P25IOExternalMethodArguments", wrapFBClientDoAttribute, orgFBClientDoAttribute};
Expand Down Expand Up @@ -767,7 +786,7 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) {

}
}

OSObject *metalPluginName = that->getProperty("MetalPluginName");
if (metalPluginName) {
metalPluginName->retain();
Expand All @@ -784,15 +803,15 @@ bool IGFX::wrapAcceleratorStart(IOService *that, IOService *provider) {
that->setName("IntelAccelerator");

bool ret = FunctionCast(wrapAcceleratorStart, callbackIGFX->orgAcceleratorStart)(that, provider);

if (metalPluginName) {
if (callbackIGFX->forceMetal) {
DBGLOG("igfx", "enabling metal support");
that->setProperty("MetalPluginName", metalPluginName);
}
metalPluginName->release();
}

return ret;
}

Expand Down Expand Up @@ -832,8 +851,8 @@ bool IGFX::wrapHwRegsNeedUpdate(void *controller, IOService *framebuffer, void *
// 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);
|| 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) {
Expand Down
20 changes: 17 additions & 3 deletions WhateverGreen/kern_igfx.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ class IGFX {
* Set to true to disable Metal support
*/
bool forceOpenGL {false};

/**
* Set to true to enable Metal support for offline rendering
*/
Expand Down Expand Up @@ -404,6 +404,20 @@ class IGFX {
}
};

struct RPSControl {
bool enabled {false};
uint32_t freq_max {0};

void initFB(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;

uint32_t (*AppleIntelFramebufferController__ReadRegister32)(void*,uint32_t) {};
void** gController {};
} RPSControl;

/**
* Ensure each modeset is a complete modeset.
*/
Expand Down Expand Up @@ -499,12 +513,12 @@ class IGFX {
* Driver-requested backlight frequency obtained from BXT_BLC_PWM_FREQ1 write attempt at system start.
*/
uint32_t driverBacklightFrequency {};

/**
* The default DPCD address
*/
static constexpr uint32_t DPCD_DEFAULT_ADDRESS = 0x0000;

/**
* The extended DPCD address
*/
Expand Down
138 changes: 138 additions & 0 deletions WhateverGreen/kern_igfx_pm.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// kern_igfx_pm.cpp
// WhateverGreen
//
// Created by Pb on 22/06/2020.
// Copyright © 2020 vit9696. All rights reserved.
//

#include <Headers/kern_atomic.hpp>
#include <Headers/kern_patcher.hpp>
#include <Headers/kern_devinfo.hpp>
#include <Headers/kern_cpu.hpp>
#include <Headers/kern_disasm.hpp>
#include <Library/LegacyIOService.h>
#include "kern_igfx.hpp"

namespace {
constexpr const char* log = "igfx_pm";

// For debugging
struct [[gnu::packed]] IGHwCsDesc {
char type;
char gap[4];
char *title;
char unk0[48];
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<void*>(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;

constexpr uint32_t GEN9_FREQUENCY_SHIFT = 23;
constexpr uint32_t GEN9_FREQ_SCALER = 3;
}

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();
}
}

/**
* Request maximum RPS at exec list submission.
* While this sounds dangerous, there appears to be a secondary mechanism
* that downclocks the GPU rather quickly back.
* Using any lower RPS lets that mechanism win the race.
*/
int IGFX::RPSControl::pmNotifyWrapper(unsigned int a0,unsigned int a1,unsigned long long * a2,unsigned int * freq) {
uint32_t cfreq = 0;

FunctionCast(IGFX::RPSControl::pmNotifyWrapper, callbackIGFX->RPSControl.orgPmNotifyWrapper)(a0, a1, a2, &cfreq);

if (!callbackIGFX->RPSControl.freq_max) {
callbackIGFX->RPSControl.freq_max = callbackIGFX->RPSControl.AppleIntelFramebufferController__ReadRegister32(*callbackIGFX->RPSControl.gController, GEN6_RP_STATE_CAP) & 0xff;
DBGLOG(log, "Read RP0 %d", callbackIGFX->RPSControl.freq_max);
}

DBGLOG(log, "pmNotifyWrapper sets freq 0x%x", cfreq);
*freq = (GEN9_FREQ_SCALER << GEN9_FREQUENCY_SHIFT) * callbackIGFX->RPSControl.freq_max;

return 0;
}

void IGFX::RPSControl::initFB(KernelPatcher &patcher, size_t index, mach_vm_address_t address, size_t size) {
AppleIntelFramebufferController__ReadRegister32 = patcher.solveSymbol<decltype(AppleIntelFramebufferController__ReadRegister32)>(index, "__ZN31AppleIntelFramebufferController14ReadRegister32Em", address, size);

gController = patcher.solveSymbol<decltype(gController)>(index, "_gController", address, size);

KernelPatcher::RouteRequest req {
"__ZL15pmNotifyWrapperjjPyPj",
&IGFX::RPSControl::pmNotifyWrapper,
orgPmNotifyWrapper
};

if (!(AppleIntelFramebufferController__ReadRegister32 && gController && patcher.routeMultiple(index, &req, 1, address, size, true, true)))
SYSLOG(log, "failed to route igfx FB PM functions");
}

0 comments on commit 6a1990e

Please sign in to comment.