ldr: code cleanup and fix soc bus table bug
This commit is contained in:
@@ -62,11 +62,14 @@ namespace ams::ldr {
|
||||
R_DEFINE_ERROR_RESULT(InvalidEmcDvfsCount, 1019);
|
||||
R_DEFINE_ERROR_RESULT(InvalidEmcSocLut, 1020);
|
||||
R_DEFINE_ERROR_RESULT(InvalidEmcRateList, 1021);
|
||||
R_DEFINE_ERROR_RESULT(InvalidNvLogRedirect, 1022);
|
||||
R_DEFINE_ERROR_RESULT(InvalidBusFreqReloc, 1023);
|
||||
}
|
||||
|
||||
namespace ams::ldr::hoc {
|
||||
/* Extra pcv .bss for 64LUT */
|
||||
constexpr size_t HocPcvScratchSize = 0x1000;
|
||||
/* Extra pcv .bss */
|
||||
constexpr size_t HocPcvScratchSize = 0x2000;
|
||||
constexpr size_t HocBusFreqBufOffset = 0x1000; /* start of the SOC bus region */
|
||||
|
||||
template<typename Pointer>
|
||||
struct PatcherEntry {
|
||||
@@ -79,6 +82,7 @@ namespace ams::ldr::hoc {
|
||||
patternFn pattern_search_fn = nullptr;
|
||||
Pointer value_search;
|
||||
size_t patched_count = 0;
|
||||
bool optional = false;
|
||||
|
||||
Result Apply(Pointer *ptr) {
|
||||
Result res = patcher_fn(ptr);
|
||||
@@ -109,7 +113,7 @@ namespace ams::ldr::hoc {
|
||||
}
|
||||
|
||||
Result CheckResult() {
|
||||
R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
||||
R_UNLESS(optional || patched_count > 0, ldr::ResultUnsuccessfulPatcher());
|
||||
|
||||
if (maximum_patched_count) {
|
||||
R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher());
|
||||
|
||||
@@ -186,6 +186,16 @@ namespace ams::ldr::hoc::pcv {
|
||||
return 0xB9000000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* ldr Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */
|
||||
inline auto AsmMakeLdrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 {
|
||||
return 0xB9400000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu);
|
||||
};
|
||||
|
||||
/* add Xd,Xn,Xm,LSL #shift (64-bit shifted register, shift 0-63). */
|
||||
inline auto AsmMakeAddShiftedReg64 = [](u32 rd, u32 rn, u32 rm, u32 shift) -> u32 {
|
||||
return 0x8B000000u | ((rm & 0x1Fu) << 16) | ((shift & 0x3Fu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu);
|
||||
};
|
||||
|
||||
/* stp Xt1,Xt2,[Xn,#imm] (signed offset, scaled by 8). */
|
||||
inline auto AsmMakeStpImm64 = [](u32 rt1, u32 rt2, u32 rn, s32 imm) -> u32 {
|
||||
return 0xA9000000u | ((static_cast<u32>(imm / 8) & 0x7Fu) << 15) | ((rt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (rt1 & 0x1Fu);
|
||||
@@ -254,6 +264,11 @@ namespace ams::ldr::hoc::pcv {
|
||||
inline auto AsmIsBl = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x94000000u; }; /* bl */
|
||||
inline auto AsmIsBCond = [](u32 ins) -> bool { return (ins & 0xFF000010u) == 0x54000000u; }; /* b.c */
|
||||
|
||||
/* ldr/str Xt,[Xn,#imm] (64-bit, unsigned scaled offset). */
|
||||
inline auto AsmIsLdrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9400000u; };
|
||||
inline auto AsmIsStrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9000000u; };
|
||||
inline auto AsmGetLdStImm64Off = [](u32 ins) -> u32 { return ((ins >> 10) & 0xFFFu) * 8u; };
|
||||
|
||||
/* Byte target address of a b/bl at pc. */
|
||||
inline auto AsmBranchTarget = [](u32 ins, uintptr_t pc) -> uintptr_t {
|
||||
s64 off = static_cast<s64>((ins & 0x03FFFFFFu) << 2);
|
||||
|
||||
@@ -620,6 +620,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
namespace {
|
||||
std::vector<u32> newEmcList;
|
||||
u32 *nsoStart;
|
||||
size_t g_nso_size = 0;
|
||||
uintptr_t g_cave_cursor = 0;
|
||||
}
|
||||
|
||||
@@ -635,26 +636,12 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void NvLogUartRedirect(uintptr_t mapped_nso, size_t nso_size) {
|
||||
const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */
|
||||
if (textEnd == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* vsnprintf(buf,size,fmt,va_list) */
|
||||
static const u32 VsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
||||
uintptr_t vsnprintf_addr = 0;
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + std::size(VsnSig)) <= textEnd; ++p) {
|
||||
bool ok = true;
|
||||
for (size_t k = 0; k < std::size(VsnSig); ++k) {
|
||||
if (p[k] != VsnSig[k]) { ok = false; break; }
|
||||
}
|
||||
if (ok) { vsnprintf_addr = reinterpret_cast<uintptr_t>(p); break; }
|
||||
}
|
||||
if (vsnprintf_addr == 0) {
|
||||
LOGGING("NvLogRedirect: vsnprintf not found");
|
||||
return;
|
||||
}
|
||||
/* Redirect pcv's NvLog() calls to UART */
|
||||
Result NvLogUartRedirect(u32 *ptr) {
|
||||
const uintptr_t mapped_nso = reinterpret_cast<uintptr_t>(nsoStart);
|
||||
const size_t nso_size = g_nso_size;
|
||||
const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */
|
||||
const uintptr_t vsnprintf_addr = reinterpret_cast<uintptr_t>(ptr);
|
||||
|
||||
/* NvLog via the VDD_SOC log */
|
||||
static const char Fmt[] = "%s(%s): DVFS request VDD_SOC %d mV\n";
|
||||
@@ -668,7 +655,7 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
}
|
||||
if (strAddr == 0) {
|
||||
LOGGING("NvLogRedirect: fmt string not found (vsnprintf@+%lx)", vsnprintf_addr - mapped_nso);
|
||||
return;
|
||||
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||
}
|
||||
|
||||
uintptr_t nvlog_addr = 0;
|
||||
@@ -694,14 +681,14 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
}
|
||||
if (nvlog_addr == 0 || nvlog_addr < mapped_nso || nvlog_addr >= textEnd) {
|
||||
LOGGING("NvLogRedirect: NvLog entry not found (fmt@+%lx)", strAddr - mapped_nso);
|
||||
return;
|
||||
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||
}
|
||||
|
||||
const uintptr_t helper = CaveReserve(40);
|
||||
if (helper == 0) {
|
||||
LOGGING("NvLogRedirect: cave unavailable (cave=%lx size=%lx)",
|
||||
static_cast<unsigned long>(g_pcv_cave), static_cast<unsigned long>(g_pcv_cave_size));
|
||||
return;
|
||||
R_THROW(ldr::ResultInvalidNvLogRedirect());
|
||||
}
|
||||
u32 *t = reinterpret_cast<u32 *>(helper);
|
||||
size_t n = 0;
|
||||
@@ -767,44 +754,61 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
|
||||
LOGGING("NvLogRedirect: stub@+%lx vsnprintf@+%lx helper@+%lx instr=%zu sites=%zu",
|
||||
nvlog_addr - mapped_nso, vsnprintf_addr - mapped_nso, helper - mapped_nso, n, patchedSites);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs */
|
||||
static void ForceVerbosity() {
|
||||
const uintptr_t textEnd = g_pcv_cave;
|
||||
if (textEnd == 0 || HOC_PCV_FORCE_VERBOSITY == 0) {
|
||||
return;
|
||||
/* Relocate C2/C3Bus to avoid issues*/
|
||||
Result BusFreqReloc(u32 *ptr) {
|
||||
const u32 busReg = AsmGetRn(ptr[0]); /* ldr Xbuf,[Xbus,#0x10] : bus struct pointer */
|
||||
const u32 bufReg = asm_get_rd(ptr[0]); /* : freq-buffer arg */
|
||||
const u32 bufOff = AsmGetLdStImm64Off(ptr[0]); /* : bus->freqBuf offset */
|
||||
const u32 cntReg = asm_get_rd(ptr[1]); /* add Xcnt,Xbus,#0x18 : arg2 (&count) */
|
||||
const u32 railReg = asm_get_rd(ptr[2]); /* str Xrail,[Xbus,#0x50]: arg0 (rail) */
|
||||
u32 *call = ptr + 3; /* the bl to relocate */
|
||||
const uintptr_t realFn = AsmBranchTarget(*call, reinterpret_cast<uintptr_t>(call));
|
||||
|
||||
/* Pick 3 scratch registers */
|
||||
u32 s[3], sc = 0;
|
||||
for (u32 r = 9; r <= 15 && sc < 3; ++r) {
|
||||
if (r != busReg && r != bufReg && r != cntReg && r != railReg) {
|
||||
s[sc++] = r;
|
||||
}
|
||||
}
|
||||
size_t patched = 0;
|
||||
for (u32 *p = nsoStart; reinterpret_cast<uintptr_t>(p + 11) <= textEnd; ++p) {
|
||||
if (p[0] != 0xA9BE7BFDu || p[1] != 0xF9000BF3u || p[2] != 0x910003FDu) { /* stp/str/mov x29,sp */
|
||||
continue;
|
||||
}
|
||||
if (!(AsmIsAddImm64(p[3]) && asm_get_rd(p[3]) == 0 && AsmGetRn(p[3]) == 29)) continue; /* add x0,x29,#imm */
|
||||
if (!(AsmIsAddImm64(p[4]) && asm_get_rd(p[4]) == 19 && AsmGetRn(p[4]) == 29)) continue; /* add x19,x29,#imm */
|
||||
if (AsmGetImm12(p[3]) != AsmGetImm12(p[4]) || !AsmIsBl(p[5])) {
|
||||
continue;
|
||||
}
|
||||
bool hasCmp = false;
|
||||
for (u32 j = 6; j <= 10; ++j) {
|
||||
if (p[j] == 0x7100001Fu) { /* cmp w0,#0 */
|
||||
hasCmp = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasCmp) {
|
||||
continue;
|
||||
}
|
||||
PATCH_OFFSET(&p[0], AsmMakeMovzW(0, static_cast<u16>(HOC_PCV_FORCE_VERBOSITY)));
|
||||
PATCH_OFFSET(&p[1], RetIns);
|
||||
++patched;
|
||||
}
|
||||
LOGGING("ForceVerbosity: patched %zu getter(s) to level %d", patched, HOC_PCV_FORCE_VERBOSITY);
|
||||
R_UNLESS(sc == 3, ldr::ResultInvalidBusFreqReloc());
|
||||
|
||||
const uintptr_t tramp = CaveReserve(9);
|
||||
R_UNLESS(tramp != 0, ldr::ResultInvalidBusFreqReloc());
|
||||
|
||||
const uintptr_t region = g_pcv_scratch + HocBusFreqBufOffset; /* [0]=counter, +0x10 + i*0x400 = bufs */
|
||||
u32 *t = reinterpret_cast<u32 *>(tramp);
|
||||
size_t n = 0;
|
||||
auto emit = [&](u32 ins) { t[n] = ins; ++n; };
|
||||
emit(AsmMakeAdrp(tramp + n * 4, region, s[0])); /* adrp s0,<region> */
|
||||
emit(AsmMakeAddImm64(s[0], s[0], region & 0xFFFu)); /* add s0,s0,#lo */
|
||||
emit(AsmMakeLdrImm32(s[1], s[0], 0x00)); /* s1 = counter */
|
||||
emit(AsmMakeAddImm64(s[2], s[1], 1)); /* s2 = counter+1 */
|
||||
emit(AsmMakeStrImm32(s[2], s[0], 0x00)); /* counter++ */
|
||||
emit(AsmMakeAddImm64(s[0], s[0], 0x10)); /* s0 = region+0x10 (buffers) */
|
||||
emit(AsmMakeAddShiftedReg64(bufReg, s[0], s[1], 10)); /* Xbuf = s0 + counter*0x400 */
|
||||
emit(AsmMakeStrImm64(bufReg, busReg, bufOff)); /* bus[freqBuf] = Xbuf */
|
||||
emit(AsmMakeB(tramp + n * 4, realFn)); /* tail-call the real function */
|
||||
|
||||
PATCH_OFFSET(call, AsmMakeBl(reinterpret_cast<uintptr_t>(call), tramp));
|
||||
const uintptr_t base = reinterpret_cast<uintptr_t>(nsoStart);
|
||||
LOGGING("BusFreqReloc: call@+%lx -> tramp@+%lx realfn@+%lx (bus=x%u buf=x%u off=0x%x scratch=x%u,x%u,x%u)",
|
||||
reinterpret_cast<uintptr_t>(call) - base, tramp - base, realFn - base, busReg, bufReg, bufOff, s[0], s[1], s[2]);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs. */
|
||||
Result ForceVerbosity(u32 *ptr) {
|
||||
PATCH_OFFSET(&ptr[0], AsmMakeMovzW(0, static_cast<u16>(HOC_PCV_FORCE_VERBOSITY))); /* movz w0,#level */
|
||||
PATCH_OFFSET(&ptr[1], RetIns); /* ret */
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Widen InitDram for a >32-entry EMC DVFS list. Freq array can be dropped to free 264 bytes, relocate the Soc LUT to that space */
|
||||
Result EmcSocLutReloc(u32 *ptr) {
|
||||
R_UNLESS(EmcSocLutPatternFn(ptr), ldr::ResultInvalidEmcSocLut()); /* str lut,[rail+0x120] ; str n,[rail+0x154] */
|
||||
constexpr u32 Window = 48;
|
||||
|
||||
u32 *freqStore = ScanAssembly(ptr - Window, Window, EmcSocFreqStoreAsm, asm_compare_no_rd); /* str x?,[x8,#0x18] */
|
||||
@@ -1473,11 +1477,9 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
nsoStart = reinterpret_cast<u32 *>(mapped_nso);
|
||||
|
||||
g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize;
|
||||
g_nso_size = nso_size;
|
||||
g_cave_cursor = g_pcv_cave; /* start the .text-cave bump allocator (0 if unavailable) */
|
||||
|
||||
NvLogUartRedirect(mapped_nso, nso_size);
|
||||
ForceVerbosity();
|
||||
|
||||
MtcGenerateFreqTables();
|
||||
|
||||
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
|
||||
@@ -1506,8 +1508,12 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
{ "EMC SoC LUT", &EmcSocLutReloc, 1, &EmcSocLutPatternFn },
|
||||
{ "EMC Rate List", &EmcRateListLimit, 0, &EmcRateListPatternFn },
|
||||
{ "EMC Rate Sess", &EmcRateSessLimit, 1, &EmcRateSessPatternFn },
|
||||
{ "Bus Freq Reloc", &BusFreqReloc, 1, &BusFreqRelocPatternFn },
|
||||
{ "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn },
|
||||
{ "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial },
|
||||
/* Debugging patches */
|
||||
{ "NvLog Redirect", &NvLogUartRedirect, 1, &NvLogVsnprintfPatternFn, 0, 0, true },
|
||||
{ "Force Verbosity", &ForceVerbosity, 3, &ForceVerbosityPatternFn, 0, 0, true },
|
||||
};
|
||||
|
||||
for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) {
|
||||
|
||||
@@ -371,6 +371,59 @@ namespace ams::ldr::hoc::pcv::mariko {
|
||||
return EmcRateSessFindClamp(ptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
inline bool BusFreqRelocPatternFn(u32 *ptr) {
|
||||
if (g_pcv_scratch == 0 || 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 (!(AsmIsLdrImm64(ptr[0]) && AsmGetLdStImm64Off(ptr[0]) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */
|
||||
if (!(AsmIsAddImm64(ptr[1]) && AsmGetImm12(ptr[1]) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */
|
||||
if (!(AsmIsStrImm64(ptr[2]) && AsmGetLdStImm64Off(ptr[2]) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/
|
||||
if (!AsmIsBl(ptr[3])) return false; /* bl GetDvfsRailUnique */
|
||||
const u32 bus = AsmGetRn(ptr[0]);
|
||||
return AsmGetRn(ptr[1]) == bus && AsmGetRn(ptr[2]) == bus;
|
||||
}
|
||||
|
||||
inline bool ForceVerbosityPatternFn(u32 *ptr) {
|
||||
if (HOC_PCV_FORCE_VERBOSITY == 0 || g_pcv_cave == 0) {
|
||||
return false;
|
||||
}
|
||||
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 (!(AsmIsAddImm64(ptr[3]) && asm_get_rd(ptr[3]) == 0 && AsmGetRn(ptr[3]) == 29)) return false; /* add x0,x29,#imm */
|
||||
if (!(AsmIsAddImm64(ptr[4]) && asm_get_rd(ptr[4]) == 19 && AsmGetRn(ptr[4]) == 29)) return false; /* add x19,x29,#imm */
|
||||
if (AsmGetImm12(ptr[3]) != AsmGetImm12(ptr[4]) || !AsmIsBl(ptr[5])) return false;
|
||||
for (u32 j = 6; j <= 10; ++j) {
|
||||
if (ptr[j] == 0x7100001Fu) { /* cmp w0,#0 */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* vsnprintf(buf,size,fmt,va_list) prologue */
|
||||
inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu };
|
||||
|
||||
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 */
|
||||
return false;
|
||||
}
|
||||
for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) {
|
||||
if (ptr[k] != NvLogVsnSig[k]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user