17 Commits
1.0.0 ... 1.0.2

Author SHA1 Message Date
souldbminersmwc
9558fd67bb update binaries 2026-03-17 20:33:19 -04:00
souldbminersmwc
59b62a835b misc: fix config bug 2026-03-17 20:30:33 -04:00
souldbminersmwc
ad6847ec59 Update zh-tw.json 2026-03-16 18:38:04 -04:00
Lightos1
061c255978 Change eristaCpuDvfsTable max to 2091, this is fine because vmax exists 2026-03-16 20:04:23 +01:00
Lightos1
bb44d0907d Fix dvfs yet again, todo make this not shit 2026-03-16 20:01:52 +01:00
Lightos1
2215b17a54 Merge pull request #45 from nangongjing1/main
Incorporate Traditional Chinese into Horizon-oc-overlay.
2026-03-16 16:18:46 +01:00
南宫镜
cc52eab3f6 Add files via upload 2026-03-16 23:13:15 +08:00
Lightos1
155a4ce50a Remove redundant else 2026-03-15 21:24:35 +01:00
Lightos1
17739377bd Fix Typo, add todo and ordering cleanup 2026-03-15 21:14:52 +01:00
Lightos1
782fae1624 minimum vmax = 800 2026-03-15 20:49:56 +01:00
Lightos1
59ca1f18d6 Add modified secmon_smc_handler.cpp for panic codes in loader 2026-03-15 20:44:25 +01:00
Lightos1
c6cda6eb30 Remove soctherm region unlock 2026-03-15 20:43:31 +01:00
Lightos1
9f3c5d8de6 Add debug panic codes 2026-03-15 20:35:45 +01:00
Souldbminer
e4c9ac6ee3 Update customize.cpp 2026-03-15 13:05:45 -04:00
souldbminersmwc
5d6500523b bump version 2026-03-15 12:36:32 -04:00
souldbminersmwc
b111ccabb5 sysclk: fix cpugovernorminimumfreq not setting correctly 2026-03-15 12:22:30 -04:00
souldbminersmwc
b90a67aa43 update binaries 2026-03-15 12:21:59 -04:00
26 changed files with 807 additions and 1159 deletions

View File

@@ -1,25 +0,0 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#define __ACCESS_TABLE_NAME__ SocthermAccessTable
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceSoctherm.GetAddress()
#define __ACCESS_TABLE_INC__ "secmon_soctherm_access_table_data.inc"
#include "secmon_define_access_table.inc"
#undef __ACCESS_TABLE_INC__
#undef __ACCESS_TABLE_ADDRESS__
#undef __ACCESS_TABLE_NAME__

View File

