This commit is contained in:
hanai3Bi
2023-04-01 01:49:31 +03:00
committed by hanabbi
parent 63bbde2f58
commit 3db0b9b380
210 changed files with 1286 additions and 48394 deletions

View File

@@ -3,6 +3,8 @@ TARGET_EXEC := test
BUILD_DIR := ./build
SRC_DIRS := ./
# CXX := clang++ g++-12
# Find all the C and C++ files we want to compile
# Note the single quotes around the * expressions. Make will incorrectly expand these otherwise.
SRCS := $(shell find $(SRC_DIRS) -name '*.cpp' -or -name '*.c' -or -name '*.s')

View File

@@ -18,39 +18,56 @@
namespace ams::ldr::oc {
volatile EristaMtcTable EristaMtcTablePlaceholder = { .rev = ERISTA_MTC_MAGIC, };
volatile MarikoMtcTable MarikoMtcTablePlaceholder = { .rev = MARIKO_MTC_MAGIC, };
volatile CustomizeTable C = {
/* DRAM Timing:
* AUTO_ADJ_MARIKO_SAFE_NO_ADJ_ERISTA: Auto adjust timings for Mariko LPDDR4X ≤3733 Mbps specs, 8Gb density; No timing adjustment for Erista. (Default)
* AUTO_ADJ_MARIKO_4266_NO_ADJ_ERISTA: Auto adjust timings for Mariko LPDDR4X 4266 Mbps specs, 8Gb density; No timing adjustments for Erista.
* NO_ADJ_ALL: No timing adjustment for both Erista and Mariko. Might achieve better performance on Mariko but lower maximum frequency is expected.
* AUTO_ADJ_SAFE_MARIKO_ONLY: Auto adjust timings for Mariko LPDDR4X ≤3733 Mbps specs, 8Gb density. (Default)
* AUTO_ADJ_4266_MARIKO_ONLY: Auto adjust timings for Mariko LPDDR4X 4266 Mbps specs, 8Gb density.
* NO_ADJ_ALL: No timing adjustment for both Erista and Mariko.
* CUSTOMIZED_ALL: Replace with values in customized table for both Erista and Mariko.
*/
.mtcConf = AUTO_ADJ_MARIKO_SAFE_NO_ADJ_ERISTA,
.mtcConf = AUTO_ADJ_SAFE_MARIKO_ONLY,
/* Mariko CPU:
* - Max Clock in kHz:
* Default: 1785000
* 2397000 might be unreachable for some SoCs.
*/
.marikoCpuMaxClock = 2397000,
/* - Boost Clock in kHz:
/* Common:
* - Boost Clock in kHz:
* Default: 1785000
* Boost clock will be applied when applications request higher CPU frequency for quicker loading.
* This will be set regardless of whether sys-clk is enabled.
*/
.marikoCpuBoostClock = 1785000,
/* - Max Voltage in mV:
.commonCpuBoostClock = 1785000,
/* - EMC Vddq (Erista Only) and RAM Vdd2 Voltage in uV
* Range: 1100'000 to 1250'000 uV
* Erista Default(HOS): 1125'000 (bootloader: 1100'000)
* Mariko Default: 1100'000 (It will not work without sys-clk-OC.)
* Value should be divided evenly by 12'500.
* Not enabled by default.
*/
.commonEmcMemVolt = 0,
/* Erista CPU:
* - Max Voltage in mV
* - CpuVoltL4T: 1235
*/
.eristaCpuMaxVolt = 1235,
/* Erista EMC(RAM):
* - RAM Clock in kHz
* [WARNING]
* RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM:
* - Graphical glitches
* - System instabilities
* - NAND corruption
*/
.eristaEmcMaxClock = 1862400,
/* Mariko CPU:
* - Max Voltage in mV:
* Default voltage: 1120
*/
.marikoCpuMaxVolt = 1235,
/* Mariko GPU:
* - Max Clock in kHz:
* Default: 921600
* NVIDIA Maximum: 1267200
* 1305600 might be unreachable for some SoCs.
*/
.marikoGpuMaxClock = 1305600,
/* Mariko EMC(RAM):
* - RAM Clock in kHz:
* Values should be ≥ 1600000, and divided evenly by 9600.
@@ -70,28 +87,107 @@ volatile CustomizeTable C = {
*/
.marikoEmcVddqVolt = 0,
/* Erista CPU:
* - Max Voltage in mV
/* Advanced Settings:
* - Erista CPU DVFS Table:
*/
.eristaCpuMaxVolt = 1235,
.eristaCpuDvfsTable = {
{ 204000, { 721094 }, {} },
{ 306000, { 754040 }, {} },
{ 408000, { 786986 }, {} },
{ 510000, { 819932 }, {} },
{ 612000, { 852878 }, {} },
{ 714000, { 885824 }, {} },
{ 816000, { 918770 }, {} },
{ 918000, { 951716 }, {} },
{ 1020000, { 984662 }, { -2875621, 358099, -8585 } },
{ 1122000, { 1017608 }, { -52225, 104159, -2816 } },
{ 1224000, { 1050554 }, { 1076868, 8356, -727 } },
{ 1326000, { 1083500 }, { 2208191, -84659, 1240 } },
{ 1428000, { 1116446 }, { 2519460, -105063, 1611 } },
{ 1581000, { 1130000 }, { 2889664, -122173, 1834 } },
{ 1683000, { 1168000 }, { 5100873, -279186, 4747 } },
{ 1785000, { 1227500 }, { 5100873, -279186, 4747 } },
// Appending table
{ 1887000, { 1235000 }, { 5100873, -279186, 4747 } },
{ 1963500, { 1235000 }, { 5100873, -279186, 4747 } },
{ 2091000, { 1235000 }, { 5100873, -279186, 4747 } },
},
/* Erista EMC(RAM):
* - RAM Clock in kHz
* [WARNING]
* RAM overclock could be UNSTABLE if timing parameters are not suitable for your DRAM:
* - Graphical glitches
* - System instabilities
* - NAND corruption
/* - Mariko CPU DVFS Table:
* 2397000 might not work for some SoCs.
*/
.eristaEmcMaxClock = 1862400,
/* - EMC Vddq (Erista Only) and RAM Vdd2 Voltage in uV
* Range: 1100'000 to 1250'000 uV
* Erista Default(HOS): 1125'000 (bootloader: 1100'000)
* Mariko Default: 1100'000 (It will not work without sys-clk-OC.)
* Value should be divided evenly by 12'500.
* Not enabled by default.
.marikoCpuDvfsTable = {
{ 204000, { 721589, -12695, 27 }, {} },
{ 306000, { 747134, -14195, 27 }, {} },
{ 408000, { 776324, -15705, 27 }, {} },
{ 510000, { 809160, -17205, 27 }, {} },
{ 612000, { 845641, -18715, 27 }, {} },
{ 714000, { 885768, -20215, 27 }, {} },
{ 816000, { 929540, -21725, 27 }, {} },
{ 918000, { 976958, -23225, 27 }, {} },
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
// Appending table
{ 2091000, { 1716501, -39395, 27 }, { 1120000 } },
{ 2193000, { 1775132, -40505, 27 }, { 1120000 } },
{ 2295000, { 1866287, -42005, 27 }, { 1120000 } },
{ 2397000, { 1961107, -43506, 27 }, { 1120000 } },
},
/* - Erista GPU DVFS Table:
*/
.commonEmcMemVolt = 0,
.eristaGpuDvfsTable = {
{ 76800, { }, { 814294, 8144, -940, 808, -21583, 226 } },
{ 153600, { }, { 856185, 8144, -940, 808, -21583, 226 } },
{ 230400, { }, { 898077, 8144, -940, 808, -21583, 226 } },
{ 307200, { }, { 939968, 8144, -940, 808, -21583, 226 } },
{ 384000, { }, { 981860, 8144, -940, 808, -21583, 226 } },
{ 460800, { }, { 1023751, 8144, -940, 808, -21583, 226 } },
{ 537600, { }, { 1065642, 8144, -940, 808, -21583, 226 } },
{ 614400, { }, { 1107534, 8144, -940, 808, -21583, 226 } },
{ 691200, { }, { 1149425, 8144, -940, 808, -21583, 226 } },
{ 768000, { }, { 1191317, 8144, -940, 808, -21583, 226 } },
{ 844800, { }, { 1233208, 8144, -940, 808, -21583, 226 } },
{ 921600, { }, { 1275100, 8144, -940, 808, -21583, 226 } },
// { 998400, { }, { 1316991, 8144, -940, 808, -21583, 226 } },
},
/* - Mariko GPU DVFS Table:
* 1305600 might not work for some SoCs.
*/
.marikoGpuDvfsTable = {
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93 } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } },
// Appending table
{ 1305600, {}, { 1374130, -13725, -859, 0, 4442, 576 } },
},
.eristaMtcTable = const_cast<EristaMtcTable *>(&EristaMtcTablePlaceholder),
.marikoMtcTable = const_cast<MarikoMtcTable *>(&MarikoMtcTablePlaceholder),
};
}

