222 lines
8.8 KiB
C++
222 lines
8.8 KiB
C++
/*
|
|
* Copyright (C) Switch-OC-Suite
|
|
*
|
|
* Copyright (c) 2023 hanai3Bi
|
|
*
|
|
* Copyright (c) Souldbminer, Lightos_ and Horizon OC Contributors
|
|
*
|
|
* 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 "../oc_common.hpp"
|
|
#include "pcv_common.hpp"
|
|
#include "pcv_erista.hpp"
|
|
#include "pcv_mariko.hpp"
|
|
|
|
namespace ams::ldr::hoc::pcv {
|
|
|
|
inline auto MatchesPattern = [](u32 *base, const auto &offsets, const auto &values) {
|
|
for (size_t i = 0; i < std::size(values); ++i) {
|
|
if (*(base + offsets[i]) != values[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
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 = nullptr;
|
|
|
|
if (isMariko) {
|
|
switch (C.tableConf) {
|
|
case TBREAK_1683: {
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTable1683Tbreak);
|
|
break;
|
|
}
|
|
case TBREAK_1581: {
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTable1581Tbreak);
|
|
break;
|
|
}
|
|
case EXTREME_TABLE: {
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoCpuDvfsTableExtreme);
|
|
break;
|
|
}
|
|
case DEFAULT_TABLE:
|
|
default: {
|
|
customize_table = default_table;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (C.eristaCpuUV) {
|
|
if (C.eristaCpuUnlock) {
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaCpuDvfsTableSLT);
|
|
} else {
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaCpuDvfsTable);
|
|
}
|
|
} else {
|
|
customize_table = default_table;
|
|
}
|
|
}
|
|
|
|
u32 cpu_max_volt = isMariko ? C.marikoCpuMaxVolt : C.eristaCpuMaxVolt;
|
|
u32 cpu_freq_threshold = 2091'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);
|
|
|
|
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);
|
|
|
|
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) {
|
|
if (isMariko) {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), cpu_max_volt * 1000);
|
|
} else {
|
|
// PATCH_OFFSET(&(entry->cvb_dfll_param.c0), cpu_max_volt * 1000);
|
|
}
|
|
}
|
|
entry++;
|
|
}
|
|
}
|
|
R_SUCCEED();
|
|
}
|
|
|
|
constexpr void ClearCvbPllEntry(cvb_entry_t *entry) {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c1), 0);
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c2), 0);
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c3), 0);
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c4), 0);
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c5), 0);
|
|
}
|
|
|
|
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;
|
|
if (isMariko) {
|
|
switch (C.marikoGpuUV) {
|
|
case 0:
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
|
break;
|
|
case 1:
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableSLT);
|
|
break;
|
|
case 2:
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTableHiOPT);
|
|
break;
|
|
default:
|
|
customize_table = const_cast<cvb_entry_t *>(C.marikoGpuDvfsTable);
|
|
break;
|
|
}
|
|
} else {
|
|
switch (C.eristaGpuUV) {
|
|
case 0:
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
|
break;
|
|
case 1:
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableSLT);
|
|
break;
|
|
case 2:
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTableHiOPT);
|
|
break;
|
|
default:
|
|
customize_table = const_cast<cvb_entry_t *>(C.eristaGpuDvfsTable);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
// Patch GPU volt
|
|
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
|
|
for (size_t i = 0; i < customize_entry_count; ++i) {
|
|
if (isMariko) {
|
|
if (C.marikoGpuVoltArray[i] != 0) {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.marikoGpuVoltArray[i] * 1000));
|
|
ClearCvbPllEntry(entry);
|
|
} else {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
|
|
}
|
|
} else {
|
|
if (C.eristaGpuVoltArray[i] != 0) {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (C.eristaGpuVoltArray[i] * 1000));
|
|
ClearCvbPllEntry(entry);
|
|
} else {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
|
|
}
|
|
}
|
|
++entry;
|
|
}
|
|
if (C.commonGpuVoltOffset && !(isMariko ? C.marikoGpuUV : C.eristaGpuUV)) {
|
|
cvb_entry_t *entry = static_cast<cvb_entry_t *>(gpu_cvb_table_head);
|
|
for (size_t i = 0; i < customize_entry_count; ++i) {
|
|
PATCH_OFFSET(&(entry->cvb_pll_param.c0), (entry->cvb_pll_param.c0 - C.commonGpuVoltOffset * 1000));
|
|
++entry;
|
|
}
|
|
}
|
|
|
|
R_SUCCEED();
|
|
};
|
|
|
|
Result MemFreqPllmLimit(u32 *ptr);
|
|
Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2
|
|
|
|
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(T, clk_src_emc); offset < sizeof(T); offset += sizeof(u32)) {
|
|
u32 *src_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(src) + offset);
|
|
u32 *dst_ent = reinterpret_cast<u32 *>(reinterpret_cast<size_t>(dst) + offset);
|
|
u32 src_val = *src_ent;
|
|
|
|
if (src_val){
|
|
PATCH_OFFSET(dst_ent, src_val == ZERO_VAL ? 0 : src_val);
|
|
}
|
|
}
|
|
|
|
R_SUCCEED();
|
|
};
|
|
|
|
void SafetyCheck();
|
|
void Patch(uintptr_t mapped_nso, size_t nso_size);
|
|
|
|
}
|