clean up pattern matching
This commit is contained in:
1 parent
b6fcd6072c
commit
4023ae6838
3 files changed
+105
-47
No files matched your search
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user