This commit is contained in:
souldbminersmwc committed 2026-10-07 19:43:05 -04:00
1 parent e28d7d1206
commit 8403386f1a
14 files changed
+277 -68

No files matched your search

@@ -60,39 +60,42 @@
*(offset) = value;
namespace ams::ldr {
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005);
R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006);
R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007);
R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008);
R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009);
R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010);
R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011);
R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012);
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
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);
R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1024);
R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1025);
R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1026);
R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1027);
R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1028);
R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1029);
R_DEFINE_ERROR_RESULT(HookUnavailable, 1030);
R_DEFINE_ERROR_RESULT(InvalidNvsrvDevTable, 1031);
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002);
R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003);
R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004);
R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005);
R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006);
R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007);
R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008);
R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009);
R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010);
R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011);
R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012);
R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013);
R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014);
R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015);
R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016);
R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017);
R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018);
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);
R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1024);
R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1025);
R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1026);
R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1027);
R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1028);
R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1029);
R_DEFINE_ERROR_RESULT(HookUnavailable, 1030);
R_DEFINE_ERROR_RESULT(InvalidNvsrvDevTable, 1031);
R_DEFINE_ERROR_RESULT(InvalidForceVerbosityPattern, 1032);
R_DEFINE_ERROR_RESULT(InvalidCpuLutMaxAsmPattern, 1033);
R_DEFINE_ERROR_RESULT(InvalidCpuLutHook, 1034);
}
namespace ams::ldr::hoc {
@@ -48,6 +48,8 @@ namespace ams::ldr::hoc::pcv::mariko {
R_TRY(SharedClkBusInstallHooks(data));
R_TRY(LutWriterInstallHooks(data));
#if HOC_UART_LOG
R_TRY(ForceVebosityInstallHooks(data));
#endif
@@ -90,6 +92,10 @@ namespace ams::ldr::hoc::pcv::mariko {
{ "Bus Freq Reloc", &BusFreqReloc, 1, &BusFreqRelocPatternFn },
{ "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn },
{ "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial },
{ "CPU Lut Max Asm", &CpuLutMaxAsm, 1, &CpuLutAsmPatternFn },
{ "CPU Lut Max Asm2", &CpuLutMaxAsm2, 1, &CpuLutAsmPatternFn2 },
{ "CPU Lut Writer", &LutWriterFind, 1, &LutWriterPatternFn },
/* Debugging patches */
#if HOC_UART_LOG
{ "NvLog Redirect", &NvLogUartRedirect, 1, &NvLogVsnprintfPatternFn, 0, 0, true },
@@ -23,6 +23,7 @@
#include "../../oc_common.hpp"
#include "../pcv_common.hpp"
#include "../pcv_asm.hpp"
#include "../pcv_hook.hpp"
namespace ams::ldr::hoc::pcv::mariko {
@@ -34,6 +35,9 @@ namespace ams::ldr::hoc::pcv::mariko {
uintptr_t originalFnCallback;
u32 table[BusFreqTableCount][EmcDvfsTableEntryCount]; /* Original bus table size: 32. */
} busData;
struct {
uintptr_t orig; /* ClDvfsLut64ExpandImpl to CLDVFS LUT writer trampoline. */
} lut64;
#if HOC_UART_LOG
u32 verbosityLevel;
#endif
@@ -235,4 +235,126 @@ namespace ams::ldr::hoc::pcv::mariko {
R_SUCCEED();
}
/* Little helper for ORR:LSL pairs which are found in here */
inline bool IsOrrLsl(u32 w) {
if (!_asm::IsOp(w, _asm::op::OrrShifted32, _asm::field::Rd, _asm::field::Rn, _asm::field::Rm, _asm::field::Shift, _asm::field::Imm6))
return false;
return _asm::Get(w, _asm::field::Shift) == 0; /* 0=LSL, 1=LSR, 2=ASR, 3=ROR */
}
/* Probably overcomplicated but I wanted to have a rock-solid way to do this regardless of FW version */
Result CpuLutMaxAsm(u32* ptr) {
/* Check for the dual ORR-LSL pairs*/
R_UNLESS(IsOrrLsl(ptr[2]), ldr::ResultInvalidCpuLutMaxAsmPattern());
R_UNLESS(IsOrrLsl(ptr[3]), ldr::ResultInvalidCpuLutMaxAsmPattern());
/* We can safely assume that we are in the right place now */
/* Safety check */
R_UNLESS(ptr - 10 >= nsoStart, ldr::ResultInvalidCpuLutMaxAsmPattern());
/* Search for the STRB */
u32* strb = _asm::ScanAssembly(ptr - 10, 10, _asm::Encode(_asm::op::StrbImm,
{_asm::field::Rt, 0}, {_asm::field::Rn, 0}, {_asm::field::Off1, 0x25D}), _asm::field::Rt,
_asm::field::Rn);
R_UNLESS(strb != nullptr, ldr::ResultInvalidCpuLutMaxAsmPattern());
/* Probably a cleaner way to do this?*/
u32 *prod = strb + 2;
const u32 rd = _asm::Get(*prod, _asm::field::Rd);
const u32 off = static_cast<u32>(prod - nsoStart) * 4;
const u32 old = *prod;
if (_asm::IsOp(*prod, _asm::op::Ubfm32, _asm::field::Rd, _asm::field::Rn, _asm::field::Immr, _asm::field::Imm6)) {
/* replacement must carry LSL#16 to make up for ubfiz's remoal */
const u32 nw = _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, rd}, {_asm::field::Imm16, 0x3F}, {_asm::field::Hw, 1});
PATCH_OFFSET(prod, nw); /* movz wD,#0x3F,LSL#16 */
} else {
/* Imm12 is 7 for 0xff*/
R_UNLESS(_asm::IsOp(*prod, _asm::op::AndImm32, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12) && _asm::Get(*prod, _asm::field::Imm12) == 7, ldr::ResultInvalidCpuLutMaxAsmPattern());
/* No need to do the lsl */
const u32 nw = _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, rd}, {_asm::field::Imm16, 0x3F});
PATCH_OFFSET(prod, nw); /* movz wD,#0x3F */
}
/* Set SAFE to entry 32 */
PATCH_OFFSET(ptr + 1, _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, _asm::Get(ptr[1], _asm::field::Rd)}, {_asm::field::Imm16, 0x20}));
R_SUCCEED();
}
namespace {
struct {
u32 *site = nullptr; /* LUT writer prologue. */
} lutWriterCache;
}
HOOK_PAYLOAD_FN void CpuLutWriterExpandImpl(u64 param) {
HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, m_HookPayloadData);
reinterpret_cast<void (*)(u64)>(data->lut64.orig)(param);
/* *(param+8) = CLDVFS mapping */
const u64 base = *reinterpret_cast<u64 *>(param + 8);
u32 *lut = reinterpret_cast<u32 *>(base + 0x200);
/* Expand the LUT in place */
const u32 last = lut[31];
lut[62] = last;
lut[63] = last;
for (int i = 30; i >= 0; --i) {
const u32 a = lut[i];
const u32 b = lut[i + 1]; /* dst 2*(i+1) > src i+1 */
lut[2 * i] = a;
lut[2 * i + 1] = (a + b) >> 1;
}
__asm__ volatile("dsb st" ::: "memory");
}
Result LutWriterFind(u32 *ptr) {
R_UNLESS(LutWriterPatternFn(ptr), ldr::ResultInvalidCpuLutHook());
u32 *prog = _asm::FindFnPrologue(ptr, 140, nsoStart);
R_UNLESS(prog != nullptr, ldr::ResultInvalidCpuLutHook());
/* Prolouge must hold MMIO base, so obtain it. */
bool ok = false;
for (u32 i = 0; i < 8; ++i) {
if (_asm::IsOp(prog[i], _asm::op::LdrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8)
&& _asm::Get(prog[i], _asm::field::Rn) == 0
&& _asm::Get(prog[i], _asm::field::Off8) == 0x8) {
ok = true;
break;
}
}
R_UNLESS(ok, ldr::ResultInvalidCpuLutHook());
lutWriterCache.site = prog;
R_SUCCEED();
}
Result LutWriterInstallHooks(HookPayloadData *data) {
R_UNLESS(lutWriterCache.site != nullptr, ldr::ResultInvalidCpuLutHook());
uintptr_t orig = 0;
R_TRY(INSTALL_IMPL_HOOK_ORIG(lutWriterCache.site, CpuLutWriterExpandImpl, &orig));
data->lut64.orig = orig;
R_SUCCEED();
}
Result CpuLutMaxAsm2(u32* ptr) {
/* movk w?,#0xFFC0,LSL#16 */
R_UNLESS(_asm::IsOp(ptr[1], _asm::op::MovkW, _asm::field::Rd, _asm::field::Imm16, _asm::field::Hw)
&& _asm::Get(ptr[1], _asm::field::Imm16) == 0xFFC0
&& _asm::Get(ptr[1], _asm::field::Hw) == 1, ldr::ResultInvalidCpuLutMaxAsmPattern());
/* and x, x, #0xff */
R_UNLESS(_asm::IsOp(ptr[2], _asm::op::AndImm32, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12) ||
_asm::Get(ptr[2], _asm::field::Imm12) != 7, ldr::ResultInvalidCpuLutMaxAsmPattern());
const u32 rd = _asm::Get(ptr[2], _asm::field::Rd);
PATCH_OFFSET(ptr + 2, _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, rd}, {_asm::field::Imm16, 0x3F})); /* movz wD,#0x3F */
R_SUCCEED();
}
}
@@ -23,9 +23,28 @@
#pragma once
#include "../pcv.hpp"
#include "../pcv_asm.hpp"
#include "../pcv_hook.hpp"
namespace ams::ldr::hoc::pcv::mariko {
extern u32 *nsoStart;
/* Maybe a cleaner way to do this? */
inline bool LutWriterPatternFn(u32 *ptr) {
if (_asm::IsOp(*ptr, _asm::op::AndImm64, _asm::field::Rd, _asm::field::Rn, _asm::field::Immr, _asm::field::Imm6)
&& _asm::Get(*ptr, _asm::field::Immr) == 61 && _asm::Get(*ptr, _asm::field::Imm6) == 7)
return true;
if (_asm::IsOp(*ptr, _asm::op::AndImm32, _asm::field::Rd, _asm::field::Rn, _asm::field::Imm12)
&& _asm::Get(*ptr, _asm::field::Imm12) == 0x747)
return true;
return false;
}
HOOK_PAYLOAD_FN void CpuLutWriterExpandImpl(u64 param);
Result LutWriterFind(u32 *ptr);
Result LutWriterInstallHooks(HookPayloadData *data);
constexpr cvb_entry_t CpuCvbTableDefault[] = {
{ 204000, { 721589, -12695, 27 }, { } },
{ 306000, { 747134, -14195, 27 }, { } },
@@ -63,10 +82,22 @@ namespace ams::ldr::hoc::pcv::mariko {
/* Refer to customize.cpp for more information */
static const u32 allowedCpuMaxFrequencies[] = { 1'963'500, 2'091'000, 2'193'000, 2'295'000, 2'397'000, 2'499'000, 2'601'000, 2'703'000, 2'805'000 };
Result CpuFreqVdd(u32 *ptr);
Result CpuVoltDVFS(u32 *ptr);
Result CpuVoltThermals(u32 *ptr);
Result CpuVoltDfll(u32 *ptr);
Result CpuLutMaxAsm(u32* ptr);
Result CpuLutMaxAsm2(u32* ptr);
ALWAYS_INLINE bool CpuLutAsmPatternFn(u32* ptr) {
/* Check if it is a DSB ST, a common indication of CL-DVFS related functions */
return *ptr == _asm::Dsb(_asm::barrier::St);
}
ALWAYS_INLINE bool CpuLutAsmPatternFn2(u32* ptr) {
/* Note: We are doing multiple checks here as it is the cleanest method */
return _asm::IsOp(*ptr, _asm::op::MovzW, _asm::field::Rd, _asm::field::Imm16, _asm::field::Hw)
&& _asm::Get(*ptr, _asm::field::Imm16) == 0xC0FF
&& _asm::Get(*ptr, _asm::field::Hw) == 0;
}
}
@@ -231,6 +231,8 @@ namespace ams::ldr::hoc::pcv::mariko {
R_DISCARD(INSTALL_IMPL_HOOK(forceVerbosityCache.sites[i], ForceVerbosityImpl));
}
}
R_SUCCEED();
}
#endif
@@ -82,6 +82,7 @@ namespace ams::ldr::hoc::pcv {
u32 max;
u32 panic;
bool value_required = false;
const char *name = "unnamed";
Result Check() {
if (!value_required && !value) {
@@ -147,26 +148,28 @@ namespace ams::ldr::hoc::pcv {
}
Validator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true },
{ C.marikoCpuBoostClock, 1020'000, 2805'000, panic::Cpu, true },
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, },
{ C.marikoCpuMaxVolt, 1000, 1235, panic::Cpu, },
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, },
{ marikoCpuDvfsMaxFreq, 1785'000, 2805'000, panic::Cpu, },
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, },
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, },
{ C.marikoSocVmax, 1000, 1200, panic::Emc, },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, },
{ marikoGpuDvfsMaxFreq, 768'000, 1574'400, panic::Gpu, },
{ C.marikoGpuVmax, 800, 995, panic::Gpu, }, /* Official GPU vMax is 1050mV */
{ C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true, "eristaCpuBoostClock" },
{ C.marikoCpuBoostClock, 1020'000, 2805'000, panic::Cpu, true, "marikoCpuBoostClock" },
{ C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, false, "eristaCpuMaxVolt" },
{ C.marikoCpuMaxVolt, 1000, 1235, panic::Cpu, false, "marikoCpuMaxVolt" },
{ eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, false, "eristaCpuDvfsMaxFreq"},
{ marikoCpuDvfsMaxFreq, 1785'000, 2805'000, panic::Cpu, false, "marikoCpuDvfsMaxFreq"},
{ C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, false, "commonEmcMemVolt" }, /* Official vmax for the RAMs is 1400-1500mV */
{ C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, false, "eristaEmcMaxClock" },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, false, "marikoEmcMaxClock" },
{ C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, false, "marikoEmcVddqVolt" },
{ C.marikoSocVmax, 1000, 1200, panic::Emc, false, "marikoSocVmax" },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, false, "eristaGpuDvfsMaxFreq"},
{ marikoGpuDvfsMaxFreq, 768'000, 1574'400, panic::Gpu, false, "marikoGpuDvfsMaxFreq"},
{ C.marikoGpuVmax, 800, 995, panic::Gpu, false, "marikoGpuVmax" }, /* Official GPU vMax is 1050mV */
};
for (auto &v : validators) {
if (R_FAILED(v.Check())) {
LOGGING("Validation FAIL: %s value=%u min=%u max=%u", v.name, static_cast<u32>(v.value), v.min, v.max);
panic::SmcError(v.panic);
CRASH("Validation FAIL");
}
}
}
@@ -66,6 +66,7 @@ namespace ams::ldr::hoc::pcv::_asm {
constexpr Descriptor Sh {22, 1};
constexpr Descriptor Shift {22, 2}; /* ADD/SUB (shifted register) shift type */
constexpr Descriptor Imm6 {10, 6}; /* ADD/SUB (shifted register) shift amount */
constexpr Descriptor Immr {16, 6}; /* UBFM immr */
constexpr Descriptor Imm16 { 5, 16}; /* MOVZ/MOVK/MOVN immediate */
constexpr Descriptor Imm12 {10, 12}; /* ADD/SUB (immediate) */
@@ -77,6 +78,8 @@ namespace ams::ldr::hoc::pcv::_asm {
constexpr Descriptor Off1 {10, 12, 0}; /* LDRB/STRB */
constexpr Descriptor Off4 {10, 12, 2}; /* 32-bit LDR/STR */
constexpr Descriptor Barrier { 8, 4}; /* DSB/DMB option (CRm) */
constexpr Descriptor Off8 {10, 12, 3}; /* 64-bit LDR/STR */
constexpr Descriptor PairOff8 {15, 7, 3}; /* X pairs */
@@ -114,6 +117,9 @@ namespace ams::ldr::hoc::pcv::_asm {
constexpr u32 MovnW = 0x12800000;
constexpr u32 AddShifted64 = 0x8B000000;
constexpr u32 AndImm32 = 0x12000000;
constexpr u32 AndImm64 = 0x92400000;
constexpr u32 Ubfm32 = 0x53000000; /* UBFIZ/UBFX/LSL aliases */
constexpr u32 OrrShifted32 = 0x2A000000;
constexpr u32 OrrShifted64 = 0xAA000000;
constexpr u32 SubsShifted32 = 0x6B000000;
@@ -140,6 +146,18 @@ namespace ams::ldr::hoc::pcv::_asm {
constexpr u32 Adrp = 0x90000000;
constexpr u32 Svc = 0xD4000001;
constexpr u32 Dsb = 0xD503309F; /* DSB, option cleared */
constexpr u32 Dmb = 0xD50330BF; /* DMB, option cleared */
constexpr u32 Isb = 0xD5033FDF; /* ISB SY (only valid option) */
}
/* Barrier options (CRm). */
namespace barrier {
constexpr u32 Sy = 0xFu, St = 0xEu, Ld = 0xDu;
constexpr u32 Ish = 0xBu, Ishst = 0xAu, Ishld = 0x9u;
constexpr u32 Nsh = 0x7u, Nshst = 0x6u, Nshld = 0x5u;
constexpr u32 Osh = 0x3u, Oshst = 0x2u, Oshld = 0x1u;
}
constexpr u32 Get(u32 ins, field::Descriptor f) {
@@ -217,6 +235,14 @@ namespace ams::ldr::hoc::pcv::_asm {
return Set(Set(ins, field::ImmAdrpHi, imm21 >> 2), field::ImmAdrpLo, imm21 & 0x3u);
}
constexpr u32 Dsb(u32 opt) {
return Encode(op::Dsb, {field::Barrier, opt});
}
constexpr u32 Dmb(u32 opt) {
return Encode(op::Dmb, {field::Barrier, opt});
}
constexpr u32 MovReg64(u32 rd, u32 rm) {
return Encode(op::OrrShifted64, {field::Rd, rd}, {field::Rn, reg::Xzr}, {field::Rm, rm});
}