@@ -123,7 +123,7 @@ namespace ams::secmon {
constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCode = MemoryRegion(UINT64_C(0x40020000), 0x20000);
static_assert(MemoryRegionPhysicalIram.Contains(MemoryRegionPhysicalIramBootCode));
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000 + 0x2000));
constexpr inline const MemoryRegion MemoryRegionVirtualDevice = MemoryRegion(UINT64_C(0x1F0040000), UINT64_C(0x40000));
static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualDevice));
constexpr inline const MemoryRegion MemoryRegionVirtualDeviceEmpty = MemoryRegion(MemoryRegionVirtualDevice.GetStartAddress(), 0);
@@ -155,9 +155,6 @@ namespace ams::secmon {
HANDLER(Disp1, Sdmmc, UINT64_C(0x54200000), UINT64_C(0x3000), true, ## __VA_ARGS__) \
HANDLER(Dsi, Disp1, UINT64_C(0x54300000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(MipiCal, Dsi, UINT64_C(0x700E3000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(Soctherm, MipiCal, UINT64_C(0x700E2000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(ExternalMemoryController1, Soctherm, UINT64_C(0x7001e000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
HANDLER(ExternalMemoryController2, ExternalMemoryController1, UINT64_C(0x7001f000), UINT64_C(0x1000), true, ## __VA_ARGS__) \
#define DEFINE_DEVICE_REGION(_NAME_, _PREV_, _ADDRESS_, _SIZE_, _SECURE_) \
constexpr inline const MemoryRegion MemoryRegionVirtualDevice##_NAME_ = MemoryRegion(MemoryRegionVirtualDevice##_PREV_.GetEndAddress() + 0x1000, _SIZE_); \

View File

@@ -0,0 +1,268 @@
/*
* Copyright (c) Atmosphère-NX
*
* 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/>.
*/
#include <exosphere.hpp>
#include "../secmon_error.hpp"
#include "../secmon_misc.hpp"
#include "secmon_smc_common.hpp"
#include "secmon_smc_handler.hpp"
#include "secmon_smc_aes.hpp"
#include "secmon_smc_carveout.hpp"
#include "secmon_smc_device_unique_data.hpp"
#include "secmon_smc_error.hpp"
#include "secmon_smc_info.hpp"
#include "secmon_smc_memory_access.hpp"
#include "secmon_smc_power_management.hpp"
#include "secmon_smc_random.hpp"
#include "secmon_smc_register_access.hpp"
#include "secmon_smc_result.hpp"
#include "secmon_smc_rsa.hpp"
namespace ams::secmon::smc {
namespace {
struct HandlerInfo {
u32 function_id;
u32 restriction_mask;
SmcHandler handler;
};
struct HandlerTable {
const HandlerInfo *entries;
size_t count;
};
enum HandlerType : int {
HandlerType_User = 0,
HandlerType_Kern = 1,
HandlerType_Count = 2,
};
enum Restriction {
Restriction_None = (0 << 0),
Restriction_Normal = (1 << 0),
Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
Restriction_SafeModeNotAllowed = (1 << 2),
};
enum SmcCallRange {
SmcCallRange_ArmArch = 0,
SmcCallRange_Cpu = 1,
SmcCallRange_Sip = 2,
SmcCallRange_Oem = 3,
SmcCallRange_Standard = 4,
SmcCallRange_TrustedApp = 0x30,
};
enum SmcArgumentType {
ArgumentType_Integer = 0,
ArgumentType_Pointer = 1,
};
enum SmcConvention {
Convention_Smc32 = 0,
Convention_Smc64 = 1,
};
enum SmcCallType {
SmcCallType_YieldingCall = 0,
SmcCallType_FastCall = 1,
};
struct SmcFunctionId {
using FunctionId = util::BitPack64::Field< 0, 8, u32>;
using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
using Reserved = util::BitPack64::Field<16, 8, u32>;
using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
using Reserved2 = util::BitPack64::Field<32, 32, u32>;
};
constinit HandlerInfo g_user_handlers[] = {
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
{ 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
{ 0xC3000002, Restriction_Normal, SmcGetConfigUser },
{ 0xC3000003, Restriction_Normal, SmcGetResult },
{ 0xC3000404, Restriction_Normal, SmcGetResultData },
{ 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
{ 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
{ 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
{ 0xC3000008, Restriction_Normal, SmcLoadAesKey },
{ 0xC3000009, Restriction_Normal, SmcComputeAes },
{ 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
{ 0xC300040B, Restriction_Normal, SmcComputeCmac },
{ 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
{ 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
{ 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
};
/* Deprecated handlerss. */
constexpr inline const HandlerInfo DecryptAndImportEsDeviceKeyHandlerInfo = {
0xC300100C, Restriction_Normal, SmcDecryptAndImportEsDeviceKey
};
constexpr inline const HandlerInfo DecryptAndImportLotusKeyHandlerInfo = {
0xC300100E, Restriction_SafeModeNotAllowed, SmcDecryptAndImportLotusKey
};
constinit HandlerInfo g_kern_handlers[] = {
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
{ 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
{ 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
{ 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
{ 0xC3000004, Restriction_Normal, SmcGetConfigKern },
{ 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
{ 0xC3000006, Restriction_Normal, SmcShowError },
{ 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
{ 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
/* NOTE: Atmosphere extension for mesosphere. This ID is subject to change at any time. */
{ 0xC3000409, Restriction_Normal, SmcSetConfig },
};
constinit HandlerInfo g_ams_handlers[] = {
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
{ 0xF0000201, Restriction_None, SmcIramCopy },
{ 0xF0000002, Restriction_None, SmcReadWriteRegister },
{ 0xF0000003, Restriction_None, SmcWriteAddress },
{ 0xF0000404, Restriction_None, SmcGetEmummcConfig },
{ 0xF0000005, Restriction_None, SmcShowError },
};
constexpr const HandlerInfo GetSecureDataHandlerInfo = {
0x67891234, Restriction_None, SmcGetSecureData
};
constinit HandlerTable g_handler_tables[] = {
{ g_user_handlers, util::size(g_user_handlers) },
{ g_kern_handlers, util::size(g_kern_handlers) },
};
constinit HandlerTable g_ams_handler_table = {
g_ams_handlers, util::size(g_ams_handlers)
};
NORETURN void InvalidSmcError(u64 id) {
SetError(pkg1::ErrorInfo_UnknownSmc);
AMS_ABORT("Invalid SMC: %lx", id);
}
const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
/* Ensure we have a valid handler type. */
if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
InvalidSmcError(id);
}
/* Provide support for legacy SmcGetSecureData. */
if (id == GetSecureDataHandlerInfo.function_id) {
return g_handler_tables[HandlerType_User];
}
/* Check if we're a user SMC. */
if (type == HandlerType_User) {
/* Nintendo uses OEM SMCs. */
/* We will assign Atmosphere extension SMCs the TrustedApplication range. */
if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) {
return g_ams_handler_table;
}
/* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
InvalidSmcError(id);
}
}
return g_handler_tables[type];
}
const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
/* Provide support for legacy SmcGetSecureData. */
if (id == GetSecureDataHandlerInfo.function_id) {
return GetSecureDataHandlerInfo;
}
/* Get and check the index. */
const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>();
if (AMS_UNLIKELY(index >= table.count)) {
InvalidSmcError(id);
}
/* Get and check the handler info. */
const auto &handler_info = table.entries[index];
/* Check that the handler isn't null. */
if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
InvalidSmcError(id);
}
/* Check that the handler's id matches. */
if (AMS_UNLIKELY(handler_info.function_id != id)) {
InvalidSmcError(id);
}
return handler_info;
}
bool IsHandlerRestricted(const HandlerInfo &info) {
return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
}
SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
/* Check if the smc is restricted. */
if (GetTargetFirmware() >= TargetFirmware_8_0_0 && AMS_UNLIKELY(IsHandlerRestricted(info))) {
return SmcResult::NotPermitted;
}
/* Invoke the smc. */
return info.handler(args);
}
}
void ConfigureSmcHandlersForTargetFirmware() {
const auto target_fw = GetTargetFirmware();
if (target_fw < TargetFirmware_5_0_0) {
g_user_handlers[DecryptAndImportEsDeviceKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportEsDeviceKeyHandlerInfo;
g_user_handlers[DecryptAndImportLotusKeyHandlerInfo.function_id & 0xFF] = DecryptAndImportLotusKeyHandlerInfo;
}
}
void HandleSmc(int type, SmcArguments &args) {
/* Get the table. */
const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]);
/* Get the handler info. */
const auto &info = GetHandlerInfo(table, args.r[0]);
/* Set the invocation result. */
args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args));
}
}

View File

@@ -101,7 +101,6 @@ namespace ams::secmon::smc {
#include "secmon_define_emc_access_table.inc"
#include "secmon_define_emc1_access_table.inc"
#include "secmon_define_emc2_access_table.inc"
#include "secmon_define_soctherm_access_table.inc"
#include "secmon_define_mc01_access_table.inc"
constexpr const AccessTableEntry AccessTables[] = {
@@ -110,7 +109,6 @@ namespace ams::secmon::smc {
{ EmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController.GetAddress(), EmcAccessTable::Address, EmcAccessTable::Size, },
{ EmcAccessTable1::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController1.GetAddress(), EmcAccessTable1::Address, EmcAccessTable1::Size, },
{ EmcAccessTable2::ReducedAccessTable.data(), MemoryRegionVirtualDeviceExternalMemoryController2.GetAddress(), EmcAccessTable2::Address, EmcAccessTable2::Size, },
{ SocthermAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceSoctherm.GetAddress(), SocthermAccessTable::Address, SocthermAccessTable::Size, },
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
{ Mc01AccessTable::ReducedAccessTable.data(), Mc01AccessTable::Address + MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
};

File diff suppressed because it is too large Load Diff

View File

@@ -98,7 +98,7 @@ volatile CustomizeTable C = {
.eristaCpuBoostClock = 1785000, // Default boost clock
.marikoCpuBoostClock = 1963000, // Default boost clock
.eristaGpuUV = 2,
.eristaGpuUV = 0,
.eristaGpuVmin = 810,
.marikoGpuUV = 0,
@@ -199,9 +199,9 @@ volatile CustomizeTable C = {
{ 1581000, { 1130000, }, { 2889664, -122173, 1834, } },
{ 1683000, { 1168000, }, { 5100873, -279186, 4747, } },
{ 1785000, { 1225000, }, { 5100873, -279186, 4747, } },
// { 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
// { 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
// { 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
{ 1887000, { 1225000, }, { 5100873, -279186, 4747, } },
{ 1989000, { 1227500, }, { 5100873, -279186, 4747, } },
{ 2091000, { 1256250, }, { 5100873, -279186, 4747, } },
},
.eristaCpuDvfsTableSLT = {

View File

@@ -99,4 +99,25 @@ namespace ams::ldr::hoc {
R_SUCCEED();
}
};
namespace panic {
/* Requires modifying g_ams_handlers in secmon_smc_handler.cpp */
constexpr inline void SmcError(u32 rgb) {
SecmonArgs args = {};
constexpr u32 SmcShowErrorID = 0xF0000005;
args.X[0] = SmcShowErrorID;
args.X[1] = rgb;
svcCallSecureMonitor(&args);
}
constexpr inline u32 PackCode(u32 r, u32 g, u32 b) {
return ((r & 0xF) << 8) | ((g & 0xF) << 4) | ((b & 0xF) << 0);
}
constexpr u32 Gpu = PackCode(0xF, 0x7, 0x0);
constexpr u32 Cpu = PackCode(0xF, 0x0, 0x0);
constexpr u32 Emc = PackCode(0x0, 0xF, 0xF);
constexpr u32 Patch = PackCode(0x8, 0x0, 0xF);
}
}

View File

@@ -84,6 +84,7 @@ namespace ams::ldr::hoc::pcv {
u32 min;
u32 max;
bool value_required = false;
u32 panic;
Result check() {
if (!value_required && !value)
@@ -143,22 +144,27 @@ namespace ams::ldr::hoc::pcv {
using namespace ams::ldr::hoc::pcv;
sValidator validators[] = {
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true },
{ C.commonEmcMemVolt, 912'500, 1350'000 }, // Official burst vmax for the RAMs is 1500mV
{ C.eristaCpuMaxVolt, 1000, 1260 },
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000 },
{ C.marikoCpuMaxVolt, 1000, 1200 },
{ C.marikoEmcMaxClock, 1600'000, 3500'000 },
{ C.marikoEmcVddqVolt, 250'000, 700'000 },
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000 },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000 },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000 },
{ marikoGpuDvfsMaxFreq, 768'000, 1536'000 },
{ C.eristaCpuBoostClock, 1020'000, 2295'000, true, panic::Cpu },
{ C.marikoCpuBoostClock, 1020'000, 2703'000, true, panic::Cpu },
{ C.eristaCpuMaxVolt, 1000, 1260, false, panic::Cpu },
{ C.marikoCpuMaxVolt, 1000, 1200, false, panic::Cpu },
{ eristaCpuDvfsMaxFreq, 1785'000, 2295'000, false, panic::Cpu },
{ marikoCpuDvfsMaxFreq, 1785'000, 2703'000, false, panic::Cpu },
{ C.commonEmcMemVolt, 912'500, 1350'000, false, panic::Emc }, // Official burst vmax for the RAMs is 1500mV
{ GET_MAX_OF_ARR(erista::maxEmcClocks), 1600'000, 2600'000, false, panic::Emc },
{ C.marikoEmcMaxClock, 1600'000, 3500'000, false, panic::Emc },
{ C.marikoEmcVddqVolt, 250'000, 700'000, false, panic::Emc },
{ eristaGpuDvfsMaxFreq, 768'000, 1152'000, false, panic::Gpu },
{ marikoGpuDvfsMaxFreq, 768'000, 1570'000, false, panic::Gpu },
{ C.marikoGpuVmax, 800, 960, false, panic::Gpu },
};
for (auto& i : validators) {
if (R_FAILED(i.check())) {
for (auto &v : validators) {
if (R_FAILED(v.check())) {
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
panic::SmcError(v.panic);
#endif
CRASH("Validation FAIL");
}
}

View File

@@ -474,6 +474,10 @@ namespace ams::ldr::hoc::pcv::erista {
for (auto &entry : patches) {
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult())) {
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
panic::SmcError(panic::Patch);
#endif
CRASH(entry.description);
}
}

View File

@@ -415,6 +415,8 @@ namespace ams::ldr::hoc::pcv::mariko {
u32 trefbw = refresh_raw + 0x40;
trefbw = MIN(trefbw, static_cast<u32>(0x3FFF));
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
CalculateTimings();
WRITE_PARAM_ALL_REG(table, emc_rd_rcd, GET_CYCLE_CEIL(tRCD));
@@ -446,7 +448,6 @@ namespace ams::ldr::hoc::pcv::mariko {
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);
const u32 dyn_self_ref_control = (static_cast<u32>(7605.0 / tCK_avg) + 260) | (table->burst_regs.emc_dyn_self_ref_control & 0xffff0000);
WRITE_PARAM_ALL_REG(table, emc_dyn_self_ref_control, dyn_self_ref_control);
WRITE_PARAM_ALL_REG(table, emc_pdex2wr, pdex2rw);
WRITE_PARAM_ALL_REG(table, emc_pdex2rd, pdex2rw);
@@ -479,8 +480,9 @@ namespace ams::ldr::hoc::pcv::mariko {
WRITE_PARAM_ALL_REG(table, emc_rdv_early_mask, rdv);
WRITE_PARAM_ALL_REG(table, emc_rdv_mask, rdv + 2);
WRITE_PARAM_ALL_REG(table, emc_tr_rdv, rdv);
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24)
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24)
/* TODO: Check this out again at some point. */
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_2, 0x24);
WRITE_PARAM_ALL_REG(table, emc_cmd_brlshft_3, 0x24);
/* This needs some clean up. */
constexpr double MC_ARB_DIV = 4.0;
@@ -498,7 +500,7 @@ namespace ams::ldr::hoc::pcv::mariko {
table->burst_mc_regs.mc_emem_arb_timing_wap2pre = CEIL(tW2P / MC_ARB_DIV) + MC_ARB_SFA;
/* Two consecutive reads between two different dram modules. */
/* Only be above 1 for 8gb ram. */
/* Only above 1 for 8gb ram. */
if (table->burst_mc_regs.mc_emem_arb_timing_r2r > 1) {
table->burst_mc_regs.mc_emem_arb_timing_r2r = CEIL(table->burst_regs.emc_rext / 4) - 1 + MC_ARB_SFA;
}
@@ -652,7 +654,7 @@ namespace ams::ldr::hoc::pcv::mariko {
// Copy unmodified 1600000 table to tmp
std::memcpy(reinterpret_cast<void *>(tmp), reinterpret_cast<void *>(table_max), sizeof(MarikoMtcTable));
/* Adjust timings properly according to the new frequency. */
MemMtcTableAutoAdjust(table_max);
@@ -810,6 +812,10 @@ namespace ams::ldr::hoc::pcv::mariko {
for (auto &entry : patches) {
LOGGING("%s Count: %zu", entry.description, entry.patched_count);
if (R_FAILED(entry.CheckResult())) {
#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)
panic::SmcError(panic::Patch);
#endif
CRASH(entry.description);
}
}

View File

@@ -38,7 +38,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# NACP building is skipped as well.
#---------------------------------------------------------------------------------
APP_TITLE := Horizon OC Monitor
APP_VERSION := 1.3.2+r4-hoc
APP_VERSION := 1.3.2+r4-hoc-r2
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source

View File

@@ -79,14 +79,17 @@ Result sysclkIpcGetAPIVersion(u32* out_ver)
Result sysclkIpcGetVersionString(char* out, size_t len)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out, len}},
);
}
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
{
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext,
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_context, sizeof(SysClkContext)}},
);
}
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
@@ -112,7 +115,7 @@ Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
{
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
);
}
@@ -128,7 +131,7 @@ Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
);
}
@@ -136,7 +139,7 @@ Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
);
}

View File

@@ -583,6 +583,7 @@ static inline uint64_t sysclkValidConfigValue(SysClkConfigValue val, uint64_t in
case HorizonOCConfigValue_DVFSOffset:
case HorizonOCConfigValue_GPUScheduling:
case HorizonOCConfigValue_RAMVoltUsageDisplayMode:
case HorizonOCConfigValue_CpuGovernorMinimumFreq:
return true;
case HorizonOCConfigValue_BatteryChargeCurrent:
return ((input >= 1024) && (input <= 3072)) || !input;

View File

@@ -79,14 +79,17 @@ Result sysclkIpcGetAPIVersion(u32* out_ver)
Result sysclkIpcGetVersionString(char* out, size_t len)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetVersionString,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out, len}},
);
}
Result sysclkIpcGetCurrentContext(SysClkContext* out_context)
{
return serviceDispatchOut(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext, *out_context);
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetCurrentContext,
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_context, sizeof(SysClkContext)}},
);
}
Result sysclkIpcGetProfileCount(u64 tid, u8* out_count)
@@ -112,7 +115,7 @@ Result sysclkIpcSetOverride(SysClkModule module, u32 hz)
Result sysclkIpcGetProfiles(u64 tid, SysClkTitleProfileList* out_profiles)
{
return serviceDispatchIn(&g_sysclkSrv, SysClkIpcCmd_GetProfiles, tid,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_profiles, sizeof(SysClkTitleProfileList)}},
);
}
@@ -128,7 +131,7 @@ Result sysclkIpcSetProfiles(u64 tid, SysClkTitleProfileList* profiles)
Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_GetConfigValues,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_Out },
.buffers = {{out_configValues, sizeof(SysClkConfigValueList)}},
);
}
@@ -136,7 +139,7 @@ Result sysclkIpcGetConfigValues(SysClkConfigValueList* out_configValues)
Result sysclkIpcSetConfigValues(SysClkConfigValueList* configValues)
{
return serviceDispatch(&g_sysclkSrv, SysClkIpcCmd_SetConfigValues,
.buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In },
.buffer_attrs = { SfBufferAttr_HipcAutoSelect | SfBufferAttr_In },
.buffers = {{configValues, sizeof(SysClkConfigValueList)}},
);
}

