From 5efb4bdd6b3299858f69a5eb20643da868a8253a Mon Sep 17 00:00:00 2001 From: Lightos1 <124387232+Lightos1@users.noreply.github.com> Date: Wed, 11 Feb 2026 16:57:26 +0100 Subject: [PATCH] Add hijack dvfs from sys-clk-eos --- Source/sys-clk/sysmodule/src/board.cpp | 218 ++++++++++++++++-- Source/sys-clk/sysmodule/src/board.h | 10 +- .../sys-clk/sysmodule/src/clock_manager.cpp | 64 +---- Source/sys-clk/sysmodule/src/ipc_service.cpp | 20 +- 4 files changed, 223 insertions(+), 89 deletions(-) diff --git a/Source/sys-clk/sysmodule/src/board.cpp b/Source/sys-clk/sysmodule/src/board.cpp index 2335c4de..fd159337 100644 --- a/Source/sys-clk/sysmodule/src/board.cpp +++ b/Source/sys-clk/sysmodule/src/board.cpp @@ -12,9 +12,9 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ - + /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , @@ -96,9 +96,23 @@ std::atomic idletick2{systemtickfrequency}; std::atomic 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(dump + FUSE_CPU_IDDQ_CALIB); gpuIDDQ = *reinterpret_cast(dump + FUSE_SOC_IDDQ_CALIB); socIDDQ = *reinterpret_cast(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(memoryInfo.type) == 0x04) { + /* Found valid memory. */ + break; + } + } + + for (u64 base = 0; base < memoryInfo.size; base += PageSize) { + u32 memorySize = std::min(memoryInfo.size, static_cast(PageSize)); + if (R_FAILED(svcReadDebugProcessMemory(buffer, handle, base + memoryInfo.addr, memorySize))) { + break; + } + + u8 *resultPattern = static_cast(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; -} \ No newline at end of file +} diff --git a/Source/sys-clk/sysmodule/src/board.h b/Source/sys-clk/sysmodule/src/board.h index 52affd10..07e1d8ba 100644 --- a/Source/sys-clk/sysmodule/src/board.h +++ b/Source/sys-clk/sysmodule/src/board.h @@ -12,9 +12,9 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ - + /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , @@ -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(); }; diff --git a/Source/sys-clk/sysmodule/src/clock_manager.cpp b/Source/sys-clk/sysmodule/src/clock_manager.cpp index 7715ce8c..ef1e755b 100644 --- a/Source/sys-clk/sysmodule/src/clock_manager.cpp +++ b/Source/sys-clk/sysmodule/src/clock_manager.cpp @@ -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); - } -} diff --git a/Source/sys-clk/sysmodule/src/ipc_service.cpp b/Source/sys-clk/sysmodule/src/ipc_service.cpp index 900fad54..34a4aa08 100644 --- a/Source/sys-clk/sysmodule/src/ipc_service.cpp +++ b/Source/sys-clk/sysmodule/src/ipc_service.cpp @@ -12,9 +12,9 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see . - * + * */ - + /* -------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * , , @@ -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; -} \ No newline at end of file