From 3d5147a6e227398812065fffc1338c0fd638e7c1 Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Sat, 21 Mar 2026 17:01:50 +0100
Subject: [PATCH] merge pcv hijacking into voltage code
---
.../sysmodule/src/board/board_ram_oc_dvfs.cpp | 202 ------------------
.../sysmodule/src/board/board_ram_oc_dvfs.hpp | 49 -----
.../sysmodule/src/board/board_volt.cpp | 171 +++++++++++++++
.../sysmodule/src/board/board_volt.hpp | 16 ++
4 files changed, 187 insertions(+), 251 deletions(-)
delete mode 100644 Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp
delete mode 100644 Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp
diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp
deleted file mode 100644
index cda16f4b..00000000
--- a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * 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):
- * , ,
- * 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
- * --------------------------------------------------------------------------
- */
-
-#include
-#include
-#include "../file_utils.h"
-#include
-#include "board_ram_oc_dvfs.hpp"
-
-namespace board {
-
- GpuVoltData voltData = {};
-
- Handle 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 CacheGpuVoltTable() {
- UnkRegulator reg = {
- .voltageMinUV = 600000,
- .voltageStep = 12500,
- .voltageMax = 1400000,
- };
-
- Handle handle = GetPcvHandle();
- if (handle == INVALID_HANDLE) {
- FileUtils::LogLine("[dvfs] 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("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
- handle = INVALID_HANDLE;
- return;
- }
-
- if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast(memoryInfo.type) == 0x4) {
- /* 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 *resultFindReg = static_cast(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg)));
- u32 index = resultFindReg - buffer;
-
- if (!resultFindReg) {
- continue;
- }
-
- /* Assuming mariko. */
- const u32 vmax = 800;
- constexpr u32 VoltageTableOffset = 312;
- if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) {
- std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable));
- voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index;
- }
-
- svcCloseHandle(handle);
- handle = INVALID_HANDLE;
- return;
- }
- }
-
- svcCloseHandle(handle);
- handle = INVALID_HANDLE;
- return;
- }
-
- void PcvHijackGpuVolts(u32 vmin) {
- u32 table[192];
- static_assert(sizeof(table) == sizeof(voltData.voltTable));
- std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable));
-
- if (voltData.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, voltData.dvfsAddress, sizeof(table));
-
- if (R_SUCCEEDED(rc)) {
- voltData.ramVmin = vmin;
- }
-
- svcCloseHandle(handle);
- FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
- }
-
- u32 GetMinimumVmin(u32 freqMhz, u32 bracket) {
- static const u32 ramTable[][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 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, };
-
- if (freqMhz <= 1600) {
- return 0;
- }
-
- for (u32 i = 0; std::size(gpuDvfsArray) < 22; ++i) {
- if (freqMhz <= ramTable[bracket][i]) {
- return gpuVoltArray[i];
- }
- }
-
- return gpuVoltArray[std::size(gpuVoltArray) - 1];
- }
-
-}
diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp
deleted file mode 100644
index d61a4b5b..00000000
--- a/Source/rewrite-hoc-clk/sysmodule/src/board/board_ram_oc_dvfs.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * 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):
- * , ,
- * 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
- * --------------------------------------------------------------------------
- */
-
-#pragma once
-#include
-
-namespace board {
-
- struct GpuVoltData {
- u32 voltTable[6][32] = {};
- u64 voltTableAddress;
- u32 ramVmin;
- };
-
- /* TODO: Find out what component this actually targets. */
- struct UnkRegulator {
- u32 voltageMinUV;
- u32 voltageStep;
- u32 voltageMax;
- }
-
- void CacheGpuVoltTable();
- void PcvHijackGpuVolts(u32 vmin);
- u32 GetMinimumVmin(u32 freqMhz, u32 bracket);
-
-}
diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp
index 8be8b891..8ddeae99 100644
--- a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp
+++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.cpp
@@ -26,10 +26,16 @@
#include
#include
+#include
+#include
#include "board.hpp"
+#include "board_volt.hpp"
+#include "../file_utils.h"
namespace board {
+ GpuVoltData voltData = {};
+
/*
* Switch Power domains (max77620):
* Name | Usage | uV step | uV min | uV default | uV max | Init
@@ -135,4 +141,169 @@ namespace board {
return out > 0 ? out : 0;
}
+ Handle 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 CacheGpuVoltTable() {
+ UnkRegulator reg = {
+ .voltageMinUV = 600000,
+ .voltageStep = 12500,
+ .voltageMax = 1400000,
+ };
+
+ Handle handle = GetPcvHandle();
+ if (handle == INVALID_HANDLE) {
+ FileUtils::LogLine("[dvfs] 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("[dvfs] Failed to get process data. %u", R_DESCRIPTION(resultProcessMemory));
+ handle = INVALID_HANDLE;
+ return;
+ }
+
+ if (memoryInfo.size && (memoryInfo.perm & 3) == 3 && static_cast(memoryInfo.type) == 0x4) {
+ /* 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 *resultFindReg = static_cast(memmem_impl(buffer, sizeof(buffer), ®, sizeof(reg)));
+ u32 index = resultFindReg - buffer;
+
+ if (!resultFindReg) {
+ continue;
+ }
+
+ /* Assuming mariko. */
+ const u32 vmax = 800;
+ constexpr u32 VoltageTableOffset = 312;
+ if (!std::memcmp(&buffer[index + VoltageTableOffset], &vmax, sizeof(vmax))) {
+ std::memcpy(voltData.voltTable, &buffer[index + VoltageTableOffset], sizeof(voltData.voltTable));
+ voltData.voltTableAddress = base + memoryInfo.addr + VoltageTableOffset + index;
+ }
+
+ svcCloseHandle(handle);
+ handle = INVALID_HANDLE;
+ return;
+ }
+ }
+
+ svcCloseHandle(handle);
+ handle = INVALID_HANDLE;
+ return;
+ }
+
+ void PcvHijackGpuVolts(u32 vmin) {
+ u32 table[192];
+ static_assert(sizeof(table) == sizeof(voltData.voltTable));
+ std::memcpy(table, voltData.voltTable, sizeof(voltData.voltTable));
+
+ if (voltData.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, voltData.dvfsAddress, sizeof(table));
+
+ if (R_SUCCEEDED(rc)) {
+ voltData.ramVmin = vmin;
+ }
+
+ svcCloseHandle(handle);
+ FileUtils::LogLine("[dvfs] voltage set to %u mV", vmin);
+ }
+
+ u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket) {
+ static const u32 ramTable[][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 gpuVoltArray[] = { 590, 600, 610, 620, 630, 640, 650, 660, 670, 680, 690, 700, 710, 720, 730, 740, 750, 760, 770, 780, 790, 800, };
+
+ if (freqMhz <= 1600) {
+ return 0;
+ }
+
+ for (u32 i = 0; i < std::size(gpuDvfsArray); ++i) {
+ if (freqMhz <= ramTable[bracket][i]) {
+ return gpuVoltArray[i];
+ }
+ }
+
+ return gpuVoltArray[std::size(gpuVoltArray) - 1];
+ }
+
}
diff --git a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp
index 64e9872b..09c2da50 100644
--- a/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp
+++ b/Source/rewrite-hoc-clk/sysmodule/src/board/board_volt.hpp
@@ -30,6 +30,22 @@
namespace board {
+ struct GpuVoltData {
+ u32 voltTable[6][32] = {};
+ u64 voltTableAddress;
+ u32 ramVmin;
+ };
+
+ /* TODO: Find out what component this actually targets. */
+ struct UnkRegulator {
+ u32 voltageMinUV;
+ u32 voltageStep;
+ u32 voltageMax;
+ };
+
u32 GetVoltage(HocClkVoltage voltage);
+ void CacheGpuVoltTable();
+ void PcvHijackGpuVolts(u32 vmin);
+ u32 GetMinimumGpuVmin(u32 freqMhz, u32 bracket);
}