From 51f935c2520d239d00bef499e753752bef8fecbe Mon Sep 17 00:00:00 2001
From: Lightos1 <124387232+Lightos1@users.noreply.github.com>
Date: Thu, 16 Apr 2026 20:51:26 +0200
Subject: [PATCH] initial mrf implementation with some bugs to fix
---
.../loader/source/oc/customize.cpp | 2 +-
.../oc/erista/calculate_timings_erista.cpp | 2 +-
.../source/oc/mariko/calculate_timings.cpp | 42 ++-
.../source/oc/mariko/calculate_timings.hpp | 2 +-
.../loader/source/oc/mtc_timing_value.hpp | 58 ++--
.../loader/source/oc/oc_common.hpp | 1 +
.../stratosphere/loader/source/oc/pcv/pcv.cpp | 2 +-
.../loader/source/oc/pcv/pcv_asm.hpp | 19 +-
.../loader/source/oc/pcv/pcv_erista.hpp | 33 +-
.../loader/source/oc/pcv/pcv_mariko.cpp | 290 +++++++++++++-----
.../loader/source/oc/pcv/pcv_mariko.hpp | 153 ++++++++-
11 files changed, 493 insertions(+), 111 deletions(-)
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);