clean up pattern matching

This commit is contained in:
souldbminersmwc committed 2026-08-27 14:56:02 -04:00
1 parent b6fcd6072c
commit 4023ae6838
3 files changed
+105 -47

No files matched your search

@@ -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 {
@@ -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<uintptr_t>(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?,<skip> */
R_UNLESS(_asm::Ignoring(*(ptr + 1), _asm::Encode(_asm::op::BCond, {_asm::field::BCond, _asm::cond::Cs}), _asm::field::Imm19),
ldr::ResultInvalidEmcDvfsCount()); /* b.cs <abort> */
/* 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<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
@@ -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?,<skip> ; cmp w?,#0x21 ; b.cs <abort> */
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<c>,#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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(ptr + std::size(NvLogVsnSig)) > g_pcv_cave) { /* must sit in .text */
if (reinterpret_cast<uintptr_t>(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);