hoc-sys: add emc patcher

This commit is contained in:
souldbminersmwc
2025-11-02 13:09:23 -05:00
parent 9f1c5570b2
commit 745ec1d9c8
8 changed files with 461 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
#include "emc_patcher.h"
#include "file_utils.h"
#include "board.h"
#define MC_BASE 0x70019000
#define EMC_BASE 0x7001B000
#define MC_EMC_BASE_SIZE 0x1000
#define EMC_TIMING_CONTROL_0 0x28
#define EMC_RAS_0 0x34
#define EMC_RAS_BIT_END 5
#define HOSSVC_HAS_MM (hosversionAtLeast(10,0,0))
EMCpatcher* EMCpatcher::instance = nullptr;
EMCpatcher* EMCpatcher::GetInstance()
{
return instance;
}
void EMCpatcher::Initialize()
{
if (!instance)
{
instance = new EMCpatcher();
FileUtils::LogLine("[emc] Initialized EMCpatcher");
}
}
Config *EMCpatcher::GetConfig()
{
return this->config;
}
void EMCpatcher::Exit()
{
if (instance)
{
FileUtils::LogLine("[emc] Exiting EMCpatcher");
delete instance;
instance = nullptr;
}
}
EMCpatcher::EMCpatcher()
{
this->config = Config::CreateDefault();
}
EMCpatcher::~EMCpatcher()
{
delete this->config;
}
void EMCpatcher::Run()
{
std::scoped_lock lock{this->patcherMutex};
this->config->Refresh();
this->ApplyEMCPatch();
}
void EMCpatcher::ApplyEMCPatch()
{
if(HOSSVC_HAS_MM) { // only for 10.0.0+, older versions need rewrites
u64 mc_virt_addr = 0;
u64 mc_out_size = 0;
u64 emc_virt_addr = 0;
u64 emc_out_size = 0;
Result rc;
rc = svcQueryMemoryMapping(&mc_virt_addr, &mc_out_size, MC_BASE, MC_EMC_BASE_SIZE); // map mc
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping");
rc = svcQueryMemoryMapping(&emc_virt_addr, &emc_out_size, EMC_BASE, MC_EMC_BASE_SIZE); // map emc
ASSERT_RESULT_OK(rc, "svcQueryMemoryMapping");
write_reg64(emc_virt_addr, EMC_RAS_0, 1);
write_reg64(emc_virt_addr, EMC_TIMING_CONTROL_0, 0x1); // apply shadow regs
svcUnmapMemory((void *)mc_virt_addr, (void *)MC_BASE, MC_EMC_BASE_SIZE); // clean up
svcUnmapMemory((void *)emc_virt_addr, (void *)EMC_BASE, MC_EMC_BASE_SIZE);
}
}

View File

@@ -0,0 +1,62 @@
#pragma once
#include <mutex>
#include <cstdint>
#include "config.h"
#include <array>
#include "errors.h"
static inline uint32_t read_reg64(uint64_t virt_addr, uint32_t offset) {
return *(volatile uint32_t *)(virt_addr + offset);
}
static inline void write_reg64(uint64_t virt_addr, uint32_t offset, uint32_t value) {
*(volatile uint32_t *)(virt_addr + offset) = value;
}
static inline uint32_t set_bits(uint32_t reg_value, uint8_t start_bit, uint8_t end_bit, uint32_t value)
{
if (end_bit < start_bit || end_bit > 31)
return reg_value;
// Create bit mask for the field
uint32_t mask = ((1u << (end_bit - start_bit + 1)) - 1u) << start_bit;
// Clear target bits and insert new value
reg_value = (reg_value & ~mask) | ((value << start_bit) & mask);
return reg_value;
}
/* Primary timings. */
const std::array<double, 8> tRCD_values = {18, 17, 16, 15, 14, 13, 12, 11};
const std::array<double, 8> tRP_values = {18, 17, 16, 15, 14, 13, 12, 11};
const std::array<double, 10> tRAS_values = {42, 36, 34, 32, 30, 28, 26, 24, 22, 20};
/* Secondary timings. */
const std::array<double, 8> tRRD_values = {10.0, 7.5, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0};
const std::array<double, 6> tRFC_values = {140, 120, 100, 80, 60, 40};
const std::array<u32, 10> tRTW_values = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; /* Is this even correct? */
const std::array<double, 10> tWTR_values = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
const std::array<u32, 7> tREFpb_values = {488, 732, 488 * 2, 488 * 3, 488 * 4, 488 * 6, 488 * 8}; /* TODO: Figure out if it's actually 8 and if this is even right. */
class EMCpatcher
{
private:
static EMCpatcher* instance;
Config* config;
std::mutex patcherMutex;
public:
static EMCpatcher* GetInstance();
static void Initialize();
Config *GetConfig();
static void Exit();
EMCpatcher();
~EMCpatcher();
void Run();
void ApplyEMCPatch();
};

View File

@@ -31,6 +31,8 @@
#include "file_utils.h"
#include "errors.h"
#include "clock_manager.h"
#include "emc_patcher.h"
IpcService::IpcService(ClockManager* clockMgr)
{
std::int32_t priority;
@@ -189,6 +191,9 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
return ipcSrv->SetReverseNXRTMode(mode);
}
break;
case HocClkIpcCmd_UpdateEMCRegs: // Trigger, not data
return ipcSrv->PatchEmcRegs();
break;
}
return SYSCLK_ERROR(Generic);
@@ -225,6 +230,7 @@ Result IpcService::Exit()
return 0;
}
Result IpcService::GetProfileCount(std::uint64_t* tid, std::uint8_t* out_count)
{
Config* config = this->clockMgr->GetConfig();
@@ -345,3 +351,9 @@ Result IpcService::SetReverseNXRTMode(ReverseNXMode mode) {
ClockManager::GetInstance()->SetRNXRTMode(mode);
return 0;
}
Result IpcService::PatchEmcRegs() {
EMCpatcher::GetInstance()->Run();
return 0;
}

View File

@@ -54,6 +54,8 @@ class IpcService
Result SetConfigValues(SysClkConfigValueList* configValues);
Result GetFreqList(SysClkIpc_GetFreqList_Args* args, std::uint32_t* out_list, std::size_t size, std::uint32_t* out_count);
Result SetReverseNXRTMode(ReverseNXMode mode);
Result PatchEmcRegs();
bool running;
Thread thread;