View File

@@ -16,35 +16,54 @@
#pragma once
#define CUST_REV 3
#define CUST_REV 4
#include "oc_common.hpp"
#include "pcv/pcv_common.hpp"
namespace ams::ldr::oc {
#include "mtc_timing_table.hpp"
enum MtcConfig {
AUTO_ADJ_MARIKO_SAFE_NO_ADJ_ERISTA = 0,
AUTO_ADJ_MARIKO_4266_NO_ADJ_ERISTA = 1,
enum MtcConfig: u32 {
AUTO_ADJ_SAFE_MARIKO_ONLY = 0,
AUTO_ADJ_4266_MARIKO_ONLY = 1,
NO_ADJ_ALL = 2,
CUSTOMIZED_ALL = 3,
};
typedef struct __attribute__((packed)) CustomizeTable {
using CustomizeCpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
using CustomizeGpuDvfsTable = pcv::cvb_entry_t[pcv::DvfsTableEntryLimit];
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(CustomizeGpuDvfsTable));
static_assert(sizeof(CustomizeCpuDvfsTable) == sizeof(pcv::cvb_entry_t) * pcv::DvfsTableEntryLimit);
constexpr uint32_t ERISTA_MTC_MAGIC = 0x43544D45; // EMTC
constexpr uint32_t MARIKO_MTC_MAGIC = 0x43544D4D; // MMTC
typedef struct CustomizeTable {
u8 cust[4] = {'C', 'U', 'S', 'T'};
u16 custRev = CUST_REV;
u16 mtcConf = AUTO_ADJ_MARIKO_SAFE_NO_ADJ_ERISTA;
u32 marikoCpuMaxClock;
u32 marikoCpuBoostClock;
u32 marikoCpuMaxVolt;
u32 marikoGpuMaxClock;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
u32 custRev = CUST_REV;
u32 mtcConf;
u32 commonCpuBoostClock;
u32 commonEmcMemVolt;
u32 eristaCpuMaxVolt;
u32 eristaEmcMaxClock;
u32 commonEmcMemVolt;
u32 marikoCpuMaxVolt;
u32 marikoEmcMaxClock;
u32 marikoEmcVddqVolt;
CustomizeCpuDvfsTable eristaCpuDvfsTable;
CustomizeCpuDvfsTable marikoCpuDvfsTable;
CustomizeGpuDvfsTable eristaGpuDvfsTable;
CustomizeGpuDvfsTable marikoGpuDvfsTable;
EristaMtcTable* eristaMtcTable;
MarikoMtcTable* marikoMtcTable;
} CustomizeTable;
static_assert(sizeof(CustomizeTable) == sizeof(u8) * 4 + sizeof(u32) * 9 + sizeof(CustomizeCpuDvfsTable) * 4 + sizeof(void*) * 2);
static_assert(sizeof(CustomizeTable) == 7000);
extern volatile CustomizeTable C;
extern volatile EristaMtcTable EristaMtcTablePlaceholder;
extern volatile MarikoMtcTable MarikoMtcTablePlaceholder;
}

View File

@@ -16,6 +16,8 @@
* from GCC preprocessor output
*/
#pragma once
struct MarikoTiming {
uint32_t emc_rc;
uint32_t emc_rfc;

View File

@@ -27,6 +27,10 @@
#include "customize.hpp"
#define PATCH_OFFSET(offset, value) \
static_assert(sizeof(__typeof__(offset)) <= sizeof(u64)); \
*(offset) = value;
namespace ams::ldr {
R_DEFINE_ERROR_RESULT(OutOfRange, 1000);
R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001);
@@ -92,7 +96,4 @@ namespace ams::ldr::oc {
R_SUCCEED();
}
};
template<typename T>
inline void PatchOffset(T* offset, T value) { static_assert(sizeof(T) < sizeof(u64)); *(offset) = value; }
}

View File

