use hooking for erista mrf
This commit is contained in:
@@ -37,7 +37,8 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
return true;
|
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 *default_table = isMariko ? (cvb_entry_t *)(&mariko::CpuCvbTableDefault) : (cvb_entry_t *)(&erista::CpuCvbTableDefault);
|
||||||
cvb_entry_t *customize_table = nullptr;
|
cvb_entry_t *customize_table = nullptr;
|
||||||
|
|
||||||
@@ -112,7 +113,8 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0);
|
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 *default_table = isMariko ? (cvb_entry_t *)(&mariko::GpuCvbTableDefault) : (cvb_entry_t *)(&erista::GpuCvbTableDefault);
|
||||||
cvb_entry_t *customize_table;
|
cvb_entry_t *customize_table;
|
||||||
if (isMariko) {
|
if (isMariko) {
|
||||||
@@ -135,7 +137,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
default:
|
default:
|
||||||
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
switch (C.eristaGpuUV) {
|
switch (C.eristaGpuUV) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -150,7 +152,7 @@ namespace ams::ldr::hoc::pcv {
|
|||||||
default:
|
default:
|
||||||
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
|
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
|
||||||
|
|||||||
@@ -27,9 +27,25 @@
|
|||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
std::vector<u32> newEmcList;
|
struct HookPayloadData {
|
||||||
u32 *nsoStart;
|
struct {
|
||||||
u32 *nsoEnd;
|
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) {
|
Result CpuVoltDvfs(u32 *ptr) {
|
||||||
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
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... */
|
/* The silicon instructs; the children obey... */
|
||||||
void MtcGenerateFreqTables() {
|
void MtcGenerateFreqTables() {
|
||||||
newEmcList.clear();
|
newEmcList.clear();
|
||||||
@@ -548,24 +531,20 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
constexpr u32 StartAdjustment = offsetof(EristaMtcTable, rate_khz) + sizeof(EristaMtcTable) * (erista::MtcTableCountDefault - 1);
|
constexpr u32 StartAdjustment = offsetof(EristaMtcTable, rate_khz) + sizeof(EristaMtcTable) * (erista::MtcTableCountDefault - 1);
|
||||||
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
|
u8 *startPtr = reinterpret_cast<u8 *>(ptr) - StartAdjustment;
|
||||||
|
|
||||||
const uintptr_t usedSlot = reinterpret_cast<uintptr_t>(startPtr) + mtcOffset;
|
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(startPtr + mtcOffset);
|
||||||
EristaMtcTable *table = reinterpret_cast<EristaMtcTable *>(usedSlot);
|
|
||||||
R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault));
|
R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault));
|
||||||
|
|
||||||
PrepareMtcMemoryRegion(startPtr, table);
|
PrepareMtcMemoryRegion(startPtr, table);
|
||||||
table = reinterpret_cast<EristaMtcTable *>(startPtr);
|
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))) {
|
if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) {
|
||||||
AbortInvalidMtc("Failed mtc validation");
|
AbortInvalidMtc("Failed mtc validation");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Cache the table for hooks. */
|
||||||
|
getMtcTableCache.mtcTable = table;
|
||||||
|
|
||||||
if (C.eristaEmcMaxClock <= EmcClkOSLimit) {
|
if (C.eristaEmcMaxClock <= EmcClkOSLimit) {
|
||||||
R_SKIP();
|
R_SKIP();
|
||||||
}
|
}
|
||||||
@@ -620,50 +599,71 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
// R_SUCCEED();
|
// 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) {
|
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 */
|
/* This is a mess but the compiler made this painful to patch so we must do it this way */
|
||||||
constexpr s32 GoodAdrpOffset = -1;
|
constexpr u32 AddrpOffset = 1;
|
||||||
constexpr s32 GoodMovOffset = -7;
|
constexpr u32 MovOffset = 7;
|
||||||
constexpr s32 GoodBlOffset = 1;
|
constexpr u32 BlOffset = 5;
|
||||||
constexpr u32 MtcGoodBlOpcode = 0x97fe6cfc;
|
constexpr u32 MovOffsetOld = 8;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* Ensure we don't dereference memory before nso start. */
|
/* 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 */
|
u32 adrp = *(ptr - AddrpOffset);
|
||||||
/* 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);
|
|
||||||
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
|
R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern());
|
||||||
|
|
||||||
|
|
||||||
/* Check for the branch instruction above the cbz to ensure we are patching the right location*/
|
/* Check for the branch instruction above the cbz to ensure we are patching the right location*/
|
||||||
u32 bl = *(ptr + GoodBlOffset);
|
u32 bl = *(ptr - BlOffset);
|
||||||
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), ldr::ResultInvalidMtcTablePattern());
|
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcBlIns), ldr::ResultInvalidMtcTablePattern());
|
||||||
|
|
||||||
|
|
||||||
/* Check for the mov that actually sets the mtc table count. */
|
/* Check for the mov that actually sets the mtc table count. */
|
||||||
u32 mov = *(ptr + GoodMovOffset);
|
u32 mov = *(ptr - MovOffset);
|
||||||
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
|
bool foundMov = false;
|
||||||
|
foundMov = asm_compare_no_rd(mov, MtcMovAsm);
|
||||||
|
|
||||||
/* Patch out the count of the mov to our custom mtc table amount*/
|
if (!foundMov) {
|
||||||
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), asm_get_rd(mov));
|
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();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
@@ -685,7 +685,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
{"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial },
|
||||||
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
{"GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
|
||||||
{"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
|
{"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 },
|
// {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit },
|
||||||
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
|
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
|
||||||
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
{"MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit },
|
||||||
@@ -711,6 +711,10 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
CRASH(entry.description);
|
CRASH(entry.description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (R_FAILED(InstallHooks())) {
|
||||||
|
panic::SmcError(panic::Patch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user