diff --git a/.gitmodules b/.gitmodules index 042cae6d..9a12ac22 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,3 @@ -[submodule "Source/Horizon-OC-Monitor/lib/Atmosphere-libs"] - path = Source/Horizon-OC-Monitor/lib/Atmosphere-libs - url = https://git.niklascfw.de/OmniNX/Atmosphere-Pro.git - branch = boot-storage -[submodule "Source/Horizon-OC-Monitor/lib/libultrahand"] - path = Source/Horizon-OC-Monitor/lib/libultrahand - url = https://github.com/ppkantorski/libultrahand - branch = main [submodule "Source/hoc-clk/overlay/lib/libultrahand"] path = Source/hoc-clk/overlay/lib/libultrahand url = https://github.com/ppkantorski/libultrahand diff --git a/COMPILING.md b/COMPILING.md index db76339a..78b7331b 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -5,3 +5,4 @@ Horizon OC Compilation Instructions 3. Clone the Horizon OC develop branch (``git clone https://github.com/Horizon-OC/Horizon-OC.git --recurse-submodules``) 4. Run ``./build.sh`` in the root directory - If you want to compile with extensions, append ``--ext`` + - Installing ``ccache`` is optional; if it is on your PATH it is used automatically to speed up rebuilds diff --git a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp index 7ecbf857..0baf0b56 100644 --- a/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/ldr_process_creation.cpp @@ -567,6 +567,11 @@ namespace ams::ldr { out->nso_size[i] = std::max(out->nso_size[i], rw_end); out->nso_size[i] += static_cast(ctx.headers[i].bss_size); + /* Reserve hook arena memory past pcv's bss. */ + if (g_is_pcv && i == ctx.main_nso_idx) { + out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize) + hoc::pcv::PcvDataArenaSize; + } + const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1); R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso()); R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso()); @@ -691,6 +696,9 @@ namespace ams::ldr { Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, size_t map_size) { const bool is_zstd = (nso_header->flags & NsoHeader::Flag_UseZbicCompression) != 0; + const size_t module_size = static_cast(nso_header->rw_dst_offset) + util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize); + const size_t arena_size = (nso_size > module_size) ? (nso_size - module_size) : 0; + /* Map and read data from file. */ { /* Map the process memory. */ @@ -730,7 +738,15 @@ namespace ams::ldr { /* Apply PCV and PTM patches */ if (g_is_pcv) { - hoc::pcv::Patch(map_address, nso_size); + const size_t text_end = static_cast(nso_header->text_size); + const size_t rx_end = util::AlignUp(text_end, os::MemoryPageSize); + const size_t ro_start = static_cast(nso_header->ro_dst_offset); + const size_t cave_end = (rx_end < ro_start) ? rx_end : ro_start; + const uintptr_t cave = map_address + text_end; + const size_t cave_size = (cave_end > text_end) ? (cave_end - text_end) : 0; + + /* module_size is used rather than nso_size to exclude the extra data section. */ + hoc::pcv::Patch(map_address, module_size, cave, cave_size, nso_address, arena_size ? (map_address + module_size) : 0); } if (g_is_ptm) { @@ -741,7 +757,7 @@ namespace ams::ldr { /* Set permissions. */ const size_t text_size = util::AlignUp(nso_header->text_size, os::MemoryPageSize); const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize); - const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize); + const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize) + arena_size; if (text_size) { const bool prevent_code_reads = (nso_header->flags & NsoHeader::Flag_PreventCodeReads); R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute)); @@ -770,8 +786,7 @@ namespace ams::ldr { const bool is_zstd = (ctx.headers[i].flags & NsoHeader::Flag_UseZbicCompression) != 0; const size_t map_size = is_zstd ? (total_end - process_info->nso_address[i]) : process_info->nso_size[i]; - R_TRY(LoadAutoLoadModule(process_info->process_handle, file, ctx.headers + i, - process_info->nso_address[i], process_info->nso_size[i], map_size)); + R_TRY(LoadAutoLoadModule(process_info->process_handle, file, ctx.headers + i, process_info->nso_address[i], process_info->nso_size[i], map_size)); } /* Load arguments, if present. */ @@ -798,6 +813,12 @@ namespace ams::ldr { } Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const AutoLoadModuleContext &ctx, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) { + /* Append extra .bss for 64LUT */ + /* TODO: REMOVE THIS. */ + if (g_is_pcv && ctx.main_nso_idx >= 0) { + g_nso_headers[ctx.main_nso_idx].bss_size += static_cast(hoc::pcv::HocPcvScratchSize); + } + /* Get CreateProcessParameter. */ svc::CreateProcessParameter param; R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit)); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp index 23523a89..5ba70a8f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.cpp @@ -38,11 +38,11 @@ volatile CustomizeTable C = { .commonEmcMemVolt = 1175000, /* LPDDR4(X) JEDEC Specification */ .eristaEmcMaxClock = 1600000, /* Maximum HB-MGCH ram rating */ -/* Available: 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */ +/* Available: 33MHz step rate, 66MHz step rate, 100MHz step rate, 133MHz step rate and jedec. */ /* Jedec freqs are 1333MHz, 1600MHz, 1866MHz, 2133MHz, 2400MHz, 2666MHz, 2933MHz, 3200MHz. */ -.stepMode = StepMode_66MHz, +.stepMode = StepMode_33MHz, -.marikoEmcMaxClock = 2133000, /* 1866MHz @ 1866tWRL is guaranteed to work on all Mariko units */ +.marikoEmcMaxClock = 2133000, /* Requires the EMC DVFS 63-entry patches (EMC DVFS Count / EMC SoC LUT). */ .marikoEmcVddqVolt = 600000, .emcDvbShift = 0, diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp index d81ac89c..a898e20b 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/customize.hpp @@ -20,8 +20,8 @@ #pragma once -#define CUST_REV 6 -#define KIP_VERSION 250 +#define CUST_REV 7 +#define KIP_VERSION 300 #include "oc_common.hpp" #include "pcv/pcv_common.hpp" @@ -40,6 +40,7 @@ enum StepMode: u32 { StepMode_100MHz = 1, StepMode_Jedec = 2, StepMode_133MHz = 3, + StepMode_33MHz = 4, }; enum ReadLatency: u32 { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp index 54fe7b7b..9440aaf8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_common.hpp @@ -20,36 +20,80 @@ #include #include -#define LOGGING(fmt, ...) ((void)0) -#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); } + + +#ifndef HOC_UART_LOG +#define HOC_UART_LOG 0 +#endif + +#if HOC_UART_LOG && !(defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)) + #undef HOC_UART_LOG + #define HOC_UART_LOG 0 +#endif + +#define HOC_PCV_NVLOG_PATCH 1 +#define HOC_PCV_FORCE_VERBOSITY 1 #include "customize.hpp" #include "oc_log.hpp" +#define HOC_IRAM_LOG 0 + +#if (!HOC_UART_LOG) && (defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING)) + #undef HOC_IRAM_LOG + #define HOC_IRAM_LOG 1 +#endif + +#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) + + #if HOC_IRAM_LOG + #define LOGGING(...) Log(__VA_ARGS__) + #elif HOC_UART_LOG + #define LOGGING(fmt, ...) AMS_LOG(fmt "\n", ##__VA_ARGS__) + #endif + +#else + #define LOGGING(...) ((void)0) +#endif + +#define CRASH(msg, ...) { ams::diag::AbortImpl(msg, __PRETTY_FUNCTION__, "", 0); __builtin_unreachable(); } + #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); - R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002); - R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003); - R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004); - R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005); - R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006); - R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007); - R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008); - R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009); - R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010); - R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011); - R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012); - R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013); - R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014); - R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015); - R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016); - R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017); - R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018); + R_DEFINE_ERROR_RESULT(OutOfRange, 1000); + R_DEFINE_ERROR_RESULT(InvalidMemPllmEntry, 1001); + R_DEFINE_ERROR_RESULT(InvalidMtcMagic, 1002); + R_DEFINE_ERROR_RESULT(InvalidMtcTable, 1003); + R_DEFINE_ERROR_RESULT(InvalidDvbTable, 1004); + R_DEFINE_ERROR_RESULT(InvalidCpuFreqVddEntry, 1005); + R_DEFINE_ERROR_RESULT(InvalidCpuVoltDfllEntry, 1006); + R_DEFINE_ERROR_RESULT(InvalidCpuDvfs, 1007); + R_DEFINE_ERROR_RESULT(InvalidCpuMinVolt, 1008); + R_DEFINE_ERROR_RESULT(InvalidGpuDvfs, 1009); + R_DEFINE_ERROR_RESULT(InvalidGpuFreqMaxPattern, 1010); + R_DEFINE_ERROR_RESULT(InvalidGpuPllEntry, 1011); + R_DEFINE_ERROR_RESULT(InvalidRegulatorEntry, 1012); + R_DEFINE_ERROR_RESULT(UninitializedPatcher, 1013); + R_DEFINE_ERROR_RESULT(UnsuccessfulPatcher, 1014); + R_DEFINE_ERROR_RESULT(SafetyCheckFailure, 1015); + R_DEFINE_ERROR_RESULT(InvalidMtcTablePattern, 1016); + R_DEFINE_ERROR_RESULT(InvalidSocVoltPattern, 1017); + R_DEFINE_ERROR_RESULT(InvalidSocVoltLimit, 1018); + R_DEFINE_ERROR_RESULT(InvalidEmcDvfsCount, 1019); + R_DEFINE_ERROR_RESULT(InvalidEmcSocLut, 1020); + R_DEFINE_ERROR_RESULT(InvalidEmcRateList, 1021); + R_DEFINE_ERROR_RESULT(InvalidNvLogRedirect, 1022); + R_DEFINE_ERROR_RESULT(InvalidBusFreqReloc, 1023); + R_DEFINE_ERROR_RESULT(HookArenaOutOfMemory, 1024); + R_DEFINE_ERROR_RESULT(HookPayloadTooLarge, 1025); + R_DEFINE_ERROR_RESULT(HookRelocationUnsupported, 1026); + R_DEFINE_ERROR_RESULT(HookSiteInvalid, 1027); + R_DEFINE_ERROR_RESULT(HookPayloadEscapes, 1028); + R_DEFINE_ERROR_RESULT(HookDataOutOfMemory, 1029); + R_DEFINE_ERROR_RESULT(HookUnavailable, 1030); } namespace ams::ldr::hoc { @@ -64,6 +108,7 @@ namespace ams::ldr::hoc { patternFn pattern_search_fn = nullptr; Pointer value_search; size_t patched_count = 0; + bool optional = false; Result Apply(Pointer *ptr) { Result res = patcher_fn(ptr); @@ -94,7 +139,7 @@ namespace ams::ldr::hoc { } Result CheckResult() { - R_UNLESS(patched_count > 0, ldr::ResultUnsuccessfulPatcher()); + R_UNLESS(optional || patched_count > 0, ldr::ResultUnsuccessfulPatcher()); if (maximum_patched_count) { R_UNLESS(patched_count <= maximum_patched_count, ldr::ResultUnsuccessfulPatcher()); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp index 660b1f50..6fccc8e4 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.cpp @@ -19,7 +19,7 @@ #include "oc_common.hpp" -#if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) +#if HOC_IRAM_LOG #include "fatal_handler_bin.h" #endif @@ -70,6 +70,32 @@ namespace ams::ldr::hoc { return rc; } + #if HOC_UART_LOG + /* Remove this? */ + void UartLog(const char *fmt, ...) { + char line[256]; + constexpr size_t PrefixLen = 13; /* "[HOC] " */ + std::memcpy(line, "[Horizon OC] ", PrefixLen); + + va_list args; + va_start(args, fmt); + int n = vsnprintf(line + PrefixLen, sizeof(line) - PrefixLen - 1, fmt, args); + va_end(args); + if (n < 0) { + return; + } + + size_t body = static_cast(n); + if (body > sizeof(line) - PrefixLen - 2) { /* vsnprintf returns the untruncated length */ + body = sizeof(line) - PrefixLen - 2; + } + size_t len = PrefixLen + body; + line[len++] = '\n'; + + svc::OutputDebugString(line, len); + } + #endif + struct log_ctx_t { u32 magic; u32 sz; @@ -81,7 +107,7 @@ namespace ams::ldr::hoc { #define IRAM_LOG_CTX_ADDR 0x4003C000 #define IRAM_LOG_MAX_SZ 4096 - #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) + #if HOC_IRAM_LOG void Log(const char *data, ...) { static const u32 max_log_sz = sizeof(working_buf) - sizeof(log_ctx_t); static bool initDone = false; @@ -112,8 +138,8 @@ namespace ams::ldr::hoc { } #endif - #if defined(AMS_BUILD_FOR_AUDITING) || defined(AMS_BUILD_FOR_DEBUGGING) void ViewLog() { + #if HOC_IRAM_LOG if (spl::GetSocType() == spl::SocType_Mariko) { return; } @@ -127,6 +153,6 @@ namespace ams::ldr::hoc { SmcRebootToIramPayload(); while(true) { } + #endif } - #endif } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp index 8f5dadb2..6522ee13 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/oc_log.hpp @@ -25,4 +25,8 @@ namespace ams::ldr::hoc { void Log(const char *data, ...); void ViewLog(); + /* Emit a formatted line over the kernel debug UART via svcOutputDebugString. + No-op unless the running kernel enables debug logging (audit/debug mesosphere). */ + void UartLog(const char *fmt, ...) __attribute__((format(printf, 1, 2))); + } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp similarity index 99% rename from Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp index a8106726..fc6ae8f8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.cpp @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -#include "../mtc_timing_value.hpp" +#include "../../mtc_timing_value.hpp" namespace ams::ldr::hoc::pcv::erista { diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp similarity index 100% rename from Source/Atmosphere/stratosphere/loader/source/oc/erista/calculate_timings_erista.hpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/calculate_timings_erista.hpp diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.cpp new file mode 100644 index 00000000..742776cb --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#include "../pcv.hpp" +#include "../../mtc_timing_value.hpp" +#include "pcv_erista_cpu.hpp" +#include "pcv_erista_gpu.hpp" +#include "pcv_erista_mtc.hpp" +#include "calculate_timings_erista.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + DEFINE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData); + + u32 *nsoStart; + + Result InstallHooks() { + R_TRY(Hooks().CheckEnabled()); + + R_TRY(Hooks().CopyPayload()); + + auto *data = Hooks().BindData(e_HookPayloadData); + R_UNLESS(data != nullptr, ldr::ResultHookDataOutOfMemory()); + + R_TRY(MtcInstallHooks(data)); + + R_SUCCEED(); + } + + void Patch(uintptr_t mapped_nso, size_t nso_size) { + nsoStart = reinterpret_cast(mapped_nso); + MtcGenerateFreqTables(); + + u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); + u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); + + PatcherEntry patches[] = { + {"CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq }, + {"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial }, + {"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial }, + {"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low }, + {"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial }, + {"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial }, + {"GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, + {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, + {"GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, + // {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, + {"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn }, + {"MEM Freq Mtc", &MemFreqMtcTable, 1, 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; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) { + u32 *ptr32 = reinterpret_cast(ptr); + for (auto &entry : patches) { + if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) { + break; + } + } + } + + for (auto &entry : patches) { + LOGGING("%s Count: %zu", entry.description, entry.patched_count); + if (R_FAILED(entry.CheckResult())) { + panic::SmcError(panic::Patch); + + CRASH(entry.description); + } + } + + if (R_FAILED(InstallHooks())) { + panic::SmcError(panic::Patch); + } + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.hpp new file mode 100644 index 00000000..8a530486 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista.hpp @@ -0,0 +1,42 @@ +/* + * 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 . + */ + +#pragma once + +#include "../../oc_common.hpp" +#include "../pcv_common.hpp" +#include "../pcv_asm.hpp" +#include "../pcv_hook.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + struct HookPayloadData { + struct { + EristaMtcTable *mtcTable; + u32 mtcCount; + } mtcTableAsm; + }; + DECLARE_HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData); + + extern u32 *nsoStart; + + void Patch(uintptr_t mapped_nso, size_t nso_size); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.cpp new file mode 100644 index 00000000..8fddf0af --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#include "../pcv.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + Result CpuVoltDvfs(u32 *ptr) { + if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) { + R_THROW(ldr::ResultInvalidCpuMinVolt()); + } + + if (C.eristaCpuVmin) { + PATCH_OFFSET(ptr, C.eristaCpuVmin); + } + + if (C.eristaCpuUV) { + PATCH_OFFSET(ptr - 2, C.eristaCpuVmin); + } + + if (C.eristaCpuMaxVolt) { + PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt); + } + + R_SUCCEED(); + } + + Result CpuVoltThermals(u32 *ptr) { + if (std::memcmp(ptr - 6, cpuVoltageThermalPattern, sizeof(cpuVoltageThermalPattern))) { + R_THROW(ldr::ResultInvalidCpuMinVolt()); + } + + if (C.eristaCpuVmin) { + PATCH_OFFSET( ptr, C.eristaCpuVmin); + PATCH_OFFSET(ptr + 3, C.eristaCpuVmin); + PATCH_OFFSET(ptr + 6, C.eristaCpuVmin); + } + + if (C.eristaCpuMaxVolt) { + PATCH_OFFSET(ptr - 2, C.eristaCpuMaxVolt); + PATCH_OFFSET(ptr + 1, C.eristaCpuMaxVolt); + PATCH_OFFSET(ptr + 4, C.eristaCpuMaxVolt); + PATCH_OFFSET(ptr + 7, C.eristaCpuMaxVolt); + } + + R_SUCCEED(); + } + + Result CpuVoltDfll(u32* ptr) { + CvbCpuDfllData *entry = reinterpret_cast(ptr); + + R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); + + if (!C.eristaCpuUV) { + R_SKIP(); + } + + switch (C.eristaCpuUV) { + case 1: + PATCH_OFFSET(&(entry->tune0_high), 0xffff); + PATCH_OFFSET(&(entry->tune1_high), 0x27007ff); + break; + case 2: + PATCH_OFFSET(&(entry->tune0_high), 0xefff); + PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); + break; + case 3: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); + break; + case 4: + PATCH_OFFSET(&(entry->tune0_high), 0xdfdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27a07ff); + break; + case 5: + PATCH_OFFSET(&(entry->tune0_high), 0xcfdf); + PATCH_OFFSET(&(entry->tune1_high), 0x37007ff); + break; + default: + break; + } + + R_SUCCEED(); + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.hpp new file mode 100644 index 00000000..e028ff54 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_cpu.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#pragma once +#include "pcv_erista.hpp" + +namespace ams::ldr::hoc::pcv::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 CpuVoltOfficial = 1227; + constexpr u32 CpuVminOfficial = 825; + constexpr u32 CpuTune0Low = 0xFFEAD0FF; + + constexpr u32 CpuVoltL4T = 1257'000; + + static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 }; + static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "Invalid cpuVoltDvfsPattern size"); + + static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 }; + static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "Invalid cpuVoltageThermalPattern size"); + + + Result CpuVoltDvfs(u32 *ptr); + Result CpuVoltThermals(u32 *ptr); + Result CpuVoltDfll(u32* ptr); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.cpp new file mode 100644 index 00000000..937af7d7 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#include "../pcv.hpp" +#include "../pcv_asm.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + Result GpuVoltDVFS(u32 *ptr) { + if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + + if (C.eristaGpuVmin) { + PATCH_OFFSET(ptr, C.eristaGpuVmin); + } + + R_SUCCEED(); + } + + Result GpuVoltThermals(u32 *ptr) { + if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + + if (C.eristaGpuVmin) { + PATCH_OFFSET(ptr, C.eristaGpuVmin); + PATCH_OFFSET(ptr + 3, C.eristaGpuVmin); + PATCH_OFFSET(ptr + 6, C.eristaGpuVmin); + PATCH_OFFSET(ptr + 9, C.eristaGpuVmin); + PATCH_OFFSET(ptr + 12, C.eristaGpuVmin); + } + + R_SUCCEED(); + } + + Result GpuFreqMaxAsm(u32 *ptr32) { + // Check if both two instructions match the pattern + u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); + if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) { + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } + + // Both instructions should operate on the same register + u8 rd = asm_get_rd(ins1); + if (rd != asm_get_rd(ins2)) { + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } + + u32 max_clock; + switch (C.eristaGpuUV) { + case 0: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; + break; + case 1: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq; + break; + case 2: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq; + break; + default: + max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; + break; + } + + u32 asm_patch[2] = { + asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), + asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd) + }; + + PATCH_OFFSET(ptr32, asm_patch[0]); + PATCH_OFFSET(ptr32 + 1, asm_patch[1]); + + R_SUCCEED(); + } + + Result GpuFreqPllMax(u32 *ptr) { + clk_pll_param *entry = reinterpret_cast(ptr); + + // All zero except for freq + for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) { + R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); + } + + // Double the max clk simply + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; + R_SUCCEED(); + } + + // patch out 1305MHz limit on erista, don't use this! + // Result GpuFreqPllLimit(u32 *ptr) { + // u32 prev_freq = *(ptr - 1); + + // if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) { + // R_THROW(ldr::ResultInvalidGpuPllEntry()); + // } + + // PATCH_OFFSET(ptr, 3600000); + + // R_SUCCEED(); + // } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.hpp new file mode 100644 index 00000000..cc964628 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_gpu.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#pragma once + +#include "pcv_erista.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + constexpr u32 GpuClkPllLimit = 2'600'000; + constexpr u32 GpuClkPllMax = 921'600'000; + constexpr u32 GpuVminOfficial = 810; + + static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, }; + static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern"); + + static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 }; + static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "Invalid gpuVoltageThermalPattern size"); + + /* 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 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 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 }; + + inline bool GpuMaxClockPatternFn(u32 *ptr32) { + return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]); + }; + + 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, } }, + { }, + }; + + Result GpuVoltDVFS(u32 *ptr); + Result GpuVoltThermals(u32 *ptr); + Result GpuFreqMaxAsm(u32 *ptr32); + Result GpuFreqPllMax(u32 *ptr); + + // patch out 1305MHz limit on erista, don't use this! + // Result GpuFreqPllLimit(u32 *ptr); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp similarity index 64% rename from Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp index d2460db3..6b2d4321 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.cpp @@ -21,163 +21,20 @@ */ #include -#include "pcv.hpp" -#include "../mtc_timing_value.hpp" -#include "../erista/calculate_timings_erista.hpp" +#include "../pcv.hpp" +#include "../../mtc_timing_value.hpp" +#include "calculate_timings_erista.hpp" namespace ams::ldr::hoc::pcv::erista { - std::vector newEmcList; - u32 *nsoStart; - u32 *nsoEnd; + namespace { + std::vector newEmcList; - Result CpuVoltDvfs(u32 *ptr) { - if (std::memcmp(ptr + 5, cpuVoltDvfsPattern, sizeof(cpuVoltDvfsPattern))) { - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } - - if (C.eristaCpuVmin) { - PATCH_OFFSET(ptr, C.eristaCpuVmin); - } - - if (C.eristaCpuUV) { - PATCH_OFFSET(ptr - 2, C.eristaCpuVmin); - } - - if (C.eristaCpuMaxVolt) { - PATCH_OFFSET(ptr + 5, C.eristaCpuMaxVolt); - } - - R_SUCCEED(); - } - - Result CpuVoltThermals(u32 *ptr) { - if (std::memcmp(ptr - 6, cpuVoltageThermalPattern, sizeof(cpuVoltageThermalPattern))) { - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } - - if (C.eristaCpuVmin) { - PATCH_OFFSET( ptr, C.eristaCpuVmin); - PATCH_OFFSET(ptr + 3, C.eristaCpuVmin); - PATCH_OFFSET(ptr + 6, C.eristaCpuVmin); - } - - if (C.eristaCpuMaxVolt) { - PATCH_OFFSET(ptr - 2, C.eristaCpuMaxVolt); - PATCH_OFFSET(ptr + 1, C.eristaCpuMaxVolt); - PATCH_OFFSET(ptr + 4, C.eristaCpuMaxVolt); - PATCH_OFFSET(ptr + 7, C.eristaCpuMaxVolt); - } - - R_SUCCEED(); - } - - Result CpuVoltDfll(u32* ptr) { - CvbCpuDfllData *entry = reinterpret_cast(ptr); - - R_UNLESS(entry->tune0_low == 0xFFEAD0FF, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_low == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); - - if (!C.eristaCpuUV) { - R_SKIP(); - } - - switch(C.eristaCpuUV) { - case 1: - PATCH_OFFSET(&(entry->tune0_high), 0xffff); - PATCH_OFFSET(&(entry->tune1_high), 0x27007ff); - break; - case 2: - PATCH_OFFSET(&(entry->tune0_high), 0xefff); - PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); - break; - case 3: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); - break; - case 4: - PATCH_OFFSET(&(entry->tune0_high), 0xdfdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27a07ff); - break; - case 5: - PATCH_OFFSET(&(entry->tune0_high), 0xcfdf); - PATCH_OFFSET(&(entry->tune1_high), 0x37007ff); - break; - default: - break; - } - - R_SUCCEED(); - } - - Result GpuVoltDVFS(u32 *ptr) { - if (std::memcmp(ptr, gpuVoltDvfsPattern, sizeof(gpuVoltDvfsPattern))) { - R_THROW(ldr::ResultInvalidGpuDvfs()); - } - - if (C.eristaGpuVmin) { - PATCH_OFFSET(ptr, C.eristaGpuVmin); - } - - R_SUCCEED(); - } - - Result GpuVoltThermals(u32 *ptr) { - if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) { - R_THROW(ldr::ResultInvalidGpuDvfs()); - } - - if (C.eristaGpuVmin) { - PATCH_OFFSET(ptr, C.eristaGpuVmin); - PATCH_OFFSET(ptr + 3, C.eristaGpuVmin); - PATCH_OFFSET(ptr + 6, C.eristaGpuVmin); - PATCH_OFFSET(ptr + 9, C.eristaGpuVmin); - PATCH_OFFSET(ptr + 12, C.eristaGpuVmin); - } - - R_SUCCEED(); - } - - Result GpuFreqMaxAsm(u32 *ptr32) { - // Check if both two instructions match the pattern - u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); - if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) { - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - } - - // Both instructions should operate on the same register - u8 rd = asm_get_rd(ins1); - if (rd != asm_get_rd(ins2)) { - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - } - - u32 max_clock; - switch (C.eristaGpuUV) { - case 0: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; - break; - case 1: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableSLT)->freq; - break; - case 2: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTableHiOPT)->freq; - break; - default: - max_clock = GetDvfsTableLastEntry(C.eristaGpuDvfsTable)->freq; - break; - } - - u32 asm_patch[2] = { - asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), - asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd) - }; - - PATCH_OFFSET(ptr32, asm_patch[0]); - PATCH_OFFSET(ptr32 + 1, asm_patch[1]); - - R_SUCCEED(); + struct { + u32 *getEristaMtcTableFnSite = nullptr; + EristaMtcTable *mtcTable = nullptr; + bool foundMtcTablePattern = false; + } getMtcTableCache; } /* Note: This does not have proper timings, so base latency adjustment will not work. */ @@ -447,39 +304,6 @@ namespace ams::ldr::hoc::pcv::erista { } } - /* Relocate the table */ - /* Rescanning is simpler than trying to extract a bunch of data from the asm patch, performance impact is negligable */ - /* Also, this is more stable :P */ - u32 RepointEristaEmcTablePtr(uintptr_t fromSlot, uintptr_t toTable) { - constexpr u32 RetIns = 0xD65F03C0; /* ret */ - u32 patched = 0; - - for (u32 *p = nsoStart; p + 4 < nsoEnd; ++p) { - const u32 ins = *p; - if (!AsmIsAdrX0(ins)) { - continue; - } - - const uintptr_t pc = reinterpret_cast(p); - if (AsmAdrTarget(ins, pc) != fromSlot) { - continue; - } - if (!(AsmIsLdpX(p[1]) && AsmIsLdpX(p[2]) && p[3] == RetIns)) { - continue; - } - - /* adr only reaches +-1MB */ - const s64 delta = static_cast(toTable) - static_cast(pc); - if (delta > 0xFFFFF || delta < -0x100000) { - continue; - } - - PATCH_OFFSET(p, AsmSetAdrTarget(ins, pc, toTable)); - ++patched; - } - return patched; - } - /* The silicon instructs; the children obey... */ void MtcGenerateFreqTables() { newEmcList.clear(); @@ -548,24 +372,20 @@ namespace ams::ldr::hoc::pcv::erista { constexpr u32 StartAdjustment = offsetof(EristaMtcTable, rate_khz) + sizeof(EristaMtcTable) * (erista::MtcTableCountDefault - 1); u8 *startPtr = reinterpret_cast(ptr) - StartAdjustment; - const uintptr_t usedSlot = reinterpret_cast(startPtr) + mtcOffset; - EristaMtcTable *table = reinterpret_cast(usedSlot); + EristaMtcTable *table = reinterpret_cast(startPtr + mtcOffset); + R_TRY(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault)); PrepareMtcMemoryRegion(startPtr, table); table = reinterpret_cast(startPtr); - /* We must do this as the NLE tables don't have enough space past them for our extended ones */ - if (usedSlot != reinterpret_cast(startPtr)) { - if (RepointEristaEmcTablePtr(usedSlot, reinterpret_cast(startPtr)) == 0) { - AbortInvalidMtc("Failed to repoint emc table"); - } - } - if (R_FAILED(MtcValidateAllTables(table, EmcListDefault, EmcListSizeDefault))) { AbortInvalidMtc("Failed mtc validation"); } + /* Cache the table for hooks. */ + getMtcTableCache.mtcTable = table; + if (C.eristaEmcMaxClock <= EmcClkOSLimit) { R_SKIP(); } @@ -593,125 +413,67 @@ namespace ams::ldr::hoc::pcv::erista { R_SUCCEED(); } - Result GpuFreqPllMax(u32 *ptr) { - clk_pll_param *entry = reinterpret_cast(ptr); + HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count) { + const HookPayloadData *data = HOOK_PAYLOAD_PTR(HookPayloadData, e_HookPayloadData); - // All zero except for freq - for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) { - R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); - } - - // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; - R_SUCCEED(); + *count = data->mtcTableAsm.mtcCount; + return data->mtcTableAsm.mtcTable; } - // patch out 1305MHz limit on erista, don't use this! - // Result GpuFreqPllLimit(u32 *ptr) { - // u32 prev_freq = *(ptr - 1); + Result MtcInstallHooks(HookPayloadData *data) { + R_UNLESS(getMtcTableCache.getEristaMtcTableFnSite != nullptr && getMtcTableCache.mtcTable != nullptr, ldr::ResultInvalidMtcTablePattern()); - // if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) { - // R_THROW(ldr::ResultInvalidGpuPllEntry()); - // } + /* Copy the data to the payload. */ + data->mtcTableAsm.mtcTable = reinterpret_cast(Hooks().ToVa(getMtcTableCache.mtcTable)); + data->mtcTableAsm.mtcCount = newEmcList.size(); - // PATCH_OFFSET(ptr, 3600000); - - // R_SUCCEED(); - // } + R_TRY(INSTALL_IMPL_HOOK(getMtcTableCache.getEristaMtcTableFnSite, GetEristaMtcTableImpl)); + R_SUCCEED(); + } Result MemMtcTableAsm(u32 *ptr) { + /* Return if the pattern was already found. */ + /* This pattern happens multiple times in this function., we only need to find it once. */ + R_UNLESS(!getMtcTableCache.foundMtcTablePattern, ldr::ResultInvalidMtcTablePattern()); + /* This is a mess but the compiler made this painful to patch so we must do it this way */ - constexpr s32 GoodAdrpOffset = -1; - constexpr s32 GoodMovOffset = -7; - constexpr s32 GoodBlOffset = 1; - constexpr u32 MtcGoodBlOpcode = 0x97fe6cfc; - - constexpr u32 MtcBadBlOpcode0 = 0x97ffae64; // bl nn::pcv::GetHardwareType - constexpr u32 MtcBadBlOpcode1 = 0x940036d5; // bl strcmp - constexpr u32 MtcBadAdrpAsm = 0xd00000a1; // adrp x1, s_ModuleResetStatus_ - - constexpr s32 MtcBadBlOffset0 = 2; - constexpr s32 MtcBadBlOffset1 = -1; - constexpr s32 MtcBadAdrpOffset = 1; + constexpr u32 AddrpOffset = 1; + constexpr u32 MovOffset = 7; + constexpr u32 BlOffset = 5; + constexpr u32 MovOffsetOld = 8; /* Ensure we don't dereference memory before nso start. */ - R_UNLESS(ptr + GoodMovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern()); + R_UNLESS(ptr - MovOffset >= nsoStart, ldr::ResultInvalidMtcTablePattern()); - /* Check for GetHardwareType asm and skip if it is found */ - /* The pattern will match on the first time, but the location is bad, so it must be skipped */ - if(AsmCompareAdrpNoImm(*(ptr + MtcBadAdrpOffset), MtcBadAdrpAsm) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset0), MtcBadBlOpcode0) && AsmBlCompareOpcodeOnly(*(ptr + MtcBadBlOffset1), MtcBadBlOpcode1)) { - R_SKIP(); - } - - /* We don't check for matching register because both registers must be x0 in order to pass the previous checks. */ - /* The correct instructions will always be x0 since the mtcTable pointer is returned. */ - u32 adrp = *(ptr + GoodAdrpOffset); + u32 adrp = *(ptr - AddrpOffset); R_UNLESS(AsmCompareAdrpNoImm(adrp, MtcAdrpAsm), ldr::ResultInvalidMtcTablePattern()); - /* Check for the branch instruction above the cbz to ensure we are patching the right location*/ - u32 bl = *(ptr + GoodBlOffset); - R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcGoodBlOpcode), ldr::ResultInvalidMtcTablePattern()); - + u32 bl = *(ptr - BlOffset); + R_UNLESS(AsmBlCompareOpcodeOnly(bl, MtcBlIns), ldr::ResultInvalidMtcTablePattern()); /* Check for the mov that actually sets the mtc table count. */ - u32 mov = *(ptr + GoodMovOffset); - R_UNLESS(asm_compare_no_rd(mov, MtcMovAsm), ldr::ResultInvalidMtcTablePattern()); + u32 mov = *(ptr - MovOffset); + bool foundMov = false; + foundMov = asm_compare_no_rd(mov, MtcMovAsm); - /* Patch out the count of the mov to our custom mtc table amount*/ - u32 movCountPatch = asm_set_rd(asm_set_imm16(MtcMovAsm, newEmcList.size()), asm_get_rd(mov)); + if (!foundMov) { + mov = *(ptr + MovOffsetOld); + /* Check old firmware offset. */ + foundMov = asm_compare_no_rd(mov, MtcMovAsm); + } - PATCH_OFFSET(ptr + GoodMovOffset, movCountPatch); + R_UNLESS(foundMov, ldr::ResultInvalidMtcTablePattern()); + + constexpr u32 PrologueWindow = 140; + u32 *functionPrologue = FindFnPrologue(ptr, PrologueWindow, nsoStart); + R_UNLESS(functionPrologue != nullptr, ldr::ResultInvalidMtcTablePattern()); + + getMtcTableCache.getEristaMtcTableFnSite = functionPrologue; + getMtcTableCache.foundMtcTablePattern = true; R_SUCCEED(); } - void Patch(uintptr_t mapped_nso, size_t nso_size) { - nsoStart = reinterpret_cast(mapped_nso); - nsoEnd = reinterpret_cast(mapped_nso + nso_size); - MtcGenerateFreqTables(); - - u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); - u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); - - PatcherEntry patches[] = { - {"CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq }, - {"CPU Volt DVFS", &CpuVoltDvfs, 1, nullptr, CpuVminOfficial }, - {"CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial }, - {"CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low }, - {"GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial }, - {"GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial }, - {"GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, - {"GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, - {"GPU PLL Max", & GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, - // {"GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, - {"MEM Table Asm", &MemMtcTableAsm, 4, &MemMtcGetGetTablePatternFn }, - {"MEM Freq Mtc", &MemFreqMtcTable, 1, 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; ptr <= mapped_nso + nso_size - sizeof(EristaMtcTable); ptr += sizeof(u32)) { - u32 *ptr32 = reinterpret_cast(ptr); - for (auto &entry : patches) { - if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) { - break; - } - } - } - - for (auto &entry : patches) { - LOGGING("%s Count: %zu\n", entry.description, entry.patched_count); - if (R_FAILED(entry.CheckResult())) { - // ViewLog(); - panic::SmcError(panic::Patch); - - CRASH(entry.description); - } - } - } - } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.hpp new file mode 100644 index 00000000..5c1c5100 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/erista/pcv_erista_mtc.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * 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 . + */ + +#pragma once + +#include "pcv_erista.hpp" +#include "../../mtc_timing_table.hpp" + +namespace ams::ldr::hoc::pcv::erista { + + constexpr u32 EmcListDefault[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000, }; + constexpr u32 EmcListSizeDefault = std::size(EmcListDefault); + constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1; + + constexpr u32 MemVoltHOS = 1125'000; + constexpr u32 EmcClkPllmLimit = 1866'000'000; + + constexpr u32 MTC_TABLE_REV = 7; + constexpr u32 MtcTableCountDefault = 10; + + constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault; + constexpr u32 MtcFullTableCount = 3; + + /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */ + enum DramId { + ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0, + ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1, + ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2, + ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4, + ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7, + }; + + enum MtcTableIndex { + T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH, WT:C */ + T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */ + T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */ + MtcTableIndex_Invalid = 3, + }; + + struct MtcDramIndex { + DramId dramId; + MtcTableIndex index; + }; + + /* TODO: Test 6gb and 8gb. */ + const inline MtcDramIndex mtcIndexTable[] = { + { ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, }, + { ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, }, + { ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, }, + { ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX, T210SdevEmcDvfsTableS6gb01, }, + { ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, }, + }; + + constexpr u32 MtcBrAsm = 0xD61F0140; + constexpr u32 MtcMovAsm = 0x52800148; + constexpr u32 MtcAdrpAsm = 0xD0000081; + constexpr u32 MtcBlIns = 0x97ffae64; + constexpr u32 MtcAddAsm = 0x91131821; + + ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) { + /* This builds an address that gets returned, so the register must be x0 by convention. */ + return AsmCompareAddNoImm12(*ptr, MtcAddAsm); + } + + Result MemFreqMtcTable(u32 *ptr); + void MtcGenerateFreqTables(); + Result MemFreqMax(u32 *ptr); + HOOK_PAYLOAD_FN EristaMtcTable *GetEristaMtcTableImpl(u32 *count); + Result MemMtcTableAsm(u32 *ptr); + + Result MtcInstallHooks(HookPayloadData *data); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp similarity index 96% rename from Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp index f83353fb..fab24dbf 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.cpp @@ -1,204 +1,204 @@ -/* - * Copyright (c) Lightos_ - * - * 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 . - */ - -#include -#include "../mtc_timing_value.hpp" -#include "timing_tables.hpp" - -namespace ams::ldr::hoc::pcv::mariko { - - void GetRext() { - if (auto r = FindRext()) { - rext = r->rext; - return; - } - - /* > 3200 */ - rext = 0x1E; - } - - void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { - latency += index * latencyStep; - } - - static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { - s32 maxIndex = -1; - for (u32 i = 0; i < latencySize; ++i) { - if (latencyArray[i]) { - maxIndex = i; - } - } - - return maxIndex; - } - - void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { - if (freq > 1600'000 && freq <= 1862'400) { /* 1866tRWL */ - latency += latencyStep * 2; - } else { /* 2133tRWL */ - latency += latencyStep * 3; - } - } - - void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { - for (u32 i = 0; i <= indexMax; ++i) { - if (latencyArray[i] != 0 && freq <= latencyArray[i]) { - SwitchLatency(latency, i, latencyStep); - return; - } - } - - SwitchLatency(latency, indexMax, latencyStep); - } - - void HandleLatency(u32 freq) { - static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); - static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); - constexpr u32 ReadLatencyStep = 4; - constexpr u32 WriteLatencyStep = 2; - bool autoLatencyRead = false, autoLatencyWrite = false; - - if (rlIndexMax == -1) { - AutoLatency(RL, freq, ReadLatencyStep); - autoLatencyRead = true; - } - - if (wlIndexMax == -1) { - AutoLatency(WL, freq, WriteLatencyStep); - autoLatencyWrite = true; - } - - if (autoLatencyRead && autoLatencyWrite) { - return; - } - - if (!autoLatencyRead) { - HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); - } - - if (!autoLatencyWrite) { - HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); - } - } - - void CalculateMrw2() { - static const u8 rlMapDBI[8] = { - 6, 12, 16, 22, 28, 32, 36, 40 - }; - - static const u8 wlMapSetA[8] = { - 4, 6, 8, 10, 12, 14, 16, 18 - }; - - u32 rlIndex = 0; - u32 wlIndex = 0; - - for (u32 i = 0; i < std::size(rlMapDBI); ++i) { - if (rlMapDBI[i] == RL) { - rlIndex = i; - break; - } - } - - for (u32 i = 0; i < std::size(wlMapSetA); ++i) { - if (wlMapSetA[i] == WL) { - wlIndex = i; - break; - } - } - - /* DBI is always enabled. */ - mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); - } - - void CalculateTimings(double tCK_avg, u32 freq) { - RL = RL_1331; - WL = WL_1331; - - HandleLatency(freq); - - GetRext(); - - /* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */ - /* This is a lazy workaround until I find the issue... */ - const bool lowFreq = freq < C.timingEmcTbreak; - - tRCD = tRCD_values[lowFreq ? C.low_t1_tRCD : C.t1_tRCD]; - tRPpb = tRP_values[lowFreq ? C.low_t2_tRP : C.t2_tRP]; - tRAS = tRAS_values[lowFreq ? C.low_t3_tRAS : C.t3_tRAS]; - tRRD = tRRD_values[lowFreq ? C.low_t4_tRRD : C.t4_tRRD]; - tRFCpb = tRFC_values[lowFreq ? C.low_t5_tRFC : C.t5_tRFC]; - - u32 tRTW = lowFreq ? C.low_t6_tRTW : C.t6_tRTW; - u32 tWTR = 10 - tWTR_values[lowFreq ? C.low_t7_tWTR : C.t7_tWTR]; - - s32 finetRTW = C.fineTune_t6_tRTW; - s32 finetWTR = C.fineTune_t7_tWTR; - - u32 tREFI = lowFreq ? C.low_t8_tREFI : C.t8_tREFI; - refresh_raw = 0xFFFF; - if (tREFI != 6) { - refresh_raw = CEIL(tREFpb_values[tREFI] / tCK_avg) - 0x40; - refresh_raw = MIN(refresh_raw, static_cast(0xFFFF)); - } - - tRC = tRAS + tRPpb; - tRFCab = tRFCpb * 2; - tXSR = static_cast(tRFCab + 7.5); - tFAW = static_cast(tRRD * 4.0); - tRPab = tRPpb + 3; - - tR2P = CEIL((RL * 0.426) - 2.0); - tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW; - tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg); - tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); - - rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017); - qpop = rdv - 14; - quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782); - quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg); - einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width; - einput = quse - CEIL(9.928 / tCK_avg); - u32 qrst_duration = FLOOR(8.399 - tCK_avg); - u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); - qrst = PACK_U32(qrst_duration, qrstLow); - ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); - qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); - tW2P = (CEIL(WL * 1.7303) * 2) - 5; - tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2)); - tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; - tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); - tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; - - wdv = WL; - wsv = WL - 2; - wev = 0xA + (WL - 14); - - u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); - u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); - obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); - - pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); - - tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; - - u32 tMMRI = tRCD + (tCK_avg * 3); - pdex2mrr = tMMRI + 10; - - CalculateMrw2(); - } - -} +/* + * Copyright (c) Lightos_ + * + * 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 . + */ + +#include +#include "../../mtc_timing_value.hpp" +#include "timing_tables.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + void GetRext() { + if (auto r = FindRext()) { + rext = r->rext; + return; + } + + /* > 3200 */ + rext = 0x1E; + } + + void SwitchLatency(volatile u32 &latency, u32 index, u32 latencyStep) { + latency += index * latencyStep; + } + + static s32 GetMaxLatencyIndex(volatile u32 *latencyArray, u32 latencySize) { + s32 maxIndex = -1; + for (u32 i = 0; i < latencySize; ++i) { + if (latencyArray[i]) { + maxIndex = i; + } + } + + return maxIndex; + } + + void AutoLatency(volatile u32 &latency, u32 freq, u32 latencyStep) { + if (freq > 1600'000 && freq <= 1862'400) { /* 1866tRWL */ + latency += latencyStep * 2; + } else { /* 2133tRWL */ + latency += latencyStep * 3; + } + } + + void HandleLatency(u32 freq, volatile u32 &latency, volatile u32 *latencyArray, u32 indexMax, u32 latencyStep) { + for (u32 i = 0; i <= indexMax; ++i) { + if (latencyArray[i] != 0 && freq <= latencyArray[i]) { + SwitchLatency(latency, i, latencyStep); + return; + } + } + + SwitchLatency(latency, indexMax, latencyStep); + } + + void HandleLatency(u32 freq) { + static s32 rlIndexMax = GetMaxLatencyIndex(C.readLatency, std::size(C.readLatency)); + static s32 wlIndexMax = GetMaxLatencyIndex(C.writeLatency, std::size(C.writeLatency)); + constexpr u32 ReadLatencyStep = 4; + constexpr u32 WriteLatencyStep = 2; + bool autoLatencyRead = false, autoLatencyWrite = false; + + if (rlIndexMax == -1) { + AutoLatency(RL, freq, ReadLatencyStep); + autoLatencyRead = true; + } + + if (wlIndexMax == -1) { + AutoLatency(WL, freq, WriteLatencyStep); + autoLatencyWrite = true; + } + + if (autoLatencyRead && autoLatencyWrite) { + return; + } + + if (!autoLatencyRead) { + HandleLatency(freq, RL, C.readLatency, rlIndexMax, ReadLatencyStep); + } + + if (!autoLatencyWrite) { + HandleLatency(freq, WL, C.writeLatency, wlIndexMax, WriteLatencyStep); + } + } + + void CalculateMrw2() { + static const u8 rlMapDBI[8] = { + 6, 12, 16, 22, 28, 32, 36, 40 + }; + + static const u8 wlMapSetA[8] = { + 4, 6, 8, 10, 12, 14, 16, 18 + }; + + u32 rlIndex = 0; + u32 wlIndex = 0; + + for (u32 i = 0; i < std::size(rlMapDBI); ++i) { + if (rlMapDBI[i] == RL) { + rlIndex = i; + break; + } + } + + for (u32 i = 0; i < std::size(wlMapSetA); ++i) { + if (wlMapSetA[i] == WL) { + wlIndex = i; + break; + } + } + + /* DBI is always enabled. */ + mrw2 = static_cast(((rlIndex & 0x7) | ((wlIndex & 0x7) << 3) | ((0 & 0x1) << 6))); + } + + void CalculateTimings(double tCK_avg, u32 freq) { + RL = RL_1331; + WL = WL_1331; + + HandleLatency(freq); + + GetRext(); + + /* At 1333WL, for some reason (incorrect ram timing config in mtc table?), tRP causes crashes at high reductions - 2 seems to be the most common limit. */ + /* This is a lazy workaround until I find the issue... */ + const bool lowFreq = freq < C.timingEmcTbreak; + + tRCD = tRCD_values[lowFreq ? C.low_t1_tRCD : C.t1_tRCD]; + tRPpb = tRP_values[lowFreq ? C.low_t2_tRP : C.t2_tRP]; + tRAS = tRAS_values[lowFreq ? C.low_t3_tRAS : C.t3_tRAS]; + tRRD = tRRD_values[lowFreq ? C.low_t4_tRRD : C.t4_tRRD]; + tRFCpb = tRFC_values[lowFreq ? C.low_t5_tRFC : C.t5_tRFC]; + + u32 tRTW = lowFreq ? C.low_t6_tRTW : C.t6_tRTW; + u32 tWTR = 10 - tWTR_values[lowFreq ? C.low_t7_tWTR : C.t7_tWTR]; + + s32 finetRTW = C.fineTune_t6_tRTW; + s32 finetWTR = C.fineTune_t7_tWTR; + + u32 tREFI = lowFreq ? C.low_t8_tREFI : C.t8_tREFI; + refresh_raw = 0xFFFF; + if (tREFI != 6) { + refresh_raw = CEIL(tREFpb_values[tREFI] / tCK_avg) - 0x40; + refresh_raw = MIN(refresh_raw, static_cast(0xFFFF)); + } + + tRC = tRAS + tRPpb; + tRFCab = tRFCpb * 2; + tXSR = static_cast(tRFCab + 7.5); + tFAW = static_cast(tRRD * 4.0); + tRPab = tRPpb + 3; + + tR2P = CEIL((RL * 0.426) - 2.0); + tR2W = FLOOR(FLOOR((5.0 / tCK_avg) + ((FLOOR(48.0 / WL) - 0.478) * 3.0)) / 1.501) + RL - (tRTW * 3) + finetRTW; + tRTM = FLOOR((10.0 + RL) + (3.502 / tCK_avg)) + FLOOR(7.489 / tCK_avg); + tRATM = CEIL((tRTM - 10.0) + (RL * 0.426)); + + rdv = RL + FLOOR((5.105 / tCK_avg) + 17.017); + qpop = rdv - 14; + quse_width = CEIL(((4.897 / tCK_avg) - FLOOR(2.538 / tCK_avg)) + 3.782); + quse = FLOOR(RL + ((5.082 / tCK_avg) + FLOOR(2.560 / tCK_avg))) - CEIL(4.820 / tCK_avg); + einput_duration = FLOOR(9.936 / tCK_avg) + 5.0 + quse_width; + einput = quse - CEIL(9.928 / tCK_avg); + u32 qrst_duration = FLOOR(8.399 - tCK_avg); + u32 qrstLow = MAX(static_cast(einput - qrst_duration - 2), static_cast(0)); + qrst = PACK_U32(qrst_duration, qrstLow); + ibdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(1, quse - qrst_duration - 2.0); + qsafe = (einput_duration + 3) + MAX(MIN(qrstLow * rdv, qrst_duration + qrst_duration), einput); + tW2P = (CEIL(WL * 1.7303) * 2) - 5; + tWTPDEN = CEIL(((1.803 / tCK_avg) + MAX(RL + (2.694 / tCK_avg), static_cast(tW2P))) + (BL / 2)); + tW2R = FLOOR(MAX((5.020 / tCK_avg) + 1.130, WL - MAX(-CEIL(0.258 * (WL - RL)), 1.964)) * 1.964) + WL - CEIL(tWTR / tCK_avg) + finetWTR; + tWTM = CEIL(WL + ((7.570 / tCK_avg) + 8.753)); + tWATM = (tWTM + (FLOOR(WL / 0.816) * 2.0)) - 4.0; + + wdv = WL; + wsv = WL - 2; + wev = 0xA + (WL - 14); + + u32 obdlyHigh = 3 / FLOOR(MIN(static_cast(2), tCK_avg * (WL - 7))); + u32 obdlyLow = MAX(WL - FLOOR((126.0 / CEIL(tCK_avg + 8.601))), 0.0); + obdly = PACK_U32_NIBBLE_HIGH_BYTE_LOW(obdlyHigh, obdlyLow); + + pdex2rw = CEIL((CEIL(12.335 - tCK_avg) + (7.430 / tCK_avg) - CEIL(tCK_avg * 11.361))); + + tCLKSTOP = FLOOR(MIN(8.488 / tCK_avg, 23.0)) + 8.0; + + u32 tMMRI = tRCD + (tCK_avg * 3); + pdex2mrr = tMMRI + 10; + + CalculateMrw2(); + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp similarity index 96% rename from Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp index 84165e07..8659d3f2 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/calculate_timings.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/calculate_timings_mariko.hpp @@ -1,24 +1,24 @@ -/* - * Copyright (c) Lightos_ - * - * 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 . - */ - -#pragma once - -namespace ams::ldr::hoc::pcv::mariko { - - void CalculateTimings(double tCK_avg, u32 freq); - -} - +/* + * Copyright (c) Lightos_ + * + * 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 . + */ + +#pragma once + +namespace ams::ldr::hoc::pcv::mariko { + + void CalculateTimings(double tCK_avg, u32 freq); + +} + diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp new file mode 100644 index 00000000..c2644620 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.cpp @@ -0,0 +1,588 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#include +#include "../pcv.hpp" +#include "../../mtc_timing_value.hpp" +#include "pcv_mariko.hpp" +#include "pcv_mariko_cpu.hpp" +#include "pcv_mariko_gpu.hpp" +#include "pcv_mariko_mtc.hpp" +#include "calculate_timings_mariko.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + u32 *nsoStart; + + namespace { + size_t g_nso_size = 0; + uintptr_t g_cave_cursor = 0; + } + + static uintptr_t CaveReserve(size_t count) { + if (g_pcv_cave == 0 || g_cave_cursor == 0) { + return 0; + } + if (g_cave_cursor + count * sizeof(u32) > g_pcv_cave + g_pcv_cave_size) { + return 0; + } + const uintptr_t entry = g_cave_cursor; + g_cave_cursor += count * sizeof(u32); + return entry; + } + + #if HOC_UART_LOG + /* Redirect pcv's NvLog() calls to UART */ + Result NvLogUartRedirect(u32 *ptr) { + const uintptr_t mapped_nso = reinterpret_cast(nsoStart); + const size_t nso_size = g_nso_size; + const uintptr_t textEnd = g_pcv_cave; /* .text ends where the cave begins */ + const uintptr_t vsnprintf_addr = reinterpret_cast(ptr); + + /* NvLog via the VDD_SOC log */ + static const char Fmt[] = "%s(%s): DVFS request VDD_SOC %d mV\n"; + constexpr size_t FmtLen = sizeof(Fmt) - 1; + uintptr_t strAddr = 0; + { + const char *hay = reinterpret_cast(mapped_nso); + for (size_t i = 0; i + FmtLen <= nso_size; ++i) { + if (std::memcmp(hay + i, Fmt, FmtLen) == 0) { strAddr = mapped_nso + i; break; } + } + } + if (strAddr == 0) { + LOGGING("NvLogRedirect: fmt string not found (vsnprintf@+%lx)", vsnprintf_addr - mapped_nso); + R_THROW(ldr::ResultInvalidNvLogRedirect()); + } + + uintptr_t nvlog_addr = 0; + for (u32 *p = nsoStart; reinterpret_cast(p + 2) <= textEnd; ++p) { + const uintptr_t pc = reinterpret_cast(p); + if (!AsmIsAdrp(p[0])) { + continue; + } + const uintptr_t adrpPage = (pc & ~static_cast(0xFFFu)) + static_cast(AsmAdrpPageOffset(p[0])); + if (adrpPage != (strAddr & ~static_cast(0xFFFu))) { + continue; + } + const u32 reg = asm_get_rd(p[0]); + if (!(AsmIsAddImm64(p[1]) && asm_get_rd(p[1]) == reg && AsmGetRn(p[1]) == reg && AsmGetImm12(p[1]) == (strAddr & 0xFFFu))) { + continue; + } + for (u32 k = 2; k <= 12 && (pc + (k + 1) * 4) <= textEnd; ++k) { + if (AsmIsBl(p[k])) { nvlog_addr = AsmBranchTarget(p[k], pc + k * 4); break; } + } + if (nvlog_addr != 0) { + break; + } + } + if (nvlog_addr == 0 || nvlog_addr < mapped_nso || nvlog_addr >= textEnd) { + LOGGING("NvLogRedirect: NvLog entry not found (fmt@+%lx)", strAddr - mapped_nso); + R_THROW(ldr::ResultInvalidNvLogRedirect()); + } + + const uintptr_t helper = CaveReserve(40); + if (helper == 0) { + LOGGING("NvLogRedirect: cave unavailable (cave=%lx size=%lx)", + static_cast(g_pcv_cave), static_cast(g_pcv_cave_size)); + R_THROW(ldr::ResultInvalidNvLogRedirect()); + } + u32 *t = reinterpret_cast(helper); + size_t n = 0; + auto emit = [&](u32 ins) { t[n] = ins; ++n; }; + + emit(AsmMakeSubImm64(31, 31, 0x200)); + emit(AsmMakeStpImm64(0, 1, 31, 0x100)); + emit(AsmMakeStpImm64(2, 3, 31, 0x110)); + emit(AsmMakeStpImm64(4, 5, 31, 0x120)); + emit(AsmMakeStpImm64(6, 7, 31, 0x130)); + emit(AsmMakeStpqImm(0, 1, 31, 0x140)); + emit(AsmMakeStpqImm(2, 3, 31, 0x160)); + emit(AsmMakeStpqImm(4, 5, 31, 0x180)); + emit(AsmMakeStpqImm(6, 7, 31, 0x1A0)); + emit(AsmMakeStrImm64(30, 31, 0x1E0)); + emit(AsmMakeAddImm64(9, 31, 0x200)); emit(AsmMakeStrImm64(9, 31, 0x1C0)); /* __stack */ + emit(AsmMakeAddImm64(9, 31, 0x140)); emit(AsmMakeStrImm64(9, 31, 0x1C8)); /* __gr_top */ + emit(AsmMakeAddImm64(9, 31, 0x1C0)); emit(AsmMakeStrImm64(9, 31, 0x1D0)); /* __vr_top */ + emit(AsmMakeMovnW(9, 0x37)); emit(AsmMakeStrImm32(9, 31, 0x1D8)); /* __gr_offs = -56 */ + emit(AsmMakeMovnW(9, 0x7F)); emit(AsmMakeStrImm32(9, 31, 0x1DC)); /* __vr_offs = -128 */ + emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (buf) */ + emit(AsmMakeMovzW(1, 0x100)); /* size = 0x100 */ + emit(AsmMakeLdrImm64(2, 31, 0x100)); /* fmt (saved x0) */ + emit(AsmMakeAddImm64(3, 31, 0x1C0)); /* ap */ + emit(AsmMakeBl(helper + n * 4, vsnprintf_addr)); + emit(AsmMakeMovReg(1, 0)); /* len = retval */ + emit(AsmMakeCmpImm32(1, 0x100)); + { const size_t at = n; emit(AsmMakeBCond(helper + at * 4, helper + (at + 2) * 4, 0x3u)); } /* b.lo +2 */ + emit(AsmMakeMovzW(1, 0xFF)); /* clamp len */ + emit(AsmMakeAddImm64(0, 31, 0x00)); /* mov x0,sp (str) */ + emit(AsmMakeSvc(0x27)); /* svcOutputDebugString */ + emit(AsmMakeLdrImm64(30, 31, 0x1E0)); + emit(AsmMakeAddImm64(31, 31, 0x200)); + emit(RetIns); + + /* Redirect the call sites as patching the actual function causes crash */ + const uintptr_t roStart = g_pcv_cave + g_pcv_cave_size; /* module .rodata start */ + size_t patchedSites = 0; + if (HOC_PCV_NVLOG_PATCH) { + for (u32 *p = nsoStart; reinterpret_cast(p + 1) <= textEnd; ++p) { + if (!AsmIsBl(*p)) { + continue; + } + const uintptr_t pc = reinterpret_cast(p); + if (AsmBranchTarget(*p, pc) != nvlog_addr) { + continue; + } + bool isFmtCall = false; + for (u32 j = 1; j <= 8 && reinterpret_cast(p - j) >= reinterpret_cast(nsoStart); ++j) { + const u32 w = *(p - j); + if (AsmIsAdrp(w) && asm_get_rd(w) == 0) { /* adrp x0, */ + const uintptr_t wpc = pc - j * 4; + const uintptr_t tgtPage = (wpc & ~static_cast(0xFFFu)) + static_cast(AsmAdrpPageOffset(w)); + if (tgtPage >= (roStart & ~static_cast(0xFFFu))) { isFmtCall = true; break; } + } + } + if (isFmtCall) { + PATCH_OFFSET(p, AsmMakeBl(pc, helper)); + ++patchedSites; + } + } + } + + LOGGING("NvLogRedirect: stub@+%lx vsnprintf@+%lx helper@+%lx instr=%zu sites=%zu", + nvlog_addr - mapped_nso, vsnprintf_addr - mapped_nso, helper - mapped_nso, n, patchedSites); + R_SUCCEED(); + } + #endif + + /* Relocate C2/C3Bus to avoid issues*/ + Result BusFreqReloc(u32 *ptr) { + const u32 busReg = AsmGetRn(ptr[0]); /* ldr Xbuf,[Xbus,#0x10] : bus struct pointer */ + const u32 bufReg = asm_get_rd(ptr[0]); /* : freq-buffer arg */ + const u32 bufOff = AsmGetLdStImm64Off(ptr[0]); /* : bus->freqBuf offset */ + const u32 cntReg = asm_get_rd(ptr[1]); /* add Xcnt,Xbus,#0x18 : arg2 (&count) */ + const u32 railReg = asm_get_rd(ptr[2]); /* str Xrail,[Xbus,#0x50]: arg0 (rail) */ + u32 *call = ptr + 3; /* the bl to relocate */ + const uintptr_t realFn = AsmBranchTarget(*call, reinterpret_cast(call)); + + /* Pick 3 scratch registers */ + u32 s[3], sc = 0; + for (u32 r = 9; r <= 15 && sc < 3; ++r) { + if (r != busReg && r != bufReg && r != cntReg && r != railReg) { + s[sc++] = r; + } + } + R_UNLESS(sc == 3, ldr::ResultInvalidBusFreqReloc()); + + const uintptr_t tramp = CaveReserve(9); + R_UNLESS(tramp != 0, ldr::ResultInvalidBusFreqReloc()); + + const uintptr_t region = g_pcv_scratch + HocBusFreqBufOffset; /* [0]=counter, +0x10 + i*0x400 = bufs */ + u32 *t = reinterpret_cast(tramp); + size_t n = 0; + auto emit = [&](u32 ins) { t[n] = ins; ++n; }; + emit(AsmMakeAdrp(tramp + n * 4, region, s[0])); /* adrp s0, */ + emit(AsmMakeAddImm64(s[0], s[0], region & 0xFFFu)); /* add s0,s0,#lo */ + emit(AsmMakeLdrImm32(s[1], s[0], 0x00)); /* s1 = counter */ + emit(AsmMakeAddImm64(s[2], s[1], 1)); /* s2 = counter+1 */ + emit(AsmMakeStrImm32(s[2], s[0], 0x00)); /* counter++ */ + emit(AsmMakeAddImm64(s[0], s[0], 0x10)); /* s0 = region+0x10 (buffers) */ + emit(AsmMakeAddShiftedReg64(bufReg, s[0], s[1], 10)); /* Xbuf = s0 + counter*0x400 */ + emit(AsmMakeStrImm64(bufReg, busReg, bufOff)); /* bus[freqBuf] = Xbuf */ + emit(AsmMakeB(tramp + n * 4, realFn)); /* tail-call the real function */ + + PATCH_OFFSET(call, AsmMakeBl(reinterpret_cast(call), tramp)); + const uintptr_t base = reinterpret_cast(nsoStart); + (void) base; + LOGGING("BusFreqReloc: call@+%lx -> tramp@+%lx realfn@+%lx (bus=x%u buf=x%u off=0x%x scratch=x%u,x%u,x%u)", + reinterpret_cast(call) - base, tramp - base, realFn - base, busReg, bufReg, bufOff, s[0], s[1], s[2]); + R_SUCCEED(); + } + + + #if HOC_UART_LOG + /* Force GetEffectiveVerbosityLevel to return a non-zero level so all NvLog runs. */ + Result ForceVerbosity(u32 *ptr) { + PATCH_OFFSET(&ptr[0], AsmMakeMovzW(0, static_cast(HOC_PCV_FORCE_VERBOSITY))); /* movz w0,#level */ + PATCH_OFFSET(&ptr[1], RetIns); /* ret */ + R_SUCCEED(); + } + #endif + + /* Widen InitDram for a >32-entry EMC DVFS list. Freq array can be dropped to free 264 bytes, relocate the Soc LUT to that space */ + Result EmcSocLutReloc(u32 *ptr) { + constexpr u32 Window = 48; + + u32 *freqStore = ScanAssembly(ptr - Window, Window, EmcSocFreqStoreAsm, asm_compare_no_rd); /* str x?,[x8,#0x18] */ + u32 *voltStore = ScanAssembly(ptr - Window, Window, EmcSocVoltStoreAsm, asm_compare_no_rd); /* str w?,[x8,#0x48] */ + u32 *readLoad = ScanAssembly(ptr - Window, Window, EmcSocReadLoadAsm, asm_compare_no_rd); /* ldr w?,[x9,#0x48] */ + R_UNLESS(freqStore && voltStore && readLoad, ldr::ResultInvalidEmcSocLut()); + + u32 *voltBase = voltStore - 2; /* `add Xb,Xsrc,Xi,LSL#2` (a cmn sits between it and store) */ + R_UNLESS(AsmIsAddShiftedReg64(*voltBase) && asm_get_rd(*voltBase) == AsmGetRn(*voltStore), + ldr::ResultInvalidEmcSocLut()); + + /* adrp Xl ; add Xl,Xl,#off ; ... ; str Xl,[rail,#0x120] */ + const u32 lutReg = asm_get_rd(ptr[0]); + const u32 railReg = AsmGetRn(ptr[0]); + R_UNLESS(AsmIsAdrp(ptr[-3]) && asm_get_rd(ptr[-3]) == lutReg, ldr::ResultInvalidEmcSocLut()); + R_UNLESS(AsmIsAddImm64(ptr[-2]) && asm_get_rd(ptr[-2]) == lutReg && AsmGetRn(ptr[-2]) == lutReg, + ldr::ResultInvalidEmcSocLut()); + + const u32 srcBase = AsmGetRn(*voltBase); /* rail ptr at +0x20 */ + const u32 wBase = asm_get_rd(*voltBase); /* base reg */ + const u32 wIdx = AsmGetRm(*voltBase); /* loop index */ + + PATCH_OFFSET(freqStore, NopIns); /* Unneeded */ + PATCH_OFFSET(voltBase, AsmMakeLdrImm64(wBase, srcBase, 0x20)); /* ldr Xb,[Xsrc,#0x20] (rail) */ + PATCH_OFFSET(voltStore - 1, AsmMakeAddImm64(wBase, wBase, 0x18)); /* add Xb,Xb,#0x18 (was cmn) */ + PATCH_OFFSET(voltStore, AsmSetLdStRegOffset(*voltStore, wIdx)); /* str Wv,[Xb,Xi,LSL#2] -> rail+0x18+i*4 */ + PATCH_OFFSET(voltStore + 1, NopIns); /* Unneeded */ + + /* rail+0x18 as the socMinLut pointer. */ + PATCH_OFFSET(ptr - 3, NopIns); + PATCH_OFFSET(ptr - 2, AsmMakeAddImm64(lutReg, railReg, 0x18));/* add Xl,rail,#0x18 */ + + /* Drop the abort branch in case of a bad read */ + for (u32 i = 1; i <= 4; ++i) { + if (AsmIsBCond(readLoad[i])) { + PATCH_OFFSET(&readLoad[i], NopIns); + break; + } + } + R_SUCCEED(); + } + + Result EmcDvfsCountLimit(u32 *ptr) { + R_UNLESS(EmcDvfsCountPatternFn(ptr), ldr::ResultInvalidEmcDvfsCount()); + + /* cmp w?,#0x21 -> cmp w?,#EmcDvfsTableEntryCount */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); + R_SUCCEED(); + } + + Result EmcRateListLimit(u32 *ptr) { + /* ptr = cmp w?,#0x20 ; ptr[1] = csel w?,w?,w?,lt (w? = min(maxCount, 32)) ; ptr[2] = bl */ + R_UNLESS(EmcRateListPatternFn(ptr), ldr::ResultInvalidEmcRateList()); + + /* The csel's Rm holds the 32 cap. */ + const u32 capReg = AsmGetRm(ptr[1]); + const u32 capMov = AsmMakeMovzW(capReg, 0x20); /* movz w,#0x20 */ + + u32 *movPtr = nullptr; + for (u32 i = 1; i <= 16; ++i) { + if (*(ptr - i) == capMov) { + movPtr = ptr - i; + break; + } + } + R_UNLESS(movPtr, ldr::ResultInvalidEmcRateList()); + + /* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount). */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); /* cmp w?,#64 */ + PATCH_OFFSET(movPtr, asm_set_imm16(*movPtr, static_cast(EmcDvfsTableEntryCount))); /* movz w?,#64 */ + R_SUCCEED(); + } + + Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { + struct { + u8 reg; + u8 val; + } __attribute__((packed)) cmd; + + I2cSession _session; + R_TRY(i2cOpenSession(&_session, dev)); + + cmd.reg = reg; + cmd.val = val; + Result res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); + i2csessionClose(&_session); + return res; + } + + Result EmcVddqVolt(u32 *ptr) { + regulator *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(regulator, type_2_3.default_uv)); + + constexpr u32 uv_step = 5'000; + constexpr u32 uv_min = 250'000; + + auto validator = [entry]() { + R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); + R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); + R_SUCCEED(); + }; + + R_TRY(validator()); + + u32 emc_uv = C.marikoEmcVddqVolt; + + if (!emc_uv) { + R_SKIP(); + } + + if (emc_uv % uv_step) { + emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding + } + + PATCH_OFFSET(ptr, emc_uv); + + i2cInitialize(); + Result resultI2C = I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step); + i2cExit(); + + R_SUCCEED(); + + return resultI2C; + } + + Result GetSocSpeedo(u32 &socSpeedo) { + constexpr u64 FusePhysicalAddress = 0x7000F000; + u64 virtualAddress = 0; + constexpr u64 Size = 0x1000; + + u64 outSize; + /* TODO: use svc::QueryMemoryMapping instead. */ + R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size)); + + constexpr u32 FuseOffset = 2048; + constexpr u32 SocSpeedoOffset = 308; + socSpeedo = *reinterpret_cast(virtualAddress + FuseOffset + SocSpeedoOffset); + + R_SUCCEED(); + } + + u32 GetSocProcessId(u32 socSpeedo) { + if (socSpeedo <= 1597) { + return 0; + } + + if (socSpeedo <= 1708) { + return 1; + } + + /* >= 1709. */ + return 2; + } + + Result SocVoltAsm(u32 *compareSpeedos) { + constexpr u32 VoltageScanLimit = 10; + /* Might actually be speedo id. */ + u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd); + R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 writeProcessIdRd = asm_get_rd(*writeProcessId); + + /* This writes 1050mV. */ + u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd); + R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 writeVoltageRd = asm_get_rd(*writeVoltage); + + /* A csel instruction is used to select the soc voltage limit register. */ + /* We care about its destination register since that is used for verification. */ + constexpr u32 VoltageSelectScanLimit = 24; + u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg); + R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); + /* Todo: check rm and rn? */ + u8 selectVoltageRd = asm_get_rd(*selectVoltage); + + /* rdCsel is then multiplied by 1000 to convert to uV. */ + /* This is pretty far down the function. */ + constexpr u32 MultiplierScanLimit = 200; + u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg); + R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern()); + u8 multiplierRn = AsmGetMullRn(*multiplier); + u8 multiplierRm = AsmGetMullRm(*multiplier); + /* One of the two registers has to be rdCsel. */ + R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern()); + u8 multiplierRd = asm_get_rd(*multiplier); + + /* Subs instruction is then used to verify against absolute limit. */ + u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd); + u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg); + R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern()); + + /* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */ + u32 *branchToAbort = limitValidation + 1; + R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern()); + + if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) { + R_SKIP(); + } + + /* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */ + /* 2200 should be high enough :D */ + u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200); + PATCH_OFFSET(compareSpeedos, compareSpeedosPatch); + + u32 socSpeedo = 0; + R_TRY(GetSocSpeedo(socSpeedo)); + + /* Adjust processId from 0 to [process id of switch booting this]. */ + /* We're overwriting the orr instruction entirly. */ + u32 processId = GetSocProcessId(socSpeedo); + u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd); + PATCH_OFFSET(writeProcessId, writeProcessIdPatch); + + /* Adjust voltage limit. */ + u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd); + PATCH_OFFSET(writeVoltage, voltageLimitPatch); + + /* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */ + PATCH_OFFSET(branchToAbort, NopIns); + + R_SUCCEED(); + } + + Result SocVoltLimit(u32 *ptr) { + R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit()); + if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) { + R_SKIP(); + } + + constexpr u32 Step = 25; + u32 maxVolt = C.marikoSocVmax; + if (maxVolt % Step) { + maxVolt = maxVolt / Step * Step; /* Round. */ + } + + u32 volt = SocVoltLimitOfficial; + for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) { + volt += Step; + PATCH_OFFSET(ptr + i, volt); + } + + R_SUCCEED(); + } + + Result EmcRateSessLimit(u32 *ptr) { + u32 movzI = 0; + R_UNLESS(EmcRateSessFindClamp(ptr, nullptr, nullptr, &movzI), ldr::ResultInvalidEmcRateList()); + + /* Reject cmd11 GetDvfsTable. */ + for (u32 i = 1; i <= 24; ++i) { + const u32 w = ptr[i]; + if (AsmIsSubX29Imm(w) && AsmGetImm12(w) >= 0x20u) { /* sub x?,x29,#>=0x20 */ + R_THROW(ldr::ResultInvalidEmcRateList()); + } + } + + /* mov x,x2 */ + u32 descReg = 0xFFu; + for (u32 i = 1; i <= 24; ++i) { + if (AsmIsMovReg(ptr[i], 2)) { descReg = asm_get_rd(ptr[i]); break; } + } + R_UNLESS(descReg != 0xFFu, ldr::ResultInvalidEmcRateList()); + + /* Repoint the duplicated-imm pair */ + u32 *adds[8]; u32 addImm[8]; u32 nAdds = 0; + for (u32 i = 1; i <= 24 && nAdds < 8; ++i) { + const u32 w = ptr[i]; + if (AsmIsAddSpImm(w)) { /* add x?,sp,#imm12 (shift 0) */ + adds[nAdds] = ptr + i; + addImm[nAdds] = AsmGetImm12(w); + ++nAdds; + } + } + u32 patched = 0; + for (u32 a = 0; a < nAdds; ++a) { + bool dup = false; + for (u32 b = 0; b < nAdds; ++b) { + if (a != b && addImm[a] == addImm[b]) { dup = true; break; } + } + if (dup) { + PATCH_OFFSET(adds[a], AsmMakeLdrImm64(asm_get_rd(*adds[a]), descReg, 0)); /* ldr x?,[x] */ + ++patched; + } + } + R_UNLESS(patched == 2, ldr::ResultInvalidEmcRateList()); + + /* min(maxCount, 32) -> min(maxCount, EmcDvfsTableEntryCount) */ + PATCH_OFFSET(ptr, AsmSubsSetImm12(*ptr, static_cast(EmcDvfsTableEntryCount))); /* cmp w?,#64 */ + PATCH_OFFSET(ptr + movzI, asm_set_imm16(*(ptr + movzI), static_cast(EmcDvfsTableEntryCount))); /* movz w?,#64 */ + R_SUCCEED(); + } + + void Patch(uintptr_t mapped_nso, size_t nso_size) { + nsoStart = reinterpret_cast(mapped_nso); + + g_pcv_scratch = mapped_nso + nso_size - HocPcvScratchSize; + g_nso_size = nso_size; + g_cave_cursor = g_pcv_cave; /* start the .text-cave bump allocator (0 if unavailable) */ + + MtcGenerateFreqTables(); + + u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); + u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); + + PatcherEntry patches[] = { + { "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit }, + { "CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq }, + { "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial }, + { "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial }, + { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low }, + { "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial }, + { "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial }, + { "GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, + { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, + { "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, + { "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, + { "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit }, + { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit }, + { "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit }, + { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit }, + { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault }, + { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default }, + { "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn }, + { "EMC DVFS Count", &EmcDvfsCountLimit, 1, &EmcDvfsCountPatternFn }, + { "EMC SoC LUT", &EmcSocLutReloc, 1, &EmcSocLutPatternFn }, + { "EMC Rate List", &EmcRateListLimit, 0, &EmcRateListPatternFn }, + { "EMC Rate Sess", &EmcRateSessLimit, 1, &EmcRateSessPatternFn }, + { "Bus Freq Reloc", &BusFreqReloc, 1, &BusFreqRelocPatternFn }, + { "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn }, + { "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial }, + /* Debugging patches */ + #if HOC_UART_LOG + { "NvLog Redirect", &NvLogUartRedirect, 1, &NvLogVsnprintfPatternFn, 0, 0, true }, + { "Force Verbosity", &ForceVerbosity, 3, &ForceVerbosityPatternFn, 0, 0, true }, + #endif + }; + + for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) { + u32 *ptr32 = reinterpret_cast(ptr); + for (auto &entry : patches) { + if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) { + break; + } + } + } + + for (auto &entry : patches) { + LOGGING("%s Count: %zu", entry.description, entry.patched_count); + if (R_FAILED(entry.CheckResult())) { + panic::SmcError(panic::Patch); + + CRASH(entry.description); + } + } + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp similarity index 71% rename from Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp index 65955f1a..33b7ed09 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko.hpp @@ -20,12 +20,14 @@ #pragma once -#include "../oc_common.hpp" -#include "pcv_common.hpp" -#include "pcv_asm.hpp" +#include "../../oc_common.hpp" +#include "../pcv_common.hpp" +#include "../pcv_asm.hpp" namespace ams::ldr::hoc::pcv::mariko { + extern u32 *nsoStart; + constexpr cvb_entry_t CpuCvbTableDefault[] = { { 204000, { 721589, -12695, 27 }, { } }, { 306000, { 747134, -14195, 27 }, { } }, @@ -299,6 +301,131 @@ namespace ams::ldr::hoc::pcv::mariko { return AsmCompareAddNoImm12(*ptr, MtcAddAsm); } + constexpr u32 EmcCountCmpAsm = 0x7100851F; /* cmp w?,#0x21 (subs wzr,w?,#0x21) */ + + /* + str ,[,#0x120] ; volt-array pointer + str w?, [,#0x154] ; num_freqs + */ + constexpr u32 EmcSocLutPtrStoreAsm = 0xF9009009; /* str x?,[x0,#0x120] (anchor) */ + constexpr u32 EmcSocLutCountStoreAsm = 0xB9015408; /* str w?,[x0,#0x154] (anchor) */ + constexpr u32 EmcSocFreqStoreAsm = 0xF9000D00; /* str x?,[x8,#0x18] */ + constexpr u32 EmcSocVoltStoreAsm = 0xB9004900; /* str w?,[x8,#0x48] (socMinLut[i]) */ + constexpr u32 EmcSocReadLoadAsm = 0xB9404929; /* ldr w?,[x9,#0x48] (socMinLut readback) */ + + inline bool EmcDvfsCountPatternFn(u32 *ptr) { + /* Local context: cbz w?, ; cmp w?,#0x21 ; b.cs */ + return asm_compare_no_rd(*ptr, EmcCountCmpAsm) && AsmCompareBrConNoImm19(*(ptr + 1), 0x54000002) /* b.cs */ + && AsmCbzCompareOpcodeOnly(*(ptr - 1), 0x34000000); /* cbz */ + } + + inline bool EmcSocLutPatternFn(u32 *ptr) { + return asm_compare_no_rd(*ptr, EmcSocLutPtrStoreAsm) /* str x?,[x0,#0x120] */ + && asm_compare_no_rd(*(ptr + 1), EmcSocLutCountStoreAsm); /* str w?,[x0,#0x154] */ + } + + /* + mov w?,#0x20 ; the 32 cap + cmp w?,#0x20 + csel w?,w?,w?,lt ; w? = min(maxCount, 32) + bl TegraGetEmcDvfsFreqTable + + cmp w?,#0x20 ; (maxCount) + csel w?,,,lt ; min(maxCount, 32) + bl + */ + constexpr u32 EmcRateCapCmpAsm = 0x710082FF; /* cmp w?,#0x20 */ + constexpr u32 EmcRateCapCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt */ + + inline bool EmcRateListPatternFn(u32 *ptr) { + return AsmSubsCompareNoReg(*ptr, EmcRateCapCmpAsm) /* cmp w?,#0x20 */ + && AsmCompareCselNoReg(*(ptr + 1), EmcRateCapCselAsm) /* csel w?,w?,w?,lt */ + && (AsmGetRn(*ptr) == AsmGetRn(*(ptr + 1))) /* min(reg, 0x20) */ + && AsmBlCompareOpcodeOnly(*(ptr + 2), 0x94000000); /* bl */ + } + + constexpr u32 EmcRateSessCmpAsm = 0x710082FF; /* cmp w?,#0x20 */ + constexpr u32 EmcRateSessMovAsm = 0x52800400; /* movz w?,#0x20 */ + constexpr u32 EmcRateSessCselAsm = 0x1A80B000; /* csel w?,w?,w?,lt (opcode + cond) */ + + inline bool EmcRateSessFindClamp(u32 *ptr, u32 *out_c, u32 *out_cap, u32 *out_movz_i) { + if (!AsmSubsCompareNoReg(ptr[0], EmcRateSessCmpAsm)) return false; /* cmp w,#0x20 */ + const u32 c = AsmGetRn(ptr[0]); + for (u32 i = 1; i <= 14; ++i) { + const u32 w = ptr[i]; + if (AsmCompareCselNoReg(w, EmcRateSessCselAsm) && AsmGetRn(w) == c && asm_get_rd(w) == c) { + const u32 cap = AsmGetRm(w); + for (u32 j = 1; j < i; ++j) { + if ((ptr[j] & 0xFFFFFFE0u) == EmcRateSessMovAsm && asm_get_rd(ptr[j]) == cap) { + if (out_c) *out_c = c; + if (out_cap) *out_cap = cap; + if (out_movz_i) *out_movz_i = j; + return true; + } + } + return false; + } + } + return false; + } + + inline bool EmcRateSessPatternFn(u32 *ptr) { + return EmcRateSessFindClamp(ptr, nullptr, nullptr, nullptr); + } + + inline bool BusFreqRelocPatternFn(u32 *ptr) { + if (g_pcv_scratch == 0 || g_pcv_cave == 0) { + return false; + } + if (reinterpret_cast(ptr + 4) > g_pcv_cave) { /* the call site lives in .text */ + return false; + } + if (!(AsmIsLdrImm64(ptr[0]) && AsmGetLdStImm64Off(ptr[0]) == 0x10)) return false; /* ldr Xbuf,[Xbus,#0x10] */ + if (!(AsmIsAddImm64(ptr[1]) && AsmGetImm12(ptr[1]) == 0x18)) return false; /* add Xcnt,Xbus,#0x18 */ + if (!(AsmIsStrImm64(ptr[2]) && AsmGetLdStImm64Off(ptr[2]) == 0x50)) return false; /* str Xrail,[Xbus,#0x50]*/ + if (!AsmIsBl(ptr[3])) return false; /* bl GetDvfsRailUnique */ + const u32 bus = AsmGetRn(ptr[0]); + return AsmGetRn(ptr[1]) == bus && AsmGetRn(ptr[2]) == bus; + } + + inline bool ForceVerbosityPatternFn(u32 *ptr) { + if (HOC_PCV_FORCE_VERBOSITY == 0 || g_pcv_cave == 0) { + return false; + } + if (reinterpret_cast(ptr + 11) > g_pcv_cave) { /* .text only */ + return false; + } + if (ptr[0] != 0xA9BE7BFDu || ptr[1] != 0xF9000BF3u || ptr[2] != 0x910003FDu) return false; /* stp/str/mov x29,sp */ + if (!(AsmIsAddImm64(ptr[3]) && asm_get_rd(ptr[3]) == 0 && AsmGetRn(ptr[3]) == 29)) return false; /* add x0,x29,#imm */ + if (!(AsmIsAddImm64(ptr[4]) && asm_get_rd(ptr[4]) == 19 && AsmGetRn(ptr[4]) == 29)) return false; /* add x19,x29,#imm */ + if (AsmGetImm12(ptr[3]) != AsmGetImm12(ptr[4]) || !AsmIsBl(ptr[5])) return false; + for (u32 j = 6; j <= 10; ++j) { + if (ptr[j] == 0x7100001Fu) { /* cmp w0,#0 */ + return true; + } + } + return false; + } + + /* vsnprintf(buf,size,fmt,va_list) prologue */ + inline constexpr u32 NvLogVsnSig[] = { 0xD10483FFu, 0xA9107BFDu, 0xF9008BFCu, 0x910403FDu, 0xF100003Fu }; + + inline bool NvLogVsnprintfPatternFn(u32 *ptr) { + if (HOC_UART_LOG == 0 || g_pcv_cave == 0) { + return false; + } + if (reinterpret_cast(ptr + std::size(NvLogVsnSig)) > g_pcv_cave) { /* must sit in .text */ + return false; + } + for (size_t k = 0; k < std::size(NvLogVsnSig); ++k) { + if (ptr[k] != NvLogVsnSig[k]) { + return false; + } + } + return true; + } + + void Patch(uintptr_t mapped_nso, size_t nso_size); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp new file mode 100644 index 00000000..01400c9d --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#include "../pcv.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + u32 CapCpuClock() { + u32 cpuCap = allowedCpuMaxFrequencies[0]; + + for (u32 freq : allowedCpuMaxFrequencies) { + if (C.marikoCpuMaxClock >= freq) { + cpuCap = freq; + } else { + break; + } + } + return cpuCap; + } + + Result CpuFreqVdd(u32 *ptr) { + dvfs_rail *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); + + R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry()); + R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry()); + + if (C.marikoCpuUVHigh) { + PATCH_OFFSET(ptr, CapCpuClock()); + } else { + PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); + } + + R_SUCCEED(); + } + + Result CpuVoltDVFS(u32 *ptr) { + CvbMeta *cpuCvbMeta = reinterpret_cast(reinterpret_cast(ptr) - offsetof(CvbMeta, vmin)); + + R_UNLESS(cpuCvbMeta->highVmin == CpuHighVminOfficial, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->unkStepMaybe == 38, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->vmax == CpuVoltOfficial, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->unkScale2 == 1000, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->speedoScale == 100, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->voltageScale == 1000, ldr::ResultInvalidCpuMinVolt()); + R_UNLESS(cpuCvbMeta->unkZero5 == 0, ldr::ResultInvalidCpuMinVolt()); + + if (C.marikoCpuLowVmin) { + PATCH_OFFSET(&(cpuCvbMeta->vmin), C.marikoCpuLowVmin); + } + + if (C.marikoCpuHighVmin) { + PATCH_OFFSET(&(cpuCvbMeta->highVmin), C.marikoCpuHighVmin); + } + + if (C.marikoCpuMaxVolt) { + PATCH_OFFSET(&(cpuCvbMeta->vmax), C.marikoCpuMaxVolt); + } + + R_SUCCEED(); + } + + Result CpuVoltThermals(u32 *ptr) { + if (std::memcmp(ptr, cpuVoltThermalData, sizeof(cpuVoltThermalData))) { + R_THROW(ldr::ResultInvalidCpuMinVolt()); + } + + if (C.marikoCpuLowVmin) { + PATCH_OFFSET(ptr, C.marikoCpuLowVmin); + PATCH_OFFSET(ptr + 3, C.marikoCpuLowVmin); + } + + if (C.marikoCpuMaxVolt) { + PATCH_OFFSET(ptr - 2, C.marikoCpuMaxVolt); + PATCH_OFFSET(ptr - 5, C.marikoCpuMaxVolt); + PATCH_OFFSET(ptr + 1, C.marikoCpuMaxVolt); + PATCH_OFFSET(ptr + 4, C.marikoCpuMaxVolt); + } + + R_SUCCEED(); + } + + Result CpuVoltDfll(u32 *ptr) { + CvbCpuDfllData *entry = reinterpret_cast(ptr); + + R_UNLESS(entry->tune0_low == 0xFFCF, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_low == 0x12207FF, ldr::ResultInvalidCpuVoltDfllEntry()); + R_UNLESS(entry->tune1_high == 0x3FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry()); + + switch (C.marikoCpuUVLow) { + case 1: + PATCH_OFFSET(&(entry->tune0_low), 0xffa0); + PATCH_OFFSET(&(entry->tune0_high), 0xffff); + PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); + PATCH_OFFSET(&(entry->tune1_high), 0x0); + break; + case 2: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27207ff); + break; + case 3: + PATCH_OFFSET(&(entry->tune0_low), 0xffdf); + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27307ff); + break; + case 4: + PATCH_OFFSET(&(entry->tune0_low), 0xffff); + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); + break; + case 5: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27707ff); + break; + case 6: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); + break; + case 7: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); + break; + case 8: + PATCH_OFFSET(&(entry->tune0_low), 0xdfff); + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); + break; + case 9: + PATCH_OFFSET(&(entry->tune0_low), 0xdfff); + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff); + break; + case 10: + PATCH_OFFSET(&(entry->tune0_low), 0xdfff); + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff); + break; + case 11: + PATCH_OFFSET(&(entry->tune0_low), 0xdfff); + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff); + break; + case 12: + PATCH_OFFSET(&(entry->tune0_low), 0xdfff); + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); + PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff); + break; + default: + break; + } + + switch (C.marikoCpuUVHigh) { + case 1: + PATCH_OFFSET(&(entry->tune1_high), 0x0); + PATCH_OFFSET(&(entry->tune0_high), 0xffff); + break; + case 2: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27207ff); + break; + case 3: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27307ff); + break; + case 4: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); + break; + case 5: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27707ff); + break; + case 6: + PATCH_OFFSET(&(entry->tune0_high), 0xffdf); + PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); + break; + case 7: + case 8: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); + break; + case 9: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff); + break; + case 10: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff); + break; + case 11: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff); + break; + case 12: + PATCH_OFFSET(&(entry->tune0_high), 0xdfff); + PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff); + break; + default: + break; + } + + R_SUCCEED(); + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp new file mode 100644 index 00000000..f6a570d4 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_cpu.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#pragma once + +#include "../pcv.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + Result CpuFreqVdd(u32 *ptr); + Result CpuVoltDVFS(u32 *ptr); + Result CpuVoltThermals(u32 *ptr); + Result CpuVoltDfll(u32 *ptr); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.cpp new file mode 100644 index 00000000..5b6c1286 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#include "../pcv.hpp" +#include "../pcv_asm.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + Result GpuVoltDVFS(u32 *ptr) { + /* Check for valid pattern. */ + for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) { + if (*(ptr + i + 1) != gpuDVFSPattern[i]) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + } + + /* Default value is 1050mV. */ + if (C.marikoGpuVmax) { + PATCH_OFFSET(ptr + 1, C.marikoGpuVmax); + } + + if (C.marikoGpuVmin) { + PATCH_OFFSET(ptr, C.marikoGpuVmin); + } + + R_SUCCEED(); + } + + Result GpuVoltThermals(u32 *ptr) { + if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) { + R_THROW(ldr::ResultInvalidGpuDvfs()); + } + + // if (C.marikoGpuBootVolt) { + // PATCH_OFFSET(ptr - 3, C.marikoGpuBootVolt); + // } + + if (C.marikoGpuVmin) { + PATCH_OFFSET(ptr, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 3, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 6, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 9, C.marikoGpuVmin); + PATCH_OFFSET(ptr + 12, C.marikoGpuVmin); + } + + R_SUCCEED(); + } + + Result GpuFreqMaxAsm(u32 *ptr32) { + // Check if both two instructions match the pattern + u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); + if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) { + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } + + // Both instructions should operate on the same register + u8 rd = asm_get_rd(ins1); + if (rd != asm_get_rd(ins2)) { + R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); + } + + u32 max_clock; + switch (C.marikoGpuUV) { + case 0: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; + break; + case 1: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq; + break; + case 2: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; + break; + case 3: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT15)->freq; + break; + case 4: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHighUV)->freq; + break; + default: + max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; + break; + } + + u32 asm_patch[2] = { + asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), + asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd) + }; + + PATCH_OFFSET(ptr32, asm_patch[0]); + PATCH_OFFSET(ptr32 + 1, asm_patch[1]); + + R_SUCCEED(); + } + + Result GpuFreqPllMax(u32 *ptr) { + clk_pll_param *entry = reinterpret_cast(ptr); + + // All zero except for freq + for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) { + R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); + } + + // Double the max clk simply + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; + R_SUCCEED(); + } + + Result GpuFreqPllLimit(u32 *ptr) { + u32 prev_freq = *(ptr - 1); + + if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) { + R_THROW(ldr::ResultInvalidGpuPllEntry()); + } + + PATCH_OFFSET(ptr, 3600000); + + R_SUCCEED(); + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.hpp new file mode 100644 index 00000000..f5e4c33f --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_gpu.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#pragma once + +#include "../pcv.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + Result GpuVoltDVFS(u32 *ptr); + Result GpuVoltThermals(u32 *ptr); + Result GpuFreqMaxAsm(u32 *ptr32); + Result GpuFreqPllMax(u32 *ptr); + Result GpuFreqPllLimit(u32 *ptr); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp similarity index 56% rename from Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp index 9d26be3a..7dacbdbe 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_mariko.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.cpp @@ -20,333 +20,14 @@ * along with this program. If not, see . */ -#include -#include "pcv.hpp" -#include "../mtc_timing_value.hpp" -#include "../mariko/calculate_timings.hpp" +#include "../pcv.hpp" +#include "../../mtc_timing_value.hpp" +#include "calculate_timings_mariko.hpp" namespace ams::ldr::hoc::pcv::mariko { - Result GpuVoltDVFS(u32 *ptr) { - /* Check for valid pattern. */ - for (size_t i = 0; i < std::size(gpuDVFSPattern); ++i) { - if (*(ptr + i + 1) != gpuDVFSPattern[i]) { - R_THROW(ldr::ResultInvalidGpuDvfs()); - } - } - - /* Default value is 1050mV. */ - if (C.marikoGpuVmax) { - PATCH_OFFSET(ptr + 1, C.marikoGpuVmax); - } - - if (C.marikoGpuVmin) { - PATCH_OFFSET(ptr, C.marikoGpuVmin); - } - - R_SUCCEED(); - } - - Result GpuVoltThermals(u32 *ptr) { - if (std::memcmp(ptr - 3, gpuVoltThermalPattern, sizeof(gpuVoltThermalPattern))) { - R_THROW(ldr::ResultInvalidGpuDvfs()); - } - - // if (C.marikoGpuBootVolt) { - // PATCH_OFFSET(ptr - 3, C.marikoGpuBootVolt); - // } - - if (C.marikoGpuVmin) { - PATCH_OFFSET(ptr, C.marikoGpuVmin); - PATCH_OFFSET(ptr + 3, C.marikoGpuVmin); - PATCH_OFFSET(ptr + 6, C.marikoGpuVmin); - PATCH_OFFSET(ptr + 9, C.marikoGpuVmin); - PATCH_OFFSET(ptr + 12, C.marikoGpuVmin); - } - - R_SUCCEED(); - } - - u32 CapCpuClock() { - u32 cpuCap = allowedCpuMaxFrequencies[0]; - - for (u32 freq : allowedCpuMaxFrequencies) { - if (C.marikoCpuMaxClock >= freq) { - cpuCap = freq; - } else { - break; - } - } - return cpuCap; - } - - Result CpuFreqVdd(u32 *ptr) { - dvfs_rail *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(dvfs_rail, freq)); - - R_UNLESS(entry->id == 1, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->min_mv == 250'000, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->step_mv == 5000, ldr::ResultInvalidCpuFreqVddEntry()); - R_UNLESS(entry->max_mv == 1525'000, ldr::ResultInvalidCpuFreqVddEntry()); - - if (C.marikoCpuUVHigh) { - PATCH_OFFSET(ptr, CapCpuClock()); - } else { - PATCH_OFFSET(ptr, GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); - } - - R_SUCCEED(); - } - - Result CpuVoltDVFS(u32 *ptr) { - CvbMeta *cpuCvbMeta = reinterpret_cast(reinterpret_cast(ptr) - offsetof(CvbMeta, vmin)); - - R_UNLESS(cpuCvbMeta->highVmin == CpuHighVminOfficial, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->unkStepMaybe == 38, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->vmax == CpuVoltOfficial, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->unkScale2 == 1000, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->speedoScale == 100, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->voltageScale == 1000, ldr::ResultInvalidCpuMinVolt()); - R_UNLESS(cpuCvbMeta->unkZero5 == 0, ldr::ResultInvalidCpuMinVolt()); - - if (C.marikoCpuLowVmin) { - PATCH_OFFSET(&(cpuCvbMeta->vmin), C.marikoCpuLowVmin); - } - - if (C.marikoCpuHighVmin) { - PATCH_OFFSET(&(cpuCvbMeta->highVmin), C.marikoCpuHighVmin); - } - - if (C.marikoCpuMaxVolt) { - PATCH_OFFSET(&(cpuCvbMeta->vmax), C.marikoCpuMaxVolt); - } - - R_SUCCEED(); - } - - Result CpuVoltThermals(u32 *ptr) { - if (std::memcmp(ptr, cpuVoltThermalData, sizeof(cpuVoltThermalData))) { - R_THROW(ldr::ResultInvalidCpuMinVolt()); - } - - if (C.marikoCpuLowVmin) { - PATCH_OFFSET(ptr, C.marikoCpuLowVmin); - PATCH_OFFSET(ptr + 3, C.marikoCpuLowVmin); - } - - if (C.marikoCpuMaxVolt) { - PATCH_OFFSET(ptr - 2, C.marikoCpuMaxVolt); - PATCH_OFFSET(ptr - 5, C.marikoCpuMaxVolt); - PATCH_OFFSET(ptr + 1, C.marikoCpuMaxVolt); - PATCH_OFFSET(ptr + 4, C.marikoCpuMaxVolt); - } - - R_SUCCEED(); - } - - Result CpuVoltDfll(u32 *ptr) { - CvbCpuDfllData *entry = reinterpret_cast(ptr); - - R_UNLESS(entry->tune0_low == 0xFFCF, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune0_high == 0x0, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_low == 0x12207FF, ldr::ResultInvalidCpuVoltDfllEntry()); - R_UNLESS(entry->tune1_high == 0x3FFF7FF, ldr::ResultInvalidCpuVoltDfllEntry()); - - switch (C.marikoCpuUVLow) { - case 1: - PATCH_OFFSET(&(entry->tune0_low), 0xffa0); - PATCH_OFFSET(&(entry->tune0_high), 0xffff); - PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); - PATCH_OFFSET(&(entry->tune1_high), 0x0); - break; - case 2: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27207ff); - break; - case 3: - PATCH_OFFSET(&(entry->tune0_low), 0xffdf); - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27307ff); - break; - case 4: - PATCH_OFFSET(&(entry->tune0_low), 0xffff); - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_low), 0x21107ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); - break; - case 5: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27707ff); - break; - case 6: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); - break; - case 7: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21607ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); - break; - case 8: - PATCH_OFFSET(&(entry->tune0_low), 0xdfff); - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); - break; - case 9: - PATCH_OFFSET(&(entry->tune0_low), 0xdfff); - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff); - break; - case 10: - PATCH_OFFSET(&(entry->tune0_low), 0xdfff); - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff); - break; - case 11: - PATCH_OFFSET(&(entry->tune0_low), 0xdfff); - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff); - break; - case 12: - PATCH_OFFSET(&(entry->tune0_low), 0xdfff); - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_low), 0x21707ff); - PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff); - break; - default: - break; - } - - switch (C.marikoCpuUVHigh) { - case 1: - PATCH_OFFSET(&(entry->tune1_high), 0x0); - PATCH_OFFSET(&(entry->tune0_high), 0xffff); - break; - case 2: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27207ff); - break; - case 3: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27307ff); - break; - case 4: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27407ff); - break; - case 5: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27707ff); - break; - case 6: - PATCH_OFFSET(&(entry->tune0_high), 0xffdf); - PATCH_OFFSET(&(entry->tune1_high), 0x27807ff); - break; - case 7: - case 8: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27b07ff); - break; - case 9: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27c07ff); - break; - case 10: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27d07ff); - break; - case 11: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27e07ff); - break; - case 12: - PATCH_OFFSET(&(entry->tune0_high), 0xdfff); - PATCH_OFFSET(&(entry->tune1_high), 0x27f07ff); - break; - default: - break; - } - - R_SUCCEED(); - } - - Result GpuFreqMaxAsm(u32 *ptr32) { - // Check if both two instructions match the pattern - u32 ins1 = *ptr32, ins2 = *(ptr32 + 1); - if (!(asm_compare_no_rd(ins1, GpuAsmPattern[0]) && asm_compare_no_rd(ins2, GpuAsmPattern[1]))) { - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - } - - // Both instructions should operate on the same register - u8 rd = asm_get_rd(ins1); - if (rd != asm_get_rd(ins2)) { - R_THROW(ldr::ResultInvalidGpuFreqMaxPattern()); - } - - u32 max_clock; - switch (C.marikoGpuUV) { - case 0: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTable)->freq; - break; - case 1: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableSLT)->freq; - break; - case 2: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; - break; - case 3: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT15)->freq; - break; - case 4: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHighUV)->freq; - break; - default: - max_clock = GetDvfsTableLastEntry(C.marikoGpuDvfsTableHiOPT)->freq; - break; - } - - u32 asm_patch[2] = { - asm_set_rd(asm_set_imm16(GpuAsmPattern[0], max_clock), rd), - asm_set_rd(asm_set_imm16(GpuAsmPattern[1], max_clock >> 16), rd) - }; - - PATCH_OFFSET(ptr32, asm_patch[0]); - PATCH_OFFSET(ptr32 + 1, asm_patch[1]); - - R_SUCCEED(); - } - - Result GpuFreqPllMax(u32 *ptr) { - clk_pll_param *entry = reinterpret_cast(ptr); - - // All zero except for freq - for (size_t i = 1; i < sizeof(clk_pll_param) / sizeof(u32); i++) { - R_UNLESS(*(ptr + i) == 0, ldr::ResultInvalidGpuPllEntry()); - } - - // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; - R_SUCCEED(); - } - - Result GpuFreqPllLimit(u32 *ptr) { - u32 prev_freq = *(ptr - 1); - - if (prev_freq != 128000 && prev_freq != 1300000 && prev_freq != 76800) { - R_THROW(ldr::ResultInvalidGpuPllEntry()); - } - - PATCH_OFFSET(ptr, 3600000); - - R_SUCCEED(); + namespace { + std::vector newEmcList; } void MemMtcTableAutoAdjust(MarikoMtcTable *table) { @@ -617,11 +298,6 @@ namespace ams::ldr::hoc::pcv::mariko { } } - namespace { - std::vector newEmcList; - u32 *nsoStart; - } - void MtcGenerateJedecTable() { const u32 jedecFreqs[] = { 1866000, 1996000, 2133000, 2400000, 2666000, 2933000, 3200000 }; constexpr u32 JedecFreqCount = std::size(jedecFreqs); @@ -638,7 +314,7 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } void MtcGenerate133StepTable() { @@ -657,12 +333,39 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); + } + + void MtcGenerate33StepTable() { + /* ~33.33MHz but rounded*/ + const u32 stepFreqs33[] = { + 1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000, + 1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000, + 2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000, + 2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000, + 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000, + 3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000, + }; + constexpr u32 StepFreqs33Size = std::size(stepFreqs33); + + for (u32 i = 0; i < StepFreqs33Size; ++i) { + if (stepFreqs33[i] <= C.marikoEmcMaxClock) { + newEmcList.push_back(stepFreqs33[i]); + } else { + break; + } + } + + if (newEmcList.back() != C.marikoEmcMaxClock) { + newEmcList.push_back(static_cast(C.marikoEmcMaxClock)); + } + + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } void MtcGenerateFreqTables() { newEmcList.clear(); - newEmcList.reserve(DvfsTableEntryCount); + newEmcList.reserve(EmcDvfsTableEntryCount); newEmcList.insert(newEmcList.end(), std::begin(EmcListDefault), std::end(EmcListDefault)); if (C.marikoEmcMaxClock <= EmcClkOSLimit) { @@ -671,6 +374,9 @@ namespace ams::ldr::hoc::pcv::mariko { u32 stepRate = 0; switch (C.stepMode) { + case StepMode_33MHz: + MtcGenerate33StepTable(); + return; case StepMode_66MHz: stepRate = 66667; break; @@ -701,7 +407,7 @@ namespace ams::ldr::hoc::pcv::mariko { newEmcList.push_back(newFreq); } - newEmcList.resize(std::min(newEmcList.size(), DvfsTableEntryLimit)); + newEmcList.resize(std::min(newEmcList.size(), EmcDvfsTableEntryLimit)); } Result VerifyMtcTable(MarikoMtcTable *tableStart, u32 expectedFreq) { @@ -769,6 +475,7 @@ namespace ams::ldr::hoc::pcv::mariko { Result MemFreqMtcTable(u32 *ptr) { static const DramId dramId = [] { DramId id = GetDramId(); + id = HOAG_4GB_MICRON_MT53E512M32D2NP_046_WTF; return id; }(); @@ -905,24 +612,23 @@ namespace ams::ldr::hoc::pcv::mariko { #undef DVB #undef DVB_OC - DvbEntry emcDvbTableOc[newEmcList.size()]; + const size_t dvbCount = std::min(newEmcList.size(), DvbTableCapacity); + DvbEntry emcDvbTableOc[DvbTableCapacity] = {}; u32 bracketIndex = 0; - for (u32 i = 0; i < newEmcList.size(); ++i) { - while (newEmcList[i] >= emcDvbOcTableBrackets[bracketIndex + 1].freq) { + for (size_t i = 0; i < dvbCount; ++i) { + const u32 freq = (i == dvbCount - 1) ? static_cast(newEmcList.back()) : newEmcList[i]; + while (freq >= emcDvbOcTableBrackets[bracketIndex + 1].freq) { ++bracketIndex; } - emcDvbTableOc[i].freq = newEmcList[i]; + emcDvbTableOc[i].freq = freq; std::memcpy(emcDvbTableOc[i].volt, emcDvbOcTableBrackets[bracketIndex].volt, sizeof(emcDvbTableOc[i].volt)); } - std::memset(mem_dvb_table_head, 0, sizeof(EmcDvbTableDefault)); - std::memcpy(mem_dvb_table_head, &emcDvbTableOc, sizeof(emcDvbTableOc)); - - /* Max dvfs entry is 32, but HOS doesn't seem to boot if exact freq doesn't exist in dvb table, - reason why it's like this - */ + /* Clear the entire 32-entry region */ + std::memset(mem_dvb_table_head, 0, DvbTableCapacity * sizeof(DvbEntry)); + std::memcpy(mem_dvb_table_head, emcDvbTableOc, dvbCount * sizeof(DvbEntry)); R_SUCCEED(); } @@ -936,57 +642,6 @@ namespace ams::ldr::hoc::pcv::mariko { R_SUCCEED(); } - Result I2cSet_U8(I2cDevice dev, u8 reg, u8 val) { - struct { - u8 reg; - u8 val; - } __attribute__((packed)) cmd; - - I2cSession _session; - R_TRY(i2cOpenSession(&_session, dev)); - - cmd.reg = reg; - cmd.val = val; - Result res = i2csessionSendAuto(&_session, &cmd, sizeof(cmd), I2cTransactionOption_All); - i2csessionClose(&_session); - return res; - } - - Result EmcVddqVolt(u32 *ptr) { - regulator *entry = reinterpret_cast(reinterpret_cast(ptr) - offsetof(regulator, type_2_3.default_uv)); - - constexpr u32 uv_step = 5'000; - constexpr u32 uv_min = 250'000; - - auto validator = [entry]() { - R_UNLESS(entry->id == 2, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type == 3, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_2_3.step_uv == uv_step, ldr::ResultInvalidRegulatorEntry()); - R_UNLESS(entry->type_2_3.min_uv == uv_min, ldr::ResultInvalidRegulatorEntry()); - R_SUCCEED(); - }; - - R_TRY(validator()); - - u32 emc_uv = C.marikoEmcVddqVolt; - - if (!emc_uv) { - R_SKIP(); - } - - if (emc_uv % uv_step) { - emc_uv = (emc_uv + uv_step - 1) / uv_step * uv_step; // rounding - } - - PATCH_OFFSET(ptr, emc_uv); - - i2cInitialize(); - Result resultI2C = I2cSet_U8(I2cDevice_Max77812_2, 0x25, (emc_uv - uv_min) / uv_step); - i2cExit(); - - return resultI2C; - } - Result MemMtcTableAsm(u32 *ptr) { constexpr u32 AddpOffset = 1; constexpr u32 BrOffset = 12; @@ -1018,170 +673,4 @@ namespace ams::ldr::hoc::pcv::mariko { R_SUCCEED(); } - Result GetSocSpeedo(u32 &socSpeedo) { - constexpr u64 FusePhysicalAddress = 0x7000F000; - u64 virtualAddress = 0; - constexpr u64 Size = 0x1000; - - u64 outSize; - /* TODO: use svc::QueryMemoryMapping instead. */ - R_TRY(svcQueryMemoryMapping(&virtualAddress, &outSize, FusePhysicalAddress, Size)); - - constexpr u32 FuseOffset = 2048; - constexpr u32 SocSpeedoOffset = 308; - socSpeedo = *reinterpret_cast(virtualAddress + FuseOffset + SocSpeedoOffset); - - R_SUCCEED(); - } - - u32 GetSocProcessId(u32 socSpeedo) { - if (socSpeedo <= 1597) { - return 0; - } - - if (socSpeedo <= 1708) { - return 1; - } - - /* >= 1709. */ - return 2; - } - - Result SocVoltAsm(u32 *compareSpeedos) { - constexpr u32 VoltageScanLimit = 10; - /* Might actually be speedo id. */ - u32 *writeProcessId = ScanAssembly(compareSpeedos, VoltageScanLimit, SocVoltWriteProcessIdAsm, asm_compare_no_rd); - R_UNLESS(writeProcessId != nullptr, ldr::ResultInvalidSocVoltPattern()); - u8 writeProcessIdRd = asm_get_rd(*writeProcessId); - - /* This writes 1050mV. */ - u32 *writeVoltage = ScanAssembly(writeProcessId, VoltageScanLimit, SocVoltWriteVoltageAsm, asm_compare_no_rd); - R_UNLESS(writeVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); - u8 writeVoltageRd = asm_get_rd(*writeVoltage); - - /* A csel instruction is used to select the soc voltage limit register. */ - /* We care about its destination register since that is used for verification. */ - constexpr u32 VoltageSelectScanLimit = 24; - u32 *selectVoltage = ScanAssembly(writeVoltage, VoltageSelectScanLimit, SocVoltSelectRegisterAsm, AsmCompareCselNoReg); - R_UNLESS(selectVoltage != nullptr, ldr::ResultInvalidSocVoltPattern()); - /* Todo: check rm and rn? */ - u8 selectVoltageRd = asm_get_rd(*selectVoltage); - - /* rdCsel is then multiplied by 1000 to convert to uV. */ - /* This is pretty far down the function. */ - constexpr u32 MultiplierScanLimit = 200; - u32 *multiplier = ScanAssembly(selectVoltage, MultiplierScanLimit, SocVoltMultiplyVoltsAsm, AsmCompareMullNoReg); - R_UNLESS(multiplier != nullptr, ldr::ResultInvalidSocVoltPattern()); - u8 multiplierRn = AsmGetMullRn(*multiplier); - u8 multiplierRm = AsmGetMullRm(*multiplier); - /* One of the two registers has to be rdCsel. */ - R_UNLESS((multiplierRn == selectVoltageRd) || (multiplierRm == selectVoltageRd), ldr::ResultInvalidSocVoltPattern()); - u8 multiplierRd = asm_get_rd(*multiplier); - - /* Subs instruction is then used to verify against absolute limit. */ - u32 limitValidationPattern = AsmSubsSetRn(SocVoltValidateLimitAsm, multiplierRd); - u32 *limitValidation = ScanAssembly(multiplier, VoltageScanLimit, limitValidationPattern, AsmSubsCompareNoReg); - R_UNLESS(limitValidation != nullptr, ldr::ResultInvalidSocVoltPattern()); - - /* There is a b.gt instruction right after (checks for socVoltageCap < socVoltageMax). */ - u32 *branchToAbort = limitValidation + 1; - R_UNLESS(AsmCompareBrConNoImm19(*branchToAbort, SocVoltBranchToAbortAsm), ldr::ResultInvalidSocVoltPattern()); - - if (!C.marikoSocVmax || C.marikoSocVmax <= 1000) { - R_SKIP(); - } - - /* Adjust 1598 speedo minimum to ensure it always goes down process id 0 branch. */ - /* 2200 should be high enough :D */ - u32 compareSpeedosPatch = AsmSubsSetImm12(*compareSpeedos, 2200); - PATCH_OFFSET(compareSpeedos, compareSpeedosPatch); - - u32 socSpeedo = 0; - R_TRY(GetSocSpeedo(socSpeedo)); - - /* Adjust processId from 0 to [process id of switch booting this]. */ - /* We're overwriting the orr instruction entirly. */ - u32 processId = GetSocProcessId(socSpeedo); - u32 writeProcessIdPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, processId), writeProcessIdRd); - PATCH_OFFSET(writeProcessId, writeProcessIdPatch); - - /* Adjust voltage limit. */ - u32 voltageLimitPatch = asm_set_rd(asm_set_imm16(SocVoltWriteVoltageAsm, C.marikoSocVmax), writeVoltageRd); - PATCH_OFFSET(writeVoltage, voltageLimitPatch); - - /* Branches to an abort if limits are invalid -- we patch the branch instruction with NOP. */ - PATCH_OFFSET(branchToAbort, NopIns); - - R_SUCCEED(); - } - - Result SocVoltLimit(u32 *ptr) { - R_UNLESS(!std::memcmp(ptr - SocVoltLimitMaxDefaultIndex, socVoltLimitArray, sizeof(socVoltLimitArray)), ldr::ResultInvalidSocVoltLimit()); - if (!C.marikoSocVmax || C.marikoSocVmax <= SocVoltLimitOfficial) { - R_SKIP(); - } - - constexpr u32 Step = 25; - u32 maxVolt = C.marikoSocVmax; - if (maxVolt % Step) { - maxVolt = maxVolt / Step * Step; /* Round. */ - } - - u32 volt = SocVoltLimitOfficial; - for (u32 i = 1; i < DvfsTableEntryCount - SocVoltLimitMaxDefaultIndex && volt < maxVolt; ++i) { - volt += Step; - PATCH_OFFSET(ptr + i, volt); - } - - R_SUCCEED(); - } - - void Patch(uintptr_t mapped_nso, size_t nso_size) { - nsoStart = reinterpret_cast(mapped_nso); - MtcGenerateFreqTables(); - u32 CpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(CpuCvbTableDefault)->freq); - u32 GpuCvbDefaultMaxFreq = static_cast(GetDvfsTableLastEntry(GpuCvbTableDefault)->freq); - - PatcherEntry patches[] = { - { "CPU Freq Vdd", &CpuFreqVdd, 1, nullptr, CpuClkOSLimit }, - { "CPU Freq Table", CpuFreqCvbTable, 1, nullptr, CpuCvbDefaultMaxFreq }, - { "CPU Volt DVFS", &CpuVoltDVFS, 1, nullptr, CpuVminOfficial }, - { "CPU Volt Thermals", &CpuVoltThermals, 1, nullptr, CpuVminOfficial }, - { "CPU Volt Dfll", &CpuVoltDfll, 1, nullptr, CpuTune0Low }, - { "GPU Volt DVFS", &GpuVoltDVFS, 1, nullptr, GpuVminOfficial }, - { "GPU Volt Thermals", &GpuVoltThermals, 1, nullptr, GpuVminOfficial }, - { "GPU Freq Table", GpuFreqCvbTable, 1, nullptr, GpuCvbDefaultMaxFreq }, - { "GPU Freq Asm", &GpuFreqMaxAsm, 2, &GpuMaxClockPatternFn }, - { "GPU PLL Max", &GpuFreqPllMax, 1, nullptr, GpuClkPllMax }, - { "GPU PLL Limit", &GpuFreqPllLimit, 4, nullptr, GpuClkPllLimit }, - { "MEM Freq Mtc", &MemFreqMtcTable, 1, nullptr, EmcClkOSLimit }, - { "MEM Freq Dvb", &MemFreqDvbTable, 1, nullptr, EmcClkOSLimit }, - { "MEM Freq Max", &MemFreqMax, 0, nullptr, EmcClkOSLimit }, - { "MEM Freq PLLM", &MemFreqPllmLimit, 2, nullptr, EmcClkPllmLimit }, - { "MEM Vddq", &EmcVddqVolt, 2, nullptr, EmcVddqDefault }, - { "MEM Vdd2", &MemVoltHandler, 2, nullptr, MemVdd2Default }, - { "MEM Table Asm", &MemMtcTableAsm, 1, &MemMtcGetGetTablePatternFn }, - { "SOC Volt Asm", &SocVoltAsm, 1, &SocVoltPatternFn }, - { "SOC Volt Limit", &SocVoltLimit, 1, nullptr, SocVoltLimitOfficial }, - }; - - for (uintptr_t ptr = mapped_nso; ptr <= mapped_nso + nso_size - sizeof(MarikoMtcTable); ptr += sizeof(u32)) { - u32 *ptr32 = reinterpret_cast(ptr); - for (auto &entry : patches) { - if (R_SUCCEEDED(entry.SearchAndApply(ptr32))) { - break; - } - } - } - - for (auto &entry : patches) { - LOGGING("%s Count: %zu", entry.description, entry.patched_count); - if (R_FAILED(entry.CheckResult())) { - panic::SmcError(panic::Patch); - - CRASH(entry.description); - } - } - } - } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.hpp new file mode 100644 index 00000000..99531813 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/pcv_mariko_mtc.hpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) Switch-OC-Suite + * + * Copyright (c) 2023 hanai3Bi + * + * Copyright (c) B3711 + * + * Copyright (c) Souldbminer 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 . + */ + +#pragma once + +#include "../pcv.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + void MtcGenerateFreqTables(); + Result MemFreqMtcTable(u32 *ptr); + Result MemFreqDvbTable(u32 *ptr); + Result MemFreqMax(u32 *ptr); + Result MemMtcTableAsm(u32 *ptr); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp similarity index 96% rename from Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.cpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp index 61842b3d..ecb4d47f 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.cpp @@ -1,47 +1,47 @@ -/* - * Copyright (c) Lightos_ - * - * 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 . - */ - -#include "../oc_common.hpp" -#include "timing_tables.hpp" - -namespace ams::ldr::hoc::pcv::mariko { - - const ReplacePatch g_rext_table[] = { - {2'133'000, 0x1A}, {2'166'000, 0x19}, {2'200'000, 0x19}, - {2'233'000, 0x19}, {2'266'000, 0x1A}, {2'300'000, 0x1B}, - {2'333'000, 0x1B}, {2'366'000, 0x1B}, {2'400'000, 0x1B}, - {2'433'000, 0x1B}, {2'466'000, 0x1B}, {2'500'000, 0x1A}, - {2'533'000, 0x1C}, {2'566'000, 0x1B}, {2'600'000, 0x1B}, - {2'633'000, 0x1B}, {2'666'000, 0x1B}, {2'700'000, 0x1C}, - {2'733'000, 0x1C}, {2'766'000, 0x1D}, {2'800'000, 0x1D}, - {2'833'000, 0x1D}, {2'866'000, 0x1D}, {2'900'000, 0x1D}, - {2'933'000, 0x1C}, {2'966'000, 0x1D}, {3'000'000, 0x1D}, - {3'033'000, 0x1D}, {3'066'000, 0x1D}, {3'100'000, 0x1D}, - {3'133'000, 0x1D}, {3'166'000, 0x1C}, {3'200'000, 0x1C}, - }; - - const u32 g_rext_table_size = sizeof(g_rext_table) / sizeof(g_rext_table[0]); - - const ReplacePatch *FindRext() { - for (u32 i = 0; i < g_rext_table_size; i++) { - if (g_rext_table[i].freq >= C.marikoEmcMaxClock) { - return &g_rext_table[i]; - } - } - return nullptr; - } - -} +/* + * Copyright (c) Lightos_ + * + * 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 . + */ + +#include "../../oc_common.hpp" +#include "timing_tables.hpp" + +namespace ams::ldr::hoc::pcv::mariko { + + const ReplacePatch g_rext_table[] = { + {2'133'000, 0x1A}, {2'166'000, 0x19}, {2'200'000, 0x19}, + {2'233'000, 0x19}, {2'266'000, 0x1A}, {2'300'000, 0x1B}, + {2'333'000, 0x1B}, {2'366'000, 0x1B}, {2'400'000, 0x1B}, + {2'433'000, 0x1B}, {2'466'000, 0x1B}, {2'500'000, 0x1A}, + {2'533'000, 0x1C}, {2'566'000, 0x1B}, {2'600'000, 0x1B}, + {2'633'000, 0x1B}, {2'666'000, 0x1B}, {2'700'000, 0x1C}, + {2'733'000, 0x1C}, {2'766'000, 0x1D}, {2'800'000, 0x1D}, + {2'833'000, 0x1D}, {2'866'000, 0x1D}, {2'900'000, 0x1D}, + {2'933'000, 0x1C}, {2'966'000, 0x1D}, {3'000'000, 0x1D}, + {3'033'000, 0x1D}, {3'066'000, 0x1D}, {3'100'000, 0x1D}, + {3'133'000, 0x1D}, {3'166'000, 0x1C}, {3'200'000, 0x1C}, + }; + + const u32 g_rext_table_size = sizeof(g_rext_table) / sizeof(g_rext_table[0]); + + const ReplacePatch *FindRext() { + for (u32 i = 0; i < g_rext_table_size; i++) { + if (g_rext_table[i].freq >= C.marikoEmcMaxClock) { + return &g_rext_table[i]; + } + } + return nullptr; + } + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp similarity index 96% rename from Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.hpp rename to Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp index c23b812d..d682000e 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/mariko/timing_tables.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/mariko/timing_tables.hpp @@ -1,31 +1,31 @@ -/* - * Copyright (c) Lightos_ - * - * 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 . - */ - -#pragma once -#include - -namespace ams::ldr::hoc::pcv::mariko { - - struct ReplacePatch { - u32 freq; - u32 rext; - }; - - extern const ReplacePatch g_rext_table[]; - extern const u32 g_rext_table_size; - const ReplacePatch *FindRext(); - -} +/* + * Copyright (c) Lightos_ + * + * 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 . + */ + +#pragma once +#include + +namespace ams::ldr::hoc::pcv::mariko { + + struct ReplacePatch { + u32 freq; + u32 rext; + }; + + extern const ReplacePatch g_rext_table[]; + extern const u32 g_rext_table_size; + const ReplacePatch *FindRext(); + +} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp index a1a94f94..dca615fc 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.cpp @@ -27,8 +27,8 @@ namespace ams::ldr::hoc::pcv { R_UNLESS(entry->freq == entry->vco_max, ldr::ResultInvalidMemPllmEntry()); // Double the max clk simply - u32 max_clk = entry->freq * 2; - entry->freq = max_clk; + u32 max_clk = entry->freq * 2; + entry->freq = max_clk; entry->vco_max = max_clk; R_SUCCEED(); } @@ -41,9 +41,9 @@ namespace ams::ldr::hoc::pcv { }; constexpr u32 uv_step = 12'500; - constexpr u32 uv_min = 600'000; + constexpr u32 uv_min = 600'000; - auto validator = [](regulator* entry) { + auto validator = [](regulator *entry) { R_UNLESS(entry->id == 1, ldr::ResultInvalidRegulatorEntry()); R_UNLESS(entry->type == 1, ldr::ResultInvalidRegulatorEntry()); R_UNLESS(entry->type_1.volt_reg == 0x17, ldr::ResultInvalidRegulatorEntry()); @@ -67,7 +67,7 @@ namespace ams::ldr::hoc::pcv { } if (emc_uv % uv_step) { - emc_uv = emc_uv / uv_step * uv_step; // rounding + emc_uv = emc_uv / uv_step * uv_step; // rounding } PATCH_OFFSET(ptr, emc_uv); @@ -76,14 +76,14 @@ namespace ams::ldr::hoc::pcv { } void SafetyCheck() { - struct sValidator { + struct Validator { volatile u32 value; u32 min; u32 max; u32 panic; bool value_required = false; - Result check() { + Result Check() { if (!value_required && !value) { R_SUCCEED(); } @@ -102,11 +102,12 @@ namespace ams::ldr::hoc::pcv { u32 eristaCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.eristaCpuDvfsTable)->freq); u32 marikoCpuDvfsMaxFreq; - if (C.marikoCpuUVHigh) { - marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); - } else { - marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); - } + if (C.marikoCpuUVHigh) { + marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTableSLT)->freq); + } else { + marikoCpuDvfsMaxFreq = static_cast(GetDvfsTableLastEntry(C.marikoCpuDvfsTable)->freq); + } + u32 eristaGpuDvfsMaxFreq; switch (C.eristaGpuUV) { case 0: @@ -145,25 +146,25 @@ namespace ams::ldr::hoc::pcv { break; } - sValidator validators[] = { - { C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true }, - { C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true }, - { C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, }, - { C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, }, - { eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, }, - { marikoCpuDvfsMaxFreq, 1785'000, 2703'000, panic::Cpu, }, - { C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */ - { C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, }, - { C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, }, - { C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, }, - { C.marikoSocVmax, 1000, 1200, panic::Emc, }, - { eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, }, - { marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, }, - { C.marikoGpuVmax, 800, 960, panic::Gpu, }, + Validator validators[] = { + { C.eristaCpuBoostClock, 1020'000, 2397'000, panic::Cpu, true }, + { C.marikoCpuBoostClock, 1020'000, 2703'000, panic::Cpu, true }, + { C.eristaCpuMaxVolt, 1000, 1260, panic::Cpu, }, + { C.marikoCpuMaxVolt, 1000, 1200, panic::Cpu, }, + { eristaCpuDvfsMaxFreq, 1785'000, 2397'000, panic::Cpu, }, + { marikoCpuDvfsMaxFreq, 1785'000, 2703'000, panic::Cpu, }, + { C.commonEmcMemVolt, 912'500, 1350'000, panic::Emc, }, /* Official vmax for the RAMs is 1400-1500mV */ + { C.eristaEmcMaxClock, 1600'000, 2600'000, panic::Emc, }, + { C.marikoEmcMaxClock, 1600'000, 3500'000, panic::Emc, }, + { C.marikoEmcVddqVolt, 400'000, 750'000, panic::Emc, }, + { C.marikoSocVmax, 1000, 1200, panic::Emc, }, + { eristaGpuDvfsMaxFreq, 768'000, 1152'000, panic::Gpu, }, + { marikoGpuDvfsMaxFreq, 768'000, 1536'000, panic::Gpu, }, + { C.marikoGpuVmax, 800, 960, panic::Gpu, }, }; for (auto &v : validators) { - if (R_FAILED(v.check())) { + if (R_FAILED(v.Check())) { panic::SmcError(v.panic); CRASH("Validation FAIL"); } @@ -171,14 +172,19 @@ namespace ams::ldr::hoc::pcv { } void WriteKipLoadToIram() { - const u32 hocMagic = 0x686F634D; + const u32 hocMagic = 0x686F634D; constexpr uintptr_t LoadMagicAddress = 0x4003DC00; /* Should be a pretty safe address. */ R_DISCARD(SmcCopyToIram(LoadMagicAddress, &hocMagic, sizeof(hocMagic))); } - void Patch(uintptr_t mapped_nso, size_t nso_size) { + void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena) { SafetyCheck(); + Hooks().Initialize(mapped_nso, nso_address, cave, cave_size, data_arena); + + g_pcv_cave = cave; + g_pcv_cave_size = cave_size; + bool isMariko = (spl::GetSocType() == spl::SocType_Mariko); if (isMariko) { mariko::Patch(mapped_nso, nso_size); diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp index 4afaedbf..c50f55a8 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv.hpp @@ -22,8 +22,15 @@ #include "../oc_common.hpp" #include "pcv_common.hpp" -#include "pcv_erista.hpp" -#include "pcv_mariko.hpp" + +#include "erista/pcv_erista_cpu.hpp" +#include "erista/pcv_erista_gpu.hpp" +#include "erista/pcv_erista_mtc.hpp" +#include "erista/pcv_erista.hpp" + +#include "mariko/pcv_mariko.hpp" + +#include "pcv_hook.hpp" namespace ams::ldr::hoc::pcv { @@ -136,7 +143,7 @@ namespace ams::ldr::hoc::pcv { default: customize_table = const_cast(C.marikoGpuDvfsTableHiOPT); break; - } + } } else { switch (C.eristaGpuUV) { case 0: @@ -151,7 +158,7 @@ namespace ams::ldr::hoc::pcv { default: customize_table = const_cast(C.eristaGpuDvfsTable); break; - } + } } size_t default_entry_count = GetDvfsTableEntryCount(default_table); @@ -187,6 +194,7 @@ namespace ams::ldr::hoc::pcv { } ++entry; } + if (C.commonGpuVoltOffset && !(isMariko ? C.marikoGpuUV : C.eristaGpuUV)) { cvb_entry_t *entry = static_cast(gpu_cvb_table_head); for (size_t i = 0; i < customize_entry_count; ++i) { @@ -201,7 +209,11 @@ namespace ams::ldr::hoc::pcv { Result MemFreqPllmLimit(u32 *ptr); Result MemVoltHandler(u32 *ptr); // Used for Erista MEM Vdd2 + EMC Vddq or Mariko MEM Vdd2 + /* Extra pcv .bss */ + constexpr size_t HocPcvScratchSize = 0x2000; + constexpr size_t HocBusFreqBufOffset = 0x1000; /* start of the SOC bus region */ + void SafetyCheck(); - void Patch(uintptr_t mapped_nso, size_t nso_size); + void Patch(uintptr_t mapped_nso, size_t nso_size, uintptr_t cave, size_t cave_size, uintptr_t nso_address, uintptr_t data_arena); } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp index 30157867..4f9f22f7 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_asm.hpp @@ -44,6 +44,15 @@ namespace ams::ldr::hoc::pcv { return ins & ((1 << 5) - 1); }; + /* Rn (bits 9:5) and Rm (bits 20:16) to get registers. */ + inline auto AsmGetRn = [](u32 ins) -> u32 { return (ins >> 5) & 0x1Fu; }; + inline auto AsmGetRm = [](u32 ins) -> u32 { return (ins >> 16) & 0x1Fu; }; + + /* Add (shifted register), 64-bit: sf=1 op=0 S=0 01011 shift(2) 0 Rm imm6 Rn Rd. */ + inline auto AsmIsAddShiftedReg64 = [](u32 ins) { + return (ins & 0xFF200000u) == 0x8B000000u; + }; + inline auto asm_set_rd = [](u32 ins, u8 rd) { return (ins & 0xFFFFFFE0) | (rd & 0x1F); }; @@ -94,6 +103,16 @@ namespace ams::ldr::hoc::pcv { return static_cast(static_cast(pc) + imm); }; + /* adrp Rd, target */ + inline auto AsmMakeAdrp = [](uintptr_t pc, uintptr_t target, u32 rd) -> u32 { + const s64 delta = static_cast(target & ~static_cast(0xFFF)) + - static_cast(pc & ~static_cast(0xFFF)); + const u32 imm = static_cast((delta >> 12) & 0x1FFFFF); /* 21-bit page */ + const u32 immlo = imm & 0x3; + const u32 immhi = (imm >> 2) & 0x7FFFF; + return 0x90000000u | (immlo << 29) | (immhi << 5) | (rd & 0x1Fu); + }; + inline auto AsmSetAdrTarget = [](u32 ins, uintptr_t pc, uintptr_t target) -> u32 { const s64 delta = static_cast(target) - static_cast(pc); const u32 immlo = static_cast(delta & 0x3); @@ -101,6 +120,167 @@ namespace ams::ldr::hoc::pcv { return (ins & ~((0x3u << 29) | (0x7FFFFu << 5))) | (immlo << 29) | (immhi << 5); }; + /* adrp: bit31=1, bits 28:24 = 10000. */ + inline auto AsmIsAdrp = [](u32 ins) -> bool { + return (ins & 0x9F000000u) == 0x90000000u; + }; + + inline auto AsmAdrpPageOffset = [](u32 ins) -> s64 { + s64 imm = static_cast((((ins >> 5) & 0x7FFFFu) << 2) | ((ins >> 29) & 0x3u)); + imm = (imm << 43) >> 43; /* sign-extend the 21-bit immediate */ + return imm << 12; + }; + + /* add (immediate), 64-bit: sf=1 op=0 S=0 100010 sh imm12 Rn Rd. */ + inline auto AsmIsAddImm64 = [](u32 ins) -> bool { + return (ins & 0xFF800000u) == 0x91000000u; + }; + + inline auto AsmGetImm12 = [](u32 ins) -> u32 { + return (ins >> 10) & 0xFFFu; + }; + + inline auto AsmMakeAddImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 { + return 0x91000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu); + }; + + /* movz Wd,#imm16 (no shift). */ + inline auto AsmMakeMovzW = [](u32 rd, u16 imm16) -> u32 { + return 0x52800000u | (static_cast(imm16) << 5) | (rd & 0x1Fu); + }; + + /* mov Xd,Xm == orr Xd,XZR,Xm. */ + inline auto AsmMakeMovReg = [](u32 rd, u32 rm) -> u32 { + return 0xAA0003E0u | ((rm & 0x1Fu) << 16) | (rd & 0x1Fu); + }; + + /* ldr Xt,[Xn,#byteOff] */ + inline auto AsmMakeLdrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0xF9400000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* b (PC-relative, +-128MB). */ + inline auto AsmMakeB = [](uintptr_t pc, uintptr_t target) -> u32 { + const s64 off = (static_cast(target) - static_cast(pc)) >> 2; + return 0x14000000u | (static_cast(off) & 0x03FFFFFFu); + }; + + /* b. (cond: LO/CC=0x3, LE=0xD, NE=0x1, ...). */ + inline auto AsmMakeBCond = [](uintptr_t pc, uintptr_t target, u32 cond) -> u32 { + const s64 off = (static_cast(target) - static_cast(pc)) >> 2; + return 0x54000000u | ((static_cast(off) & 0x7FFFFu) << 5) | (cond & 0xFu); + }; + + /* sub Xd,Xn,#imm12 (shift 0). */ + inline auto AsmMakeSubImm64 = [](u32 rd, u32 rn, u32 imm12) -> u32 { + return 0xD1000000u | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu); + }; + + /* cmp Wn,#imm12 == subs WZR,Wn,#imm12. */ + inline auto AsmMakeCmpImm32 = [](u32 rn, u32 imm12) -> u32 { + return 0x7100001Fu | ((imm12 & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5); + }; + + /* str Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */ + inline auto AsmMakeStrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0xB9000000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* ldr Wt,[Xn,#byteOff] (32-bit, unsigned scaled by 4). */ + inline auto AsmMakeLdrImm32 = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0xB9400000u | (((byteOff / 4u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* add Xd,Xn,Xm,LSL #shift (64-bit shifted register, shift 0-63). */ + inline auto AsmMakeAddShiftedReg64 = [](u32 rd, u32 rn, u32 rm, u32 shift) -> u32 { + return 0x8B000000u | ((rm & 0x1Fu) << 16) | ((shift & 0x3Fu) << 10) | ((rn & 0x1Fu) << 5) | (rd & 0x1Fu); + }; + + /* stp Xt1,Xt2,[Xn,#imm] (signed offset, scaled by 8). */ + inline auto AsmMakeStpImm64 = [](u32 rt1, u32 rt2, u32 rn, s32 imm) -> u32 { + return 0xA9000000u | ((static_cast(imm / 8) & 0x7Fu) << 15) | ((rt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (rt1 & 0x1Fu); + }; + + /* stp Qt1,Qt2,[Xn,#imm] (128-bit SIMD, signed offset scaled by 16). */ + inline auto AsmMakeStpqImm = [](u32 qt1, u32 qt2, u32 rn, s32 imm) -> u32 { + return 0xAD000000u | ((static_cast(imm / 16) & 0x7Fu) << 15) | ((qt2 & 0x1Fu) << 10) | ((rn & 0x1Fu) << 5) | (qt1 & 0x1Fu); + }; + + /* str Xt,[Xn,#byteOff] (unsigned scaled by 8). */ + inline auto AsmMakeStrImm64 = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0xF9000000u | (((byteOff / 8u) & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* movn Wd,#imm16 (no shift): loads ~imm16, e.g. movn Wd,#0x37 == -56. */ + inline auto AsmMakeMovnW = [](u32 rd, u16 imm16) -> u32 { + return 0x12800000u | (static_cast(imm16) << 5) | (rd & 0x1Fu); + }; + + /* bl (PC-relative, +-128MB). */ + inline auto AsmMakeBl = [](uintptr_t pc, uintptr_t target) -> u32 { + const s64 off = (static_cast(target) - static_cast(pc)) >> 2; + return 0x94000000u | (static_cast(off) & 0x03FFFFFFu); + }; + + /* svc #imm16. */ + inline auto AsmMakeSvc = [](u16 imm16) -> u32 { + return 0xD4000001u | (static_cast(imm16) << 5); + }; + + constexpr u32 RetIns = 0xD65F03C0u; /* ret (x30) */ + + /* ldrb Wt,[Xn,#imm] (unsigned byte, scale 1). */ + inline auto AsmMakeLdrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0x39400000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* cbz Wt,. */ + inline auto AsmMakeCbz = [](uintptr_t pc, uintptr_t target, u32 rt) -> u32 { + const s64 off = (static_cast(target) - static_cast(pc)) >> 2; + return 0x34000000u | ((static_cast(off) & 0x7FFFFu) << 5) | (rt & 0x1Fu); + }; + + /* strb Wt,[Xn,#imm] (unsigned byte, scale 1). */ + inline auto AsmMakeStrbImm = [](u32 rt, u32 rn, u32 byteOff) -> u32 { + return 0x39000000u | ((byteOff & 0xFFFu) << 10) | ((rn & 0x1Fu) << 5) | (rt & 0x1Fu); + }; + + /* mov Xd,X == orr Xd,XZR,X (matches any Rd). */ + inline auto AsmIsMovReg = [](u32 ins, u32 rm) -> bool { + return (ins & 0xFFFFFFE0u) == (0xAA0003E0u | ((rm & 0x1Fu) << 16)); + }; + + /* add Xd,sp,#imm12 (shift 0). */ + inline auto AsmIsAddSpImm = [](u32 ins) -> bool { + return (ins & 0xFFC003E0u) == 0x910003E0u; + }; + + /* sub Xd,x29,#imm12 (shift 0). */ + inline auto AsmIsSubX29Imm = [](u32 ins) -> bool { + return (ins & 0xFFC003E0u) == 0xD10003A0u; + }; + + inline auto AsmIsB = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x14000000u; }; /* b */ + inline auto AsmIsBl = [](u32 ins) -> bool { return (ins & 0xFC000000u) == 0x94000000u; }; /* bl */ + inline auto AsmIsBCond = [](u32 ins) -> bool { return (ins & 0xFF000010u) == 0x54000000u; }; /* b.c */ + + /* ldr/str Xt,[Xn,#imm] (64-bit, unsigned scaled offset). */ + inline auto AsmIsLdrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9400000u; }; + inline auto AsmIsStrImm64 = [](u32 ins) -> bool { return (ins & 0xFFC00000u) == 0xF9000000u; }; + inline auto AsmGetLdStImm64Off = [](u32 ins) -> u32 { return ((ins >> 10) & 0xFFFu) * 8u; }; + + /* Byte target address of a b/bl at pc. */ + inline auto AsmBranchTarget = [](u32 ins, uintptr_t pc) -> uintptr_t { + s64 off = static_cast((ins & 0x03FFFFFFu) << 2); + off = (off << 36) >> 36; /* sign-extend the 28-bit branch offset */ + return static_cast(static_cast(pc) + off); + }; + + /* Rewrite a scaled immediate-offset load/store (`op Wt,[Xn,#imm]`) into its register-offset form */ + inline auto AsmSetLdStRegOffset = [](u32 ldstImm, u32 rm) -> u32 { + return (ldstImm & 0xC0C003FFu) | 0x38207800u | ((rm & 0x1Fu) << 16); + }; + inline auto AsmIsLdpX = [](u32 ins) { return (ins & 0xFE400000u) == 0xA8400000u; }; @@ -114,7 +294,6 @@ namespace ams::ldr::hoc::pcv { bool secondMatch = (ins2 & StpRegsImmMask) == (cmp2 & StpRegsImmMask); - constexpr u32 MovMask = ~((1u << 5) - 1u); bool thirdMatch = (ins3 & MovMask) == (cmp3 & MovMask); @@ -188,4 +367,25 @@ namespace ams::ldr::hoc::pcv { return (ins1 & ClearImm19) == (ins2 & ClearImm19); }; + inline bool AsmIsFramePush(u32 ins) { + constexpr u32 FramePushMask = 0xFFC07FFF; + constexpr u32 FramePushValue = 0xA9807BFD; + return (ins & FramePushMask) == FramePushValue; + } + + inline u32 *FindFnPrologue(u32 *ptr, u32 margin, u32 *nsoStart) { + for (u32 i = 0; i <= margin; ++i) { + u32 *candidate = ptr - i; + if (candidate < nsoStart) { + break; + } + + if (AsmIsFramePush(*candidate)) { + return candidate; + } + } + + return nullptr; + } + } diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp index 87eafaa3..1d3b9232 100644 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_common.hpp @@ -173,6 +173,19 @@ namespace ams::ldr::hoc::pcv { constexpr size_t DvfsTableEntryCount = 32; constexpr size_t DvfsTableEntryLimit = DvfsTableEntryCount - 1; + // EMC-only limit. + constexpr size_t EmcDvfsTableEntryCount = 64; + constexpr size_t EmcDvfsTableEntryLimit = EmcDvfsTableEntryCount - 1; + + // The pcv SoC-voltage DVB table is a fixed 32-entry region. (it doesn't need to be larger) + constexpr size_t DvbTableCapacity = 32; + + // extra .bss location, 0 until Patch() runs. + inline uintptr_t g_pcv_scratch = 0; + + inline uintptr_t g_pcv_cave = 0; + inline size_t g_pcv_cave_size = 0; + template size_t GetDvfsTableEntryCount(T *table_head) { using NT = std::remove_const_t>; diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp deleted file mode 100644 index 6e46f8fa..00000000 --- a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_erista.hpp +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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 . - */ - -#pragma once - -#include "../oc_common.hpp" -#include "pcv_common.hpp" -#include "pcv_asm.hpp" - -namespace ams::ldr::hoc::pcv::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 CpuVoltOfficial = 1227; - constexpr u32 CpuVminOfficial = 825; - constexpr u32 CpuTune0Low = 0xFFEAD0FF; - - constexpr u32 CpuVoltL4T = 1257'000; - - static const u32 cpuVoltDvfsPattern[] = { 1227, 1000, 100, 1000, 0 }; - static_assert(sizeof(cpuVoltDvfsPattern) == 0x14, "Invalid cpuVoltDvfsPattern size"); - - static const u32 cpuVoltageThermalPattern[] = { 950, 1132, 0, 950, 1227, 0, 825, 1227, 15000, 825, 1170, 60000, 825, 1132, 80000 }; - static_assert(sizeof(cpuVoltageThermalPattern) == 0x3c, "Invalid cpuVoltageThermalPattern size"); - - constexpr u32 GpuClkPllLimit = 2'600'000; - constexpr u32 GpuClkPllMax = 921'600'000; - constexpr u32 GpuVminOfficial = 810; - - constexpr u16 CpuMinVolts[] = { 950, 850, 825, 810 }; - - static const u32 gpuVoltDvfsPattern[] = { 810, 1150, 1000, 100, 1000, 10, }; - static_assert(sizeof(gpuVoltDvfsPattern) == (sizeof(u32) * 6), "Invalid gpuVoltDvfsPattern"); - - static const u32 gpuVoltThermalPattern[] = { 950, 1132, 0, 810, 1132, 15000, 810, 1132, 30000, 810, 1132, 50000, 810, 1132, 70000, 810, 1132, 105000 }; - static_assert(sizeof(gpuVoltThermalPattern) == 0x48, "Invalid gpuVoltageThermalPattern size"); - - /* 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 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 GpuAsmPattern[] = { 0x52820000, 0x72A001C0 }; - - inline bool GpuMaxClockPatternFn(u32 *ptr32) { - return asm_compare_no_rd(*ptr32, GpuAsmPattern[0]); - }; - - 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 EmcListDefault[] = { 40800, 68000, 102000, 204000, 408000, 665600, 800000, 1065600, 1331200, 1600000, }; - constexpr u32 EmcListSizeDefault = std::size(EmcListDefault); - constexpr u32 EmcListEndDefault = EmcListSizeDefault - 1; - - constexpr u32 MemVoltHOS = 1125'000; - constexpr u32 EmcClkPllmLimit = 1866'000'000; - - constexpr u32 MTC_TABLE_REV = 7; - constexpr u32 MtcTableCountDefault = 10; - - constexpr size_t MtcFullTableSize = sizeof(EristaMtcTable) * MtcTableCountDefault; - constexpr u32 MtcFullTableCount = 3; - - /* These dramids were copied from Hekate -- see /bdk/mem/sdram.h */ - enum DramId { - ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH = 0, - ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE = 1, - ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC = 2, - ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH = 4, - ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX = 7, - }; - - enum MtcTableIndex { - T210SdevEmcDvfsTableS4gb01 = 0, /* HB-MGCH, WT:C */ - T210SdevEmcDvfsTableS6gb01 = 1, /* HM-MGCH */ - T210SdevEmcDvfsTableH4gb01 = 2, /* HR-NLE */ - MtcTableIndex_Invalid = 3, - }; - - struct MtcDramIndex { - DramId dramId; - MtcTableIndex index; - }; - - /* TODO: Test 6gb and 8gb. */ - const inline MtcDramIndex mtcIndexTable[] = { - { ICOSA_4GB_SAMSUNG_K4F6E304HB_MGCH, T210SdevEmcDvfsTableS4gb01, }, - { ICOSA_4GB_MICRON_MT53B512M32D2NP_062_WTC, T210SdevEmcDvfsTableS4gb01, }, - { ICOSA_6GB_SAMSUNG_K4FHE3D4HM_MGCH, T210SdevEmcDvfsTableS6gb01, }, - { ICOSA_8GB_SAMSUNG_K4FBE3D4HM_MGXX, T210SdevEmcDvfsTableS6gb01, }, - { ICOSA_4GB_HYNIX_H9HCNNNBPUMLHR_NLE, T210SdevEmcDvfsTableH4gb01, }, - }; - - constexpr u32 MtcBrAsm = 0xD61F0140; - constexpr u32 MtcMovAsm = 0x52800148; - constexpr u32 MtcAdrpAsm = 0xD0000081; - constexpr u32 MtcBlIns = 0x97ffae64; - constexpr u32 MtcAddAsm = 0x91131821; - - ALWAYS_INLINE bool MemMtcGetGetTablePatternFn(u32 *ptr) { - /* This builds an address that gets returned, so the register must be x0 by convention. */ - return AsmCompareAddNoImm12(*ptr, MtcAddAsm); - } - - void Patch(uintptr_t mapped_nso, size_t nso_size); - -} diff --git a/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_hook.hpp b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_hook.hpp new file mode 100644 index 00000000..fcc6a47a --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/source/oc/pcv/pcv_hook.hpp @@ -0,0 +1,367 @@ +/* + * Copyright (c) Lightos_ + * + * 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 . + */ + +#pragma once + +#include "../oc_common.hpp" + +#define HOOK_PAYLOAD_FN __attribute__((section("hoc_hookpayload"), used, noinline, visibility("hidden"))) + +/* Inline asm because fuck compilers: GCC refuses to put a variable and a function in the same section. */ +/* It wants alloc+write for one and alloc+exec for the other. */ +/* You are supposed to be able to override that by writing the flags into the section name yourself, */ +/* like section("hoc_hookpayload,\"ax\",%progbits"), instead of letting GCC pick them. */ +/* But GCC takes that whole string as the name and never reads the flags out of it, so it appends the */ +/* ones it wanted anyway: the .section directive it emits ends up carrying two sets of flags, and the */ +/* assembler rejects it. The failure lands in the generated assembly, not in anything we wrote. */ +/* Therefore we must use inline assembly, which reaches the assembler exactly as written. */ +#define DEFINE_HOOK_PAYLOAD_PTR(type, name) \ + asm(".section hoc_hookpayload,\"ax\",%progbits\n" \ + ".balign 8\n" \ + ".global " #name "\n" \ + ".hidden " #name "\n" \ + #name ": .zero 8\n" \ + ".text\n"); \ + extern "C" __attribute__((visibility("hidden"))) type *name + +#define DECLARE_HOOK_PAYLOAD_PTR(type, name) \ + extern "C" __attribute__((visibility("hidden"))) type *name + +#define HOOK_PAYLOAD_PTR(type, name) \ + ([]() -> type * { \ + type **_hoc_pp; \ + __asm__("adr %0, " #name : "=r"(_hoc_pp)); \ + return *_hoc_pp; \ + }()) + +extern "C" const u8 __start_hoc_hookpayload[]; +extern "C" const u8 __stop_hoc_hookpayload[]; + +namespace ams::ldr::hoc::pcv { + + constexpr size_t HookPageSize = 0x1000; + constexpr size_t PcvDataArenaSize = 0x1000; + + inline s64 SignExtend(u64 value, int bits) { + const int shift = 64 - bits; + return static_cast(value << shift) >> shift; + } + + inline u32 EncodeRelBranch(u32 opc, uintptr_t site_va, uintptr_t target_va) { + const s64 delta = static_cast(target_va) - static_cast(site_va); + AMS_ABORT_UNLESS((delta & 0x3) == 0); + AMS_ABORT_UNLESS(delta >= -0x08000000 && delta <= 0x07FFFFFC); + return opc | (static_cast(delta >> 2) & 0x03FFFFFFu); + } + + inline u32 EncodeB(uintptr_t site_va, uintptr_t target_va) { return EncodeRelBranch(0x14000000u, site_va, target_va); } + inline u32 EncodeBL(uintptr_t site_va, uintptr_t target_va) { return EncodeRelBranch(0x94000000u, site_va, target_va); } + + inline u32 EncodePairSp(bool load, u32 rt1, u32 rt2, s32 imm) { + const u32 base = load ? 0xA9400000u : 0xA9000000u; + const u32 imm7 = static_cast((imm / 8) & 0x7F); + return base | (imm7 << 15) | (rt2 << 10) | (31u << 5) | rt1; + } + + inline u32 EncodeSpAdjust(bool sub, u32 imm12) { + const u32 base = sub ? 0xD1000000u : 0x91000000u; + return base | ((imm12 & 0xFFFu) << 10) | (31u << 5) | 31u; + } + + inline Result RelocateInstruction(u32 insn, uintptr_t old_site_va, uintptr_t new_site_va, u32 *out) { + const u32 top6 = insn & 0xFC000000u; + if (top6 == 0x14000000u || top6 == 0x94000000u) { + const s64 old_imm = SignExtend(static_cast(insn & 0x03FFFFFFu) << 2, 28); + const uintptr_t target = old_site_va + old_imm; + *out = EncodeRelBranch(top6, new_site_va, target); + R_SUCCEED(); + } + + R_UNLESS((insn & 0xFF000010u) != 0x54000000u, ldr::ResultHookRelocationUnsupported()); /* b.cond */ + R_UNLESS((insn & 0x7E000000u) != 0x34000000u, ldr::ResultHookRelocationUnsupported()); /* cbz/cbnz */ + R_UNLESS((insn & 0x7E000000u) != 0x36000000u, ldr::ResultHookRelocationUnsupported()); /* tbz/tbnz */ + R_UNLESS((insn & 0x1F000000u) != 0x10000000u, ldr::ResultHookRelocationUnsupported()); /* adr/adrp */ + R_UNLESS((insn & 0x3B000000u) != 0x18000000u, ldr::ResultHookRelocationUnsupported()); /* ldr literal */ + + *out = insn; + R_SUCCEED(); + } + + class HookContext { + private: + uintptr_t m_map_base = 0; /* loader-side base of pcv's NSO mapping. */ + uintptr_t m_va_base = 0; /* pcv-side base of the same memory. */ + uintptr_t m_cave = 0; /* loader-side cave base. (0 if unavailable) */ + size_t m_cave_size = 0; + uintptr_t m_payload = 0; /* loader-side base of the payload copy. */ + size_t m_payload_sz = 0; + size_t m_used = 0; + uintptr_t m_data = 0; /* loader-side data arena base, 0 if none. */ + size_t m_data_used = 0; + public: + constexpr HookContext() = default; + + void Initialize(uintptr_t map_base, uintptr_t va_base, uintptr_t cave, size_t cave_size, uintptr_t data) { + m_map_base = map_base; + m_va_base = va_base; + m_cave = cave; + m_cave_size = cave_size; + m_payload = 0; + m_payload_sz = 0; + m_used = 0; + m_data = data; + m_data_used = 0; + } + + bool IsEnabled() const { return m_cave != 0 && m_cave_size != 0; } + + Result CheckEnabled() const { + R_UNLESS(this->IsEnabled(), ldr::ResultHookUnavailable()); + R_SUCCEED(); + } + + size_t CaveSize() const { return m_cave_size; } + size_t CaveUsed() const { return m_used; } + size_t CaveFree() const { return m_cave_size > m_used ? m_cave_size - m_used : 0; } + + size_t DataSize() const { return m_data != 0 ? PcvDataArenaSize : 0; } + size_t DataUsed() const { return m_data_used; } + size_t DataFree() const { return this->DataSize() - m_data_used; } + + /* Convert loader mapped address to pcv-side address. */ + uintptr_t ToVa(const void *loader_ptr) const { + return m_va_base + (reinterpret_cast(loader_ptr) - m_map_base); + } + + uintptr_t CaveVa() const { return ToVa(reinterpret_cast(m_cave)); } + + Result CopyPayload() { + R_TRY(this->CheckEnabled()); + + const size_t size = static_cast(__stop_hoc_hookpayload - __start_hoc_hookpayload); + + /* Zero length: Linker garbage collected the payload .(happens when nothing references it) */ + /* __start_ and _stop_ don't prevent this. */ + /* Copying here would succeed at first but fail later by jumping to empty memory. */ + R_UNLESS(size != 0, ldr::ResultUninitializedPatcher()); + R_UNLESS(size <= m_cave_size, ldr::ResultHookPayloadTooLarge()); + + m_payload = m_cave; + m_payload_sz = size; + std::memcpy(reinterpret_cast(m_payload), __start_hoc_hookpayload, size); + + m_used = util::AlignUp(size, sizeof(u32)); + R_SUCCEED(); + } + + /* pcv-side address of a payload symbol's copy. */ + uintptr_t PayloadVa(const void *loader_sym) const { + const uintptr_t offset = reinterpret_cast(loader_sym) - reinterpret_cast(__start_hoc_hookpayload); + return this->ToVa(reinterpret_cast(m_payload + offset)); + } + + /* Loader-side, writable pointer to a payload variable's copy in the cave. */ + template + T *PayloadCopyOf(T &loader_sym) const { + const uintptr_t offset = reinterpret_cast(std::addressof(loader_sym)) - reinterpret_cast(__start_hoc_hookpayload); + return reinterpret_cast(m_payload + offset); + } + + /* Reserves space in the writable data arena past pcv's .bss. */ + /* Returns a zeroed, loader-side pointer. */ + /* The data arena is too far from the cave to be addressed directly by symbol, so we must store a pointer to it in the cave section. */ + template + T *DataAlloc() { + const size_t size = util::AlignUp(sizeof(T), alignof(T) > 8 ? alignof(T) : 8); + if (m_data == 0 || m_data_used + size > PcvDataArenaSize) { + return nullptr; + } + + T *p = reinterpret_cast(m_data + m_data_used); + m_data_used += size; + + std::memset(p, 0, sizeof(T)); + return p; + } + + template + T *BindData(T *&loader_sym) { + T *block = this->DataAlloc(); + if (block != nullptr) { + *this->PayloadCopyOf(loader_sym) = reinterpret_cast(this->ToVa(block)); + } + return block; + } + + /* Replaces the function entirely starting at function prologue. */ + /* Preserves the function arguments. */ + Result InstallImpl(u32 *site, const void *fn, uintptr_t *out_orig = nullptr) { + R_TRY(this->CheckEnabled()); + R_UNLESS(site != nullptr, ldr::ResultHookSiteInvalid()); + R_UNLESS(m_payload != 0, ldr::ResultUninitializedPatcher()); + R_TRY(this->ValidatePayloadFn(fn)); + + if (out_orig != nullptr) { + u32 *tramp = this->AllocCode(2); + R_UNLESS(tramp != nullptr, ldr::ResultHookArenaOutOfMemory()); + + u32 relocated; + R_TRY(RelocateInstruction(site[0], this->ToVa(site), this->ToVa(&tramp[0]), std::addressof(relocated))); + tramp[0] = relocated; + tramp[1] = EncodeB(this->ToVa(&tramp[1]), this->ToVa(site) + sizeof(u32)); + + *out_orig = this->ToVa(tramp); + } + + site[0] = EncodeB(this->ToVa(site), this->PayloadVa(fn)); + + R_SUCCEED(); + } + + /* Takes the same arguments as the hooked function, does not replace. */ + Result InstallIntercept(u32 *site, const void *fn) { + R_TRY(this->CheckEnabled()); + R_UNLESS(site != nullptr, ldr::ResultHookSiteInvalid()); + R_UNLESS(m_payload != 0, ldr::ResultUninitializedPatcher()); + R_TRY(this->ValidatePayloadFn(fn)); + + /* x0-x7: arguments */ + /* x8: result pointer */ + /* x18: platform register */ + /* x30: return address */ + /* x29: keeps pairs clean */ + /* x9-x17: scratch */ + constexpr u32 FrameSize = 0x60; + constexpr u32 Pairs[][2] = { {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 18}, {29, 30} }; + constexpr u32 PairCount = sizeof(Pairs) / sizeof(Pairs[0]); + constexpr u32 StubWords = 1 + PairCount + 1 + PairCount + 1 + 1 + 1; + + u32 *stub = this->AllocCode(StubWords); + R_UNLESS(stub != nullptr, ldr::ResultHookArenaOutOfMemory()); + + u32 i = 0; + stub[i++] = EncodeSpAdjust(true, FrameSize); + for (u32 p = 0; p < PairCount; ++p) { + stub[i++] = EncodePairSp(false, Pairs[p][0], Pairs[p][1], static_cast(p * 16)); + } + + stub[i] = EncodeBL(this->ToVa(&stub[i]), this->PayloadVa(fn)); + ++i; + + for (u32 p = 0; p < PairCount; ++p) { + stub[i++] = EncodePairSp(true, Pairs[p][0], Pairs[p][1], static_cast(p * 16)); + } + stub[i++] = EncodeSpAdjust(false, FrameSize); + + u32 relocated; + R_TRY(RelocateInstruction(site[0], this->ToVa(site), this->ToVa(&stub[i]), std::addressof(relocated))); + stub[i] = relocated; + ++i; + + stub[i] = EncodeB(this->ToVa(&stub[i]), this->ToVa(site) + sizeof(u32)); + ++i; + + AMS_ABORT_UNLESS(i == StubWords); + + site[0] = EncodeB(this->ToVa(site), this->ToVa(stub)); + R_SUCCEED(); + } + + /* Checks entry to first ret that everything still points to valid data. */ + Result ValidatePayloadFn(const void *fn) const { + constexpr u32 MaxInstructions = 512; + constexpr u32 RetInsn = 0xD65F03C0u; + + const uintptr_t lo = m_payload; + const uintptr_t hi = m_payload + m_payload_sz; + + const u32 *insns = reinterpret_cast(fn); + const uintptr_t base = m_payload + (reinterpret_cast(fn) - reinterpret_cast(__start_hoc_hookpayload)); + + auto check = [&](uintptr_t target) -> Result { + if (target < lo || target >= hi) { + R_THROW(ldr::ResultHookPayloadEscapes()); + } + R_SUCCEED(); + }; + + for (u32 i = 0; i < MaxInstructions; ++i) { + const u32 insn = insns[i]; + const uintptr_t site = base + i * sizeof(u32); + + if (insn == RetInsn) { + R_SUCCEED(); + } + + const u32 top6 = insn & 0xFC000000u; + if (top6 == 0x14000000u || top6 == 0x94000000u) { /* b / bl */ + const uintptr_t target = site + SignExtend(static_cast(insn & 0x03FFFFFFu) << 2, 28); + R_TRY(check(target)); + } else if ((insn & 0xFF000010u) == 0x54000000u || /* b.cond */ + (insn & 0x7E000000u) == 0x34000000u || /* cbz / cbnz */ + (insn & 0x3B000000u) == 0x18000000u) { /* ldr literal */ + const uintptr_t target = site + SignExtend(static_cast((insn >> 5) & 0x7FFFFu) << 2, 21); + R_TRY(check(target)); + } else if ((insn & 0x7E000000u) == 0x36000000u) { /* tbz / tbnz */ + const uintptr_t target = site + SignExtend(static_cast((insn >> 5) & 0x3FFFu) << 2, 16); + R_TRY(check(target)); + } else if ((insn & 0x9F000000u) == 0x10000000u) { /* adr */ + const u64 imm = (static_cast((insn >> 5) & 0x7FFFFu) << 2) | ((insn >> 29) & 0x3u); + const uintptr_t target = site + SignExtend(imm, 21); + R_TRY(check(target)); + } else if ((insn & 0x9F000000u) == 0x90000000u) { /* adrp */ + /* ADRP is page-relative and the cave is at an arbitrary offset, any adrp would point to potential garbage. */ + R_THROW(ldr::ResultHookPayloadEscapes()); + } + } + + /* No return found. */ + R_THROW(ldr::ResultHookPayloadEscapes()); + } + private: + u32 *AllocCode(size_t words) { + const size_t bytes = words * sizeof(u32); + if (!this->IsEnabled() || m_used + bytes > m_cave_size) { + return nullptr; + } + + u32 *p = reinterpret_cast(m_cave + m_used); + m_used += bytes; + return p; + } + }; + + inline HookContext &Hooks() { + static HookContext s_context; + return s_context; + } + +} + +/* Hook custom impl. Original becomes unreachable. */ +/* Starts at function entry. Preserves arguments. */ +#define INSTALL_IMPL_HOOK(site, fn) \ + (::ams::ldr::hoc::pcv::Hooks().InstallImpl((site), reinterpret_cast(&(fn)))) + +/* Maintains original function. */ +/* Starts at function entry. Preserves arguments. */ +#define INSTALL_IMPL_HOOK_ORIG(site, fn, out_orig) \ + (::ams::ldr::hoc::pcv::Hooks().InstallImpl((site), reinterpret_cast(&(fn)), (out_orig))) + +/* Takes the same arguments as the hooked function, does not replace it and cannot change what it does */ +/* but it can be placed anywhere, not just at a function entry. */ +#define INSTALL_INTERC_HOOK(site, fn) \ + (::ams::ldr::hoc::pcv::Hooks().InstallIntercept((site), reinterpret_cast(&(fn)))) diff --git a/Source/Atmosphere/stratosphere/loader/system_module.mk b/Source/Atmosphere/stratosphere/loader/system_module.mk new file mode 100644 index 00000000..35bbcde3 --- /dev/null +++ b/Source/Atmosphere/stratosphere/loader/system_module.mk @@ -0,0 +1,136 @@ +#--------------------------------------------------------------------------------- +# pull in common stratosphere sysmodule configuration +#--------------------------------------------------------------------------------- +THIS_MAKEFILE := $(abspath $(lastword $(MAKEFILE_LIST))) +CURRENT_DIRECTORY := $(abspath $(dir $(THIS_MAKEFILE))) +include $(dir $(abspath $(lastword $(MAKEFILE_LIST))))/../../libraries/config/templates/stratosphere.mk + +ifneq ($(strip $(HOC_UART_LOG)),) + export CFLAGS += -DHOC_UART_LOG=$(HOC_UART_LOG) + export CXXFLAGS += -DHOC_UART_LOG=$(HOC_UART_LOG) +endif + +ATMOSPHERE_SYSTEM_MODULE_TARGETS := kip + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(__RECURSIVE__),1) +#--------------------------------------------------------------------------------- + +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +CFILES := $(call FIND_SOURCE_FILES,$(SOURCES),c) +CPPFILES := $(call FIND_SOURCE_FILES,$(SOURCES),cpp) +SFILES := $(call FIND_SOURCE_FILES,$(SOURCES),s) + +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + $(foreach dir,$(AMS_LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) $(foreach dir,$(AMS_LIBDIRS),-L$(dir)/$(ATMOSPHERE_LIBRARY_DIR)) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: clean all check_lib + +#--------------------------------------------------------------------------------- +all: $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + @$(MAKE) __RECURSIVE__=1 OUTPUT=$(CURDIR)/$(ATMOSPHERE_OUT_DIR)/$(TARGET) \ + DEPSDIR=$(CURDIR)/$(ATMOSPHERE_BUILD_DIR) \ + --no-print-directory -C $(ATMOSPHERE_BUILD_DIR) \ + -f $(THIS_MAKEFILE) + +$(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a: check_lib + @$(SILENTCMD)echo "Checked library." + +ifeq ($(ATMOSPHERE_CHECKED_LIBSTRATOSPHERE),1) +check_lib: +else +check_lib: + @$(MAKE) --no-print-directory -C $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere -f $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/libstratosphere.mk +endif + +$(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR): + @[ -d $@ ] || mkdir -p $@ + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(ATMOSPHERE_OUT_DIR) $(ATMOSPHERE_BUILD_DIR) + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(foreach target,$(ATMOSPHERE_SYSTEM_MODULE_TARGETS),$(OUTPUT).$(target)) + +$(OUTPUT).kip : $(OUTPUT).elf +$(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm +$(OUTPUT).nso : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +$(OFILES) : $(ATMOSPHERE_LIBRARIES_DIR)/libstratosphere/$(ATMOSPHERE_LIBRARY_DIR)/libstratosphere.a + +%.npdm : %.npdm.json + @echo built ... $< $@ + @npdmtool $< $@ + @echo built ... $(notdir $@) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/Source/Horizon-OC-Monitor/.gitignore b/Source/Horizon-OC-Monitor (deprecated)/.gitignore similarity index 100% rename from Source/Horizon-OC-Monitor/.gitignore rename to Source/Horizon-OC-Monitor (deprecated)/.gitignore diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.lst b/Source/Horizon-OC-Monitor (deprecated)/Horizon-OC-Monitor.lst similarity index 100% rename from Source/Horizon-OC-Monitor/Horizon-OC-Monitor.lst rename to Source/Horizon-OC-Monitor (deprecated)/Horizon-OC-Monitor.lst diff --git a/Source/Horizon-OC-Monitor/LICENSE b/Source/Horizon-OC-Monitor (deprecated)/LICENSE similarity index 100% rename from Source/Horizon-OC-Monitor/LICENSE rename to Source/Horizon-OC-Monitor (deprecated)/LICENSE diff --git a/Source/Horizon-OC-Monitor/Makefile b/Source/Horizon-OC-Monitor (deprecated)/Makefile similarity index 100% rename from Source/Horizon-OC-Monitor/Makefile rename to Source/Horizon-OC-Monitor (deprecated)/Makefile diff --git a/Source/Horizon-OC-Monitor (deprecated)/README.md b/Source/Horizon-OC-Monitor (deprecated)/README.md new file mode 100644 index 00000000..234e0d02 --- /dev/null +++ b/Source/Horizon-OC-Monitor (deprecated)/README.md @@ -0,0 +1,9 @@ +Thanks to NaGa for Status Monitor Pro!

