use hooking for erista mrf

This commit is contained in:
Lightos1
2026-07-27 15:02:53 +02:00
parent 0cf4927c47
commit 6e4a28ee67
2 changed files with 87 additions and 81 deletions

View File

@@ -37,7 +37,8 @@ namespace ams::ldr::hoc::pcv {
return true;
};
template <bool isMariko> Result CpuFreqCvbTable(u32 *ptr) {
template <bool isMariko>
Result CpuFreqCvbTable(u32 *ptr) {
cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::CpuCvbTableDefault) : (cvb_entry_t *)(&erista::CpuCvbTableDefault);
cvb_entry_t *customize_table = nullptr;
@@ -112,7 +113,8 @@ namespace ams::ldr::hoc::pcv {
PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0);
}
template <bool isMariko> Result GpuFreqCvbTable(u32 *ptr) {
template <bool isMariko>
Result GpuFreqCvbTable(u32 *ptr) {
cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::GpuCvbTableDefault) : (cvb_entry_t *)(&erista::GpuCvbTableDefault);
cvb_entry_t *customize_table;
if (isMariko) {
@@ -135,7 +137,7 @@ namespace ams::ldr::hoc::pcv {
default:
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
break;
}
}
} else {
switch (C.eristaGpuUV) {
case 0:
@@ -150,7 +152,7 @@ namespace ams::ldr::hoc::pcv {
default:
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
break;
}
}
}
size_t default_entry_count = GetDvfsTableEntryCount(default_table);

View File

