@@ -24,7 +24,7 @@
|
||||
|
||||
namespace ams::ldr::hoc::pcv {
|
||||
|
||||
constexpr u32 NopIns = 0x1f2003d5;
|
||||
constexpr u32 NopIns = 0xD503201F;
|
||||
|
||||
template <typename Compare>
|
||||
u32 *ScanAssembly(u32 *ptr, u32 scanLimit, u32 pattern, Compare comp) {
|
||||
@@ -76,6 +76,30 @@ namespace ams::ldr::hoc::pcv {
|
||||
SF | Op | S | | RM | Cond | 0 | 0 | Rn | Rd
|
||||
31 | 30 | 29 | 28 27 26 25 24 23 | 20 19 18 17 16 | 15 14 13 12 | 11 | 10 | 9 8 7 6 5 | 4 3 2 1 0
|
||||
*/
|
||||
inline auto AsmCbzCompareOpcodeOnly = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 24) == 0;
|
||||
};
|
||||
|
||||
inline auto AsmBlCompareOpcodeOnly = [](u32 ins1, u32 ins2) {
|
||||
return ((ins1 ^ ins2) >> 26) == 0;
|
||||
};
|
||||
inline bool AsmComparePrologue(u32 ins1, u32 ins2, u32 ins3, u32 cmp1, u32 cmp2, u32 cmp3) {
|
||||
constexpr u32 StpImmMask = ~((((1u << 7) - 1u) << 15));
|
||||
|
||||
bool firstMatch = (ins1 & StpImmMask) == (cmp1 & StpImmMask);
|
||||
|
||||
constexpr u32 StpRegsImmMask = ~(((1u << 5) - 1u) |(((1u << 5) - 1u) << 10) | (((1u << 7) - 1u) << 15));
|
||||
|
||||
bool secondMatch = (ins2 & StpRegsImmMask) == (cmp2 & StpRegsImmMask);
|
||||
|
||||
|
||||
constexpr u32 MovMask = ~((1u << 5) - 1u);
|
||||
|
||||
bool thirdMatch = (ins3 & MovMask) == (cmp3 & MovMask);
|
||||
|
||||
return firstMatch && secondMatch && thirdMatch;
|
||||
}
|
||||
|
||||
inline auto AsmCompareCselNoReg = [](u32 ins1, u32 ins2) {
|
||||
constexpr u32 ClearReg = ~(((1 << 10) - 1) | (((1 << 5) - 1) << 16));
|
||||
return ((ins1 & ClearReg) ^ (ins2 & ClearReg)) == 0;
|
||||
|
||||
@@ -450,6 +450,54 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
// R_SUCCEED();
|
||||
// }
|
||||
|
||||
|
||||
Result MemMtcTableAsm(u32 *ptr) {
|
||||
/* 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 nn::pcv::GetHardwareType
|
||||
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. */
|
||||
R_UNLESS(ptr + GoodMovOffset >= 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);
|
||||
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());
|
||||
|
||||
|
||||
/* 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());
|
||||
|
||||
/* 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));
|
||||
|
||||
PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch);
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size) {
|
||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
|
||||
@@ -469,6 +517,7 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
{"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
|
||||
{"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
|
||||
{"MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
|
||||
{"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn },
|
||||
};
|
||||
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) {
|
||||
@@ -480,9 +529,12 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
}
|
||||
}
|
||||
|
||||
// ViewLog();
|
||||
|
||||
for (auto &entry : patches) {
|
||||
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
|
||||
if (R_FAILED(entry.CheckResult())) {
|
||||
// ViewLog();
|
||||
panic::SmcError(panic::Patch);
|
||||
|
||||
CRASH(entry.description);
|
||||
|
||||
@@ -147,6 +147,17 @@ namespace ams::ldr::hoc::pcv::erista {
|
||||
{ ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, },
|
||||
};
|
||||
|
||||
constexpr u32 MtcBrAsm = 0xD61F0140;
|
||||
constexpr u32 MtcMovAsm = 0x52800148;
|
||||
constexpr u32 MtcAdrpAsm = 0xD0000081;
|
||||
constexpr u32 MtcBlIns = 0x97ffae64;
|
||||
constexpr u32 MtcAddAsm = 0x91131821;
|
||||
|
||||
ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) {
|
||||
/* This builds an address that gets returned, so the register must be x0 by convention. */
|
||||
return AsmCompareAddNoImm12(*ptr, MtcAddAsm);
|
||||
}
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
}
|
||||
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
BIN
dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp
vendored
Binary file not shown.
BIN
dist/atmosphere/kips/hoc.kip
vendored
BIN
dist/atmosphere/kips/hoc.kip
vendored
Binary file not shown.
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
BIN
dist/switch/.overlays/horizon-oc-overlay.ovl
vendored
Binary file not shown.
Reference in New Issue
Block a user