diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 1be77755..9f85de34 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -39,7 +39,7 @@ volatile CustomizeTable C = { .eristaEmcMaxClock1 = 1600000, .eristaEmcMaxClock2 = 1600000, -.marikoEmcMaxClock = 1866000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ +.marikoEmcMaxClock = 1633000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ .marikoEmcVddqVolt = 600000, /* Micron: 600mV, other manafacturers: 640mV */ .emcDvbShift = 0, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp index 721c1a92..ad482924 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -#include "../oc_common.hpp" +#include #include "../mtc_timing_value.hpp" namespace ams::ldr::hoc::pcv::erista { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp index 78f8e1f3..91a49bbb 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -#include "../oc_common.hpp" +#include #include "../mtc_timing_value.hpp" #include "timing_tables.hpp" @@ -59,8 +59,46 @@ namespace ams::ldr::hoc::pcv::mariko { mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); } - void CalculateTimings() { + void CalculateTimings(double tCK_avg) { GetRext(); + + tR2P = CEIL((RL * 0.426) - 2.0); + tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW; + tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg); + tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); + + rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017); + qpop = rdv - 14; + quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782); + quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg); + einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width; + einput = quse - CEIL(9.928 / tCK_avg); + u32 qrst_duration = FLOOR(8.399 - tCK_avg); + u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); + qrst = PACK_U32(qrst_duration, qrstLow); + ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); + qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); + tW2P = (CEIL(WL * 1.7303) * 2) - 5; + tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2)); + tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; + tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); + tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; + + wdv = WL; + wsv = WL - 2; + wev = 0xA + (WL - 14); + + u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); + u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); + obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); + + pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); + + tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; + + u32 tMMRI = tRCD + (tCK_avg * 3); + pdex2mrr = tMMRI + 10; + CalculateMrw2(); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp index dbf63bae..61b324de 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp @@ -18,7 +18,7 @@ namespace ams::ldr::hoc::pcv::mariko { - void CalculateTimings(); + void CalculateTimings(double tCK_avg); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp index b6a72cc6..765e66c0 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/mtc_timing_value.hpp @@ -111,8 +111,6 @@ namespace ams::ldr::hoc { const std::array tWTR_values = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }; const std::array tREFpb_values = { 3900, 5850, 7800, 11700, 15600, 99999 }; - const double tCK_avg = 1000'000.0 / C.marikoEmcMaxClock; - const u32 tRCD = tRCD_values[C.t1_tRCD]; const u32 tRPpb = tRP_values[C.t2_tRP]; const u32 tRAS = tRAS_values[C.t3_tRAS]; @@ -128,43 +126,39 @@ namespace ams::ldr::hoc { const u32 tFAW = static_cast(tRRD * 4.0); const double tRPab = tRPpb + 3; - const u32 tR2P = CEIL((RL * 0.426) - 2.0); - const u32 tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (C.t6_tRTW * 3) + finetRTW; - const u32 tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg); - const u32 tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); + inline u32 tR2P; + inline u32 tR2W; + inline u32 tRTM; + inline u32 tRATM; inline u32 rext; - const u32 rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017); - const u32 qpop = rdv - 14; - const u32 quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782); - const u32 quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg); - const u32 einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width; - const u32 einput = quse - CEIL(9.928 / tCK_avg); - const u32 qrst_duration = FLOOR(8.399 - tCK_avg); - const u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); - const u32 qrst = PACK_U32(qrst_duration, qrstLow); - const u32 ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); - const u32 qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); - const u32 tW2P = (CEIL(WL * 1.7303) * 2) - 5; - const u32 tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2)); - const u32 tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; - const u32 tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); - const u32 tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; + inline u32 rdv; + inline u32 qpop; + inline u32 quse_width; + inline u32 quse; + inline u32 einput_duration; + inline u32 einput; + inline u32 qrst; + inline u32 ibdly; + inline u32 qsafe; - const u32 wdv = WL; - const u32 wsv = WL - 2; - const u32 wev = 0xA + (WL - 14); + inline u32 tW2P; + inline u32 tWTPDEN; + inline u32 tW2R; + inline u32 tWTM; + inline u32 tWATM; - const u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); - const u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); - const u32 obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); + inline u32 wdv; + inline u32 wsv; + inline u32 wev; - const u32 pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); + inline u32 obdly; - const u32 tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; + inline u32 pdex2rw; - const double tMMRI = tRCD + (tCK_avg * 3); - const double pdex2mrr = tMMRI + 10; /* Do this properly? */ + inline u32 tCLKSTOP; + + inline u32 pdex2mrr; inline u8 mrw2; } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 2e900f72..2205c4a3 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -51,6 +51,7 @@ namespace ams::ldr { R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013); R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014); R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015); + R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016); } namespace ams::ldr::hoc { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index 3b29b705..0b9ff6c8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -178,7 +178,7 @@ namespace ams::ldr::hoc::pcv { if (isMariko) { mariko::Patch(mapped_nso, nso_size); } else { - erista::Patch(mapped_nso, nso_size); + mariko::Patch(mapped_nso, nso_size); } #endif 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 bf6d0b1d..ac96bb19 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -20,10 +20,12 @@ #pragma once -#include "../oc_common.hpp" +#include namespace ams::ldr::hoc::pcv { + constexpr u32 NopIns = 0x1f2003d5; + inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) { return ((ins1 ^ ins2) >> 5) == 0; }; @@ -44,4 +46,19 @@ namespace ams::ldr::hoc::pcv { return static_cast((ins >> 5) & 0xFFFF); }; + inline auto AsmCompareBrNoRd = [](u32 ins1, u32 ins2) { + constexpr u32 RegMask = ~(((1 << 5) - 1) << 5); + return ((ins1 & RegMask) ^ (ins2 & RegMask)) == 0; + }; + + inline auto AsmCompareAddNoImm12 = [](u32 ins1, u32 ins2) { + constexpr u32 Imm12Mask = ~(((1 << 12) - 1) << 10); + return ((ins1 & Imm12Mask) ^ (ins2 & Imm12Mask)) == 0; + }; + + inline auto AsmCompareAdrpNoImm = [](u32 ins1, u32 ins2) { + constexpr u32 ImmMask = ~((((1 << 2) - 1) << 29) | (((1 << 19) - 1) << 5)); + return ((ins1 & ImmMask) ^ (ins2 & ImmMask)) == 0; + }; + } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp index 35dd3326..c9b180ac 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp @@ -113,7 +113,38 @@ namespace ams::ldr::hoc::pcv::erista { constexpr u32 EmcClkMinFreq = 40800; /* 40.8 MHz table only exists on erista. */ constexpr u32 EmcClkPllmLimit = 1866'000'000; - constexpr u32 MTC_TABLE_REV = 7; + constexpr u32 MTC_TABLE_REV = 7; + constexpr u32 MtcTableCountDefault = 10; + + constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault; + constexpr u32 MtcFullTableCount = 3; + + /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */ + enum DramId { + ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0, + ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1, + ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2, /* This doesn't have a table in pcv? Wtf? */ + ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4, + ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7, /* No table, but expected */ + }; + + enum MtcTableIndex { + T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH */ + T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */ + T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */ + MtcTableIndex_Invalid = 3, + }; + + struct MtcDramIndex { + DramId dramId; + MtcTableIndex index; + }; + + constexpr MtcDramIndex mtcIndexTable[] = { + { ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, }, + { ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, }, + { ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, }, + }; void Patch(uintptr_t mapped_nso, size_t nso_size); 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 6e9313f5..c55b545a 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp @@ -18,6 +18,7 @@ * along with this program. If not, see . */ +#include #include "pcv.hpp" #include "../mtc_timing_value.hpp" #include "../mariko/calculate_timings.hpp" @@ -53,7 +54,7 @@ namespace ams::ldr::hoc::pcv::mariko { R_SKIP(); } - PATCH_OFFSET(ptr + 0, C.marikoGpuVmin); + PATCH_OFFSET(ptr, C.marikoGpuVmin); PATCH_OFFSET(ptr + 3, C.marikoGpuVmin); PATCH_OFFSET(ptr + 6, C.marikoGpuVmin); PATCH_OFFSET(ptr + 9, C.marikoGpuVmin); @@ -349,6 +350,8 @@ namespace ams::ldr::hoc::pcv::mariko { TABLE->shadow_regs_ca_train.PARAM = VALUE; \ TABLE->shadow_regs_rdwr_train.PARAM = VALUE; + const double tCK_avg = 1000'000.0 / table->rate_khz; + #define GET_CYCLE_CEIL(PARAM) u32(CEIL(double(PARAM) / tCK_avg)) /* Ram power down */ @@ -372,7 +375,7 @@ namespace ams::ldr::hoc::pcv::mariko { const u32 dyn_self_ref_control = (static_cast(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000); - CalculateTimings(); + CalculateTimings(tCK_avg); WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD)); WRITE_PARAM_ALL_REG(table, emc_wr_rcd, GET_CYCLE_CEIL(tRCD)); @@ -399,7 +402,7 @@ namespace ams::ldr::hoc::pcv::mariko { WRITE_PARAM_ALL_REG(table, emc_twtm, tWTM); WRITE_PARAM_ALL_REG(table, emc_twatm, tWATM); WRITE_PARAM_ALL_REG(table, emc_rext, rext); - WRITE_PARAM_ALL_REG(table, emc_wext, (C.marikoEmcMaxClock >= 2533000) ? 0x19 : 0x16); + WRITE_PARAM_ALL_REG(table, emc_wext, (table->rate_khz >= 2533000) ? 0x19 : 0x16); WRITE_PARAM_ALL_REG(table, emc_refresh, refresh_raw); WRITE_PARAM_ALL_REG(table, emc_pre_refresh_req_cnt, refresh_raw / 4); WRITE_PARAM_ALL_REG(table, emc_trefbw, trefbw); @@ -443,7 +446,7 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr double MC_ARB_DIV = 4.0; constexpr u32 MC_ARB_SFA = 2; - table->burst_mc_regs.mc_emem_arb_cfg = C.marikoEmcMaxClock / (33.3 * 1000) / MC_ARB_DIV; + table->burst_mc_regs.mc_emem_arb_cfg = table->rate_khz / (33.3 * 1000) / MC_ARB_DIV; table->burst_mc_regs.mc_emem_arb_timing_rcd = CEIL(GET_CYCLE_CEIL(tRCD) / MC_ARB_DIV) - 2; table->burst_mc_regs.mc_emem_arb_timing_rp = CEIL(GET_CYCLE_CEIL(tRPpb) / MC_ARB_DIV) - 1; table->burst_mc_regs.mc_emem_arb_timing_rc = CEIL(GET_CYCLE_CEIL(tRC) / MC_ARB_DIV) - 1; @@ -485,7 +488,7 @@ namespace ams::ldr::hoc::pcv::mariko { table->la_scale_regs.mc_mll_mpcorer_ptsa_rate = 0x115; - if (C.marikoEmcMaxClock >= 2133000) { + if (table->rate_khz >= 2133000) { table->la_scale_regs.mc_ftop_ptsa_rate = 0x1F; } else { table->la_scale_regs.mc_ftop_ptsa_rate = 0x1B; @@ -494,14 +497,14 @@ namespace ams::ldr::hoc::pcv::mariko { table->la_scale_regs.mc_ptsa_grant_decrement = 0x17ff; constexpr u32 MaskHigh = 0xFF00FFFF; - constexpr u32 Mask2 = 0xFFFFFF00; - constexpr u32 Mask3 = 0xFF00FF00; + constexpr u32 Mask2 = 0xFFFFFF00; + constexpr u32 Mask3 = 0xFF00FF00; - const u32 allowance1 = static_cast(0x32000 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF; - const u32 allowance2 = static_cast(0x9C40 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF; - const u32 allowance3 = static_cast(0xB540 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF; - const u32 allowance4 = static_cast(0x9600 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF; - const u32 allowance5 = static_cast(0x8980 / (C.marikoEmcMaxClock / 0x3E8)) & 0xFF; + const u32 allowance1 = static_cast(0x32000 / (table->rate_khz / 0x3E8)) & 0xFF; + const u32 allowance2 = static_cast(0x9C40 / (table->rate_khz / 0x3E8)) & 0xFF; + const u32 allowance3 = static_cast(0xB540 / (table->rate_khz / 0x3E8)) & 0xFF; + const u32 allowance4 = static_cast(0x9600 / (table->rate_khz / 0x3E8)) & 0xFF; + const u32 allowance5 = static_cast(0x8980 / (table->rate_khz / 0x3E8)) & 0xFF; table->la_scale_regs.mc_latency_allowance_xusb_0 = (table->la_scale_regs.mc_latency_allowance_xusb_0 & MaskHigh) | (allowance1 << 16); table->la_scale_regs.mc_latency_allowance_xusb_1 = (table->la_scale_regs.mc_latency_allowance_xusb_1 & MaskHigh) | (allowance1 << 16); @@ -525,8 +528,8 @@ namespace ams::ldr::hoc::pcv::mariko { table->dram_timings.t_rp = tRFCpb; table->dram_timings.t_rfc = tRFCab; - table->dram_timings.rl = RL; + table->dram_timings.rl = RL; table->emc_mrw2 = (table->emc_mrw2 & ~0xFFu) | static_cast(mrw2); table->emc_cfg_2 = 0x11083D; } @@ -535,24 +538,24 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 PllOscInKHz = 38400; constexpr u32 PllOscHalfKHz = 19200; - double target_freq_d = static_cast(C.marikoEmcMaxClock); + double target_freq_d = static_cast(table->rate_khz); - s32 divm_candidate_half = static_cast(C.marikoEmcMaxClock / PllOscHalfKHz); + s32 divm_candidate_half = static_cast(table->rate_khz / PllOscHalfKHz); - bool remainder_check = (C.marikoEmcMaxClock - PllOscInKHz * (C.marikoEmcMaxClock / PllOscInKHz)) > (C.marikoEmcMaxClock - PllOscHalfKHz * divm_candidate_half) && static_cast(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0; + bool remainder_check = (table->rate_khz - PllOscInKHz * (table->rate_khz / PllOscInKHz)) > (table->rate_khz - PllOscHalfKHz * divm_candidate_half) && static_cast(((target_freq_d / PllOscHalfKHz - divm_candidate_half - 0.5) * 8192.0)) != 0; u32 divm_final = remainder_check + 1; table->pllmb_divm = divm_final; double div_step_d = static_cast(PllOscInKHz) / divm_final; - s32 divn_integer = static_cast(C.marikoEmcMaxClock / div_step_d); + s32 divn_integer = static_cast(table->rate_khz / div_step_d); table->pllmb_divn = divn_integer; u32 divn_fraction = static_cast((target_freq_d / div_step_d - divn_integer - 0.5) * 8192.0); u32 actual_freq_khz = static_cast((divn_integer + 0.5 + divn_fraction * 0.000122070312) * div_step_d); - if (C.marikoEmcMaxClock - 2366001 < 133999) { + if (table->rate_khz - 2366001 < 133999) { s32 divn_fraction_ssc = static_cast((actual_freq_khz * 0.997 / div_step_d - divn_integer - 0.5) * 8192.0); double delta_scaled = (0.3 / div_step_d + 0.3 / div_step_d) * (divn_fraction - divn_fraction_ssc); @@ -580,7 +583,7 @@ namespace ams::ldr::hoc::pcv::mariko { table->pllm_ss_cfg &= 0xBFFFFFFF; table->pllmb_ss_cfg &= 0xBFFFFFFF; - u64 pair = (static_cast(divn_fraction) << 32) | static_cast(C.marikoEmcMaxClock); + u64 pair = (static_cast(divn_fraction) << 32) | static_cast(table->rate_khz); u32 pll_misc = (table->pllm_ss_ctrl2 & 0xFFFF0000) | static_cast((pair - actual_freq_khz) >> 32); table->pllm_ss_ctrl2 = pll_misc; @@ -588,52 +591,168 @@ namespace ams::ldr::hoc::pcv::mariko { } } - Result MemFreqMtcTable(u32 *ptr) { - u32 khz_list[] = {1600000, 1331200, 204000}; - u32 khz_list_size = sizeof(khz_list) / sizeof(u32); + Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) { + Log("Rate_khz: %u, revision: %u\n", tableStart->rate_khz, tableStart->rev); + R_UNLESS(tableStart->rate_khz == expectedFreq, ldr::ResultInvalidMtcTable()); + R_UNLESS(tableStart->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); - // Generate list for mtc table pointers - MarikoMtcTable *table_list[khz_list_size]; - for (u32 i = 0; i < khz_list_size; i++) { - u8 *table = reinterpret_cast(ptr) - offsetof(MarikoMtcTable, rate_khz) - i * sizeof(MarikoMtcTable); - table_list[i] = reinterpret_cast(table); - R_UNLESS(table_list[i]->rate_khz == khz_list[i], ldr::ResultInvalidMtcTable()); - R_UNLESS(table_list[i]->rev == MTC_TABLE_REV, ldr::ResultInvalidMtcTable()); + R_SUCCEED(); + } + + Result MtcValidateAllTables(MarikoMtcTable *tableStart, const u32 *validationList, u32 tableCount) { + for (u32 i = 0; i < tableCount; ++i) { + R_UNLESS(R_SUCCEEDED(VerifyMtcTable(&tableStart[i], validationList[i])), ldr::ResultInvalidMtcTable()); } - if (C.marikoEmcMaxClock <= EmcClkOSLimit) + R_SUCCEED(); + } + + DramId GetDramId() { + u64 id64; + splGetConfig(SplConfigItem_DramId, &id64); + return static_cast(id64); + } + + MtcTableIndex GetMtcDramIndex(DramId dramId) { + for (u32 i = 0; i < std::size(mtcIndexTable); ++i) { + if (mtcIndexTable[i].dramId == dramId) { + return mtcIndexTable[i].index; + } + } + + return MtcTableIndex_Invalid; + } + + NORETURN void AbortInvalidDramId() { + // Log("Invalid dram id\n"); + // ViewLog(); + panic::SmcError(panic::Emc); + CRASH("Invalid dram id\n"); + } + + u32 GetMtcOffset(MtcTableIndex index) { + if (index < T210b0SdevEmcDvfsTableS4gb03) { + return index * mariko::MtcFullTableSize; + } + + /* There are 2 erista mtc tables between T210b0SdevEmcDvfsTableS4gb01 and T210b0SdevEmcDvfsTableS4gb03, so we have to do this adjustment. */ + return mariko::MtcFullTableSize * index + (2 * erista::MtcFullTableSize); + } + + void PrepareMtcMemoryRegion(u8 *firstTable, MarikoMtcTable *usedTable) { + memmove(firstTable, usedTable, mariko::MtcFullTableSize); + + /* Clear all other tables */ + /* 1 erista table is excluded because it's always before firstTable. */ + /* We also exclude the used table obviously. */ + constexpr size_t RemainingRegionSize = (mariko::MtcFullTableSize) * 16 + (erista::MtcFullTableSize * 2); + memset(firstTable + mariko::MtcFullTableSize, 0, RemainingRegionSize); + } + + std::vector newEmcList; + void MtcGenerateFreqTables() { + constexpr u32 OverwrittenEristaTables = erista::MtcFullTableCount - 1; /* We don't overwrite the first table */ + constexpr size_t MaxFreqSize = (OverwrittenEristaTables * erista::MtcTableCountDefault) + (mariko::MtcFullTableCount * mariko::MtcTableCountDefault); + constexpr u32 StepHz = 100000; + constexpr u32 RoundHz = 1000; + + newEmcList.clear(); + newEmcList.reserve(MaxFreqSize); + newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault)); + + if (C.marikoEmcMaxClock <= EmcClkOSLimit) { + return; + } + + for (u32 stepIndex = 1;; ++stepIndex) { + u32 newFreq = EmcClkOSLimit + (stepIndex * StepHz + 1) / 3; + newFreq = (newFreq / RoundHz) * RoundHz; + + if (newFreq > C.marikoEmcMaxClock) { + break; + } + + newEmcList.push_back(newFreq); + } + + newEmcList.resize(std::min(newEmcList.size(), MaxFreqSize)); + } + + void MtcExtendTables(MarikoMtcTable *table) { + for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) { + std::memcpy(&table[i], &table[i - 1], sizeof(MarikoMtcTable)); + table[i].rate_khz = newEmcList[i]; + } + } + + bool patchedMtc = false; + Result MemFreqMtcTable(u32 *ptr) { + if (C.marikoEmcMaxClock <= EmcClkOSLimit || patchedMtc) { R_SKIP(); + } - MarikoMtcTable *table_alt = table_list[1], *table_max = table_list[0]; - MarikoMtcTable *tmp = new MarikoMtcTable; + static const DramId dramId = [] { + DramId id = GetDramId(); + id = IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL; + //Log("Dram id: %u\n", id); + return id; + }(); - // Copy unmodified 1600000 table to tmp - std::memcpy(reinterpret_cast(tmp), reinterpret_cast(table_max), sizeof(MarikoMtcTable)); + static const MtcTableIndex mtcIndex = [] { + MtcTableIndex idx = GetMtcDramIndex(dramId); + /* If for some reason this happens, there is chance of recovering this. */ + if (idx == MtcTableIndex_Invalid) { + AbortInvalidDramId(); + } + return idx; + }(); - /* Adjust timings properly according to the new frequency. */ - MemMtcTableAutoAdjust(table_max); + /* Offset to dram id specific mtc table. */ + static const u32 mtcOffset = GetMtcOffset(mtcIndex); - MemMtcPllmbDivisor(table_max); - // Overwrite 13312000 table with unmodified 1600000 table copied back - std::memcpy(reinterpret_cast(table_alt), reinterpret_cast(tmp), sizeof(MarikoMtcTable)); + /* Offset from 1600MHz pointer to 204Mhz table start. */ + constexpr u32 StartAdjustment = offsetof(MarikoMtcTable, rate_khz) + sizeof(MarikoMtcTable) * 2; + u8 *startPtr = reinterpret_cast(ptr) - StartAdjustment; + MarikoMtcTable *table = reinterpret_cast(startPtr + mtcOffset); + R_UNLESS(R_SUCCEEDED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault)), ldr::ResultInvalidMtcTable()); - delete tmp; + table = reinterpret_cast(startPtr); - PATCH_OFFSET(ptr, C.marikoEmcMaxClock); + PrepareMtcMemoryRegion(startPtr, table); + if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) { + panic::SmcError(panic::Emc); + } + + MtcExtendTables(table); + for (u32 i = 0; i < newEmcList.size(); ++i) { + Log("freqList[%u] = %u\n", i, newEmcList[i]); + } + + if (R_FAILED(MtcValidateAllTables(table, newEmcList.data(), newEmcList.size()))) { + panic::SmcError(panic::Emc); + } + + for (u32 i = mariko::MtcTableCountDefault; i < newEmcList.size(); ++i) { + MemMtcTableAutoAdjust(&table[i]); + MemMtcPllmbDivisor(&table[i]); + } + + patchedMtc = true; R_SUCCEED(); } Result MemFreqDvbTable(u32 *ptr) { emc_dvb_dvfs_table_t *default_end = reinterpret_cast(ptr); - emc_dvb_dvfs_table_t *new_start = default_end + 1; + emc_dvb_dvfs_table_t *new_start = default_end + 1; // Validate existing table void *mem_dvb_table_head = reinterpret_cast(new_start) - sizeof(EmcDvbTableDefault); - bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0; + bool validated = std::memcmp(mem_dvb_table_head, EmcDvbTableDefault, sizeof(EmcDvbTableDefault)) == 0; R_UNLESS(validated, ldr::ResultInvalidDvbTable()); - if (C.marikoEmcMaxClock <= EmcClkOSLimit) + if (C.marikoEmcMaxClock <= EmcClkOSLimit) { R_SKIP(); + } int32_t voltAdd = 25 * C.emcDvbShift; @@ -670,8 +789,9 @@ namespace ams::ldr::hoc::pcv::mariko { } Result MemFreqMax(u32 *ptr) { - if (C.marikoEmcMaxClock <= EmcClkOSLimit) + if (C.marikoEmcMaxClock <= EmcClkOSLimit) { R_SKIP(); + } PATCH_OFFSET(ptr, C.marikoEmcMaxClock); R_SUCCEED(); @@ -685,12 +805,13 @@ namespace ams::ldr::hoc::pcv::mariko { I2cSession _session; Result res = i2cOpenSession(&_session, dev); - if (R_FAILED(res)) + if (R_FAILED(res)) { return res; + } cmd.reg = reg; cmd.val = val; - res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); i2csessionClose(&_session); return res; } @@ -702,21 +823,24 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 uv_min = 250'000; auto validator = [entry]() { - R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry()); R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); R_SUCCEED(); }; R_TRY(validator()); u32 emc_uv = C.marikoEmcVddqVolt; - if (!emc_uv) - R_SKIP(); - if (emc_uv % uv_step) + if (!emc_uv) { + R_SKIP(); + } + + if (emc_uv % uv_step) { emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding + } PATCH_OFFSET(ptr, emc_uv); @@ -731,28 +855,53 @@ namespace ams::ldr::hoc::pcv::mariko { return resultI2C; } + Result MemMtcTableAsm(u32 *ptr) { + u32 adrp = *(ptr - 1); + R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTable()); + + /* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */ + /* The correct instructions will always be x0 since the mtcTable pointer is returned. */ + + /* Pray this does not break. */ + u32 br = *(ptr - 12); + R_UNLESS(AsmCompareBrNoRd(br, MtcBrAsm), ldr::ResultInvalidMtcTable()); + + /* Pray this does not break either. */ + u32 mov = *(ptr - 10); + R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTable()); + + u8 movRd = asm_get_rd(mov); + u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), movRd); + + PATCH_OFFSET(ptr - 12, NopIns); + PATCH_OFFSET(ptr - 10, movCountPatch); + R_SUCCEED(); + } + void Patch(uintptr_t mapped_nso, size_t nso_size) { + MtcGenerateFreqTables(); u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); PatcherEntry patches[] = { - {"CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit}, - {"CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq}, - {"CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial}, - {"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial}, - {"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, 0x0000FFCF}, - {"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial}, - {"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial}, - {"GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq}, - {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn}, - {"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax}, - {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit}, - {"MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit}, - {"MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit}, - {"MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit}, - {"MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit}, - {"MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault}, - {"MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default}, + { "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit }, + { "CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq }, + { "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial }, + { "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial }, + { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low }, + { "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial }, + { "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial }, + { "GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, + { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, + { "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, + { "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, + { "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit }, + { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit }, + { "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit }, + { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit }, + { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault }, + { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default }, + { "Mem Table Asm", &MemMtcTableAsm, 0, &MemMtcGetGetTablePatternFn }, }; for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) { @@ -774,6 +923,7 @@ namespace ams::ldr::hoc::pcv::mariko { CRASH(entry.description); } } + // ViewLog(); } } 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 4c57bf1c..2d61861d 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp @@ -52,6 +52,8 @@ namespace ams::ldr::hoc::pcv::mariko { constexpr u32 CpuVoltOfficial = 1120; constexpr u32 CpuVminOfficial = 620; + constexpr u32 CpuTune0Low = 0xFFCF; + static const u32 cpuVoltagePatchValues[] = { 850, 38, 1120, 1000, 100, 1000, 0 }; static const s32 cpuVoltagePatchOffsets[] = { -2, -1, 5, 6, 7, 8, 9 }; static_assert(sizeof(cpuVoltagePatchValues) == sizeof(cpuVoltagePatchOffsets), "Invalid cpuVoltagePatch size"); @@ -117,12 +119,161 @@ namespace ams::ldr::hoc::pcv::mariko { { 1600000, { 675, 650, 637, } }, }; + constexpr u32 EmcListDefault[] = { 204000, 1331200, 1600000, }; + constexpr u32 EmcListSizeDefault = std::size(EmcListDefault); + constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1; + constexpr u32 EmcRateStep = 33'000; + constexpr u32 EmcRateStepScale = 33'200; + constexpr u32 EmcClkOSAlt = 1331'200; constexpr u32 EmcClkPllmLimit = 2133'000'000; constexpr u32 EmcVddqDefault = 600'000; constexpr u32 MemVdd2Default = 1100'000; - constexpr u32 MTC_TABLE_REV = 3; + constexpr u32 MTC_TABLE_REV = 3; + constexpr u32 MtcTableCountDefault = 3; + + constexpr size_t MtcFullTableSize = sizeof(MarikoMtcTable) * MtcTableCountDefault; + constexpr u32 MtcFullTableCount = 17; + + /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */ + enum DramId : u64 { + HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 3, + AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 5, + IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE = 6, + + IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 8, + IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 9, + IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 10, + IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE = 11, + + HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ = 12, + HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ = 13, + HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME = 14, + HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE = 15, + + IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 17, + IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 18, + HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 19, + + IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 20, + HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 21, + AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL = 22, + + HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 23, + AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL = 24, + + IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 25, + HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF = 26, + AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF = 27, + + AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL = 28, + + IOWA_4GB_HYNIX_H54G46CYRBX267 = 29, + HOAG_4GB_HYNIX_H54G46CYRBX267 = 30, + AULA_4GB_HYNIX_H54G46CYRBX267 = 31, + + IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 32, + HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB = 33, + AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB = 34, + }; + + enum MtcTableIndex { + T210b0SdevEmcDvfsTableS4gb01 = 0, /* Samsung */ + T210b0SdevEmcDvfsTableS4gb03 = 1, /* Samsung AM-MGCJ 4Gb */ // guessed + T210b0SdevEmcDvfsTableS8gb03 = 2, /* Samsung AM-MGCJ 8Gb */ // Guesed + T210b0SdevEmcDvfsTableH4gb03 = 3, /* Hynix */ + T210b0SdevEmcDvfsTableM4gb03 = 4, /* Micron */ + T210b0SdevEmcDvfsTableS4gbY01 = 5, /* Samsung */ + T210b0SdevEmcDvfsTableS1y4gbY01 = 6, /* Samsung */ + T210b0SdevEmcDvfsTableS1y8gbY01 = 7, /* Samsung */ + T210b0SdevEmcDvfsTableS1y4gbX03 = 8, /* Samsung AA-MGCL 4GB */ + T210b0SdevEmcDvfsTableS1y8gbX03 = 9, /* Samsung AA-MGCL 8GB */ + T210b0SdevEmcDvfsTableS1y4gb01 = 10, /* Samsung */ + T210b0SdevEmcDvfsTableM1y4gb01 = 11, /* Micron */ + T210b0SdevEmcDvfsTableH1y4gb01 = 12, /* Hynix */ + T210b0SdevEmcDvfsTableS1y8gb04 = 13, /* Samsung */ + T210b0SdevEmcDvfsTableS1z4gb01 = 14, /* Samsung */ + T210b0SdevEmcDvfsTableH1a4gb01 = 15, /* Hynix */ + T210b0SdevEmcDvfsTableM1a4gb01 = 16, /* Micron */ + MtcTableIndex_Invalid = 17, + }; + + struct MtcDramIndex { + DramId dramId; + MtcTableIndex index; + }; + + constexpr MtcDramIndex mtcIndexTable[] = { + { HOAG_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), }, + { AULA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), }, + { IOWA_4GB_HYNIX_H9HCNNNBKMMLXR_NEE, static_cast(0), }, + { IOWA_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), }, + { IOWA_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), }, + { IOWA_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), }, + { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), }, + { HOAG_4GB_SAMSUNG_K4U6E3S4AM_MGCJ, static_cast(0), }, + { HOAG_8GB_SAMSUNG_K4UBE3D4AM_MGCJ, static_cast(0), }, + { HOAG_4GB_HYNIX_H9HCNNNBKMMLHR_NME, static_cast(0), }, + { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTE, static_cast(0), }, + { IOWA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, T210b0SdevEmcDvfsTableS1y4gbX03, }, + { IOWA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), }, + { HOAG_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), }, + { IOWA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), }, + { HOAG_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), }, + { AULA_4GB_SAMSUNG_K4U6E3S4AB_MGCL, static_cast(0), }, + { HOAG_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), }, + { AULA_4GB_SAMSUNG_K4U6E3S4AA_MGCL, static_cast(0), }, + { IOWA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), }, + { HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), }, + { AULA_4GB_MICRON_MT53E512M32D2NP_046_WTF, static_cast(0), }, + { AULA_8GB_SAMSUNG_K4UBE3D4AA_MGCL, static_cast(0), }, + { IOWA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), }, + { HOAG_4GB_HYNIX_H54G46CYRBX267, static_cast(0), }, + { AULA_4GB_HYNIX_H54G46CYRBX267, static_cast(0), }, + { IOWA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), }, + { HOAG_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), }, + { AULA_4GB_MICRON_MT53E512M32D1NP_046_WTB, static_cast(0), }, + }; + + /* + 710006abfc 40 01 1f d6 br x10 + */ + + /* + 710006ac28 a0 03 00 90 adrp x0,0x71000de000 + 710006ac2c 00 80 16 91 add x0=>SdevEmcDvfsTableS4gb01,x0,#0x5a0 + */ + + /* Br */ + /* + | Z | OP | Fixed | A | M | RN | RM + 31 30 29 28 27 26 25 | 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 |11 | 10 | 9 8 7 6 5 | 4 3 2 1 0 + 1 1 0 1 0 1 1 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 Rn 0 0 0 0 0 + Z op A M Rm + */ + + /* Adrp */ + /* + OP | ImmLow | ImmHigh | RD + 31 | 30 29 28 27 26 25 24 | 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 | 4 3 2 1 0 + */ + + /* ADD (immediate) */ + /* + SF | OP | S | Fixed value | Sh | Imm12 | RN | RD + 31 | 30 | 29 | 28 27 26 25 24 23 | 22 | 21 20 19 18 17 16 15 14 13 12 11 10 | 9 8 7 6 5 | 4 3 2 1 0 + */ + + constexpr u32 MtcBrAsm = 0xD61F0140; + constexpr u32 MtcMovAsm = 0x52800068; + constexpr u32 MtcAdrpAsm = 0x900003A0; + constexpr u32 MtcAddAsm = 0x91168000; + + ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) { + /* This builds an address that gets returned, so the register must be x0 by convention. */ + return AsmCompareAddNoImm12(*ptr, MtcAddAsm); + } void Patch(uintptr_t mapped_nso, size_t nso_size);