Add hijack dvfs from sys-clk-eos

This commit is contained in:
Lightos1
2026-02-11 16:57:26 +01:00
parent e331f1249b
commit 5efb4bdd6b
4 changed files with 223 additions and 89 deletions

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -96,9 +96,23 @@ std::atomic<uint64_t> idletick2{systemtickfrequency};
std::atomic<uint64_t> idletick3{systemtickfrequency};
u32 cpu0, cpu1, cpu2, cpu3, cpuAvg;
u16 cpuSpeedo0, cpuSpeedo2, socSpeedo0; // CPU, GPU, SOC
u32 speedoBracket;
u16 cpuIDDQ, gpuIDDQ, socIDDQ;
u8 g_dramID = 0;
static const u32 ramBrackets[][22] = {
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
{ 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, },
{ 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, },
{ 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, },
};
static const u32 gpuDvfsArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800};
u32 dvfsTable[6][32] = {};
u64 dvfsAddress;
u32 ramVmin;
const char* Board::GetModuleName(SysClkModule module, bool pretty)
{
ASSERT_ENUM_VALID(SysClkModule, module);
@@ -311,7 +325,7 @@ void Board::fuseReadSpeedos() {
cpuIDDQ = *reinterpret_cast<const u16*>(dump + FUSE_CPU_IDDQ_CALIB);
gpuIDDQ = *reinterpret_cast<const u16*>(dump + FUSE_SOC_IDDQ_CALIB);
socIDDQ = *reinterpret_cast<const u16*>(dump + FUSE_GPU_IDDQ_CALIB);
svcCloseHandle(debug);
return;
}
@@ -458,7 +472,7 @@ std::uint32_t Board::GetHz(SysClkModule module)
hz = 60;
return hz;
}
if(HOSSVC_HAS_CLKRST)
{
ClkrstSession session = {0};
@@ -753,7 +767,7 @@ std::int32_t Board::GetPowerMw(SysClkPowerSensor sensor)
}
std::uint32_t Board::GetPartLoad(SysClkPartLoad loadSource)
{
{
switch(loadSource)
{
case SysClkPartLoad_EMC:
@@ -792,6 +806,7 @@ u8 Board::GetDramID() {
void Board::FetchHardwareInfos()
{
fuseReadSpeedos();
SetSpeedoBracket();
u64 sku = 0, dramID = 0;
Result rc = splInitialize();
ASSERT_RESULT_OK(rc, "splInitialize");
@@ -801,7 +816,7 @@ void Board::FetchHardwareInfos()
rc = splGetConfig(SplConfigItem_DramId, &dramID);
ASSERT_RESULT_OK(rc, "splGetConfig");
splExit();
switch(sku)
@@ -816,9 +831,12 @@ void Board::FetchHardwareInfos()
g_socType = SysClkSocType_Erista;
}
if (g_socType == SysClkSocType_Mariko) {
CacheDvfsTable();
}
g_consoleType = (HorizonOCConsoleType)sku;
g_dramID = (u8)dramID;
}
/*
@@ -926,6 +944,168 @@ std::uint32_t Board::GetVoltage(HocClkVoltage voltage)
return out > 0 ? out : 0;
}
void Board::SetSpeedoBracket() {
if (cpuSpeedo2 >= 1754) {
speedoBracket = 3;
} else if (cpuSpeedo2 >= 1690) {
speedoBracket = 2;
} else if (cpuSpeedo2 > 1625) {
speedoBracket = 1;
} else {
speedoBracket = 0;
}
}
u32 Board::GetMinimumGpuVoltage(u32 freqMhz) {
if (freqMhz <= 1600)
return 0;
for (u32 voltageIndex = 0; voltageIndex < 22; ++voltageIndex) {
if (freqMhz <= ramBrackets[speedoBracket][voltageIndex]) {
return gpuDvfsArray[voltageIndex];
}
}
return 800;
}
Handle Board::GetPcvHandle() {
constexpr u64 PcvID = 0x10000000000001a;
u64 processIDList[80]{};
s32 processCount = 0;
Handle handle = INVALID_HANDLE;
DebugEventInfo debugEvent{};
/* Get all running processes. */
Result resultGetProcessList = svcGetProcessList(&processCount, processIDList, std::size(processIDList));
if (R_FAILED(resultGetProcessList)) {
return INVALID_HANDLE;
}
/* Try to find pcv. */
for (int i = 0; i < processCount; ++i) {
if (handle != INVALID_HANDLE) {
svcCloseHandle(handle);
handle = INVALID_HANDLE;
}
/* Try to debug process, if it fails, try next process. */
Result resultSvcDebugProcess = svcDebugActiveProcess(&handle, processIDList[i]);
if (R_FAILED(resultSvcDebugProcess)) {
continue;
}
/* Try to get a debug event. */
Result resultDebugEvent = svcGetDebugEvent(&debugEvent, handle);
if (R_SUCCEEDED(resultDebugEvent)) {
if (debugEvent.info.create_process.program_id == PcvID) {
return handle;
}
}
}
/* Failed to get handle. */
return INVALID_HANDLE;
}
void Board::CacheDvfsTable() {
const u32 voltagePattern[] = { 600000, 12500, 1400000, };
Handle handle = GetPcvHandle();
if (handle == INVALID_HANDLE) {
FileUtils::LogLine("[Board] Invalid handle!");
return;
}
MemoryInfo memoryInfo = {};
u64 address = 0;
u32 pageInfo = 0;
constexpr u32 PageSize = 0x1000;
u8 buffer[PageSize];
/* Loop until failure. */
while (true) {
/* Find pcv heap. */
while (true) {
Result resultProcessMemory = svcQueryDebugProcessMemory(&memoryInfo, &pageInfo, handle, address);
address = memoryInfo.addr + memoryInfo.size;
if (R_FAILED(resultProcessMemory) || !address) {
svcCloseHandle(handle);
FileUtils::LogLine("[Board] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
handle = INVALID_HANDLE;
return;
}
if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast<char>(memoryInfo.type) == 0x04) {
/* Found valid memory. */
break;
}
}
for (u64 base = 0; base < memoryInfo.size; base += PageSize) {
u32 memorySize = std::min(memoryInfo.size, static_cast<u64>(PageSize));
if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) {
break;
}
u8 *resultPattern = static_cast<u8 *>(memmem(buffer, sizeof(buffer), voltagePattern, sizeof(voltagePattern)));
u32 index = resultPattern - buffer;
if (!resultPattern) {
continue;
}
/* Assuming mariko. */
const u32 vmax = 800;
constexpr u32 DvfsTableOffset = 312;
if (!std::memcmp(&buffer[index + DvfsTableOffset], &vmax, sizeof(vmax))) {
std::memcpy(dvfsTable, &buffer[index + DvfsTableOffset], sizeof(dvfsTable));
dvfsAddress = base + memoryInfo.addr + DvfsTableOffset + index;
}
svcCloseHandle(handle);
handle = INVALID_HANDLE;
return;
}
}
svcCloseHandle(handle);
handle = INVALID_HANDLE;
return;
}
void Board::PcvHijackDvfs(u32 vmin) {
u32 table[192];
static_assert(sizeof(table) == sizeof(dvfsTable));
std::memcpy(table, dvfsTable, sizeof(dvfsTable));
if (ramVmin == vmin) {
return;
}
for (u32 i = 0; i < std::size(table); ++i) {
if (table[i] && table[i] <= vmin) {
table[i] = vmin;
}
}
Handle handle = GetPcvHandle();
if (handle == INVALID_HANDLE) {
FileUtils::LogLine("Invalid handle!");
return;
}
Result rc = svcWriteDebugProcessMemory(handle, table, dvfsAddress, sizeof(table));
if (R_SUCCEEDED(rc)) {
ramVmin = vmin;
}
svcCloseHandle(handle);
}
#define MC_REGISTER_BASE 0x70019000
#define MC_REGISTER_REGION_SIZE 0x1000
@@ -1021,13 +1201,13 @@ void Board::UpdateShadowRegs(u32 tRCD_i, u32 tRP_i, u32 tRAS_i, u32 tRRD_i, u32
double tXSR = (double) (tRFCab + 7.5);
args = {};
args.X[0] = 0xF0000002;
args.X[1] = EMC_REGISTER_BASE + EMC_INTSTATUS_0;
svcCallSecureMonitor(&args);
args = {};
args.X[0] = 0xF0000002;
args.X[1] = EMC_REGISTER_BASE + EMC_INTSTATUS_0;
svcCallSecureMonitor(&args);
if(args.X[1] == (EMC_REGISTER_BASE + EMC_INTSTATUS_0)) { // if param 1 is identical read failed, exosphere needs patch!
writeNotification("Horizon OC\nExosphere not patched\nfor EMC r/w");
writeNotification("Horizon OC\nExosphere not patched\nfor EMC r/w");
return;
}
@@ -1079,21 +1259,21 @@ void Board::UpdateShadowRegs(u32 tRCD_i, u32 tRP_i, u32 tRAS_i, u32 tRRD_i, u32
WRITE_REGISTER_MC(MC_EMEM_ARB_DA_COVERS_0, da_covers);
// TODO: modify mc_emem_arb_misc0
WRITE_REGISTER_MC(MC_TIMING_CONTROL_0, 0x1); // update timing regs as they are shadowed
WRITE_REGISTER_EMC(EMC_TIMING_CONTROL_0, 0x1);
}
bool Board::IsDram8GB() {
SecmonArgs args = {};
args.X[0] = 0xF0000002;
args.X[1] = MC_REGISTER_BASE + MC_EMEM_CFG_0;
svcCallSecureMonitor(&args);
SecmonArgs args = {};
args.X[0] = 0xF0000002;
args.X[1] = MC_REGISTER_BASE + MC_EMEM_CFG_0;
svcCallSecureMonitor(&args);
if(args.X[1] == (MC_REGISTER_BASE + MC_EMEM_CFG_0)) { // if param 1 is identical read failed
writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!");
writeNotification("Horizon OC\nSecmon read failed!\n This may be a hardware issue!");
return false;
} else
return args.X[1] == 0x00002000 ? true : false;
}
}

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -33,6 +33,8 @@
class Board
{
public:
static void PcvHijackDvfs(u32 vmin);
static u32 GetMinimumGpuVoltage(u32 freqMhz);
static void fuseReadSpeedos();
static u16 getSpeedo(HorizonOCSpeedo speedoType);
static u16 getIDDQ(HorizonOCSpeedo speedoType);
@@ -67,4 +69,8 @@ class Board
static void FetchHardwareInfos();
static PcvModule GetPcvModule(SysClkModule sysclkModule);
static PcvModuleId GetPcvModuleId(SysClkModule sysclkModule);
private:
static void SetSpeedoBracket();
static void CacheDvfsTable();
static Handle GetPcvHandle();
};

View File

@@ -441,6 +441,7 @@ void ClockManager::GovernorThread(void* arg)
bool prevBoostMode = true;
void ClockManager::Tick()
{
std::scoped_lock lock{this->contextMutex};
@@ -559,9 +560,17 @@ void ClockManager::Tick()
targetHz / 1000000, targetHz / 100000 - targetHz / 1000000 * 10
);
/* Dvfs here. */
Board::SetHz((SysClkModule)module, nearestHz);
this->context->freqs[module] = nearestHz;
RefreshContext();
continue;
}
Board::SetHz((SysClkModule)module, nearestHz);
this->context->freqs[module] = nearestHz;
}
if(module == SysClkModule_CPU && this->config->GetConfigValue(HocClkConfigValue_FixCpuVoltBug)) {
FixCpuBug();
}
@@ -648,6 +657,7 @@ bool ClockManager::RefreshContext()
break;
case SysClkModule_MEM:
Board::ResetToStockMem();
/* Dvfs with vmin = 0 here. */
break;
}
}
@@ -705,7 +715,7 @@ bool ClockManager::RefreshContext()
for (unsigned int voltageSource = 0; voltageSource < HocClkVoltage_EnumMax; voltageSource++)
{
this->context->voltages[voltageSource] = Board::GetVoltage((HocClkVoltage)voltageSource);
this->context->voltages[voltageSource] = Board::GetVoltage(HocClkVoltage_GPU);
}
if (this->ConfigIntervalTimeout(SysClkConfigValue_CsvWriteIntervalMs, ns, &this->lastCsvWriteNs))
@@ -1027,55 +1037,3 @@ void ClockManager::UpdateRamTimings() {
Board::UpdateShadowRegs(t1_tRCD, t2_tRP, t3_tRAS, t4_tRRD, t5_tRFC, t6_tRTW, t7_tWTR, t8_tREFI, ramFreq, rlAdd, wlAdd, hpMode);
}
unsigned int ramBrackets[][22] =
{
{ 2133, 2200, 2266, 2300, 2366, 2400, 2433, 2466, 2533, 2566, 2600, 2633, 2700, 2733, 2766, 2833, 2866, 2900, 2933, 3033, 3066, 3100, },
{ 2300, 2366, 2433, 2466, 2533, 2566, 2633, 2700, 2733, 2800, 2833, 2900, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3266, },
{ 2433, 2466, 2533, 2600, 2666, 2733, 2766, 2800, 2833, 2866, 2933, 2966, 3033, 3066, 3100, 3133, 3166, 3200, 3233, 3300, 3333, 3366, },
{ 2500, 2533, 2600, 2633, 2666, 2733, 2800, 2866, 2900, 2966, 3033, 3100, 3166, 3200, 3233, 3266, 3300, 3333, 3366, 3400, 3400, 3400, }
};
unsigned int gpuDvfsArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800};
int ClockManager::GetSpeedoBracket (int speedo)
{
int speedoBracket = 3;
if ((speedo < 1754) && (speedoBracket = 2, speedo < 1690)) {
speedoBracket = !!(1625 < speedo);
}
return speedoBracket;
}
unsigned int ClockManager::GetGpuVoltage (unsigned int freq, int speedo)
{
long int loop;
int bracket = GetSpeedoBracket(speedo);
if (freq < 1601)
return 610;
loop = 0;
do
{
if (freq <= ramBrackets[bracket][loop])
return gpuDvfsArray[loop];
loop++;
} while (loop != 22);
return 800;
}
void ClockManager::calculateGpuVmin (void)
{
if(this->config->Refresh()) {
SysClkConfigValueList configValues;
this->config->GetConfigValues(&configValues);
int speedo = Board::getSpeedo(HorizonOCSpeedo_CPU), freq = this->config->GetConfigValue(KipConfigValue_marikoEmcMaxClock);
configValues.values[KipConfigValue_marikoGpuVmin] = GetGpuVoltage(freq, speedo);
this->config->SetConfigValues(&configValues, true);
}
}

View File

@@ -12,9 +12,9 @@
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
/* --------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <p-sam@d3vs.net>, <natinusala@gmail.com>, <m4x@m4xw.net>
@@ -43,7 +43,7 @@ IpcService::IpcService(ClockManager* clockMgr)
this->running = false;
this->clockMgr = clockMgr;
}
void IpcService::SetRunning(bool running)
@@ -214,11 +214,6 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
return ipcSrv->UpdateEmcRegs();
}
break;
case HocClkIpcCmd_CalculateGpuVmin:
if (r->data.size >= 0) {
return ipcSrv->CalculateGPUVmin();
}
break;
}
return SYSCLK_ERROR(Generic);
@@ -377,13 +372,13 @@ Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) {
Result IpcService::SetKipData() {
this->clockMgr->SetKipData();
return 0;
}
Result IpcService::GetKipData() {
this->clockMgr->GetKipData();
return 0;
}
@@ -392,8 +387,3 @@ Result IpcService::UpdateEmcRegs() {
return 0;
}
Result IpcService::CalculateGPUVmin() {
this->clockMgr->calculateGpuVmin();
return 0;
}