@@ -27,9 +27,25 @@
namespace ams::ldr::hoc::pcv::erista {
std::vector<u32> newEmcList;
u32 *nsoStart;
u32 *nsoEnd;
struct HookPayloadData {
struct {
EristaMtcTable *mtcTable;
u32 mtcCount;
} mtcTableAsm;
};
DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
namespace {
std::vector<u32> newEmcList;
u32 *nsoStart;
u32 *nsoEnd;
struct {
u32 *getEristaMtcTableFnSite = nullptr;
EristaMtcTable *mtcTable = nullptr;
bool foundMtcTablePattern = false;
} getMtcTableCache;
}
Result CpuVoltDvfs(u32 *ptr) {
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
@@ -447,39 +463,6 @@ namespace ams::ldr::hoc::pcv::erista {
}
}
/* Relocate the table */
/* Rescanning is simpler than trying to extract a bunch of data from the asm patch, performance impact is negligable */
/* Also, this is more stable :P */
u32 RepointEristaEmcTablePtr(uintptr_t fromSlot, uintptr_t toTable) {
constexpr u32 RetIns = 0xD65F03C0; /* ret */
u32 patched = 0;
for (u32 *p = nsoStart; p + 4 < nsoEnd; ++p) {
const u32 ins = *p;
if (!AsmIsAdrX0(ins)) {
continue;
}
const uintptr_t pc = reinterpret_cast<uintptr_t>(p);
if (AsmAdrTarget(ins, pc) != fromSlot) {
continue;
}
if (!(AsmIsLdpX(p[1]) && AsmIsLdpX(p[2]) && p[3] == RetIns)) {
continue;
}
/* adr only reaches +-1MB */
const s64 delta = static_cast<s64>(toTable) - static_cast<s64>(pc);
if (delta > 0xFFFFF || delta < -0x100000) {
continue;
}
PATCH_OFFSET(p, AsmSetAdrTarget(ins, pc, toTable));
++patched;
}
return patched;
}
/* The silicon instructs; the children obey... */
void MtcGenerateFreqTables() {
newEmcList.clear();
@@ -548,24 +531,20 @@ namespace ams::ldr::hoc::pcv::erista {
constexpr u32 StartAdjustment = offsetof(EristaMtcTable, rate_khz) + sizeof(EristaMtcTable) * (erista::MtcTableCountDefault - 1);
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
const uintptr_t usedSlot = reinterpret_cast<uintptr_t>(startPtr) + mtcOffset;
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(usedSlot);
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(startPtr + mtcOffset);
R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault));
PrepareMtcMemoryRegion(startPtr, table);
table = reinterpret_cast<EristaMtcTable *>(startPtr);
/* We must do this as the NLE tables don't have enough space past them for our extended ones */
if (usedSlot != reinterpret_cast<uintptr_t>(startPtr)) {
if (RepointEristaEmcTablePtr(usedSlot, reinterpret_cast<uintptr_t>(startPtr)) == 0) {
AbortInvalidMtc("Failed to repoint emc table");
}
}
if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
AbortInvalidMtc("Failed mtc validation");
}
/* Cache the table for hooks. */
getMtcTableCache.mtcTable = table;
if (C.eristaEmcMaxClock <= EmcClkOSLimit) {
R_SKIP();
}
@@ -620,50 +599,71 @@ namespace ams::ldr::hoc::pcv::erista {
// R_SUCCEED();
// }
HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count) {
const HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData);
*count = data->mtcTableAsm.mtcCount;
return data->mtcTableAsm.mtcTable;
}
Result MemMtcTableAsm(u32 *ptr) {
/* Return if the pattern was already found. */
/* This pattern happens multiple times in this function., we only need to find it once. */
R_UNLESS(!getMtcTableCache.foundMtcTablePattern, ldr::ResultInvalidMtcTablePattern());
/* This is a mess but the compiler made this painful to patch so we must do it this way */
constexpr s32 GoodAdrpOffset = -1;
constexpr s32 GoodMovOffset = -7;
constexpr s32 GoodBlOffset = 1;
constexpr u32 MtcGoodBlOpcode = 0x97fe6cfc;
constexpr u32 MtcBadBlOpcode0 = 0x97ffae64; // bl nn::pcv::GetHardwareType
constexpr u32 MtcBadBlOpcode1 = 0x940036d5; // bl strcmp
constexpr u32 MtcBadAdrpAsm = 0xd00000a1; // adrp x1, s_ModuleResetStatus_
constexpr s32 MtcBadBlOffset0 = 2;
constexpr s32 MtcBadBlOffset1 = -1;
constexpr s32 MtcBadAdrpOffset = 1;
constexpr u32 AddrpOffset = 1;
constexpr u32 MovOffset = 7;
constexpr u32 BlOffset = 5;
constexpr u32 MovOffsetOld = 8;
/* Ensure we don't dereference memory before nso start. */
R_UNLESS(ptr + GoodMovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
R_UNLESS(ptr - MovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern());
/* Check for GetHardwareType asm and skip if it is found */
/* The pattern will match on the first time, but the location is bad, so it must be skipped */
if(AsmCompareAdrpNoImm(*(ptr + MtcBadAdrpOffset), MtcBadAdrpAsm) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset0), MtcBadBlOpcode0) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset1), MtcBadBlOpcode1)) {
R_SKIP();
}
/* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */
/* The correct instructions will always be x0 since the mtcTable pointer is returned. */
u32 adrp = *(ptr + GoodAdrpOffset);
u32 adrp = *(ptr - AddrpOffset);
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
/* Check for the branch instruction above the cbz to ensure we are patching the right location*/
u32 bl = *(ptr + GoodBlOffset);
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), ldr::ResultInvalidMtcTablePattern());
u32 bl = *(ptr - BlOffset);
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcBlIns), ldr::ResultInvalidMtcTablePattern());
/* Check for the mov that actually sets the mtc table count. */
u32 mov = *(ptr + GoodMovOffset);
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
u32 mov = *(ptr - MovOffset);
bool foundMov = false;
foundMov = asm_compare_no_rd(mov, MtcMovAsm);
/* Patch out the count of the mov to our custom mtc table amount*/
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), asm_get_rd(mov));
if (!foundMov) {
mov = *(ptr + MovOffsetOld);
/* Check old firmware offset. */
foundMov = asm_compare_no_rd(mov, MtcMovAsm);
}
PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch);
R_UNLESS(foundMov, ldr::ResultInvalidMtcTablePattern());
constexpr u32 PrologueWindow = 140;
u32 *functionPrologue = FindFnPrologue(ptr, PrologueWindow, nsoStart);
R_UNLESS(functionPrologue != nullptr, ldr::ResultInvalidMtcTablePattern());
getMtcTableCache.getEristaMtcTableFnSite = functionPrologue;
getMtcTableCache.foundMtcTablePattern = true;
R_SUCCEED();
}
Result InstallHooks() {
R_TRY(Hooks().CheckEnabled());
R_TRY(Hooks().CopyPayload());
R_UNLESS(getMtcTableCache.getEristaMtcTableFnSite != nullptr && getMtcTableCache.mtcTable != nullptr, ldr::ResultInvalidMtcTablePattern());
/* Copy the data to the payload. */
auto *data = Hooks().BindData(e_HookPayloadData);
R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory());
data->mtcTableAsm.mtcTable = reinterpret_cast<EristaMtcTable *>(Hooks().ToVa(getMtcTableCache.mtcTable));
data->mtcTableAsm.mtcCount = newEmcList.size();
R_TRY(INSTALL_IMPL_HOOK(getMtcTableCache.getEristaMtcTableFnSite, GetEristaMtcTableImpl));
R_SUCCEED();
}
@@ -685,7 +685,7 @@ namespace ams::ldr::hoc::pcv::erista {
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
{"GPU PLL Max", & GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
{"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax },
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
@@ -711,6 +711,10 @@ namespace ams::ldr::hoc::pcv::erista {
CRASH(entry.description);
}
}
if (R_FAILED(InstallHooks())) {
panic::SmcError(panic::Patch);
}
}
}