ldr: Fix nle mrf via pointer hack
This commit is contained in:
@@ -27,9 +27,11 @@
|
|||||||
|
|
||||||
namespace ams::ldr::hoc::pcv::erista {
|
namespace ams::ldr::hoc::pcv::erista {
|
||||||
|
|
||||||
std::vector<u32> newEmcList;
|
namespace {
|
||||||
u32 *nsoStart;
|
std::vector<u32> newEmcList;
|
||||||
u32 *nsoEnd;
|
u32 *nsoStart;
|
||||||
|
u32 **patchPointerPtr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
Result CpuVoltDvfs(u32 *ptr) {
|
Result CpuVoltDvfs(u32 *ptr) {
|
||||||
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) {
|
||||||
@@ -417,14 +419,23 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
return index * erista::MtcFullTableSize + mariko::MtcFullTableSize;
|
return index * erista::MtcFullTableSize + mariko::MtcFullTableSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PrepareMtcMemoryRegion(u8 *firstTable, EristaMtcTable *usedTable) {
|
size_t GetRemainingRegionSize(MtcTableIndex index) {
|
||||||
/* Move the table, this is nessasary for NLE */
|
size_t fullRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount) + (erista::MtcFullTableSize) * (erista::MtcFullTableCount);
|
||||||
memmove(firstTable, usedTable, erista::MtcFullTableSize);
|
|
||||||
|
|
||||||
|
if (index > T210SdevEmcDvfsTableS4gb01) {
|
||||||
|
fullRegionSize -= mariko::MtcFullTableSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const size_t prevTables = index * erista::MtcFullTableSize;
|
||||||
|
return fullRegionSize - prevTables;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrepareMtcMemoryRegion(u8 *tableStart, size_t remainingSize) {
|
||||||
/* Clear all other tables. */
|
/* Clear all other tables. */
|
||||||
/* The used table is excluded. */
|
/* The used table is excluded. */
|
||||||
constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
|
// constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * (mariko::MtcFullTableCount) + (erista::MtcFullTableSize * (erista::MtcFullTableCount - 1));
|
||||||
memset(firstTable + erista::MtcFullTableSize, 0, RemainingRegionSize);
|
|
||||||
|
memset(tableStart + erista::MtcFullTableSize, 0, remainingSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MtcExtendTables(EristaMtcTable *table) {
|
void MtcExtendTables(EristaMtcTable *table) {
|
||||||
@@ -434,39 +445,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();
|
||||||
@@ -515,6 +493,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Todo: Test other dram ids. */
|
||||||
Result MemFreqMtcTable(u32 *ptr) {
|
Result MemFreqMtcTable(u32 *ptr) {
|
||||||
static const DramId dramId = [] {
|
static const DramId dramId = [] {
|
||||||
DramId id = GetDramId();
|
DramId id = GetDramId();
|
||||||
@@ -535,19 +514,10 @@ 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(reinterpret_cast<u8 *>(table), GetRemainingRegionSize(mtcIndex));
|
||||||
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");
|
||||||
@@ -567,6 +537,9 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
MemMtcTableAutoAdjust(&table[i]);
|
MemMtcTableAutoAdjust(&table[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Hack: Update the ptr32 in Patch via a pointer-pointer. */
|
||||||
|
/* This avoids overwriting the 1600MHz table. */
|
||||||
|
*patchPointerPtr = &table[newEmcList.size() - 1];
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +580,6 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
// R_SUCCEED();
|
// R_SUCCEED();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
Result MemMtcTableAsm(u32 *ptr) {
|
Result MemMtcTableAsm(u32 *ptr) {
|
||||||
/* 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 s32 GoodAdrpOffset = -1;
|
||||||
@@ -637,17 +609,15 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
u32 adrp = *(ptr + GoodAdrpOffset);
|
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 + GoodBlOffset);
|
||||||
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), ldr::ResultInvalidMtcTablePattern());
|
R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), 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 + GoodMovOffset);
|
||||||
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
|
R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern());
|
||||||
|
|
||||||
/* Patch out the count of the mov to our custom mtc table amount*/
|
/* 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));
|
u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), asm_get_rd(mov));
|
||||||
|
|
||||||
PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch);
|
PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch);
|
||||||
@@ -657,7 +627,6 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
|
|
||||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||||
nsoEnd = reinterpret_cast<u32 *>(mapped_nso + nso_size);
|
|
||||||
MtcGenerateFreqTables();
|
MtcGenerateFreqTables();
|
||||||
|
|
||||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||||
@@ -681,6 +650,14 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Hack: This avoids overwriting mtc tables in bad ways on nle rams. */
|
||||||
|
/* Compiler obviously complains due to the pointer warcrimes, but patchPointerPtr will not be used after Patch returns, so this is fine. */
|
||||||
|
u32 *ptr32 = nullptr;
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wdangling-pointer"
|
||||||
|
|
||||||
|
patchPointerPtr = &ptr32;
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
||||||
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
u32 *ptr32 = reinterpret_cast<u32 *>(ptr);
|
||||||
for (auto &entry : patches) {
|
for (auto &entry : patches) {
|
||||||
@@ -690,12 +667,9 @@ namespace ams::ldr::hoc::pcv::erista {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ViewLog();
|
|
||||||
|
|
||||||
for (auto &entry : patches) {
|
for (auto &entry : patches) {
|
||||||
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
|
LOGGING("%s Count: %zu\n", entry.description, entry.patched_count);
|
||||||
if (R_FAILED(entry.CheckResult())) {
|
if (R_FAILED(entry.CheckResult())) {
|
||||||
// ViewLog();
|
|
||||||
panic::SmcError(panic::Patch);
|
panic::SmcError(panic::Patch);
|
||||||
|
|
||||||
CRASH(entry.description);
|
CRASH(entry.description);
|
||||||
|
|||||||
Reference in New Issue
Block a user