From 4023ae68381150e20a83034eed4e681df2adaa58 Mon Sep 17 00:00:00 2001 From: souldbminersmwc Date: Thu, 27 Aug 2026 14:56:02 -0400 Subject: [PATCH] clean up pattern matching --- .../loader/source/oc/oc_common.hpp | 1 + .../source/oc/pcv/mariko/pcv_mariko.cpp | 86 +++++++++++++++++-- .../source/oc/pcv/mariko/pcv_mariko.hpp | 65 ++++++-------- 3 files changed, 105 insertions(+), 47 deletions(-) diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index bbaaeb97..cb360cdb 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -92,6 +92,7 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1029); R_DEFINE_ERROR_RESULT(HookUnavailable, 1030); R_DEFINE_ERROR_RESULT(InvalidNvsrvDevTable, 1031); + R_DEFINE_ERROR_RESULT(InvalidForceVerbosityPattern, 1032); } namespace ams::ldr::hoc { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp index 1223493b..c1359830 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp @@ -42,7 +42,8 @@ namespace ams::ldr::hoc::pcv::mariko { DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData); namespace { - size_t g_nso_size = 0; + size_t g_nso_size = 0; + bool g_payloadReady = false; /* set once Hooks().CopyPayload() has run (see Patch()) */ } [[maybe_unused]] static uintptr_t CaveReserve(size_t count) { @@ -57,6 +58,14 @@ namespace ams::ldr::hoc::pcv::mariko { const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */ const uintptr_t vsnprintf_addr = reinterpret_cast(ptr); + { + using namespace _asm::field; + R_UNLESS(_asm::Ignoring(ptr[1], NvLogStpFpLrAsm, PairOff8), ldr::ResultInvalidNvLogRedirect()); /* stp x29,x30,[sp,#imm] */ + R_UNLESS(_asm::Ignoring(ptr[2], NvLogStrSpillAsm, Rt, Off8), ldr::ResultInvalidNvLogRedirect()); /* str x?,[sp,#imm] */ + R_UNLESS(_asm::Ignoring(ptr[3], NvLogMovFpAsm, Imm12), ldr::ResultInvalidNvLogRedirect()); /* add x29,sp,#imm */ + R_UNLESS(ptr[4] == NvLogCmpSizeAsm, ldr::ResultInvalidNvLogRedirect()); /* cmp x1,#0 */ + } + /* NvLog via the VDD_SOC log */ static const char Fmt[] = "%s(%s): DVFS request VDD_SOC %d mV\n"; constexpr size_t FmtLen = sizeof(Fmt) - 1; @@ -188,6 +197,15 @@ namespace ams::ldr::hoc::pcv::mariko { /* Relocate and extend C2/C3Bus to avoid issues (likely buffer overflow) */ Result BusFreqReloc(u32 *ptr) { + using namespace _asm::field; + + const u32 bus = _asm::Get(ptr[0], Rn); + R_UNLESS(_asm::IsOp(ptr[1], _asm::op::AddImm64, Rd, Rn, Imm12) && _asm::Get(ptr[1], Imm12) == 0x18 && _asm::Get(ptr[1], Rn) == bus, + ldr::ResultInvalidBusFreqReloc()); /* add Xcnt,Xbus,#0x18 */ + R_UNLESS(_asm::IsOp(ptr[2], _asm::op::StrImm64, Rt, Rn, Off8) && _asm::Get(ptr[2], Off8) == 0x50 && _asm::Get(ptr[2], Rn) == bus, + ldr::ResultInvalidBusFreqReloc()); /* str Xrail,[Xbus,#0x50] */ + R_UNLESS(_asm::IsOp(ptr[3], _asm::op::Bl, Imm26), ldr::ResultInvalidBusFreqReloc()); /* bl GetDvfsRailUniqueFreqList */ + constexpr u32 PrologueMargin = 140; u32 *functionPrologue = _asm::FindFnPrologue(ptr, PrologueMargin, nsoStart); R_UNLESS(functionPrologue != nullptr, ldr::ResultInvalidBusFreqReloc()); @@ -198,12 +216,47 @@ namespace ams::ldr::hoc::pcv::mariko { } #if HOC_UART_LOG + namespace { + struct { + u32 *sites[3] = {}; + u32 count = 0; + } forceVerbosityCache; + } + + HOOK_PAYLOAD_FN u32 ForceVerbosityImpl() { + HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData); + return data->verbosityLevel; + } + /* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs. */ Result ForceVerbosity(u32 *ptr) { - if (C.pcvLogVerbosity != 0xff) { - PATCH_OFFSET(&ptr[0], _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, 0}, {_asm::field::Imm16, C.pcvLogVerbosity})); /* movz w0,#level */ - PATCH_OFFSET(&ptr[1], _asm::RetIns); /* ret */ + using namespace _asm::field; + + R_UNLESS(_asm::IsOp(ptr[1], _asm::op::StrImm64, Rt, Rn, Off8) && _asm::Get(ptr[1], Rn) == _asm::reg::Sp, + ldr::ResultInvalidForceVerbosityPattern()); /* str x?,[sp,#imm] */ + R_UNLESS(_asm::IsOp(ptr[2], _asm::op::AddImm64, Rd, Rn, Imm12) + && _asm::Get(ptr[2], Rd) == _asm::reg::Fp && _asm::Get(ptr[2], Rn) == _asm::reg::Sp, + ldr::ResultInvalidForceVerbosityPattern()); /* add x29,sp,#imm */ + R_UNLESS(_asm::IsOp(ptr[3], _asm::op::AddImm64, Rn, Imm12) + && _asm::Get(ptr[3], Rd) == 0 && _asm::Get(ptr[3], Rn) == _asm::reg::Fp, + ldr::ResultInvalidForceVerbosityPattern()); /* add x0,x29,#imm */ + R_UNLESS(_asm::IsOp(ptr[4], _asm::op::AddImm64, Rd, Rn, Imm12) && _asm::Get(ptr[4], Rn) == _asm::reg::Fp, + ldr::ResultInvalidForceVerbosityPattern()); /* add x?,x29,#imm */ + R_UNLESS(_asm::Get(ptr[3], Imm12) == _asm::Get(ptr[4], Imm12) + && _asm::IsOp(ptr[5], _asm::op::Bl, Imm26), + ldr::ResultInvalidForceVerbosityPattern()); + + bool foundCmp = false; + for (u32 j = 6; j <= 10; ++j) { + if (ptr[j] == 0x7100001Fu) { /* cmp w0,#0 */ + foundCmp = true; + break; + } } + R_UNLESS(foundCmp, ldr::ResultInvalidForceVerbosityPattern()); + + R_UNLESS(forceVerbosityCache.count < std::size(forceVerbosityCache.sites), ldr::ResultInvalidForceVerbosityPattern()); + forceVerbosityCache.sites[forceVerbosityCache.count++] = ptr; R_SUCCEED(); } #endif @@ -214,6 +267,8 @@ namespace ams::ldr::hoc::pcv::mariko { using namespace _asm::field; + R_UNLESS(_asm::Ignoring(ptr[1], EmcSocLutCountStoreAsm, Rt), ldr::ResultInvalidEmcSocLut()); /* str w?,[x0,#0x154] */ + u32 *freqStore = _asm::ScanAssembly(ptr - Window, Window, EmcSocFreqStoreAsm, Rt); /* str x?,[x8,#0x18] */ u32 *voltStore = _asm::ScanAssembly(ptr - Window, Window, EmcSocVoltStoreAsm, Rt); /* str w?,[x8,#0x48] */ u32 *readLoad = _asm::ScanAssembly(ptr - Window, Window, EmcSocReadLoadAsm, Rt); /* ldr w?,[x9,#0x48] */ @@ -253,7 +308,9 @@ namespace ams::ldr::hoc::pcv::mariko { } Result EmcDvfsCountLimit(u32 *ptr) { - R_UNLESS(EmcDvfsCountPatternFn(ptr), ldr::ResultInvalidEmcDvfsCount()); + R_UNLESS(_asm::IsOp(*(ptr - 1), _asm::op::Cbz, _asm::field::Imm19, _asm::field::Rt), ldr::ResultInvalidEmcDvfsCount()); /* cbz w?, */ + R_UNLESS(_asm::Ignoring(*(ptr + 1), _asm::Encode(_asm::op::BCond, {_asm::field::BCond, _asm::cond::Cs}), _asm::field::Imm19), + ldr::ResultInvalidEmcDvfsCount()); /* b.cs */ /* cmp w?,#0x21 -> cmp w?,#EmcDvfsTableEntryCount */ PATCH_OFFSET(ptr, _asm::Set(*ptr, _asm::field::Imm12, EmcDvfsTableEntryCount)); @@ -261,8 +318,10 @@ namespace ams::ldr::hoc::pcv::mariko { } Result EmcRateListLimit(u32 *ptr) { - /* ptr = cmp w?,#0x20 ; ptr[1] = csel w?,w?,w?,lt (w? = min(maxCount, 32)) ; ptr[2] = bl */ - R_UNLESS(EmcRateListPatternFn(ptr), ldr::ResultInvalidEmcRateList()); + R_UNLESS(_asm::Ignoring(ptr[1], EmcRateCapCselAsm, _asm::field::Rd, _asm::field::Rn, _asm::field::Rm) + && _asm::Get(ptr[0], _asm::field::Rn) == _asm::Get(ptr[1], _asm::field::Rn), + ldr::ResultInvalidEmcRateList()); /* csel w?,w?,w?,lt, min(reg, 0x20) */ + R_UNLESS(_asm::IsOp(ptr[2], _asm::op::Bl, _asm::field::Imm26), ldr::ResultInvalidEmcRateList()); /* The csel's Rm holds the 32 cap. */ const u32 capReg = _asm::Get(ptr[1], _asm::field::Rm); @@ -521,13 +580,22 @@ namespace ams::ldr::hoc::pcv::mariko { Result InstallHooks() { R_TRY(Hooks().CheckEnabled()); - R_TRY(Hooks().CopyPayload()); + R_UNLESS(g_payloadReady, ldr::ResultUninitializedPatcher()); auto *data = Hooks().BindData(m_HookPayloadData); R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory()); R_TRY(SharedClkBusInstallHooks(data)); +#if HOC_UART_LOG + if (forceVerbosityCache.count != 0 && C.pcvLogVerbosity != 0xff) { + data->verbosityLevel = C.pcvLogVerbosity; + for (u32 i = 0; i < forceVerbosityCache.count; ++i) { + R_TRY(INSTALL_IMPL_HOOK(forceVerbosityCache.sites[i], ForceVerbosityImpl)); + } + } +#endif + R_SUCCEED(); } @@ -537,6 +605,8 @@ namespace ams::ldr::hoc::pcv::mariko { g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize; g_nso_size = nso_size; + g_payloadReady = R_SUCCEEDED(Hooks().CopyPayload()); + MtcGenerateFreqTables(); u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp index cc99ba21..a3381515 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp @@ -34,6 +34,9 @@ namespace ams::ldr::hoc::pcv::mariko { uintptr_t originalFnCallback; u32 table[BusFreqTableCount][EmcDvfsTableEntryCount]; /* Original bus table size: 32. */ } busData; +#if HOC_UART_LOG + u32 verbosityLevel; +#endif }; DECLARE_HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData); @@ -84,18 +87,14 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 EmcSocLutCountStoreAsm = 0xB9015408; /* str w?,[x0,#0x154] (anchor) */ constexpr u32 EmcSocFreqStoreAsm = 0xF9000D00; /* str x?,[x8,#0x18] */ constexpr u32 EmcSocVoltStoreAsm = 0xB9004900; /* str w?,[x8,#0x48] (socMinLut[i]) */ - constexpr u32 EmcSocReadLoadAsm = 0xB9404929; /* ldr w?,[x9,#0x48] (socMinLut readback) */ + constexpr u32 EmcSocReadLoadAsm = 0xB9404929; /* ldr w?,[x9,#0x48] (socMinLut) */ inline bool EmcDvfsCountPatternFn(u32 *ptr) { - /* Local context: cbz w?, ; cmp w?,#0x21 ; b.cs */ - return _asm::Ignoring(*ptr, EmcCountCmpAsm, _asm::field::Rd) - && _asm::Ignoring(*(ptr + 1), _asm::Encode(_asm::op::BCond, {_asm::field::BCond, _asm::cond::Cs}), _asm::field::Imm19) /* b.cs */ - && _asm::IsOp(*(ptr - 1), _asm::op::Cbz, _asm::field::Imm19, _asm::field::Rt); /* cbz */ + return _asm::Ignoring(*ptr, EmcCountCmpAsm, _asm::field::Rd); } inline bool EmcSocLutPatternFn(u32 *ptr) { - return _asm::Ignoring(*ptr, EmcSocLutPtrStoreAsm, _asm::field::Rt) /* str x?,[x0,#0x120] */ - && _asm::Ignoring(*(ptr + 1), EmcSocLutCountStoreAsm, _asm::field::Rt); /* str w?,[x0,#0x154] */ + return _asm::Ignoring(*ptr, EmcSocLutPtrStoreAsm, _asm::field::Rt); /* str x?,[x0,#0x120] */ } /* @@ -112,10 +111,7 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 EmcRateCapCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt */ inline bool EmcRateListPatternFn(u32 *ptr) { - return _asm::Ignoring(*ptr, EmcRateCapCmpAsm, _asm::field::Rd, _asm::field::Rn) /* cmp w?,#0x20 */ - && _asm::Ignoring(*(ptr + 1), EmcRateCapCselAsm, _asm::field::Rd, _asm::field::Rn, _asm::field::Rm) /* csel w?,w?,w?,lt */ - && (_asm::Get(*ptr, _asm::field::Rn) == _asm::Get(*(ptr + 1), _asm::field::Rn)) /* min(reg, 0x20) */ - && _asm::IsOp(*(ptr + 2), _asm::op::Bl, _asm::field::Imm26); /* bl <_asm::Get*DvfsFreqTable> */ + return _asm::Ignoring(*ptr, EmcRateCapCmpAsm, _asm::field::Rd, _asm::field::Rn); } constexpr u32 EmcRateSessCmpAsm = 0x710082FF; /* cmp w?,#0x20 */ @@ -123,7 +119,6 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 EmcRateSessCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt (opcode + cond) */ inline bool EmcRateSessFindClamp(u32 *ptr, u32 *out_c, u32 *out_cap, u32 *out_movz_i) { - if (!_asm::Ignoring(ptr[0], EmcRateSessCmpAsm, _asm::field::Rd, _asm::field::Rn)) return false; /* cmp w,#0x20 */ const u32 c = _asm::Get(ptr[0], _asm::field::Rn); for (u32 i = 1; i <= 14; ++i) { const u32 w = ptr[i]; @@ -144,22 +139,18 @@ namespace ams::ldr::hoc::pcv::mariko { } inline bool EmcRateSessPatternFn(u32 *ptr) { - return EmcRateSessFindClamp(ptr, nullptr, nullptr, nullptr); + return _asm::Ignoring(*ptr, EmcRateSessCmpAsm, _asm::field::Rd, _asm::field::Rn); } inline bool BusFreqRelocPatternFn(u32 *ptr) { - if (g_pcv_scratch == 0 || g_pcv_cave == 0) { + if (g_pcv_cave == 0) { return false; } if (reinterpret_cast(ptr + 4) > g_pcv_cave) { /* the call site lives in .text */ return false; } - if (!(_asm::IsOp(ptr[0], _asm::op::LdrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) && _asm::Get(ptr[0], _asm::field::Off8) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */ - if (!(_asm::IsOp(ptr[1], _asm::op::AddImm64, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12) && _asm::Get(ptr[1], _asm::field::Imm12) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */ - if (!(_asm::IsOp(ptr[2], _asm::op::StrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) && _asm::Get(ptr[2], _asm::field::Off8) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/ - if (!_asm::IsOp(ptr[3], _asm::op::Bl, _asm::field::Imm26)) return false; /* bl GetDvfsRailUnique */ - const u32 bus = _asm::Get(ptr[0], _asm::field::Rn); - return _asm::Get(ptr[1], _asm::field::Rn) == bus && _asm::Get(ptr[2], _asm::field::Rn) == bus; + return _asm::IsOp(*ptr, _asm::op::LdrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) + && _asm::Get(*ptr, _asm::field::Off8) == 0x10; /* ldr Xbuf,[Xbus,#0x10] */ } inline bool ForceVerbosityPatternFn(u32 *ptr) { @@ -169,34 +160,30 @@ namespace ams::ldr::hoc::pcv::mariko { if (reinterpret_cast(ptr + 11) > g_pcv_cave) { /* .text only */ return false; } - if (ptr[0] != 0xA9BE7BFDu || ptr[1] != 0xF9000BF3u || ptr[2] != 0x910003FDu) return false; /* stp/str/mov x29,sp */ - if (!(_asm::IsOp(ptr[3], _asm::op::AddImm64, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12) && _asm::Get(ptr[3], _asm::field::Rd) == 0 && _asm::Get(ptr[3], _asm::field::Rn) == _asm::reg::Fp)) return false; /* add x0,x29,#imm */ - if (!(_asm::IsOp(ptr[4], _asm::op::AddImm64, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12) && _asm::Get(ptr[4], _asm::field::Rd) == 19 && _asm::Get(ptr[4], _asm::field::Rn) == _asm::reg::Fp)) return false; /* add x19,x29,#imm */ - if (_asm::Get(ptr[3], _asm::field::Imm12) != _asm::Get(ptr[4], _asm::field::Imm12) || !_asm::IsOp(ptr[5], _asm::op::Bl, _asm::field::Imm26)) return false; - for (u32 j = 6; j <= 10; ++j) { - if (ptr[j] == 0x7100001Fu) { /* cmp w0,#0 */ - return true; - } - } - return false; + return _asm::IsFramePushPre(*ptr); } - /* vsnprintf(buf,size,fmt,va_list) prologue */ - inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu }; + /* + sub sp, sp, #imm + stp x29,x30,[sp,#imm] + str x?, [sp,#imm] + add x29,sp, #imm + subs xzr, x1, #0 + */ + constexpr u32 NvLogSubSpAsm = 0xD10003FFu; /* sub sp,sp,#0 */ + constexpr u32 NvLogStpFpLrAsm = 0xA9007BFDu; /* stp x29,x30,[sp,#0] */ + constexpr u32 NvLogStrSpillAsm = 0xF90003E0u; /* str x?,[sp,#0] */ + constexpr u32 NvLogMovFpAsm = 0x910003FDu; /* add x29,sp,#0 */ + constexpr u32 NvLogCmpSizeAsm = 0xF100003Fu; /* subs xzr,x1,#0 */ inline bool NvLogVsnprintfPatternFn(u32 *ptr) { if (HOC_UART_LOG == 0 || g_pcv_cave == 0) { return false; } - if (reinterpret_cast(ptr + std::size(NvLogVsnSig)) > g_pcv_cave) { /* must sit in .text */ + if (reinterpret_cast(ptr + 5) > g_pcv_cave) { /* must sit in .text */ return false; } - for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) { - if (ptr[k] != NvLogVsnSig[k]) { - return false; - } - } - return true; + return _asm::Ignoring(*ptr, NvLogSubSpAsm, _asm::field::Imm12); } void Patch(uintptr_t mapped_nso, size_t nso_size);