diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 9c51fff8..f7d797a7 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -95,7 +95,7 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(InvalidForceVerbosityPattern, 1032); R_DEFINE_ERROR_RESULT(InvalidCpuLutMaxAsmPattern, 1033); R_DEFINE_ERROR_RESULT(InvalidCpuLutHook, 1034); - + R_DEFINE_ERROR_RESULT(InvalidDvcoRateConfig, 1035); } 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 75479c91..5bbda9a8 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 @@ -95,6 +95,7 @@ namespace ams::ldr::hoc::pcv::mariko { { "CPU Lut Max Asm", &CpuLutMaxAsm, 1, &CpuLutAsmPatternFn }, { "CPU Lut Max Asm2", &CpuLutMaxAsm2, 1, &CpuLutAsmPatternFn2 }, { "CPU Lut Writer", &LutWriterFind, 1, &LutWriterPatternFn }, + { "CPU Dvco Rate Cfg", &CpuLutDvcoRateCfg, 1, &CpuLutDvcoRateCfgPatternFn }, /* Debugging patches */ #if HOC_UART_LOG diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp index d394a160..37d4709c 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp @@ -273,8 +273,8 @@ namespace ams::ldr::hoc::pcv::mariko { 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})); + /* Set SAFE to entry 31 */ + PATCH_OFFSET(ptr + 1, _asm::Encode(_asm::op::MovzW, {_asm::field::Rd, _asm::Get(ptr[1], _asm::field::Rd)}, {_asm::field::Imm16, 0x1F})); R_SUCCEED(); } @@ -355,4 +355,99 @@ namespace ams::ldr::hoc::pcv::mariko { R_SUCCEED(); } + + + /* FW 20.4.0 + 71000571dc 01 01 00 d0 adrp param_2=>aCldvfssetdvcor,aSSRequestedDKh+0x1f = "ClDvfsSetDvcoRateMin" + = " %s divider %#x diff %d\n" + 71000571e0 21 40 27 91 add param_2=>aCldvfssetdvcor,param_2,#0x9d0 = "ClDvfsSetDvcoRateMin" + 71000571e4 c3 9f ff 97 bl nn::pcv::NvLog undefined NvLog(char * fmt, ...) + loc_71000571E8 XREF[1]: 71000571b4(j) + 71000571e8 69 aa 42 a9 ldp x9,x10,[x19, #0x28] + 71000571ec 4a fd 41 d3 lsr x10,x10,#0x1 + 71000571f0 68 26 41 f9 ldr x8,[x19, #0x248] Anchor + 71000571f4 0b 0d 0a cb sub x11,x8,x10, LSL #0x3 Target + 71000571f8 6b 4e 01 f9 str x11,[x19, #0x298] Verify1 + 71000571fc 2c 0d 40 f9 ldr x12,[x9, #0x18] Verify2 + 7100057200 7f 01 0c eb cmp x11,x12 + 7100057204 6b 81 8c 9a csel x11,x11,x12,hi + 7100057208 6b 4e 01 f9 str x11,[x19, #0x298] + 710005720c 2b c9 40 f9 ldr x11,[x9, #0x190] + 7100057210 6b 52 01 f9 str x11,[x19, #0x2a0] + 7100057214 8b 00 00 b4 cbz x11,loc_7100057224 + 7100057218 f4 4f 41 a9 ldp x20,x19,[sp, #local_10] + 710005721c fd 7b c2 a8 ldp x29=>local_20,x30,[sp], #0x20 + 7100057220 c0 03 5f d6 ret + loc_7100057224 XREF[1]: 7100057214(j) + 7100057224 08 15 0a 8b add x8,x8,x10, LSL #0x5 Target + 7100057228 2a 55 81 b9 ldrsw x10,[x9, #0x154] Anchor + 710005722c 68 52 01 f9 str x8,[x19, #0x2a0] Verify1 + + */ + + Result CpuLutDvcoRateCfg(u32* ptr) { + /* Anchor via scanning to prevent ordering issues */ + u32 *sub = _asm::ScanAssembly(ptr + 1, 4, + _asm::Encode(_asm::op::SubShifted64, {_asm::field::Rd, 0}, {_asm::field::Rn, 0}, {_asm::field::Rm, 0}, + {_asm::field::Shift, 0}, {_asm::field::Imm6, 3}), + _asm::field::Rd, _asm::field::Rn, _asm::field::Rm); + R_UNLESS(sub != nullptr, ldr::ResultInvalidDvcoRateConfig()); + + /* Verify */ + R_UNLESS(_asm::IsOp(sub[1], _asm::op::StrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) + && _asm::Get(sub[1], _asm::field::Off8) == 0x298, ldr::ResultInvalidDvcoRateConfig()); + + /* 22.x uses pre-index ldr x?,[x?,#0x18]! here (0xF8418D6D class). */ + const bool isLdr18 = _asm::IsOp(sub[2], _asm::op::LdrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) + && _asm::Get(sub[2], _asm::field::Off8) == 0x18; + const bool isLdrPre18 = _asm::IsOp(sub[2], _asm::op::LdrPreImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Imm9) + && _asm::Get(sub[2], _asm::field::Imm9) == 0x18; + R_UNLESS(isLdr18 || isLdrPre18, ldr::ResultInvalidDvcoRateConfig()); + + /* Patch the target */ + PATCH_OFFSET(sub, _asm::Encode(_asm::op::SubShifted64, + {_asm::field::Rd, _asm::Get(*sub, _asm::field::Rd)}, + {_asm::field::Rn, _asm::Get(*sub, _asm::field::Rn)}, + {_asm::field::Rm, _asm::Get(*sub, _asm::field::Rm)}, + {_asm::field::Shift, 0}, {_asm::field::Imm6, 4})); + + /* Search for the second anchor */ + u32* ldrsw = _asm::ScanAssembly(sub, 20, + _asm::Encode(_asm::op::LdrsWImm64, {_asm::field::Rt, 0}, {_asm::field::Rn, 0}, {_asm::field::Off4, 0x154}), + _asm::field::Rt, _asm::field::Rn); + + R_UNLESS(ldrsw != nullptr, ldr::ResultInvalidDvcoRateConfig()); + + /* Validate the str #0x2a0 */ + auto isCorrectStr = [](u32 w) { + return _asm::IsOp(w, _asm::op::StrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) + && _asm::Get(w, _asm::field::Off8) == 0x2a0; + }; + R_UNLESS(isCorrectStr(ldrsw[1]) || isCorrectStr(ldrsw[2]), ldr::ResultInvalidDvcoRateConfig()); + + /* Validate the target in both directuins */ + /* Due to codegen difference */ + u32 *add = nullptr; + if (_asm::IsOp(ldrsw[-1], _asm::op::AddShifted64, _asm::field::Rd, _asm::field::Rn, _asm::field::Rm, _asm::field::Shift, _asm::field::Imm6) + && _asm::Get(ldrsw[-1], _asm::field::Shift) == 0 + && _asm::Get(ldrsw[-1], _asm::field::Imm6) == 5) { + add = ldrsw - 1; + } + else if (_asm::IsOp(ldrsw[1], _asm::op::AddShifted64, _asm::field::Rd, _asm::field::Rn, _asm::field::Rm, _asm::field::Shift, _asm::field::Imm6) + && _asm::Get(ldrsw[1], _asm::field::Shift) == 0 + && _asm::Get(ldrsw[1], _asm::field::Imm6) == 5) { + add = ldrsw + 1; + } + + R_UNLESS(add != nullptr, ldr::ResultInvalidDvcoRateConfig()); + + /* Patch the second target */ + PATCH_OFFSET(add, _asm::Encode(_asm::op::AddShifted64, + {_asm::field::Rd, _asm::Get(*add, _asm::field::Rd)}, + {_asm::field::Rn, _asm::Get(*add, _asm::field::Rn)}, + {_asm::field::Rm, _asm::Get(*add, _asm::field::Rm)}, + {_asm::field::Shift, 0}, {_asm::field::Imm6, 6})); + + R_SUCCEED(); + } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp index a7b5bb4c..bbd5b7c8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp @@ -88,7 +88,8 @@ namespace ams::ldr::hoc::pcv::mariko { Result CpuVoltDfll(u32 *ptr); Result CpuLutMaxAsm(u32* ptr); Result CpuLutMaxAsm2(u32* ptr); - + Result CpuLutDvcoRateCfg(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); @@ -100,4 +101,10 @@ namespace ams::ldr::hoc::pcv::mariko { && _asm::Get(*ptr, _asm::field::Imm16) == 0xC0FF && _asm::Get(*ptr, _asm::field::Hw) == 0; } + + ALWAYS_INLINE bool CpuLutDvcoRateCfgPatternFn(u32* ptr) { + /* Again, cleaner to ensure we are on the correct instruction here */ + return _asm::IsOp(*ptr, _asm::op::LdrImm64, _asm::field::Rt, _asm::field::Rn, _asm::field::Off8) + && _asm::Get(*ptr, _asm::field::Off8) == 0x248; + } } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp index 86c5f044..5f9171ee 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -78,6 +78,7 @@ 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 Imm9 {12, 9}; /* pre/post-index offset */ constexpr Descriptor Barrier { 8, 4}; /* DSB/DMB option (CRm) */ constexpr Descriptor Off8 {10, 12, 3}; /* 64-bit LDR/STR */ @@ -117,6 +118,7 @@ namespace ams::ldr::hoc::pcv::_asm { constexpr u32 MovnW = 0x12800000; constexpr u32 AddShifted64 = 0x8B000000; + constexpr u32 SubShifted64 = 0xCB000000; constexpr u32 AndImm32 = 0x12000000; constexpr u32 AndImm64 = 0x92400000; constexpr u32 Ubfm32 = 0x53000000; /* UBFIZ/UBFX/LSL aliases */ @@ -130,8 +132,10 @@ namespace ams::ldr::hoc::pcv::_asm { constexpr u32 LdrbImm = 0x39400000; constexpr u32 StrbImm = 0x39000000; constexpr u32 LdrImm32 = 0xB9400000; + constexpr u32 LdrsWImm64 = 0xB9800000; /* LDRSW x, 32-bit mem, Off4-scaled */ constexpr u32 StrImm32 = 0xB9000000; constexpr u32 LdrImm64 = 0xF9400000; + constexpr u32 LdrPreImm64 = 0xF8400C00; /* LDR (pre-index): bits 11:10 fixed */ constexpr u32 StrImm64 = 0xF9000000; constexpr u32 StpImm64 = 0xA9000000; diff --git a/Source/hoc-clk/sysmodule/src/board/board_volt.cpp b/Source/hoc-clk/sysmodule/src/board/board_volt.cpp index c0502b24..36f4aadc 100644 --- a/Source/hoc-clk/sysmodule/src/board/board_volt.cpp +++ b/Source/hoc-clk/sysmodule/src/board/board_volt.cpp @@ -659,7 +659,7 @@ namespace board { u32 hz = board::GetHz(HocClkModule_CPU); /* Voltage needs to be above the forced minimum voltage, so we set a high frequency (will be capped by pcv) for a short time. */ /* This frequency is only applied for ~255ms. */ - constexpr u32 HighFreqVoltBump = 2091'000'000; + constexpr u32 HighFreqVoltBump = 1963'000'000; board::SetHz(HocClkModule_CPU, HighFreqVoltBump); svcSleepThread(5'000'000); board::SetHz(HocClkModule_CPU, hz); @@ -737,6 +737,8 @@ namespace board { } void PadLutTail(u32 *lut, u32 validCount) { + if (validCount == 0) + return; for (u32 i = validCount; i < LutSize; ++i) { lut[i] = lut[validCount - 1]; } @@ -744,13 +746,21 @@ namespace board { } void InitializeCpuLut(bool kip) { - InitializeLutPtr(); - CacheCpuLut(); - cpuVoltData.outputCfg = *reinterpret_cast(cldvfs + CL_DVFS_OUTPUT_CFG_0); if(kip) { LutSize = 63; /* Account for patched LUT by KIP */ } + + InitializeLutPtr(); + + CacheCpuLut(); + + cpuVoltData.outputCfg = *reinterpret_cast(cldvfs + CL_DVFS_OUTPUT_CFG_0); + cpuVoltData.initialized = true; + + for(u32 i = 0; i < LutSize; i++) { + file::utils::LogLine("[dvfs] CPU Real LUT %d: %dmV", i, GetLutVolt(*(u32*)(cldvfs + CL_DVFS_LUT_TABLE_0 + (i * sizeof(u32))))); + } } /* TODO: Verify table on consoles */ @@ -804,7 +814,7 @@ namespace board { return; } - u32 tmpLut[LutSize]; + u32 tmpLut[MaxPotentialLutSize]; std::memcpy(tmpLut, cpuVoltData.table, sizeof(tmpLut)); const u32 minCode = std::min(GetLutCode(vmin), tmpLut[LutSize - 1]);