diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp index e8f24261..53ff75d5 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -24,7 +24,7 @@ namespace ams::ldr::hoc::pcv { - constexpr u32 NopIns = 0x1f2003d5; + constexpr u32 NopIns = 0xD503201F; template 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; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp index 85ef4de2..c83ab8ae 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp @@ -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(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); u32 GpuCvbDefaultMaxFreq = static_cast(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); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp index 11f9041d..a178bd2d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp @@ -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); -} +} \ No newline at end of file diff --git a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp index c57011fd..3f4cbf14 100644 Binary files a/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp and b/dist/atmosphere/contents/00FF0000636C6BFF/exefs.nsp differ diff --git a/dist/atmosphere/kips/hoc.kip b/dist/atmosphere/kips/hoc.kip index 701024e6..8ea1880b 100644 Binary files a/dist/atmosphere/kips/hoc.kip and b/dist/atmosphere/kips/hoc.kip differ diff --git a/dist/switch/.overlays/horizon-oc-overlay.ovl b/dist/switch/.overlays/horizon-oc-overlay.ovl index 7ae042c2..a946da1c 100644 Binary files a/dist/switch/.overlays/horizon-oc-overlay.ovl and b/dist/switch/.overlays/horizon-oc-overlay.ovl differ