sys-clk-OC
This commit is contained in:
524
Source/sys-clk-OC/sysmodule/src/clock_manager.cpp
Normal file
524
Source/sys-clk-OC/sysmodule/src/clock_manager.cpp
Normal file
@@ -0,0 +1,524 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------
|
||||
* "THE BEER-WARE LICENSE" (Revision 42):
|
||||
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
|
||||
* wrote this file. As long as you retain this notice you can do whatever you
|
||||
* want with this stuff. If you meet any of us some day, and you think this
|
||||
* stuff is worth it, you can buy us a beer in return. - The sys-clk authors
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#define FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK
|
||||
#include <nxExt.h>
|
||||
#include "errors.h"
|
||||
#include "clock_manager.h"
|
||||
#include "file_utils.h"
|
||||
#include "clocks.h"
|
||||
#include "process_management.h"
|
||||
#include <cstring>
|
||||
|
||||
ClockManager* ClockManager::instance = NULL;
|
||||
|
||||
ClockManager* ClockManager::GetInstance()
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
void ClockManager::Exit()
|
||||
{
|
||||
if(instance)
|
||||
{
|
||||
delete instance;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::Initialize()
|
||||
{
|
||||
if(!instance)
|
||||
{
|
||||
instance = new ClockManager();
|
||||
}
|
||||
}
|
||||
|
||||
ClockManager::ClockManager()
|
||||
{
|
||||
this->config = Config::CreateDefault();
|
||||
this->context = new SysClkContext;
|
||||
this->context->applicationId = 0;
|
||||
this->context->profile = SysClkProfile_Handheld;
|
||||
this->context->enabled = false;
|
||||
for(unsigned int i = 0; i < SysClkModule_EnumMax; i++)
|
||||
{
|
||||
this->context->freqs[i] = 0;
|
||||
this->context->overrideFreqs[i] = 0;
|
||||
}
|
||||
this->running = false;
|
||||
this->lastTempLogNs = 0;
|
||||
this->lastCsvWriteNs = 0;
|
||||
}
|
||||
|
||||
ClockManager::~ClockManager()
|
||||
{
|
||||
delete this->config;
|
||||
delete this->context;
|
||||
}
|
||||
|
||||
bool ClockManager::IsCpuBoostMode()
|
||||
{
|
||||
std::uint32_t confId = 0;
|
||||
Result rc = 0;
|
||||
rc = apmExtGetCurrentPerformanceConfiguration(&confId);
|
||||
ASSERT_RESULT_OK(rc, "apmExtGetCurrentPerformanceConfiguration");
|
||||
if(confId == 0x92220009 || confId == 0x9222000A)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
SysClkProfile ClockManager::ReverseNXProfile(bool ForceDock)
|
||||
{
|
||||
RealProfile = Clocks::GetCurrentProfile();
|
||||
switch(RealProfile)
|
||||
{
|
||||
case SysClkProfile_HandheldChargingOfficial:
|
||||
#ifdef FORCE_ALL_HANDHELD_MODES_TO_USE_DOCK_CLOCK
|
||||
case SysClkProfile_HandheldChargingUSB:
|
||||
case SysClkProfile_HandheldCharging:
|
||||
case SysClkProfile_Handheld:
|
||||
#endif
|
||||
if (ForceDock)
|
||||
return SysClkProfile_Docked;
|
||||
else
|
||||
return RealProfile;
|
||||
case SysClkProfile_Docked:
|
||||
if (ForceDock)
|
||||
return SysClkProfile_Docked;
|
||||
else
|
||||
return FileUtils::IsDownclockDockEnabled() ? SysClkProfile_HandheldChargingOfficial : SysClkProfile_Docked;
|
||||
default:
|
||||
return RealProfile;
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXTool()
|
||||
{
|
||||
char ReverseNXToolAsm[] = "_ZN2nn2oe18GetPerformanceModeEv.asm64"; // Checking one asm64 file is enough
|
||||
char ReverseNXToolAsmPath[128];
|
||||
uint8_t flag = 0;
|
||||
snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%s", ReverseNXToolAsm);
|
||||
|
||||
FILE *readReverseNXToolAsm;
|
||||
readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb");
|
||||
|
||||
// Enforce mode globally: Enabled
|
||||
if(readReverseNXToolAsm != NULL)
|
||||
{
|
||||
checkReverseNXToolAsm(readReverseNXToolAsm, &flag);
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Handheld globally");
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
isDockedReverseNX = false;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Enforce Docked globally");
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
isDockedReverseNX = true;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(ReverseNXToolAsmPath, sizeof ReverseNXToolAsmPath, "/SaltySD/patches/%016lX/%s", this->context->applicationId, ReverseNXToolAsm);
|
||||
readReverseNXToolAsm = fopen(ReverseNXToolAsmPath, "rb");
|
||||
// Found game-specific setting
|
||||
if(readReverseNXToolAsm != NULL)
|
||||
{
|
||||
checkReverseNXToolAsm(readReverseNXToolAsm, &flag);
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Handheld in %016lX", this->context->applicationId);
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
isDockedReverseNX = false;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-Tool patches detected: Force Docked in %016lX", this->context->applicationId);
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
isDockedReverseNX = true;
|
||||
isEnabledReverseNX = true;
|
||||
isEnabledReverseNXTool = true;
|
||||
break;
|
||||
default:
|
||||
isEnabledReverseNXTool = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ClockManager::GameStartBoost()
|
||||
{
|
||||
if (tickStartBoost && this->GetConfig()->Enabled())
|
||||
{
|
||||
if (Clocks::GetCurrentHz(SysClkModule_CPU) != MAX_CPU)
|
||||
{
|
||||
Clocks::SetHz(SysClkModule_CPU, MAX_CPU);
|
||||
this->context->freqs[SysClkModule_CPU] = MAX_CPU;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
// If user exit the game
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
tickStartBoost = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tickStartBoost == 1)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Boost done, reset to stock");
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
tickStartBoost--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClockManager::SetRunning(bool running)
|
||||
{
|
||||
this->running = running;
|
||||
}
|
||||
|
||||
bool ClockManager::Running()
|
||||
{
|
||||
return this->running;
|
||||
}
|
||||
|
||||
void ClockManager::Tick()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
|
||||
if(!GameStartBoost())
|
||||
{
|
||||
bool cpuBoost = FileUtils::IsBoostEnabled() ? IsCpuBoostMode() : false;
|
||||
if (this->RefreshContext() || this->config->Refresh())
|
||||
{
|
||||
std::uint32_t hz = 0;
|
||||
std::uint32_t hzForceOverride = 0;
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
hz = this->context->overrideFreqs[module];
|
||||
|
||||
if(!hz)
|
||||
{
|
||||
hz = this->config->GetAutoClockHz(this->context->applicationId, (SysClkModule)module, this->context->profile);
|
||||
hzForceOverride = this->config->GetAutoClockHz(0xA111111111111111, (SysClkModule)module, this->context->profile);
|
||||
if (!hz && hzForceOverride)
|
||||
hz = hzForceOverride;
|
||||
|
||||
if(isEnabledReverseNX && !hz)
|
||||
{
|
||||
switch(module)
|
||||
{
|
||||
case SysClkModule_CPU:
|
||||
hz = 1020'000'000;
|
||||
break;
|
||||
case SysClkModule_GPU:
|
||||
if (!isDockedReverseNX && ((FileUtils::IsDownclockDockEnabled() && RealProfile == SysClkProfile_Docked)
|
||||
|| RealProfile != SysClkProfile_Docked))
|
||||
hz = 460'800'000;
|
||||
else
|
||||
hz = 768'000'000;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hz)
|
||||
{
|
||||
hz = Clocks::GetNearestHz((SysClkModule)module, isEnabledReverseNX ? RealProfile : this->context->profile, hz);
|
||||
|
||||
if (hz != this->context->freqs[module] && this->context->enabled)
|
||||
{
|
||||
if (cpuBoost)
|
||||
{
|
||||
if (module == SysClkModule_CPU && hz < MAX_CPU)
|
||||
{
|
||||
hz = MAX_CPU;
|
||||
FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max");
|
||||
}
|
||||
}
|
||||
FileUtils::LogLine("[mgr] %s clock set : %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
Clocks::SetHz((SysClkModule)module, hz);
|
||||
this->context->freqs[module] = hz;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (FileUtils::IsBoostEnabled())
|
||||
{
|
||||
// If user doesn't set any freq but with sys-clk enabled, then boost CPU in CpuBoostMode
|
||||
if(cpuBoost && this->GetConfig()->Enabled())
|
||||
{
|
||||
if(this->context->freqs[SysClkModule_CPU] != MAX_CPU)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] CpuBoostMode detected, bump CPU to max");
|
||||
Clocks::SetHz(SysClkModule_CPU, MAX_CPU);
|
||||
this->context->freqs[SysClkModule_CPU] = MAX_CPU;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClockManager::WaitForNextTick()
|
||||
{
|
||||
svcSleepThread(this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) * 1000000ULL);
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXToolAsm(FILE* readFile, uint8_t* flag)
|
||||
{
|
||||
// Copied from ReverseNXTool
|
||||
uint8_t Docked[0x10] = {0xE0, 0x03, 0x00, 0x32, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t Handheld[0x10] = {0x00, 0x00, 0xA0, 0x52, 0xC0, 0x03, 0x5F, 0xD6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
uint8_t filebuffer[0x10] = {0};
|
||||
uint8_t cmpresult = 0;
|
||||
fread(&filebuffer, 1, 16, readFile);
|
||||
cmpresult = memcmp(filebuffer, Docked, sizeof(Docked));
|
||||
if (cmpresult != 0)
|
||||
{
|
||||
cmpresult = memcmp(filebuffer, Handheld, sizeof(Handheld));
|
||||
if (cmpresult != 0)
|
||||
*flag = 0; // Set to default
|
||||
else
|
||||
*flag = 1; // Handheld
|
||||
}
|
||||
else
|
||||
*flag = 2; // Docked
|
||||
|
||||
fclose(readFile);
|
||||
}
|
||||
|
||||
void ClockManager::checkReverseNXRT(bool recheckReverseNX, uint8_t* flag)
|
||||
{
|
||||
FILE* readReverseNXRTConf = fopen(FILE_REVERSENX_RT_CONF_PATH, "rb");
|
||||
if (readReverseNXRTConf != NULL)
|
||||
{
|
||||
uint8_t ReverseNXRTConfArr[9];
|
||||
fread(ReverseNXRTConfArr, 9, 1, readReverseNXRTConf);
|
||||
fclose(readReverseNXRTConf);
|
||||
remove(FILE_REVERSENX_RT_CONF_PATH);
|
||||
|
||||
uint8_t currentTid[8];
|
||||
for(int i = 0; i < 8; i++)
|
||||
currentTid[i] = this->context->applicationId >> 8*(7-i);
|
||||
|
||||
uint8_t cmpresult = memcmp(currentTid, ReverseNXRTConfArr, sizeof(currentTid));
|
||||
|
||||
if (cmpresult == 0)
|
||||
*flag = ReverseNXRTConfArr[8]; // 1: Handheld, 2: Docked, 3: Reset
|
||||
else
|
||||
*flag = 0; // 0: Not applicable
|
||||
}
|
||||
else if (recheckReverseNX)
|
||||
*flag = prevReverseNXRT; // Use previous state when profile changes
|
||||
else
|
||||
*flag = 0;
|
||||
}
|
||||
|
||||
bool ClockManager::RefreshContext()
|
||||
{
|
||||
bool hasChanged = false;
|
||||
|
||||
bool enabled = this->GetConfig()->Enabled();
|
||||
if(enabled != this->context->enabled)
|
||||
{
|
||||
this->context->enabled = enabled;
|
||||
FileUtils::LogLine("[mgr] " TARGET " status: %s", enabled ? "enabled" : "disabled");
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
std::uint64_t applicationId = ProcessManagement::GetCurrentApplicationId();
|
||||
if (applicationId != this->context->applicationId)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] TitleID change: %016lX", applicationId);
|
||||
prevReverseNXRT = 0; // Reset ReverseNX-RT previous state when Title ID changes
|
||||
this->context->applicationId = applicationId;
|
||||
hasChanged = true;
|
||||
|
||||
if (FileUtils::IsReverseNXEnabled() || recheckReverseNX)
|
||||
{
|
||||
// A new game starts or the real profile changes, then we need to check if ReverseNXTool patches are applied
|
||||
isEnabledReverseNX = false;
|
||||
|
||||
// Check if ReverseNXTool patches are applied
|
||||
if (applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
}
|
||||
|
||||
if (FileUtils::IsBoostStartEnabled() && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
{
|
||||
// If a game starts and overrides for CPU are not enabled, then set MAX_CPU for 10 sec
|
||||
std::uint32_t overcpu = this->context->overrideFreqs[SysClkModule_CPU];
|
||||
if (!overcpu)
|
||||
{
|
||||
tickStartBoost = (std::uint32_t)( 10'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1;
|
||||
FileUtils::LogLine("[mgr] A game starts, bump CPU to max for 10 sec");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!tickCheckReverseNXRT || recheckReverseNX)
|
||||
{
|
||||
uint8_t flag = 0;
|
||||
checkReverseNXRT(recheckReverseNX, &flag);
|
||||
|
||||
switch(flag)
|
||||
{
|
||||
case 1:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Handheld Mode");
|
||||
this->context->profile = ReverseNXProfile(false);
|
||||
prevReverseNXRT = flag;
|
||||
isEnabledReverseNX = true;
|
||||
isDockedReverseNX = false;
|
||||
hasChanged = true;
|
||||
break;
|
||||
case 2:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT detected: Enforce Docked Mode");
|
||||
this->context->profile = ReverseNXProfile(true);
|
||||
prevReverseNXRT = flag;
|
||||
isEnabledReverseNX = true;
|
||||
isDockedReverseNX = true;
|
||||
hasChanged = true;
|
||||
break;
|
||||
case 3:
|
||||
FileUtils::LogLine("[mgr] ReverseNX-RT disabled: Reset to System-controlled Mode and recheck ReverseNX-Tool");
|
||||
RealProfile = Clocks::GetCurrentProfile();
|
||||
this->context->profile = RealProfile;
|
||||
prevReverseNXRT = 0;
|
||||
isEnabledReverseNX = false;
|
||||
isDockedReverseNX = false;
|
||||
hasChanged = true;
|
||||
if (this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
break;
|
||||
case 0:
|
||||
if (recheckReverseNX && isEnabledReverseNXTool && this->context->applicationId != PROCESS_MANAGEMENT_QLAUNCH_TID)
|
||||
this->checkReverseNXTool();
|
||||
break;
|
||||
}
|
||||
// Check once per sec
|
||||
tickCheckReverseNXRT = (std::uint32_t)( 1'000 / this->GetConfig()->GetConfigValue(SysClkConfigValue_PollingIntervalMs) ) + 1;
|
||||
}
|
||||
tickCheckReverseNXRT--;
|
||||
|
||||
if (recheckReverseNX)
|
||||
recheckReverseNX = false;
|
||||
|
||||
SysClkProfile profile = Clocks::GetCurrentProfile();
|
||||
if (profile != this->context->profile && !isEnabledReverseNX)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Profile change: %s", Clocks::GetProfileName(profile, true));
|
||||
this->context->profile = profile;
|
||||
hasChanged = true;
|
||||
}
|
||||
if (profile != RealProfile && isEnabledReverseNX)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] Profile change: %s, recheck ReverseNX", Clocks::GetProfileName(profile, true));
|
||||
this->context->profile = profile;
|
||||
RealProfile = profile;
|
||||
hasChanged = true;
|
||||
recheckReverseNX = true;
|
||||
}
|
||||
|
||||
// restore clocks to stock values on app or profile change
|
||||
if(hasChanged)
|
||||
{
|
||||
if (profile == SysClkProfile_Handheld)
|
||||
MAX_CPU = 1963'500'000;
|
||||
else
|
||||
MAX_CPU = 2295'000'000;
|
||||
Clocks::ResetToStock();
|
||||
}
|
||||
|
||||
std::uint32_t hz = 0;
|
||||
for (unsigned int module = 0; module < SysClkModule_EnumMax; module++)
|
||||
{
|
||||
hz = Clocks::GetCurrentHz((SysClkModule)module);
|
||||
uint32_t cur_mhz = hz/1000'000;
|
||||
uint32_t be4_mhz = this->context->freqs[module]/1000'000;
|
||||
if (hz != 0 && cur_mhz != be4_mhz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s clock change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
this->context->freqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
|
||||
hz = this->GetConfig()->GetOverrideHz((SysClkModule)module);
|
||||
if (hz != this->context->overrideFreqs[module])
|
||||
{
|
||||
if(hz)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override change: %u.%u Mhz", Clocks::GetModuleName((SysClkModule)module, true), hz/1000000, hz/100000 - hz/1000000*10);
|
||||
}
|
||||
else
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s override disabled", Clocks::GetModuleName((SysClkModule)module, true));
|
||||
}
|
||||
this->context->overrideFreqs[module] = hz;
|
||||
hasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// temperatures do not and should not force a refresh, hasChanged untouched
|
||||
std::uint32_t millis = 0;
|
||||
std::uint64_t ns = armTicksToNs(armGetSystemTick());
|
||||
std::uint64_t tempLogInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_TempLogIntervalMs) * 1000000ULL;
|
||||
bool shouldLogTemp = tempLogInterval && ((ns - this->lastTempLogNs) > tempLogInterval);
|
||||
for (unsigned int sensor = 0; sensor < SysClkThermalSensor_EnumMax; sensor++)
|
||||
{
|
||||
millis = Clocks::GetTemperatureMilli((SysClkThermalSensor)sensor);
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
FileUtils::LogLine("[mgr] %s temp: %u.%u °C", Clocks::GetThermalSensorName((SysClkThermalSensor)sensor, true), millis/1000, (millis - millis/1000*1000) / 100);
|
||||
}
|
||||
this->context->temps[sensor] = millis;
|
||||
}
|
||||
|
||||
if(shouldLogTemp)
|
||||
{
|
||||
this->lastTempLogNs = ns;
|
||||
}
|
||||
|
||||
std::uint64_t csvWriteInterval = this->GetConfig()->GetConfigValue(SysClkConfigValue_CsvWriteIntervalMs) * 1000000ULL;
|
||||
|
||||
if(csvWriteInterval && ((ns - this->lastCsvWriteNs) > csvWriteInterval))
|
||||
{
|
||||
FileUtils::WriteContextToCsv(this->context);
|
||||
this->lastCsvWriteNs = ns;
|
||||
}
|
||||
|
||||
return hasChanged;
|
||||
}
|
||||
|
||||
SysClkContext ClockManager::GetCurrentContext()
|
||||
{
|
||||
std::scoped_lock lock{this->contextMutex};
|
||||
return *this->context;
|
||||
}
|
||||
|
||||
Config* ClockManager::GetConfig()
|
||||
{
|
||||
return this->config;
|
||||
}
|
||||
Reference in New Issue
Block a user