View File

@@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk
# version control constants
#---------------------------------------------------------------------------------
#TARGET_VERSION := $(shell git describe --dirty --always --tags)
APP_VERSION := 1.0.0
APP_VERSION := 1.0.2
TARGET_VERSION := $(APP_VERSION)
#---------------------------------------------------------------------------------

View File

@@ -0,0 +1,132 @@
{
"Horizon OC Zeus": "Horizon OC Zeus",
"Edit App Profile": "編輯應用配置",
"Advanced": "高級",
"Edit Global Profile": "編輯全局配置",
"Temporary Overrides": "臨時覆蓋",
"Temporary Overrides Reset": "臨時覆蓋 重設",
"Settings": "設置",
"Information": "資訊",
"Enable": "啟用",
"Uncapped Clocks": "解除頻率上限",
"Override Boost Mode": "覆蓋加速模式",
"CPU Max Display Clock": "CPU 最大顯示頻率",
"Thermal Throttle": "溫度節流",
"Thermal Throttle Threshold": "溫度節流閾值",
"Handheld TDP": "掌機模式 TDP",
"Handheld TDP Limit": "掌機模式 TDP 限制",
"Lite TDP Limit": "Lite TDP 限制",
"Enforce Board Limit": "強制主板限制",
"Battery Charge Current": "電池充電電流",
"Display Refresh Rate Changing": "顯示刷新率變更",
"Fix CPU Volt Bug": "修復 CPU 電壓錯誤",
"[cfg] no enum format string": "[cfg] 無枚舉格式字串",
"KIP": "KIP",
"Save KIP Settings": "保存 KIP 設置",
"RAM Settings": "記憶體設定",
"CPU Settings": "CPU 設置",
"GPU Settings": "GPU 設置",
"Experimental": "實驗性功能",
"Charge Current Override": "充電電流覆蓋",
"Disabled": "禁用",
"HP Mode": "高性能模式",
"EMC Max Clock": "EMC 最大頻率",
"EMC VDD2 Voltage": "EMC VDD2 電壓",
"EMC VDDQ Voltage": "EMC VDDQ 電壓",
"DVB Shift": "DVB 偏移",
"Memory Timings": "記憶體時序",
"Memory Latencies": "記憶體延遲",
"t1 tRCD": "t1 tRCD",
"t2 tRP": "t2 tRP",
"t3 tRAS": "t3 tRAS",
"t4 tRRD": "t4 tRRD",
"t5 tRFC": "t5 tRFC",
"t6 tRTW": "t6 tRTW",
"t7 tWTR": "t7 tWTR",
"t8 tREFI": "t8 tREFI",
"Update RAM Timings": "更新記憶體時序",
"\uE150 This feature is EXPERIMENTAL": "\uE150 此功能為實驗性功能",
"and should only be used for testing!": "僅應用於測試!",
"Read Latency": "讀取延遲",
"Write Latency": "寫入延遲",
"CPU UV": "CPU 降壓",
"CPU Unlock": "CPU 解鎖",
"CPU VMIN": "CPU 最低電壓",
"CPU Max Voltage": "CPU 最大電壓",
"CPU UV Table": "CPU 降壓表",
"CPU Low UV": "CPU 低頻降壓",
"CPU High UV": "CPU 高頻降壓",
"CPU Max Clock": "CPU 最大頻率",
"CPU Low VMIN": "CPU 低頻最低電壓",
"CPU High VMIN": "CPU 高頻最低電壓",
"GPU Undervolt Table": "GPU 降壓表",
"Calculate GPU Vmin": "計算 GPU 最低電壓",
"GPU VMIN": "GPU 最低電壓",
"GPU VMAX": "GPU 最大電壓",
"GPU Volt Offset": "GPU 電壓偏移",
"GPU Custom Table": "GPU 自訂表",
"GPU Custom Table (mV)": "GPU 自訂表 (mV)",
"\uE150 Setting GPU Clocks past": "\uE150 將 GPU 頻率設置超過",
"1075MHz without UV, 1152MHz on SLT or ": "無降壓時的 1075MHz、SLT 時的 1152MHz 或",
"1228MHz on HiOPT can cause ": "HiOPT 時的 1228MHz 可能會造成",
"permanent damage to your Switch!": "對您的 Switch 造成永久性損壞!",
"Proceed at your own risk!": "風險自負!",
"921MHz without UV and 960MHz on": "無降壓時的 921MHz 和",
"SLT or HiOPT can cause ": "SLT 或 HiOPT 時的 960MHz 可能會造成",
"Auto": "自動",
"Sleep Mode": "休眠模式",
"Stock": "默認",
"Dev OC": "開發超頻",
"Boost Mode": "加速模式",
"Safe Max": "安全最大值",
"Unsafe Max": "不安全最大值",
"Absolute Max": "絕對最大值",
"Boost Mode & Safe Max": "加速模式 & 安全最大值",
"Official Rating": "官方額定值",
"Default (Mariko)": "默認 (Mariko)",
"Default (Erista)": "默認 (Erista)",
"Rating": "額定值",
"Safe Max (Mariko)": "安全最大值 (Mariko)",
"Safe Max (Erista)": "安全最大值 (Erista)",
"Default": "默認",
"1581MHz Tbreak": "1581MHz Tbreak",
"1683MHz Tbreak": "1683MHz Tbreak",
"Extreme UV Table": "極限降壓表",
"No UV": "不降壓",
"SLT Table": "SLT 表",
"HiOPT Table": "HiOPT 表",
"Power": "功耗",
"Temp": "溫度",
"Voltage": "電壓",
"TDP Threshold": "TDP 閾值",
"Lite TDP Threshold": "Lite TDP 閾值",
"Thermal Throttle Limit": "溫度節流限制",
"1600BL": "1600BL",
"1866BL": "1866BL",
"2133BL": "2133BL",
"BAT": "電池",
"FAN": "風扇",
"DISP": "顯示",
"Board": "主板",
"Skin": "外殼",
"Now": "當前",
"Avg": "平均",
"App ID": "應用 ID",
"Profile": "配置",
"CPU": "CPU",
"GPU": "GPU",
"Memory": "記憶體",
"Display": "顯示",
"Governor": "調速器",
"SOC": "SOC",
"PCB": "PCB",
"PMIC": "PMIC",
"Docked": "底座模式",
"Handheld": "掌機模式",
"Charging": "充電中",
"USB Charger": "USB 充電器",
"PD Charger": "PD 充電器",
"VDD2": "VDD2",
"VDDQ": "VDDQ",
"GPU DVFS": "GPU DVFS"
}