+ +**Horizon-OC-Monitor is deprecated.** If you want to compile it anyway, create a `lib` folder and clone Atmosphere-libs and libultrahand into it:

+ +```bash +mkdir lib +git clone https://github.com/Atmosphere-NX/Atmosphere-libs lib/Atmosphere-libs +git clone https://github.com/ppkantorski/libultrahand lib/libultrahand +``` diff --git a/Source/Horizon-OC-Monitor/config/status-monitor/config.ini.template b/Source/Horizon-OC-Monitor (deprecated)/config/status-monitor/config.ini.template similarity index 100% rename from Source/Horizon-OC-Monitor/config/status-monitor/config.ini.template rename to Source/Horizon-OC-Monitor (deprecated)/config/status-monitor/config.ini.template diff --git a/Source/Horizon-OC-Monitor/docs/config.md b/Source/Horizon-OC-Monitor (deprecated)/docs/config.md similarity index 100% rename from Source/Horizon-OC-Monitor/docs/config.md rename to Source/Horizon-OC-Monitor (deprecated)/docs/config.md diff --git a/Source/Horizon-OC-Monitor/docs/modes.md b/Source/Horizon-OC-Monitor (deprecated)/docs/modes.md similarity index 100% rename from Source/Horizon-OC-Monitor/docs/modes.md rename to Source/Horizon-OC-Monitor (deprecated)/docs/modes.md diff --git a/Source/Horizon-OC-Monitor/include/Battery.hpp b/Source/Horizon-OC-Monitor (deprecated)/include/Battery.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/include/Battery.hpp rename to Source/Horizon-OC-Monitor (deprecated)/include/Battery.hpp diff --git a/Source/Horizon-OC-Monitor/include/Misc.hpp b/Source/Horizon-OC-Monitor (deprecated)/include/Misc.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/include/Misc.hpp rename to Source/Horizon-OC-Monitor (deprecated)/include/Misc.hpp diff --git a/Source/Horizon-OC-Monitor/include/SaltyNX.h b/Source/Horizon-OC-Monitor (deprecated)/include/SaltyNX.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/SaltyNX.h rename to Source/Horizon-OC-Monitor (deprecated)/include/SaltyNX.h diff --git a/Source/Horizon-OC-Monitor/include/audsnoop.h b/Source/Horizon-OC-Monitor (deprecated)/include/audsnoop.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/audsnoop.h rename to Source/Horizon-OC-Monitor (deprecated)/include/audsnoop.h diff --git a/Source/Horizon-OC-Monitor/include/i2c.h b/Source/Horizon-OC-Monitor (deprecated)/include/i2c.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/i2c.h rename to Source/Horizon-OC-Monitor (deprecated)/include/i2c.h diff --git a/Source/Horizon-OC-Monitor/include/ipc.h b/Source/Horizon-OC-Monitor (deprecated)/include/ipc.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/ipc.h rename to Source/Horizon-OC-Monitor (deprecated)/include/ipc.h diff --git a/Source/Horizon-OC-Monitor/include/max17050.h b/Source/Horizon-OC-Monitor (deprecated)/include/max17050.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/max17050.h rename to Source/Horizon-OC-Monitor (deprecated)/include/max17050.h diff --git a/Source/Horizon-OC-Monitor/include/pcv_types.h b/Source/Horizon-OC-Monitor (deprecated)/include/pcv_types.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/pcv_types.h rename to Source/Horizon-OC-Monitor (deprecated)/include/pcv_types.h diff --git a/Source/Horizon-OC-Monitor/include/pwm.h b/Source/Horizon-OC-Monitor (deprecated)/include/pwm.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/pwm.h rename to Source/Horizon-OC-Monitor (deprecated)/include/pwm.h diff --git a/Source/Horizon-OC-Monitor/include/rgltr.h b/Source/Horizon-OC-Monitor (deprecated)/include/rgltr.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/rgltr.h rename to Source/Horizon-OC-Monitor (deprecated)/include/rgltr.h diff --git a/Source/Horizon-OC-Monitor/include/rgltr_services.h b/Source/Horizon-OC-Monitor (deprecated)/include/rgltr_services.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/rgltr_services.h rename to Source/Horizon-OC-Monitor (deprecated)/include/rgltr_services.h diff --git a/Source/Horizon-OC-Monitor/include/tmp451.h b/Source/Horizon-OC-Monitor (deprecated)/include/tmp451.h similarity index 100% rename from Source/Horizon-OC-Monitor/include/tmp451.h rename to Source/Horizon-OC-Monitor (deprecated)/include/tmp451.h diff --git a/Source/Horizon-OC-Monitor/lang/de.json b/Source/Horizon-OC-Monitor (deprecated)/lang/de.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/de.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/de.json diff --git a/Source/Horizon-OC-Monitor/lang/en.json b/Source/Horizon-OC-Monitor (deprecated)/lang/en.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/en.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/en.json diff --git a/Source/Horizon-OC-Monitor/lang/es.json b/Source/Horizon-OC-Monitor (deprecated)/lang/es.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/es.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/es.json diff --git a/Source/Horizon-OC-Monitor/lang/fr.json b/Source/Horizon-OC-Monitor (deprecated)/lang/fr.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/fr.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/fr.json diff --git a/Source/Horizon-OC-Monitor/lang/it.json b/Source/Horizon-OC-Monitor (deprecated)/lang/it.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/it.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/it.json diff --git a/Source/Horizon-OC-Monitor/lang/ja.json b/Source/Horizon-OC-Monitor (deprecated)/lang/ja.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/ja.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/ja.json diff --git a/Source/Horizon-OC-Monitor/lang/ko.json b/Source/Horizon-OC-Monitor (deprecated)/lang/ko.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/ko.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/ko.json diff --git a/Source/Horizon-OC-Monitor/lang/nl.json b/Source/Horizon-OC-Monitor (deprecated)/lang/nl.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/nl.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/nl.json diff --git a/Source/Horizon-OC-Monitor/lang/pl.json b/Source/Horizon-OC-Monitor (deprecated)/lang/pl.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/pl.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/pl.json diff --git a/Source/Horizon-OC-Monitor/lang/pt.json b/Source/Horizon-OC-Monitor (deprecated)/lang/pt.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/pt.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/pt.json diff --git a/Source/Horizon-OC-Monitor/lang/ru.json b/Source/Horizon-OC-Monitor (deprecated)/lang/ru.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/ru.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/ru.json diff --git a/Source/Horizon-OC-Monitor/lang/uk.json b/Source/Horizon-OC-Monitor (deprecated)/lang/uk.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/uk.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/uk.json diff --git a/Source/Horizon-OC-Monitor/lang/zh-cn.json b/Source/Horizon-OC-Monitor (deprecated)/lang/zh-cn.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/zh-cn.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/zh-cn.json diff --git a/Source/Horizon-OC-Monitor/lang/zh-tw.json b/Source/Horizon-OC-Monitor (deprecated)/lang/zh-tw.json similarity index 100% rename from Source/Horizon-OC-Monitor/lang/zh-tw.json rename to Source/Horizon-OC-Monitor (deprecated)/lang/zh-tw.json diff --git a/Source/Horizon-OC-Monitor/out/config/status-monitor/config.ini.template b/Source/Horizon-OC-Monitor (deprecated)/out/config/status-monitor/config.ini.template similarity index 100% rename from Source/Horizon-OC-Monitor/out/config/status-monitor/config.ini.template rename to Source/Horizon-OC-Monitor (deprecated)/out/config/status-monitor/config.ini.template diff --git a/Source/Horizon-OC-Monitor/source/Utils.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/Utils.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/Utils.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/Utils.hpp diff --git a/Source/Horizon-OC-Monitor/source/audsnoop.c b/Source/Horizon-OC-Monitor (deprecated)/source/audsnoop.c similarity index 100% rename from Source/Horizon-OC-Monitor/source/audsnoop.c rename to Source/Horizon-OC-Monitor (deprecated)/source/audsnoop.c diff --git a/Source/Horizon-OC-Monitor/source/main.cpp b/Source/Horizon-OC-Monitor (deprecated)/source/main.cpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/main.cpp rename to Source/Horizon-OC-Monitor (deprecated)/source/main.cpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Battery.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Battery.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Battery.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Battery.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Configurator.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Configurator.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Configurator.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Configurator.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/FPS_Counter.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/FPS_Counter.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/FPS_Counter.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/FPS_Counter.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/FPS_Graph.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/FPS_Graph.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/FPS_Graph.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/FPS_Graph.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Full.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Full.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Full.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Full.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Micro.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Micro.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Micro.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Micro.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Mini.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Mini.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Mini.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Mini.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Misc.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Misc.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Misc.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Misc.hpp diff --git a/Source/Horizon-OC-Monitor/source/modes/Resolutions.hpp b/Source/Horizon-OC-Monitor (deprecated)/source/modes/Resolutions.hpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/modes/Resolutions.hpp rename to Source/Horizon-OC-Monitor (deprecated)/source/modes/Resolutions.hpp diff --git a/Source/Horizon-OC-Monitor/source/pwm.c b/Source/Horizon-OC-Monitor (deprecated)/source/pwm.c similarity index 100% rename from Source/Horizon-OC-Monitor/source/pwm.c rename to Source/Horizon-OC-Monitor (deprecated)/source/pwm.c diff --git a/Source/Horizon-OC-Monitor/source/rgltr_services.cpp b/Source/Horizon-OC-Monitor (deprecated)/source/rgltr_services.cpp similarity index 100% rename from Source/Horizon-OC-Monitor/source/rgltr_services.cpp rename to Source/Horizon-OC-Monitor (deprecated)/source/rgltr_services.cpp diff --git a/Source/Horizon-OC-Monitor/source/sysclk_ipc.c b/Source/Horizon-OC-Monitor (deprecated)/source/sysclk_ipc.c similarity index 100% rename from Source/Horizon-OC-Monitor/source/sysclk_ipc.c rename to Source/Horizon-OC-Monitor (deprecated)/source/sysclk_ipc.c diff --git a/Source/Horizon-OC-Monitor/.gitmodules b/Source/Horizon-OC-Monitor/.gitmodules deleted file mode 100644 index 5eca49b2..00000000 --- a/Source/Horizon-OC-Monitor/.gitmodules +++ /dev/null @@ -1,7 +0,0 @@ -[submodule "lib/Atmosphere-libs"] - path = lib/Atmosphere-libs - url = https://git.niklascfw.de/OmniNX/Atmosphere-Pro.git - branch = boot-storage -[submodule "lib/libultrahand"] - path = lib/libultrahand - url = https://github.com/ppkantorski/libultrahand diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf new file mode 100644 index 00000000..d0cbd027 Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.elf differ diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp new file mode 100644 index 00000000..146958db Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.nacp differ diff --git a/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl new file mode 100644 index 00000000..95495b80 Binary files /dev/null and b/Source/Horizon-OC-Monitor/Horizon-OC-Monitor.ovl differ diff --git a/Source/Horizon-OC-Monitor/README.md b/Source/Horizon-OC-Monitor/README.md deleted file mode 100644 index 998a85a4..00000000 --- a/Source/Horizon-OC-Monitor/README.md +++ /dev/null @@ -1 +0,0 @@ -Thanks to NaGa for Status Monitor Pro! \ No newline at end of file diff --git a/Source/Horizon-OC-Monitor/lib/Atmosphere-libs b/Source/Horizon-OC-Monitor/lib/Atmosphere-libs deleted file mode 160000 index e6221460..00000000 --- a/Source/Horizon-OC-Monitor/lib/Atmosphere-libs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e62214601248cfeca075295edc275569aeb99e6f diff --git a/Source/Horizon-OC-Monitor/lib/libultrahand b/Source/Horizon-OC-Monitor/lib/libultrahand deleted file mode 160000 index f7ab6c9e..00000000 --- a/Source/Horizon-OC-Monitor/lib/libultrahand +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f7ab6c9efeeda97e761b26ddd9aba0034d2f9c88 diff --git a/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl b/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl new file mode 100644 index 00000000..95495b80 Binary files /dev/null and b/Source/Horizon-OC-Monitor/out/switch/.overlays/Horizon-OC-Monitor.ovl differ diff --git a/Source/hoc-clk/common/include/hocclk/clock_manager.h b/Source/hoc-clk/common/include/hocclk/clock_manager.h index 7349b10a..fcc8ec2b 100644 --- a/Source/hoc-clk/common/include/hocclk/clock_manager.h +++ b/Source/hoc-clk/common/include/hocclk/clock_manager.h @@ -82,9 +82,10 @@ typedef struct { u8 custRev; u16 kipVersion; bool isKipLoaded; + bool rebootRequired; // Reserved for future use - u8 reserved[0x35A]; + u8 reserved[0x359]; } HocClkContext; typedef struct @@ -95,7 +96,7 @@ typedef struct }; } HocClkTitleProfileList; -#define HOCCLK_FREQ_LIST_MAX 48 +#define HOCCLK_FREQ_LIST_MAX 64 #define HOCCLK_GLOBAL_PROFILE_TID 0xA111111111111111 diff --git a/Source/hoc-clk/overlay/Makefile b/Source/hoc-clk/overlay/Makefile index 979948aa..1542d854 100644 --- a/Source/hoc-clk/overlay/Makefile +++ b/Source/hoc-clk/overlay/Makefile @@ -39,7 +39,7 @@ include ${TOPDIR}/lib/libultrahand/ultrahand.mk # version control constants #--------------------------------------------------------------------------------- #TARGET_VERSION := $(shell git describe --dirty --always --tags) -APP_VERSION := 2.5.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this +APP_VERSION := 3.0.0 # ensure to set KIP_VERSION and CUST_REV in sysmodule Makefile when updating this TARGET_VERSION := $(APP_VERSION) #--------------------------------------------------------------------------------- diff --git a/Source/hoc-clk/overlay/lang/de.json b/Source/hoc-clk/overlay/lang/de.json index 00e05a56..c06d9277 100644 --- a/Source/hoc-clk/overlay/lang/de.json +++ b/Source/hoc-clk/overlay/lang/de.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Kompilieren mit minimalen Funktionen", - "THE BEER-WARE LICENSE": "DIE BIERWAREN-LIZENZ" + "THE BEER-WARE LICENSE": "DIE BIERWAREN-LIZENZ", + "Horizon OC\nKip opening failed": "Horizon OC\nKIP konnte nicht geöffnet werden", + "Horizon OC\nKip read failed": "Horizon OC\nKIP konnte nicht gelesen werden", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nVeraltetes KIP erkannt!\nBitte Horizon OC aktualisieren", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nVeraltetes Sysmodule erkannt!\nBitte Horizon OC aktualisieren", + "Horizon OC\nKip write failed": "Horizon OC\nKIP-Schreiben fehlgeschlagen", + "Horizon OC\nKip config set failed": "Horizon OC\nKIP-Konfiguration fehlgeschlagen", + "Kip is not loaded!": "KIP ist nicht geladen!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP wurde aktualisiert\nBitte starte die Konsole neu", + "Horizon OC has been installed": "Horizon OC wurde installiert", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nKonfigurationspuffer stimmt nicht überein", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequenz deaktiviert.\nNeustart zum Anwenden.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon-Lesen fehlgeschlagen!\n Dies kann ein Hardwareproblem sein!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nI2C-Schreiben fehlgeschlagen\nbeim Setzen von VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP-Versionskonflikt\nBitte Horizon OC neu installieren" } diff --git a/Source/hoc-clk/overlay/lang/en.json b/Source/hoc-clk/overlay/lang/en.json index 727fab93..88121956 100644 --- a/Source/hoc-clk/overlay/lang/en.json +++ b/Source/hoc-clk/overlay/lang/en.json @@ -137,5 +137,19 @@ "or 1228MHz on HiOPT can cause ": "or 1228MHz on HiOPT can cause ", "permanent damage to your Switch!": "permanent damage to your Switch!", "921MHz without UV and 960MHz on": "921MHz without UV and 960MHz on", - "SLT or HiOPT can cause ": "SLT or HiOPT can cause " + "SLT or HiOPT can cause ": "SLT or HiOPT can cause ", + "Horizon OC\nKip opening failed": "Horizon OC\nKip opening failed", + "Horizon OC\nKip read failed": "Horizon OC\nKip read failed", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nKip write failed", + "Horizon OC\nKip config set failed": "Horizon OC\nKip config set failed", + "Kip is not loaded!": "Kip is not loaded!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP has been updated\nPlease reboot your console", + "Horizon OC has been installed": "Horizon OC has been installed", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nConfig Buffer Mismatch", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nDeactivated frequency.\nReboot to apply.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon read failed!\n This may be a hardware issue!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nFailed to write I2C\nwhile setting vddq", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/es.json b/Source/hoc-clk/overlay/lang/es.json index 7743bef3..ff8e8aed 100644 --- a/Source/hoc-clk/overlay/lang/es.json +++ b/Source/hoc-clk/overlay/lang/es.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Compilado con funciones mínimas", - "THE BEER-WARE LICENSE": "LICENCIA BEER-WARE" + "THE BEER-WARE LICENSE": "LICENCIA BEER-WARE", + "Horizon OC\nKip opening failed": "Horizon OC\nError al abrir el KIP", + "Horizon OC\nKip read failed": "Horizon OC\nError al leer el KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n¡KIP desactualizado detectado!\nActualiza Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n¡Sysmodule desactualizado detectado!\nActualiza Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nError al escribir el KIP", + "Horizon OC\nKip config set failed": "Horizon OC\nError al configurar el KIP", + "Kip is not loaded!": "¡El KIP no está cargado!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nEl KIP se ha actualizado\nReinicia la consola", + "Horizon OC has been installed": "Horizon OC se ha instalado", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nDesajuste del búfer de configuración", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrecuencia desactivada.\nReinicia para aplicar.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\n¡Error al leer Secmon!\n ¡Puede ser un problema de hardware!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nError al escribir I2C\nal configurar VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersión de KIP no coincide\nReinstala Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/fr.json b/Source/hoc-clk/overlay/lang/fr.json index c8f94ed2..7e193c0f 100644 --- a/Source/hoc-clk/overlay/lang/fr.json +++ b/Source/hoc-clk/overlay/lang/fr.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X : %u Y : %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Compilation avec fonctionnalités minimales", - "THE BEER-WARE LICENSE": "LA LICENCE BEER-WARE" + "THE BEER-WARE LICENSE": "LA LICENCE BEER-WARE", + "Horizon OC\nKip opening failed": "Horizon OC\nÉchec d'ouverture du KIP", + "Horizon OC\nKip read failed": "Horizon OC\nÉchec de lecture du KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nKIP obsolète détecté !\nVeuillez mettre à jour Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nSysmodule obsolète détecté !\nVeuillez mettre à jour Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nÉchec d'écriture du KIP", + "Horizon OC\nKip config set failed": "Horizon OC\nÉchec de configuration du KIP", + "Kip is not loaded!": "Le KIP n'est pas chargé !", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nLe KIP a été mis à jour\nVeuillez redémarrer la console", + "Horizon OC has been installed": "Horizon OC a été installé", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nIncohérence du tampon de configuration", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFréquence désactivée.\nRedémarrez pour appliquer.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nÉchec de lecture Secmon !\n Cela peut être un problème matériel !", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nÉchec d'écriture I2C\nlors du réglage de VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersion du KIP incompatible\nVeuillez réinstaller Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/it.json b/Source/hoc-clk/overlay/lang/it.json index 9706a9b2..2ab2629e 100644 --- a/Source/hoc-clk/overlay/lang/it.json +++ b/Source/hoc-clk/overlay/lang/it.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Compilazione con funzionalità minime", - "THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE" + "THE BEER-WARE LICENSE": "THE BEER-WARE LICENSE", + "Horizon OC\nKip opening failed": "Horizon OC\nApertura del KIP non riuscita", + "Horizon OC\nKip read failed": "Horizon OC\nLettura del KIP non riuscita", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nKIP obsoleto rilevato!\nAggiorna Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nSysmodule obsoleto rilevato!\nAggiorna Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nScrittura del KIP non riuscita", + "Horizon OC\nKip config set failed": "Horizon OC\nConfigurazione del KIP non riuscita", + "Kip is not loaded!": "Il KIP non è caricato!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nIl KIP è stato aggiornato\nRiavvia la console", + "Horizon OC has been installed": "Horizon OC è stato installato", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nBuffer di configurazione non corrispondente", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequenza disattivata.\nRiavvia per applicare.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nLettura Secmon non riuscita!\n Potrebbe essere un problema hardware!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nScrittura I2C non riuscita\ndurante l'impostazione di VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersione KIP non corrispondente\nReinstalla Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/ja.json b/Source/hoc-clk/overlay/lang/ja.json index 8dd8a981..f2c9ce30 100644 --- a/Source/hoc-clk/overlay/lang/ja.json +++ b/Source/hoc-clk/overlay/lang/ja.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "最小限の機能でコンパイルする", - "THE BEER-WARE LICENSE": "ビール製品ライセンス" + "THE BEER-WARE LICENSE": "ビール製品ライセンス", + "Horizon OC\nKip opening failed": "Horizon OC\nKIPを開けませんでした", + "Horizon OC\nKip read failed": "Horizon OC\nKIPの読み取りに失敗しました", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n古いKIPを検出しました!\nHorizon OCを更新してください", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n古いsysmoduleを検出しました!\nHorizon OCを更新してください", + "Horizon OC\nKip write failed": "Horizon OC\nKIPの書き込みに失敗しました", + "Horizon OC\nKip config set failed": "Horizon OC\nKIP設定に失敗しました", + "Kip is not loaded!": "KIPが読み込まれていません!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIPが更新されました\n本体を再起動してください", + "Horizon OC has been installed": "Horizon OCがインストールされました", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\n設定バッファが一致しません", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\n周波数を無効化しました。\n再起動して適用してください。", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmonの読み取りに失敗しました!\n ハードウェアの問題の可能性があります!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nVDDQ設定中に\nI2C書き込みに失敗しました", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIPバージョンが一致しません\nHorizon OCを再インストールしてください" } diff --git a/Source/hoc-clk/overlay/lang/jp.json b/Source/hoc-clk/overlay/lang/jp.json index af92cbf7..ee430cb9 100644 --- a/Source/hoc-clk/overlay/lang/jp.json +++ b/Source/hoc-clk/overlay/lang/jp.json @@ -142,5 +142,19 @@ "or 1228MHz on HiOPT can cause ": "or 1228MHz on HiOPT can cause", "permanent damage to your Switch!": "permanent damage to your Switch!", "921MHz without UV and 960MHz on": "921MHz without UV and 960MHz on", - "SLT or HiOPT can cause ": "SLT or HiOPT can cause" + "SLT or HiOPT can cause ": "SLT or HiOPT can cause", + "Horizon OC\nKip opening failed": "Horizon OC\nKip opening failed", + "Horizon OC\nKip read failed": "Horizon OC\nKip read failed", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nKip write failed", + "Horizon OC\nKip config set failed": "Horizon OC\nKip config set failed", + "Kip is not loaded!": "Kip is not loaded!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP has been updated\nPlease reboot your console", + "Horizon OC has been installed": "Horizon OC has been installed", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nConfig Buffer Mismatch", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nDeactivated frequency.\nReboot to apply.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon read failed!\n This may be a hardware issue!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nFailed to write I2C\nwhile setting vddq", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/ko.json b/Source/hoc-clk/overlay/lang/ko.json index 8865d37f..47e3a220 100644 --- a/Source/hoc-clk/overlay/lang/ko.json +++ b/Source/hoc-clk/overlay/lang/ko.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%umV", "Compiling with minimal features": "최소한의 기능으로 컴파일하기", - "THE BEER-WARE LICENSE": "맥주 제품 라이센스" + "THE BEER-WARE LICENSE": "맥주 제품 라이센스", + "Horizon OC\nKip opening failed": "Horizon OC\nKIP을 열지 못했습니다", + "Horizon OC\nKip read failed": "Horizon OC\nKIP 읽기에 실패했습니다", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n오래된 KIP이 감지되었습니다!\nHorizon OC를 업데이트하세요", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n오래된 sysmodule이 감지되었습니다!\nHorizon OC를 업데이트하세요", + "Horizon OC\nKip write failed": "Horizon OC\nKIP 쓰기에 실패했습니다", + "Horizon OC\nKip config set failed": "Horizon OC\nKIP 설정에 실패했습니다", + "Kip is not loaded!": "KIP이 로드되지 않았습니다!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP이 업데이트되었습니다\n본체를 재부팅하세요", + "Horizon OC has been installed": "Horizon OC가 설치되었습니다", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\n설정 버퍼가 일치하지 않습니다", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\n주파수가 비활성화되었습니다.\n적용하려면 재부팅하세요.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon 읽기에 실패했습니다!\n 하드웨어 문제일 수 있습니다!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nVDDQ 설정 중\nI2C 쓰기에 실패했습니다", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP 버전이 일치하지 않습니다\nHorizon OC를 다시 설치하세요" } diff --git a/Source/hoc-clk/overlay/lang/nl.json b/Source/hoc-clk/overlay/lang/nl.json index 5095d2be..7d447716 100644 --- a/Source/hoc-clk/overlay/lang/nl.json +++ b/Source/hoc-clk/overlay/lang/nl.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Compileren met minimale functies", - "THE BEER-WARE LICENSE": "DE LICENTIE VOOR BIERWAREN" + "THE BEER-WARE LICENSE": "DE LICENTIE VOOR BIERWAREN", + "Horizon OC\nKip opening failed": "Horizon OC\nKIP openen mislukt", + "Horizon OC\nKip read failed": "Horizon OC\nKIP lezen mislukt", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nVerouderde KIP gedetecteerd!\nWerk Horizon OC bij", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nVerouderde sysmodule gedetecteerd!\nWerk Horizon OC bij", + "Horizon OC\nKip write failed": "Horizon OC\nKIP schrijven mislukt", + "Horizon OC\nKip config set failed": "Horizon OC\nKIP-configuratie mislukt", + "Kip is not loaded!": "KIP is niet geladen!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP is bijgewerkt\nHerstart je console", + "Horizon OC has been installed": "Horizon OC is geïnstalleerd", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nConfiguratiebuffer komt niet overeen", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequentie gedeactiveerd.\nHerstart om toe te passen.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nSecmon lezen mislukt!\n Dit kan een hardwareprobleem zijn!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nI2C schrijven mislukt\nbij het instellen van VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP-versie komt niet overeen\nInstalleer Horizon OC opnieuw" } diff --git a/Source/hoc-clk/overlay/lang/pl.json b/Source/hoc-clk/overlay/lang/pl.json index eecc5071..a320fe9b 100644 --- a/Source/hoc-clk/overlay/lang/pl.json +++ b/Source/hoc-clk/overlay/lang/pl.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Kompilacja z minimalnymi funkcjami", - "THE BEER-WARE LICENSE": "LICENCJA NA WYROBY PIWNE" + "THE BEER-WARE LICENSE": "LICENCJA NA WYROBY PIWNE", + "Horizon OC\nKip opening failed": "Horizon OC\nNie udało się otworzyć KIP", + "Horizon OC\nKip read failed": "Horizon OC\nNie udało się odczytać KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nWykryto przestarzały KIP!\nZaktualizuj Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nWykryto przestarzały sysmodule!\nZaktualizuj Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nZapis KIP nie powiódł się", + "Horizon OC\nKip config set failed": "Horizon OC\nKonfiguracja KIP nie powiodła się", + "Kip is not loaded!": "KIP nie jest załadowany!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP został zaktualizowany\nUruchom ponownie konsolę", + "Horizon OC has been installed": "Horizon OC został zainstalowany", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nNiezgodność bufora konfiguracji", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nCzęstotliwość wyłączona.\nZrestartuj, aby zastosować.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nOdczyt Secmon nie powiódł się!\n To może być problem sprzętowy!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nZapis I2C nie powiódł się\npodczas ustawiania VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nNiezgodność wersji KIP\nZainstaluj ponownie Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/pt.json b/Source/hoc-clk/overlay/lang/pt.json index 906bab87..86b7dea1 100644 --- a/Source/hoc-clk/overlay/lang/pt.json +++ b/Source/hoc-clk/overlay/lang/pt.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "Compilando com recursos mínimos", - "THE BEER-WARE LICENSE": "A LICENÇA DE CERVEJA" + "THE BEER-WARE LICENSE": "A LICENÇA DE CERVEJA", + "Horizon OC\nKip opening failed": "Horizon OC\nFalha ao abrir o KIP", + "Horizon OC\nKip read failed": "Horizon OC\nFalha ao ler o KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nKIP desatualizado detectado!\nAtualize o Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nSysmodule desatualizado detectado!\nAtualize o Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nFalha ao gravar o KIP", + "Horizon OC\nKip config set failed": "Horizon OC\nFalha ao configurar o KIP", + "Kip is not loaded!": "O KIP não está carregado!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nO KIP foi atualizado\nReinicie o console", + "Horizon OC has been installed": "Horizon OC foi instalado", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nIncompatibilidade do buffer de configuração", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nFrequência desativada.\nReinicie para aplicar.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nFalha na leitura do Secmon!\n Isso pode ser um problema de hardware!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nFalha ao gravar I2C\nao definir VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nVersão do KIP incompatível\nReinstale o Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/ru.json b/Source/hoc-clk/overlay/lang/ru.json index 5d9f5831..d3b36cb2 100644 --- a/Source/hoc-clk/overlay/lang/ru.json +++ b/Source/hoc-clk/overlay/lang/ru.json @@ -232,5 +232,19 @@ "%u.%u%u mV": "%u.%u%u мВ", "Compiling with minimal features": "Собрано с урезанием функций", "THE BEER-WARE LICENSE": "BEER-WARE LICENSE", - "Auto": "Авто" + "Auto": "Авто", + "Horizon OC\nKip opening failed": "Horizon OC\nНе удалось открыть KIP", + "Horizon OC\nKip read failed": "Horizon OC\nНе удалось прочитать KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nОбнаружен устаревший KIP!\nОбновите Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nОбнаружен устаревший sysmodule!\nОбновите Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nОшибка записи KIP", + "Horizon OC\nKip config set failed": "Horizon OC\nОшибка настройки KIP", + "Kip is not loaded!": "KIP не загружен!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP обновлён\nПерезагрузите консоль", + "Horizon OC has been installed": "Horizon OC установлен", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nНесоответствие буфера конфигурации", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nЧастота отключена.\nПерезагрузите для применения.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nОшибка чтения Secmon!\n Возможно, проблема с оборудованием!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nОшибка записи I2C\nпри установке VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nНесовпадение версии KIP\nПереустановите Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/template.json b/Source/hoc-clk/overlay/lang/template.json index 51c51c1a..3d4e5610 100644 --- a/Source/hoc-clk/overlay/lang/template.json +++ b/Source/hoc-clk/overlay/lang/template.json @@ -211,4 +211,18 @@ "X: %u Y: %u": "", "%u.%u%u mV": "", "Compiling with minimal features": "", + "Horizon OC\nKip opening failed": "", + "Horizon OC\nKip read failed": "", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "", + "Horizon OC\nKip write failed": "", + "Horizon OC\nKip config set failed": "", + "Kip is not loaded!": "", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "", + "Horizon OC has been installed": "", + "Horizon OC\nConfig Buffer Mismatch": "", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "" } diff --git a/Source/hoc-clk/overlay/lang/uk.json b/Source/hoc-clk/overlay/lang/uk.json index 7af7040a..ffd3b5ac 100644 --- a/Source/hoc-clk/overlay/lang/uk.json +++ b/Source/hoc-clk/overlay/lang/uk.json @@ -211,5 +211,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u мВ", "Compiling with minimal features": "Компіляція з мінімальними можливостями", - "THE BEER-WARE LICENSE": "ЛІЦЕНЗІЯ НА ПИВНИЙ ПОСУД" + "THE BEER-WARE LICENSE": "ЛІЦЕНЗІЯ НА ПИВНИЙ ПОСУД", + "Horizon OC\nKip opening failed": "Horizon OC\nНе вдалося відкрити KIP", + "Horizon OC\nKip read failed": "Horizon OC\nНе вдалося прочитати KIP", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\nВиявлено застарілий KIP!\nОновіть Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\nВиявлено застарілий sysmodule!\nОновіть Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\nПомилка запису KIP", + "Horizon OC\nKip config set failed": "Horizon OC\nПомилка налаштування KIP", + "Kip is not loaded!": "KIP не завантажено!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP оновлено\nПерезавантажте консоль", + "Horizon OC has been installed": "Horizon OC встановлено", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\nНевідповідність буфера конфігурації", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\nЧастоту вимкнено.\nПерезавантажте для застосування.", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\nПомилка читання Secmon!\n Це може бути апаратна проблема!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\nПомилка запису I2C\nпід час встановлення VDDQ", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nНевідповідність версії KIP\nПеревстановіть Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/zh-cn.json b/Source/hoc-clk/overlay/lang/zh-cn.json index fbe19238..225aaeff 100644 --- a/Source/hoc-clk/overlay/lang/zh-cn.json +++ b/Source/hoc-clk/overlay/lang/zh-cn.json @@ -13,9 +13,7 @@ "Enabled (Default)": "启用 (默认)", "Enable": "启用", "Fatal error": "严重错误", - "Could not connect to hoc-clk sysmodule.\\n\\n": "无法连接hoc-clk系统模块。\\n\\n", - "Please make sure everything is\\n\\n": "请确保所有内容均已\\n\\n", - "correctly installed and enabled.": "正确安装且启用。", + "hoc-clk is not running.\n\n\nPlease make sure it is correctly\n\ninstalled and enabled.": "刚安装需要重启生效。\n\n\n请进入后台管理确认后台程序正在运行。", "Edit App Profile": "编辑应用配置", "Edit Global Profile": "编辑全局配置", @@ -27,9 +25,7 @@ "Updates": "更新菜单", "Credits": "致谢", - "Application changed\\n\\n": "应用出现变更\\n\\n", - "The running application changed\\n\\n": "当前应用在编辑过程中\\n\\n", - "while editing was going on.": "出现了变更。", + "Could not connect to hoc-clk.\n\n\nPlease make sure it is correctly\n\ninstalled and enabled.": "应用已变更\n\n\n无法连接到 hoc-clk。\n\n首次安装请重启。", "Sleep Mode": "睡眠模式", "Stock": "原厂默认", @@ -148,6 +144,7 @@ "3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (需要极限体质)", "3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (需要极限体质)", "JEDEC.": "JEDEC", + "RAM-Timing tBreak": "内存时序切换阈值", "CPU Boost Clock": "CPU加速频率", "Auto CPU RAM OC": "高内存频率自动提频CPU", @@ -183,10 +180,10 @@ "Official Service": "官方服务", "96.6% limit": "使用率最高96.6%", "99.7% limit": "使用率最高99.7%", - " Setting GPU Clocks past": "警告:在没有适当降压", - "1228MHz without a proper undervolt": "的情况下将GPU频率超到", - "can cause degradation or damage": "1228MHz以上可能会", - "to your console!": "更伤主机,乃至造成损失!", + " Setting GPU Clocks past": "警告:未进行合理降压的前提下,", + "1305MHz without a proper undervolt": "将GPU频率拉到1228MHz以上(1305MHz),", + "can cause degradation or damage": "会加速硬件老化损耗,", + "to your console!": "甚至对主机造成永久性硬件损坏!", "1075MHz without UV, 1152MHz on SLT": "无降压1075MHz,SLT下1152MHz", "or 1228MHz on HiOPT can cause ": "或HiOPT下1228MHz可能会伤主机,", "permanent damage to your Switch!": "乃至造成无法挽回的损失!", @@ -269,5 +266,19 @@ "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "以最小功能编译", - "THE BEER-WARE LICENSE": "啤酒软件许可协议" + "THE BEER-WARE LICENSE": "啤酒软件许可协议", + "Horizon OC\nKip opening failed": "Horizon OC\n无法打开 KIP", + "Horizon OC\nKip read failed": "Horizon OC\n读取 KIP 失败", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n检测到过时的 KIP!\n请更新 Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n检测到过时的系统模块!\n请更新 Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\n写入 KIP 失败", + "Horizon OC\nKip config set failed": "Horizon OC\n设置 KIP 配置失败", + "Kip is not loaded!": "KIP 未加载!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP 已更新\n请重启主机", + "Horizon OC has been installed": "Horizon OC 已安装", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\n配置缓冲区不匹配", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\n已停用频率。\n重启以应用。", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\n读取 Secmon 失败!\n 这可能是硬件问题!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\n设置 VDDQ 时\nI2C 写入失败", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP 版本不匹配\n请重新安装 Horizon OC" } diff --git a/Source/hoc-clk/overlay/lang/zh-tw.json b/Source/hoc-clk/overlay/lang/zh-tw.json index 75765af9..7b71ad2a 100644 --- a/Source/hoc-clk/overlay/lang/zh-tw.json +++ b/Source/hoc-clk/overlay/lang/zh-tw.json @@ -4,212 +4,281 @@ "Installed": "已安裝", "Not Installed": "未安裝", "Default": "預設", - "Do Not Override": "不要覆蓋", - "Do not override": "不要覆蓋", - "Disabled": "已停用", - "Enabled": "已啟用", - "Enabled (Default)": "已啟用(預設)", + "Do Not Override": "不修改", + "No Override": "不修改", + "Do not override": "不修改", + "Auto": "自動", + "Disabled": "停用", + "Enabled": "啟用", + "Enabled (Default)": "啟用 (預設)", "Enable": "啟用", - "Fatal error": "致命錯誤", - "Could not connect to hoc-clk sysmodule.\\n\\n": "無法連接到 hoc-clk 系統模組。\\n\\n", - "Please make sure everything is\\n\\n": "請確保一切正常\\n\\n", - "correctly installed and enabled.": "正確安裝並啟用。", + "Fatal error": "嚴重錯誤", + "hoc-clk is not running.\n\n\nPlease make sure it is correctly\n\ninstalled and enabled.": "剛安裝需要重啟生效。\n\n\n請進入後台管理確認後台程式正在執行。", - "Edit App Profile": "編輯應用程式設定檔", - "Edit Global Profile": "編輯全域設定檔", - "Temporary Overrides": "臨時覆蓋", - "Temporary Overrides ": "臨時覆蓋", - "  Reset": " 重設", + "Edit App Profile": "編輯應用配置", + "Edit Global Profile": "編輯全域配置", + "Temporary Overrides": "臨時配置", + "Temporary Overrides ": "臨時配置", + "  Reset": "  重設", "Settings": "設定", "About": "關於", - "Credits": "製作人員", + "Updates": "更新選單", + "Credits": "致謝", - "Application changed\\n\\n": "應用程式已更改\\n\\n", - "The running application changed\\n\\n": "正在運行的應用程式已更改\\n\\n", - "while editing was going on.": "當編輯正在進行時。", + "Could not connect to hoc-clk.\n\n\nPlease make sure it is correctly\n\ninstalled and enabled.": "應用已變更\n\n\n無法連接到 hoc-clk。\n\n首次安裝請重啟。", "Sleep Mode": "睡眠模式", "Stock": "原廠預設", - "Dev OC": "開發OC", - "Boost Mode": "升壓模式", + "Dev OC": "開發者超頻", + "Boost Mode": "加速模式", "Safe Max": "安全最大值", - "Unsafe Max": "不安全最大值", + "Unsafe Max": "危險最大值", "Absolute Max": "絕對最大值", - "Handheld Safe Max": "手持式安全最大值", + "Handheld Safe Max": "掌機安全最大值", - "General Settings": "常規設定", - "Governor Settings": "調速器設定", + "General Settings": "通用設定", + "Governor Settings": "調頻器設定", "Safety Settings": "安全設定", - "Save KIP Settings": "儲存 KIP 設定", + "Save KIP Settings": "儲存KIP設定", "RAM Settings": "記憶體設定", - "CPU Settings": "中央處理器設定", + "CPU Settings": "CPU設定", "GPU Settings": "GPU設定", - "Display Settings": "顯示設定", + "Display Settings": "螢幕設定", "Experimental Settings": "實驗性設定", - "Experimental": "實驗性的", + "Experimental": "實驗性功能", - " Settings marked in blue": "藍色標示的設定", - "don't require a reboot to apply!": "無需重開機即可套用!", - "You can also press  to show": "也可按  顯示", - "information about each setting.": "每項設定的說明。", + " Settings marked in blue": "藍色的設定更改後可立即生效,", + "don't require a reboot to apply!": "無需重啟主機。", + "You can also press  to show": "按可以查看每項設置的", + "information about each setting.": "詳細說明(英文)。", - " Experimental Settings are incomplete ": "實驗性設定尚未完成", - "and may not work correctly or at all!": "且可能無法正常運作!", - "Here be dragons!": "此處有危險!", + " Experimental Settings are incomplete ": "實驗性設定尚未正式完成,", + "and may not work correctly or at all!": "可能導致當機崩潰等意外後果!", + "Here be dragons!": "風險自負!", - "RAM Voltage Display Mode": "RAM電壓顯示模式", + "RAM Voltage Display Mode": "記憶體電壓顯示模式", "RAM Display Unit": "記憶體顯示單位", "Polling Interval": "輪詢間隔", + "Enable Experimental Settings": "啟用實驗性設定", - "GPU Scheduling Override Method": "GPU調度覆蓋方法", - "GPU Scheduling Override": "GPU 調度覆蓋", - "GPU Boot Volt": "GPU 啟動電壓", - "GPU Boot Voltage": "GPU 啟動電壓", + "Mariko Middle Clocks": "啟用GPU中頻", + "Live CPU Undervolt": "即時CPU降壓", + "GPU Scheduling Override Method": "GPU調度修改方式", + "GPU Scheduling Override": "GPU調度修改", + "GPU Boot Volt": "GPU啟動電壓", + "GPU Boot Voltage": "GPU啟動電壓", "Memory Frequency Measurement Mode": "記憶體頻率測量模式", - " Overriding the charge current": "覆蓋充電電流", - "can be dangerous and may cause": "可能很危險並可能導致", - "damage to your battery or charger!": "損壞電池或充電器!", - "Charge Current Override": "充電電流覆蓋", - "Display Color Preset": "顯示顏色預設", - "Basic": "基本", + " Overriding the charge current": "修改電池充電電流", + "can be dangerous and may cause": "存在一定風險,可能會傷電池", + "damage to your battery or charger!": "或充電器!", + "Charge Current Override": "修改充電電流", + " Overriding the input current": "修改輸入電流可能會增加主機", + "limit increases power draw from": "從充電器獲取的電流。", + "your charger. And board stress ": "主機板本身最大電流為", + "use 1500mA max.": "1500mA。", + "Input Current Limit Override": "修改主機輸入電流", + "Display Color Preset": "螢幕濾鏡", + "Basic": "基礎", "Saturated": "飽和", "Washed": "淡色", "Natural": "自然", "Vivid": "鮮艷", - "CPU Governor Minimum Frequency": "CPU調速器最低頻率", - " Usage of unsafe display": "不安全的顯示率", - "refresh rates may cause stress": "刷新率可能會造成壓力", - "or damage to your display! ": "或損壞您的顯示器!", - "Proceed at your own risk!": "請自行承擔風險!", - "Max Handheld Display": "最大手持顯示器", - "Max Handheld Display Hz": "最大手持顯示率 Hz", - "Display Clock": "顯示時鐘", - " Adjust the display voltage": "調整顯示電壓", - "with caution to avoid damage": "請謹慎操作以避免損壞", - "to your display panel! ": "顯示面板!", - "Display Voltage": "顯示電壓", + "CPU Governor Minimum Frequency": "CPU調頻器最低頻率", + " Usage of unsafe display": "調整螢幕更新率", + "refresh rates may cause stress": "可能會傷螢幕,", + "or damage to your display! ": "乃至造成損失!", + "Proceed at your own risk!": "風險自負!", + "Display Refresh Rate Changing": "修改螢幕更新率", + "Max Handheld Display": "掌機最大更新率", + "Max Handheld Display Hz": "掌機最大更新率", + "Display Clock": "螢幕更新率", + " Adjust the display voltage": "請謹慎調整顯示電壓", + "with caution to avoid damage": "請謹慎以避免損壞", + "to your display panel! ": "螢幕面板!", + "Display Voltage": "螢幕電壓", - "Thermal Throttle Limit": "熱油門限制", - "Official Rating": "官方評級", + "Uncapped Clocks": "解鎖頻率", + "Thermal Throttle": "過熱保護", + "Thermal Throttle Limit": "過熱保護溫度", + "Official Rating": "官方額定值", "TDP Threshold": "TDP閾值", "Power": "電源", - "HP Mode": "高效能模式", + "HP Mode": "高性能模式", - "DVB Shift": "DVB 偏移", - "SoC Max Volt": "SoC 最大電壓", + "DVB Shift": "DVB偏移", + "SoC Max Volt": "SoC最高電壓", "Step Mode": "步進模式", - "RAM VDD2 Voltage": "RAM VDD2 電壓", - "RAM VDDQ Voltage": "RAM VDDQ 電壓", + "RAM VDD2 Voltage": "記憶體VDD2電壓", + "RAM VDDQ Voltage": "記憶體VDDQ電壓", "Voltage": "電壓", - "RAM Frequency Editor": "RAM頻率編輯器", - "Ram Max Clock": "記憶體最大時脈", - "RAM Latency Editor": "RAM 延遲編輯器", - "RAM Timing Reductions": "RAM 時序減少", + "RAM Frequency Editor": "記憶體頻率編輯", + "Ram Max Clock": "記憶體最高頻率", + "RAM Latency Editor": "記憶體延遲編輯", + "RAM Timing Reductions": "記憶體時序最佳化", + "SOC Voltage Table": "自訂SOC電壓", + "SOC Custom Voltages": "自訂SOC電壓", "Memory Timings": "記憶體時序", "Advanced": "進階", - "t6 tRTW Fine Tune": "t6 tRTW 微調", - "tRTW Fine Tune": "tRTW 微調", - "t7 tWTR Fine Tune": "t7 tWTR 微調", - "tWTR Fine Tune": "tWTR 微調", + "t6 tRTW Fine Tune": "t6 tRTW微調", + "tRTW Fine Tune": "tRTW微調", + "t7 tWTR Fine Tune": "t7 tWTR微調", + "tWTR Fine Tune": "tWTR微調", "Memory Latencies": "記憶體延遲", "Read Latency": "讀取延遲", "Write Latency": "寫入延遲", - "High speedo needed!": "需要高 Speedo 值!", - "3333MHz (Needs extreme Speedo/PLL)": "3333MHz(需要極高的 Speedo/PLL)", - "3366MHz (Needs extreme Speedo/PLL)": "3366MHz(需要極高的 Speedo/PLL)", - "3400MHz (Needs extreme Speedo/PLL)": "3400MHz(需要極高的 Speedo/PLL)", - "3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz(需要荒謬的 Speedo/PLL)", - "3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz(需要荒謬的 Speedo/PLL)", - "3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz(需要荒謬的 Speedo/PLL)", - "JEDEC.": "JEDEC。", + "Latency Graph": "延遲可視化圖", + "Latency Max": "延遲最高頻率" + "1333 Latency Max": "1333延遲最高頻率", + "1600 Latency Max": "1600延遲最高頻率", + "1866 Latency Max": "1866延遲最高頻率", + "2133 Latency Max": "2133延遲最高頻率", + "Read": "讀取", + "Write": "寫入", + "Same": "相同" + "High speedo needed!": "需要高體質!", + "3333MHz (Needs extreme Speedo/PLL)": "3333MHz (需要極高體質)", + "3366MHz (Needs extreme Speedo/PLL)": "3366MHz (需要極高體質)", + "3400MHz (Needs extreme Speedo/PLL)": "3400MHz (需要極高體質)", + "3433MHz (Needs ridiculous Speedo/PLL)": "3433MHz (需要極限體質)", + "3466MHz (Needs ridiculous Speedo/PLL)": "3466MHz (需要極限體質)", + "3500MHz (Needs ridiculous Speedo/PLL)": "3500MHz (需要極限體質)", + "JEDEC.": "JEDEC", + "RAM-Timing tBreak": "記憶體時序切換閾值", - "CPU Boost Clock": "CPU 升壓時脈", - "CPU UV": "CPU 降壓", + "CPU Boost Clock": "CPU加速頻率", + "Auto CPU RAM OC": "高記憶體頻率自動提頻CPU", + "Auto CPU RAM OC CPU clock": "自動提頻CPU頻率", + "Auto CPU RAM OC Threshold": "自動提頻觸發閾值", + "Overwrite Boost Mode": "接管系統CPU調度", + "CPU UV": "CPU降壓", "CPU Unlock": "CPU解鎖", - "CPU VMIN": "CPU 最低電壓", - "CPU Max Voltage": "CPU最大電壓", - "CPU Max Clock": "CPU 最大時脈", - "Extreme UV Table": "極限降壓表", - "CPU UV Table": "CPU 降壓表", - "CPU Low UV": "CPU 低壓降壓", - "CPU High UV": "CPU 高壓降壓", - "CPU Low VMIN": "CPU 低 VMIN", - "CPU High VMIN": "CPU 高 VMIN", + "CPU VMIN": "CPU最低電壓", + "CPU Max Voltage": "CPU最高電壓", + "CPU Max Clock": "CPU最高頻率", + "Extreme UV Table": "極限降壓", + "CPU UV Table": "CPU降壓模式", + "CPU Low UV": "CPU低頻降壓", + "CPU High UV": "CPU高頻降壓", + "CPU Low VMIN": "CPU低頻最低電壓", + "CPU High VMIN": "CPU高頻最低電壓", - "No Undervolt": "無欠壓", - "SLT Table": "SLT表", - "HiOPT Table": "HiOPT表", - "GPU Undervolt Table": "GPU 欠壓表", + "No Undervolt": "無降壓", + "SLT Table": "SLT", + "HiOPT Table": "HiOPT", + "High UV Table": "高降壓", + "GPU Undervolt Table": "GPU降壓模式", "GPU Minimum Voltage": "GPU最低電壓", - "Calculate GPU Vmin": "計算 GPU Vmin", - "GPU VMIN": "GPU VMIN", - "GPU Maximum Voltage": "GPU最大電壓", + "Calculate GPU Vmin": "計算GPU最低電壓", + "GPU VMIN": "GPU最低電壓", + "GPU Maximum Voltage": "GPU最高電壓", "GPU Voltage Offset": "GPU電壓偏移", - "GPU DVFS Mode": "GPU DVFS 模式", - "GPU DVFS Offset": "GPU DVFS 偏移", - "GPU Voltage Table": "GPU電壓表", - "GPU Custom Table (mV)": "GPU 自訂表 (mV)", + "GPU DVFS Mode": "GPU DVFS模式", + "GPU DVFS Offset": "GPU DVFS偏移", + "GPU Voltage Table": "自訂GPU電壓", + "GPU Custom Table (mV)": "GPU自訂電壓(mV)", "Official Service": "官方服務", - "96.6% limit": "96.6%限制", - "99.7% limit": "99.7%限制", - " Setting GPU Clocks past": "將 GPU 頻率設定超過", - "1228MHz without a proper undervolt": "1228MHz 未適當降壓", - "can cause degradation or damage": "可能導致劣化或損壞", - "to your console!": "您的主機!", - "1075MHz without UV, 1152MHz on SLT": "無 UV 時為 1075MHz,SLT 時為 1152MHz", - "or 1228MHz on HiOPT can cause ": "或 HiOPT 上的 1228MHz 可能會導致", - "permanent damage to your Switch!": "對您的 Switch 造成永久性損壞!", - "921MHz without UV and 960MHz on": "無 UV 時為 921MHz,開啟時為 960MHz", - "SLT or HiOPT can cause ": "SLT 或 HiOPT 可能會導致", + "96.6% limit": "使用率最高96.6%", + "99.7% limit": "使用率最高99.7%", + " Setting GPU Clocks past": "警告:未進行合理降壓的前提下,", + "1305MHz without a proper undervolt": "將GPU頻率拉到1228MHz以上(1305MHz),", + "can cause degradation or damage": "會加速硬體老化損耗,", + "to your console!": "甚至對主機造成永久性硬體損壞!", + "1075MHz without UV, 1152MHz on SLT": "無降壓1075MHz,SLT下1152MHz", + "or 1228MHz on HiOPT can cause ": "或HiOPT下1228MHz可能會傷主機,", + "permanent damage to your Switch!": "乃至造成無法挽回的損失!", + "921MHz without UV and 960MHz on": "無降壓921MHz,SLT/HiOPT 表下 960MHz", + "SLT or HiOPT can cause ": "可能導致", - "Default (Mariko)": "預設 (Mariko)", - "Default (Erista)": "預設 (Erista)", - "Rating": "評級", - "Safe Max (Mariko)": "安全最大值 (Mariko)", - "Safe Max (Erista)": "安全最大值 (Erista)", + "Default (Mariko)": "預設(續航版)", + "Default (Erista)": "預設(初版)", + "Rating": "額定值", + "Safe Max (Mariko)": "安全最大值(續航版)", + "Safe Max (Erista)": "安全最大值(初版)", "Voltages": "電壓", - "RAM Voltage:": "記憶體電壓:", - "Display Voltage:": "顯示電壓:", + "RAM Voltage:": "記憶體電壓:", + "Display Voltage:": "螢幕電壓:", "Temperatures": "溫度", - "PLLX Temp:": "PLLX 溫度:", - "AOTAG Temp:": "AOTAG 溫度:", - "BQ24193 Temp:": "BQ24193 溫度:", + "PLLX Temp:": "PLLX溫度:", + "AOTAG Temp:": "AOTAG溫度:", + "BQ24193 Temp:": "BQ24193溫度:", "Normal": "正常", "Warm": "溫熱", - "Hot": "過熱", - "Overheat": "嚴重過熱", + "Hot": "較熱", + "Overheat": "過熱", "Not Patched": "未修補", "Invalid": "無效", "RAM Bandwidth": "記憶體頻寬", - "RAM BW (Peak):": "RAM頻寬(峰值):", - "RAM BW (All):": "RAM頻寬(全部):", - "RAM BW (CPU):": "RAM頻寬(CPU):", - "RAM BW (GPU):": "RAM頻寬(GPU):", + "RAM BW (Peak):": "記憶體頻寬(最高):", + "RAM BW (All):": "記憶體頻寬(目前):", + "RAM BW (CPU):": "記憶體頻寬(CPU):", + "RAM BW (GPU):": "記憶體頻寬(GPU):", "Hardware Info": "硬體資訊", - "Console Type:": "主機類型:", - "Speedo:": "Speedo:", - "DRAM Module: ": "DRAM 模組:", + "Console Type:": "主機類型:", + "Speedo:": "體質分:", + "DRAM Module: ": "記憶體顆粒: ", "Software Info": "軟體資訊", - "KIP version:": "KIP 版本:", - "sys-dock status:": "系統塢站狀態:", - "SaltyNX status:": "SaltyNX 狀態:", - "RR Display status:": "RR 顯示狀態:", - "Wafer Position:": "晶圓位置:", - "IDDQ:": "IDDQ:", - "Module: ": "模組:", - "Board": "主板", - "Display": "顯示", + "KIP version:": "KIP版本:", + "sys-dock status:": "sys-dock狀態:", + "SaltyNX status:": "SaltyNX狀態:", + "RR Display status:": "RR顯示狀態:", + "General Info": "其他資訊", + "Wafer Position:": "晶圓位置:", + "IDDQ:": "IDDQ:", + "Module: ": "模組: ", + + "App ID": "應用ID", + "Profile": "目前配置", + "Docked": "底座模式", + "Handheld": "掌機模式", + "PD Charger": "PD充電器", + "USB Charger": "USB充電器", + "Memory": "記憶體", + "MEM": "記憶體", + "Governor": "調頻器", + "Board": "主機板", + "Display": "螢幕", + "BAT": "電池", + "Fan": "風扇", + "Now": "目前", + "Avg": "平均", + "OLED": "螢幕", + "LCD": "螢幕" + + "Releases": "發行版本", + "Horizon OC + Extensions": "Horizon OC+附加包", + "Status": "狀態", + "Downloading": "正在下載", + "Extracting": "正在解壓", + "Download cancelled": "下載已取消", + "Download failed -- check network": "下載失敗,請檢查網路", + "Extraction cancelled": "解壓已取消", + "Extraction failed -- archive may be corrupt": "解壓失敗,文件可能損壞", + "Update complete! Restart required.": "更新完成,重啟生效", - "Developers": "開發商", - "Contributors": "貢獻者", - "Testers": "測試人員", + "Developers": "開發", + "Contributors": "貢獻", + "Testers": "測試", "Translators": "翻譯", "Special Thanks": "特別感謝", "X: %u Y: %u": "X: %u Y: %u", "%u.%u%u mV": "%u.%u%u mV", "Compiling with minimal features": "使用最少的功能進行編譯", - "THE BEER-WARE LICENSE": "啤酒製品許可證" + "THE BEER-WARE LICENSE": "啤酒製品許可證", + "Horizon OC\nKip opening failed": "Horizon OC\n無法開啟 KIP", + "Horizon OC\nKip read failed": "Horizon OC\n讀取 KIP 失敗", + "Horizon OC\nOutdated kip detected!\nPlease update Horizon OC": "Horizon OC\n偵測到過時的 KIP!\n請更新 Horizon OC", + "Horizon OC\nOutdated sysmodule detected!\nPlease update Horizon OC": "Horizon OC\n偵測到過時的系統模組!\n請更新 Horizon OC", + "Horizon OC\nKip write failed": "Horizon OC\n寫入 KIP 失敗", + "Horizon OC\nKip config set failed": "Horizon OC\n設定 KIP 組態失敗", + "Kip is not loaded!": "KIP 尚未載入!", + "Horizon OC\nKIP has been updated\nPlease reboot your console": "Horizon OC\nKIP 已更新\n請重新啟動主機", + "Horizon OC has been installed": "Horizon OC 已安裝", + "Horizon OC\nConfig Buffer Mismatch": "Horizon OC\n組態緩衝區不符", + "Horizon OC\nDeactivated frequency.\nReboot to apply.": "Horizon OC\n已停用頻率。\n重新啟動以套用。", + "Horizon OC\nSecmon read failed!\n This may be a hardware issue!": "Horizon OC\n讀取 Secmon 失敗!\n 這可能是硬體問題!", + "Horizon OC\nFailed to write I2C\nwhile setting vddq": "Horizon OC\n設定 VDDQ 時\nI2C 寫入失敗", + "Horizon OC\nKip version mismatch\nPlease reinstall Horizon OC": "Horizon OC\nKIP 版本不符\n請重新安裝 Horizon OC" } diff --git a/Source/hoc-clk/overlay/src/main.cpp b/Source/hoc-clk/overlay/src/main.cpp index 41c411c5..1024dac5 100644 --- a/Source/hoc-clk/overlay/src/main.cpp +++ b/Source/hoc-clk/overlay/src/main.cpp @@ -96,6 +96,15 @@ class AppOverlay : public tsl::Overlay { ""); } + HocClkContext context = {}; + if (R_SUCCEEDED(hocclkIpcGetCurrentContext(&context)) && context.rebootRequired) { + return initially("Horizon OC has been updated.\n\n" + "\n" + "Please reboot your console\n\n" + "to finish applying the update.", + ""); + } + return initially(); } }; diff --git a/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp index 7aee9637..894253e8 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/base_gui.cpp @@ -132,7 +132,7 @@ void BaseGui::preDraw(tsl::gfx::Renderer *renderer) { drawDynamicUltraText(renderer, LOGO_TEXT_X, TEXT_Y, LOGO_LABEL_FONT_SIZE, STATIC_TEAL, false); - static const std::string versionStr = "Version " + getVersionString() + " \"Gaea\""; + static const std::string versionStr = "Version " + getVersionString() + " \"Athena\""; static constexpr tsl::Color versionColor(9, 9, 9, 15); static constexpr s32 vx = LOGO_TEXT_X + 15; static constexpr s32 vy = TEXT_Y + 18; diff --git a/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp b/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp index 13c34f0a..5fa73752 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/config_info_strings.cpp @@ -228,15 +228,15 @@ std::vector ConfigInfoStrings(HocClkConfigValue val, bool isMariko, case KipConfigValue_stepMode: return { "The step that RAM clocks take.", - "Options (with examples):", + "Options:", + " - 33MHz - 33 MHz step (ex. 1600, 1633, 1666, 1700, etc.)", " - 66MHz - 66 MHz step (ex. 1600, 1666, 1733, etc.)", " - 100MHz - 100 MHz step (ex. 1600, 1700, 1800, etc.)", - " - 133MHz - 66 MHz step (ex. 1600, 1733, 1866, etc.)", + " - 133MHz - 133 MHz step (ex. 1600, 1733, 1866, etc.)", " - JEDEC:", " - 1600, 1866, 1996, 2133, 2400, 2666, 2933 and 3200 MHz are used", "The RAM max clock will always be available regardless of the step mode, but the intermediate frequencies will be limited by the selected step mode.", - "This setting does not affect performance and the option you choose mostly is based on your personal taste", - "33 MHz step mode is not possible due to certain limitations of Horizon OS", + "This setting does not affect performance and the option you choose mostly is based on your personal taste", "Default: 66 MHz", }; diff --git a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp index 330165c9..616f8e7e 100644 --- a/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp +++ b/Source/hoc-clk/overlay/src/ui/gui/misc_gui.cpp @@ -1143,6 +1143,7 @@ class RamSubmenuGui : public MiscGui { if (IsMariko()) { std::vector stepMode = { + NamedValue("33MHz", 4), NamedValue("66MHz", 0), NamedValue("100MHz", 1), NamedValue("133MHz", 3), // Mantain compatability @@ -1248,9 +1249,12 @@ class RamSubmenuGui : public MiscGui { NamedValue("3133 MHz", 3133000), NamedValue("3166 MHz", 3166000), NamedValue("3200 MHz", 3200000, "JEDEC."), - NamedValue("3233 MHz", 3233000, "High speedo needed!"), - NamedValue("3266 MHz", 3266000, "High speedo needed!"), - NamedValue("3300 MHz", 3300000, "High speedo needed!"), + NamedValue("3233 MHz", 3233000, "±1720 speedo"), + NamedValue("3266 MHz", 3266000, "±1740 speedo"), + NamedValue("3300 MHz", 3300000, "±1760 speedo"), + NamedValue("3333 MHz", 3333000, "±1780 speedo"), + NamedValue("3366 MHz", 3366000, "±1800 speedo"), + NamedValue("3400 MHz", 3400000, "±1820 speedo") }; } @@ -1396,15 +1400,12 @@ class RamTimingsSubmenuGui : public MiscGui { NamedValue("3133 MHz", 3133000), NamedValue("3166 MHz", 3166000), NamedValue("3200 MHz", 3200000, "JEDEC."), - NamedValue("3233 MHz", 3233000, "High speedo needed"), - NamedValue("3266 MHz", 3266000, "High speedo needed!"), - NamedValue("3300 MHz", 3300000, "High speedo needed!"), - // NamedValue("3333MHz (Needs extreme Speedo/PLL)", 3333000), - // NamedValue("3366MHz (Needs extreme Speedo/PLL)", 3366000), - // NamedValue("3400MHz (Needs extreme Speedo/PLL)", 3400000), - // NamedValue("3433MHz (Needs ridiculous Speedo/PLL)", 3433000), - // NamedValue("3466MHz (Needs ridiculous Speedo/PLL)", 3466000), - // NamedValue("3500MHz (Needs ridiculous Speedo/PLL)", 3500000), + NamedValue("3233 MHz", 3233000, "±1720 speedo"), + NamedValue("3266 MHz", 3266000, "±1740 speedo"), + NamedValue("3300 MHz", 3300000, "±1760 speedo"), + NamedValue("3333 MHz", 3333000, "±1780 speedo"), + NamedValue("3366 MHz", 3366000, "±1800 speedo"), + NamedValue("3400 MHz", 3400000, "±1820 speedo"), }; RamDisplayUnit unit = (RamDisplayUnit)this->configList->values[HocClkConfigValue_RamDisplayUnit]; @@ -2192,9 +2193,12 @@ class CpuSubmenuGui : public MiscGui { NamedValue("3133 MHz", 3133000), NamedValue("3166 MHz", 3166000), NamedValue("3200 MHz", 3200000, "JEDEC."), - NamedValue("3233 MHz", 3233000, "High speedo needed!"), - NamedValue("3266 MHz", 3266000, "High speedo needed!"), - NamedValue("3300 MHz", 3300000, "High speedo needed!"), + NamedValue("3233 MHz", 3233000, "±1720 speedo"), + NamedValue("3266 MHz", 3266000, "±1740 speedo"), + NamedValue("3300 MHz", 3300000, "±1760 speedo"), + NamedValue("3333 MHz", 3333000, "±1780 speedo"), + NamedValue("3366 MHz", 3366000, "±1800 speedo"), + NamedValue("3400 MHz", 3400000, "±1820 speedo"), }; addConfigToggle(HocClkConfigValue_AutoRAMCPUOverclock, "Auto CPU RAM OC"); addConfigButton(HocClkConfigValue_AutoRamCpuCpuOCFreq, "Auto CPU RAM OC CPU clock", ValueRange(0, 0, 1, "", 1), "CPU Clock", diff --git a/Source/hoc-clk/sysmodule/Makefile b/Source/hoc-clk/sysmodule/Makefile index dfbda2d8..03b7eb07 100644 --- a/Source/hoc-clk/sysmodule/Makefile +++ b/Source/hoc-clk/sysmodule/Makefile @@ -28,9 +28,9 @@ INCLUDES := ../common/include src/hos src/soc src/i2c src/util src/pwr src/ipc EXEFS_SRC := exefs_src LIBNAMES := minIni # major minor patch -TARGET_VERSION := 2.5.0 -KIP_VERSION := 250 -CUST_REV := 6 +TARGET_VERSION := 3.0.0 +KIP_VERSION := 300 +CUST_REV := 7 #--------------------------------------------------------------------------------- # options for code generation diff --git a/Source/hoc-clk/sysmodule/src/file/file_utils.cpp b/Source/hoc-clk/sysmodule/src/file/file_utils.cpp index efa651a4..1de9ae5c 100644 --- a/Source/hoc-clk/sysmodule/src/file/file_utils.cpp +++ b/Source/hoc-clk/sysmodule/src/file/file_utils.cpp @@ -83,6 +83,24 @@ namespace fileUtils { va_list args; va_start(args, format); + + char buff[0xfff]; + int len = vsnprintf(buff, sizeof(buff), format, args); + va_end(args); + + if (len < 0) { + return; + } + // Leave room for the newline + NUL. + if ((size_t)len >= sizeof(buff) - 1) { + len = sizeof(buff) - 2; + } + buff[len++] = '\n'; + buff[len] = '\0'; + + // Debug UART log + svcOutputDebugString(buff, len); + if (g_has_initialized) { RefreshFlags(false); @@ -93,14 +111,11 @@ namespace fileUtils { timespec now = {}; clock_gettime(CLOCK_REALTIME, &now); - fprintf(file, "[%luls] ", now.tv_sec - bootTimeS); - vfprintf(file, format, args); - fprintf(file, "\n"); + fprintf(file, "[%luls] %s", now.tv_sec - bootTimeS, buff); fclose(file); } } } - va_end(args); } void WriteContextToCsv(const HocClkContext *context) { diff --git a/Source/hoc-clk/sysmodule/src/file/kip.cpp b/Source/hoc-clk/sysmodule/src/file/kip.cpp index 3fc0e416..4f5f675a 100644 --- a/Source/hoc-clk/sysmodule/src/file/kip.cpp +++ b/Source/hoc-clk/sysmodule/src/file/kip.cpp @@ -222,7 +222,8 @@ namespace kip { !config::GetConfigValue(HocClkConfigValue_IsFirstLoad)) { MigrateKipData(cust_get_cust_rev(&table), cust_get_kip_version(&table)); SetKipData(); - notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console"); + clockManager::gContext.rebootRequired = true; + notification::writeNotification("Horizon OC\nKIP has been updated\nPlease reboot your console to use Horizon OC"); return; } if (config::GetConfigValue(HocClkConfigValue_IsFirstLoad) == true) { diff --git a/Source/hoc-clk/sysmodule/src/hos/lang.cpp b/Source/hoc-clk/sysmodule/src/hos/lang.cpp new file mode 100644 index 00000000..6d959c01 --- /dev/null +++ b/Source/hoc-clk/sysmodule/src/hos/lang.cpp @@ -0,0 +1,141 @@ +/* + * 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 . + * + */ + +#include "lang.hpp" + +#include +#include +#include +#include + +#include "../file/file_utils.hpp" + +namespace lang { + namespace { + + constexpr const char *UltrahandConfigPath = "sdmc:/config/ultrahand/config.ini"; + constexpr const char *LangDir = "sdmc:" FILE_CONFIG_DIR "/lang/"; + + std::string gLoadedLang; + std::unordered_map gCache; + + void NormalizeNewlines(std::string &s) { + size_t n = 0; + while ((n = s.find("\\n", n)) != std::string::npos) { + s.replace(n, 2, "\n"); + n += 1; + } + } + + // Lightweight Ultrahand-compatible flat JSON string map parser. + void ParseJsonContent(const std::string &content, std::unordered_map &result) { + size_t pos = 0; + + while ((pos = content.find('"', pos)) != std::string::npos) { + const size_t keyStart = pos + 1; + const size_t keyEnd = content.find('"', keyStart); + if (keyEnd == std::string::npos) { + break; + } + + const size_t colonPos = content.find(':', keyEnd); + if (colonPos == std::string::npos) { + break; + } + + const size_t valueStart = content.find('"', colonPos); + const size_t valueEnd = content.find('"', valueStart + 1); + if (valueStart == std::string::npos || valueEnd == std::string::npos) { + break; + } + + std::string key = content.substr(keyStart, keyEnd - keyStart); + std::string value = content.substr(valueStart + 1, valueEnd - valueStart - 1); + + NormalizeNewlines(key); + NormalizeNewlines(value); + + result[std::move(key)] = std::move(value); + pos = valueEnd + 1; + } + } + + bool ReadFileContent(const std::string &filePath, std::string &content) { + FILE *file = fopen(filePath.c_str(), "r"); + if (!file) { + return false; + } + + char buffer[256]; + while (fgets(buffer, sizeof(buffer), file) != nullptr) { + content += buffer; + } + fclose(file); + return true; + } + + bool LoadLangFile(const std::string &langCode) { + const std::string path = std::string(LangDir) + langCode + ".json"; + std::string content; + if (!ReadFileContent(path, content)) { + return false; + } + + gCache.clear(); + ParseJsonContent(content, gCache); + gLoadedLang = langCode; + return true; + } + + std::string GetDefaultLang() { + char buf[32] = {}; + ini_gets("ultrahand", "default_lang", "en", buf, sizeof(buf), UltrahandConfigPath); + if (buf[0] == '\0') { + return "en"; + } + return buf; + } + + void EnsureLoaded() { + const std::string langCode = GetDefaultLang(); + if (langCode == gLoadedLang && !gCache.empty()) { + return; + } + + if (!LoadLangFile(langCode) && langCode != "en") { + LoadLangFile("en"); + } + } + + } // namespace + + std::string Translate(const std::string &text) { + if (text.empty()) { + return text; + } + + EnsureLoaded(); + + const auto it = gCache.find(text); + if (it != gCache.end() && !it->second.empty()) { + return it->second; + } + + return text; + } + +} // namespace lang diff --git a/Source/hoc-clk/sysmodule/src/hos/lang.hpp b/Source/hoc-clk/sysmodule/src/hos/lang.hpp new file mode 100644 index 00000000..7b03635b --- /dev/null +++ b/Source/hoc-clk/sysmodule/src/hos/lang.hpp @@ -0,0 +1,29 @@ +/* + * 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 . + * + */ + +#pragma once + +#include + +namespace lang { + + // Translates text using Ultrahand's language setting and + // /config/horizon-oc/lang/.json (same format as the overlay). + // Returns the original text when no translation is available. + std::string Translate(const std::string &text); + +} // namespace lang diff --git a/Source/hoc-clk/sysmodule/src/hos/notification.cpp b/Source/hoc-clk/sysmodule/src/hos/notification.cpp index c5a4f5f0..333b0604 100644 --- a/Source/hoc-clk/sysmodule/src/hos/notification.cpp +++ b/Source/hoc-clk/sysmodule/src/hos/notification.cpp @@ -15,6 +15,7 @@ * */ +#include "lang.hpp" #include "notification.h" namespace notification { @@ -27,13 +28,15 @@ namespace notification { } fclose(flagFile); + const std::string translated = lang::Translate(message); + std::string filename = "hoc-" + std::to_string(std::time(nullptr)) + ".notify"; std::string fullPath = "sdmc:/config/ultrahand/notifications/" + filename; FILE *file = fopen(fullPath.c_str(), "w"); if (file) { fprintf(file, "{\n"); - fprintf(file, " \"text\": \"%s\",\n", message.c_str()); + fprintf(file, " \"text\": \"%s\",\n", translated.c_str()); fprintf(file, " \"fontSize\": 28\n"); fprintf(file, "}\n"); fclose(file); diff --git a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp index 6e85abdf..a4e75ca5 100644 --- a/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp +++ b/Source/hoc-clk/sysmodule/src/mgr/clock_manager.cpp @@ -295,6 +295,47 @@ namespace clockManager { hz++; } + /* Since it is a pain to patch the vtables in ipc we can just hack the freqs in. You can still set them. */ + constexpr u64 EmcClkOSLimitHz = 1600000ULL * 1000; // 1600 MHz + const u64 maxHz = static_cast(config::GetConfigValue(KipConfigValue_marikoEmcMaxClock)) * 1000; + if (module == HocClkModule_MEM && board::GetSocType() == HocClkSocType_Mariko && + kip::kipAvailable && maxHz >= EmcClkOSLimitHz && + config::GetConfigValue(KipConfigValue_stepMode) == 4 /* 33 MHz */) { + + /* Drop the clkrst entries above the OS limit */ + u32 kept = 0; + for (u32 i = 0; i < gFreqTable[module].count; ++i) { + if (static_cast(gFreqTable[module].list[i]) <= EmcClkOSLimitHz) { + gFreqTable[module].list[kept++] = gFreqTable[module].list[i]; + } + } + gFreqTable[module].count = kept; + + auto push = [&](u64 freqHz) { + if (freqHz > maxHz || gFreqTable[module].count >= HOCCLK_FREQ_LIST_MAX) { + return; + } + gFreqTable[module].list[gFreqTable[module].count++] = static_cast(freqHz); + }; + + static const u32 stepFreqs33[] = { + 1633000, 1666000, 1700000, 1733000, 1766000, 1800000, 1833000, 1866000, 1900000, 1933000, + 1966000, 2000000, 2033000, 2066000, 2100000, 2133000, 2166000, 2200000, 2233000, 2266000, + 2300000, 2333000, 2366000, 2400000, 2433000, 2466000, 2500000, 2533000, 2566000, 2600000, + 2633000, 2666000, 2700000, 2733000, 2766000, 2800000, 2833000, 2866000, 2900000, 2933000, + 2966000, 3000000, 3033000, 3066000, 3100000, 3133000, 3166000, 3200000, 3233000, 3266000, + 3300000, 3333000, 3366000, 3400000, 3433000, 3466000, 3500000, + }; + for (u32 f : stepFreqs33) { + push(static_cast(f) * 1000); + } + + if (gFreqTable[module].count == 0 || + static_cast(gFreqTable[module].list[gFreqTable[module].count - 1]) != maxHz) { + push(maxHz); + } + } + fileUtils::LogLine("[mgr] count = %u", gFreqTable[module].count); } @@ -408,12 +449,11 @@ namespace clockManager { if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) { board::PcvHijackGpuVolts(0); // Reset to vMin - u32 targetHz = gContext.overrideFreqs[HocClkModule_GPU]; - u32 nearestHz = GetNearestOverrideHz(HocClkModule_GPU); + u32 targetHz = GetNearestOverrideHz(HocClkModule_GPU); board::ResetToStockGpu(); if (targetHz) - board::SetHz(HocClkModule_GPU, nearestHz); + board::SetHz(HocClkModule_GPU, targetHz); } } @@ -461,7 +501,10 @@ namespace clockManager { u32 nearestFreq = GetCurrentNearestFrequency(HocClkModule_MEM); if (targetRamHz != nearestFreq) { - ApplyGpuDvfs(targetRamHz); + if (config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) { + ApplyGpuDvfs(targetRamHz); + } + board::SetHz(HocClkModule_MEM, targetRamHz); } } @@ -560,7 +603,6 @@ namespace clockManager { if (module == HocClkModule_MEM && targetHz > oldHz && config::GetConfigValue(HocClkConfigValue_DVFSMode) == DVFSMode_Hijack) { ApplyGpuDvfs(targetHz); } - board::SetHz((HocClkModule)module, nearestHz); gContext.freqs[module] = nearestHz; @@ -739,6 +781,11 @@ namespace clockManager { gContext = {}; gContext.applicationId = 0; gContext.profile = HocClkProfile_Handheld; + + /* Load the KIP customize table before building the freq tables: the MEM freq-list + synthesis in RefreshFreqTableRow reads marikoEmcMaxClock / stepMode from it. */ + kip::GetKipData(); + for (unsigned int module = 0; module < HocClkModule_EnumMax; module++) { gContext.freqs[module] = 0; gContext.realFreqs[module] = 0; @@ -757,8 +804,6 @@ namespace clockManager { gLastTempLogNs = 0; gLastCsvWriteNs = 0; - kip::GetKipData(); - board::FuseData *fuse = board::GetFuseData(); gContext.speedos[HocClkSpeedo_CPU] = fuse->cpuSpeedo; diff --git a/build.sh b/build.sh index 3eb884f7..c8040a74 100755 --- a/build.sh +++ b/build.sh @@ -2,7 +2,10 @@ EXT=0 LDR_MAKE="nx_release" +LDR_SET=0 NO_EXO=0 +JOBS="" +UART_LOGGING=0 ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" DIST_DIR="$ROOT_DIR/dist" @@ -14,10 +17,21 @@ while [ $# -gt 0 ]; do ;; --ldr=*) LDR_MAKE="${1#*=}" + LDR_SET=1 + ;; + -u|--uart) + UART_LOGGING=1 ;; --no-exo) NO_EXO=1 ;; + -j) + shift + JOBS="$1" + ;; + -j*) + JOBS="${1#-j}" + ;; *) echo "Unknown option: $1" exit 1 @@ -26,6 +40,10 @@ while [ $# -gt 0 ]; do shift done +if [ "$UART_LOGGING" -eq 1 ] && [ "$LDR_SET" -eq 0 ]; then + LDR_MAKE="nx_audit" +fi + LDR_BUILD_PATH="${LDR_MAKE#nx_}" echo @@ -37,8 +55,24 @@ if [ "$NO_EXO" -eq 1 ]; then echo "NO_EXO = 1" fi +if [ "$UART_LOGGING" -eq 1 ]; then + echo "UART_LOGGING = 1 (loader build: $LDR_MAKE, UART logging enabled)" +fi + CORES="$(nproc --all)" echo "CORES: $CORES" +JOBS="${JOBS:-$CORES}" +echo "JOBS: $JOBS" + +if command -v ccache >/dev/null 2>&1; then + export ATMOSPHERE_CCACHE="ccache" + export CCACHE_BASEDIR="$ROOT_DIR" + export CCACHE_SLOPPINESS="time_macros,include_file_mtime,include_file_ctime,pch_defines,locale" + export CCACHE_MAXSIZE="10G" + echo "CCACHE: enabled ($(ccache --version | head -n1))" +else + echo "CCACHE: not found, building without it" +fi SRC="Source/Atmosphere/stratosphere/loader/" @@ -61,9 +95,9 @@ mkdir -p "$DEST" echo echo "*** Patching loader ***" cp -vr "$SRC"/. "$DEST"/ -echo if [ "$NO_EXO" -eq 0 ]; then + echo echo "*** Patching exosphere ***" EXO_SRC="Source/Atmosphere-Patches" EXO_DEST="build/atmosphere/exosphere/program/source/smc" @@ -78,10 +112,32 @@ if [ "$NO_EXO" -eq 0 ]; then cp -v "$EXO_SRC/secmon_memory_layout.hpp" "$LIBEXO_DEST/" fi +CCACHE_MKS="build/atmosphere/libraries/config/common.mk +build/atmosphere/libraries/config/templates/stratosphere.mk +build/atmosphere/libraries/libstratosphere/libstratosphere.mk" + +for CCACHE_MK in $CCACHE_MKS; do + if ! grep -q "ATMOSPHERE_CCACHE" "$CCACHE_MK"; then + echo + echo "*** Patching $CCACHE_MK for ccache ***" + cat >> "$CCACHE_MK" <<'EOF' + +ifneq ($(strip $(ATMOSPHERE_CCACHE)),) +ifneq ($(firstword $(CC)),$(ATMOSPHERE_CCACHE)) +export CC := $(ATMOSPHERE_CCACHE) $(CC) +endif +ifneq ($(firstword $(CXX)),$(ATMOSPHERE_CCACHE)) +export CXX := $(ATMOSPHERE_CCACHE) $(CXX) +endif +endif +EOF + fi +done + echo echo "*** Compiling loader ***" cd build/atmosphere/stratosphere/loader || exit 1 -make -j$CORES "$LDR_MAKE" +make -j$JOBS HOC_UART_LOG=$UART_LOGGING "$LDR_MAKE" hactool -t kip1 "out/nintendo_nx_arm64_armv8a/$LDR_BUILD_PATH/loader.kip" --uncompress=hoc.kip cd "$ROOT_DIR" # exit cp -v build/atmosphere/stratosphere/loader/hoc.kip dist/atmosphere/kips/hoc.kip @@ -90,24 +146,31 @@ if [ "$NO_EXO" -eq 0 ]; then echo echo "*** Compiling exosphere ***" cd build/atmosphere/exosphere - make -j$CORES + make -j$JOBS cd "$ROOT_DIR" cp -v build/atmosphere/exosphere/out/nintendo_nx_arm64_armv8a/release/exosphere.bin dist/atmosphere/exosphere.bin fi +if [ -n "$ATMOSPHERE_CCACHE" ]; then + echo + echo "*** ccache stats ***" + ccache --show-stats +fi + cd Source/hoc-clk/ ./build.sh cp -r dist/ ../../ cd "$ROOT_DIR" -echo "*** Compiling horizon-oc-monitor ***" -cd Source/Horizon-OC-Monitor/ -make -j$CORES -cp -v Horizon-OC-Monitor.ovl ../../dist/switch/.overlays/Horizon-OC-Monitor.ovl +echo "*** Downloading Status-Monitor ***" +wget "https://github.com/ppkantorski/Status-monitor-overlay/releases/latest/download/Status-Monitor-Overlay.ovl" +mv -v Status-Monitor-Overlay.ovl "$DIST_DIR"/switch/.overlays/Status-Monitor-Overlay.ovl + if [ "$EXT" -eq 1 ]; then - cd ../ + cd Source/ + echo echo "*** Compiling extensions ***" @@ -123,16 +186,18 @@ if [ "$EXT" -eq 1 ]; then cd hekate/ echo echo "*** Compiling custom Hekate ***" - make -j$CORES + make -j$JOBS echo mkdir -p "$DIST_DIR/bootloader/sys/" cp -v output/nyx.bin "$ROOT_DIR"/dist/bootloader/sys/nyx.bin + cp -v output/hekate.bin "$ROOT_DIR"/dist/bootloader/update.bin + cp -v output/hekate.bin "$ROOT_DIR"/dist/payload.bin cd "$ROOT_DIR"/Source/Benchmark-Toolbox echo echo "*** Compiling Benchmark-Toolbox ***" - make -j$CORES + make -j$JOBS cp -v Benchmark-Toolbox.nro "$DIST_DIR"/switch/Benchmark-Toolbox.nro fi