diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index ee36c324..caa0e6c8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -82,8 +82,9 @@ namespace ams::ldr::hoc::pcv { { }, }; - constexpr u32 GpuClkPllMax = 1300'000'000; - constexpr u32 GpuClkPllLimit = 2'600'000; + constexpr u32 GpuOsLimit = 921'600; + constexpr u32 GpuClkPllMax = 1300'000'000; + constexpr u32 GpuClkPllLimit = 2'600'000; constexpr u32 GpuVminOfficial = 610; static const u32 gpuDVFSPattern[] = { 1050, 1000, 100, 1000, 10, }; @@ -121,6 +122,10 @@ namespace ams::ldr::hoc::pcv { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); }; + inline auto AsmGetImm16 = [](u32 ins) { + return static_cast((ins >> 5) & 0xFFFF); + }; + inline bool GpuMaxClockPatternFn(u32 *ptr32) { return asm_compare_no_rd(*ptr32, asm_pattern[0]); } @@ -181,8 +186,10 @@ namespace ams::ldr::hoc::pcv { static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 }; static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "invalid cpuVoltageThermalPattern size"); - constexpr u32 GpuClkPllLimit = 2'600'000; - constexpr u32 GpuClkPllMax = 921'600'000; + + constexpr u32 GpuOsLimit = 921'600; + constexpr u32 GpuClkPllLimit = 2'600'000; + constexpr u32 GpuClkPllMax = 921'600'000; constexpr u32 GpuVminOfficial = 810; constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 }; @@ -230,6 +237,10 @@ namespace ams::ldr::hoc::pcv { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); }; + inline auto AsmGetImm16 = [](u32 ins) { + return static_cast((ins >> 5) & 0xFFFF); + }; + inline bool GpuMaxClockPatternFn(u32 *ptr32) { return asm_compare_no_rd(*ptr32, asm_pattern[0]); }; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp index b1030793..7d60c81f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -268,13 +268,21 @@ namespace ams::ldr::hoc::pcv::mariko { Result GpuFreqMaxAsm(u32 *ptr32) { // Check if both two instructions match the pattern u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); - if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1]))) + if (!(asm_compare_no_rd(ins1, asm_pattern[0]) && asm_compare_no_rd(ins2, asm_pattern[1]))) { R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } // Both instructions should operate on the same register u8 rd = asm_get_rd(ins1); - if (rd != asm_get_rd(ins2)) + if (rd != asm_get_rd(ins2)) { R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } + + /* Verify the limit. */ + /* TODO: Make this a little bit cleaner at some point. */ + if (AsmGetImm16(ins1) != (GpuOsLimit & 0xFFFF) || AsmGetImm16(ins2) != (GpuOsLimit >> 16)) { + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } u32 max_clock; switch (C.marikoGpuUV) { @@ -291,6 +299,7 @@ namespace ams::ldr::hoc::pcv::mariko { max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; break; } + u32 asm_patch[2] = { asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd), asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)