diff --git a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp index 7ecbf857..3ec6ac0d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp @@ -798,6 +798,11 @@ namespace ams::ldr { } Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { + /* Append extra .bss for 64LUT */ + if (g_is_pcv && ctx.main_nso_idx >= 0) { + g_nso_headers[ctx.main_nso_idx].bss_size += static_cast(hoc::HocPcvScratchSize); + } + /* Get CreateProcessParameter. */ svc::CreateProcessParameter param; R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 23523a89..ee7eba3f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -38,11 +38,11 @@ volatile CustomizeTable C = { .commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */ .eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */ -/* Available: 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */ +/* Available: 33MHz step rate, 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */ /* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */ -.stepMode = StepMode_66MHz, +.stepMode = StepMode_33MHz, -.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ +.marikoEmcMaxClock = 2666000, /* Requires the EMC DVFS 63-entry patches (EMC DVFS Count / EMC SoC LUT). */ .marikoEmcVddqVolt = 600000, .emcDvbShift = 0, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index d81ac89c..a898e20b 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -20,8 +20,8 @@ #pragma once -#define CUST_REV 6 -#define KIP_VERSION 250 +#define CUST_REV 7 +#define KIP_VERSION 300 #include "oc_common.hpp" #include "pcv/pcv_common.hpp" @@ -40,6 +40,7 @@ enum StepMode: u32 { StepMode_100MHz = 1, StepMode_Jedec = 2, StepMode_133MHz = 3, + StepMode_33MHz = 4, }; enum ReadLatency: u32 { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 54fe7b7b..8254785d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -50,9 +50,15 @@ namespace ams::ldr { 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); } namespace ams::ldr::hoc { + /* Extra pcv .bss for 64LUT */ + constexpr size_t HocPcvScratchSize = 0x1000; + template struct PatcherEntry { using patternFn = bool(*)(Pointer *ptr); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index a1a94f94..6f514aeb 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -27,8 +27,8 @@ namespace ams::ldr::hoc::pcv { R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry()); // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; entry->vco_max = max_clk; R_SUCCEED(); } @@ -41,14 +41,14 @@ namespace ams::ldr::hoc::pcv { }; constexpr u32 uv_step = 12'500; - constexpr u32 uv_min = 600'000; + constexpr u32 uv_min = 600'000; - auto validator = [](regulator* entry) { - R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); + auto validator = [](regulator *entry) { + R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_1.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); R_SUCCEED(); }; @@ -67,7 +67,7 @@ namespace ams::ldr::hoc::pcv { } if (emc_uv % uv_step) { - emc_uv = emc_uv / uv_step * uv_step; // rounding + emc_uv = emc_uv / uv_step * uv_step; // rounding } PATCH_OFFSET(ptr, emc_uv); @@ -102,11 +102,11 @@ namespace ams::ldr::hoc::pcv { u32 eristaCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); u32 marikoCpuDvfsMaxFreq; - if (C.marikoCpuUVHigh) { - marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); - } else { - marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); - } + if (C.marikoCpuUVHigh) { + marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); + } else { + marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); + } u32 eristaGpuDvfsMaxFreq; switch (C.eristaGpuUV) { case 0: @@ -146,20 +146,80 @@ namespace ams::ldr::hoc::pcv { } sValidator validators[] = { - { C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true }, - { C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true }, - { C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, }, - { C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, }, - { eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, }, - { marikoCpuDvfsMaxFreq, 1785'000, 2703'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, 1536'000, panic::Gpu, }, - { C.marikoGpuVmax, 800, 960, panic::Gpu, }, + { C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true }, + { C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true }, + { + C.eristaCpuMaxVolt, + 1000, + 1260, + panic::Cpu, + }, + { + C.marikoCpuMaxVolt, + 1000, + 1200, + panic::Cpu, + }, + { + eristaCpuDvfsMaxFreq, + 1785'000, + 2397'000, + panic::Cpu, + }, + { + marikoCpuDvfsMaxFreq, + 1785'000, + 2703'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, + 1536'000, + panic::Gpu, + }, + { + C.marikoGpuVmax, + 800, + 960, + panic::Gpu, + }, }; for (auto &v : validators) { @@ -171,7 +231,7 @@ namespace ams::ldr::hoc::pcv { } void WriteKipLoadToIram() { - const u32 hocMagic = 0x686F634D; + const u32 hocMagic = 0x686F634D; constexpr uintptr_t LoadMagicAddress = 0x4003DC00; /* Should be a pretty safe address. */ R_DISCARD(SmcCopyToIram(LoadMagicAddress, &hocMagic, sizeof(hocMagic))); } @@ -189,4 +249,4 @@ namespace ams::ldr::hoc::pcv { WriteKipLoadToIram(); } -} +} // namespace ams::ldr::hoc::pcv diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 4afaedbf..1e5141ee 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -36,8 +36,7 @@ namespace ams::ldr::hoc::pcv { return true; }; - template - Result CpuFreqCvbTable(u32 *ptr) { + template Result CpuFreqCvbTable(u32 *ptr) { cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::CpuCvbTableDefault) : (cvb_entry_t *)(&erista::CpuCvbTableDefault); cvb_entry_t *customize_table = nullptr; @@ -112,8 +111,7 @@ namespace ams::ldr::hoc::pcv { PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0); } - template - Result GpuFreqCvbTable(u32 *ptr) { + template Result GpuFreqCvbTable(u32 *ptr) { cvb_entry_t *default_table = isMariko ? (cvb_entry_t *)(&mariko::GpuCvbTableDefault) : (cvb_entry_t *)(&erista::GpuCvbTableDefault); cvb_entry_t *customize_table; if (isMariko) { @@ -136,7 +134,7 @@ namespace ams::ldr::hoc::pcv { default: customize_table = const_cast(C.marikoGpuDvfsTableHiOPT); break; - } + } } else { switch (C.eristaGpuUV) { case 0: @@ -151,7 +149,7 @@ namespace ams::ldr::hoc::pcv { default: customize_table = const_cast(C.eristaGpuDvfsTable); break; - } + } } size_t default_entry_count = GetDvfsTableEntryCount(default_table); @@ -204,4 +202,4 @@ namespace ams::ldr::hoc::pcv { void SafetyCheck(); void Patch(uintptr_t mapped_nso, size_t nso_size); -} +} // namespace ams::ldr::hoc::pcv 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 30157867..b297a21d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -44,6 +44,15 @@ namespace ams::ldr::hoc::pcv { return ins & ((1 << 5) - 1); }; + /* Rn (bits 9:5) and Rm (bits 20:16) to get registers. */ + inline auto AsmGetRn = [](u32 ins) -> u32 { return (ins >> 5) & 0x1Fu; }; + inline auto AsmGetRm = [](u32 ins) -> u32 { return (ins >> 16) & 0x1Fu; }; + + /* Add (shifted register), 64-bit: sf=1 op=0 S=0 01011 shift(2) 0 Rm imm6 Rn Rd. */ + inline auto AsmIsAddShiftedReg64 = [](u32 ins) { + return (ins & 0xFF200000u) == 0x8B000000u; + }; + inline auto asm_set_rd = [](u32 ins, u8 rd) { return (ins & 0xFFFFFFE0) | (rd & 0x1F); }; @@ -94,6 +103,16 @@ namespace ams::ldr::hoc::pcv { return static_cast(static_cast(pc) + imm); }; + /* adrp Rd, target */ + inline auto AsmMakeAdrp = [](uintptr_t pc, uintptr_t target, u32 rd) -> u32 { + const s64 delta = static_cast(target & ~static_cast(0xFFF)) + - static_cast(pc & ~static_cast(0xFFF)); + const u32 imm = static_cast((delta >> 12) & 0x1FFFFF); /* 21-bit page */ + const u32 immlo = imm & 0x3; + const u32 immhi = (imm >> 2) & 0x7FFFF; + return 0x90000000u | (immlo << 29) | (immhi << 5) | (rd & 0x1Fu); + }; + inline auto AsmSetAdrTarget = [](u32 ins, uintptr_t pc, uintptr_t target) -> u32 { const s64 delta = static_cast(target) - static_cast(pc); const u32 immlo = static_cast(delta & 0x3); @@ -101,6 +120,76 @@ namespace ams::ldr::hoc::pcv { return (ins & ~((0x3u << 29) | (0x7FFFFu << 5))) | (immlo << 29) | (immhi << 5); }; + /* adrp: bit31=1, bits 28:24 = 10000. */ + inline auto AsmIsAdrp = [](u32 ins) -> bool { + return (ins & 0x9F000000u) == 0x90000000u; + }; + + inline auto AsmAdrpPageOffset = [](u32 ins) -> s64 { + s64 imm = static_cast((((ins >> 5) & 0x7FFFFu) << 2) | ((ins >> 29) & 0x3u)); + imm = (imm << 43) >> 43; /* sign-extend the 21-bit immediate */ + return imm << 12; + }; + + /* add (immediate), 64-bit: sf=1 op=0 S=0 100010 sh imm12 Rn Rd. */ + inline auto AsmIsAddImm64 = [](u32 ins) -> bool { + return (ins & 0xFF800000u) == 0x91000000u; + }; + + inline auto AsmGetImm12 = [](u32 ins) -> u32 { + return (ins >> 10) & 0xFFFu; + }; + + inline auto AsmMakeAddImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 { + return 0x91000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu); + }; + + /* movz Wd,#imm16 (no shift). */ + inline auto AsmMakeMovzW = [](u32 rd, u16 imm16) -> u32 { + return 0x52800000u | (static_cast(imm16) << 5) | (rd & 0x1Fu); + }; + + /* mov Xd,Xm == orr Xd,XZR,Xm. */ + inline auto AsmMakeMovReg = [](u32 rd, u32 rm) -> u32 { + return 0xAA0003E0u | ((rm & 0x1Fu) << 16) | (rd & 0x1Fu); + }; + + /* ldr Xt,[Xn,#byteOff] */ + inline auto AsmMakeLdrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0xF9400000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* mov Xd,X == orr Xd,XZR,X (matches any Rd). */ + inline auto AsmIsMovReg = [](u32 ins, u32 rm) -> bool { + return (ins & 0xFFFFFFE0u) == (0xAA0003E0u | ((rm & 0x1Fu) << 16)); + }; + + /* add Xd,sp,#imm12 (shift 0). */ + inline auto AsmIsAddSpImm = [](u32 ins) -> bool { + return (ins & 0xFFC003E0u) == 0x910003E0u; + }; + + /* sub Xd,x29,#imm12 (shift 0). */ + inline auto AsmIsSubX29Imm = [](u32 ins) -> bool { + return (ins & 0xFFC003E0u) == 0xD10003A0u; + }; + + inline auto AsmIsB = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x14000000u; }; /* b */ + inline auto AsmIsBl = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x94000000u; }; /* bl */ + inline auto AsmIsBCond = [](u32 ins) -> bool { return (ins & 0xFF000010u) == 0x54000000u; }; /* b.c */ + + /* Byte target address of a b/bl at pc. */ + inline auto AsmBranchTarget = [](u32 ins, uintptr_t pc) -> uintptr_t { + s64 off = static_cast((ins & 0x03FFFFFFu) << 2); + off = (off << 36) >> 36; /* sign-extend the 28-bit branch offset */ + return static_cast(static_cast(pc) + off); + }; + + /* Rewrite a scaled immediate-offset load/store (`op Wt,[Xn,#imm]`) into its register-offset form */ + inline auto AsmSetLdStRegOffset = [](u32 ldstImm, u32 rm) -> u32 { + return (ldstImm & 0xC0C003FFu) | 0x38207800u | ((rm & 0x1Fu) << 16); + }; + inline auto AsmIsLdpX = [](u32 ins) { return (ins & 0xFE400000u) == 0xA8400000u; }; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp index 87eafaa3..26e3aa98 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp @@ -173,6 +173,16 @@ namespace ams::ldr::hoc::pcv { constexpr size_t DvfsTableEntryCount = 32; constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1; + // EMC-only limit. + constexpr size_t EmcDvfsTableEntryCount = 64; + constexpr size_t EmcDvfsTableEntryLimit = EmcDvfsTableEntryCount - 1; + + // The pcv SoC-voltage DVB table is a fixed 32-entry region. (it doesn't need to be larger) + constexpr size_t DvbTableCapacity = 32; + + // extra .bss location, 0 until Patch() runs. + inline uintptr_t g_pcv_scratch = 0; + template size_t GetDvfsTableEntryCount(T *table_head) { using NT = std::remove_const_t>; 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 9d26be3a..eb76f21f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -622,6 +622,82 @@ namespace ams::ldr::hoc::pcv::mariko { u32 *nsoStart; } + /* 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] */ + u32 *voltStore = ScanAssembly(ptr - Window, Window, EmcSocVoltStoreAsm, asm_compare_no_rd); /* str w?,[x8,#0x48] */ + u32 *readLoad = ScanAssembly(ptr - Window, Window, EmcSocReadLoadAsm, asm_compare_no_rd); /* ldr w?,[x9,#0x48] */ + R_UNLESS(freqStore && voltStore && readLoad, ldr::ResultInvalidEmcSocLut()); + + u32 *voltBase = voltStore - 2; /* `add Xb,Xsrc,Xi,LSL#2` (a cmn sits between it and store) */ + R_UNLESS(AsmIsAddShiftedReg64(*voltBase) && asm_get_rd(*voltBase) == AsmGetRn(*voltStore), + ldr::ResultInvalidEmcSocLut()); + + /* adrp Xl ; add Xl,Xl,#off ; ... ; str Xl,[rail,#0x120] */ + const u32 lutReg = asm_get_rd(ptr[0]); + const u32 railReg = AsmGetRn(ptr[0]); + R_UNLESS(AsmIsAdrp(ptr[-3]) && asm_get_rd(ptr[-3]) == lutReg, ldr::ResultInvalidEmcSocLut()); + R_UNLESS(AsmIsAddImm64(ptr[-2]) && asm_get_rd(ptr[-2]) == lutReg && AsmGetRn(ptr[-2]) == lutReg, + ldr::ResultInvalidEmcSocLut()); + + const u32 srcBase = AsmGetRn(*voltBase); /* rail ptr at +0x20 */ + const u32 wBase = asm_get_rd(*voltBase); /* base reg */ + const u32 wIdx = AsmGetRm(*voltBase); /* loop index */ + + PATCH_OFFSET(freqStore, NopIns); /* Unneeded */ + PATCH_OFFSET(voltBase, AsmMakeLdrImm64(wBase, srcBase, 0x20)); /* ldr Xb,[Xsrc,#0x20] (rail) */ + PATCH_OFFSET(voltStore - 1, AsmMakeAddImm64(wBase, wBase, 0x18)); /* add Xb,Xb,#0x18 (was cmn) */ + PATCH_OFFSET(voltStore, AsmSetLdStRegOffset(*voltStore, wIdx)); /* str Wv,[Xb,Xi,LSL#2] -> rail+0x18+i*4 */ + PATCH_OFFSET(voltStore + 1, NopIns); /* Unneeded */ + + /* rail+0x18 as the socMinLut pointer. */ + PATCH_OFFSET(ptr - 3, NopIns); + PATCH_OFFSET(ptr - 2, AsmMakeAddImm64(lutReg, railReg, 0x18));/* add Xl,rail,#0x18 */ + + /* Drop the abort branch in case of a bad read */ + for (u32 i = 1; i <= 4; ++i) { + if (AsmIsBCond(readLoad[i])) { + PATCH_OFFSET(&readLoad[i], NopIns); + break; + } + } + R_SUCCEED(); + } + + Result EmcDvfsCountLimit(u32 *ptr) { + R_UNLESS(EmcDvfsCountPatternFn(ptr), ldr::ResultInvalidEmcDvfsCount()); + + /* cmp w?,#0x21 -> cmp w?,#EmcDvfsTableEntryCount */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); + R_SUCCEED(); + } + + 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()); + + /* The csel's Rm holds the 32 cap. */ + const u32 capReg = AsmGetRm(ptr[1]); + const u32 capMov = AsmMakeMovzW(capReg, 0x20); /* movz w,#0x20 */ + + u32 *movPtr = nullptr; + for (u32 i = 1; i <= 16; ++i) { + if (*(ptr - i) == capMov) { + movPtr = ptr - i; + break; + } + } + R_UNLESS(movPtr, ldr::ResultInvalidEmcRateList()); + + /* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount). */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); /* cmp w?,#64 */ + PATCH_OFFSET(movPtr, asm_set_imm16(*movPtr, static_cast(EmcDvfsTableEntryCount))); /* movz w?,#64 */ + R_SUCCEED(); + } + void MtcGenerateJedecTable() { const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 }; constexpr u32 JedecFreqCount = std::size(jedecFreqs); @@ -638,7 +714,7 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } void MtcGenerate133StepTable() { @@ -657,12 +733,39 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); + } + + void MtcGenerate33StepTable() { + /* ~33.33MHz but rounded*/ + const u32 stepFreqs33[] = { + 1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000, + 1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000, + 2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000, + 2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000, + 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000, + 3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000, + }; + constexpr u32 StepFreqs33Size = std::size(stepFreqs33); + + for (u32 i = 0; i < StepFreqs33Size; ++i) { + if (stepFreqs33[i] <= C.marikoEmcMaxClock) { + newEmcList.push_back(stepFreqs33[i]); + } else { + break; + } + } + + if (newEmcList.back() != C.marikoEmcMaxClock) { + newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); + } + + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } void MtcGenerateFreqTables() { newEmcList.clear(); - newEmcList.reserve(DvfsTableEntryCount); + newEmcList.reserve(EmcDvfsTableEntryCount); newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault)); if (C.marikoEmcMaxClock <= EmcClkOSLimit) { @@ -671,6 +774,9 @@ namespace ams::ldr::hoc::pcv::mariko { u32 stepRate = 0; switch (C.stepMode) { + case StepMode_33MHz: + MtcGenerate33StepTable(); + return; case StepMode_66MHz: stepRate = 66667; break; @@ -701,7 +807,7 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(newFreq); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) { @@ -905,24 +1011,23 @@ namespace ams::ldr::hoc::pcv::mariko { #undef DVB #undef DVB_OC - DvbEntry emcDvbTableOc[newEmcList.size()]; - + const size_t dvbCount = std::min(newEmcList.size(), DvbTableCapacity); + DvbEntry emcDvbTableOc[DvbTableCapacity] = {}; + u32 bracketIndex = 0; - for (u32 i = 0; i < newEmcList.size(); ++i) { - while (newEmcList[i] >= emcDvbOcTableBrackets[bracketIndex + 1].freq) { + for (size_t i = 0; i < dvbCount; ++i) { + const u32 freq = (i == dvbCount - 1) ? static_cast(newEmcList.back()) : newEmcList[i]; + while (freq >= emcDvbOcTableBrackets[bracketIndex + 1].freq) { ++bracketIndex; } - emcDvbTableOc[i].freq = newEmcList[i]; + emcDvbTableOc[i].freq = freq; std::memcpy(emcDvbTableOc[i].volt, emcDvbOcTableBrackets[bracketIndex].volt, sizeof(emcDvbTableOc[i].volt)); } - std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault)); - std::memcpy(mem_dvb_table_head, &emcDvbTableOc, sizeof(emcDvbTableOc)); - - /* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table, - reason why it's like this - */ + /* Clear the entire 32-entry region */ + std::memset(mem_dvb_table_head, 0, DvbTableCapacity * sizeof(DvbEntry)); + std::memcpy(mem_dvb_table_head, emcDvbTableOc, dvbCount * sizeof(DvbEntry)); R_SUCCEED(); } @@ -1136,9 +1241,61 @@ namespace ams::ldr::hoc::pcv::mariko { R_SUCCEED(); } + Result EmcRateSessLimit(u32 *ptr) { + u32 movzI = 0; + R_UNLESS(EmcRateSessFindClamp(ptr, nullptr, nullptr, &movzI), ldr::ResultInvalidEmcRateList()); + + /* Reject cmd11 GetDvfsTable. */ + for (u32 i = 1; i <= 24; ++i) { + const u32 w = ptr[i]; + if (AsmIsSubX29Imm(w) && AsmGetImm12(w) >= 0x20u) { /* sub x?,x29,#>=0x20 */ + R_THROW(ldr::ResultInvalidEmcRateList()); + } + } + + /* mov x,x2 */ + u32 descReg = 0xFFu; + for (u32 i = 1; i <= 24; ++i) { + if (AsmIsMovReg(ptr[i], 2)) { descReg = asm_get_rd(ptr[i]); break; } + } + R_UNLESS(descReg != 0xFFu, ldr::ResultInvalidEmcRateList()); + + /* Repoint the duplicated-imm pair */ + u32 *adds[8]; u32 addImm[8]; u32 nAdds = 0; + for (u32 i = 1; i <= 24 && nAdds < 8; ++i) { + const u32 w = ptr[i]; + if (AsmIsAddSpImm(w)) { /* add x?,sp,#imm12 (shift 0) */ + adds[nAdds] = ptr + i; + addImm[nAdds] = AsmGetImm12(w); + ++nAdds; + } + } + u32 patched = 0; + for (u32 a = 0; a < nAdds; ++a) { + bool dup = false; + for (u32 b = 0; b < nAdds; ++b) { + if (a != b && addImm[a] == addImm[b]) { dup = true; break; } + } + if (dup) { + PATCH_OFFSET(adds[a], AsmMakeLdrImm64(asm_get_rd(*adds[a]), descReg, 0)); /* ldr x?,[x] */ + ++patched; + } + } + R_UNLESS(patched == 2, ldr::ResultInvalidEmcRateList()); + + /* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount) */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); /* cmp w?,#64 */ + PATCH_OFFSET(ptr + movzI, asm_set_imm16(*(ptr + movzI), static_cast(EmcDvfsTableEntryCount))); /* movz w?,#64 */ + R_SUCCEED(); + } + void Patch(uintptr_t mapped_nso, size_t nso_size) { nsoStart = reinterpret_cast(mapped_nso); + + g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize; + MtcGenerateFreqTables(); + u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); @@ -1161,6 +1318,10 @@ namespace ams::ldr::hoc::pcv::mariko { { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault }, { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default }, { "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn }, + { "EMC DVFS Count", &EmcDvfsCountLimit, 1, &EmcDvfsCountPatternFn }, + { "EMC SoC LUT", &EmcSocLutReloc, 1, &EmcSocLutPatternFn }, + { "EMC Rate List", &EmcRateListLimit, 0, &EmcRateListPatternFn }, + { "EMC Rate Sess", &EmcRateSessLimit, 1, &EmcRateSessPatternFn }, { "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn }, { "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial }, }; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp index 65955f1a..7bfe1f91 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp @@ -299,6 +299,78 @@ namespace ams::ldr::hoc::pcv::mariko { return AsmCompareAddNoImm12(*ptr, MtcAddAsm); } + constexpr u32 EmcCountCmpAsm = 0x7100851F; /* cmp w?,#0x21 (subs wzr,w?,#0x21) */ + + /* + str ,[,#0x120] ; volt-array pointer + str w?, [,#0x154] ; num_freqs + */ + constexpr u32 EmcSocLutPtrStoreAsm = 0xF9009009; /* str x?,[x0,#0x120] (anchor) */ + 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) */ + + inline bool EmcDvfsCountPatternFn(u32 *ptr) { + /* Local context: cbz w?, ; cmp w?,#0x21 ; b.cs */ + return asm_compare_no_rd(*ptr, EmcCountCmpAsm) && AsmCompareBrConNoImm19(*(ptr + 1), 0x54000002) /* b.cs */ + && AsmCbzCompareOpcodeOnly(*(ptr - 1), 0x34000000); /* cbz */ + } + + inline bool EmcSocLutPatternFn(u32 *ptr) { + return asm_compare_no_rd(*ptr, EmcSocLutPtrStoreAsm) /* str x?,[x0,#0x120] */ + && asm_compare_no_rd(*(ptr + 1), EmcSocLutCountStoreAsm); /* str w?,[x0,#0x154] */ + } + + /* + mov w?,#0x20 ; the 32 cap + cmp w?,#0x20 + csel w?,w?,w?,lt ; w? = min(maxCount, 32) + bl TegraGetEmcDvfsFreqTable + + cmp w?,#0x20 ; (maxCount) + csel w?,,,lt ; min(maxCount, 32) + bl + */ + constexpr u32 EmcRateCapCmpAsm = 0x710082FF; /* cmp w?,#0x20 */ + constexpr u32 EmcRateCapCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt */ + + inline bool EmcRateListPatternFn(u32 *ptr) { + return AsmSubsCompareNoReg(*ptr, EmcRateCapCmpAsm) /* cmp w?,#0x20 */ + && AsmCompareCselNoReg(*(ptr + 1), EmcRateCapCselAsm) /* csel w?,w?,w?,lt */ + && (AsmGetRn(*ptr) == AsmGetRn(*(ptr + 1))) /* min(reg, 0x20) */ + && AsmBlCompareOpcodeOnly(*(ptr + 2), 0x94000000); /* bl */ + } + + constexpr u32 EmcRateSessCmpAsm = 0x710082FF; /* cmp w?,#0x20 */ + constexpr u32 EmcRateSessMovAsm = 0x52800400; /* movz w?,#0x20 */ + 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 (!AsmSubsCompareNoReg(ptr[0], EmcRateSessCmpAsm)) return false; /* cmp w,#0x20 */ + const u32 c = AsmGetRn(ptr[0]); + for (u32 i = 1; i <= 14; ++i) { + const u32 w = ptr[i]; + if (AsmCompareCselNoReg(w, EmcRateSessCselAsm) && AsmGetRn(w) == c && asm_get_rd(w) == c) { + const u32 cap = AsmGetRm(w); + for (u32 j = 1; j < i; ++j) { + if ((ptr[j] & 0xFFFFFFE0u) == EmcRateSessMovAsm && asm_get_rd(ptr[j]) == cap) { + if (out_c) *out_c = c; + if (out_cap) *out_cap = cap; + if (out_movz_i) *out_movz_i = j; + return true; + } + } + return false; + } + } + return false; + } + + inline bool EmcRateSessPatternFn(u32 *ptr) { + return EmcRateSessFindClamp(ptr, nullptr, nullptr, nullptr); + } + void Patch(uintptr_t mapped_nso, size_t nso_size); } diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf new file mode 100644 index 00000000..d0cbd027 Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf differ diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp new file mode 100644 index 00000000..146958db Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp differ diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl new file mode 100644 index 00000000..95495b80 Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl differ diff --git a/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl b/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl new file mode 100644 index 00000000..95495b80 Binary files /dev/null and b/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl differ diff --git a/Source/hoc-clk/common/include/hocclk/clock_manager.h b/Source/hoc-clk/common/include/hocclk/clock_manager.h index 7349b10a..fcc8ec2b 100644 --- a/Source/hoc-clk/common/include/hocclk/clock_manager.h +++ b/Source/hoc-clk/common/include/hocclk/clock_manager.h @@ -82,9 +82,10 @@ typedef struct { u8 custRev; u16 kipVersion; bool isKipLoaded; + bool rebootRequired; // Reserved for future use - u8 reserved[0x35A]; + u8 reserved[0x359]; } HocClkContext; typedef struct @@ -95,7 +96,7 @@ typedef struct }; } HocClkTitleProfileList; -#define HOCCLK_FREQ_LIST_MAX 48 +#define HOCCLK_FREQ_LIST_MAX 64 #define HOCCLK_GLOBAL_PROFILE_TID 0xA111111111111111 diff --git a/Source/hoc-clk/overlay/Makefile b/Source/hoc-clk/overlay/Makefile index 979948aa..1542d854 100644 --- a/Source/hoc-clk/overlay/Makefile +++ b/Source/hoc-clk/overlay/Makefile @@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk # version control constants #--------------------------------------------------------------------------------- #TARGET_VERSION := $(shell git describe --dirty --always --tags) -APP_VERSION := 2.5.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this +APP_VERSION := 3.0.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this TARGET_VERSION := $(APP_VERSION) #--------------------------------------------------------------------------------- diff --git a/Source/hoc-clk/overlay/src/main.cpp b/Source/hoc-clk/overlay/src/main.cpp index 41c411c5..1024dac5 100644 --- a/Source/hoc-clk/overlay/src/main.cpp +++ b/Source/hoc-clk/overlay/src/main.cpp @@ -96,6 +96,15 @@ class AppOverlay : public tsl::Overlay { ""); } + HocClkContext context = {}; + if (R_SUCCEEDED(hocclkIpcGetCurrentContext(&context)) && context.rebootRequired) { + return initially("Horizon OC has been updated.\n\n" + "\n" + "Please reboot your console\n\n" + "to finish applying the update.", + ""); + } + return initially(); } }; diff --git a/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp index 7aee9637..894253e8 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp @@ -132,7 +132,7 @@ void BaseGui::preDraw(tsl::gfx::Renderer *renderer) { drawDynamicUltraText(renderer, LOGO_TEXT_X, TEXT_Y, LOGO_LABEL_FONT_SIZE, STATIC_TEAL, false); - static const std::string versionStr = "Version " + getVersionString() + " \"Gaea\""; + static const std::string versionStr = "Version " + getVersionString() + " \"Athena\""; static constexpr tsl::Color versionColor(9, 9, 9, 15); static constexpr s32 vx = LOGO_TEXT_X + 15; static constexpr s32 vy = TEXT_Y + 18; diff --git a/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp b/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp index 13c34f0a..ce640050 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp @@ -228,15 +228,15 @@ std::vector ConfigInfoStrings(HocClkConfigValue val, bool isMariko, case KipConfigValue_stepMode: return { "The step that RAM clocks take.", - "Options (with examples):", + "Options:", + " - 33MHz - 3 MHz step (ex. 1600, 1633, 1666, 1700, etc.)", " - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)", " - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)", - " - 133MHz - 66 MHz step (ex. 1600, 1733, 1866, etc.)", + " - 133MHz - 133 MHz step (ex. 1600, 1733, 1866, etc.)", " - JEDEC:", " - 1600, 1866, 1996, 2133, 2400, 2666, 2933 and 3200 MHz are used", "The RAM max clock will always be available regardless of the step mode, but the intermediate frequencies will be limited by the selected step mode.", - "This setting does not affect performance and the option you choose mostly is based on your personal taste", - "33 MHz step mode is not possible due to certain limitations of Horizon OS", + "This setting does not affect performance and the option you choose mostly is based on your personal taste", "Default: 66 MHz", }; diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp index 38a8f49f..616f8e7e 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp @@ -1143,6 +1143,7 @@ class RamSubmenuGui : public MiscGui { if (IsMariko()) { std::vector stepMode = { + NamedValue("33MHz", 4), NamedValue("66MHz", 0), NamedValue("100MHz", 1), NamedValue("133MHz", 3), // Mantain compatability diff --git a/Source/hoc-clk/sysmodule/Makefile b/Source/hoc-clk/sysmodule/Makefile index dfbda2d8..03b7eb07 100644 --- a/Source/hoc-clk/sysmodule/Makefile +++ b/Source/hoc-clk/sysmodule/Makefile @@ -28,9 +28,9 @@ INCLUDES := ../common/include src/hos src/soc src/i2c src/util src/pwr src/ipc EXEFS_SRC := exefs_src LIBNAMES := minIni # major minor patch -TARGET_VERSION := 2.5.0 -KIP_VERSION := 250 -CUST_REV := 6 +TARGET_VERSION := 3.0.0 +KIP_VERSION := 300 +CUST_REV := 7 #--------------------------------------------------------------------------------- # options for code generation diff --git a/Source/hoc-clk/sysmodule/src/file/kip.cpp b/Source/hoc-clk/sysmodule/src/file/kip.cpp index 3fc0e416..4f5f675a 100644 --- a/Source/hoc-clk/sysmodule/src/file/kip.cpp +++ b/Source/hoc-clk/sysmodule/src/file/kip.cpp @@ -222,7 +222,8 @@ namespace kip { !config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) { MigrateKipData(cust_get_cust_rev(&table), cust_get_kip_version(&table)); SetKipData(); - notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console"); + clockManager::gContext.rebootRequired = true; + notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console to use Horizon OC"); return; } if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) { diff --git a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp index 6e85abdf..a7d37824 100644 --- a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp +++ b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp @@ -295,6 +295,47 @@ namespace clockManager { hz++; } + /* Since it is a pain to patch the vtables in ipc we can just hack the freqs in. You can still set them. */ + constexpr u64 EmcClkOSLimitHz = 1600000ULL * 1000; // 1600 MHz + const u64 maxHz = static_cast(config::GetConfigValue(KipConfigValue_marikoEmcMaxClock)) * 1000; + if (module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko && + kip::kipAvailable && maxHz >= EmcClkOSLimitHz && + config::GetConfigValue(KipConfigValue_stepMode) == 4 /* 33 MHz */) { + + /* Drop the clkrst entries above the OS limit */ + u32 kept = 0; + for (u32 i = 0; i < gFreqTable[module].count; ++i) { + if (static_cast(gFreqTable[module].list[i]) <= EmcClkOSLimitHz) { + gFreqTable[module].list[kept++] = gFreqTable[module].list[i]; + } + } + gFreqTable[module].count = kept; + + auto push = [&](u64 freqHz) { + if (freqHz > maxHz || gFreqTable[module].count >= HOCCLK_FREQ_LIST_MAX) { + return; + } + gFreqTable[module].list[gFreqTable[module].count++] = static_cast(freqHz); + }; + + static const u32 stepFreqs33[] = { + 1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000, + 1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000, + 2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000, + 2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000, + 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000, + 3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000, + }; + for (u32 f : stepFreqs33) { + push(static_cast(f) * 1000); + } + + if (gFreqTable[module].count == 0 || + static_cast(gFreqTable[module].list[gFreqTable[module].count - 1]) != maxHz) { + push(maxHz); + } + } + fileUtils::LogLine("[mgr] count = %u", gFreqTable[module].count); } @@ -560,7 +601,6 @@ namespace clockManager { if (module == HocClkModule_MEM && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) { ApplyGpuDvfs(targetHz); } - board::SetHz((HocClkModule)module, nearestHz); gContext.freqs[module] = nearestHz; @@ -739,6 +779,11 @@ namespace clockManager { gContext = {}; gContext.applicationId = 0; gContext.profile = HocClkProfile_Handheld; + + /* Load the KIP customize table before building the freq tables: the MEM freq-list + synthesis in RefreshFreqTableRow reads marikoEmcMaxClock / stepMode from it. */ + kip::GetKipData(); + for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) { gContext.freqs[module] = 0; gContext.realFreqs[module] = 0; @@ -757,8 +802,6 @@ namespace clockManager { gLastTempLogNs = 0; gLastCsvWriteNs = 0; - kip::GetKipData(); - board::FuseData *fuse = board::GetFuseData(); gContext.speedos[HocClkSpeedo_CPU] = fuse->cpuSpeedo; diff --git a/build.sh b/build.sh index 3eb311aa..f1c5f441 100644 --- a/build.sh +++ b/build.sh @@ -3,6 +3,7 @@ EXT=0 LDR_MAKE="nx_release" NO_EXO=0 +JOBS="" ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIST_DIR="$ROOT_DIR/dist" @@ -18,6 +19,13 @@ while [ $# -gt 0 ]; do --no-exo) NO_EXO=1 ;; + -j) + shift + JOBS="$1" + ;; + -j*) + JOBS="${1#-j}" + ;; *) echo "Unknown option: $1" exit 1 @@ -39,6 +47,8 @@ fi CORES="$(nproc --all)" echo "CORES: $CORES" +JOBS="${JOBS:-$CORES}" +echo "JOBS: $JOBS" SRC="Source/Atmosphere/stratosphere/loader/" @@ -88,7 +98,7 @@ fi echo echo "*** Compiling loader ***" cd build/atmosphere/stratosphere/loader || exit 1 -make -j$CORES "$LDR_MAKE" +make -j$JOBS "$LDR_MAKE" hactool -t kip1 "out/nintendo_nx_arm64_armv8a/$LDR_BUILD_PATH/loader.kip" --uncompress=hoc.kip cd "$ROOT_DIR" # exit cp -v build/atmosphere/stratosphere/loader/hoc.kip dist/atmosphere/kips/hoc.kip @@ -97,7 +107,7 @@ if [ "$NO_EXO" -eq 0 ]; then echo echo "*** Compiling exosphere ***" cd build/atmosphere/exosphere - make -j$CORES + make -j$JOBS cd "$ROOT_DIR" cp -v build/atmosphere/exosphere/out/nintendo_nx_arm64_armv8a/release/exosphere.bin dist/atmosphere/exosphere.bin fi @@ -131,7 +141,7 @@ if [ "$EXT" -eq 1 ]; then cd hekate/ echo echo "*** Compiling custom Hekate ***" - make -j$CORES + make -j$JOBS echo mkdir -p "$DIST_DIR/bootloader/sys/" @@ -142,7 +152,7 @@ if [ "$EXT" -eq 1 ]; then cd "$ROOT_DIR"/Source/Benchmark-Toolbox echo echo "*** Compiling Benchmark-Toolbox ***" - make -j$CORES + make -j$JOBS cp -v Benchmark-Toolbox.nro "$DIST_DIR"/switch/Benchmark-Toolbox.nro fi