@@ -62,7 +62,53 @@ void saveExec(const char* file_loc, const void* buf, size_t size) {
fclose(fp);
}
Result Test_PcvDvfsTable() {
using namespace ams::ldr::oc::pcv;
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&mariko::CpuCvbTableDefault)) == 18);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&erista::CpuCvbTableDefault)) == 16);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&mariko::GpuCvbTableDefault)) == 17);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&erista::GpuCvbTableDefault)) == 12);
cvb_entry_t last_mariko_cpu_cvb_entry_default = { 1963500, { 1675751, -38635, 27 }, { 1120000 } };
assert(memcmp(GetDvfsTableLastEntry((cvb_entry_t *)(&mariko::CpuCvbTableDefault)), (void *)&last_mariko_cpu_cvb_entry_default, sizeof(last_mariko_cpu_cvb_entry_default)) == 0);
assert(GetDvfsTableLastEntry((cvb_entry_t *)(&erista::GpuCvbTableDefault))->freq == 921600);
// Customized table default
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.eristaCpuDvfsTable)) == 19);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoCpuDvfsTable)) == 22);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.eristaGpuDvfsTable)) == 12);
assert(GetDvfsTableEntryCount((cvb_entry_t *)(&ams::ldr::oc::C.marikoGpuDvfsTable)) == 18);
constexpr size_t limit = ams::ldr::oc::pcv::DvfsTableEntryLimit;
cvb_entry_t customized_table[limit] = {};
for (int i = 0; i < limit; i++) {
assert(GetDvfsTableEntryCount(customized_table) == i);
auto p = GetDvfsTableLastEntry(customized_table);
if (p)
assert(p->freq == i);
customized_table[i].freq = i + 1;
}
R_SUCCEED();
}
void unitTest() {
UnitTest test[] = {
{ "PCV DVFS Table", &Test_PcvDvfsTable }
};
for (auto &t : test) {
t.Test();
}
}
int main(int argc, char** argv) {
unitTest();
const char* pcv_opt = "pcv";
const char* ptm_opt = "ptm";
const char* save_opt = "-s";

View File

@@ -19,10 +19,12 @@
#ifndef ATMOSPHERE_IS_STRATOSPHERE
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
typedef uint8_t u8;
typedef uint16_t u16;
@@ -43,4 +45,34 @@ typedef int Result;
#define R_DEFINE_ERROR_RESULT(name, rc) \
inline Result Result##name() { return rc; }
#define HEXDUMP(ptr, len) \
{ \
const uint8_t* p = reinterpret_cast<const uint8_t *>(ptr); \
size_t i, j; \
for (i = 0; i < len; i += 16) { \
printf("%06zx: ", i); \
for (j = 0; j < 16 && i + j < len; j++) \
printf("%02x ", p[i + j]); \
for (; j < 16; j++) \
printf(" "); \
for (j = 0; j < 16 && i + j < len; j++) \
printf("%c", isprint(p[i + j]) ? p[i + j] : '.'); \
printf("\n"); \
} \
} \
typedef struct UnitTest {
using Func = Result(*)();
const char* description;
Func fun = nullptr;
void Test() {
Result res = fun();
if (R_FAILED(res)) {
CRASH(description);
}
}
} UnitTest;
#endif

View File

@@ -63,7 +63,7 @@ Result MemVoltHandler(u32* ptr) {
if (emc_uv % uv_step)
emc_uv = emc_uv / uv_step * uv_step; // rounding
PatchOffset(ptr, emc_uv);
PATCH_OFFSET(ptr, emc_uv);
R_SUCCEED();
}
@@ -91,16 +91,23 @@ void SafetyCheck() {
}
};
u32 eristaCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq);
u32 marikoCpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
u32 eristaGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq);
u32 marikoGpuDvfsMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq);
sValidator validators[] = {
{ C.marikoCpuMaxClock, 1785'000, 3000'000 },
{ C.marikoCpuBoostClock, 1020'000, 3000'000, true },
{ C.marikoCpuMaxVolt, 1100, 1300 },
{ C.marikoGpuMaxClock, 768'000, 1536'000 },
{ C.marikoEmcMaxClock, 1600'000, 2400'000 },
{ C.marikoEmcVddqVolt, 550'000, 650'000 },
{ C.commonCpuBoostClock, 1020'000, 3000'000, true },
{ C.commonEmcMemVolt, 1100'000, 1250'000 },
{ C.eristaCpuMaxVolt, 1100, 1300 },
{ C.eristaEmcMaxClock, 1600'000, 2400'000 },
{ C.commonEmcMemVolt, 1100'000, 1250'000 },
{ C.marikoCpuMaxVolt, 1100, 1300 },
{ C.marikoEmcMaxClock, 1600'000, 2400'000 },
{ C.marikoEmcVddqVolt, 550'000, 650'000 },
{ eristaCpuDvfsMaxFreq, 1785'000, 3000'000 },
{ marikoCpuDvfsMaxFreq, 1785'000, 3000'000 },
{ eristaGpuDvfsMaxFreq, 768'000, 1536'000 },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
};
for (auto& i : validators) {

View File

@@ -17,141 +17,240 @@
#pragma once
#include "../oc_common.hpp"
#include "pcv_common.hpp"
namespace ams::ldr::oc::pcv {
typedef struct cvb_coefficients {
s32 c0 = 0;
s32 c1 = 0;
s32 c2 = 0;
s32 c3 = 0;
s32 c4 = 0;
s32 c5 = 0;
} cvb_coefficients;
typedef struct cpu_freq_cvb_table_t {
u64 freq;
cvb_coefficients cvb_dfll_param;
cvb_coefficients cvb_pll_param;
} cpu_freq_cvb_table_t;
static_assert(sizeof(cpu_freq_cvb_table_t) == 0x38);
typedef struct gpu_cvb_pll_table_t {
u64 freq;
cvb_coefficients cvb_dfll_param;
cvb_coefficients cvb_pll_param;
} gpu_cvb_pll_table_t;
static_assert(sizeof(gpu_cvb_pll_table_t) == 0x38);
typedef struct emc_dvb_dvfs_table_t {
u64 freq;
s32 volt[4] = {0};
} emc_dvb_dvfs_table_t;
typedef struct __attribute__((packed)) div_nmp {
u8 divn_shift;
u8 divn_width;
u8 divm_shift;
u8 divm_width;
u8 divp_shift;
u8 divp_width;
u8 override_divn_shift;
u8 override_divm_shift;
u8 override_divp_shift;
} div_nmp;
typedef struct __attribute__((packed)) clk_pll_param {
u32 freq;
u32 input_min;
u32 input_max;
u32 cf_min;
u32 cf_max;
u32 vco_min;
u32 vco_max;
s32 lock_delay;
u32 fixed_rate;
u32 unk_0;
struct div_nmp *div_nmp;
u32 unk_1[4];
void (*unk_fn)(u64* unk_struct); // set_defaults?
} clk_pll_param;
typedef struct __attribute__((packed)) dvfs_rail {
u32 id;
u32 unk_0[5];
u32 freq;
u32 unk_1[8];
u32 unk_flag;
u32 min_mv;
u32 step_mv;
u32 max_mv;
u32 unk_2[11];
} dvfs_rail;
typedef struct __attribute__((packed)) regulator {
u64 id;
const char* name;
u32 type;
union {
struct {
u32 volt_reg;
u32 step_uv;
u32 min_uv;
u32 default_uv;
u32 max_uv;
u32 unk_0[2];
} type_1;
struct {
u32 unk_0;
u32 step_uv;
u32 unk_1;
u32 min_uv;
u32 max_uv;
u32 unk_2;
u32 default_uv;
} type_2_3;
namespace mariko {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPUB01_CVB_TABLE
{ 204000, { 721589, -12695, 27 }, {} },
{ 306000, { 747134, -14195, 27 }, {} },
{ 408000, { 776324, -15705, 27 }, {} },
{ 510000, { 809160, -17205, 27 }, {} },
{ 612000, { 845641, -18715, 27 }, {} },
{ 714000, { 885768, -20215, 27 }, {} },
{ 816000, { 929540, -21725, 27 }, {} },
{ 918000, { 976958, -23225, 27 }, {} },
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
{ },
};
u32 unk_x[60];
} regulator;
static_assert(sizeof(regulator) == 0x120);
constexpr u32 CpuClkOSLimit = 1785'000;
constexpr u16 CpuMinVolts[] = { 800, 637, 620, 610 };
constexpr u32 EmcClkOSLimit = 1600'000;
constexpr u32 CpuClkOfficial = 1963'500;
constexpr u32 CpuVoltOfficial = 1120;
#define R_SKIP() R_SUCCEED()
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93 } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } },
{ },
};
constexpr u32 GpuClkPllLimit = 1300'000'000;
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | 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
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | 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
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 asm_pattern[] = { 0x52820000, 0x72A001C0 };
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) { return ((ins1 ^ ins2) >> 5) == 0; };
inline auto asm_get_rd = [](u32 ins) { return ins & ((1 << 5) - 1); };
inline auto asm_set_rd = [](u32 ins, u8 rd) { return (ins & 0xFFFFFFE0) | (rd & 0x1F); };
inline auto asm_set_imm16 = [](u32 ins, u16 imm) { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); };
inline bool GpuMaxClockPatternFn(u32* ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
}
constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
{ 1065600, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
};
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;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
namespace erista {
constexpr cvb_entry_t CpuCvbTableDefault[] = {
// CPU_PLL_CVB_TABLE_ODN
{ 204000, { 721094 }, {} },
{ 306000, { 754040 }, {} },
{ 408000, { 786986 }, {} },
{ 510000, { 819932 }, {} },
{ 612000, { 852878 }, {} },
{ 714000, { 885824 }, {} },
{ 816000, { 918770 }, {} },
{ 918000, { 951716 }, {} },
{ 1020000, { 984662 }, { -2875621, 358099, -8585 } },
{ 1122000, { 1017608 }, { -52225, 104159, -2816 } },
{ 1224000, { 1050554 }, { 1076868, 8356, -727 } },
{ 1326000, { 1083500 }, { 2208191, -84659, 1240 } },
{ 1428000, { 1116446 }, { 2519460, -105063, 1611 } },
{ 1581000, { 1130000 }, { 2889664, -122173, 1834 } },
{ 1683000, { 1168000 }, { 5100873, -279186, 4747 } },
{ 1785000, { 1227500 }, { 5100873, -279186, 4747 } },
{ },
};
constexpr u32 CpuVoltL4T = 1235'000;
constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 };
inline bool CpuMaxVoltPatternFn(u32* ptr32) {
u32 val = *ptr32;
return (val == 1132 || val == 1170 || val == 1227);
}
constexpr cvb_entry_t GpuCvbTableDefault[] = {
// NA_FREQ_CVB_TABLE
{ 76800, { }, { 814294, 8144, -940, 808, -21583, 226 } },
{ 153600, { }, { 856185, 8144, -940, 808, -21583, 226 } },
{ 230400, { }, { 898077, 8144, -940, 808, -21583, 226 } },
{ 307200, { }, { 939968, 8144, -940, 808, -21583, 226 } },
{ 384000, { }, { 981860, 8144, -940, 808, -21583, 226 } },
{ 460800, { }, { 1023751, 8144, -940, 808, -21583, 226 } },
{ 537600, { }, { 1065642, 8144, -940, 808, -21583, 226 } },
{ 614400, { }, { 1107534, 8144, -940, 808, -21583, 226 } },
{ 691200, { }, { 1149425, 8144, -940, 808, -21583, 226 } },
{ 768000, { }, { 1191317, 8144, -940, 808, -21583, 226 } },
{ 844800, { }, { 1233208, 8144, -940, 808, -21583, 226 } },
{ 921600, { }, { 1275100, 8144, -940, 808, -21583, 226 } },
{ },
};
constexpr u32 MemVoltHOS = 1125'000;
constexpr u32 EmcClkPllmLimit = 1866'000'000;
constexpr u32 MTC_TABLE_REV = 7;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
template<bool isMariko>
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 = const_cast<cvb_entry_t *>(isMariko ? C.marikoCpuDvfsTable : C.eristaCpuDvfsTable);
u32 cpu_max_volt = isMariko ? C.marikoCpuMaxVolt : C.eristaCpuMaxVolt;
u32 cpu_freq_threshold = isMariko ? 1020'000 : 1785'000;
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
size_t default_table_size = default_entry_count * sizeof(cvb_entry_t);
size_t customize_entry_count = GetDvfsTableEntryCount(customize_table);
size_t customize_table_size = customize_entry_count * sizeof(cvb_entry_t);
// Validate existing table
cvb_entry_t* table_free = reinterpret_cast<cvb_entry_t *>(ptr) + 1;
void* cpu_cvb_table_head = reinterpret_cast<u8 *>(table_free) - default_table_size;
bool validated = std::memcmp(cpu_cvb_table_head, default_table, default_table_size) == 0;
R_UNLESS(validated, ldr::ResultInvalidCpuDvfs());
std::memcpy(cpu_cvb_table_head, static_cast<void *>(customize_table), customize_table_size);
// Patch CPU max volt
if (cpu_max_volt) {
cvb_entry_t* entry = static_cast<cvb_entry_t *>(cpu_cvb_table_head);
for (size_t i = 0; i < customize_entry_count; i++) {
if (entry->freq >= cpu_freq_threshold) {
PATCH_OFFSET(&(entry->cvb_pll_param.c0), cpu_max_volt * 1000);
}
entry++;
}
}
R_SUCCEED();
}
template<bool isMariko>
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 = const_cast<cvb_entry_t *>(isMariko ? C.marikoGpuDvfsTable : C.eristaGpuDvfsTable);
size_t default_entry_count = GetDvfsTableEntryCount(default_table);
size_t default_table_size = default_entry_count * sizeof(cvb_entry_t);
size_t customize_entry_count = GetDvfsTableEntryCount(customize_table);
size_t customize_table_size = customize_entry_count * sizeof(cvb_entry_t);
// Validate existing table
cvb_entry_t* table_free = reinterpret_cast<cvb_entry_t *>(ptr) + 1;
void* gpu_cvb_table_head = reinterpret_cast<u8 *>(table_free) - default_table_size;
bool validated = std::memcmp(gpu_cvb_table_head, default_table, default_table_size) == 0;
R_UNLESS(validated, ldr::ResultInvalidGpuDvfs());
std::memcpy(gpu_cvb_table_head, (void*)customize_table, customize_table_size);
R_SUCCEED();
};
Result MemFreqPllmLimit(u32* ptr);
Result MemVoltHandler(u32* ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
template<typename Table>
Result MemMtcTableClone(Table* des, Table* src) {
constexpr u32 mtc_magic = 0x5F43544D;
template<typename T>
Result MemMtcCustomizeTable(T* dst, T* src) {
constexpr u32 mtc_magic = std::is_same_v<T, MarikoMtcTable> ? MARIKO_MTC_MAGIC : ERISTA_MTC_MAGIC;
R_UNLESS(src->rev == mtc_magic, ldr::ResultInvalidMtcMagic());
constexpr u32 ZERO_VAL = UINT32_MAX;
// Skip params from dvfs_ver to clock_src;
for (size_t offset = offsetof(Table, clk_src_emc); offset < sizeof(Table); offset += sizeof(u32)) {
for (size_t offset = offsetof(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) {
u32* src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
u32* des_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(des) + offset);
u32* dst_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(dst) + offset);
u32 src_val = *src_ent;
constexpr u32 placeholder_val = UINT32_MAX;
if (src_val != placeholder_val) {
PatchOffset(des_ent, src_val);
if (src_val) {
PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val);
}
}
R_SUCCEED();
};
namespace erista {
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
namespace mariko {
void Patch(uintptr_t mapped_nso, size_t nso_size);
}
void SafetyCheck();
void Patch(uintptr_t mapped_nso, size_t nso_size);

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) Switch-OC-Suite
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace ams::ldr::oc::pcv {
typedef struct cvb_coefficients {
s32 c0 = 0;
s32 c1 = 0;
s32 c2 = 0;
s32 c3 = 0;
s32 c4 = 0;
s32 c5 = 0;
} cvb_coefficients;
typedef struct cvb_entry_t {
u64 freq;
cvb_coefficients cvb_dfll_param;
cvb_coefficients cvb_pll_param;
} cvb_entry_t;
static_assert(sizeof(cvb_entry_t) == 0x38);
typedef struct emc_dvb_dvfs_table_t {
u64 freq;
s32 volt[4] = {0};
} emc_dvb_dvfs_table_t;
typedef struct __attribute__((packed)) div_nmp {
u8 divn_shift;
u8 divn_width;
u8 divm_shift;
u8 divm_width;
u8 divp_shift;
u8 divp_width;
u8 override_divn_shift;
u8 override_divm_shift;
u8 override_divp_shift;
} div_nmp;
typedef struct __attribute__((packed)) clk_pll_param {
u32 freq;
u32 input_min;
u32 input_max;
u32 cf_min;
u32 cf_max;
u32 vco_min;
u32 vco_max;
s32 lock_delay;
u32 fixed_rate;
u32 unk_0;
struct div_nmp *div_nmp;
u32 unk_1[4];
void (*unk_fn)(u64* unk_struct); // set_defaults?
} clk_pll_param;
typedef struct __attribute__((packed)) dvfs_rail {
u32 id;
u32 unk_0[5];
u32 freq;
u32 unk_1[8];
u32 unk_flag;
u32 min_mv;
u32 step_mv;
u32 max_mv;
u32 unk_2[11];
} dvfs_rail;
typedef struct __attribute__((packed)) regulator {
u64 id;
const char* name;
u32 type;
union {
struct {
u32 volt_reg;
u32 step_uv;
u32 min_uv;
u32 default_uv;
u32 max_uv;
u32 unk_0[2];
} type_1;
struct {
u32 unk_0;
u32 step_uv;
u32 unk_1;
u32 min_uv;
u32 max_uv;
u32 unk_2;
u32 default_uv;
} type_2_3;
};
u32 unk_x[60];
} regulator;
static_assert(sizeof(regulator) == 0x120);
constexpr u32 CpuClkOSLimit = 1785'000;
constexpr u32 EmcClkOSLimit = 1600'000;
#define R_SKIP() R_SUCCEED()
// Count 32 / Index 31 is reserved to be empty
constexpr size_t DvfsTableEntryCount = 32;
constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1;
template<typename T>
size_t GetDvfsTableEntryCount(T* table_head) {
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
auto is_empty = [](NT* entry) {
uint8_t* m = reinterpret_cast<uint8_t *>(entry);
for (size_t i = 0; i < sizeof(NT); i++) {
if (*(m + i))
return false;
}
return true;
};
NT* table = const_cast<NT *>(table_head);
size_t count = 0;
while (count < DvfsTableEntryLimit) {
if (is_empty(table++)) {
return count;
}
count++;
}
return DvfsTableEntryLimit;
}
template<typename T>
T* GetDvfsTableLastEntry(T* table_head) {
using NT = std::remove_const_t<std::remove_volatile_t<T>>;
NT* table = const_cast<NT *>(table_head);
size_t count = GetDvfsTableEntryCount(table_head);
if (!count) {
return nullptr;
}
size_t index = count - 1;
return table + index;
}
}

View File

@@ -14,36 +14,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pcv_erista.hpp"
#include "pcv.hpp"
namespace ams::ldr::oc::pcv::erista {
Result CpuFreqCvbTable(u32* ptr) {
cpu_freq_cvb_table_t* default_end = reinterpret_cast<cpu_freq_cvb_table_t *>(ptr);
cpu_freq_cvb_table_t* new_start = default_end + 1;
// Validate existing table
void* cpu_cvb_table_head = reinterpret_cast<u8 *>(new_start) - sizeof(CpuCvbTableDefault);
bool validated = std::memcmp(cpu_cvb_table_head, CpuCvbTableDefault, sizeof(CpuCvbTableDefault)) == 0;
R_UNLESS(validated, ldr::ResultInvalidCpuDvfs());
std::memcpy(reinterpret_cast<void *>(new_start), CpuCvbTableAppend, sizeof(CpuCvbTableAppend));
// Patch CPU max volt in existing and appended CPU dvfs table
if (C.eristaCpuMaxVolt) {
size_t table_size = sizeof(CpuCvbTableAppend) / sizeof(cpu_freq_cvb_table_t);
cpu_freq_cvb_table_t* entry = new_start;
for (size_t i = 0; i < table_size; i++) {
if (entry->cvb_dfll_param.c0 == CpuVoltL4T) {
PatchOffset(reinterpret_cast<u32 *>(&(entry->cvb_dfll_param.c0)), C.eristaCpuMaxVolt * 1000);
}
entry++;
}
}
R_SUCCEED();
}
Result CpuVoltRange(u32* ptr) {
u32 min_volt_got = *(ptr - 1);
for (const auto& mv : CpuMinVolts) {
@@ -53,7 +27,7 @@ Result CpuVoltRange(u32* ptr) {
if (!C.eristaCpuMaxVolt)
R_SKIP();
PatchOffset(ptr, C.eristaCpuMaxVolt);
PATCH_OFFSET(ptr, C.eristaCpuMaxVolt);
R_SUCCEED();
}
R_THROW(ldr::ResultInvalidCpuMinVolt());
@@ -80,7 +54,12 @@ Result MemFreqMtcTable(u32* ptr) {
for (u32 i = khz_list_size - 1; i > 0; i--)
std::memcpy(static_cast<void *>(table_list[i]), static_cast<void *>(table_list[i - 1]), sizeof(EristaMtcTable));
PatchOffset(ptr, C.eristaEmcMaxClock);
PATCH_OFFSET(ptr, C.eristaEmcMaxClock);
// Handle customize table replacement
if (C.mtcConf == CUSTOMIZED_ALL) {
MemMtcCustomizeTable(table_list[0], const_cast<EristaMtcTable *>(C.eristaMtcTable));
}
R_SUCCEED();
}
@@ -89,19 +68,23 @@ Result MemFreqMax(u32* ptr) {
if (C.eristaEmcMaxClock <= EmcClkOSLimit)
R_SKIP();
PatchOffset(ptr, C.eristaEmcMaxClock);
PATCH_OFFSET(ptr, C.eristaEmcMaxClock);
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
PatcherEntry<u32> patches[] = {
{ "CPU Freq Table", &CpuFreqCvbTable, 1, nullptr, CpuClkOSLimit },
{ "CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn },
{ "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
{ "MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
{ "CPU Freq Table", CpuFreqCvbTable<false>, 1, nullptr, CpuCvbDefaultMaxFreq },
{ "CPU Volt Limit", &CpuVoltRange, 0, &CpuMaxVoltPatternFn },
{ "GPU Freq Table", GpuFreqCvbTable<false>, 1, nullptr, GpuCvbDefaultMaxFreq },
{ "MEM Freq Mtc", &MemFreqMtcTable, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit },
{ "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit },
{ "MEM Volt", &MemVoltHandler, 2, nullptr, MemVoltHOS },
};
for (uintptr_t ptr = mapped_nso;

View File

@@ -1,65 +0,0 @@
/*
* Copyright (C) Switch-OC-Suite
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "pcv.hpp"
namespace ams::ldr::oc::pcv::erista {
constexpr cpu_freq_cvb_table_t CpuCvbTableDefault[] = {
// CPU_PLL_CVB_TABLE_ODN
{ 204000, { 721094 }, {} },
{ 306000, { 754040 }, {} },
{ 408000, { 786986 }, {} },
{ 510000, { 819932 }, {} },
{ 612000, { 852878 }, {} },
{ 714000, { 885824 }, {} },
{ 816000, { 918770 }, {} },
{ 918000, { 951716 }, {} },
{ 1020000, { 984662 }, { -2875621, 358099, -8585 } },
{ 1122000, { 1017608 }, { -52225, 104159, -2816 } },
{ 1224000, { 1050554 }, { 1076868, 8356, -727 } },
{ 1326000, { 1083500 }, { 2208191, -84659, 1240 } },
{ 1428000, { 1116446 }, { 2519460, -105063, 1611 } },
{ 1581000, { 1130000 }, { 2889664, -122173, 1834 } },
{ 1683000, { 1168000 }, { 5100873, -279186, 4747 } },
{ 1785000, { 1227500 }, { 5100873, -279186, 4747 } },
};
constexpr u32 CpuVoltL4T = 1235'000;
constexpr cpu_freq_cvb_table_t CpuCvbTableAppend[] = {
{ 1887000, { CpuVoltL4T }, { 5100873, -279186, 4747 } },
{ 1963500, { CpuVoltL4T }, { 5100873, -279186, 4747 } },
{ 2091000, { CpuVoltL4T }, { 5100873, -279186, 4747 } },
};
constexpr u32 CpuMinVolts[] = { 950, 850, 825, 810 };
inline bool CpuMaxVoltPatternFn(u32* ptr32) {
u32 val = *ptr32;
return (val == 1132 || val == 1170 || val == 1227);
}
constexpr u32 MemVoltHOS = 1125'000;
constexpr u32 EmcClkPllmLimit = 1866'000'000;
constexpr u32 MTC_TABLE_REV = 7;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -14,7 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "pcv_mariko.hpp"
#include "pcv.hpp"
namespace ams::ldr::oc::pcv::mariko {
@@ -26,37 +26,7 @@ Result CpuFreqVdd(u32* ptr) {
R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry());
R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry());
if (!C.marikoCpuMaxClock)
R_SKIP();
PatchOffset(ptr, C.marikoCpuMaxClock);
R_SUCCEED();
}
Result CpuFreqCvbTable(u32* ptr) {
cpu_freq_cvb_table_t* default_end = reinterpret_cast<cpu_freq_cvb_table_t *>(ptr);
cpu_freq_cvb_table_t* new_start = default_end + 1;
// Validate existing table
void* cpu_cvb_table_head = reinterpret_cast<u8 *>(new_start) - sizeof(CpuCvbTableDefault);
bool validated = std::memcmp(cpu_cvb_table_head, CpuCvbTableDefault, sizeof(CpuCvbTableDefault)) == 0;
R_UNLESS(validated, ldr::ResultInvalidCpuDvfs());
if (C.marikoCpuMaxClock > CpuClkOfficial)
std::memcpy(reinterpret_cast<void *>(new_start), CpuCvbTableAppend, sizeof(CpuCvbTableAppend));
// Patch CPU max volt in existing and appended CPU dvfs table
if (C.marikoCpuMaxVolt) {
size_t table_size = (sizeof(CpuCvbTableDefault) + sizeof(CpuCvbTableAppend)) / sizeof(cpu_freq_cvb_table_t);
cpu_freq_cvb_table_t* entry = static_cast<cpu_freq_cvb_table_t *>(cpu_cvb_table_head);
for (size_t i = 0; i < table_size; i++) {
if (entry->cvb_pll_param.c0 == CpuVoltOfficial * 1000) {
PatchOffset(reinterpret_cast<u32 *>(&(entry->cvb_pll_param.c0)), C.marikoCpuMaxVolt * 1000);
}
entry++;
}
}
PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq);
R_SUCCEED();
}
@@ -70,29 +40,12 @@ Result CpuVoltRange(u32* ptr) {
if (!C.marikoCpuMaxVolt)
R_SKIP();
PatchOffset(ptr, C.marikoCpuMaxVolt);
PATCH_OFFSET(ptr, C.marikoCpuMaxVolt);
R_SUCCEED();
}
R_THROW(ldr::ResultInvalidCpuMinVolt());
}
Result GpuFreqCvbTable(u32* ptr) {
gpu_cvb_pll_table_t* default_end = reinterpret_cast<gpu_cvb_pll_table_t *>(ptr);
gpu_cvb_pll_table_t* new_start = default_end + 1;
// Validate existing table
void* gpu_cvb_table_head = reinterpret_cast<u8 *>(new_start) - sizeof(GpuCvbTableDefault);
bool validated = std::memcmp(gpu_cvb_table_head, GpuCvbTableDefault, sizeof(GpuCvbTableDefault)) == 0;
R_UNLESS(validated, ldr::ResultInvalidGpuDvfs());
if (C.marikoGpuMaxClock <= GpuClkOfficial)
R_SKIP();
std::memcpy(reinterpret_cast<void *>(new_start), GpuCvbTableAppend, sizeof(GpuCvbTableAppend));
R_SUCCEED();
}
Result GpuFreqMaxAsm(u32* ptr32) {
// Check if both two instructions match the pattern
u32 ins1 = *ptr32, ins2 = *(ptr32 + 1);
@@ -104,15 +57,13 @@ Result GpuFreqMaxAsm(u32* ptr32) {
if (rd != asm_get_rd(ins2))
R_THROW(ldr::ResultInvalidGpuFreqMaxPattern());
if (!C.marikoGpuMaxClock)
R_SKIP();
u32 max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq;
u32 asm_patch[2] = {
asm_set_rd(asm_set_imm16(asm_pattern[0], C.marikoGpuMaxClock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], C.marikoGpuMaxClock >> 16), rd)
asm_set_rd(asm_set_imm16(asm_pattern[0], max_clock), rd),
asm_set_rd(asm_set_imm16(asm_pattern[1], max_clock >> 16), rd)
};
PatchOffset(ptr32, asm_patch[0]);
PatchOffset(ptr32 + 1, asm_patch[1]);
PATCH_OFFSET(ptr32, asm_patch[0]);
PATCH_OFFSET(ptr32 + 1, asm_patch[1]);
R_SUCCEED();
}
@@ -149,7 +100,7 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
* you'd better calculate timings yourself rather than relying on following algorithm.
*/
if (C.mtcConf == NO_ADJ_ALL)
if (C.mtcConf != AUTO_ADJ_SAFE_MARIKO_ONLY && C.mtcConf != AUTO_ADJ_SAFE_MARIKO_ONLY)
return;
#define ADJUST_PROP(TARGET, REF) \
@@ -189,7 +140,7 @@ void MemMtcTableAutoAdjust(MarikoMtcTable* table, const MarikoMtcTable* ref) {
ADJUST_PARAM_TABLE(table, la_scale_regs.mc_ptsa_grant_decrement, ref);
/* Timings that are available in or can be derived from LPDDR4X datasheet or TRM */
const bool use_4266_spec = C.mtcConf == AUTO_ADJ_MARIKO_4266_NO_ADJ_ERISTA;
const bool use_4266_spec = C.mtcConf == AUTO_ADJ_4266_MARIKO_ONLY;
// tCK_avg (average clock period) in ns
const double tCK_avg = 1000'000. / C.marikoEmcMaxClock;
// tRPpb (row precharge time per bank) in ns
@@ -335,7 +286,12 @@ Result MemFreqMtcTable(u32* ptr) {
delete tmp;
PatchOffset(ptr, C.marikoEmcMaxClock);
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
// Handle customize table replacement
if (C.mtcConf == CUSTOMIZED_ALL) {
MemMtcCustomizeTable(table_list[0], reinterpret_cast<MarikoMtcTable *>(reinterpret_cast<u8 *>(C.marikoMtcTable)));
}
R_SUCCEED();
}
@@ -367,7 +323,7 @@ Result MemFreqMax(u32* ptr) {
if (C.marikoEmcMaxClock <= EmcClkOSLimit)
R_SKIP();
PatchOffset(ptr, C.marikoEmcMaxClock);
PATCH_OFFSET(ptr, C.marikoEmcMaxClock);
R_SUCCEED();
}
@@ -384,7 +340,7 @@ Result EmcVddqVolt(u32* ptr) {
R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry());
R_SUCCEED();
};
R_TRY(validator());
u32 emc_uv = C.marikoEmcVddqVolt;
@@ -394,25 +350,28 @@ Result EmcVddqVolt(u32* ptr) {
if (emc_uv % uv_step)
emc_uv = emc_uv / uv_step * uv_step; // rounding
PatchOffset(ptr, emc_uv);
PATCH_OFFSET(ptr, emc_uv);
R_SUCCEED();
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
u32 CpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq);
u32 GpuCvbDefaultMaxFreq = static_cast<u32>(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq);
PatcherEntry<u32> patches[] = {
{ "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit },
{ "CPU Freq Table", &CpuFreqCvbTable, 1, nullptr, CpuClkOfficial },
{ "CPU Volt Limit", &CpuVoltRange, 13, nullptr, CpuVoltOfficial },
{ "GPU Freq Table", &GpuFreqCvbTable, 1, nullptr, GpuClkOfficial },
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
{ "GPU Freq PLL", &GpuFreqPllLimit, 1, 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<true>, 1, nullptr, CpuCvbDefaultMaxFreq },
{ "CPU Volt Limit", &CpuVoltRange, 13, nullptr, CpuVoltOfficial },
{ "GPU Freq Table", GpuFreqCvbTable<true>, 1, nullptr, GpuCvbDefaultMaxFreq },
{ "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn },
{ "GPU Freq PLL", &GpuFreqPllLimit, 1, 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 }
};
for (uintptr_t ptr = mapped_nso;

View File

@@ -1,127 +0,0 @@
/*
* Copyright (C) Switch-OC-Suite
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "pcv.hpp"
namespace ams::ldr::oc::pcv::mariko {
constexpr cpu_freq_cvb_table_t CpuCvbTableDefault[] = {
// CPUB01_CVB_TABLE
{ 204000, { 721589, -12695, 27 }, {} },
{ 306000, { 747134, -14195, 27 }, {} },
{ 408000, { 776324, -15705, 27 }, {} },
{ 510000, { 809160, -17205, 27 }, {} },
{ 612000, { 845641, -18715, 27 }, {} },
{ 714000, { 885768, -20215, 27 }, {} },
{ 816000, { 929540, -21725, 27 }, {} },
{ 918000, { 976958, -23225, 27 }, {} },
{ 1020000, { 1028021, -24725, 27 }, { 1120000 } },
{ 1122000, { 1082730, -26235, 27 }, { 1120000 } },
{ 1224000, { 1141084, -27735, 27 }, { 1120000 } },
{ 1326000, { 1203084, -29245, 27 }, { 1120000 } },
{ 1428000, { 1268729, -30745, 27 }, { 1120000 } },
{ 1581000, { 1374032, -33005, 27 }, { 1120000 } },
{ 1683000, { 1448791, -34505, 27 }, { 1120000 } },
{ 1785000, { 1527196, -36015, 27 }, { 1120000 } },
{ 1887000, { 1609246, -37515, 27 }, { 1120000 } },
{ 1963500, { 1675751, -38635, 27 }, { 1120000 } },
};
constexpr cpu_freq_cvb_table_t CpuCvbTableAppend[] = {
{ 2091000, { 1716501, -39395, 27 }, { 1120000 } },
{ 2193000, { 1775132, -40505, 27 }, { 1120000 } },
{ 2295000, { 1866287, -42005, 27 }, { 1120000 } },
// 2397000 kHz is not listed in l4t
{ 2397000, { 1961107, -43506, 27 }, { 1120000 } },
};
constexpr u32 CpuMinVolts[] = { 800, 637, 620, 610 };
constexpr u32 CpuClkOfficial = 1963'500;
constexpr u32 CpuVoltOfficial = 1120;
constexpr gpu_cvb_pll_table_t GpuCvbTableDefault[] = {
// GPUB01_NA_CVB_TABLE
{ 76800, {}, { 610000, } },
{ 153600, {}, { 610000, } },
{ 230400, {}, { 610000, } },
{ 307200, {}, { 610000, } },
{ 384000, {}, { 610000, } },
{ 460800, {}, { 610000, } },
{ 537600, {}, { 801688, -10900, -163, 298, -10599, 162 } },
{ 614400, {}, { 824214, -5743, -452, 238, -6325, 81 } },
{ 691200, {}, { 848830, -3903, -552, 119, -4030, -2 } },
{ 768000, {}, { 891575, -4409, -584, 0, -2849, 39 } },
{ 844800, {}, { 940071, -5367, -602, -60, -63, -93 } },
{ 921600, {}, { 986765, -6637, -614, -179, 1905, -13 } },
{ 998400, {}, { 1098475, -13529, -497, -179, 3626, 9 } },
{ 1075200, {}, { 1163644, -12688, -648, 0, 1077, 40 } },
{ 1152000, {}, { 1204812, -9908, -830, 0, 1469, 110 } },
{ 1228800, {}, { 1277303, -11675, -859, 0, 3722, 313 } },
{ 1267200, {}, { 1335531, -12567, -867, 0, 3681, 559 } },
};
constexpr gpu_cvb_pll_table_t GpuCvbTableAppend[] = {
// 1305600 kHz is not listed in l4t
{ 1305600, {}, { 1374130, -13725, -859, 0, 4442, 576 } },
};
constexpr u32 GpuClkOfficial = 1267'200;
constexpr u32 GpuClkPllLimit = 1300'000'000;
/* GPU Max Clock asm Pattern:
*
* MOV W11, #0x1000 MOV (wide immediate) 0x1000 0xB (11)
* sf | opc | | hw | imm16 | 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
* 0 | 1 0 | 1 0 0 1 0 1| 0 0| 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 |0 1 0 1 1
*
* MOVK W11, #0xE, LSL#16 <shift>16 0xE 0xB (11)
* sf | opc | | hw | imm16 | 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
* 0 | 1 1 | 1 0 0 1 0 1| 0 1| 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 |0 1 0 1 1
*/
inline constexpr u32 asm_pattern[] = { 0x52820000, 0x72A001C0 };
inline auto asm_compare_no_rd = [](u32 ins1, u32 ins2) { return ((ins1 ^ ins2) >> 5) == 0; };
inline auto asm_get_rd = [](u32 ins) { return ins & ((1 << 5) - 1); };
inline auto asm_set_rd = [](u32 ins, u8 rd) { return (ins & 0xFFFFFFE0) | (rd & 0x1F); };
inline auto asm_set_imm16 = [](u32 ins, u16 imm) { return (ins & 0xFFE0001F) | ((imm & 0xFFFF) << 5); };
inline bool GpuMaxClockPatternFn(u32* ptr32) {
return asm_compare_no_rd(*ptr32, asm_pattern[0]);
}
constexpr emc_dvb_dvfs_table_t EmcDvbTableDefault[] = {
{ 204000, { 637, 637, 637, } },
{ 408000, { 637, 637, 637, } },
{ 800000, { 637, 637, 637, } },
{ 1065600, { 637, 637, 637, } },
{ 1331200, { 650, 637, 637, } },
{ 1600000, { 675, 650, 637, } },
};
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;
void Patch(uintptr_t mapped_nso, size_t nso_size);
}

View File

@@ -19,20 +19,20 @@
namespace ams::ldr::oc::ptm {
Result CpuPtmBoost(perf_conf_entry* entry) {
if (!C.marikoCpuBoostClock)
if (!C.commonCpuBoostClock)
R_SUCCEED();
u32 cpuPtmBoostNew = C.marikoCpuBoostClock * 1000;
u32 cpuPtmBoostNew = C.commonCpuBoostClock * 1000;
PatchOffset(&(entry->cpu_freq_1), cpuPtmBoostNew);
PatchOffset(&(entry->cpu_freq_2), cpuPtmBoostNew);
PATCH_OFFSET(&(entry->cpu_freq_1), cpuPtmBoostNew);
PATCH_OFFSET(&(entry->cpu_freq_2), cpuPtmBoostNew);
R_SUCCEED();
}
Result MemPtm(perf_conf_entry* entry) {
PatchOffset(&(entry->emc_freq_1), memPtmLimit);
PatchOffset(&(entry->emc_freq_2), memPtmLimit);
PATCH_OFFSET(&(entry->emc_freq_1), memPtmLimit);
PATCH_OFFSET(&(entry->emc_freq_2), memPtmLimit);
R_SUCCEED();
}
@@ -52,13 +52,6 @@ bool PtmTablePatternFn(u32* ptr) {
}
void Patch(uintptr_t mapped_nso, size_t nso_size) {
#ifdef ATMOSPHERE_IS_STRATOSPHERE
// Ptm patcher is disabled for Erista
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
if (!isMariko)
return;
#endif
perf_conf_entry* confTable = nullptr;
for (uintptr_t ptr = mapped_nso;
ptr <= mapped_nso + nso_size - sizeof(perf_conf_entry) * entryCnt;
@@ -75,10 +68,15 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
CRASH("confTable not found!");
}
PatcherEntry<perf_conf_entry> patches[] = {
{ "CPU Ptm Boost", &CpuPtmBoost, 2, },
{ "MEM Ptm", &MemPtm, 16, },
};
PatcherEntry<perf_conf_entry> cpuPtmBoostPatch = { "CPU Ptm Boost", &CpuPtmBoost, 2, };
PatcherEntry<perf_conf_entry> memPtmPatch = { "MEM Ptm", &MemPtm, 16, };
#ifdef ATMOSPHERE_IS_STRATOSPHERE
bool isMariko = (spl::GetSocType() == spl::SocType_Mariko);
#else
bool isMariko = true;
#endif
for (u32 i = 0; i < entryCnt; i++) {
perf_conf_entry* entry = confTable + i;
@@ -90,7 +88,7 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
switch (entry->cpu_freq_1) {
case cpuPtmBoost:
patches[0].Apply(entry);
cpuPtmBoostPatch.Apply(entry);
break;
case cpuPtmDefault:
case cpuPtmDevOC:
@@ -104,7 +102,9 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
case memPtmLimit:
case memPtmAlt:
case memPtmClamp:
patches[1].Apply(entry);
if (isMariko) {
memPtmPatch.Apply(entry);
}
break;
default:
LOGGING("%u (0x%08x) @%p", entry->emc_freq_1, entry->conf_id, &(entry->emc_freq_2));
@@ -112,10 +112,14 @@ void Patch(uintptr_t mapped_nso, size_t nso_size) {
}
}
for (auto& patch : patches) {
LOGGING("%s Count: %zu", patch.description, patch.patched_count);
if (R_FAILED(patch.CheckResult()))
CRASH(patch.description);
LOGGING("%s Count: %zu", cpuPtmBoostPatch.description, cpuPtmBoostPatch.patched_count);
if (R_FAILED(cpuPtmBoostPatch.CheckResult()))
CRASH(cpuPtmBoostPatch.description);
if (isMariko) {
LOGGING("%s Count: %zu", memPtmPatch.description, memPtmPatch.patched_count);
if (R_FAILED(memPtmPatch.CheckResult()))
CRASH(memPtmPatch.description);
}
}