View File

@@ -48,16 +48,11 @@ std::string getVersionString() {
return std::string(buf);
}
// ---------------------------------------------
// AQUATIC BLUE COLORS (4-bit color space)
// ---------------------------------------------
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15); // Deep ocean blue
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15); // Bright aqua cyan
static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15); // Mid aqua
static constexpr tsl::Color dynamicLogoRGB1 = tsl::Color(0, 4, 8, 15);
static constexpr tsl::Color dynamicLogoRGB2 = tsl::Color(7, 15, 15, 15);
static constexpr tsl::Color STATIC_AQUA = tsl::Color(2, 10, 12, 15);
const std::string name = "Horizon OC Zeus";
// ---------------------------------------------
// FULLY ENHANCED ANIMATED LOGO EFFECT
// ---------------------------------------------
static s32 drawDynamicUltraText(
tsl::gfx::Renderer* renderer,
s32 startX,
@@ -68,7 +63,6 @@ static s32 drawDynamicUltraText(
{
static constexpr double cycleDuration = 1.6;
const std::string name = "Horizon OC Zeus";
s32 currentX = startX;
const u64 currentTime_ns = armTicksToNs(armGetSystemTick());
@@ -89,15 +83,9 @@ static s32 drawDynamicUltraText(
double s1 = n * n * (3.0 - 2.0 * n);
double blend = std::clamp(s1, 0.0, 1.0);
// ---------------------------------------------
// Glow Pulse (brightness modulation)
// ---------------------------------------------
double glow = (cos(phase * 1.5) + 1.0) * 0.5;
double brightness = 0.75 + glow * 0.25;
// ---------------------------------------------
// Color interpolation (4-bit!)
// ---------------------------------------------
u8 r = static_cast<u8>(
(dynamicLogoRGB1.r + (dynamicLogoRGB2.r - dynamicLogoRGB1.r) * blend) * brightness
);
@@ -112,21 +100,15 @@ static s32 drawDynamicUltraText(
g = std::clamp<u8>(g, 0, 15);
b = std::clamp<u8>(b, 0, 15);
// ---------------------------------------------
// ZEUS Lightning Flash
// ---------------------------------------------
bool lightning = (fmod(timeNow, 5.0) < 0.15);
if (lightning) {
r = std::min<u8>(r + 4, 15);
g = std::min<u8>(g + 4, 15);
b = std::min<u8>(b + 15, 15); // strong blue spike
b = std::min<u8>(b + 15, 15);
}
tsl::Color color(r, g, b, 15);
// ---------------------------------------------
// Static Position (no vertical wobble)
// ---------------------------------------------
std::string ls(1, letter);
if (useNotificationMethod)
@@ -138,11 +120,7 @@ static s32 drawDynamicUltraText(
return currentX;
}
// ---------------------------------------------
// PRE-DRAW HOOK
// ---------------------------------------------
void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
{
void BaseGui::preDraw(tsl::gfx::Renderer* renderer) {
drawDynamicUltraText(
renderer,
LOGO_X,
@@ -153,9 +131,6 @@ void BaseGui::preDraw(tsl::gfx::Renderer* renderer)
);
}
// ---------------------------------------------
// UI SETUP
// ---------------------------------------------
tsl::elm::Element* BaseGui::createUI()
{
BaseFrame* rootFrame = new BaseFrame(this);
@@ -163,9 +138,6 @@ tsl::elm::Element* BaseGui::createUI()
return rootFrame;
}
// ---------------------------------------------
// LIVE UPDATE
// ---------------------------------------------
void BaseGui::update()
{
this->refresh();

View File

@@ -1292,11 +1292,11 @@ protected:
// });
addConfigButton(KipConfigValue_marikoGpuVmin, "GPU VMIN", ValueRange(0, 0, 0, "0", 1), "GPU VMIN", &thresholdsDisabled, {}, mGpuVoltsVmin, false);
ValueThresholds MgpuVmaxThresholds(800, 850);
ValueThresholds MgpuVmaxThresholds(805, 850);
addConfigButton(
KipConfigValue_marikoGpuVmax,
"GPU Maximum Voltage",
ValueRange(750, 960, 5, "mV", 1),
ValueRange(800, 960, 5, "mV", 1),
"GPU Maximum Voltage",
&MgpuVmaxThresholds,
{},

View File

@@ -145,14 +145,14 @@ ClockManager::ClockManager()
this->context->isDram8GB = Board::IsDram8GB();
Board::SetGpuSchedulingMode((GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling), (GpuSchedulingOverrideMethod)this->config->GetConfigValue(HorizonOCConfigValue_GPUSchedulingMethod));
this->context->gpuSchedulingMode = (GpuSchedulingMode)this->config->GetConfigValue(HorizonOCConfigValue_GPUScheduling);
this->context->isSysDockInstalled = this->sysDockIntegration->getCurrentSysDockState();
this->context->isSaltyNXInstalled = this->saltyNXIntegration->getCurrentSaltyNXState();
if(this->context->isSaltyNXInstalled) {
this->saltyNXIntegration->LoadSaltyNX();
}
threadStart(&cpuGovernorTHREAD);
threadStart(&gpuGovernorTHREAD);
threadStart(&vrrTHREAD);
@@ -635,7 +635,7 @@ void ClockManager::HandleGovernor(uint32_t targetHz) {
effectiveState == GovernorState_Enabled_CpuVrr ||
effectiveState == GovernorState_Enabled_GpuVrr ||
effectiveState == GovernorState_Enabled_Vrr);
isCpuGovernorEnabled = newCpuGovernorState;
isGpuGovernorEnabled = newGpuGovernorState;
isVRREnabled = newVrrGovernorState;
@@ -675,22 +675,18 @@ void ClockManager::DVFSBeforeSet(u32 targetHz) {
void ClockManager::DVFSAfterSet(u32 targetHz) {
s32 dvfsOffset = this->config->GetConfigValue(HorizonOCConfigValue_DVFSOffset);
dvfsOffset = std::max(dvfsOffset, -50);
u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000) + dvfsOffset;
Board::PcvHijackDvfs(vmin);
dvfsOffset = std::max(dvfsOffset, -80);
u32 vmin = Board::GetMinimumGpuVoltage(targetHz / 1000000);
targetHz = this->context->overrideFreqs[SysClkModule_GPU];
if (!targetHz)
{
targetHz = this->config->GetAutoClockHz(this->context->applicationId, SysClkModule_GPU, this->context->profile, false);
if(!targetHz)
targetHz = this->config->GetAutoClockHz(GLOBAL_PROFILE_ID, SysClkModule_GPU, this->context->profile, false);
if (vmin) {
vmin += dvfsOffset;
}
u32 maxHz = this->GetMaxAllowedHz(SysClkModule_GPU, this->context->profile);
u32 nearestHz = this->GetNearestHz(SysClkModule_GPU, targetHz, maxHz);
Board::PcvHijackDvfs(vmin);
if(targetHz) {
if (targetHz) {
Board::SetHz(SysClkModule_GPU, ~0);
Board::SetHz(SysClkModule_GPU, nearestHz);
} else {
@@ -1030,7 +1026,7 @@ bool ClockManager::RefreshContext()
this->context->fps = saltyNXIntegration->GetFPS();
else
this->context->fps = 254; // N/A
return hasChanged;
}

View File

@@ -119,9 +119,15 @@ Result IpcService::ServiceHandlerFunc(void* arg, const IpcServerRequest* r, u8*
break;
case SysClkIpcCmd_GetCurrentContext:
*out_dataSize = sizeof(SysClkContext);
return ipcSrv->GetCurrentContext((SysClkContext*)out_data);
if(r->data.size >= sizeof(std::uint64_t) && r->hipc.meta.num_recv_buffers >= 1)
{
size_t bufSize = hipcGetBufferSize(r->hipc.data.recv_buffers);
if(bufSize >= sizeof(SysClkContext))
{
return ipcSrv->GetCurrentContext((SysClkContext*)hipcGetBufferAddress(r->hipc.data.recv_buffers));
}
}
break;
case SysClkIpcCmd_Exit:
return ipcSrv->Exit();

168
dist/README.md vendored Normal file
View File

@@ -0,0 +1,168 @@
<div align="center">
<img src="assets/logo.png" alt="logo" width="768"/>
---
![License: GPL-2.0](https://img.shields.io/badge/GPL--2.0-red?style=for-the-badge)
![Nintendo Switch](https://img.shields.io/badge/Nintendo_Switch-E60012?style=for-the-badge\&logo=nintendo-switch\&logoColor=white)
[![Discord](https://img.shields.io/badge/Discord-5865F2?style=for-the-badge\&logo=discord\&logoColor=white)](https://dsc.gg/horizonoc)
![VSCode](https://img.shields.io/badge/VSCode-0078D4?style=for-the-badge\&logo=visual%20studio%20code\&logoColor=white)
![Made with Notepad++](assets/np++.png?raw=true)
![C++](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge\&logo=c%2B%2B\&logoColor=white)
![Downloads](https://img.shields.io/github/downloads/souldbminersmwc/Horizon-OC/total.svg?style=for-the-badge)
---
</div>
## ⚠️ Disclaimer
> **THIS TOOL CAN BE DANGEROUS IF MISUSED. PROCEED WITH CAUTION.**
> Due to the design of Horizon OS, **overclocking RAM can cause NAND OR SD CORRUPTION.**
> Ensure you have a **full NAND, PROINFO, EMUMMC and SD backup** before proceeding.
---
## About
**Horizon OC** is an open-source overclocking tool for Nintendo Switch consoles running **Atmosphere custom firmware**.
It enables advanced CPU, GPU, and RAM tuning with user-friendly configuration tools.
---
## Features
* **CPU:** Up to 1963MHz (Mariko) / 1785MHz (Erista)
* **GPU:** Up to 1075MHz (Mariko) / 921MHz (Erista)
* **RAM:** Up to 1866/2133MHz (Mariko) / 1600MHz (Erista)
* Over/undervolting support
* Built-in configurator
* Compatible with most homebrew
> It is recommended to read the [guide](https://rentry.co/howtoget60fps) before proceeding, as this can help you get a *significant* performance boost over the default settings, often times with less power draw and heat output
---
## Installation
1. Ensure you have the latest versions of
* [Atmosphere](https://github.com/Atmosphere-NX/Atmosphere)
* [Ultrahand Overlay](https://github.com/ppkantorski/Ultrahand-Overlay)
2. Download and extract the **Horizon OC Package** to the root of your SD card.
3. If using **Hekate**, edit `hekate_ipl.ini` to include:
```
kip1=atmosphere/kips/hoc.kip
```
*(No changes needed if using fusee.)*
---
## Configuration
1. Open the Horizon OC Overlay
2. Open the settings menu
3. Adjust your overclocking settings as desired. A helpful guide can be found [here.](https://rentry.co/mariko#oc-settings-for-horizon-oc)
4. Click **Save KIP Settings** to apply your configuration.
---
## Building from Source
Refer to COMPILATION.md
---
## Clock table
### MEM clocks
* 3200 → max on mariko, JEDEC.
* 2933 → JEDEC.
* 2666 → JEDEC.
* 2400 → max on erista, JEDEC.
* 2133 → mariko safe max (4266 Modules), JEDEC.
* 1996 → JEDEC.
* 1866 → mariko safe max (3733 Modules), JEDEC.
* 1600 → official docked, boost mode, erista safe max, JEDEC.
* 1331 → official handheld, JEDEC.
* 1065
* 800
* 665
### CPU clocks
* 2601 → mariko absolute max, very dangerous
* 2499
* 2397 → mariko safe max with UV (low speedo)
* 2295
* 2193
* 2091
* 1963 → mariko no UV max clock
* 1887
* 1785 → erista no UV max clock, boost mode
* 1683
* 1581
* 1428
* 1326
* 1224 → sdev oc
* 1122
* 1020 → official docked & handheld
* 918
* 816
* 714
* 612 → sleep mode
**Notes:**
1. On Erista, CPU in handheld is capped to 1581MHz
### GPU clocks
* 1536 → absolute max clock on mariko. very dangerous
* 1459
* 1382
* 1305
* 1267 → NVIDIA T214 rating
* 1228 → mariko HiOPT safe clock
* 1152 → mariko SLT max clock
* 1075 → mariko no UV max clock. absolute max clock on erista. very dangerous
* 998 → NVIDIA T210 rating
* 960 (erista only) → erista slt/hiopt safe max clock
* 921 → erista no UV max clock
* 844
* 768 → official docked
* 691
* 614
* 537
* 460 → max handheld
* 384 → official handheld
* 307 → official handheld
* 230
* 153
* 76 → boost mode
**Notes:**
1. GPU overclock is capped at 460MHz on erista in handheld
2. On Mariko, cap with No uv is 614MHz, with SLT it is 691MHz and with HiOPT it's 768MHz
3. Clocks higher than 768MHz on erista need the official charger is plugged in.
4. On Mariko, cap with No uv is 844MHz, with SLT it is 921MHz and with HiOPT it's 998MHz
---
## Credits
* **Lightos's Cat** - Cat
* **Souldbminer** hoc-clk and loader development
* **Lightos** loader patches development
* **SciresM** - Atmosphere CFW
* **CTCaer** - L4T, Hekate, perfect ram timings
* **KazushiMe** Switch OC Suite
* **hanai3bi (meha)** Switch OC Suite, EOS, sys-clk-eos
* **NaGaa95** L4T-OC-kernel
* **B3711 (halop)** EOS
* **sys-clk team (m4xw, p-sam, natinusala)** sys-clk
* **b0rd2death** Ultrahand sys-clk & Status Monitor fork
* **MasaGratoR and ZachyCatGames** - General help
* **MasaGratoR** - Status Monitor & Display Refresh Rate Driver
* **Dom, Samybigio, Arcdelta, Miki, Happy, Flopsider, Winnerboi77, Blaise, Alvise, TDRR, agjeococh, frost, letum00 and Xenshen** - Testing
* **Samybigio2011** - Italian translations

Binary file not shown.

View File

@@ -0,0 +1,132 @@
{
"Horizon OC Zeus": "Horizon OC Zeus",
"Edit App Profile": "編輯應用配置",
"Advanced": "高級",
"Edit Global Profile": "編輯全局配置",
"Temporary Overrides": "臨時覆蓋",
"Temporary Overrides Reset": "臨時覆蓋 重設",
"Settings": "設置",
"Information": "資訊",
"Enable": "啟用",
"Uncapped Clocks": "解除頻率上限",
"Override Boost Mode": "覆蓋加速模式",
"CPU Max Display Clock": "CPU 最大顯示頻率",
"Thermal Throttle": "溫度節流",
"Thermal Throttle Threshold": "溫度節流閾值",
"Handheld TDP": "掌機模式 TDP",
"Handheld TDP Limit": "掌機模式 TDP 限制",
"Lite TDP Limit": "Lite TDP 限制",
"Enforce Board Limit": "強制主板限制",
"Battery Charge Current": "電池充電電流",
"Display Refresh Rate Changing": "顯示刷新率變更",
"Fix CPU Volt Bug": "修復 CPU 電壓錯誤",
"[cfg] no enum format string": "[cfg] 無枚舉格式字串",
"KIP": "KIP",
"Save KIP Settings": "保存 KIP 設置",
"RAM Settings": "記憶體設定",
"CPU Settings": "CPU 設置",
"GPU Settings": "GPU 設置",
"Experimental": "實驗性功能",
"Charge Current Override": "充電電流覆蓋",
"Disabled": "禁用",
"HP Mode": "高性能模式",
"EMC Max Clock": "EMC 最大頻率",
"EMC VDD2 Voltage": "EMC VDD2 電壓",
"EMC VDDQ Voltage": "EMC VDDQ 電壓",
"DVB Shift": "DVB 偏移",
"Memory Timings": "記憶體時序",
"Memory Latencies": "記憶體延遲",
"t1 tRCD": "t1 tRCD",
"t2 tRP": "t2 tRP",
"t3 tRAS": "t3 tRAS",
"t4 tRRD": "t4 tRRD",
"t5 tRFC": "t5 tRFC",
"t6 tRTW": "t6 tRTW",
"t7 tWTR": "t7 tWTR",
"t8 tREFI": "t8 tREFI",
"Update RAM Timings": "更新記憶體時序",
"\uE150 This feature is EXPERIMENTAL": "\uE150 此功能為實驗性功能",
"and should only be used for testing!": "僅應用於測試!",
"Read Latency": "讀取延遲",
"Write Latency": "寫入延遲",
"CPU UV": "CPU 降壓",
"CPU Unlock": "CPU 解鎖",
"CPU VMIN": "CPU 最低電壓",
"CPU Max Voltage": "CPU 最大電壓",
"CPU UV Table": "CPU 降壓表",
"CPU Low UV": "CPU 低頻降壓",
"CPU High UV": "CPU 高頻降壓",
"CPU Max Clock": "CPU 最大頻率",
"CPU Low VMIN": "CPU 低頻最低電壓",
"CPU High VMIN": "CPU 高頻最低電壓",
"GPU Undervolt Table": "GPU 降壓表",
"Calculate GPU Vmin": "計算 GPU 最低電壓",
"GPU VMIN": "GPU 最低電壓",
"GPU VMAX": "GPU 最大電壓",
"GPU Volt Offset": "GPU 電壓偏移",
"GPU Custom Table": "GPU 自訂表",
"GPU Custom Table (mV)": "GPU 自訂表 (mV)",
"\uE150 Setting GPU Clocks past": "\uE150 將 GPU 頻率設置超過",
"1075MHz without UV, 1152MHz on SLT or ": "無降壓時的 1075MHz、SLT 時的 1152MHz 或",
"1228MHz on HiOPT can cause ": "HiOPT 時的 1228MHz 可能會造成",
"permanent damage to your Switch!": "對您的 Switch 造成永久性損壞!",
"Proceed at your own risk!": "風險自負!",
"921MHz without UV and 960MHz on": "無降壓時的 921MHz 和",
"SLT or HiOPT can cause ": "SLT 或 HiOPT 時的 960MHz 可能會造成",
"Auto": "自動",
"Sleep Mode": "休眠模式",
"Stock": "默認",
"Dev OC": "開發超頻",
"Boost Mode": "加速模式",
"Safe Max": "安全最大值",
"Unsafe Max": "不安全最大值",
"Absolute Max": "絕對最大值",
"Boost Mode & Safe Max": "加速模式 & 安全最大值",
"Official Rating": "官方額定值",
"Default (Mariko)": "默認 (Mariko)",
"Default (Erista)": "默認 (Erista)",
"Rating": "額定值",
"Safe Max (Mariko)": "安全最大值 (Mariko)",
"Safe Max (Erista)": "安全最大值 (Erista)",
"Default": "默認",
"1581MHz Tbreak": "1581MHz Tbreak",
"1683MHz Tbreak": "1683MHz Tbreak",
"Extreme UV Table": "極限降壓表",
"No UV": "不降壓",
"SLT Table": "SLT 表",
"HiOPT Table": "HiOPT 表",
"Power": "功耗",
"Temp": "溫度",
"Voltage": "電壓",
"TDP Threshold": "TDP 閾值",
"Lite TDP Threshold": "Lite TDP 閾值",
"Thermal Throttle Limit": "溫度節流限制",
"1600BL": "1600BL",
"1866BL": "1866BL",
"2133BL": "2133BL",
"BAT": "電池",
"FAN": "風扇",
"DISP": "顯示",
"Board": "主板",
"Skin": "外殼",
"Now": "當前",
"Avg": "平均",
"App ID": "應用 ID",
"Profile": "配置",
"CPU": "CPU",
"GPU": "GPU",
"Memory": "記憶體",
"Display": "顯示",
"Governor": "調速器",
"SOC": "SOC",
"PCB": "PCB",
"PMIC": "PMIC",
"Docked": "底座模式",
"Handheld": "掌機模式",
"Charging": "充電中",
"USB Charger": "USB 充電器",
"PD Charger": "PD 充電器",
"VDD2": "VDD2",
"VDDQ": "VDDQ",
"GPU DVFS": "GPU DVFS"
}

Binary file not shown.

Binary file not shown.